pax_global_header00006660000000000000000000000064132274750020014514gustar00rootroot0000000000000052 comment=fca4261026a9bfc1963a16eee2607883aa08bd27 ulfius-2.2.4/000077500000000000000000000000001322747500200130305ustar00rootroot00000000000000ulfius-2.2.4/.gitignore000066400000000000000000000014011322747500200150140ustar00rootroot00000000000000*.o *.so *.so.* *.a example_programs/auth_example/auth_client example_programs/auth_example/auth_server example_programs/injection_example/injection_example example_programs/proxy_example/proxy example_programs/request_example/client example_programs/request_example/mail example_programs/request_example/server example_programs/simple_example/simple_example example_programs/stream_example/stream_example example_programs/stream_example/stream_client example_programs/test_u_map/test_u_map example_programs/multiple_callbacks_example/multiple_callbacks_example example_programs/sheep_counter/sheep_counter example_programs/websocket_example/websocket_example example_programs/*/valgrind*.txt test/valgrind*.txt test/*.o test/core test/u_map test/framework *.key *.pem ulfius-2.2.4/.gitmodules000066400000000000000000000002651322747500200152100ustar00rootroot00000000000000[submodule "lib/orcania"] path = lib/orcania url = https://github.com/babelouest/orcania.git [submodule "lib/yder"] path = lib/yder url = https://github.com/babelouest/yder.git ulfius-2.2.4/API.md000066400000000000000000001536731322747500200140020ustar00rootroot00000000000000# Ulfius API Documentation ## Header file Include file `ulfius.h` in your source file: ```C #include ``` If you have disabled `libcurl` during the build, define the macro `U_DISABLE_CURL` before including `ulfius.h`: ```C #define U_DISABLE_CURL #include ``` If you have disabled `libjansson` during the build, define the macro `U_DISABLE_JANSSON` before including `ulfius.h`: ```C #define U_DISABLE_JANSSON #include ``` If you have disabled `libgnutls` during the build, define the macro `U_DISABLE_WEBSOCKET` before including `ulfius.h`: ```C #define U_DISABLE_WEBSOCKET #include ``` If you have disabled more than one library, for example both `libcurl` and `libjansson`, define both macros before including `ulfius.h`: ```C #define U_DISABLE_CURL #define U_DISABLE_JANSSON #include ``` On your linker command, add ulfius as a dependency library, e.g. `-lulfius` for gcc. ## API Documentation ### Update existing programs from Ulfius 2.0 to 2.1 - An annoying bug has been fixed that made streaming data a little buggy when used on raspbian. Now if you don't know the data size you're sending, use the macro U_STREAM_SIZE_UNKOWN instead of the previous value -1. There is some updates in the stream callback function parameter types. Check the [streaming data documentation](#streaming-data). - The websocket data structures are no longer available directly in `struct _u_response` or `struct _u_instance`. But you shouldn't use them like this anyway so it won't be a problem. - Unify and update functions name `ulfius_set_*_body_response`. You may have to update your legacy code. The new functions names are: ```c int ulfius_set_string_body_response(struct _u_response * response, const uint status, const char * body); int ulfius_set_binary_body_response(struct _u_response * response, const uint status, const char * body, const size_t length); int ulfius_set_empty_body_response(struct _u_response * response, const uint status); ``` ### Update existing programs from Ulfius 1.x to 2.0 If you already have programs that use Ulfius 1.x and want to update them to the brand new fresh Ulfius 2.0, it may require the following minor changes. #### Endpoints definitions Endpoints structure have changed, `ulfius_add_endpoint_by_val` now requires only one callback function, but requires a priority number. If you don't use authentication callback functions, you can simply remove the `NULL, NULL, NULL` parameters corresponding to the former authentication callback function pointer, the authentication callback user data, and the realm value. Then add any number as a priority, 0 for example. If you use authentication callback functions, split your `ulfius_add_endpoint_by_val` call in 2 separate calls, one for the authentication function, one for the main callback function. For example: ```C // An Ulfius 1.x call ulfius_add_endpoint_by_val(&instance, "GET", "/", NULL, &auth_callback, my_auth_data, "my realm", &main_callback, my_main_data); // The same behaviour with Ulfius 2.0 ulfius_add_endpoint_by_val(&instance, "GET", "/", NULL, 0, &auth_callback, my_auth_data); ulfius_add_endpoint_by_val(&instance, "GET", "/", NULL, 1, &main_callback, my_main_data); // In this case, the realm value "my realm" must be specified in the response ``` #### Callback return value The return value for the callback functions must be adapted, instead of U_OK, U_ERROR or U_ERROR_UNAUTHORIZED, you must use one of the following: ```C #define U_CALLBACK_CONTINUE 0 // Will replace U_OK #define U_CALLBACK_COMPLETE 1 #define U_CALLBACK_UNAUTHORIZED 2 // Will replace U_ERROR_UNAUTHORIZED #define U_CALLBACK_ERROR 3 // Will replace U_ERROR ``` If you want more details on the multiple callback functions, check the [documentation](#callback-functions-return-value). Other functions may have change their name or signature, check the documentation for more information. ### Return values When specified, some functions return `U_OK` on success, and other values otherwise. `U_OK` is 0, other values are non-0 values. The defined return value list is the following: ```C #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 ``` ### Memory management Ulfius uses memory allocation functions `malloc/realloc/free` by default, but you can overwrite this and use any other memory allocation functions of your choice. Use Orcania's functions `o_set_alloc_funcs` and `o_get_alloc_funcs` to set and get memory allocation functions. ```C 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); ``` Data structures allocated have their specific cleanup functions. To free pointer allocated, you should use the function `u_free` that is intended to use your memory management functions. ```c /** * ulfius_clean_instance * * Clean memory allocated by a struct _u_instance * */ void ulfius_clean_instance(struct _u_instance * u_instance); /** * 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_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); /** * free the struct _u_map's inner components * return U_OK on success */ int u_map_clean(struct _u_map * u_map); /** * free data allocated by ulfius functions */ void u_free(void * data); ``` ### Initialization When initialized, Ulfius runs a thread in background that will listen to the specified port and dispatch the calls to the specified functions. Ulfius allows adding and removing new endpoints during the instance execution. To run a webservice, you must initialize a `struct _u_instance` and add your endpoints. The `struct _u_instance` is defined as: ```C /** * * 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; }; ``` In the `struct _u_instance` structure, the element `port` must be set to the port number you want to listen to, the element `bind_address` is used if you want to listen only to a specific IP address. The element `mhd_daemon` is used by the framework, don't modify it. You can use the functions `ulfius_init_instance` and `ulfius_clean_instance` to facilitate the manipulation of the structure: ```C /** * 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, int 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); ``` The `struct _u_endpoint` is defined as: ```C /** * * 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; }; ``` Some functions help you facilitate endpoints manipulation: ```C /** * 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); /** * 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_set_default_callback_function * Set the default callback function * This callback will be called if no endpoint match the url called * 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 the callback function * to remove a default callback function, call ulfius_set_default_callback_function with NULL parameter for callback_function * return U_OK on success */ int ulfius_set_default_callback_function(struct _u_instance * u_instance, int (* callback_function)(const struct _u_request * request, struct _u_response * response, void * user_data), void * user_data); ``` HTTP Method can be an existing or not existing method, or * for any method, you must specify a url_prefix, a url_format or both, callback_function is mandatory, user_data is optional. Your `struct _u_endpoint` array **MUST** end with an empty `struct _u_endpoint`. You can manually declare an endpoint or use the dedicated functions as `int ulfius_add_endpoint` or `int ulfius_add_endpoint_by_val`. If you manipulate the attribute `u_instance.endpoint_list`, you must end the list with an empty endpoint (see `const struct _u_endpoint * ulfius_empty_endpoint()`), and you must set the attribute `u_instance.nb_endpoints` accordingly. Also, you must use dynamically allocated values for attributes `http_method`, `url_prefix` and `url_format`. Ulfius allows multiple callbacks for the same endpoint. This is helpful when you need to execute several actions in sequence, for example check authentication, get resource, set cookie, then gzip response body. That's also why a priority must be set for each callback. The priority is in descending order, which means that it starts with 0 (highest priority) and priority decreases when priority number increases. There is no more signification to the priority number, which means you can use any incrementation of your choice. `Warning`: Having 2 callback functions with the same priority number will result in an undefined result for the callback functions execution order. To help passing parameters between callback functions of the same request, the value `struct _u_response.shared_data` can bse used. But it will not be allocated or freed by the framework, the program using this variable must free by itself. ### Start and stop webservice #### Start webservice The starting point function are `ulfius_start_framework` or `ulfius_start_secure_framework`: ```C /** * ulfius_start_framework * Initializes the framework and run the webservice based on the parameters given * return truze if no error * * 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); ``` In your program where you want to start the web server, simply execute the function `ulfius_start_framework(struct _u_instance * u_instance)` for a non-secure http connection or `ulfius_start_secure_framework(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem)` for a secure https connection, using a valid private key and a valid corresponding server certificate, see openssl documentation for certificate generation. Those function accept the previously declared `instance` as first parameter. You can reuse the same callback function as much as you want for different endpoints. On success, these functions returns `U_CALLBACK_CONTINUE` or `U_CALLBACK_COMPLETE`, otherwise an error code. Note: for security concerns, after running `ulfius_start_secure_framework`, you can free the parameters `key_pem` and `cert_pem` if you want to. #### Stop webservice To stop the webservice, call the following function: ```C /** * 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); ``` ### Callback functions The callback function is the function executed when a user calls an endpoint managed by your webservice (as defined in your `struct _u_endpoint` list). The callback function has the following signature: ```C 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); ``` In the callback function definition, the variables `request` and `response` will be initialized by the framework, and the `user_data` variable will be assigned to the user_data defined in your endpoint list definition. The request variable is defined as: ```C /** * * 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; }; ``` The response variable is defined as: ```C /** * * 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; }; ``` In the response variable set by the framework to the callback function, the structure is initialized with no data. The user can set the `binary_body` before the return statement, or no response body at all if no need. If a `binary_body` is set, its size must be set to `binary_body_length`. `binary_body` is free'd by the framework when the response has been sent to the client, so you must use dynamically allocated values. If no status is set, status 200 will be sent to the client. Some functions are dedicated to handle the response: ```C /** * 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_response * Add a binary body to a response * return U_OK on success */ int ulfius_set_binary_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); ``` ### JSON body in request and response In Ulfius 2.0, hard dependency with libjansson has been removed, the jansson library is now optional but enabled by default. If you want to remove libjansson dependency, build Ulfius library with the flag `JANSSONFLAG=-DU_DISABLE_JANSSON` ``` $ make JANSSONFLAG=-DU_DISABLE_JANSSON ``` if libjansson library is enabled, the following functions are available in Ulfius: ```C /** * 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); ``` The `jansson` api documentation is available at the following address: [Jansson documentation](https://jansson.readthedocs.org/). #### Callback functions return value The callback returned value can have the following values: - `U_CALLBACK_CONTINUE`: The framework can transfer the request and the response to the next callback function in priority order if there is one, or complete the transaction and send back the response to the client. - `U_CALLBACK_COMPLETE`: The framework must complete the transaction and send the response to the client without calling any further callback function. - `U_CALLBACK_UNAUTHORIZED`: The framework must complete the transaction without calling any further callback function and send an unauthorized response to the client with the status 401, the body specified and the `auth_realm` value if specified. - `U_CALLBACK_ERROR`: An error occured during execution, the framework must complete the transaction without calling any further callback function and send an error 500 to the client. In addition with manipulating the raw parameters of the structures, you can use the `_u_request` and `_u_response` structures by using specific functions designed to facilitate their use and memory management: ```C /** * 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 free'd 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 free'd after use */ struct _u_response * ulfius_duplicate_response(const struct _u_response * response); ``` ### Memory management The Ulfius framework will automatically free the variables referenced by the request and responses structures, except for `struct _u_response.shared_data`, so you must use dynamically allocated values for the response pointers. ### Character encoding You may be careful with characters encoding if you use non UTF8 characters in your application or webservice, and especially if you use different encodings in the same application. Ulfius has not been fully tested in cases like that. ### Cookie management The map_cookie structure will contain a set of key/values for the cookies. The cookie structure is defined as ```C /** * 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; }; ``` You can use the function `ulfius_add_cookie_to_response` in your callback function to facilitate cookies management. This function is defined as: ```C /** * 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); ``` ### File upload Ulifius allows file upload to the server. Beware that an uploaded file will be stored in the request object in memory, so uploading large files may dramatically slow the application or even crash it, depending on your system. An uploaded file is stored in the `request->map_body` structure. You can use `u_map_get_length` to get the exact length of the file as it may not be a string format. If you want to limit the size of a post parameter, if you want to limit the file size for example, set the value `struct _u_instance.max_post_param_size`. Files or post data exceeding this size will be truncated to the size `struct _u_instance.max_post_param_size`. If this parameter is 0, then no limit is set. Default value is 0. If you want to handle file upload yourself, you can intercept the file upload process with your own callback function. Before running the webservice with `ulfius_start_framework`, you must call the function `ulfius_set_upload_file_callback_function` with a pointer to your file upload callback function. By using this method, the specified callback function will be executed as much as needed with a chunk of the file upload each time. This function `ulfius_set_upload_file_callback_function` has the following prototype: ```C /** * ulfius_set_upload_file_callback_function * * Set the callback function to handle file upload * Used to facilitate large files upload management * The callback function file_upload_callback will be called * multiple times, with the uploaded file in striped in parts * * Warning: If this function is used, all the uploaded files * for the instance will be managed via this function, and they * will no longer be available in the struct _u_request in the * ulfius callback function afterwards. * * Thanks to Thad Phetteplace for the help on this feature * * u_instance: pointer to a struct _u_instance that describe its port and bind address * file_upload_callback: Pointer to a callback function that will handle all file uploads * cls: a pointer that will be passed to file_upload_callback each tim it's called */ int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance, int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls), void * cls); ``` This callback function will be called before all the other callback functions, and be aware that not all parameters, especially url parameters, will be present during the file upload callback function executions. See `examples/sheep_counter` for a file upload example. ### Streaming data If you need to stream data, i.e. send a variable and potentially large amount of data, you can define and use `stream_callback_function` in the `struct _u_response`. Not that if you stream data to the client, any data that was in the `response->binary_body` will be ignored. You must at least set the function pointer `struct _u_response.stream_callback` to stream data. Set `stream_size` to U_STREAM_SIZE_UNKOWN if you don't know the size of the data you need to send, like in audio stream for example. Set `stream_block_size` according to you system resources to avoid out of memory errors, also, set `stream_callback_free` with a pointer to a function that will free values allocated by your stream callback function, as a `close()` file for example, and finally, you can set `stream_user_data` to a pointer. You can use the function `ulfius_set_stream_response` to set those parameters. The prototype of the `stream_callback` function is the following: ```C ssize_t stream_callback (void * stream_user_data, // Your predefined user_data uint64_t offset, // the position of the current data to send char * out_buf, // The output buffer to fill with data size_t max); // the max size of data to be put in the out_buf ``` The return value must be the size of the data put in `out_buf`. This function will be called over and over in loop as long as the client has the connection opened. If you want to close the stream from the server side, return `U_STREAM_END` in the `stream_callback` function. If a problem occured, you can close the connection with a `U_STREAM_ERROR` return value. While the `stream_callback_free` function is as simple as: ```C void stream_callback_free (void * stream_user_data); ``` Check the program `stream_example` in the example folder. ### Websockets communication Websocket communication is possible via callback functions defined in your program. The framework will handle sending and receiving messages with the clients, and your program will deal with high level functions to facilitate the communication process. The communication will respect the [RFC6455](https://tools.ietf.org/html/rfc6455) of the websocket protocol. #### Starting a websocket communication To start a websocket communication between the client and your program, you must use the dedicated function `ulfius_start_websocket_cb` with proper values: ```C /** * 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); ``` According to the Websockets RFC, parameters `websocket_protocol` and `websocket_extensions` are specific for your application. In Ulfius Implementation, if you specify a list of protocols as a string of protocol names, separated by a comma, Ulfius will check each one and see if they match the list of protocols specified by the client. The resulting protocol list will be sent back to the client. If no protocol match your list, the connection will be closed by the framework and will return an error 400 to the client. If you set a `NULL` value, Ulfius will accept all protocols sent by the client. This behaviour is the same with websocket extension check. 3 callback functions are available for the websocket implementation: - `websocket_manager_callback`: This function will be called in a separate thread, and the websocket will remain open as long as this callback function is not completed. In this function, your program will have access to the websocket status (connected or not), and the list of messages sent and received. When this function ends, the websocket will close itself automatically. - `websocket_incoming_message_callback`: This function will be called every time a new message is sent by the client. Although, it is in synchronous mode, which means that you won't have 2 different `websocket_incoming_message_callback` of the same websocket executed at the same time. - `websocket_onclose_callback`: This function will be called right after the websocket connection is closed, but before the websocket structure is cleaned. You must specify at least one of the callback functions `websocket_manager_callback` or `websocket_incoming_message_callback`. When the function `ulfius_stop_framework` is called, it will wait for all running websockets to end by themselves, there is no force close. So if you have a `websocket_manager_callback` function running, you *MUST* end this function in order to make a clean stop of the http daemon. For each of these callback function, you can specify a `*_user_data` pointer containing any data you need. #### Manipulating websocket data structure The websocket manager data structure definition is the following: ```C /** * 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; }; ``` The most important data you will need are `message_list_incoming`, `message_list_outcoming` and `connected`. The other ones are reserved for the framework, do not alterate them. The `connected` value will tell you wether the websocket is connected or not, `message_list_incoming`, `message_list_outcoming` will contain the message lists. These lists are updated in real time during the execution, do not try to update them directly. When an incoming message will arrive, the `message_list_incoming` table will be updated, and the `websocket_incoming_message_callback` function will be called too, depending on your application behaviour, use the method you prefer for incoming messages. #### Messages manipulation A message has the following structure: ```C /** * 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; }; ``` A message list has the following strucure: ```C struct _websocket_message_list { struct _websocket_message ** list; size_t len; }; ``` If you want to send a message to the client, you must use the dedicated function `ulfius_websocket_send_message`: ```C /** * 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); ``` You can also pop the first message of the incoming or outcoming if you need to with `ulfius_websocket_pop_first_message`, this will remove the first message of the list, and return it as a pointer. You can free a message using the function `ulfius_clear_websocket_message`: ```C /** * 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); ``` ### struct _u_map API The `struct _u_map` is a simple key/value mapping API used in the requests and the response for setting parameters. The available functions to use this structure are: ```C /** * 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 */ size_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 */ size_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 target * If key is already present in target, it's overwritten * return U_OK on success, error otherwise */ int u_map_copy_into(const struct _u_map * source, struct _u_map * target); /** * 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); ``` ## Output request functions Ulfius allows output functions to send HTTP or SMTP requests. These functions use `libcurl`. You can disable these functions by appending the argument `CURLFLAG=-DU_DISABLE_CURL` when you build the library: ``` $ make CURLFLAG=-DU_DISABLE_CURL ``` ### Send HTTP request API The functions `int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response)` and `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)` are based on `libcurl` api. They allow to send an HTTP request with the parameters specified by the `_u_request` structure. Use the parameter `_u_request.http_url` to specify the distant url to call. You can fill the maps in the `_u_request` structure with parameters, they will be used to build the request. Note that if you fill `_u_request.map_post_body` with parameters, the content-type `application/x-www-form-urlencoded` will be use to encode the data. The response parameters is stored into the `_u_response` structure. If you specify NULL for the response structure, the http call will still be made but no response details will be returned. If you use `ulfius_send_http_request`, the response body will be stored in the parameter `response->*body*`, if you use `ulfius_send_http_streaming_request`, the response body will be available in the `write_body_function` specified in the call. The `ulfius_send_http_streaming_request` can be used for streaming data or large response. Return value is `U_OK` on success. This functions are defined as: ```C /** * 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); ``` ### Send SMTP request API The function `ulfius_send_smtp_email` is used to send emails using a smtp server. It is based on `libcurl` API. It's used to send emails (without attached files) via a smtp server. This function is defined as: ```C /** * Send an email * 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); ``` ulfius-2.2.4/INSTALL.md000066400000000000000000000111031322747500200144540ustar00rootroot00000000000000# Install Ulfius ## Debian-ish packages [![Packaging status](https://repology.org/badge/vertical-allrepos/ulfius.svg)](https://repology.org/metapackage/ulfius) Ulfius is now available in Debian Buster (testing) and some Debian based distributions. To install it on your device, use the following command as root: ```shell # apt install libulfius-dev # Or apt install libulfius.1 if you don't need the development files ``` ## Manual install ### Prerequisites #### External dependencies To install all the external dependencies, for Debian based distributions (Debian, Ubuntu, Raspbian, etc.), run as root: ```shell # apt-get install libmicrohttpd-dev libjansson-dev libcurl4-gnutls-dev libgnutls28-dev libgcrypt20-dev ``` #### Note Here libcurl4-gnutls-dev for the example, but any `libcurl*-dev` library should be sufficent, depending on your needs and configuration. Note that gnutls is mandatory for websocket management and https support. Also, it seems that Debian Wheezy uses an old version of libjansson (2.3), you can either upgrade to jessie or download the latest version of libjansson from [github](https://github.com/akheron/jansson). As in version 2.0, libcurl and libjansson are no longer mandatory if you don't need one or both. If you want to use the websocket server functions, you need to install libmicrohttpd 0.9.53 minimum version. ### Installation #### Install Ulfius as a shared library Download Ulfius source code from Github, get the submodules, compile and install each submodule, then compile and install ulfius: ```shell $ git clone https://github.com/babelouest/ulfius.git $ cd ulfius/ $ git submodule update --init $ cd lib/orcania $ make && sudo make install $ cd ../yder $ make && sudo make install $ cd ../.. $ make $ sudo make install ``` #### Update Ulfius If you update Ulfius from a previous version, you must install the corresponding version of the submodules as well: ```shell $ cd ulfius/ $ git pull # Or git checkout $ git submodule update $ cd lib/orcania $ make && sudo make install $ cd ../yder $ make && sudo make install $ cd ../.. $ make $ sudo make install ``` #### Disable dependencies To disable libcurl functions, append the option `CURLFLAG=-DU_DISABLE_CURL` to the make command when you build Ulfius: ```shell $ make CURLFLAG=-DU_DISABLE_CURL ``` If libcurl functions are disabled, `libcurl4-gnutls-dev` is no longer mandatory for install. To disable libjansson functions, append the option `JANSSONFLAG=-DU_DISABLE_JANSSON` to the make command when you build Ulfius and Orcania: ```shell $ make JANSSONFLAG=-DU_DISABLE_JANSSON ``` If libjansson functions are disabled, `libjansson-dev` is no longer mandatory for install. To disable websocket implementation and avoid installing libgnutls, append the option `WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET` to the make command when you build Ulfius: ```shell $ make WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET ``` If websocket functions are disabled, `libgnutls-dev` is no longer mandatory for install. To disable two or more libraries, append options, example: ```shell $ make CURLFLAG=-DU_DISABLE_CURL JANSSONFLAG=-DU_DISABLE_JANSSON ``` #### Installation directory By default, the shared libraries and the header files will be installed in the `/usr/local` location. To change this setting, you can modify the `PREFIX` value in the `src/Makefile`, `lib/orcania/src/Makefile` and `lib/yder/src/Makefile` files. ```shell $ make PREFIX=/tmp install # to install ulfius in /tmp/lib for example ``` You can install Ulfius without root permission if your user has write access to `$(PREFIX)`. A `ldconfig` command is executed at the end of the install, it will probably fail if you don't have root permission, but this is harmless. If you choose to install Ulfius in another directory, you must set your environment variable `LD_LIBRARY_PATH` properly. #### Install from a `.zip` archive If you download Ulfius as a `.zip` or `.tar.gz` file via github release tab, you must initiaize the submodules prior to the compilaton with the following command: ```shell $ cd ulfius/ $ git submodule update --init ``` #### Install as a static archive To install Ulfius library as a static archive, `libulfius.a`, use the make commands `make static*`: The archives `liborcania.a` and `libyder.a` must previously be installed in the $(PREFIX) directory. ```shell $ cd ulfius/src $ make static && sudo make static-install # or make PREFIX=/tmp static-install if you want to install in `/tmp/lib` ``` The example program `example_program/simple_example` has a static make command to test and validate building a program with the archive. ulfius-2.2.4/LICENSE000066400000000000000000000635361322747500200140520ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. (This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.) Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This library 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; either version 2.1 of the License, or (at your option) any later version. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. {signature of Ty Coon}, 1 April 1990 Ty Coon, President of Vice That's all there is to it! ulfius-2.2.4/Makefile000066400000000000000000000025531322747500200144750ustar00rootroot00000000000000# # Ulfius Framework # # Makefile used to build all programs # # Copyright 2014-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # LIBULFIUS_LOCATION=./src LIBORCANIA_LOCATION=lib/orcania/src LIBYDER_LOCATION=lib/yder/src EXAMPLES_LOCATION=./example_programs TESTS_LOCATION=./test ifeq (($(JANSSONFLAG)),"") ADD_JANSSONFLAG="JANSSONFLAG=-DU_DISABLE_JANSSON" endif ifeq (($(CURLFLAG)),"") ADD_CURLFLAG="CURLFLAG=-DU_DISABLE_CURL" endif ifeq (($(WEBSOCKETFLAG)),"") ADD_WEBSOCKETFLAG="WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET" endif all: libulfius.so debug: cd $(LIBULFIUS_LOCATION) && $(MAKE) debug $(ADD_JANSSONFLAG) $(ADD_CURLFLAG) $(ADD_WEBSOCKETFLAG) clean: cd $(LIBORCANIA_LOCATION) && $(MAKE) clean cd $(LIBYDER_LOCATION) && $(MAKE) clean cd $(LIBULFIUS_LOCATION) && $(MAKE) clean cd $(EXAMPLES_LOCATION) && $(MAKE) clean cd $(TESTS_LOCATION) && $(MAKE) clean install: cd $(LIBULFIUS_LOCATION) && $(MAKE) install uninstall: cd $(LIBULFIUS_LOCATION) && $(MAKE) uninstall libulfius.so: cd $(LIBULFIUS_LOCATION) && $(MAKE) $(ADD_JANSSONFLAG) $(ADD_CURLFLAG) $(ADD_WEBSOCKETFLAG) ulfius-2.2.4/README.md000066400000000000000000000277231322747500200143220ustar00rootroot00000000000000# Ulfius HTTP Framework for REST Applications in C. Based on [GNU Libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) for the backend web server, [Jansson](http://www.digip.org/jansson/) for the json manipulation library, and [Libcurl](http://curl.haxx.se/libcurl/) for the http/smtp client API. Used to facilitate creation of web applications in C programs with a small memory footprint, as in embedded systems applications. You can create webservices in HTTP or HTTPS mode, stream data, or implement server websockets. ## Hello World! example application The source code of a hello world using Ulfius is the following: ```c /** * test.c * Small Hello World! example * to compile with gcc, run the following command * gcc -o test test.c -lulfius */ #include #include #define PORT 8080 /** * Callback function for the web application on /helloworld url call */ int callback_hello_world (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World!"); return U_CALLBACK_CONTINUE; } /** * main function */ int main(void) { struct _u_instance instance; // Initialize instance with the port number if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { fprintf(stderr, "Error ulfius_init_instance, abort\n"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", "/helloworld", NULL, 0, &callback_hello_world, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { fprintf(stderr, "Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } ``` ## Installation See [INSTALL.md](INSTALL.md) file for installation details ## Documentation See [API.md](API.md) file for API documentation details ## Example programs source code Example programs are available to understand the different functionalities available, see [example_programs](https://github.com/babelouest/ulfius/blob/master/example_programs) folder for detailed sample source codes and documentation. ## Example callback functions Example callback functions are available in the folder [example_callbacks](https://github.com/babelouest/ulfius/blob/master/example_callbacks). The example callback functions available are: - static file server: to provide static files of a specific folder - oauth2 bearer: to check the validity of a Oauth2 bearer jwt token. Requires [libjwt](https://github.com/benmcollins/libjwt). ## Projects using Ulfius framework - [Angharad](https://github.com/babelouest/angharad), House automation system for ZWave and other types of devices - [Glewlwyd](https://github.com/babelouest/glewlwyd), a lightweight OAuth2 authentication server that provides [JSON Web Tokens](https://jwt.io/) - [Hutch](https://github.com/babelouest/hutch), a safe locker for passwords and other secrets, using client side encryption only - [Taliesin](https://github.com/babelouest/taliesin), a lightweight audio streaming server - [Taulas Raspberry Pi Serial interface](https://github.com/babelouest/taulas/tree/master/taulas_raspberrypi_serial), an interface for Arduino devices that implent [Taulas](https://github.com/babelouest/taulas/) protocol, a house automation protocol ## What's new in Ulfius 2.2? Allow to use your own callback function when uploading files with `ulfius_set_upload_file_callback_function`, so a large file can be uploaded, even with the option `struct _u_instance.max_post_param_size` set. ## What's new in Ulfius 2.1? I know it wasn't long since Ulfius 2.0 was released. But after some review and tests, I realized some adjustments had to be made to avoid bugs and to clean the framework a little bit more. Some of the adjustments made in the new release: - An annoying bug has been fixed that made streaming data a little buggy when used on raspbian. Now if you don't know the data size you're sending, use the macro U_STREAM_SIZE_UNKOWN instead of the previous value -1. There is some updates in the stream callback function parameter types. Check the [streaming data documentation](API.md#streaming-data). - Fix bug on `ulfius_send_http_request` that didn't send back all headers value with the same name (#19) - Fix websocket declaration structures to have them outside of the `ulfius.h`, because it could lead to horrifying bugs when you compile ulfius with websocket but add `#define U_DISABLE_WEBSOCKET` in your application. - Add proxy value for outgoing requests (#18) - Unify and update functions name `ulfius_set_[string|json|binary]_body`. You may have to update your legacy code. The minor version number has been incremented, from 2.0 to 2.1 because some of the changes may require changes in your own code. ## What's new in Ulfius 2.0? Ulfius 2.0 brings several changes that make the library incompatible with Ulfius 1.0.x branch. The goal of making Ulfius 2.0 is to make a spring cleaning of some functions, remove what is apparently useless, and should bring bugs and memory loss. The main new features are multiple callback functions and websockets implementation. ### Multiple callback functions Instead of having an authentication callback function, then a main callback function, you can now have as much callback functions as you want for the same endpoint. A `priority` number has been added in the `struct _u_endpoint` and the auth_callback function and its dependencies have been removed. For example, let's say you have the following endpoints defined: - `GET` `/api/tomato/:tomato` => `tomato_get_callback` function, priority 10 - `GET` `/api/potato/:potato` => `potato_get_callback` function, priority 10 - `GET` `/api/*` => `api_validate_callback` function, priority 5 - `*` `*` => `authentication_callback` function, priority 1 - `GET` `*` => `gzip_body_callback` function, priority 99 Then if the client calls the url `GET` `/api/potato/myPotato`, the following callback functions will be called in that order: - `authentication_callback` - `api_validate_callback` - `potato_get_callback` - `gzip_body_callback` *Warning:* In this example, the url parameter `myPotato` will be availabe only in the `potato_get_callback` function, because the other endpoints did not defined a url parameter after `/potato`. If you need to communicate between callback functions for any purpose, you can use the new parameter `struct _u_response.shared_data`. This is a `void *` pointer initialized to `NULL`. If you use it, remember to free it after use, because the framework won't. ### Keep only binary_body in struct _u_request and struct _u_response the values `string_body` and `json_body` have been removed from the structures `struct _u_request` and `struct _u_response`. This may be painless in the response if you used only the functions `ulfius_set_xxx_body_response`. Otherwise, you should make small arrangements to your code. ### Websockets Ulfius now allows websockets communication between the client and the server. Check the [API.md](API.md#websockets-communication) file for implementation details. Using websocket requires [libgnutls](https://www.gnutls.org/). It also requires a recent version of [Libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/), at least 0.9.53. If you dont need or can't use this feature, you can disable it by adding the option `WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET` to the make command when you build Ulfius: ```shell $ make WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET ``` ### Remove libjansson and libcurl hard dependency In Ulfius 1.0, libjansson and libcurl were mandatory to build the library, but their usage was not in the core of the framework. Although they can be very useful, so the dependency is now optional. They are enabled by default, but if you don't need them, you can disable them when you build Ulfius library. #### libjansson dependency This dependency allows to use the following functions: ```c /** * 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); ``` If you want to disable these functions, append `JANSSONFLAG=-DU_DISABLE_JANSSON` when you build Ulfius library. ``` $ git clone https://github.com/babelouest/ulfius.git $ cd ulfius/ $ git submodule update --init $ make JANSSONFLAG=-DU_DISABLE_JANSSON $ sudo make install ``` #### libcurl dependency This dependency allows to use the following functions: ```c /** * 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); ``` If you want to disable these functions, append `CURLFLAG=-DU_DISABLE_CURL` when you build Ulfius library. ``` $ git clone https://github.com/babelouest/ulfius.git $ cd ulfius/ $ git submodule update --init $ make CURLFLAG=-DU_DISABLE_CURL $ sudo make install ``` If you wan to disable libjansson and libcurl, you can append both parameters. ``` $ git clone https://github.com/babelouest/ulfius.git $ cd ulfius/ $ git submodule update --init $ make CURLFLAG=-DU_DISABLE_CURL JANSSONFLAG=-DU_DISABLE_JANSSON $ sudo make install ``` ### Ready-to-use callback functions You can find some ready-to-use callback functions in the folder [example_callbacks](https://github.com/babelouest/ulfius/blob/master/example_callbacks). ## Questions, problems ? I'm open for questions and suggestions, feel free to open an issue or send a pull request if you feel like it! ulfius-2.2.4/example_callbacks/000077500000000000000000000000001322747500200164625ustar00rootroot00000000000000ulfius-2.2.4/example_callbacks/oauth2_bearer/000077500000000000000000000000001322747500200212045ustar00rootroot00000000000000ulfius-2.2.4/example_callbacks/oauth2_bearer/README.md000066400000000000000000000034141322747500200224650ustar00rootroot00000000000000# Token validation for resource service based on [Ulfius](https://github.com/babelouest/ulfius) framework These files contain an authentication callback for Ulfius framework to validate a Glewlwyd access token with the correct scope. To use this file, you must create a `struct _glewlwyd_resource_config` with your specific parameters: ```C struct _glewlwyd_resource_config { int method; // Values are G_METHOD_HEADER, G_METHOD_BODY or G_METHOD_URL for the access_token location, see https://tools.ietf.org/html/rfc6750 char * oauth_scope; // Scope values required by the resource, multiple values must be separated by a space character char * jwt_decode_key; // The key used to decode an access token jwt_alg_t jwt_alg; // The algorithm used to encode a token, see http://benmcollins.github.io/libjwt/ char * realm; // Optional, a realm value that will be sent back to the client }; ``` Then, you use `callback_check_glewlwyd_access_token` as authentication callback for your ulfius endpoints that need to validate a glewlwyd access_token, example: ```C struct _glewlwyd_resource_config g_config; g_config.method = G_METHOD_HEADER; g_config.oauth_scope = "scope1"; g_config.jwt_decode_key = "secret"; g_config.jwt_alg = JWT_ALG_HS512; g_config.realm = "example"; // First example, add an endpoint with the authentication callback callback_check_glewlwyd_access_token ulfius_add_endpoint_by_val(instance, "GET", "/api", "/resurce/:id", &callback_check_glewlwyd_access_token, (void*)g_config, NULL, &callback_get_resource, (void*)config); // Second example, use callback_check_glewlwyd_access_token as a default authentication callback ulfius_set_default_auth_function(instance, &callback_check_glewlwyd_access_token, (void*)g_config, NULL); ``` ulfius-2.2.4/example_callbacks/oauth2_bearer/glewlwyd_resource.c000066400000000000000000000236261322747500200251260ustar00rootroot00000000000000/** * * Glewlwyd OAuth2 Authorization token check * * Copyright 2016-2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * License as published by the Free Software Foundation; * version 3 of the License. * * This program 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 Lesser General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include "glewlwyd_resource.h" /** * check if bearer token has some of the specified scope */ int callback_check_glewlwyd_access_token (const struct _u_request * request, struct _u_response * response, void * user_data) { struct _glewlwyd_resource_config * config = (struct _glewlwyd_resource_config *)user_data; json_t * j_access_token = NULL, * j_res_scope; int res = U_CALLBACK_UNAUTHORIZED, res_validity; const char * token_value = NULL; char * response_value = NULL; if (config != NULL) { switch (config->method) { case G_METHOD_HEADER: if (u_map_get(request->map_header, HEADER_AUTHORIZATION) != NULL) { if (o_strstr(u_map_get(request->map_header, HEADER_AUTHORIZATION), HEADER_PREFIX_BEARER) == u_map_get(request->map_header, HEADER_AUTHORIZATION)) { token_value = u_map_get(request->map_header, HEADER_AUTHORIZATION) + o_strlen(HEADER_PREFIX_BEARER); } } break; case G_METHOD_BODY: if (o_strstr(u_map_get(request->map_header, ULFIUS_HTTP_HEADER_CONTENT), MHD_HTTP_POST_ENCODING_FORM_URLENCODED) != NULL && u_map_get(request->map_post_body, BODY_URL_PARAMETER) != NULL) { token_value = u_map_get(request->map_post_body, BODY_URL_PARAMETER); } break; case G_METHOD_URL: token_value = u_map_get(request->map_url, BODY_URL_PARAMETER); break; } if (token_value != NULL) { j_access_token = access_token_check_signature(config, token_value); if (check_result_value(j_access_token, G_OK)) { res_validity = access_token_check_validity(config, json_object_get(j_access_token, "grants")); if (res_validity == G_OK) { j_res_scope = access_token_check_scope(config, json_object_get(j_access_token, "grants")); if (check_result_value(j_res_scope, G_ERROR_INSUFFICIENT_SCOPE)) { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"insufficient_scope\",error_description=\"The scope is invalid\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } else if (!check_result_value(j_res_scope, G_OK)) { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"Internal server error\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } else { res = U_CALLBACK_CONTINUE; response->shared_data = (void*)json_pack("{ssso}", "username", json_string_value(json_object_get(json_object_get(j_access_token, "grants"), "username")), "scope", json_copy(json_object_get(j_res_scope, "scope"))); if (response->shared_data == NULL) { res = U_CALLBACK_ERROR; } } json_decref(j_res_scope); } else if (res_validity == G_ERROR_INVALID_TOKEN) { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"The access token is invalid\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } else { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"Internal server error\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } } else { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_request\",error_description=\"The access token is invalid\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } json_decref(j_access_token); } else { response_value = msprintf(HEADER_PREFIX_BEARER "%s%s%serror=\"invalid_token\",error_description=\"The access token is missing\"", (config->realm!=NULL?"realm=":""), (config->realm!=NULL?config->realm:""), (config->realm!=NULL?",":"")); u_map_put(response->map_header, HEADER_RESPONSE, response_value); o_free(response_value); } } return res; } /** * Validates if an access_token grants has a valid scope * return the final scope list on success */ json_t * access_token_check_scope(struct _glewlwyd_resource_config * config, json_t * j_access_token) { int i, scope_count_token, scope_count_expected; char ** scope_list_token, ** scope_list_expected; json_t * j_res = NULL, * j_scope_final_list = json_array(); if (j_scope_final_list != NULL) { if (j_access_token != NULL) { scope_count_token = split_string(json_string_value(json_object_get(j_access_token, "scope")), " ", &scope_list_token); scope_count_expected = split_string(config->oauth_scope, " ", &scope_list_expected); if (scope_count_token > 0 && scope_count_expected > 0) { for (i=0; scope_count_expected > 0 && scope_list_expected[i] != NULL; i++) { if (string_array_has_value((const char **)scope_list_token, scope_list_expected[i])) { json_array_append_new(j_scope_final_list, json_string(scope_list_expected[i])); } } if (json_array_size(j_scope_final_list) > 0) { j_res = json_pack("{siso}", "result", G_OK, "scope", json_copy(j_scope_final_list)); } else { j_res = json_pack("{si}", "result", G_ERROR_INSUFFICIENT_SCOPE); } } else { j_res = json_pack("{si}", "result", G_ERROR_INTERNAL); } free_string_array(scope_list_token); free_string_array(scope_list_expected); } else { j_res = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } } else { j_res = json_pack("{si}", "result", G_ERROR_INTERNAL); } json_decref(j_scope_final_list); return j_res; } /** * Validates if an access_token grants has valid parameters: * - username: non empty string * - type: match "access_token" * - iat + expires_in < now */ int access_token_check_validity(struct _glewlwyd_resource_config * config, json_t * j_access_token) { time_t now; json_int_t expiration; int res; if (j_access_token != NULL) { // Token is valid, check type and expiration date time(&now); expiration = json_integer_value(json_object_get(j_access_token, "iat")) + json_integer_value(json_object_get(j_access_token, "expires_in")); if (now < expiration && json_object_get(j_access_token, "type") != NULL && json_is_string(json_object_get(j_access_token, "type")) && 0 == o_strcmp("access_token", json_string_value(json_object_get(j_access_token, "type"))) && json_object_get(j_access_token, "username") != NULL && json_is_string(json_object_get(j_access_token, "username")) && json_string_length(json_object_get(j_access_token, "username")) > 0) { res = G_OK; } else { res = G_ERROR_INVALID_REQUEST; } } else { res = G_ERROR_INVALID_TOKEN; } return res; } /** * validates if the token value is a valid jwt and has a valid signature */ json_t * access_token_check_signature(struct _glewlwyd_resource_config * config, const char * token_value) { json_t * j_return, * j_grants; jwt_t * jwt = NULL; char * grants; if (token_value != NULL) { if (!jwt_decode(&jwt, token_value, (const unsigned char *)config->jwt_decode_key, o_strlen(config->jwt_decode_key)) && jwt_get_alg(jwt) == config->jwt_alg) { grants = jwt_get_grants_json(jwt, NULL); j_grants = json_loads(grants, JSON_DECODE_ANY, NULL); if (j_grants != NULL) { j_return = json_pack("{siso}", "result", G_OK, "grants", j_grants); } else { j_return = json_pack("{si}", "result", G_ERROR); } o_free(grants); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } jwt_free(jwt); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } return j_return; } /** * Return the ayload of an access token */ json_t * access_token_get_payload(const char * token_value) { json_t * j_return, * j_grants; jwt_t * jwt = NULL; char * grants; if (token_value != NULL) { if (!jwt_decode(&jwt, token_value, NULL, 0)) { grants = jwt_get_grants_json(jwt, NULL); j_grants = json_loads(grants, JSON_DECODE_ANY, NULL); if (j_grants != NULL) { j_return = json_pack("{siso}", "result", G_OK, "grants", j_grants); } else { j_return = json_pack("{si}", "result", G_ERROR); } o_free(grants); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } jwt_free(jwt); } else { j_return = json_pack("{si}", "result", G_ERROR_INVALID_TOKEN); } return j_return; } ulfius-2.2.4/example_callbacks/oauth2_bearer/glewlwyd_resource.h000066400000000000000000000035461322747500200251320ustar00rootroot00000000000000/** * * Glewlwyd OAuth2 Authorization token check * * Copyright 2016-2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * License as published by the Free Software Foundation; * version 3 of the License. * * This program 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 Lesser General Public License * along with this program. If not, see . * */ #include #define G_OK 0 #define G_ERROR 1 #define G_ERROR_INTERNAL 2 #define G_ERROR_INVALID_REQUEST 3 #define G_ERROR_INVALID_TOKEN 4 #define G_ERROR_INSUFFICIENT_SCOPE 5 #define G_METHOD_HEADER 0 #define G_METHOD_BODY 1 #define G_METHOD_URL 2 #define HEADER_PREFIX_BEARER "Bearer " #define HEADER_RESPONSE "WWW-Authenticate" #define HEADER_AUTHORIZATION "Authorization" #define BODY_URL_PARAMETER "access_token" struct _glewlwyd_resource_config { int method; char * oauth_scope; char * jwt_decode_key; jwt_alg_t jwt_alg; char * realm; }; int callback_check_glewlwyd_access_token (const struct _u_request * request, struct _u_response * response, void * user_data); json_t * access_token_check_signature(struct _glewlwyd_resource_config * config, const char * token_value); json_t * access_token_get_payload(const char * token_value); int access_token_check_validity(struct _glewlwyd_resource_config * config, json_t * j_access_token); json_t * access_token_check_scope(struct _glewlwyd_resource_config * config, json_t * j_access_token); ulfius-2.2.4/example_callbacks/static_file/000077500000000000000000000000001322747500200207505ustar00rootroot00000000000000ulfius-2.2.4/example_callbacks/static_file/README.md000066400000000000000000000006161322747500200222320ustar00rootroot00000000000000# static file server callback function for Ulfius Framework Provides a simple static file server. `user_data` must be initialized with a `struct static_file_config` containing the following informations: - `files_path`: path to the DocumentRoot folder, can be relative or absolute - `mime_types`: a `struct _u_map` containing a set of mime-types with file extension as key and mime-type as value ulfius-2.2.4/example_callbacks/static_file/static_file_callback.c000066400000000000000000000103031322747500200252130ustar00rootroot00000000000000/** * * Static file server Ulfius callback * * Copyright 2017 Nicolas Mora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * License as published by the Free Software Foundation; * version 3 of the License. * * This program 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 Lesser General Public License * along with this program. If not, see . * */ #include #include #include #include #include "static_file_callback.h" /** * Return the filename extension */ const char * get_filename_ext(const char *path) { const char *dot = strrchr(path, '.'); if(!dot || dot == path) return "*"; if (strchr(dot, '?') != NULL) { *strchr(dot, '?') = '\0'; } return dot; } /** * Streaming callback function to ease sending large files */ static ssize_t callback_static_file_stream(void * cls, uint64_t pos, char * buf, size_t max) { if (cls != NULL) { return fread (buf, 1, max, (FILE *)cls); } else { return U_STREAM_END; } } /** * Cleanup FILE* structure when streaming is complete */ static void callback_static_file_stream_free(void * cls) { if (cls != NULL) { fclose((FILE *)cls); } } /** * static file callback endpoint */ int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data) { size_t length; FILE * f; char * file_requested, * file_path, * url_dup_save; const char * content_type; /* * Comment this if statement if you put static files url not in root, like /app */ if (response->shared_data != NULL) { return U_CALLBACK_CONTINUE; } if (user_data != NULL && ((struct _static_file_config *)user_data)->files_path != NULL) { file_requested = o_strdup(request->http_url); url_dup_save = file_requested; while (file_requested[0] == '/') { file_requested++; } file_requested += o_strlen(((struct _static_file_config *)user_data)->url_prefix); while (file_requested[0] == '/') { file_requested++; } if (strchr(file_requested, '#') != NULL) { *strchr(file_requested, '#') = '\0'; } if (strchr(file_requested, '?') != NULL) { *strchr(file_requested, '?') = '\0'; } if (file_requested == NULL || o_strlen(file_requested) == 0 || 0 == o_strcmp("/", file_requested)) { o_free(url_dup_save); url_dup_save = file_requested = o_strdup("index.html"); } file_path = msprintf("%s/%s", ((struct _static_file_config *)user_data)->files_path, file_requested); if (access(file_path, F_OK) != -1) { f = fopen (file_path, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); content_type = u_map_get_case(((struct _static_file_config *)user_data)->mime_types, get_filename_ext(file_requested)); if (content_type == NULL) { content_type = u_map_get(((struct _static_file_config *)user_data)->mime_types, "*"); y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested)); } u_map_put(response->map_header, "Content-Type", content_type); u_map_put(response->map_header, "Cache-Control", "public, max-age=31536000"); if (ulfius_set_stream_response(response, 200, callback_static_file_stream, callback_static_file_stream_free, length, STATIC_FILE_CHUNK, f) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_file - Error ulfius_set_stream_response"); } } } else { ulfius_set_string_body_response(response, 404, "File not found"); } o_free(file_path); o_free(url_dup_save); return U_CALLBACK_CONTINUE; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error, user_data is NULL or inconsistent"); return U_CALLBACK_ERROR; } } ulfius-2.2.4/example_callbacks/static_file/static_file_callback.h000066400000000000000000000025061322747500200252260ustar00rootroot00000000000000/** * struct static_file_config must be initialized with proper values * files_path: path (relative or absolute) to the DocumentRoot folder * url_prefix: prefix used to access the callback function * mime_types: a struct _u_map filled with all the mime-types needed for a static file server * * example of mime-types used in Hutch: * { * key = ".html" * value = "text/html" * }, * { * key = ".css" * value = "text/css" * }, * { * key = ".js" * value = "application/javascript" * }, * { * key = ".png" * value = "image/png" * }, * { * key = ".jpg" * value = "image/jpeg" * }, * { * key = ".jpeg" * value = "image/jpeg" * }, * { * key = ".ttf" * value = "font/ttf" * }, * { * key = ".woff" * value = "font/woff" * }, * { * key = ".woff2" * value = "font/woff2" * }, * { * key = ".map" * value = "application/octet-stream" * }, * { * key = "*" * value = "application/octet-stream" * } * */ #ifndef _STATIC_FILE #define _STATIC_FILE #define STATIC_FILE_CHUNK 256 struct _static_file_config { char * files_path; char * url_prefix; struct _u_map * mime_types; }; int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data); const char * get_filename_ext(const char *path); #endifulfius-2.2.4/example_programs/000077500000000000000000000000001322747500200163755ustar00rootroot00000000000000ulfius-2.2.4/example_programs/Makefile000066400000000000000000000035601322747500200200410ustar00rootroot00000000000000# # Example program # # Makefile used to build all programs # # Copyright 2014-2015 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # ULFIUS_LOCATION=../src SIMPLE_EXAMPLE_LOCATION=./simple_example SHEEP_COUNTER_LOCATION=./sheep_counter PROXY_EXAMPLE_LOCATION=./proxy_example REQUEST_EXAMPLE_LOCATION=./request_example TEST_U_MAP_LOCATION=./test_u_map INJECTION_EXAMPLE_LOCATION=./injection_example AUTH_EXAMPLE_LOCATION=./auth_example STREAM_EXAMPLE_LOCATION=./stream_example MULTIPLE_CALLBACKS_LOCATION=./multiple_callbacks_example WEBSOCKET_EXAMPLE_LOCATION=./websocket_example all: debug debug: cd $(ULFIUS_LOCATION) && $(MAKE) debug cd $(SIMPLE_EXAMPLE_LOCATION) && $(MAKE) debug cd $(SHEEP_COUNTER_LOCATION) && $(MAKE) debug cd $(PROXY_EXAMPLE_LOCATION) && $(MAKE) debug cd $(REQUEST_EXAMPLE_LOCATION) && $(MAKE) debug cd $(INJECTION_EXAMPLE_LOCATION) && $(MAKE) debug cd $(AUTH_EXAMPLE_LOCATION) && $(MAKE) debug cd $(STREAM_EXAMPLE_LOCATION) && $(MAKE) debug cd $(TEST_U_MAP_LOCATION) && $(MAKE) debug cd $(MULTIPLE_CALLBACKS_LOCATION) && $(MAKE) debug cd $(WEBSOCKET_EXAMPLE_LOCATION) && $(MAKE) debug clean: cd $(SIMPLE_EXAMPLE_LOCATION) && $(MAKE) clean cd $(SHEEP_COUNTER_LOCATION) && $(MAKE) clean cd $(PROXY_EXAMPLE_LOCATION) && $(MAKE) clean cd $(REQUEST_EXAMPLE_LOCATION) && $(MAKE) clean cd $(INJECTION_EXAMPLE_LOCATION) && $(MAKE) clean cd $(AUTH_EXAMPLE_LOCATION) && $(MAKE) clean cd $(STREAM_EXAMPLE_LOCATION) && $(MAKE) clean cd $(TEST_U_MAP_LOCATION) && $(MAKE) clean cd $(MULTIPLE_CALLBACKS_LOCATION) && $(MAKE) clean cd $(WEBSOCKET_EXAMPLE_LOCATION) && $(MAKE) clean ulfius-2.2.4/example_programs/README.md000066400000000000000000000024421322747500200176560ustar00rootroot00000000000000# Ulfius Framework example programs Those programs require ulfius to be installed with its dependencies. To do so, in the root folder, run `make && sudo make install` to compile and install the library. Refer to the `README.md` for more information. Each program implement some functionalities provided by Ulfius, such as url parsing, request filling and response manipulation. For each example program, you can look at its `README.md`. In general, a simple `make ` or `make test` is sufficient to run the example program. ## Example programs available The example programs were developped to help implementing the functionnalities of the framework and test them. The tested functionnalities are: - `simple_example`: endpoint tests, url parameters, body parameters, json body parameters and cookies - `sheep_counter`: file server, upload file - `injection_example`: endpoints injection and remove during the program execution - `proxy_example`: proxyfies calls to anither url - `auth_example`: HTTP Basic Auth - `stream_example`: data streaming (server and client side) - `request_example`: send http and smtp requests - `test_u_map`: struct _u_map tests - `multiple_callbacks_example`: Test new feature in Ulfius 2.0 where you can access multiple callback functions on a single endpoint, one after the other ulfius-2.2.4/example_programs/auth_example/000077500000000000000000000000001322747500200210515ustar00rootroot00000000000000ulfius-2.2.4/example_programs/auth_example/Makefile000066400000000000000000000025041322747500200225120ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-lc -lulfius -lyder -lorcania -L$(ULFIUS_LOCATION) all: auth_client auth_server clean: rm -f *.o auth_client auth_server debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: auth_client auth_server libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET auth_client.o: auth_client.c libulfius.so $(CC) $(CFLAGS) auth_client.c -DDEBUG -g -O0 auth_client: auth_client.o $(CC) -o auth_client auth_client.o $(LIBS) test_auth_client: auth_client LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./auth_client auth_server.o: auth_server.c libulfius.so $(CC) $(CFLAGS) auth_server.c -DDEBUG -g -O0 auth_server: auth_server.o $(CC) -o auth_server auth_server.o $(LIBS) test_auth_server: auth_server LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./auth_server ulfius-2.2.4/example_programs/auth_example/README.md000066400000000000000000000007651322747500200223400ustar00rootroot00000000000000# Authentication example Provides an application that implement basic authentication ## Compile and run ```bash $ make $ ./auth_server ``` ## Usage Open in your browser the url `http://localhost:2884/auth/basic`, then on the authentication prompt, enter the `USER` and `PASSWORD` specified in `auth_server.c` (default test/testpassword) to be authenticated and allowed to access the endpoint. In another console, you can run the client program to test the use cases ```bash $ ./auth_client ``` ulfius-2.2.4/example_programs/auth_example/auth_client.c000066400000000000000000000126601322747500200235210ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #define U_DISABLE_JANSSON #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define SERVER_URL "http://localhost:2884/auth/basic" #define SERVER_URL_DEFAULT "http://localhost:2884/auth/default" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { len = snprintf(NULL, 0, "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } void print_response(struct _u_response * response) { if (response != NULL) { char response_body[response->binary_body_length + 1]; strncpy(response_body, response->binary_body, response->binary_body_length); response_body[response->binary_body_length] = '\0'; printf("status is\n%ld\n\nstring body is \n%s\n\n", response->status, response_body); } } int main (int argc, char **argv) { struct _u_response response; int res; struct _u_request req_list[6]; y_init_logs("auth_client", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "logs start"); ulfius_init_request(&req_list[0]); req_list[0].http_verb = o_strdup("GET"); req_list[0].http_url = o_strdup(SERVER_URL); ulfius_init_request(&req_list[1]); req_list[1].http_verb = o_strdup("GET"); req_list[1].http_url = o_strdup(SERVER_URL); req_list[1].auth_basic_user = o_strdup("test"); req_list[1].auth_basic_password = o_strdup("testpassword"); ulfius_init_request(&req_list[2]); req_list[2].http_verb = o_strdup("GET"); req_list[2].http_url = o_strdup(SERVER_URL); req_list[2].auth_basic_user = o_strdup("test"); req_list[2].auth_basic_password = o_strdup("wrongpassword"); ulfius_init_request(&req_list[3]); req_list[3].http_verb = o_strdup("GET"); req_list[3].http_url = o_strdup(SERVER_URL "/404"); ulfius_init_request(&req_list[4]); req_list[4].http_verb = o_strdup("GET"); req_list[4].http_url = o_strdup(SERVER_URL_DEFAULT); req_list[4].auth_basic_user = o_strdup("test"); req_list[4].auth_basic_password = o_strdup("testpassword"); ulfius_init_request(&req_list[5]); req_list[5].http_verb = o_strdup("GET"); req_list[5].http_url = o_strdup(SERVER_URL_DEFAULT); req_list[5].auth_basic_user = o_strdup("test"); req_list[5].auth_basic_password = o_strdup("wrongpassword"); printf("Press to run auth tests no authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[0], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run auth tests success authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[1], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run auth tests error authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[2], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run auth tests 404\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[3], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run default auth tests success authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[4], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run default auth tests error authentication\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[5], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); // Wait for the user to press on the console to quit the application printf("Press to quit test\n"); getchar(); ulfius_clean_request(&req_list[0]); ulfius_clean_request(&req_list[1]); ulfius_clean_request(&req_list[2]); ulfius_clean_request(&req_list[3]); ulfius_clean_request(&req_list[4]); ulfius_clean_request(&req_list[5]); y_close_logs(); return 0; } ulfius-2.2.4/example_programs/auth_example/auth_server.c000066400000000000000000000052411322747500200235460ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This program implements basic auth example * * Copyright 2016-2017 Nicolas Mora * * License MIT * */ #include #include #include #define U_DISABLE_JANSSON #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define PORT 2884 #define PREFIX "/auth" #define USER "test" #define PASSWORD "testpassword" /** * Auth function for basic authentication */ int auth_basic (const struct _u_request * request, struct _u_response * response, void * user_data) { y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth user: %s", request->auth_basic_user); y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth password: %s", request->auth_basic_password); y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth param: %s", (char *)user_data); if (request->auth_basic_user != NULL && request->auth_basic_password != NULL && 0 == o_strcmp(request->auth_basic_user, USER) && 0 == o_strcmp(request->auth_basic_password, PASSWORD)) { return U_CALLBACK_CONTINUE; } else { ulfius_set_string_body_response(response, 401, "Error authentication"); return U_CALLBACK_UNAUTHORIZED; } } /** * Callback function for basic authentication */ int callback_auth_basic (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Basic auth callback"); return U_CALLBACK_CONTINUE; } int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; y_init_logs("auth_server", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "logs start"); if (ulfius_init_instance(&instance, PORT, NULL, "auth_basic_default") != U_OK) { printf("Error ulfius_init_instance, abort\n"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/basic", 0, &auth_basic, "auth param"); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/basic", 1, &callback_auth_basic, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/default", 1, &callback_auth_basic, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/default", 0, &auth_basic, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application printf("Press to quit server\n"); getchar(); } else { printf("Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); y_close_logs(); return 0; } ulfius-2.2.4/example_programs/injection_example/000077500000000000000000000000001322747500200220725ustar00rootroot00000000000000ulfius-2.2.4/example_programs/injection_example/Makefile000066400000000000000000000021731322747500200235350ustar00rootroot00000000000000# # injection_example program # # Makefile used to build the software # # Copyright 2016-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-lc -lulfius -lyder -ljansson -L$(ULFIUS_LOCATION) all: injection_example clean: rm -f *.o injection_example debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: injection_example libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET injection_example.o: injection_example.c $(CC) $(CFLAGS) injection_example.c -DDEBUG -g -O0 injection_example: libulfius.so injection_example.o $(CC) -o injection_example injection_example.o $(LIBS) test: injection_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./injection_example ulfius-2.2.4/example_programs/injection_example/README.md000066400000000000000000000014551322747500200233560ustar00rootroot00000000000000# Injection example Provides an example program that will add and remove endpoints during the execution ## Compile and run ```bash $ make test ``` ## Usage Right after starting the program, the endpoints available are the following: - `GET http://localhost:4528/inject/first` - `GET http://localhost:4528/inject/second` - `GET http://localhost:4528/inject/third` On the program console, press `` one time will add this new endpoint: - `GET http://localhost:4528/inject/fourth` Press `` again to add the following endpoint: - `GET http://localhost:4528/inject/fifth` Press `` to remove the following endpoint: - `GET http://localhost:4528/inject/fourth` Then press `` to quit the application You can test the presence or absence of the specified endpoints between each step. ulfius-2.2.4/example_programs/injection_example/injection_example.c000066400000000000000000000101761322747500200257400ustar00rootroot00000000000000/** * * Ulfius Framework injection_example program * * This example program describes the endpoints injections * * Copyright 2016-2017 Nicolas Mora * * License MIT * */ #include #include #include #include #include #define U_DISABLE_JANSSON #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define PORT 4528 #define PREFIX "/inject" /** * callback functions declaration */ int callback_first (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_second (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_third (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_fourth (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_fifth (const struct _u_request * request, struct _u_response * response, void * user_data); int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; y_init_logs("injection_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting injection_example"); if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/first", 1, &callback_first, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/second", 1, &callback_second, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/third", 1, &callback_third, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Start framework on port %d", instance.port); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to inject %s/fourth endpoint", PREFIX); getchar(); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/fourth", 1, &callback_fourth, NULL); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to inject %s/fifth endpoint", PREFIX); getchar(); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/fifth", 1, &callback_fifth, NULL); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to remove %s/fourth endpoint", PREFIX); getchar(); ulfius_remove_endpoint_by_val(&instance, "GET", PREFIX, "/fourth"); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to quit the application"); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); y_close_logs(); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback callback_first */ int callback_first (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_first!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_second */ int callback_second (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_second!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_third */ int callback_third (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_third!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_fourth */ int callback_fourth (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_fourth!"); return U_CALLBACK_CONTINUE; } /** * Callback callback_fifth */ int callback_fifth (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World from callback_fifth!"); return U_CALLBACK_CONTINUE; } ulfius-2.2.4/example_programs/multiple_callbacks_example/000077500000000000000000000000001322747500200237425ustar00rootroot00000000000000ulfius-2.2.4/example_programs/multiple_callbacks_example/Makefile000066400000000000000000000024661322747500200254120ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # LIBYDER_LOCATION=../../lib/yder/src CC=gcc CFLAGS=-c -Wall -I$(LIBYDER_LOCATION) -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-L$(LIBYDER_LOCATION) -lc -lulfius -lyder -lorcania -L$(ULFIUS_LOCATION) all: multiple_callbacks_example clean: rm -f *.o multiple_callbacks_example valgrind.txt debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: multiple_callbacks_example libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET multiple_callbacks_example.o: multiple_callbacks_example.c $(CC) $(CFLAGS) multiple_callbacks_example.c -DDEBUG -g -O0 multiple_callbacks_example: libulfius.so multiple_callbacks_example.o $(CC) -o multiple_callbacks_example multiple_callbacks_example.o $(LIBS) test: multiple_callbacks_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./multiple_callbacks_example ulfius-2.2.4/example_programs/multiple_callbacks_example/README.md000066400000000000000000000024421322747500200252230ustar00rootroot00000000000000# multiple_callbacks_example Run a webservice with multiple endpoint callbacks possible and different levels of priority ## Compile and run ```bash $ make test ``` ## Endpoints available: ### Multiple callbacks in cascade - `GET http://localhost:6875/multiple/zero`: Send "Level zero" response (status 200) - `GET http://localhost:6875/multiple/zero/one`: Send "Level zero\nlevel one" response (status 200) - `GET http://localhost:6875/multiple/zero/one/two`: Send "Level zero\nlevel one\nlevel two" response (status 200) - `GET http://localhost:6875/multiple/zero/one/two/three`: Send "Level zero\nlevel one\nlevel two\nlevel three" response (status 200) ### Multiple callbacks but the cascade is stopped at `/zero/onec/` - `GET http://localhost:6875/multiple/zero/one`: Send "Level zero\nlevel one" response (status 200) - `GET http://localhost:6875/multiple/zero/one/two`: Send "Level zero\nlevel one" response (status 200) ### Hello World with authentication - `GET http://localhost:6875/multiple/auth/data`: Send "Hello World!" response if authentication is correct (status 200), otherwise status 401 with realm "default_realm" - `PUT http://localhost:6875/multiple/auth/data`: Send "Hello World!" response if authentication is correct (status 200), otherwise status 401 with realm "specific_realm" ulfius-2.2.4/example_programs/multiple_callbacks_example/multiple_callbacks_example.c000066400000000000000000000224331322747500200314570ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the multiple callback feature * that are available in ulfius 2.0 * * Copyright 2017 Nicolas Mora * * License MIT * */ #include #include #include #include #include #define U_DISABLE_JANSSON #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define PORT 6875 #define PREFIX "/multiple" #define USER "test" #define PASSWORD "testpassword" #define DEFAULT_REALM "default_realm" #define SPECIFIC_REALM "specific_realm" /** * callback functions declaration */ int callback_multiple_level_zero (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_one (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_two (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_three (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_one_complete (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_final (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_auth_check (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_multiple_level_auth_data (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data); /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, value is %s", keys[i], value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s", keys[i], value); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } int main (int argc, char **argv) { int ret; // Set the framework port number struct _u_instance instance; y_init_logs("multiple_callbacks_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting multiple_callbacks_example"); if (ulfius_init_instance(&instance, PORT, NULL, DEFAULT_REALM) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Maximum body size sent by the client is 1 Kb instance.max_post_body_size = 1024; // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "*", PREFIX, "/zero/*", 99, &callback_multiple_level_final, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/*", 0, &callback_multiple_level_zero, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/one/*", 1, &callback_multiple_level_one, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/one/two/*", 2, &callback_multiple_level_two, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/one/two/three/", 3, &callback_multiple_level_three, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/onec/*", 1, &callback_multiple_level_one_complete, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/zero/onec/two/*", 2, &callback_multiple_level_two, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/auth/*", 1, &callback_multiple_level_auth_check, NULL); ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, "/auth/*", 1, &callback_multiple_level_auth_check, NULL); ulfius_add_endpoint_by_val(&instance, "*", PREFIX, "/auth/data/", 2, &callback_multiple_level_auth_data, NULL); // default_endpoint declaration ulfius_set_default_endpoint(&instance, &callback_default, NULL); // Start the framework ret = ulfius_start_framework(&instance); if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Start framework on port %d", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); y_close_logs(); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function of level zero */ int callback_multiple_level_zero (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Level zero"); response->shared_data = o_strdup("shared from level zero"); return U_CALLBACK_CONTINUE; } /** * Callback function of level one */ int callback_multiple_level_one (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level one"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); return U_CALLBACK_CONTINUE; } /** * Callback function of level one with complete return */ int callback_multiple_level_one_complete (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level one"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); o_free(response->shared_data); return U_CALLBACK_COMPLETE; } /** * Callback function of level two */ int callback_multiple_level_two (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level two"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); return U_CALLBACK_CONTINUE; } /** * Callback function of level three */ int callback_multiple_level_three (const struct _u_request * request, struct _u_response * response, void * user_data) { char old_body[response->binary_body_length + 1], * new_body; memcpy(old_body, response->binary_body, response->binary_body_length); old_body[response->binary_body_length] = '\0'; new_body = msprintf("%s\n%s", old_body, "Level three"); ulfius_set_string_body_response(response, 200, new_body); o_free(new_body); y_log_message(Y_LOG_LEVEL_DEBUG, "shared_data is %s", response->shared_data); return U_CALLBACK_CONTINUE; } /** * Final callback function called on multiple calls */ int callback_multiple_level_final (const struct _u_request * request, struct _u_response * response, void * user_data) { o_free(response->shared_data); return U_CALLBACK_COMPLETE; } /** * Check authentication */ int callback_multiple_level_auth_check (const struct _u_request * request, struct _u_response * response, void * user_data) { y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth user: %s", request->auth_basic_user); y_log_message(Y_LOG_LEVEL_DEBUG, "basic auth password: %s", request->auth_basic_password); if (request->auth_basic_user != NULL && request->auth_basic_password != NULL && 0 == o_strcmp(request->auth_basic_user, USER) && 0 == o_strcmp(request->auth_basic_password, PASSWORD)) { return U_CALLBACK_CONTINUE; } else { if (0 == o_strcmp("PUT", request->http_verb)) { response->auth_realm = o_strdup(SPECIFIC_REALM); } ulfius_set_string_body_response(response, 401, "Error authentication"); return U_CALLBACK_UNAUTHORIZED; } } /** * Send Hello World! */ int callback_multiple_level_auth_data (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World!"); return U_CALLBACK_CONTINUE; } /** * Default callback function called if no endpoint has a match */ int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 404, "Page not found, do what you want"); return U_CALLBACK_CONTINUE; } ulfius-2.2.4/example_programs/proxy_example/000077500000000000000000000000001322747500200212715ustar00rootroot00000000000000ulfius-2.2.4/example_programs/proxy_example/Makefile000066400000000000000000000017211322747500200227320ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-lc -lorcania -lulfius -ljansson -lyder -L$(ULFIUS_LOCATION) all: proxy clean: rm -f *.o proxy debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: proxy libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET proxy.o: proxy.c $(CC) $(CFLAGS) proxy.c -DDEBUG -g -O0 proxy: libulfius.so proxy.o $(CC) -o proxy proxy.o $(LIBS) test: proxy LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./proxy ulfius-2.2.4/example_programs/proxy_example/README.md000066400000000000000000000004451322747500200225530ustar00rootroot00000000000000# Proxy example Run a simple proxy application that will redirect all calls to `PROXY_DEST` address ## Compile and run ```bash $ make test ``` ## Usage: Open in your browser the url [http://localhost:7799/](http://localhost:7799/) to access the url specified by `PROXY_DEST` in `proxy.c` ulfius-2.2.4/example_programs/proxy_example/proxy.c000066400000000000000000000043671322747500200226300ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the main features * that are available in a callback function * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #define U_DISABLE_WEBSOCKET #define U_DISABLE_JANSSON #include "../../src/ulfius.h" #define PORT 7799 #define PROXY_DEST "https://www.wikipedia.org" /** * callback function declaration */ int callback_get (const struct _u_request * request, struct _u_response * response, void * user_data); int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", NULL, "*", 0, &callback_get, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %d\n", instance.port); printf("Go to url / to proxify %s\n", PROXY_DEST); // Wait for the user to press on the console to quit the application getchar(); } else { printf("Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function that put a "Hello World!" string in the response */ int callback_get (const struct _u_request * request, struct _u_response * response, void * user_data) { struct _u_request * req = ulfius_duplicate_request(request); struct _u_response * res = o_malloc(sizeof(struct _u_response)); ulfius_init_response(res); int len; o_free(req->http_url); u_map_remove_from_key(req->map_header, "Host"); len = snprintf(NULL, 0, "%s%s", PROXY_DEST, request->http_url); req->http_url = o_malloc((len+1)*sizeof(char)); snprintf(req->http_url, (len+1), "%s%s", PROXY_DEST, request->http_url); printf("Accessing %s as proxy\n", req->http_url); req->timeout = 30; ulfius_send_http_request(req, res); ulfius_copy_response(response, res); ulfius_clean_response_full(res); ulfius_clean_request_full(req); return U_CALLBACK_CONTINUE; } ulfius-2.2.4/example_programs/request_example/000077500000000000000000000000001322747500200216005ustar00rootroot00000000000000ulfius-2.2.4/example_programs/request_example/Makefile000066400000000000000000000022261322747500200232420ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) -I$(ULFIUS_LOCATION) ULFIUS_LOCATION=../../src LIBS=-lc -ljansson -lyder -lorcania -lulfius -L$(ULFIUS_LOCATION) all: client server mail clean: rm -f *.o client server mail debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: client server mail libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET client.o: client.c libulfius.so $(CC) $(CFLAGS) client.c -DDEBUG -g -O0 client: client.o $(CC) -o client client.o $(LIBS) server.o: server.c libulfius.so $(CC) $(CFLAGS) server.c -DDEBUG -g -O0 server: server.o $(CC) -o server server.o $(LIBS) mail.o: mail.c libulfius.so $(CC) $(CFLAGS) mail.c -DDEBUG -g -O0 mail: mail.o $(CC) -o mail mail.o $(LIBS) ulfius-2.2.4/example_programs/request_example/README.md000066400000000000000000000007411322747500200230610ustar00rootroot00000000000000# HTTP Request send examples Provides some basic http request send examples. ## Compile and run ```bash $ make $ ./server ``` On another console, run the following command ```bash $ ./client ``` Then follow the instructions and observe the results. ## Mail send example Modify the file `mail.c` values for `MAIL_SERVER`, `MAIL_SENDER` and `MAIL_RECIPIENT` with your own, then compile and run: ```bash $ ./mail ``` An email should be sent to the `MAIL_RECIPIENT` address. ulfius-2.2.4/example_programs/request_example/client.c000066400000000000000000000203701322747500200232240ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define SERVER_URL_PREFIX "http://localhost:7778/curl" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { len = snprintf(NULL, 0, "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } void print_response(struct _u_response * response) { if (response != NULL) { char * headers = print_map(response->map_header); char response_body[response->binary_body_length + 1]; strncpy(response_body, response->binary_body, response->binary_body_length); response_body[response->binary_body_length] = '\0'; printf("protocol is\n%s\n\n headers are \n%s\n\n body is \n%s\n\n", response->protocol, headers, response_body); o_free(headers); } } int main (int argc, char **argv) { struct _u_map headers, url_params, post_params, req_headers; char * string_body = "param1=one¶m2=two"; json_t * json_body = json_object(); struct _u_response response; int res; u_map_init(&headers); u_map_init(&url_params); u_map_put(&url_params, "test", "one"); u_map_put(&url_params, "other_test", "two"); u_map_init(&post_params); u_map_put(&post_params, "third_test", "three"); u_map_put(&post_params, "fourth_test", "four"); u_map_put(&post_params, "extreme_test", "Here ! are %9_ some $ ö\\)]= special châraçters"); u_map_init(&req_headers); u_map_put(&req_headers, "Content-Type", MHD_HTTP_POST_ENCODING_FORM_URLENCODED); json_object_set_new(json_body, "param1", json_string("one")); json_object_set_new(json_body, "param2", json_string("two")); struct _u_request req_list[8]; ulfius_init_request(&req_list[0]); ulfius_init_request(&req_list[1]); ulfius_init_request(&req_list[2]); ulfius_init_request(&req_list[3]); ulfius_init_request(&req_list[4]); ulfius_init_request(&req_list[5]); ulfius_init_request(&req_list[6]); ulfius_init_request(&req_list[7]); // Parameters in url req_list[0].http_verb = strdup("GET"); req_list[0].http_url = strdup(SERVER_URL_PREFIX "/get/"); req_list[0].timeout = 20; u_map_copy_into(req_list[0].map_url, &url_params); // No parameters req_list[1].http_verb = strdup("DELETE"); req_list[1].http_url = strdup(SERVER_URL_PREFIX "/delete/"); req_list[1].timeout = 20; // Parameters in post_map and string_body req_list[2].http_verb = strdup("POST"); req_list[2].http_url = strdup(SERVER_URL_PREFIX "/post/param/"); req_list[2].timeout = 20; u_map_copy_into(req_list[2].map_post_body, &post_params); req_list[2].binary_body = strdup(string_body); req_list[2].binary_body_length = strlen(string_body); // Paremeters in string body, header MHD_HTTP_POST_ENCODING_FORM_URLENCODED req_list[3].http_verb = strdup("POST"); req_list[3].http_url = strdup(SERVER_URL_PREFIX "/post/plain/"); req_list[3].timeout = 20; u_map_copy_into(req_list[3].map_header, &req_headers); req_list[3].binary_body = strdup(string_body); req_list[3].binary_body_length = strlen(string_body); // Parameters in json_body req_list[4].http_verb = strdup("POST"); req_list[4].http_url = strdup(SERVER_URL_PREFIX "/post/json/"); req_list[4].timeout = 20; u_map_copy_into(req_list[4].map_url, &url_params); ulfius_set_json_body_request(&req_list[4], json_body); // Paremeters in string body, header MHD_HTTP_POST_ENCODING_FORM_URLENCODED req_list[5].http_verb = strdup("PUT"); req_list[5].http_url = strdup(SERVER_URL_PREFIX "/put/plain/"); req_list[5].timeout = 20; u_map_copy_into(req_list[5].map_header, &req_headers); req_list[5].binary_body = strdup(string_body); req_list[5].binary_body_length = strlen(string_body); // Parameters in json_body req_list[6].http_verb = strdup("PUT"); req_list[6].http_url = strdup(SERVER_URL_PREFIX "/put/json/"); req_list[6].timeout = 20; u_map_copy_into(req_list[6].map_url, &url_params); ulfius_set_json_body_request(&req_list[6], json_body); // Parameters in post_map req_list[7].http_verb = strdup("POST"); req_list[7].http_url = strdup(SERVER_URL_PREFIX "/post/param/"); req_list[7].timeout = 20; u_map_copy_into(req_list[6].map_post_body, &post_params); printf("Press to run get test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[0], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run get test with no interest on the response\n"); getchar(); printf("Request sent, result is %d\n", ulfius_send_http_request(&req_list[0], NULL)); printf("Press to run delete test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[1], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post parameters test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[2], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post plain test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[3], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post json test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[4], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run put plain test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[5], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run put json test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[6], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); printf("Press to run post only test\n"); getchar(); ulfius_init_response(&response); res = ulfius_send_http_request(&req_list[7], &response); if (res == U_OK) { print_response(&response); } else { printf("Error in http request: %d\n", res); } ulfius_clean_response(&response); // Wait for the user to press on the console to quit the application printf("Press to quit test\n"); getchar(); json_decref(json_body); u_map_clean(&headers); u_map_clean(&url_params); u_map_clean(&post_params); u_map_clean(&req_headers); ulfius_clean_request(&req_list[0]); ulfius_clean_request(&req_list[1]); ulfius_clean_request(&req_list[2]); ulfius_clean_request(&req_list[3]); ulfius_clean_request(&req_list[4]); ulfius_clean_request(&req_list[5]); ulfius_clean_request(&req_list[6]); ulfius_clean_request(&req_list[7]); return 0; } ulfius-2.2.4/example_programs/request_example/mail.c000066400000000000000000000011641322747500200226700ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define MAIL_SERVER "localhost" #define MAIL_SENDER "test@example.org" #define MAIL_RECIPIENT "test@example.com" int main(int argc, char ** argv) { printf("Send mail: %d\n", ulfius_send_smtp_email(MAIL_SERVER, 0, 0, 0, NULL, NULL, MAIL_SENDER, MAIL_RECIPIENT, NULL, NULL, "test", "This is a test\nHello!!!")); return 0; } ulfius-2.2.4/example_programs/request_example/server.c000066400000000000000000000072171322747500200232610ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program send several requests to describe * the function ulfius_request_http behaviour * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define PORT 7778 #define PREFIX "/curl" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { len = snprintf(NULL, 0, "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s\n", keys[i], u_map_get(map, keys[i])); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } /** * Callback function that put "Hello World!" and all the data sent by the client in the response as string (http method, url, params, cookies, headers, post, json, and user specific data in the response */ int callback (const struct _u_request * request, struct _u_response * response, void * user_data) { char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie), * post_params = print_map(request->map_post_body); char request_body[request->binary_body_length + 1]; strncpy(request_body, request->binary_body, request->binary_body_length); request_body[request->binary_body_length] = '\0'; response->binary_body = o_strdup("ok"); response->binary_body_length = strlen("ok"); response->status = 200; printf("######################################################\n###################### Callback ######################\n######################################################\n\nMethod is %s\n url is %s\n\n parameters from the url are \n%s\n\n cookies are \n%s\n\n headers are \n%s\n\n post parameters are \n%s\n\n raw body is \n%s\n\n user data is %s\n\n", request->http_verb, request->http_url, url_params, cookies, headers, post_params, request_body, (char *)user_data); ulfius_add_cookie_to_response(response, "cook", "ie", NULL, 5000, NULL, NULL, 0, 0); u_map_put(response->map_header, "multiple-key", "value 1"); u_map_put(response->map_header, "Multiple-Key", "value 2"); o_free(url_params); o_free(headers); o_free(cookies); o_free(post_params); return U_CALLBACK_CONTINUE; } int main (int argc, char **argv) { // Initialize the instance struct _u_instance instance; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "*", PREFIX, "*", 0, &callback, NULL); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application printf("Press to quit server\n"); getchar(); } else { printf("Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } ulfius-2.2.4/example_programs/sheep_counter/000077500000000000000000000000001322747500200212405ustar00rootroot00000000000000ulfius-2.2.4/example_programs/sheep_counter/Makefile000066400000000000000000000020611322747500200226770ustar00rootroot00000000000000# # Sheep counter program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-lc -lulfius -lyder -lorcania -ljansson -L$(ULFIUS_LOCATION) all: sheep_counter clean: rm -f *.o sheep_counter debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: sheep_counter libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET sheep_counter.o: sheep_counter.c $(CC) $(CFLAGS) sheep_counter.c -DDEBUG -g -O0 sheep_counter: libulfius.so sheep_counter.o $(CC) -o sheep_counter sheep_counter.o $(LIBS) test: sheep_counter LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./sheep_counter ulfius-2.2.4/example_programs/sheep_counter/README.md000066400000000000000000000024121322747500200225160ustar00rootroot00000000000000# sheep_example Run a simple file webserver and a small REST API that listen on port 7437, and implement a file upload service. ## Compile and run ```bash $ make test ``` ## Usage: ### Sheep counter Open in your browser the url [http://localhost:7437/index.html](http://localhost:7437/index.html), it's a jquery application that will count and display sheeps to help you fall asleep. There click on the buttons available to call the underline API. The API endpoints are the following: - `POST http://localhost:7437/sheep`: Initialise the counter with the specified `nbsheep` value provided in the json body (default 0) and return the new `nbsheep` value in the response as a json - `PUT http://localhost:7437/sheep`: Add a sheep to the current counter and return the new `nbsheep` value in the response as a json - `DELETE http://localhost:7437/sheep`: Reset the sheep counter to 0 and return the new `nbsheep` value in the response as a json ### File upload Open in your browser the url [http://localhost:7437/upload.html](http://localhost:7437/upload.html), there upload a file, preferably not a big one, then click on the `Upload File` button. The API endpoint is the following: - `http://localhost:7437/upload`: upload a file and show informations about it in the response. ulfius-2.2.4/example_programs/sheep_counter/sheep_counter.c000066400000000000000000000242371322747500200242570ustar00rootroot00000000000000/** * * Ulfius Framework sheep_counter program * * This program implements a small set of webservices and a tiny static files server * * As a result, the index.html page will increment a sheep counter and * shows a new sheep on the screen every time an increment is done (every 5 seconds) * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define PORT 7437 #define PREFIX "/sheep" #define FILE_PREFIX "/upload" #define STATIC_FOLDER "static" // Callback function used to serve static files that are present in the static folder int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to start a new count by setting the number of sheeps int callback_sheep_counter_start (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to reset the number of sheeps to 0 int callback_sheep_counter_reset (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to add one sheep to the counter int callback_sheep_counter_add (const struct _u_request * request, struct _u_response * response, void * user_data); // Callback function used to upload file int callback_upload_file (const struct _u_request * request, struct _u_response * response, void * user_data); // File upload callback function int file_upload_callback (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * user_data); /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } /** * return the filename extension */ const char * get_filename_ext(const char *path) { const char *dot = strrchr(path, '.'); if(!dot || dot == path) return "*"; return dot + 1; } /** * Main function */ int main (int argc, char **argv) { // jansson integer type can vary #if JSON_INTEGER_IS_LONG_LONG long long nb_sheep = 0; #else long nb_sheep = 0; #endif // Initialize the instance struct _u_instance instance; y_init_logs("sheep_counter", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting sheep_counter"); if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } // Max post param size is 16 Kb, which means an uploaded file is no more than 16 Kb instance.max_post_param_size = 16*1024; if (ulfius_set_upload_file_callback_function(&instance, &file_upload_callback, "my cls") != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_set_upload_file_callback_function"); } // MIME types that will define the static files struct _u_map mime_types; u_map_init(&mime_types); u_map_put(&mime_types, ".html", "text/html"); u_map_put(&mime_types, ".css", "text/css"); u_map_put(&mime_types, ".js", "application/javascript"); u_map_put(&mime_types, ".png", "image/png"); u_map_put(&mime_types, ".jpeg", "image/jpeg"); u_map_put(&mime_types, ".jpg", "image/jpeg"); u_map_put(&mime_types, "*", "application/octet-stream"); // Endpoint list declaration // The first 3 are webservices with a specific url // The last endpoint will be called for every GET call and will serve the static files ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, NULL, 1, &callback_sheep_counter_start, &nb_sheep); ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, NULL, 1, &callback_sheep_counter_add, &nb_sheep); ulfius_add_endpoint_by_val(&instance, "DELETE", PREFIX, NULL, 1, &callback_sheep_counter_reset, &nb_sheep); ulfius_add_endpoint_by_val(&instance, "*", FILE_PREFIX, NULL, 1, &callback_upload_file, NULL); ulfius_add_endpoint_by_val(&instance, "GET", "*", NULL, 1, &callback_static_file, &mime_types); // Start the framework if (ulfius_start_framework(&instance) == U_OK) { printf("Start sheep counter on port %d\n", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { printf("Error starting framework\n"); } // Clean the mime map u_map_clean(&mime_types); printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); y_close_logs(); return 0; } /** * Start a new counter by setting the number of sheeps to a specific value * return the current number of sheeps in a json object */ int callback_sheep_counter_start (const struct _u_request * request, struct _u_response * response, void * user_data) { json_t * json_nb_sheep = ulfius_get_json_body_request(request, NULL), * json_body = NULL; #if JSON_INTEGER_IS_LONG_LONG long long * nb_sheep = user_data; #else long * nb_sheep = user_data; #endif if (json_nb_sheep != NULL) { * nb_sheep = json_integer_value(json_nb_sheep); } else { * nb_sheep = 0; } json_body = json_object(); json_object_set_new(json_body, "nbsheep", json_integer(* nb_sheep)); ulfius_set_json_body_response(response, 200, json_body); json_decref(json_nb_sheep); json_decref(json_body); return U_CALLBACK_CONTINUE; } /** * Reset the number of sheeps to 0 * return the current number of sheeps in a json object */ int callback_sheep_counter_reset (const struct _u_request * request, struct _u_response * response, void * user_data) { json_t * json_body = NULL; #if JSON_INTEGER_IS_LONG_LONG long long * nb_sheep = user_data; #else long * nb_sheep = user_data; #endif * nb_sheep = 0; json_body = json_object(); json_object_set_new(json_body, "nbsheep", json_integer(0)); ulfius_set_json_body_response(response, 200, json_body); json_decref(json_body); return U_CALLBACK_CONTINUE; } /** * Adds one sheep * return the current number of sheeps in a json object */ int callback_sheep_counter_add (const struct _u_request * request, struct _u_response * response, void * user_data) { json_t * json_body = NULL; #if JSON_INTEGER_IS_LONG_LONG long long * nb_sheep = user_data; #else long * nb_sheep = user_data; #endif (*nb_sheep)++; json_body = json_object(); json_object_set_new(json_body, "nbsheep", json_integer(*nb_sheep)); ulfius_set_json_body_response(response, 200, json_body); json_decref(json_body); return U_CALLBACK_CONTINUE; } /** * serve a static file to the client as a very simple http server */ int callback_static_file (const struct _u_request * request, struct _u_response * response, void * user_data) { void * buffer = NULL; long length; FILE * f; char * file_path = msprintf("%s%s", STATIC_FOLDER, request->http_url); const char * content_type; if (access(file_path, F_OK) != -1) { f = fopen (file_path, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc(length*sizeof(void)); if (buffer) { fread (buffer, 1, length, f); } fclose (f); } if (buffer) { content_type = u_map_get((struct _u_map *)user_data, get_filename_ext(request->http_url)); response->binary_body = buffer; response->binary_body_length = length; u_map_put(response->map_header, "Content-Type", content_type); response->status = 200; } else { response->status = 404; } } else { response->status = 404; } o_free(file_path); return U_CALLBACK_CONTINUE; } /** * upload a file */ int callback_upload_file (const struct _u_request * request, struct _u_response * response, void * user_data) { char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie), * post_params = print_map(request->map_post_body); char * string_body = msprintf("Upload file\n\n method is %s\n url is %s\n\n parameters from the url are \n%s\n\n cookies are \n%s\n\n headers are \n%s\n\n post parameters are \n%s\n\n", request->http_verb, request->http_url, url_params, cookies, headers, post_params); ulfius_set_string_body_response(response, 200, string_body); o_free(url_params); o_free(headers); o_free(cookies); o_free(post_params); o_free(string_body); return U_CALLBACK_CONTINUE; } /** * File upload callback function */ int file_upload_callback (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls) { y_log_message(Y_LOG_LEVEL_DEBUG, "Got from file '%s' of the key '%s', offset %llu, size %zu, cls is '%s'", filename, key, off, size, cls); return U_OK; } ulfius-2.2.4/example_programs/sheep_counter/static/000077500000000000000000000000001322747500200225275ustar00rootroot00000000000000ulfius-2.2.4/example_programs/sheep_counter/static/README.md000066400000000000000000000000001322747500200237740ustar00rootroot00000000000000ulfius-2.2.4/example_programs/sheep_counter/static/index.html000066400000000000000000000035331322747500200245300ustar00rootroot00000000000000 Sheep counter



ulfius-2.2.4/example_programs/sheep_counter/static/sheep.png000066400000000000000000000433401322747500200243450ustar00rootroot00000000000000‰PNG  IHDR ,cAnûsBIT|dˆ pHYsYYÑe–8tEXtSoftwarewww.inkscape.org›î<XtEXtCopyrightCC0 Public Domain Dedication http://creativecommons.org/publicdomain/zero/1.0/Æã½ùEùIDATxÚí¸%U•¶‹ÐMjrnb%'ÉQE•¨$1 (ð똂‚€ Ì ƒ6IÑVe ƒ"Š? ŠŽAÉ ºf¿÷œ‚²îÚ»vå]ç¬õ<ߣܾ÷œ {{åE*ã,ó¬m°‹ÁþGkp¶Á7 ®6¸Áà6ƒ¿Ünððÿ4ˆ‡øgêçâÿ½zøYg3ü¾ëÕkÌ«¯AEeteQƒm ÞeðÙ!\gð÷t{ þÇà"ƒã Þi°Á"úúTTú!s¬c°¯Á ³ î ˆdÊâNƒï ‰iŸ¡Ö4—¾n•nešÁÖnЇG€l|ñ„ÁÏ N4xÃPËSQQiP6xóÐ?ó;ƒÚØìsÎ9g<÷ÜsO`Ê”)ñÔ©S'0ï¼óÆóÍ7ßøÿÉÏùä÷ùÛ–‰gñƒ3 ö4XP—‹ŠJ5™Ã`Cƒ\kð|]vŽ9æ˜ ÈcÁŒ]tÑx‰%–ˆ—Zj©x™e–‰—[n¹x…VˆW^yåZÀgñ™|6ßÁwñ|7×ÀµpM5Òs?6øÁúº”TTüdªÁn_ŽŽÙJq®¹æšØà -´ÐĦ‡ê$–º±üòËO\#×Ê5£Yq5ÒÝ_4xÁ]f**/ âµ_5x¤ì&ÃäY`âÅ[,^z饃&š2÷„ÖÄ=r¯Èè!ƒ/EƒÔƒ¹uù©Œ£Ìi°ãðT~°ÌFÂß‚–°ä’KNh£B6E´%î3ŽgQ’ŒþapžÁvC“WEe¤eYƒEƒD½Â› ÊŠ+®8v„“ž φgTRCús4ˆ(.­ËTeÔ´ .:G½Åøo0;¦OŸ®$SBCZ|ñÅãù矿¨ƒ›ˆÙÛ{©‰¦ÒgYÌà£-B:lL ÕrêÕŽx¦%ÈèΡV¤Ù*½‘Ñ AîQß…>Ï<óLœÖJ:íQ¶‚dDâãùkèòV U67¸$òLÄqª¤Ó=5,àÄ&‹ZºWêrW E¶7øïÈ3«'é²Ë.«x'Ó¦M+’½}M4(…QQéD¶0ø‘j;£§ñ® hEWl¦ÛA¥-YoÑš·8‰bÖÝO•¯È“ˆˆœm¢ÛC¥)YÝà²<âÁ±‰*Oí“nâÑi¼S§õìáá´Šn•º„*ô“ žÍóï,¼ðÂ#U¡˜\Â;ö ¢g¢AO&­ÊW)-$hp_žÆƒcY‰G‰(’;=Ò‰R¨©Z˜Þ˜G<Ôb)ñŒ7qøxѯ ¶Ôm¥’'tÖûržŸ‡E7Ž  {Ù>"ÿÐyC“^Ee’ÐÚó×"¢?Öe)\ÎjÖH1 à-ºÝTY&d0;+ÑIß×M¦ð©ùôñ^^·ßx ã_uE¶¨F_i¥•tc) 5³È"‹äù‡$pˆnÃñ“% ¾å$ªŸGQ‡ˆµ”£ ]n°¸nËñzóüÍ¥õН›GQ'h’Óßšt]u{Ž®Ì Úd¼h[ôÖ°º¢É:3"¨9‘²3†kUe„„ùç7EŽé4=×M¢h¬µmè·kê¶ ÙÝåh¦ØP«Ô]$1æº> QªôTæš\bR!Ñ šQéfPt Ò;=ˆf×°–rô0ÊuUähªÕêŠPÀZÌé?ôƒ¥t[÷Ch—y—íeRD¨y=Šó†¨-tÐéö[ö4xÒ^'ª‹]zµÃ${Ú`oÝæaÊQ¶û”)SÔäRôÊ$cÍ:üBÇèvËÙ|¶F¹£–3”%cLNìX¦̲½$ê¸t1+ú êÉ$D9ÑüJÝQߨBìêïQŒR‡£¨õúaÔW¥E¡…ÆÍ6g3Ó tá*Fmn™#{úVƒå”Ú‘• þYúö¨³Y1Ê•õçôí‘Nãh\˜Íý×ÈÒ­PÍŠq(á ‘ÖBBw¬¦4ÑŒ¬YÚhÐoE“ ã”´èè1D[õ”.ê胶0»’bIȦÀ`¥z•òÞÈÒ¿GÉG1ÎpLã¸ßàJÕd…hP#ŽÆÑ¨P8IéÊJ#ådºÁÿJ–¢=]x ÅËptZüÓ0mE¥€Xu‹ô@a{]p E!"gN›Þ{Ê7Ø|>ºÐ ;Ø#ú•–mä …¥ßÑh—BÑ }/ÒîŠN9Só|ŠFó„NUš‘å}¶ g%…¢8 92¦ß«t󯲛Á Rm—ÎéR(Ê—mXjÇhÜ÷F¥l mT©üÕÂR…¢zwEK‹×l8îä³X4¨âÔχöº€Šê =¥ŸÐÑ÷šÓà‡’ªÍÄŠú›šYüAWkdì„HÛ¨*!´w=vÜÈØl)×GJ·Ðñ ²3†SsñÅŸ˜ ËÁÀL5Ê`ȸMÀósÀç÷ÃÔO>‡Ï›>}ú„ST#šÝÂRAÏ^Üs\ÈgMƒÇ"atŽ6k6,KG½¥—^z‚T #¡Z¢ŽžÃµƒïâ}“bAi ÄYAT\£¾¯fKdì‘h š™Ícpc$ôqÖˆW½áWˆM„¬XGÏ Áz€ѰФ¸MǨh£–‡!SG™€þCÎõk6D Ù¨h4ŽÆå½DŠÆ„Ç&Ò÷߈SúøQ%Ÿ"ar©¶Ö(w‚¡Ýnïã;òàÞy’šnÅa™EÏÝnÔÈgQƒ»²7;uêTuJzj9˜!˜$økš0w_ '#Uâ[e•UâÕV[-^c5âµÖZ+^{íµãu×]÷%¬³Î:?¯xÅ+âÕW_ý%̘1câs ÌÄ÷Äw4eò™l*¾Kוߺb ÏòîaŽÞÈÈLÉ©~Ÿ|Òa³VÕr0ËøœÄt ‹ 6Ø Þd“M:ÃÆo´‚|Xó}òµÂÐu8×ìÀ©ƒ9ÔG²)BJhnDÄДʘ£hW˜™j¢yå=d°tŸ èRÉÐêËár|"E´ÔeL þ¶¯ÚMÀ÷„fƒ‰YÄlã™óìu-¾¼-ë𢾒Ïҋǯ1î/›ˆ &…ï†ÁôàÄÇÙpà ǞtlÀ§Å3"+Û÷Ùò{˜gE[y"kÝòœ^ß7òYhèIמÎâa±ûnH SAI§8xf[I…¦8YÇѹŒƒØ'z“8æq´*i4çÄ&2hIÀÍÞqtV³W- Š›õ¡ÇÏõRäaÜ^"¾.Ÿd; GªD» š†yæeG¿¥¥©ýus„L@K*í89øPa-!ÍI}F=lÞ—(š£Oοø/ÇÉ,s$(î*ùL3¸7{Á¼Üq*ðË3·x$Øéæ ¼“<ÓŒ05‘¢1o`vO4œ|VÊ@ÚGëA¥]sÍ5u³êÞpäiCãPaïZ÷Ç„F>3 žÇ6ø\‰„¼@Twu.÷ËYíHÌ{齎Ãà‹Cú)ƒ•B" o£ã9Ïw€ƒ¹ë‚OEy¬¿þúÎwŒdz˜[^ ù¬'õùåÓÆ1úv"r²êª«ê&]íÊ!Âüe5{Ù–_?úŽ”Á;ÊÑ×bäDT­g4Ë<\Ú‡Î(G{-Ò—tM>›d“G9ìÎI`ó pßø t³Ž6pÌÚ2Ú‰€Žjÿ!ö´pßìýN§«N,Hû¨†Øm ò ÛtƒŽOîÅ/2±FF5øÂÞîùŠ®ÈgËqI:Ä™lS½É¦ÕbÑñ4É\yC£ØpϢͻ  «Ç¡Á¼‹|HJÓÍ8ÞázÇt‰‰µ3&Zкðýˆ>QzØ8”ÕߣÈkÁ”µ¤[‹Ôª/è"ÉòÁѨ!vETÙÛ|„£FBÓóëm‘ÏòϲöckÒ ù0ÁA7œB£ˆlµ€£ÔŽÆ¢Á +´A@§ŒrÞ­-¥’¬ R!«%/èÄ6Z­>šýâQwB‡¤F³ ´TQ•„X[£R!Àžè1ƒ…›$ JUÞ£’ál[4ªù(ʘcÒaF"먤ªXº¼¯Én‡·â” êxlÝ ùwÝPв5d¶ÄÕQhSÃÞîï›êš¸‹ô GØž8©t#)ªÍÚÖShöŽåàÞ± º¤OÙž¼x’Ä’¦/ ©À·i7 Ũ‚5†ÄAíCFmŒ˜¶dFo[…€Îo«ªsÉ5i"ý QKµ ¡BñrwFj³|Ì4BùMu®°t‘8»,ùÌmpææSžóÇ絛F¡°kE¸/ò&»¢15‘Dlæø!—–רH Île[&rÚ«¯Ñ)…¢Ø_y® ˆªîÂXKYÓŽÁ™_|–KëI!(õï(͘fÖ© Y¢aç”! »š0¿ø [ñgšèÔÇ£PÔç#²uùLW6Ô±¿ÉÌï/EÉg&¢_djºrz0·´÷ŽBÑ\"—Y†»jsAö¸EÁX£RíWÕäC—ÉÅÏIçVsK¡hX®QBìÅ*­c!0ÒcªÖ†ý(ûUZX.è¥D)˜Y‡BÑ®6ä Û—rOð·‚²ñC_òY Ûz’(ëïqåö0ÞC+ÏŠî*ô-ƒ_Šz-å ª Ñí§ æ÷! Ýë˜÷Ž÷Ýeo”ºŠî#eøm\~Ù"Îi~²Ì{­Quœ,hSïðë`?…",kg+é`/ûæ ¡1A@Ð Ÿõ>tCö‹0 N([Þ‰OTùê W(ÂÃZk­eµZpÃøø¡]çãÿy¾lïùg ¹= Eø~!|?eI(M@‚ÿ÷¹åˆ‡t‡&¦}Hs%"b»Ò6êžz‚Y¡DTŸ&Ä¡–Ž’§}@À2²gu‰€öÎþ"*¬ÍÑdëã£äS<Ï¢÷ñ°(01+¯·’”ß_©Ër€¼æYÒ5'ë’ûNfÃcƒ&¨¾Éz4!Þa’1$"¦!üÍ›$:.û‹¶²¶0’O½©ðéN;ï¼s|üñÇÇW_}u|×]wųgÏŽ³òâ‹/ÆwÜqG|å•WÆÇ{l¼ýöÛ{‘‹¤Í’Ì-ÖŠ¦¶Ùf›Åþð‡ã+®¸"¾í¶ÛâçŸ>–ä¾û½öÚø´ÓN‹wß}wgýazã¨Ãº Yúý¼4¨Ëȃ€Ž‘è»Ù_´Ùw¶E¬'J±Ó?OëÙzë­ã¯|å+ñc=—•‡~8>÷Üs'6ržˆÁymDóòü<8ÌO:餸ž{î)}ßÏ<óL|饗ÆoxÜf-ÿƺÖ5éOB¶CrJŠQsÑ—ItGžÚ2qB-Ö$Ãbí2]Ý wÝu×ø¿øE\·\sÍ5ñvÛmçÜŒM–É0uÖEºh„_|ñ„&W§Üzë­ñà$"ÿº6ý5wÛ{ÄÙŸ% !Šû§,ù,d0;«ži« a·qŠJ§*7޹Á5†œÿ‚Vi{†øcfÍš7-3gΜ8©lŽÚ&4´ðÎÎ<óÌø…^hô¾õ«_M\‹íù‡¹eM'Q%®³‹5Î;ä»]‡ÚQ–€݋ê‹—d+©U†ÔTÌ7Ü‹ÿ3£É ˆGjtÆCUó±=3Nè'žx"nK0ëöÞ{oëõÔ9ÍÇF>›nºi|ûí··vßܧ>õ)ëõ°nC\;Ùhk¾éÔ¬f¡XøF)I>ÌEqÙ4M@ïðm@1™_ÍïBDÐŽªšjùdq3r­u$/ò¾l'æá‡?÷ÜsÜûøC«/ƒ´€ÐÖË|„œØUɈ½É;Çå3‹îsˆ²Ê’%#ú g,Ik·åñ¡¾£“¥ìÜäbyÙ˜lPŠ,X€öÄÏø.®…ß÷ U¥ ­ŒBzV<¥]Ëe—]&†ÃyÞØûU¢]¶ˆ)‘­®å¦›nš8mŽðÖí9J®¤,…8{ˆ{Iö{ŒŸ±çØ{ aMáHö8Ÿ“€Š‹'}*M@-ÁHòøÂ&²W«€‡RÔ@Z<,”üàq(‚_H:eÑÊF7m&ûqÇÌ}ß|óÍbh™÷R·NÖt(û m‡= ˜í˜H>šd‚ ÚÔWÓô ÉY´"‰JEËš@Hƒ ¥–ç{ÁÄ¡ Ž`éyònˤHŸõž÷¼'¸ûþÙÏ~&®[6S(ë¥ë}•$¿ºúƒÙ|@‚pmš€þ–eÿª³¿`GNÏ&SìùléÔ®b6ÔmzI×wÔQGÅ¡Ê(ªÙEž)“´¡7ß|óÎ|>yrÎ9çmŠa>IiE3è‹î/ö0Ú—OSB) :°îLÈgžaX¬Ö`Ù¢UÔqØ\HÙz'6æ *3Ž2Ô¿¬j‡-Ê©%Ù¾øEH’ UˆÄ±éªd¹K!~žÜ!Ë[ÞòñÔa-ÑI4K6¬}ö@Ò¢…½QÖ/Ëßq¯ìQÞu™‘ÍICz BMØ S¢áÀ°ÊMèËhI˜'°+7Œº›¨oü/”Ÿñï<`)+[j¦J9 F:±~ùË_Æ¡ å’Ùè“{ÁJZïÙgŸü}SÎ!!˜ôhÓRÔŽ= ¹Cø9V{ˆ½”Þ[üŒ}ÂÞbÖ15=L‚¥9Ù*ÐξE¨¡Ar†’V/5sz×»Þ÷E$mÀ'QOJ´$¼Ût’a]rê©§U…ü¥‚U†Ö r¨ldÉÚÚÏ7(4H¹@!´Z z’5 Ñ î¼óÎÞÐïÿûI¦2ÿ—X*ù~.¿üòÞÜ÷ÓO?-ºÈðÏ–?H䞆)ŸŠß :¢êž®­1aƒ„ZnqðÁÇ}“=öØ£†IÆsö÷qžÖ]ÛÕ´|îsŸ ®LƒC SPòy†d‘$ Ã¢ai|®]"²/#”DI3k¢¸´iùþ÷¿/N³-’«rÊ)§ôî¾ÿñLòcáî²Ûf,…ÂÙC·LÊLÈ6"ó4Á>Uçø¶ EZBÈÛà¤Ê^™§RÿžÐ…Þ;Ù.3,k~Y¹÷Þ{ã> m<²ï ¯k’: –‰XÕ WÌA@§A@߬2ˆ°+H§mãžY$ÙëúÀ>÷U9ä¯Ü|ÙßÛj«­z{ß_úÒ—&ݾ¡® H ¼„à2Áj*A@@@WIv~#`!Œ|&½®6Zl4%dlûlDüÙßûÄ'>ÑÛû¦B¿ˆùÙFIÖ·´¾ØÜ!(y$”cü`Ò(æ¤C\èTÑÆ>KLÿÈ#ŒÔF”L])rDÅyŸ%kæã隀¤‡Ô»«mP¢‘G@B’ä¯  [³Î¶>P¶zv ¡%löºPMÛ’gŸ}6¾è¢‹&úüÐ똰(šÉN;í4á þûßÿ^ø3‰`e‹i%g¿”÷T6퀾Öô¿ÞvÛm'œ›D 8\è™ôío»µ¨ÏMÊdï’€¤H{6[Ú¶ÚV–† z3ô¿uÖµ…,›†’2Ÿ]´@mC¨¬—üOÙ1KŸþô§ 'fý;,ø¼†Y¼¢DA‰ÊûßÿþÜAŒ$åýüç?oü™Ò¯(-;! ÝÈÉ”Ж֬·A@wgÕÌÐÉÆ É>O/’ìu½ùÍon%{·H}Ýë_ÿúø©§žòþüwÜq’¶™½÷lKÌ—"Bóü-·Ü²ÐÅo|ã>×£>:˜HXâ„Rû›.“‡“~]yzM¤Þß7‚q³/4„fÝÙë:è ƒÝ$—\rI¡Îu öÚk/ïÔ€7¾ñ“þ>kîf™Ã¾‚FF#þ2ã—ô£5öléJu|š€$ÿ§ÔB9„ 蜊ø¿C@„šYé ùIC¨×É^×;ÞñŽÆ6È<`+òó‚o7F©wt¶µnV½¦$ÆWN?ýôÒ÷€¦õä“O6ò|Ï8ã 1§««ü²„€¤@¤ÔUþžÿšÛCГ¡yÔËä•išÕÄ)•½®ý÷ß¿1¢­i•~/8ª}DÒN²Pöt£–¯ã¼j·?¬ ¡sc(5ah× IJñïu‘$ "´Aè úOèù&{5ò“FÚ„Ð~5{]»í¶[cTG—<ú"ç …y5wÙÅÅ;òBõUïa‹-¶hà©mëJ»NH"ì¤OV¨þ =5ÝŒ¬)•/ÔÎuÙè\S¤ˆÜ}÷ݵt½£`ù0YS¿Ôã?žûÙÿøÇ+ß„ˆ&U·ì³Ï>ÁtÛ$ú–ä‚@e“·]ÅàëÿqÌ‹žî› &5f ! Q*DŧÖD/›ÕA@ûØÇœß‰dÜRÄQ:•o¸á†R¥eÐD·Ål]_—Ýм’‚0ɤ™6 É}óÐSÐc}sBKÕæ¡Œ†–ÌÃ[n¹¥öÍqýõ×ײq?ùÉO:¿‡†í>SG8y³¿÷Å/~1÷>=ôÐZî°N¡/PÖ¯Õe·Âÿ I.É b““´Zþà M,z o$åA„ÒZ:Î:ë¬Ú èoû[-÷ÜsÏu~ϱÇëÕHêä〇«ÞfoÝÍî ï‡ÔîCB@RÝ]B@h$¡t@ôÃß?i"Fò€²7"%Æ…4½`÷ÝwoÄ$5/Š?üáÎïØzë­ÅLdÉŸõá Ì3?¥^Md›Kè.„ÿ’2ÞɉJ6z[~ Û °‰ˆ…€îè[)FöFBÅœ^Àõ>ôÐCµo’ªÚ>—àWÉfX»¦ŽHù)W]u•ó;Ð\lÓI}ñùϾÖçJ‚¦”mß•:„˜@ªK6ze®<ˆ2°è}+FÍ&½IµI]Bšêm**TÙK>'_ÐñÐ%„fÿµÛvßÒ¦}ÛÛÞ–{_øÂ* ¡¬;víµ×Љ~]&¸f ({0p'wÔôtMÀ(PŒú{èwÙ ½#böFºl“`³×¥^EMDÃÈ£)3 ŠèS^a¨Ôu’äE×Iݼ«¼E«R¹‡O=ØOúÓÚŸ)¹[R“¾®ÖÏêõ9ï}ï{ÅL[߆oÒb{õ«_í}L¥Àg†Y†ÿƒ éqÄñO~ò“ÆžÍÜÊN„m£5 i$Uz³7éF)c~YÆòœ/PÈÓQ³„:Ùœ²Òu×]‡*W^y¥Ø[¨ÈxbR¤!!gÆO%9ô]N÷¶K0Ò:‚¦7|“Žè2Ú…€Ž…€Þ/PÓ,Z§ ¢äš1=øàƒÁmB²‰¥ñ¿eš½I¥lŸòŒ¶…(­ÒaÑu†½äÿñ1Ášòã–‰~iˆ„Á‘ÐAÒlø¶Óº«8¡CiÇZDÀDjÊTF0Ϥ†j,ì2ã®Ù¸RkU|K4ºE0[ÉØ¶…ø»6ã%ÿä„ÎPS‘°2æ—Cz+´[öÒî.›]÷5 ïSž^÷º×MÔu-D È&–®Ñ5ŠÙçô–È—4…{î¹§óûÆ/ù»º.»z Ã'G(æ—ƒ€v…€6’æ %ѵCKD”`y¿Ë±=÷ßüÊW¾Ò5«zßR.Qb†úDÆšÒH’”®¨jWÓ/l¨E2¡›RÊš_ÀÒ¹s=hQ)Ç"ͤ¡iA!ׂ¹ÖKþ ¤ÑùÍ7ßÜú&¤¢ÞÖÔŒ–'u9’2Ã_ãw¿ûÝÖïí‹‘?¶äÆ.£^>æ—TŒ*Ý »êý“…%µbÁh(H½EÚ¬-©ÚŽ£«:¢!U¡"ø%G:¡ç6f^qúŸtÒIÖñ7\KÎW6“Mäð8ꨣ&ÌÀ6äòË/íÉ&¥¯”Ëü’rÌx—ÙM_g©LíWN%üQJnÌ:´Ò~ 6R»‹Ú¡¡ŒK)³°„ÆL/aÓM7m4LÍ5×L8•mßÏIÕDä’²¤à'›9sfcüÇ?þqÂçæ*ëp•™„d~IfmVi¨[q(ÒzÕ³ ã×iú¶-Ÿ€rOè®#E#DyÓ,HÜ£UE’4 O·Ùfçwrè4íûÈk@Â<úºœó„ý)=qÍMÃç‚Ù•&kùØæÃsÙM_§ÿ6‰Œ—…ðÜ/NÐi’2ý]tÜïÛTŒ¢‹L*wœµ´M¥Ô¢HÓ-ŠIÉþà?hu§Í ˜¶î¿S^- þÝï~wPºr^*\µ`Ò Œ6¦ŒŒ9ï¼óâ /¼pBc ³!uTT¹“T'u´þ]˜¯l²¼š¯l”“|cŽ9f"£úk_ûÚDqïùçŸ?áÏ:ì°Ã&f¹û¶'á3C˜¤"aõÕWwÔŸe7~]y|esr’OЮ®\ Ð´ ´‚l2Ôr ßÚP‘ñÊuL’@ë©+ÒUR^W“Hº=tYÝ^¦öËU†‘MIPǸ沕ï&ØÎiZÞG¥ëbì‡oS26T×›©ŽÅ•éïS¤2IHQCÞ Ý•«‹xÐŽB‰r•q>'!øìá›MB¬3xTÕùlÇ–Š2ò Ôä:‹6º­ù@RßC_\EœÔ½-o¨ PÑÙä¡L±Œ·Ì¼{›‰‰#$'s‘Ö«Yàëô ÁÜ(UµŸªÎgK+Öû"A®µÕ„µ‘â]¦!vˆÓQ›hÇ™Dþx‘>“ß!ï‚¿á™ô!GÊfŠ`.‘¶àkž²eߤôì/¤L2”PrþVÙchãUÉÇÕˆ,+gæ9¢C*ÏàB¬ßiCK`c%ŽJž$ÃÿǡͿõÝu9®“ñ4Ü3÷ޯij ‡'´hVQ?`ù)}Ãæ.©J@E§^ˆ€*Ð;}4 ¶gqD‡ØšU¡¨Kû‘Úp$íRlÐÖÐÁ‚tˆD@›¹±6GÀúÖ„ÙfV)£ ýH :…*u_E¨›J4¿Ás>έPŠT%våštQ+ú„¼ÈW©†Íæÿ©¢á讃|,5`ÏÌYä×¾fX¾ )“˜ëÕE­èSÄÓ‡|€ùåg.(£$à讋€ôÿD9Ýרx]jA|w6:Â÷Ù©/ØZ®úäÿdû@ס=¬#ñБ€x²‹€öòɈI ’N…*]üŠZnd!E}m¹zU|@uj?ÚÃE@Ó}ªlCª“ò"HºÓ®="¯æ+Ïü"?ªNª[û±$Ò.åÈEÕ¼.›×Káxþ»L#u…¢ËqË.ó+ëj`»¢Ôe’†«f!”ÝyÈ…Eü@!ô ’r#ÐŒF5!O1>Žg[ÿŸ<ó«h)FÝÚÅüúн³¨¨ë®‰’ªGøä.]ðŠ[°! ©µJžùUtOÖ­ýXè`Z¾¨¨q°yÝÚ¤Z!´²>ÖA)F¶I§Eú?û¸EЏFêÌûI ´ž=ô1{É-EòÒ*_ay¾WÊŠÆFf²€šbŠ>š^@J¶•ú?Wé\QWÖs‚BðÛ¨€œ\†€º Ë£jJ]â8)ø÷¾4¬W¨é•u>K½¡¤î‡eË¥ª¶[-PÿõÙ"´SÑŒËô—×=È—Á%g4ÄÉËÔ1E_¢^ ¤yj®Ú¯¢.¬•:*Þ³”ƒm‹ÐTƒ'²7îë%oÛ!$OIÎhNNL±›q)4á0«ý£l¼œÏ¾ƒ ëê÷“… ­VË:ž³{ª ¿£òýíMÐ4ƒÇ³L]å&›(Õpõ7A­•^8£$¤h“|l¦—O¯ç<ó«©BSIûœÏtÑX0jHÎË>°ª7Qw³lY†¯)F˜’¢ ³ HåBUL¯´[#[Ðræó9Qƒ²†Á‹U3£›vJ»*}Qmù¶Å¢>!EäAÔiz%Õ¾m8™ÏpÃjQÃrE]Îè¦Æúp”ðÚO¼DۢѽF»ê [é¬bz%û§©Bï—E-ȶu?¼&ŠVóÔP©Lƒ…aói²¢’OU`æKë®L±iÖùÜt¦³O‚¯Á–QKr}ZPS5òšmóÂ¥Q>,tÛŽ,F©lƒB\4;ÐvQ.Ž\²‡Co‹R¶§ÔfCòûT&'û¦Mòi3ôn“}êJœjª\Ã'ÔšÇD“òƒPpØçVÚEà+Hpâ¥D“ßKÐ!{z’(Ês ­°´Jm—O¡i™>?!ÀâÂxS›4·ÁíMhAi‡Z½¢‹úƒØŒ®E}[y qØÒÒ‘Í 6Ø Öïµ5YϦCŒBU»o¾O®‹¶a!Ò?̵,‡6¥ÕEB¾u0Rº<§tÒÔ¬îÚ4,ÎÃX*µ©+úG«Ÿïlü®µÃ²ý|$à–œÎUý>i?FŒ÷Ǧ´ º}´ ‚”8½òY_r…ð¹øAr T%XJŠ|'ï¢Kí°l'C €¶`GU¿OWÞÙŸ†Q'r@EªMo#&[~P^d¬O2ȸ$æX“ž© ü~¦m]‘®tÄË)šhŠ×Gò±˜îûF ZКԂªbŽIÝ%Hm\“dKRò]èÎi›#4l¦2ßg‹úäâꫳ9‰xÙ|‹}t:;´Ÿ› æŒ:–}Ûp®Uí+Œo΃mÓ J§ûIÛ€ÿ T¿%u>lš¢ßų(ó]múêö÷$äc;Èê¶Ú„ÅOú¦(oª:=Ã7D_V’Z¶Œñ\9B¡gN—Õ€µÈwYª¤½ÐF®~»:ý= l$_¶»a°ÜÓo 我êh¦í›tU6cºH¶¨-T]„„8ÉC ÕÛÂÁ>À öý4À¢¾Ÿ4šÔ 1¹ª¶O-J>˜ð}Œx9*ÞÁ«£ÀdVÝžMP‘¬Q[xr²UÏK¡úP²}«˜E˜¦¾ßÃ=—ýž2æ^—&W^¢a_Ãí9ɺ—GÊjÃ1¬µõ Ê«+SÀZt,­‹„|5¡$JÖµƒmÌršå‚¿k*ôž™Ò!öm.K>˜¢}% h²Œk_= TNmÛë_¦ŸPѪa[os,µ­ºOxÜ÷;H[(ûuçT¡õµÞ6ùp/}'‹/ôÄ(`YÄປ–åMUÄ9]tH*´‹„|¢ciàƒèJbƒ—Ñ‚ðc4­©ìƒ£9-«¼Ï‰†9a÷ûê˜tÚ´&-Þ¦[D!+â*:¦6„|ò„BцÊÊ÷óq¾ý|ž!ÚJ]ZOµ\R¨Ýåòq8žßõ@æ–æ×Öí­)¿O™†/ A²>Ó¡hC<«"Ás-i²ùÎl¾’:røÞºÚg¸ÈÇÖEaT4K.Üu!$úÊzÏ5!]ÇðCßEÇ4 0;öÙ7RÖv=›Õ·0µŒvaù’O-@ˆ46áJ—WØrÄF…|,kÇóÚQÏä8)«-ò5ÉŠúƒ|HŸT™޳´M³ ‚0]9;×ÕDâ#›¹j ]ÓæVº°ÔöÎG!Ú•˜^ ÿ訇2¯T-ßæ)Á¥̧‡tb´ºæù€Là6 QCØÙŽÜcU‚@Ê>'T|žyÓ³ s+ÝÇÙÖ-aTÈÇQ y‹ÁÔ¨§BÿèÙmGŤbÖè~7yàÝm~R¯{dÏØs@NóO7d2W=dTÇgG~ÓiJ;/ °YmFÆÒ¤SÚ_3_/¶¾Y|o6§¤Ñf¶ûJùżþŒ6´h˜fY_àcne·­ö®ò°ãEQ¼Å)†˜ŽŠâ_o¥òMnU)ÿ°|îw¢u7lKæ7øUÔà€Ãª¤3×¼Q¼ø†Q¼Ú[vs|ïôÍzîö_‹â^30Û\ ’{)2c@IM^XLá?éÿ>¶ý’ÑpŒ öJ£Énôñ(^ï}Q¼Ö»£xƒŒ9½1ÉÞ8Ð^—Ù*Š—Ü$Š]Çhí«˜ïYÆhX‹ ÖK]&ZÑé2Ž2‹†â*‚,kp—DBUjÆÐʧÖÂkDñ*{Â9®~±µ~¡UóOFžIue}yZ®v(ÏʘD+²Øñ?=5˜ £x‰£Î"fQ £-äs‡Á2J3nY{˜^)GˆÈªkQŸÎ”о9íŒú¼Ý—Û! Ýš‡ä;;y.˜ã樆x|ÂêÉ–g{ök¾=,ò)R…ïÈõy,ôjWñ ž*CB8îÊh;8%g¼i }ls¾!€ouG>ià«Xa×|Ç&φˆÙ¨kD¾Ä“¼ÓåwøçÐj|ŸùÊ{†A:ž‰onœƒ|žŒÝIU Èë¤!—*š§ŠzùfÃÏ·t/¶ž±ûßÙmÿõ èö…ˆÈØ5>ž*ïwîù¢x¹þœ¼gNè½lPB¼ÀzMJ6@R[ƽ@¤€CÔP&úëèjÈzÒI9ÙÓày_j"39ÎÇ6‰‡HÊ„ ¶`ùÀb¦ûcŸ£Zd…»ªÕK•¼LâÕöhº.?Ü"kº3›“ú/£Ër ù¼`°·ÒH59 4ÇÎ%¡*!õ<,¹i{äóÊOò‰êºvN[òˆúâ'‚4‹(IahQZ&ÚVçXÞÅÌAŽ™ÒQ ­YäCuû¡JõÈá¶…—u<×Y.‘‰fk=š ñšABc”SW„ŸÂúW&#²É1yV¸y™ËÙü˜lÆ<ÿÍg9Œ0·Iµ9«·:+Ч­XOx¼¥âRÈçß”6ê•##aÄ䘆ˆò€ú ØÀh®Ó–HJ'flyf/¸r>Øš·!¢äôÆWÔeb#sL,®Ý÷àHˆÇÇäáwФ`v1}‡(Þö B0à˃ éïºè–èÈó™=,oRi@Úµµæ ± m'ãjûEñN3ýH$©Ê+¿˜”ýï'iù@”y5rl†"sÙ2J†86m¦a^Ñ[ Í¡ˆ¶Ê;ö©“âß“Z¾²¥5¼‹Õ˜ ¥6lÞ%ò5ñɇ½q°ÒD³BGÅ礓19ñXÜ,@À»6­|(³ðÉšÝá‚(^û°Md‘ns®_®ÏНË7¹"K§>×ÉË=—Ƀâ÷Ùèé†ýU4H ÂÊôáD|Zöò;E‰7 ­6(©È:§çœ[.“ép€`2Éb/¥‡vd7ƒ§‹.ä솵å“?ÂBË ¯º¯=JµòžÙ¶ÕÓ?Âuie3Á%í bÂWƒS; ~†IWÔìqlž¶Ç¿!kÊï—8ª),N¿»UÞ"_sÓÄÃúµ\'m5öPZhWv&X•ª©±½L%{êý qüç ­F^^ÎJ»»k¾]«þ¤µ<ßHâ m²#`T±ÜG“(cfVm;Óš5 ™bMõ±Âärä@=a°£ÒA7rl™ÅÄ©)9 ·nv¢86øÐ 91÷Ôœ:ˆœØ mÎLˆM¾pã …PŠÙ'2%Y¾¾m'¸æ¢š÷ˆf†9‰I˜´¤¥a?£€èæÈÿò3º&ºrŽlsÞà=’ÖFT òq\Ó½+ t# UÏšNàAʾ¡:}PïûY$JŸÃâµå÷°pÙ¶–§t(„œØ„¾›Ûwc']lÄ\·–S´ ×góÕE–Ú8ȦÌTzYÓÿÛfÖÑ@îU§ ´ ›ùÍ5ÔÑËÇ‘ãn1XIi ;ùåä—¢ÙÏàû·üØàÿäç–+™[´\œŽ6Pº±óL¹¦‹¾0‘¥ÏO‘©´G¥&Ê'gR)š¥›n´ÆQ†”ØÀü-Ÿ¹• Uç5ŠRp|×5k63oêBƒˆçŠ»Õÿì=*ÚÁ V èN¶˜üRX,—Ä~k°˜=Ïg£É¤±ùçì„9úm}®)[xM{ &@ÙÂÄ GËÍZ'{BLIîT’?•­iª³•nÁ²A‰°Ñ§º©QB._!ÑOM´È³‡HsÞçW£ÁT•åäÉ/æh ù$¸ØYD“)"[äüÐp¬ˆÖ󒵟j_bù÷YpuÇáÄÎ+Ö ©ÙžÉåòõ@tÀe^'úþÜ]‘||Y¬Koéä£aUZûa¬Ž´°È²ízÓØ’ùºŒŽÙ6'þ–Ј'›þ`#‘ìAC‚©GWGÚhœ¯&WXòáÉ/Šè/<å¿ N2øžÁlßçwÖ˜¤µ$„!EÍhšÏF“+ÛÉÌ aÓ°lšP=ml„HnOÈä“@ ϳf¤ÏÞ¡ qÈî¦Û=<ùÿ“_Ö÷=Èä¨Ìß|ÀS :lÒâH’ܤQÓ «¤É¦„Ó %9,8³»0/ RÉ'„Ôf_c['LÞ.ž ‘TöÛJb¤hc–T\šš‘Åw“Á¼ºÝÃ’ù¢IÍÇ6ô ‘§ ²' 'í³ûï"±!² WºçÏR¯’k‚Ò‹9Aƒˆº8¹qLK§¶œÒôý˜‚M¦'¸2žÓï—ÿïKB Ú#ÑMzå‘'E :Q·|X2còKú7yÜò‚oòøÛWOÎï1§œ´h(NMÈg÷ä;#©pÏn:zWæƒ-L܆)&ùCÐÀš*©Èƒô~›$CK>Ó_6Ðmެ2ù%îA"/ë²{MÎßÝ<)GˆbËýX÷ˆ—ÛkL™&›yQNÍ®ˆ“ZJVlº½¨-é. ]wò»lQ¸¦ž½å9üÜ`ÝúaÈ“M°=}9¯Ï¼Ø•†¦™í÷ïæMŽÄp fÃÄ´k¥ÀZv[9ß'lhËJîÊKû=¤x“Q1É’&ë®ÈØ– Ð1⋳$^ [?¹iò ºÒƒ€68Ò`ƒ÷üÕò{18Ù`I1šÁ•Ôóe¶y9ÛYŠzñ7ÉB£ {^ÂZ—ÀoµÔ^Tr<ó]m'gJ°fS×Gß!á;ÿl0·ný0䣓_Ð×WÌù!äþvg^‹ƒEBH8ê¾H8¤Š>Ïôrõ² aãqúKm/šÐ‚$í‡r‘Bê2˜&xSYÕ–ü ýuë‡!K<‰½¶Ø ßçKg<àI@?v’›%‰fd}7´[¥7ðªû䟖¶6]; ³2¼ëÖ‚$íSµ‹¨—ƹÖA„&¾—Ö»‘œ =§nÿ0äàÈ;}y¶`¦õ3Ðxò¡r’ßf“ÁLqšeÿ s+žu]+¡èPȦÕ9jFrÂw]Ýžõ‡¹ÞÏ¢©goÑ‚´Ñ|@ò9ú¢Ý$ÖD¥}7RÞN4ì÷ÌôLiæ•mH&^( e j¨b^ò¯@H¡h? òj¶š*§±ø ¿¡Û>,ù`äÕ„þxºÝ«öHGÎÏ‚ã™P®OPTþ6d ùhêÈŽ–´«k½¸×(§%KYÚTû ¾Â,¨Û>,Y{x28ˆèmô½IGb^V5ÎnHj¿¤y^Y'ežöŠ: ÒšpFKŽøPúûø:¢›Ž\Zü…é–SÚÈï7xü__›ÿ¾‚yBф֒M±Ÿd’L‹¬áz_ßOhè¼{æZë6¿šò§4íˆnòÚqr ßw‘nõðåâÉ/nGGâáébô*ÛôŠð°Ï)ëËñé˜Ç¦ µÂ;[-ÁV! ©=)¹G!Þ{ž#:µtM8£øþH3£ƒ—-ä…²Î0LÿˆÁsÃûz÷ž‘ü?yÄÅö™SÞutQS J}˜Ôþ#í/ €Ïû㾚ø~Ëš[G·xøòuÿ(™»lÂ7""%ÑIž’c7® R4¬Ê¸áIe,æy„Øå0AÖa!I>¬&|x–hØ{t{÷Ã'tcQòaaIê4‹ËÇ“5Û¤[’C:ëo Ý )Ž—¢J!å?I “Ýç6‘Á-圣ۻ²˜ÁÏ|ɱÙòŽž-Ö$:©¢šÓTrÂúöšé ’ÖRvÌNÔ³V«4$‹„Ž–Rt°‰TˆHìÒ™k»//’áã©Y’è¤.¾ž¬6Ýæ5TH×\Wù¥!ß»T)IÙÊM$Â÷Ü£Ûº2ÕàÑ`œÏï"Gµzg 3Š#%ÛáOÉ:aùïÐ{K‘¼2 ‰’IÚD©éæd”çHϤ _ž%RÕ÷XŽr¦ä…¢³~£¬ö$ùŒ $s6ªB#ªÐ HêØ‡)™ Ó³Á§óaW]«øa¸7©,§ –Ô}Á`ÝÆý•SŠlË.íGòaH‘#ÂÙR•y×Í·ª´k­´¸ ùÞ9\$'4‡I62ÚDDÏÒ®u ÝÆý•/ ¡Z"bÖsÉI‰™&…UCwÂIÂ,ƒ¦œú:ámVrª×]Rb!ÿMt÷W.ȾP—ZÒZ¢œÁRîiýR·ÁP³€=zÔÔ‚Ðð’#˜ÿNÆ)5í²ÿ6ºû+g1Á$ÒpvRölrbJ§YÈIˆj‚ÉQ@L/Þ§äª{褔 аUz*)³u1Dýö5ÙpNòÒbJê –þÈ»M v6x­Á^C¼vø³†¿Ö28+ ¼ ‰„ìÄ L2Þ'×ÞtPÁ[T·qe×"Ž`[+ [ÉdãTdÁJ¾¡¼€®aQó—¨\Qä.QNã¶!¥a@>¼»¬y†#º®ïÅ7)Ԣݩ[¸ÿ9Aø”»âêÞ'Ùì˜q|¾¤M…œƒijIAøLÉgObè}’/-d’Þ ïTšçåò))„µäŸª[¸ÿr®”Ï#i5Rá!¦T‘&^øyX¬RòZ^RWUà\³enùc+Txö–4J62³±B$ Is¥+ïT"‰ª÷AdÐÒÆåEƒtûö_–0x0²´VHHÁ–ärZKÅ‹Éi:áƱjiŠžàß*>{f\Ý9Z[Pœ’F$¾;Þ©”(X¶¸­šˆ£ãùŸ§[wtdŸá‰b­“’ÀlÎg›Ï(qXÚL°. ¡óQ.Ûˆähr%v ±fÜåôV‚°ÑÄpìwÙ°^"™„€¤5R$°€¹–øsZ¾üYÏ£'ï0˜]$l̦À‰s–$D´¤ôi-¸„€XÀ@*Æl¢›rÁàóYàl ˆ†ëç:,æ• <£¢z»ñ-‰“nÝMà‰È±YÑÔÐ,!PHœwQ‡ïÅ—€xŽü\Ц ly\ï‚kå=P¨ÌßKõ‚ü¥¢Ù«°ìnð÷¨b> Ñ N0ŸzYß¡Ö"ào òSp’œÅŠkòjIpòn×гŸÇàÓÏÕyÍ<a%χçÐP‹>oW- ëûkºŸYËê6mYlh^<5”p×C0ó aÔ°iYÉàôh0zFŸý2x«nÍñ’¥ Ž6¸eLým'luÓüœ¹Wû\6Œ¶ÛóÚà»oŽtóØ LJGƒ¹cœF/ŒÐB2ôEºÄà³C‡|hj>9C1ÔN¯2¸Ã8è!ž0ø¹Áìa0M·ŠM0EÖúŒú.¨¬g Ð ®áå?}JO8ðd…Eûp ÷”à·ÃïgA/4æ?Íà“Ñ ‘ùþÑ 4bùž¿üFë ßÃÁÑ ãåqŸ7ø–Áüzø,~Ÿz>¥ž[ÙçÿbÎ{}|H’hÐ×\mðmƒÿŒ5ˆ58Ð`{ƒéº¥&Ëÿ¼¥8œžûmËIEND®B`‚ulfius-2.2.4/example_programs/sheep_counter/static/styles.css000066400000000000000000000000701322747500200245610ustar00rootroot00000000000000/* Your Style */ body { background-color: #00AA00; } ulfius-2.2.4/example_programs/sheep_counter/static/upload.html000066400000000000000000000005341322747500200247030ustar00rootroot00000000000000
Select files to upload:

ulfius-2.2.4/example_programs/simple_example/000077500000000000000000000000001322747500200214015ustar00rootroot00000000000000ulfius-2.2.4/example_programs/simple_example/Makefile000066400000000000000000000025121322747500200230410ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # PREFIX=/usr/local LIBYDER_LOCATION=../../lib/yder/src CC=gcc CFLAGS=-c -Wall -I$(LIBYDER_LOCATION) -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-L$(LIBYDER_LOCATION) -lc -lulfius -lyder -lorcania -L$(ULFIUS_LOCATION) all: simple_example clean: rm -f *.o simple_example debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: simple_example libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET simple_example.o: simple_example.c $(CC) $(CFLAGS) simple_example.c -DDEBUG -g -O0 simple_example: libulfius.so simple_example.o $(CC) -o simple_example simple_example.o $(LIBS) test: simple_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./simple_example static: simple_example.o $(CC) -o simple_example simple_example.o $(PREFIX)/lib/liborcania.a $(PREFIX)/lib/libyder.a $(PREFIX)/lib/libulfius.a -ljansson -lmicrohttpd -lpthread -lgnutls ulfius-2.2.4/example_programs/simple_example/README.md000066400000000000000000000032411322747500200226600ustar00rootroot00000000000000# simple_example Run a simple webservice that listen on port 8537. ## Compile and run ```bash $ make test ``` ## Compile with Ulfius built as a static archive Ulfius must be built as a static archive and installed in the $(PREFIX) directory. ```bash $ make static ``` ### https connection If you want this program to listen to a secure (https) connection, create a certificate with its certificate and private keys in to separate files, and run the program with the options `-secure `. ## Endpoints available: - `GET http://localhost:8537/test`: A "Hello World!" response (status 200) - `GET http://localhost:8537/test/empty`: An empty response (status 200) - `POST http://localhost:8537/test`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `GET http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `POST http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `PUT http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `DELETE http://localhost:8537/test/param/:foo`: Will display all requests parameters (body, headers, cookies, etc.) given by the client - `GET http://localhost:8537/test/multiple/:multiple/:multiple/:not_multiple`: Will display all requests parameters (body, headers, cookies, etc.) given by the client, and multiple values in `map_url:multiple` key - `GET http://localhost:8537/testcookie/:lang/:extra` Will send a cookie to the client with `lang` and `extra` values in it ulfius-2.2.4/example_programs/simple_example/simple_example.c000066400000000000000000000211161322747500200245520ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the main features * that are available in a callback function * * Copyright 2015-2017 Nicolas Mora * * License MIT * */ #include #include #include #include #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define PORT 8537 #define PREFIX "/test" #define PREFIXJSON "/testjson" #define PREFIXCOOKIE "/testcookie" /** * callback functions declaration */ int callback_get_test (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_get_empty_response (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_post_test (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_all_test_foo (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_get_cookietest (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data); /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, value is %s", keys[i], value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s", keys[i], value); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } char * read_file(const char * filename) { char * buffer = NULL; long length; FILE * f = fopen (filename, "rb"); if (filename != NULL) { if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc (length + 1); if (buffer) { fread (buffer, 1, length, f); } buffer[length] = '\0'; fclose (f); } return buffer; } else { return NULL; } } int main (int argc, char **argv) { int ret; // Set the framework port number struct _u_instance instance; y_init_logs("simple_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting simple_example"); if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Maximum body size sent by the client is 1 Kb instance.max_post_body_size = 1024; // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, NULL, 0, &callback_get_test, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/empty", 0, &callback_get_empty_response, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/multiple/:multiple/:multiple/:not_multiple", 0, &callback_all_test_foo, NULL); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, NULL, 0, &callback_post_test, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 1"); ulfius_add_endpoint_by_val(&instance, "POST", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 2"); ulfius_add_endpoint_by_val(&instance, "PUT", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 3"); ulfius_add_endpoint_by_val(&instance, "DELETE", PREFIX, "/param/:foo", 0, &callback_all_test_foo, "user data 4"); ulfius_add_endpoint_by_val(&instance, "GET", PREFIXCOOKIE, "/:lang/:extra", 0, &callback_get_cookietest, NULL); // default_endpoint declaration ulfius_set_default_endpoint(&instance, &callback_default, NULL); // Start the framework if (argc == 4 && strcmp("-secure", argv[1]) == 0) { // If command-line options are -secure , then open an https connection char * key_pem = read_file(argv[2]), * cert_pem = read_file(argv[3]); ret = ulfius_start_secure_framework(&instance, key_pem, cert_pem); o_free(key_pem); o_free(cert_pem); } else { // Open an http connection ret = ulfius_start_framework(&instance); } if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Start %sframework on port %d", ((argc == 4 && strcmp("-secure", argv[1]) == 0)?"secure ":""), instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_DEBUG, "End framework"); y_close_logs(); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function that put a "Hello World!" string in the response */ int callback_get_test (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, "Hello World!"); return U_CALLBACK_CONTINUE; } /** * Callback function that put an empty response and a status 200 */ int callback_get_empty_response (const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_CONTINUE; } /** * Callback function that put a "Hello World!" and the post parameters send by the client in the response */ int callback_post_test (const struct _u_request * request, struct _u_response * response, void * user_data) { char * post_params = print_map(request->map_post_body); char * response_body = msprintf("Hello World!\n%s", post_params); ulfius_set_string_body_response(response, 200, response_body); o_free(response_body); o_free(post_params); return U_CALLBACK_CONTINUE; } /** * Callback function that put "Hello World!" and all the data sent by the client in the response as string (http method, url, params, cookies, headers, post, json, and user specific data in the response */ int callback_all_test_foo (const struct _u_request * request, struct _u_response * response, void * user_data) { char * url_params = print_map(request->map_url), * headers = print_map(request->map_header), * cookies = print_map(request->map_cookie), * post_params = print_map(request->map_post_body); char * response_body = msprintf("Hello World!\n\n method is %s\n url is %s\n\n parameters from the url are \n%s\n\n cookies are \n%s\n\n headers are \n%s\n\n post parameters are \n%s\n\n user data is %s\n\nclient address is %s\n\n", request->http_verb, request->http_url, url_params, cookies, headers, post_params, (char *)user_data, inet_ntoa(((struct sockaddr_in *)request->client_address)->sin_addr)); ulfius_set_string_body_response(response, 200, response_body); o_free(url_params); o_free(headers); o_free(cookies); o_free(post_params); o_free(response_body); return U_CALLBACK_CONTINUE; } /** * Callback function that sets cookies in the response * The counter cookie is incremented every time the client reloads this url */ int callback_get_cookietest (const struct _u_request * request, struct _u_response * response, void * user_data) { const char * lang = u_map_get(request->map_url, "lang"), * extra = u_map_get(request->map_url, "extra"), * counter = u_map_get(request->map_cookie, "counter"); char new_counter[8]; int i_counter; if (counter == NULL) { i_counter = 0; } else { i_counter = strtol(counter, NULL, 10); i_counter++; } snprintf(new_counter, 7, "%d", i_counter); ulfius_add_cookie_to_response(response, "lang", lang, NULL, 0, NULL, NULL, 0, 0); ulfius_add_cookie_to_response(response, "extra", extra, NULL, 0, NULL, NULL, 0, 0); ulfius_add_cookie_to_response(response, "counter", new_counter, NULL, 0, NULL, NULL, 0, 0); ulfius_set_string_body_response(response, 200, "Cookies set!"); return U_CALLBACK_CONTINUE; } /** * Default callback function called if no endpoint has a match */ int callback_default (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 404, "Page not found, do what you want"); return U_CALLBACK_CONTINUE; } ulfius-2.2.4/example_programs/stream_example/000077500000000000000000000000001322747500200214035ustar00rootroot00000000000000ulfius-2.2.4/example_programs/stream_example/Makefile000066400000000000000000000024251322747500200230460ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2016 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-lc -lorcania -lulfius -lyder -L$(ULFIUS_LOCATION) all: stream_example stream_client clean: rm -f *.o stream_example stream_client debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: stream_example stream_client libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET stream_example.o: stream_example.c $(CC) $(CFLAGS) stream_example.c -DDEBUG -g -O0 stream_example: libulfius.so stream_example.o $(CC) -o stream_example stream_example.o $(LIBS) stream_client.o: stream_client.c $(CC) $(CFLAGS) stream_client.c -DDEBUG -g -O0 stream_client: libulfius.so stream_client.o $(CC) -o stream_client stream_client.o $(LIBS) test: stream_example stream_client LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./stream_example ulfius-2.2.4/example_programs/stream_example/README.md000066400000000000000000000007041322747500200226630ustar00rootroot00000000000000# Streaming example Provides a streaming example with raw data or an mp3 file ## Compile and run ```bash $ make $ ./stream_example [path/to/an/mp3/file] ``` ## Usage run the `stream_client` program to view the streaming in real time. If you have specified an mp3 file to the stream_example program, open the following url in your browser: [http://localhost:7876/stream/audio](http://localhost:7876/stream/audio), this should play the audio file. ulfius-2.2.4/example_programs/stream_example/stream_client.c000066400000000000000000000047001322747500200244010ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program get a stream data from the server * * Copyright 2016 Nicolas Mora * * License MIT * */ #include #include #include #define U_DISABLE_JANSSON #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define URL "http://localhost:7876/stream" size_t my_write_body(void * contents, size_t size, size_t nmemb, void * user_data) { printf("got %s", (char *)contents); return size * nmemb; } size_t my_write_meta_body(void * contents, size_t size, size_t nmemb, void * user_data) { printf("got %zu %zu\n", size, nmemb); return size * nmemb; } int main(void) { struct _u_request request; struct _u_response response; int res; y_init_logs("stream_example client", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting stream_example"); ulfius_init_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("GET"); request.http_url = o_strdup(URL); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to run stream test"); getchar(); res = ulfius_send_http_streaming_request(&request, &response, my_write_body, NULL); if (res == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "ulfius_send_http_streaming_request OK"); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error in http request: %d", res); } ulfius_clean_response(&response); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to run stream audio test"); ulfius_init_response(&response); o_free(request.http_url); request.http_url = o_strdup(URL "/audio"); getchar(); res = ulfius_send_http_streaming_request(&request, &response, my_write_meta_body, NULL); if (res == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "ulfius_send_http_streaming_request OK"); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error in http request: %d", res); } ulfius_clean_response(&response); y_log_message(Y_LOG_LEVEL_DEBUG, "Press to run no stream test"); ulfius_init_response(&response); o_free(request.http_url); request.http_url = o_strdup("http://www.w3.org/"); getchar(); res = ulfius_send_http_request(&request, &response); if (res == U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "ulfius_send_http_request OK"); } else { y_log_message(Y_LOG_LEVEL_DEBUG, "Error in http request: %d", res); } ulfius_clean_response(&response); ulfius_clean_request(&request); y_close_logs(); return 0; } ulfius-2.2.4/example_programs/stream_example/stream_example.c000066400000000000000000000072471322747500200245670ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program streams some data * * Copyright 2016 Nicolas Mora * * License MIT * */ #include #include #include #include #include #include #define U_DISABLE_JANSSON #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" #define PORT 7876 #define PREFIX "/stream" /** * callback functions declaration */ int callback_get_stream (const struct _u_request * request, struct _u_response * response, void * user_data); int callback_get_audio_stream (const struct _u_request * request, struct _u_response * response, void * user_data); ssize_t stream_data (void * cls, uint64_t pos, char *buf, size_t max); ssize_t stream_audio_file (void * cls, uint64_t pos, char *buf, size_t max); void free_stream_data(void * cls); void free_stream_audio_file(void * cls); int main (int argc, char **argv) { int ret; // Set the framework port number struct _u_instance instance; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { printf("Error ulfius_init_instance, abort\n"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, NULL, 0, &callback_get_stream, NULL); if (argc > 1) { ulfius_add_endpoint_by_val(&instance, "GET", PREFIX, "/audio", 0, &callback_get_audio_stream, argv[1]); } // Start the framework ret = ulfius_start_framework(&instance); if (ret == U_OK) { printf("Start framework on port %d\n", instance.port); // Wait for the user to press on the console to quit the application getchar(); } else { printf("Error starting framework\n"); } printf("End framework\n"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return 0; } /** * Callback function */ int callback_get_stream (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_stream_response(response, 200, stream_data, free_stream_data, U_STREAM_SIZE_UNKOWN, 32 * 1024, o_strdup("stream test")); return U_CALLBACK_CONTINUE; } int callback_get_audio_stream (const struct _u_request * request, struct _u_response * response, void * user_data) { FILE * file = fopen((char *)user_data, "rb"); int fd; struct stat buf; printf("stream audio file\n"); if (file != NULL) { fd = fileno (file); if (-1 == fd) { fclose (file); return U_ERROR; /* internal error */ } if ( (0 != fstat (fd, &buf)) || (! S_ISREG (buf.st_mode)) ) { /* not a regular file, refuse to serve */ fclose (file); file = NULL; return U_ERROR; } u_map_put(response->map_header, "Content-Type", "audio/mpeg"); ulfius_set_stream_response(response, 200, stream_audio_file, free_stream_audio_file, buf.st_size, 32 * 1024, file); return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_ERROR; } } ssize_t stream_data (void * cls, uint64_t pos, char * buf, size_t max) { printf("stream data %" PRIu64 " %zu\n", pos, max); sleep(1); if (pos <= 100) { snprintf(buf, max, "%s %" PRIu64 "\n", (char *)cls, pos); return strlen(buf); } else { return U_STREAM_END; } } ssize_t stream_audio_file (void * cls, uint64_t pos, char * buf, size_t max) { FILE *file = cls; (void) fseek (file, pos, SEEK_SET); return fread (buf, 1, max, file); } void free_stream_data(void * cls) { printf("clean data\n"); o_free(cls); } void free_stream_audio_file(void * cls) { printf("clean file\n"); FILE *file = cls; fclose (file); } ulfius-2.2.4/example_programs/test_u_map/000077500000000000000000000000001322747500200205355ustar00rootroot00000000000000ulfius-2.2.4/example_programs/test_u_map/Makefile000066400000000000000000000020351322747500200221750ustar00rootroot00000000000000# # Example program # # Makefile used to build the software # # Copyright 2014-2015 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -D_REENTRANT $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src LIBS=-lc -lorcania -lulfius -lyder -L$(ULFIUS_LOCATION) all: test_u_map clean: rm -f *.o test_u_map debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: test_u_map libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL WEBSOCKETFLAG=-DU_DISABLE_WEBSOCKET test_u_map.o: test_u_map.c $(CC) $(CFLAGS) test_u_map.c -DDEBUG -g -O0 test_u_map: libulfius.so test_u_map.o $(CC) -o test_u_map test_u_map.o $(LIBS) test: test_u_map LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./test_u_map ulfius-2.2.4/example_programs/test_u_map/README.md000066400000000000000000000002021322747500200220060ustar00rootroot00000000000000# _u_map test program Execute use cases for `struct _u_map` and display the results ## Compile and run ```bash $ make test ``` ulfius-2.2.4/example_programs/test_u_map/test_u_map.c000066400000000000000000000161331322747500200230450ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program describes the main features * that are available in struct _u_map * * Copyright 2015 Nicolas Mora * * License MIT * */ #include #include #include #define U_DISABLE_JANSSON #define U_DISABLE_CURL #define U_DISABLE_WEBSOCKET #include "../../src/ulfius.h" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, length is %zu, value is %.*s", keys[i], u_map_get_length(map, keys[i]), (int)u_map_get_length(map, keys[i]), value); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } /** * put the content of a file in the u_map with the file_path as the key using u_map_put_binary */ int put_file_content_in_map (struct _u_map * map, const char * file_path, uint64_t offset) { void * buffer = NULL; long length; FILE * f; int res = U_OK; if (access(file_path, F_OK) != -1 && map != NULL) { f = fopen (file_path, "rb"); if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = o_malloc(length*sizeof(void)); if (buffer) { fread (buffer, 1, length, f); } fclose (f); } if (buffer) { res = u_map_put_binary(map,file_path, (char *)buffer, offset, length); o_free(buffer); } else { res = U_ERROR; } } else { res = U_ERROR_PARAMS; } return res; } int main (int argc, char **argv) { y_init_logs("test_u_map", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting test_u_map"); struct _u_map map, * map_copy; char * print, * print_copy; u_map_init(&map); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 1, map is\n%s\n", print); o_free(print); u_map_put(&map, "key1", "value1"); u_map_put(&map, "key2", "value2"); u_map_put(&map, "key3", "value3"); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 2, map is\n%s\n", print); o_free(print); u_map_put(&map, "key2", "value4"); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 3, map is\n%s\n", print); o_free(print); map_copy = u_map_copy(&map); u_map_put(map_copy, "key4", "value5"); print = print_map(&map); print_copy = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 4, map is\n%s\nmap_copy is\n%s\n", print, print_copy); o_free(print); o_free(print_copy); u_map_remove_at(&map, 2); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 5: remove item 2 from map, map is\n%s\n", print); o_free(print); u_map_remove_from_key(map_copy, "key1"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 6: remove key:key1 from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_key(map_copy, "key_nope"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 7: remove key:key_nope from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_key_case(map_copy, "Key2"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 8: remove case key:Key2 from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_value(map_copy, "value3"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 9: remove value:value3 from map_copy, map is\n%s\n", print); o_free(print); u_map_remove_from_value_case(map_copy, "Value5"); print = print_map(map_copy); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 10: remove case value:Value5 from map_copy, map is\n%s\n", print); o_free(print); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key key1? %d", u_map_has_key(&map, "key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key key_nope? %d", u_map_has_key(&map, "key_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key Key1? %d", u_map_has_key(&map, "Key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key Key1 (no case) ? %d", u_map_has_key_case(&map, "Key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has key Key_nope (no case) ? %d", u_map_has_key_case(&map, "Key_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value value1? %d", u_map_has_value(&map, "value1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value value_nope? %d", u_map_has_value(&map, "value_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value Value1? %d", u_map_has_value(&map, "Value1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value Value1 (no case) ? %d", u_map_has_value_case(&map, "Value1")); y_log_message(Y_LOG_LEVEL_DEBUG, "map has value Value_nope (no case) ? %d", u_map_has_value_case(&map, "Value_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key key1: %s", u_map_get(&map, "key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key key_nope: %s", u_map_get(&map, "key_nope")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key Key1 (no case): %s", u_map_get_case(&map, "Key1")); y_log_message(Y_LOG_LEVEL_DEBUG, "get value from key Key_nope (no case): %s", u_map_get_case(&map, "Key_nope")); put_file_content_in_map(&map, "../sheep_counter/static/sheep.png", 0); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 11, map is\n%s\n", print); o_free(print); u_map_remove_from_key(&map, "../sheep_counter/static/sheep.png"); put_file_content_in_map(&map, "Makefile", 0); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 12, map is\n%s\n", print); o_free(print); u_map_put_binary(&map, "Makefile", "Replace the first characters", 0, strlen("Replace the first characters")); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 12, map is\n%s\n", print); o_free(print); u_map_put_binary(&map, "Makefile", "Append at the end of the value", u_map_get_length(&map, "Makefile"), strlen("Append at the end of the value")); print = print_map(&map); y_log_message(Y_LOG_LEVEL_DEBUG, "iteration 12, map is\n%s\n", print); o_free(print); u_map_clean(&map); u_map_clean_full(map_copy); y_close_logs(); return 0; } ulfius-2.2.4/example_programs/websocket_example/000077500000000000000000000000001322747500200220765ustar00rootroot00000000000000ulfius-2.2.4/example_programs/websocket_example/Makefile000066400000000000000000000025411322747500200235400ustar00rootroot00000000000000# # Websocket example program # # Makefile used to build the software # # Copyright 2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the MIT 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. # CC=gcc CFLAGS=-c -Wall -I$(ULFIUS_LOCATION) -I$(STATIC_FILE_LOCATION) $(ADDITIONALFLAGS) ULFIUS_LOCATION=../../src STATIC_FILE_LOCATION=../../example_callbacks/static_file LIBS=-lc -lulfius -lyder -lorcania -L$(ULFIUS_LOCATION) all: websocket_example clean: rm -f *.o websocket_example valgrind.txt debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: websocket_example libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug JANSSONFLAG=-DU_DISABLE_JANSSON CURLFLAG=-DU_DISABLE_CURL static_file_callback.o: $(STATIC_FILE_LOCATION)/static_file_callback.c $(CC) $(CFLAGS) $(STATIC_FILE_LOCATION)/static_file_callback.c websocket_example.o: websocket_example.c $(CC) $(CFLAGS) websocket_example.c websocket_example: libulfius.so static_file_callback.o websocket_example.o $(CC) -o websocket_example websocket_example.o static_file_callback.o $(LIBS) test: websocket_example LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./websocket_example ulfius-2.2.4/example_programs/websocket_example/README.md000066400000000000000000000021071322747500200233550ustar00rootroot00000000000000# websocket_example Run a webservice with a websocket endpoint that will print the client messages, and send 10 messages at the same time, one per seconds, then close the connection. ## Compile and run ### HTTP mode Run a HTTP (non secure) webservice ```bash $ make debug $ LD_LIBRARY_PATH=../../src: ./websocket_example ``` ### HTTPS mode You must have a certificate key and pem files available. To generate one, you can for example use the following command: ```bash $ openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.key -sha256 ``` Run a HTTPS (secure) webservice ```bash $ make debug $ LD_LIBRARY_PATH=../../src: ./websocket_example -https cert.key cert.pem ``` ## Endpoints available: ### Websocket - `GET http://localhost:9275/websocket`: Websocket endpoint ### Static file - `GET http://localhost:9275/static`: will serve the files located in the `static` folder ## Test the websocket Open in your browser the url [http[s]://localhost:9275/static](http://localhost:9275/static), this will open an HTML page where you can test the websockets behaviour. ulfius-2.2.4/example_programs/websocket_example/static/000077500000000000000000000000001322747500200233655ustar00rootroot00000000000000ulfius-2.2.4/example_programs/websocket_example/static/index.html000066400000000000000000000050601322747500200253630ustar00rootroot00000000000000 Websocket example



ulfius-2.2.4/example_programs/websocket_example/static/styles.css000066400000000000000000000000701322747500200254170ustar00rootroot00000000000000/* Your Style */ body { background-color: #AAAAAA; } ulfius-2.2.4/example_programs/websocket_example/websocket_example.c000066400000000000000000000177441322747500200257600ustar00rootroot00000000000000/** * * Ulfius Framework example program * * This example program implements a websocket * * Copyright 2017 Nicolas Mora * * License MIT * */ #include #include #include #include #include #include #include #define U_DISABLE_JANSSON #define U_DISABLE_CURL #include "../../src/ulfius.h" #include #define PORT 9275 #define PREFIX_WEBSOCKET "/websocket" #define PREFIX_STATIC "/static" int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data); /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int i; size_t len; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); line = msprintf("%s: %s", keys[i], value); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } char * read_file(const char * filename) { char * buffer = NULL; long length; FILE * f = fopen (filename, "rb"); if (filename != NULL) { if (f) { fseek (f, 0, SEEK_END); length = ftell (f); fseek (f, 0, SEEK_SET); buffer = malloc (length + 1); if (buffer) { fread (buffer, 1, length, f); } buffer[length] = '\0'; fclose (f); } return buffer; } else { return NULL; } } /** * main function * open the wbservice on port 9275 */ int main(int argc, char ** argv) { int ret; struct _u_instance instance; struct _static_file_config file_config; char * cert_file = NULL, * key_file = NULL; y_init_logs("websocket_example", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting websocket_example"); file_config.mime_types = o_malloc(sizeof(struct _u_map)); u_map_init(file_config.mime_types); u_map_put(file_config.mime_types, ".html", "text/html"); u_map_put(file_config.mime_types, ".css", "text/css"); u_map_put(file_config.mime_types, ".js", "application/javascript"); u_map_put(file_config.mime_types, ".png", "image/png"); u_map_put(file_config.mime_types, ".jpg", "image/jpeg"); u_map_put(file_config.mime_types, ".jpeg", "image/jpeg"); u_map_put(file_config.mime_types, ".ttf", "font/ttf"); u_map_put(file_config.mime_types, ".woff", "font/woff"); u_map_put(file_config.mime_types, ".woff2", "font/woff2"); u_map_put(file_config.mime_types, ".map", "application/octet-stream"); u_map_put(file_config.mime_types, "*", "application/octet-stream"); file_config.files_path = "static"; file_config.url_prefix = PREFIX_STATIC; if (ulfius_init_instance(&instance, PORT, NULL, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error ulfius_init_instance, abort"); return(1); } u_map_put(instance.default_headers, "Access-Control-Allow-Origin", "*"); // Endpoint list declaration ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_WEBSOCKET, NULL, 0, &callback_websocket, NULL); ulfius_add_endpoint_by_val(&instance, "GET", PREFIX_STATIC, "*", 0, &callback_static_file, &file_config); // Start the framework if (argc > 3 && 0 == o_strcmp(argv[1], "-https")) { key_file = read_file(argv[2]); cert_file = read_file(argv[3]); if (key_file == NULL || cert_file == NULL) { printf("Error reading https certificate files\n"); ret = U_ERROR_PARAMS; } else { ret = ulfius_start_secure_framework(&instance, key_file, cert_file); } free(key_file); free(cert_file); } else { ret = ulfius_start_framework(&instance); } if (ret == U_OK) { y_log_message(Y_LOG_LEVEL_INFO, "Start framework on port %d %s", instance.port, (argc > 1 && 0 == o_strcmp(argv[1], "-https"))?"https mode":"http mode"); // Wait for the user to press on the console to quit the application getchar(); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error starting framework"); } y_log_message(Y_LOG_LEVEL_INFO, "End framework"); ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); u_map_clean_full(file_config.mime_types); y_close_logs(); return 0; } /** * websocket_onclose_callback * onclose callback function * Used to clear data after the websocket connection is closed */ void websocket_onclose_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_onclose_user_data) { if (websocket_onclose_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_onclose_user_data is %s", websocket_onclose_user_data); } } /** * websocket_manager_callback * send 5 text messages and 1 ping for 11 seconds, then closes the websocket */ void websocket_manager_callback(const struct _u_request * request, struct _websocket_manager * websocket_manager, void * websocket_manager_user_data) { int i, ret; char * my_message; if (websocket_manager_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_manager_user_data is %s", websocket_manager_user_data); } for (i=0; i<5; i++) { sleep(2); if (websocket_manager != NULL && websocket_manager->connected) { my_message = msprintf("Send message #%d", i); ret = ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_TEXT, o_strlen(my_message), my_message); o_free(my_message); if (ret != U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Error send message"); break; } if (i == 2) { ret = ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_PING, 0, NULL); if (ret != U_OK) { y_log_message(Y_LOG_LEVEL_DEBUG, "Error send ping message"); break; } sleep(1); } } else { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket not connected"); break; } } y_log_message(Y_LOG_LEVEL_DEBUG, "Closing websocket_manager_callback"); } /** * websocket_incoming_message_callback * Read incoming message and prints it on the console */ void websocket_incoming_message_callback (const struct _u_request * request, struct _websocket_manager * websocket_manager, const struct _websocket_message * last_message, void * websocket_incoming_message_user_data) { if (websocket_incoming_message_user_data != NULL) { y_log_message(Y_LOG_LEVEL_DEBUG, "websocket_incoming_message_user_data is %s", websocket_incoming_message_user_data); } y_log_message(Y_LOG_LEVEL_DEBUG, "Incoming message, opcode: %x, mask: %d, len: %d", last_message->opcode, last_message->has_mask, last_message->data_len); if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) { y_log_message(Y_LOG_LEVEL_DEBUG, "text payload '%.*s'", last_message->data_len, last_message->data); } else if (last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) { y_log_message(Y_LOG_LEVEL_DEBUG, "binary payload"); } } /** * Ulfius main callback function that simplu calls the websocket manager and closes */ int callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data) { char * websocket_user_data = "my_user_data"; if (ulfius_set_websocket_response(response, NULL, NULL, &websocket_manager_callback, websocket_user_data, &websocket_incoming_message_callback, websocket_user_data, &websocket_onclose_callback, websocket_user_data) == U_OK) { return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_ERROR; } } ulfius-2.2.4/lib/000077500000000000000000000000001322747500200135765ustar00rootroot00000000000000ulfius-2.2.4/lib/orcania/000077500000000000000000000000001322747500200152125ustar00rootroot00000000000000ulfius-2.2.4/lib/yder/000077500000000000000000000000001322747500200145415ustar00rootroot00000000000000ulfius-2.2.4/src/000077500000000000000000000000001322747500200136175ustar00rootroot00000000000000ulfius-2.2.4/src/Makefile000066400000000000000000000054071322747500200152650ustar00rootroot00000000000000# # Ulfius Framework # # Makefile used to build the software # # Copyright 2015-2017 Nicolas Mora # # 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 . # LIBORCANIA_LOCATION=../lib/orcania/src LIBYDER_LOCATION=../lib/yder/src PREFIX=/usr/local CC=gcc CFLAGS=-c -pedantic -std=gnu99 -fPIC -Wall -D_REENTRANT -I$(PREFIX)/include -I$(LIBORCANIA_LOCATION) -I$(LIBYDER_LOCATION) $(ADDITIONALFLAGS) $(JANSSONFLAG) $(CURLFLAG) $(WEBSOCKETFLAG) LIBS=-L$(PREFIX)/lib -L$(LIBORCANIA_LOCATION) -L$(LIBYDER_LOCATION) -lc -lmicrohttpd -lyder -lorcania -lpthread OUTPUT=libulfius.so VERSION=2.2.4 ifndef JANSSONFLAG LJANSSON=-ljansson endif ifndef CURLFLAG LCURL=-lcurl endif ifndef WEBSOCKETFLAG LWEBSOCKET=-lgnutls endif all: release libulfius.so: ulfius.o u_map.o u_request.o u_response.o u_send_request.o u_websocket.o $(CC) -shared -Wl,-soname,$(OUTPUT) -o $(OUTPUT).$(VERSION) ulfius.o u_map.o u_request.o u_response.o u_send_request.o u_websocket.o $(LIBS) $(LJANSSON) $(LCURL) $(LWEBSOCKET) ln -sf $(OUTPUT).$(VERSION) $(OUTPUT) libulfius.a: ulfius.o u_map.o u_request.o u_response.o u_send_request.o u_websocket.o ar rcs libulfius.a ulfius.o u_map.o u_request.o u_response.o u_send_request.o u_websocket.o ulfius.o: ulfius.h u_private.h ulfius.c $(CC) $(CFLAGS) ulfius.c u_map.o: ulfius.h u_private.h u_map.c $(CC) $(CFLAGS) u_map.c u_request.o: ulfius.h u_private.h u_request.c $(CC) $(CFLAGS) u_request.c u_response.o: ulfius.h u_private.h u_response.c $(CC) $(CFLAGS) u_response.c u_websocket.o: ulfius.h u_private.h u_websocket.c $(CC) $(CFLAGS) u_websocket.c u_send_request.o: ulfius.h u_private.h u_send_request.c $(CC) $(CFLAGS) u_send_request.c clean: rm -f *.o *.so *.a $(OUTPUT) $(OUTPUT).* install: all cp $(OUTPUT).$(VERSION) $(PREFIX)/lib cp ulfius.h $(PREFIX)/include -ldconfig static-install: static cp libulfius.a $(PREFIX)/lib cp ulfius.h $(PREFIX)/include uninstall: rm -f $(PREFIX)/lib/$(OUTPUT) libulfius.a rm -f $(PREFIX)/lib/$(OUTPUT).* rm -f $(PREFIX)/include/ulfius.h debug: ADDITIONALFLAGS=-DDEBUG -g -O0 debug: libulfius.so release: ADDITIONALFLAGS=-O3 release: libulfius.so static: ADDITIONALFLAGS=-O3 static: libulfius.a static-debug: ADDITIONALFLAGS=-DDEBUG -g -O0 static-debug: libulfius.a ulfius-2.2.4/src/u_map.c000066400000000000000000000412161322747500200150700ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_umap.c: Simple map structure functions definitions * not memory friendly, all pointer returned must be freed after use * * Copyright 2015-2017 Nicolas Mora * * 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 . * */ #include #include #include "u_private.h" #include "ulfius.h" /** * 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) { if (map != NULL) { map->nb_values = 0; map->keys = o_malloc(sizeof(char *)); if (map->keys == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for map->keys"); return U_ERROR_MEMORY; } map->keys[0] = NULL; map->values = o_malloc(sizeof(char *)); if (map->values == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for map->values"); o_free(map->keys); return U_ERROR_MEMORY; } map->values[0] = NULL; map->lengths = o_malloc(sizeof(size_t)); if (map->lengths == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for map->lengths"); o_free(map->keys); o_free(map->values); return U_ERROR_MEMORY; } map->lengths[0] = 0; return U_OK; } else { return U_ERROR_PARAMS; } } /** * free the struct _u_map's inner components * return U_OK on success */ int u_map_clean(struct _u_map * u_map) { if (u_map != NULL) { u_map_clean_enum(u_map->keys); u_map_clean_enum(u_map->values); o_free(u_map->lengths); return U_OK; } else { return U_ERROR_PARAMS; } } /** * free the struct _u_map and its components * return U_OK on success */ int u_map_clean_full(struct _u_map * u_map) { if (u_map_clean(u_map) == U_OK) { o_free(u_map); return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { int i; if (array != NULL) { for (i=0; array[i] != NULL; i++) { o_free(array[i]); array[i] = NULL; } o_free(array); return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { return (const char **)u_map->keys; } /** * 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 (const char **)u_map->values; } /** * 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) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { return 1; } } } return 0; } /** * 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 u_map_has_value_binary(u_map, value, strlen(value)); } /** * return true if the sprcified u_map contains the specified value * false otherwise * search is case sensitive */ int u_map_has_value_binary(const struct _u_map * u_map, const char * value, size_t length) { int i; if (u_map != NULL && value != NULL) { for (i=0; u_map->values[i] != NULL; i++) { if (0 == memcmp(u_map->values[i], value, length)) { return 1; } } } return 0; } /** * 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) { if (value != NULL) { return u_map_put_binary(u_map, key, value, 0, strlen(value)+1); } else { return u_map_put_binary(u_map, key, NULL, 0, 0); } } /** * 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) { int i; char * dup_key, * dup_value; if (u_map != NULL && key != NULL && strlen(key) > 0) { for (i=0; i < u_map->nb_values; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { // Key already exist, extend and/or replace value if (u_map->lengths[i] < (offset + length)) { u_map->values[i] = o_realloc(u_map->values[i], (offset + length)*sizeof(char)); if (u_map->values[i] == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->values"); return U_ERROR_MEMORY; } } if (value != NULL) { memcpy(u_map->values[i]+offset, value, length); } if (u_map->lengths[i] < (offset + length)) { u_map->lengths[i] = (offset + length); } return U_OK; } } if (u_map->values[i] == NULL) { // Not found, add key/value dup_key = o_strdup(key); if (dup_key == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dup_key"); return U_ERROR_MEMORY; } if (value != NULL) { dup_value = o_malloc((offset + length)*sizeof(char)); if (dup_value == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dup_value"); o_free(dup_key); return U_ERROR_MEMORY; } memcpy((dup_value + offset), value, length); } else { dup_value = NULL; } // Append key for (i = 0; u_map->keys[i] != NULL; i++); u_map->keys = o_realloc(u_map->keys, (i + 2)*sizeof(char *)); if (u_map->keys == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->keys"); o_free(dup_key); o_free(dup_value); return U_ERROR_MEMORY; } u_map->keys[i] = (char *)dup_key; u_map->keys[i+1] = NULL; // Append value u_map->values = o_realloc(u_map->values, (i + 2)*sizeof(char *)); if (u_map->values == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->values"); o_free(dup_key); o_free(dup_value); return U_ERROR_MEMORY; } u_map->values[i] = (char *)dup_value; u_map->values[i+1] = NULL; // Append length u_map->lengths = o_realloc(u_map->lengths, (i + 2)*sizeof(size_t)); if (u_map->lengths == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths"); o_free(dup_key); o_free(dup_value); return U_ERROR_MEMORY; } u_map->lengths[i] = (offset + length); u_map->lengths[i+1] = 0; u_map->nb_values++; } return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { int i, res, found = 0; if (u_map == NULL || key == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == o_strcmp(u_map->keys[i], key)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * 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) { int i, res, found = 0; if (u_map == NULL || key == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == o_strcasecmp(u_map->keys[i], key)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * 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) { return u_map_remove_from_value_binary(u_map, value, strlen(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 * value, size_t length) { int i, res, found = 0; if (u_map == NULL || value == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == memcmp(u_map->values[i], value, length)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * 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) { int i, res, found = 0; if (u_map == NULL || value == NULL) { return U_ERROR_PARAMS; } else { for (i = u_map->nb_values-1; i >= 0; i--) { if (0 == o_strcasecmp(u_map->values[i], value)) { found = 1; res = u_map_remove_at(u_map, i); if (res != U_OK) { return res; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } } /** * 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) { int i; if (u_map == NULL || index < 0) { return U_ERROR_PARAMS; } else if (index >= u_map->nb_values) { return U_ERROR_NOT_FOUND; } else { o_free(u_map->keys[index]); o_free(u_map->values[index]); for (i = index; i < u_map->nb_values; i++) { u_map->keys[i] = u_map->keys[i + 1]; u_map->values[i] = u_map->values[i + 1]; u_map->lengths[i] = u_map->lengths[i + 1]; } u_map->keys = o_realloc(u_map->keys, (u_map->nb_values)*sizeof(char *)); if (u_map->keys == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->keys"); return U_ERROR_MEMORY; } u_map->values = o_realloc(u_map->values, (u_map->nb_values)*sizeof(char *)); if (u_map->values == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->values"); return U_ERROR_MEMORY; } u_map->lengths = o_realloc(u_map->lengths, (u_map->nb_values)*sizeof(char *)); if (u_map->lengths == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map->lengths"); return U_ERROR_MEMORY; } u_map->nb_values--; return U_OK; } } /** * 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) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { if (u_map->lengths[i] > 0) { return u_map->values[i]; } else { return NULL; } } } return NULL; } else { return NULL; } } /** * 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) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->keys[i], key)) { return 1; } } } return 0; } /** * 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) { int i; if (u_map != NULL && value != NULL) { for (i=0; u_map->values[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->values[i], value)) { return 1; } } } return 0; } /** * 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) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->keys[i], key)) { return u_map->values[i]; } } return NULL; } else { return NULL; } } /** * 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) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcmp(u_map->keys[i], key)) { return u_map->lengths[i]; } } return -1; } else { return -1; } } /** * 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) { int i; if (u_map != NULL && key != NULL) { for (i=0; u_map->keys[i] != NULL; i++) { if (0 == o_strcasecmp(u_map->keys[i], key)) { return u_map->lengths[i]; } } return -1; } else { return -1; } } /** * Create an exact copy of the specified struct _u_map * return a reference to the copy, NULL otherwise * returned value must be cleaned after use */ struct _u_map * u_map_copy(const struct _u_map * source) { struct _u_map * copy = NULL; const char ** keys, * value; int i; if (source != NULL) { copy = o_malloc(sizeof(struct _u_map)); if (copy == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_map_copy.copy"); return NULL; } if (u_map_init(copy) != U_OK) { o_free(copy); return NULL; } keys = u_map_enum_keys(source); for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(source, keys[i]); if (value == NULL || u_map_put_binary(copy, keys[i], value, 0, source->lengths[i]) != U_OK) { return NULL; } } } return copy; } /** * 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) { const char ** keys; int i, res; if (source != NULL && dest != NULL) { keys = u_map_enum_keys(source); for (i=0; keys != NULL && keys[i] != NULL; i++) { res = u_map_put(dest, keys[i], u_map_get(source, keys[i])); if (res != U_OK) { return res; } } return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { if (source != NULL) { if (source->nb_values >= 0) { return source->nb_values; } } return -1; } /** * 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) { int ret = u_map_clean(u_map); if (ret == U_OK) { return u_map_init(u_map); } else { return ret; } } ulfius-2.2.4/src/u_private.h000066400000000000000000000125371322747500200157760ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_private.h: private structures and functions declarations * * Copyright 2015-2017 Nicolas Mora * * 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 . * */ #ifndef __U_PRIVATE_H__ #define __U_PRIVATE_H__ #include "ulfius.h" /********************************** * Internal functions declarations **********************************/ /** * 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_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); #ifndef U_DISABLE_WEBSOCKET /** * 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); /** * 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); /** * Close the websocket */ int ulfius_close_websocket(struct _websocket * websocket); /** * 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 // __U_PRIVATE_H__ulfius-2.2.4/src/u_request.c000066400000000000000000000433061322747500200160050ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_request.c: request related functions defintions * * Copyright 2015-2017 Nicolas Mora * * 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 . * */ #include #include #include "u_private.h" #include "ulfius.h" /** * Splits the url to an array of char * */ static char ** ulfius_split_url(const char * prefix, const char * url) { char * saveptr = NULL, * cur_word = NULL, ** to_return = o_malloc(sizeof(char*)), * url_cpy = NULL, * url_cpy_addr = NULL; int counter = 1; if (to_return != NULL) { to_return[0] = NULL; if (prefix != NULL) { url_cpy = url_cpy_addr = o_strdup(prefix); cur_word = strtok_r( url_cpy, ULFIUS_URL_SEPARATOR, &saveptr ); while (cur_word != NULL) { to_return = o_realloc(to_return, (counter+1)*sizeof(char*)); if (to_return == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return"); break; } to_return[counter-1] = o_strdup(cur_word); if (to_return[counter-1] == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return[counter-1]"); break; } to_return[counter] = NULL; counter++; cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); } o_free(url_cpy_addr); url_cpy_addr = NULL; } if (url != NULL) { url_cpy = url_cpy_addr = o_strdup(url); cur_word = strtok_r( url_cpy, ULFIUS_URL_SEPARATOR, &saveptr ); while (cur_word != NULL) { if (0 != o_strcmp("", cur_word) && cur_word[0] != '?') { to_return = o_realloc(to_return, (counter+1)*sizeof(char*)); if (to_return == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return"); break; } to_return[counter-1] = o_strdup(cur_word); if (to_return[counter-1] == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return[counter-1]"); break; } to_return[counter] = NULL; counter++; } cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); } o_free(url_cpy_addr); url_cpy_addr = NULL; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_split_url.to_return"); } return to_return; } /** * Compare two endoints by their priorities * Used by qsort to compare endpoints */ static int compare_endpoint_priorities(const void * a, const void * b) { struct _u_endpoint * e1 = *(struct _u_endpoint **)a, * e2 = *(struct _u_endpoint **)b; if (e1->priority < e2->priority) { return -1; } else if (e1->priority > e2->priority) { return 1; } else { return 0; } } /** * ulfius_url_format_match * return true if splitted_url matches splitted_url_format * false otherwise */ static int ulfius_url_format_match(const char ** splitted_url, const char ** splitted_url_format) { int i; for (i=0; splitted_url_format[i] != NULL; i++) { if (splitted_url_format[i][0] == '*' && splitted_url_format[i+1] == NULL) { return 1; } if (splitted_url[i] == NULL || (splitted_url_format[i][0] != '@' && splitted_url_format[i][0] != ':' && 0 != o_strcmp(splitted_url_format[i], splitted_url[i]))) { return 0; } } return (splitted_url[i] == NULL && splitted_url_format[i] == NULL); } /** * 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 * returned value must be free'd after use */ struct _u_endpoint ** ulfius_endpoint_match(const char * method, const char * url, struct _u_endpoint * endpoint_list) { char ** splitted_url, ** splitted_url_format; struct _u_endpoint ** endpoint_returned = o_malloc(sizeof(struct _u_endpoint *)); int i; size_t count = 0; if (endpoint_returned == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for endpoint_returned"); } else { endpoint_returned[0] = NULL; if (method != NULL && url != NULL && endpoint_list != NULL) { splitted_url = ulfius_split_url(url, NULL); for (i=0; (splitted_url != NULL && !ulfius_equals_endpoints(&(endpoint_list[i]), ulfius_empty_endpoint())); i++) { if (0 == o_strcasecmp(endpoint_list[i].http_method, method) || endpoint_list[i].http_method[0] == '*') { splitted_url_format = ulfius_split_url(endpoint_list[i].url_prefix, endpoint_list[i].url_format); if (splitted_url_format != NULL && ulfius_url_format_match((const char **)splitted_url, (const char **)splitted_url_format)) { endpoint_returned = o_realloc(endpoint_returned, (count+2)*sizeof(struct _u_endpoint *)); if (endpoint_returned != NULL) { endpoint_returned[count] = (endpoint_list + i); endpoint_returned[count + 1] = NULL; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error reallocating memory for endpoint_returned"); } count++; u_map_clean_enum(splitted_url_format); splitted_url_format = NULL; } u_map_clean_enum(splitted_url_format); splitted_url_format = NULL; } } u_map_clean_enum(splitted_url); splitted_url = NULL; } } qsort(endpoint_returned, count, sizeof(struct endpoint_list *), &compare_endpoint_priorities); return endpoint_returned; } /** * 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) { char * saveptr = NULL, * cur_word = NULL, * url_cpy = NULL, * url_cpy_addr = NULL; char * saveptr_format = NULL, * saveptr_prefix = NULL, * cur_word_format = NULL, * url_format_cpy = NULL, * url_format_cpy_addr = NULL; if (map != NULL && endpoint != NULL) { url_cpy = url_cpy_addr = o_strdup(url); url_format_cpy = url_format_cpy_addr = o_strdup(endpoint->url_prefix); cur_word = strtok_r( url_cpy, ULFIUS_URL_SEPARATOR, &saveptr ); if (endpoint->url_prefix != NULL && url_format_cpy == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for url_format_cpy"); } else if (url_format_cpy != NULL) { cur_word_format = strtok_r( url_format_cpy, ULFIUS_URL_SEPARATOR, &saveptr_prefix ); } while (cur_word_format != NULL && cur_word != NULL) { // Ignoring url_prefix words cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); cur_word_format = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr_prefix ); } o_free(url_format_cpy_addr); url_format_cpy = url_format_cpy_addr = o_strdup(endpoint->url_format); if (endpoint->url_format != NULL && url_format_cpy == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for url_format_cpy"); return U_ERROR_MEMORY; } else if (url_format_cpy != NULL) { cur_word_format = strtok_r( url_format_cpy, ULFIUS_URL_SEPARATOR, &saveptr_format ); } while (cur_word_format != NULL && cur_word != NULL) { if (cur_word_format[0] == ':' || cur_word_format[0] == '@') { if (u_map_has_key(map, cur_word_format+1)) { char * concat_url_param = msprintf("%s,%s", u_map_get(map, cur_word_format+1), cur_word); if (concat_url_param == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resources for concat_url_param"); o_free(url_cpy_addr); o_free(url_format_cpy_addr); return U_ERROR_MEMORY; } else if (u_map_put(map, cur_word_format+1, concat_url_param) != U_OK) { url_cpy_addr = NULL; url_format_cpy_addr = NULL; return U_ERROR_MEMORY; } o_free(concat_url_param); } else { if (u_map_put(map, cur_word_format+1, cur_word) != U_OK) { url_cpy_addr = NULL; url_format_cpy_addr = NULL; return U_ERROR_MEMORY; } } } cur_word = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr ); cur_word_format = strtok_r( NULL, ULFIUS_URL_SEPARATOR, &saveptr_format ); } o_free(url_cpy_addr); o_free(url_format_cpy_addr); url_cpy_addr = NULL; url_format_cpy_addr = NULL; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_init_request * Initialize a request structure by allocating inner elements * return U_OK on success */ int ulfius_init_request(struct _u_request * request) { if (request != NULL) { request->map_url = o_malloc(sizeof(struct _u_map)); request->auth_basic_user = NULL; request->auth_basic_password = NULL; request->map_header = o_malloc(sizeof(struct _u_map)); request->map_cookie = o_malloc(sizeof(struct _u_map)); request->map_post_body = o_malloc(sizeof(struct _u_map)); if (request->map_post_body == NULL || request->map_cookie == NULL || request->map_url == NULL || request->map_header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for request->map*"); ulfius_clean_request(request); return U_ERROR_MEMORY; } u_map_init(request->map_url); u_map_init(request->map_header); u_map_init(request->map_cookie); u_map_init(request->map_post_body); request->http_protocol = NULL; request->http_verb = NULL; request->http_url = NULL; request->proxy = NULL; request->timeout = 0L; request->check_server_certificate = 1; request->client_address = NULL; request->binary_body = NULL; request->binary_body_length = 0; return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { if (request != NULL) { o_free(request->http_protocol); o_free(request->http_verb); o_free(request->http_url); o_free(request->proxy); o_free(request->auth_basic_user); o_free(request->auth_basic_password); o_free(request->client_address); u_map_clean_full(request->map_url); u_map_clean_full(request->map_header); u_map_clean_full(request->map_cookie); u_map_clean_full(request->map_post_body); o_free(request->binary_body); request->http_protocol = NULL; request->http_verb = NULL; request->http_url = NULL; request->proxy = NULL; request->client_address = NULL; request->map_url = NULL; request->map_header = NULL; request->map_cookie = NULL; request->map_post_body = NULL; request->binary_body = NULL; return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { if (ulfius_clean_request(request) == U_OK) { o_free(request); return U_OK; } else { return U_ERROR_PARAMS; } } /** * create a new request based on the source elements * returned value must be free'd after use */ struct _u_request * ulfius_duplicate_request(const struct _u_request * request) { struct _u_request * new_request = NULL; if (request != NULL) { new_request = o_malloc(sizeof(struct _u_request)); if (new_request == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_request"); return NULL; } if (ulfius_init_request(new_request) == U_OK) { new_request->http_protocol = o_strdup(request->http_protocol); new_request->http_verb = o_strdup(request->http_verb); new_request->http_url = o_strdup(request->http_url); new_request->proxy = o_strdup(request->proxy); if ((new_request->http_verb == NULL && request->http_verb != NULL) || (new_request->http_url == NULL && request->http_url != NULL) || (new_request->proxy == NULL && request->proxy != NULL) || (new_request->http_protocol == NULL && request->http_protocol != NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_duplicate_request"); ulfius_clean_request_full(new_request); return NULL; } if (request->client_address != NULL) { new_request->client_address = o_malloc(sizeof(struct sockaddr)); if (new_request->client_address == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_request->client_address"); ulfius_clean_request_full(new_request); return NULL; } memcpy(new_request->client_address, request->client_address, sizeof(struct sockaddr)); } new_request->check_server_certificate = request->check_server_certificate; new_request->timeout = request->timeout; new_request->auth_basic_user = o_strdup(request->auth_basic_user); new_request->auth_basic_password = o_strdup(request->auth_basic_password); u_map_clean_full(new_request->map_url); u_map_clean_full(new_request->map_header); u_map_clean_full(new_request->map_cookie); u_map_clean_full(new_request->map_post_body); new_request->map_url = u_map_copy(request->map_url); new_request->map_header = u_map_copy(request->map_header); new_request->map_cookie = u_map_copy(request->map_cookie); new_request->map_post_body = u_map_copy(request->map_post_body); if ((new_request->map_url == NULL && request->map_url != NULL) || (new_request->map_header == NULL && request->map_header != NULL) || (new_request->map_cookie == NULL && request->map_cookie != NULL) || (new_request->map_post_body == NULL && request->map_post_body != NULL)) { ulfius_clean_request_full(new_request); return NULL; } if (request->binary_body != NULL && request->binary_body_length > 0) { new_request->binary_body = o_malloc(request->binary_body_length); if (new_request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_request->binary_body"); ulfius_clean_request_full(new_request); return NULL; } memcpy(new_request->binary_body, request->binary_body, request->binary_body_length); } else { new_request->binary_body_length = 0; new_request->binary_body = NULL; } new_request->binary_body_length = request->binary_body_length; } else { o_free(new_request); new_request = NULL; } } return new_request; } #ifndef U_DISABLE_JANSSON /** * ulfius_set_json_body_request * Add a json_t j_body to a response * return U_OK on success */ int ulfius_set_json_body_request(struct _u_request * request, json_t * j_body) { if (request != NULL && j_body != NULL && (json_is_array(j_body) || json_is_object(j_body))) { // Free all the bodies available o_free(request->binary_body); request->binary_body = NULL; request->binary_body_length = 0; request->binary_body = (void*) json_dumps(j_body, JSON_COMPACT); if (request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for request->binary_body"); return U_ERROR_MEMORY; } request->binary_body_length = strlen((char*)request->binary_body); u_map_put(request->map_header, ULFIUS_HTTP_HEADER_CONTENT, ULFIUS_HTTP_ENCODING_JSON); return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_get_json_body_request * Get JSON structure from the request body if the request is valid * request: struct _u_request used * json_error: structure to store json_error_t if specified */ json_t * ulfius_get_json_body_request(const struct _u_request * request, json_error_t * json_error) { if (request != NULL && request->map_header != NULL && NULL != o_strstr(u_map_get_case(request->map_header, ULFIUS_HTTP_HEADER_CONTENT), ULFIUS_HTTP_ENCODING_JSON)) { return json_loadb(request->binary_body, request->binary_body_length, JSON_DECODE_ANY, json_error); } else if (json_error != NULL) { json_error->line = 1; json_error->position = 1; snprintf(json_error->source, (JSON_ERROR_SOURCE_LENGTH - 1), "ulfius_get_json_body_request"); if (NULL == request) { json_error->column = 7; snprintf(json_error->text, (JSON_ERROR_TEXT_LENGTH - 1), "Request not set."); } else if (NULL == request->map_header) { json_error->column = 26; snprintf(json_error->text, (JSON_ERROR_TEXT_LENGTH - 1), "Request header not set."); } else if (NULL == o_strstr(u_map_get_case(request->map_header, ULFIUS_HTTP_HEADER_CONTENT), ULFIUS_HTTP_ENCODING_JSON)) { json_error->column = 57; snprintf(json_error->text, (JSON_ERROR_TEXT_LENGTH - 1), "HEADER content not valid. Expected containging '%s' in header - received '%s'.", ULFIUS_HTTP_ENCODING_JSON, u_map_get_case(request->map_header, ULFIUS_HTTP_HEADER_CONTENT)); } } return NULL; } #endif ulfius-2.2.4/src/u_response.c000066400000000000000000000672351322747500200161620ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_response.c: response related functions defintions * * Copyright 2015-2017 Nicolas Mora * * 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 . * */ #include #include "u_private.h" #include "ulfius.h" /** * Add a cookie in the cookie map as defined in the RFC 6265 * Returned value must be free'd after use */ static char * ulfius_get_cookie_header(const struct _u_cookie * cookie) { char * attr_expires = NULL, * attr_max_age = NULL, * attr_domain = NULL, * attr_path = NULL; char * attr_secure = NULL, * attr_http_only = NULL, * cookie_header_value = NULL; if (cookie != NULL) { if (cookie->expires != NULL) { attr_expires = msprintf("; %s=%s", ULFIUS_COOKIE_ATTRIBUTE_EXPIRES, cookie->expires); if (attr_expires == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_expires"); return NULL; } } else { attr_expires = o_strdup(""); } if (cookie->max_age > 0) { attr_max_age = msprintf("; %s=%d", ULFIUS_COOKIE_ATTRIBUTE_MAX_AGE, cookie->max_age); if (attr_max_age == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_max_age"); o_free(attr_expires); attr_expires = NULL; return NULL; } } else { attr_max_age = o_strdup(""); } if (cookie->domain != NULL) { attr_domain = msprintf("; %s=%s", ULFIUS_COOKIE_ATTRIBUTE_DOMAIN, cookie->domain); if (attr_domain == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_domain"); o_free(attr_expires); attr_expires = NULL; o_free(attr_max_age); attr_max_age = NULL; return NULL; } } else { attr_domain = o_strdup(""); } if (cookie->path != NULL) { attr_path = msprintf("; %s=%s", ULFIUS_COOKIE_ATTRIBUTE_PATH, cookie->path); if (attr_path == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_path"); o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; return NULL; } } else { attr_path = o_strdup(""); } if (cookie->secure) { attr_secure = msprintf("; %s", ULFIUS_COOKIE_ATTRIBUTE_SECURE); if (attr_secure == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_secure"); o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); o_free(attr_path); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; attr_path = NULL; return NULL; } } else { attr_secure = o_strdup(""); } if (cookie->http_only) { attr_http_only = msprintf("; %s", ULFIUS_COOKIE_ATTRIBUTE_HTTPONLY); if (attr_http_only == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for attr_http_only"); o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); o_free(attr_path); o_free(attr_secure); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; attr_path = NULL; attr_secure = NULL; return NULL; } } else { attr_http_only = o_strdup(""); } if (attr_expires == NULL || attr_max_age == NULL || attr_domain == NULL || attr_path == NULL || attr_secure == NULL || attr_http_only == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_get_cookie_header"); } else { cookie_header_value = msprintf("%s=%s%s%s%s%s%s%s", cookie->key, cookie->value, attr_expires, attr_max_age, attr_domain, attr_path, attr_secure, attr_http_only); if (cookie_header_value == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for cookie_header_value"); } } o_free(attr_expires); o_free(attr_max_age); o_free(attr_domain); o_free(attr_path); o_free(attr_secure); o_free(attr_http_only); attr_expires = NULL; attr_max_age = NULL; attr_domain = NULL; attr_path = NULL; attr_secure = NULL; attr_http_only = NULL; return cookie_header_value; } else { return NULL; } } /** * 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) { const char ** header_keys = u_map_enum_keys(response_map_header); const char * header_value; int i = -1, ret; if (header_keys != NULL && response != NULL && response_map_header != NULL) { for (i=0; header_keys != NULL && header_keys[i] != NULL; i++) { header_value = u_map_get(response_map_header, header_keys[i]); if (header_value != NULL) { ret = MHD_add_response_header (response, header_keys[i], header_value); if (ret == MHD_NO) { i = -1; break; } } } } return i; } /** * 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) { int i, ret; char * header; if (mhd_response != NULL && response != NULL) { for (i=0; inb_cookies; i++) { header = ulfius_get_cookie_header(&response->map_cookie[i]); if (header != NULL) { ret = MHD_add_response_header (mhd_response, MHD_HTTP_HEADER_SET_COOKIE, header); o_free(header); if (ret == MHD_NO) { i = -1; break; } } else { i = -1; break; } } return i; } else { return -1; } } /** * 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 unsigned int max_age, const char * domain, const char * path, const int secure, const int http_only) { int i; if (response != NULL && key != NULL) { // Look for cookies with the same key for (i=0; inb_cookies; i++) { if (0 == o_strcmp(response->map_cookie[i].key, key)) { // Key found, replace cookie o_free(response->map_cookie[i].value); o_free(response->map_cookie[i].expires); o_free(response->map_cookie[i].domain); o_free(response->map_cookie[i].path); response->map_cookie[i].value = o_strdup(value!=NULL?value:""); response->map_cookie[i].expires = o_strdup(expires); response->map_cookie[i].domain = o_strdup(domain); response->map_cookie[i].path = o_strdup(path); response->map_cookie[i].max_age = max_age; response->map_cookie[i].secure = secure; response->map_cookie[i].http_only = http_only; if ((value != NULL && response->map_cookie[i].value == NULL) || (expires != NULL && response->map_cookie[i].expires == NULL) || (domain != NULL && response->map_cookie[i].domain == NULL) || (path != NULL && response->map_cookie[i].path == NULL)) { ulfius_clean_cookie(&response->map_cookie[i]); o_free(response->map_cookie[i].value); o_free(response->map_cookie[i].expires); o_free(response->map_cookie[i].domain); o_free(response->map_cookie[i].path); return U_ERROR_MEMORY; } else { return U_OK; } } } // Key not found, inserting a new cookie if (response->nb_cookies == 0) { response->map_cookie = o_malloc(sizeof(struct _u_cookie)); if (response->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->map_cookie"); return U_ERROR_MEMORY; } } else { response->map_cookie = o_realloc(response->map_cookie, (response->nb_cookies + 1) * sizeof(struct _u_cookie)); if (response->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->map_cookie"); return U_ERROR_MEMORY; } } response->map_cookie[response->nb_cookies].key = o_strdup(key); response->map_cookie[response->nb_cookies].value = o_strdup(value!=NULL?value:""); response->map_cookie[response->nb_cookies].expires = o_strdup(expires); response->map_cookie[response->nb_cookies].max_age = max_age; response->map_cookie[response->nb_cookies].domain = o_strdup(domain); response->map_cookie[response->nb_cookies].path = o_strdup(path); response->map_cookie[response->nb_cookies].secure = secure; response->map_cookie[response->nb_cookies].http_only = http_only; if ((key != NULL && response->map_cookie[response->nb_cookies].key == NULL) || (value != NULL && response->map_cookie[response->nb_cookies].value == NULL) || (expires != NULL && response->map_cookie[response->nb_cookies].expires == NULL) || (domain != NULL && response->map_cookie[response->nb_cookies].domain == NULL) || (path != NULL && response->map_cookie[response->nb_cookies].path == NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_add_cookie_to_response"); ulfius_clean_cookie(&response->map_cookie[response->nb_cookies]); o_free(response->map_cookie[response->nb_cookies].key); o_free(response->map_cookie[response->nb_cookies].value); o_free(response->map_cookie[response->nb_cookies].expires); o_free(response->map_cookie[response->nb_cookies].domain); o_free(response->map_cookie[response->nb_cookies].path); return U_ERROR_MEMORY; } response->nb_cookies++; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_clean_cookie * clean the cookie's elements * return U_OK on success */ int ulfius_clean_cookie(struct _u_cookie * cookie) { if (cookie != NULL) { o_free(cookie->key); o_free(cookie->value); o_free(cookie->expires); o_free(cookie->domain); o_free(cookie->path); cookie->key = NULL; cookie->value = NULL; cookie->expires = NULL; cookie->domain = NULL; cookie->path = NULL; return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { if (source != NULL && dest != NULL) { dest->key = o_strdup(source->key); dest->value = o_strdup(source->value); dest->expires = o_strdup(source->expires); dest->max_age = source->max_age; dest->domain = o_strdup(source->domain); dest->path = o_strdup(source->path); dest->secure = source->secure; dest->http_only = source->http_only; if (dest->path == NULL || dest->domain == NULL || dest->expires == NULL || dest->value == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for ulfius_copy_cookie"); o_free(dest->path); o_free(dest->domain); o_free(dest->expires); o_free(dest->value); return U_ERROR_MEMORY; } else { return U_OK; } } return U_ERROR_PARAMS; } /** * 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) { int i; if (response != NULL) { o_free(response->protocol); response->protocol = NULL; u_map_clean_full(response->map_header); response->map_header = NULL; for (i=0; inb_cookies; i++) { ulfius_clean_cookie(&response->map_cookie[i]); } o_free(response->auth_realm); o_free(response->map_cookie); o_free(response->binary_body); response->auth_realm = NULL; response->map_cookie = NULL; response->binary_body = NULL; #ifndef U_DISABLE_WEBSOCKET o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_extensions); o_free(response->websocket_handle); #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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) { if (ulfius_clean_response(response) == U_OK) { o_free(response); return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_init_response * Initialize a response structure by allocating inner elements * return U_OK on success */ int ulfius_init_response(struct _u_response * response) { if (response != NULL) { response->status = 200; response->map_header = o_malloc(sizeof(struct _u_map)); if (response->map_header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->map_header"); return U_ERROR_MEMORY; } if (u_map_init(response->map_header) != U_OK) { return U_ERROR_PARAMS; } response->auth_realm = NULL; response->map_cookie = NULL; response->nb_cookies = 0; response->protocol = NULL; response->binary_body = NULL; response->binary_body_length = 0; response->stream_callback = NULL; response->stream_size = -1; response->stream_block_size = ULFIUS_STREAM_BLOCK_SIZE_DEFAULT; response->stream_callback_free = NULL; response->shared_data = NULL; #ifndef U_DISABLE_WEBSOCKET response->websocket_handle = o_malloc(sizeof(struct _websocket_handle)); if (response->websocket_handle == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->websocket_handle"); return U_ERROR_MEMORY; } ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = NULL; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data = NULL; #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * create a new response based on the source elements * return value must be free'd after use */ struct _u_response * ulfius_duplicate_response(const struct _u_response * response) { struct _u_response * new_response = NULL; int i; if (response != NULL) { new_response = o_malloc(sizeof(struct _u_response)); if (new_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response"); return NULL; } ulfius_init_response(new_response); new_response->status = response->status; new_response->protocol = o_strdup(response->protocol); if (new_response->protocol == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response->protocol"); ulfius_clean_response_full(new_response); return NULL; } u_map_clean_full(new_response->map_header); new_response->map_header = u_map_copy(response->map_header); new_response->auth_realm = o_strdup(response->auth_realm); new_response->nb_cookies = response->nb_cookies; if (response->nb_cookies > 0) { new_response->map_cookie = o_malloc(response->nb_cookies*sizeof(struct _u_cookie)); if (new_response->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response->map_cookie"); o_free(new_response); return NULL; } for (i=0; inb_cookies; i++) { ulfius_copy_cookie(&new_response->map_cookie[i], &response->map_cookie[i]); } } else { new_response->map_cookie = NULL; } if (response->binary_body != NULL && response->binary_body_length > 0) { new_response->binary_body = o_malloc(response->binary_body_length); if (new_response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for new_response->binary_body"); o_free(new_response->map_cookie); o_free(new_response); return NULL; } new_response->binary_body_length = response->binary_body_length; memcpy(new_response->binary_body, response->binary_body, response->binary_body_length); } #ifndef U_DISABLE_WEBSOCKET if ((new_response->websocket_handle = o_malloc(sizeof(struct _websocket_handle))) != NULL) { if (response->websocket_handle != NULL) { ((struct _websocket_handle *)new_response->websocket_handle)->websocket_protocol = o_strdup(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); ((struct _websocket_handle *)new_response->websocket_handle)->websocket_extensions = o_strdup(((struct _websocket_handle *)response->websocket_handle)->websocket_extensions); ((struct _websocket_handle *)new_response->websocket_handle)->websocket_manager_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_manager_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_incoming_message_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_incoming_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_onclose_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback; ((struct _websocket_handle *)new_response->websocket_handle)->websocket_onclose_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data; } } else { ulfius_clean_response_full(new_response); return NULL; } #endif } return new_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) { int i; if (dest != NULL && source != NULL) { dest->status = source->status; dest->protocol = o_strdup(source->protocol); if (dest->protocol == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dest->protocol"); return U_ERROR_MEMORY; } u_map_clean_full(dest->map_header); dest->map_header = u_map_copy(source->map_header); if (dest->map_header == NULL) { return U_ERROR_MEMORY; } dest->nb_cookies = source->nb_cookies; if (source->nb_cookies > 0) { dest->map_cookie = o_malloc(source->nb_cookies*sizeof(struct _u_cookie)); if (dest->map_cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dest->map_cookie"); return U_ERROR_MEMORY; } for (i=0; inb_cookies; i++) { ulfius_copy_cookie(&dest->map_cookie[i], &source->map_cookie[i]); } } else { dest->map_cookie = NULL; } if (source->binary_body != NULL && source->binary_body_length > 0) { dest->binary_body = o_malloc(source->binary_body_length); if (dest->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for dest->binary_body"); return U_ERROR_MEMORY; } dest->binary_body_length = source->binary_body_length; memcpy(dest->binary_body, source->binary_body, source->binary_body_length); } #ifndef U_DISABLE_WEBSOCKET if (source->websocket_handle != NULL) { ((struct _websocket_handle *)dest->websocket_handle)->websocket_protocol = o_strdup(((struct _websocket_handle *)source->websocket_handle)->websocket_protocol); ((struct _websocket_handle *)dest->websocket_handle)->websocket_extensions = o_strdup(((struct _websocket_handle *)source->websocket_handle)->websocket_extensions); ((struct _websocket_handle *)dest->websocket_handle)->websocket_manager_callback = ((struct _websocket_handle *)source->websocket_handle)->websocket_manager_callback; ((struct _websocket_handle *)dest->websocket_handle)->websocket_manager_user_data = ((struct _websocket_handle *)source->websocket_handle)->websocket_manager_user_data; ((struct _websocket_handle *)dest->websocket_handle)->websocket_incoming_message_callback = ((struct _websocket_handle *)source->websocket_handle)->websocket_incoming_message_callback; ((struct _websocket_handle *)dest->websocket_handle)->websocket_incoming_user_data = ((struct _websocket_handle *)source->websocket_handle)->websocket_incoming_user_data; ((struct _websocket_handle *)dest->websocket_handle)->websocket_onclose_callback = ((struct _websocket_handle *)source->websocket_handle)->websocket_onclose_callback; ((struct _websocket_handle *)dest->websocket_handle)->websocket_onclose_user_data = ((struct _websocket_handle *)source->websocket_handle)->websocket_onclose_user_data; } #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_set_string_body_response * Set a string binary_body to a response * binary_body must end with a '\0' character * return U_OK on success */ int ulfius_set_string_body_response(struct _u_response * response, const unsigned int status, const char * binary_body) { if (response != NULL && binary_body != NULL) { size_t binary_body_length = strlen(binary_body); // Free the binary_body available o_free(response->binary_body); response->binary_body = o_malloc(binary_body_length); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } else { response->status = status; response->binary_body_length = binary_body_length; memcpy(response->binary_body, binary_body, binary_body_length); return U_OK; } } else { return U_ERROR_PARAMS; } } /** * ulfius_set_binary_body_response * Add a binary binary_body to a response * return U_OK on success */ int ulfius_set_binary_body_response(struct _u_response * response, const unsigned int status, const char * binary_body, const size_t length) { if (response != NULL && binary_body != NULL && length > 0) { // Free all the bodies available o_free(response->binary_body); response->binary_body = NULL; response->binary_body_length = 0; response->binary_body = o_malloc(length); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } memcpy(response->binary_body, binary_body, length); response->binary_body_length = length; response->status = status; return U_OK; } else { return U_ERROR_PARAMS; } } /** * 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 unsigned int status) { if (response != NULL) { // Free all the bodies available response->binary_body = NULL; response->binary_body_length = 0; response->status = status; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_set_stream_response * Set an stream response with a status * set stream_size to -1 if unknown * set stream_block_size to a proper value based on the system * return U_OK on success */ int ulfius_set_stream_response(struct _u_response * response, const unsigned int 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) { if (response != NULL && stream_callback != NULL) { // Free all the bodies available o_free(response->binary_body); response->binary_body = NULL; response->binary_body_length = 0; response->status = status; response->stream_callback = stream_callback; response->stream_callback_free = stream_callback_free; response->stream_size = stream_size; response->stream_block_size = stream_block_size; response->stream_user_data = stream_user_data; return U_OK; } else { return U_ERROR_PARAMS; } } #ifndef U_DISABLE_JANSSON /** * ulfius_set_json_body_response * Add a json_t j_body to a response * return U_OK on success */ int ulfius_set_json_body_response(struct _u_response * response, const unsigned int status, const json_t * j_body) { if (response != NULL && j_body != NULL && (json_is_array(j_body) || json_is_object(j_body))) { // Free all the bodies available o_free(response->binary_body); response->binary_body = NULL; response->binary_body_length = 0; response->binary_body = (void*) json_dumps(j_body, JSON_COMPACT); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } response->binary_body_length = strlen((char*)response->binary_body); response->status = status; u_map_put(response->map_header, ULFIUS_HTTP_HEADER_CONTENT, ULFIUS_HTTP_ENCODING_JSON); return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_get_json_body_response * Get JSON structure from the request body if the request is valid * request: struct _u_request used * json_error: structure to store json_error_t if specified */ json_t * ulfius_get_json_body_response(struct _u_response * response, json_error_t * json_error) { if (response != NULL && response->map_header != NULL && NULL != o_strstr(u_map_get_case(response->map_header, ULFIUS_HTTP_HEADER_CONTENT), ULFIUS_HTTP_ENCODING_JSON)) { return json_loadb(response->binary_body, response->binary_body_length, JSON_DECODE_ANY, json_error); } return NULL; } #endif /** * 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) { if (response != NULL && key != NULL && value != NULL) { return u_map_put(response->map_header, key, value); return U_OK; } else { return U_ERROR_PARAMS; } } ulfius-2.2.4/src/u_send_request.c000066400000000000000000000764201322747500200170210ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_send_request.c: send request related functions defintions * * Copyright 2015-2017 Nicolas Mora * * 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 . * */ #include "u_private.h" #include "ulfius.h" #ifndef U_DISABLE_CURL #include #include #include #include /** * Internal structure used to store temporarly the response body */ typedef struct _body { char * data; size_t size; } body; /** * ulfius_write_body * Internal function used to write the body response into a _body structure */ static size_t ulfius_write_body(void * contents, size_t size, size_t nmemb, void * user_data) { size_t realsize = size * nmemb; body * body_data = (body *) user_data; body_data->data = o_realloc(body_data->data, body_data->size + realsize + 1); if(body_data->data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for body_data->data"); return 0; } memcpy(&(body_data->data[body_data->size]), contents, realsize); body_data->size += realsize; body_data->data[body_data->size] = 0; return realsize; } /** * write_header * Write the header value into the response map_header structure * return the size_t of the header written */ static size_t write_header(void * buffer, size_t size, size_t nitems, void * user_data) { struct _u_response * response = (struct _u_response *) user_data; char * header = (char *)buffer, * key, * value, * saveptr, * tmp; if (strchr(header, ':') != NULL) { if (response->map_header != NULL) { // Expecting a header (key: value) key = trimwhitespace(strtok_r(header, ":", &saveptr)); value = trimwhitespace(strtok_r(NULL, "", &saveptr)); if (!u_map_has_key_case(response->map_header, key)) { u_map_put(response->map_header, key, value); } else { tmp = msprintf("%s, %s", u_map_get_case(response->map_header, key), value); if (u_map_remove_from_key_case(response->map_header, key) != U_OK || u_map_put(response->map_header, key, tmp)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting header value for name %s", key); } o_free(tmp); } } } else if (strlen(trimwhitespace(header)) > 0) { // Expecting the HTTP/x.x header if (response->protocol != NULL) { o_free(response->protocol); } response->protocol = o_strdup(header); if (response->protocol == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->protocol"); return 0; } } return nitems * size; } /** * 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) { body body_data; body_data.size = 0; body_data.data = NULL; int res; res = ulfius_send_http_streaming_request(request, response, ulfius_write_body, (void *)&body_data); if (res == U_OK && response != NULL) { if (body_data.data != NULL && body_data.size > 0) { response->binary_body = o_malloc(body_data.size); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); o_free(body_data.data); return U_ERROR_MEMORY; } memcpy(response->binary_body, body_data.data, body_data.size); response->binary_body_length = body_data.size; } o_free(body_data.data); return U_OK; } else { o_free(body_data.data); return res; } } /** * 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) { CURLcode res; CURL * curl_handle = NULL; struct curl_slist * header_list = NULL, * cookies_list = NULL; char * key_esc, * value_esc, * cookie, * header, * param, * fp = "?", * np = "&"; const char * value, ** keys; int i, has_params = 0, len; struct _u_request * copy_request = NULL; o_malloc_t malloc_fn; o_realloc_t realloc_fn; o_free_t free_fn; if (request != NULL) { // Duplicate the request and work on it copy_request = ulfius_duplicate_request(request); if (copy_request == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_duplicate_request"); return U_ERROR_MEMORY; } o_get_alloc_funcs(&malloc_fn, &realloc_fn, &free_fn); if (curl_global_init_mem(CURL_GLOBAL_DEFAULT, malloc_fn, free_fn, realloc_fn, *o_strdup, *calloc) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_global_init_mem"); return U_ERROR_MEMORY; } curl_handle = curl_easy_init(); if (copy_request != NULL) { // Append header values if (copy_request->map_header == NULL) { copy_request->map_header = o_malloc(sizeof(struct _u_map)); if (copy_request->map_header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->map_header"); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } if (u_map_init(copy_request->map_header) != U_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } } // Set basic auth if defined if (copy_request->auth_basic_user != NULL && copy_request->auth_basic_password != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC) == CURLE_OK) { if (curl_easy_setopt(curl_handle, CURLOPT_USERNAME, copy_request->auth_basic_user) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, copy_request->auth_basic_password) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting HTTP Basic user name or password"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting HTTP Basic Auth option"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } } // Set proxy if defined if (copy_request->proxy != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_PROXY, copy_request->proxy) != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting proxy option"); ulfius_clean_request_full(copy_request); curl_easy_cleanup(curl_handle); return U_ERROR_LIBCURL; } } has_params = (o_strchr(copy_request->http_url, '?') != NULL); if (copy_request->map_url != NULL && u_map_count(copy_request->map_url) > 0) { // Append url parameters keys = u_map_enum_keys(copy_request->map_url); // Append parameters from map_url for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(copy_request->map_url, keys[i]); key_esc = curl_easy_escape(curl_handle, keys[i], 0); value_esc = curl_easy_escape(curl_handle, value, 0); if (key_esc == NULL || value_esc == NULL) { curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } param = msprintf("%s=%s", key_esc, value_esc); if (param == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for param"); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } if (has_params == 0) { copy_request->http_url = o_realloc(copy_request->http_url, strlen(copy_request->http_url) + strlen(param) + 2); if (copy_request->http_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->http_url"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } strcat(copy_request->http_url, fp); strcat(copy_request->http_url, param); has_params = 1; } else { copy_request->http_url = o_realloc(copy_request->http_url, strlen(copy_request->http_url) + strlen(param) + 2); if (copy_request->http_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->http_url"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } strcat(copy_request->http_url, np); strcat(copy_request->http_url, param); } o_free(param); curl_free(key_esc); curl_free(value_esc); } } if (u_map_count(copy_request->map_post_body) > 0) { // Append MHD_HTTP_POST_ENCODING_FORM_URLENCODED post parameters keys = u_map_enum_keys(copy_request->map_post_body); for (i=0; keys != NULL && keys[i] != NULL; i++) { // Build parameter value = u_map_get(copy_request->map_post_body, keys[i]); if (value == NULL) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } key_esc = curl_easy_escape(curl_handle, keys[i], 0); value_esc = curl_easy_escape(curl_handle, value, 0); if (key_esc == NULL || value_esc == NULL) { curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } param = msprintf("%s=%s", key_esc, value_esc); if (param == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for param"); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } // Append parameter to body if (copy_request->binary_body_length == 0) { len = strlen(param) + sizeof(char); copy_request->binary_body = o_malloc(len); if (copy_request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->binary_body"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } strcpy(copy_request->binary_body, ""); strcat(copy_request->binary_body, param); copy_request->binary_body_length = len; } else { len = (copy_request->binary_body_length + strlen(param) + sizeof(char)); copy_request->binary_body = o_realloc(copy_request->binary_body, len); if (copy_request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for copy_request->binary_body"); o_free(param); curl_free(key_esc); curl_free(value_esc); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } memcpy((char*)copy_request->binary_body + copy_request->binary_body_length, np, 1); memcpy((char*)copy_request->binary_body + copy_request->binary_body_length + 1, param, strlen(param)); copy_request->binary_body_length = len; } o_free(param); curl_free(key_esc); curl_free(value_esc); } if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, copy_request->binary_body) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields"); return U_ERROR_LIBCURL; } if (u_map_put(copy_request->map_header, ULFIUS_HTTP_HEADER_CONTENT, MHD_HTTP_POST_ENCODING_FORM_URLENCODED) != U_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } } if (copy_request->map_header != NULL && u_map_count(copy_request->map_header) > 0) { // Append map headers keys = u_map_enum_keys(copy_request->map_header); for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(copy_request->map_header, keys[i]); if (value == NULL) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } header = msprintf("%s:%s", keys[i], value); if (header == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for header"); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } header_list = curl_slist_append(header_list, header); if (header_list == NULL) { o_free(header); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } o_free(header); } } if (copy_request->map_cookie != NULL && u_map_count(copy_request->map_cookie) > 0) { // Append cookies keys = u_map_enum_keys(copy_request->map_cookie); for (i=0; keys != NULL && keys[i] != NULL; i++) { value = u_map_get(copy_request->map_cookie, keys[i]); if (value == NULL) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } cookie = msprintf("%s=%s", keys[i], value); if (cookie == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for cookie"); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } if (curl_easy_setopt(curl_handle, CURLOPT_COOKIE, cookie) != CURLE_OK) { o_free(cookie); ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); return U_ERROR_MEMORY; } o_free(cookie); } } // Request parameters if (curl_easy_setopt(curl_handle, CURLOPT_URL, copy_request->http_url) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, copy_request->http_verb) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, header_list) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (1)"); return U_ERROR_LIBCURL; } // Set CURLOPT_WRITEFUNCTION if specified if (write_body_function != NULL && curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_body_function) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (2)"); return U_ERROR_LIBCURL; } // Set CURLOPT_WRITEDATA if specified if (write_body_data != NULL && curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, write_body_data) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (3)"); return U_ERROR_LIBCURL; } // Disable certificate validation if needed if (!copy_request->check_server_certificate) { if (curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (4)"); return U_ERROR_LIBCURL; } } // Set request timeout value if (copy_request->timeout) { if (curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, copy_request->timeout) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (5)"); return U_ERROR_LIBCURL; } } // Response parameters if (response != NULL) { if (response->map_header != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, write_header) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, response) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers"); return U_ERROR_LIBCURL; } } } if (copy_request->binary_body != NULL) { if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, copy_request->binary_body) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields"); return U_ERROR_LIBCURL; } if (copy_request->binary_body_length > 0) { if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, copy_request->binary_body_length) != CURLE_OK) { ulfius_clean_request_full(copy_request); curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields"); return U_ERROR_LIBCURL; } } } if (curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1) != CURLE_OK) { curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl CURLOPT_NOSIGNAL"); return U_ERROR_LIBCURL; } if (curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, "") != CURLE_OK) { // Apparently you have to do that to tell libcurl you'll need cookies afterwards curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl CURLOPT_COOKIEFILE"); return U_ERROR_LIBCURL; } res = curl_easy_perform(curl_handle); if(res == CURLE_OK && response != NULL) { if (curl_easy_getinfo (curl_handle, CURLINFO_RESPONSE_CODE, &response->status) != CURLE_OK) { curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing http request, libcurl error: %d, error message %s", res, curl_easy_strerror(res)); return U_ERROR_LIBCURL; } if (curl_easy_getinfo(curl_handle, CURLINFO_COOKIELIST, &cookies_list) == CURLE_OK) { struct curl_slist * nc = cookies_list; char * key = NULL, * value = NULL, * expires = NULL, * domain = NULL, * path = NULL; int secure = 0, http_only = 0; while (nc != NULL) { char * nc_dup = o_strdup(nc->data), * saveptr, * elt; int counter = 0; if (nc_dup != NULL) { elt = strtok_r(nc_dup, "\t", &saveptr); while (elt != NULL) { // libcurl cookie format is domain\tsecure\tpath\thttp_only\texpires\tkey\tvalue switch (counter) { case 0: domain = o_strdup(elt); break; case 1: secure = (0==o_strcmp(elt, "TRUE")); break; case 2: path = o_strdup(elt); break; case 3: http_only = (0==o_strcmp(elt, "TRUE")); break; case 4: expires = o_strdup(elt); break; case 5: key = o_strdup(elt); break; case 6: value = o_strdup(elt); break; } elt = strtok_r(NULL, "\t", &saveptr); counter++; } if (ulfius_add_cookie_to_response(response, key, value, expires, 0, domain, path, secure, http_only) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error adding cookie %s/%s to response", key, value); } o_free(key); o_free(value); o_free(domain); o_free(path); o_free(expires); } o_free(nc_dup); nc = nc->next; } } else { curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing http request, libcurl error: %d, error message %s", res, curl_easy_strerror(res)); return U_ERROR_LIBCURL; } curl_slist_free_all(cookies_list); } curl_slist_free_all(header_list); curl_easy_cleanup(curl_handle); ulfius_clean_request_full(copy_request); if (res == CURLE_OK) { return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing curl command: %s", curl_easy_strerror(res)); return U_ERROR_LIBCURL; } } } return U_ERROR_PARAMS; } /** * ulfius_send_smtp_email body fill function and structures */ #define MAIL_DATE 0 #define MAIL_TO 1 #define MAIL_FROM 2 #define MAIL_CC 3 #define MAIL_SUBJECT 4 #define MAIL_DATA 5 #define MAIL_END 6 struct upload_status { int lines_read; char * to; char * from; char * cc; char * subject; char * data; }; static size_t smtp_payload_source(void * ptr, size_t size, size_t nmemb, void * userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; char * data = NULL; size_t len = 0; if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { return 0; } if (upload_ctx->lines_read == MAIL_DATE) { time_t now; time(&now); data = o_malloc(128*sizeof(char)); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_DATE\n"); return 0; } else { strftime(data, 128, "Date: %a, %d %b %Y %T %z\r\n", gmtime(&now)); len = strlen(data); } } else if (upload_ctx->lines_read == MAIL_TO) { data = msprintf("To: %s\r\n", upload_ctx->to); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_TO\n"); return 0; } len = strlen(data); } else if (upload_ctx->lines_read == MAIL_FROM) { data = msprintf("From: %s\r\n", upload_ctx->from); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_FROM\n"); return 0; } len = strlen(data); } else if (upload_ctx->lines_read == MAIL_CC && upload_ctx->cc) { data = msprintf("Cc: %s\r\n", upload_ctx->cc); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_CC\n"); return 0; } len = strlen(data); } else if (upload_ctx->lines_read == MAIL_SUBJECT) { data = msprintf("Subject: %s\r\n", upload_ctx->subject); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_SUBJECT\n"); return 0; } len = strlen(data); } else if (upload_ctx->lines_read == MAIL_DATA) { data = msprintf("Content-Type: text/plain; charset=utf-8\r\n\r\n%s\r\n", upload_ctx->data); if (data == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for MAIL_DATA\n"); return 0; } len = strlen(data); } if (upload_ctx->lines_read != MAIL_END) { memcpy(ptr, data, (len+1)); upload_ctx->lines_read++; // Skip next if it's cc and there is no cc if (upload_ctx->lines_read == MAIL_CC && !upload_ctx->cc) { upload_ctx->lines_read++; } o_free(data); return len; } else if (upload_ctx->lines_read == MAIL_END) { return 0; } y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting mail payload, len is %d, lines_read is %d", len, upload_ctx->lines_read); return 0; } /** * 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) { CURL * curl_handle; CURLcode res = CURLE_OK; char * smtp_url; int cur_port; struct curl_slist * recipients = NULL; struct upload_status upload_ctx; if (host != NULL && from != NULL && to != NULL && mail_body != NULL) { curl_handle = curl_easy_init(); if (curl_handle != NULL) { if (port == 0 && !use_tls) { cur_port = 25; } else if (port == 0 && use_tls) { cur_port = 587; } else { cur_port = port; } smtp_url = msprintf("smtp%s://%s:%d", use_tls?"s":"", host, cur_port); if (smtp_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for smtp_url"); return U_ERROR_MEMORY; } curl_easy_setopt(curl_handle, CURLOPT_URL, smtp_url); if (use_tls) { curl_easy_setopt(curl_handle, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); } if (use_tls && !verify_certificate) { curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); } if (user != NULL && password != NULL) { curl_easy_setopt(curl_handle, CURLOPT_USERNAME, user); curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password); } curl_easy_setopt(curl_handle, CURLOPT_MAIL_FROM, from); recipients = curl_slist_append(recipients, to); if (cc != NULL) { recipients = curl_slist_append(recipients, cc); } if (bcc != NULL) { recipients = curl_slist_append(recipients, bcc); } curl_easy_setopt(curl_handle, CURLOPT_MAIL_RCPT, recipients); upload_ctx.lines_read = 0; upload_ctx.to = (char*)to; upload_ctx.from = (char*)from; upload_ctx.cc = (char*)cc; upload_ctx.subject = (char*)subject; upload_ctx.data = (char*)mail_body; curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, smtp_payload_source); curl_easy_setopt(curl_handle, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L); res = curl_easy_perform(curl_handle); curl_slist_free_all(recipients); curl_easy_cleanup(curl_handle); o_free(smtp_url); if (res != CURLE_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending smtp message, libcurl error: %d, error message %s", res, curl_easy_strerror(res)); return U_ERROR_LIBCURL; } else { return U_OK; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing curl_easy_init"); return U_ERROR_LIBCURL; } } else { return U_ERROR_PARAMS; } } #endif ulfius-2.2.4/src/u_websocket.c000066400000000000000000000776771322747500200163250ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * u_websocket.c: websocket implementation * * Copyright 2017 Nicolas Mora * * 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 . * */ #include "u_private.h" #include "ulfius.h" #ifndef U_DISABLE_WEBSOCKET #include #include #include #include #include #include /** * 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) { if (response != NULL && (websocket_manager_callback != NULL || websocket_incoming_message_callback)) { if (((struct _websocket_handle *)response->websocket_handle)->websocket_protocol != NULL) { o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); } ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol = o_strdup(websocket_protocol); if (((struct _websocket_handle *)response->websocket_handle)->websocket_protocol == NULL && websocket_protocol != NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Error allocating resources for response->websocket_protocol"); return U_ERROR_MEMORY; } if (((struct _websocket_handle *)response->websocket_handle)->websocket_extensions != NULL) { o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_extensions); } ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions = o_strdup(websocket_extensions); if (((struct _websocket_handle *)response->websocket_handle)->websocket_extensions == NULL && websocket_extensions != NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Error allocating resources for response->websocket_extensions"); o_free(((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); return U_ERROR_MEMORY; } ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback = websocket_manager_callback; ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data = websocket_manager_user_data; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback = websocket_incoming_message_callback; ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data = websocket_incoming_user_data; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback = websocket_onclose_callback; ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data = websocket_onclose_user_data; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Run websocket in a separate thread * 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_thread_websocket(void * data) { int opcode; struct _websocket * websocket = (struct _websocket*)data; struct _websocket_message * message = NULL; pthread_t thread_websocket_manager; pthread_mutexattr_t mutexattr; int thread_ret_websocket_manager = 0, poll_ret; if (websocket != NULL && websocket->websocket_manager != NULL) { pthread_mutexattr_init ( &mutexattr ); pthread_mutexattr_settype( &mutexattr, PTHREAD_MUTEX_RECURSIVE ); if (pthread_mutex_init(&(websocket->websocket_manager->read_lock), &mutexattr) != 0 || pthread_mutex_init(&(websocket->websocket_manager->write_lock), &mutexattr) != 0) { y_log_message(Y_LOG_LEVEL_ERROR, "Impossible to initialize Mutex Lock for websocket"); websocket->websocket_manager->connected = 0; } pthread_mutexattr_destroy( &mutexattr ); if (websocket->websocket_manager_callback != NULL && websocket->websocket_manager->connected) { websocket->websocket_manager->manager_closed = 0; thread_ret_websocket_manager = pthread_create(&thread_websocket_manager, NULL, ulfius_thread_websocket_manager_run, (void *)websocket); if (thread_ret_websocket_manager) { y_log_message(Y_LOG_LEVEL_ERROR, "Error creating websocket manager thread, return code: %d", thread_ret_websocket_manager); websocket->websocket_manager->connected = 0; } } else { websocket->websocket_manager->manager_closed = 1; } while (websocket->websocket_manager->connected && !websocket->websocket_manager->closing) { message = NULL; if (pthread_mutex_lock(&websocket->websocket_manager->read_lock)) { websocket->websocket_manager->connected = 0; } poll_ret = poll(&websocket->websocket_manager->fds, 1, U_WEBSOCKET_USEC_WAIT); if (poll_ret == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Error poll websocket read"); } else if (poll_ret > 0) { opcode = ulfius_read_incoming_message(websocket->websocket_manager, &message); if (opcode == U_WEBSOCKET_OPCODE_CLOSE) { // Send close command back, then close the socket if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending close command"); } websocket->websocket_manager->closing = 1; } else if (opcode == U_WEBSOCKET_OPCODE_PING) { // Send pong command if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_PONG, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending pong command"); } } else if (opcode != U_WEBSOCKET_OPCODE_NONE && message != NULL) { if (websocket->websocket_incoming_message_callback != NULL) { // TODO: run in thread websocket->websocket_incoming_message_callback(websocket->request, websocket->websocket_manager, message, websocket->websocket_incoming_user_data); } } if (message != NULL) { if (ulfius_push_websocket_message(websocket->websocket_manager->message_list_incoming, message) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error pushing new websocket message in list"); } } } pthread_mutex_unlock(&websocket->websocket_manager->read_lock); } if (ulfius_close_websocket(websocket) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error closing websocket"); } // Wait for thread manager to close pthread_join(thread_websocket_manager, NULL); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error websocket parameters"); } ulfius_clear_websocket(websocket); return NULL; } /** * Websocket callback function for MHD * Starts the websocket manager if set, */ 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) { struct _websocket * websocket = (struct _websocket*)cls; pthread_t thread_websocket; int thread_ret_websocket = 0, thread_detach_websocket = 0; if (websocket != NULL) { websocket->urh = urh; websocket->websocket_manager = o_malloc(sizeof(struct _websocket_manager)); // Run websocket manager in a thread if set if (websocket->websocket_manager != NULL) { websocket->websocket_manager->message_list_incoming = o_malloc(sizeof(struct _websocket_message_list)); websocket->websocket_manager->message_list_outcoming = o_malloc(sizeof(struct _websocket_message_list)); ulfius_init_websocket_message_list(websocket->websocket_manager->message_list_incoming); ulfius_init_websocket_message_list(websocket->websocket_manager->message_list_outcoming); websocket->websocket_manager->sock = sock; websocket->websocket_manager->fds.fd = sock; websocket->websocket_manager->fds.events = POLLIN; websocket->websocket_manager->connected = 1; websocket->websocket_manager->closing = 0; thread_ret_websocket = pthread_create(&thread_websocket, NULL, ulfius_thread_websocket, (void *)websocket); thread_detach_websocket = pthread_detach(thread_websocket); if (thread_ret_websocket || thread_detach_websocket) { y_log_message(Y_LOG_LEVEL_ERROR, "Error creating or detaching websocket manager thread, return code: %d, detach code: %d", thread_ret_websocket, thread_detach_websocket); ulfius_clear_websocket(websocket); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error allocating resources for websocket_manager"); ulfius_clear_websocket(websocket); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error websocket is NULL"); ulfius_clear_websocket(websocket); } return; } /** * 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 */ int ulfius_read_incoming_message(struct _websocket_manager * websocket_manager, struct _websocket_message ** message) { int len, opcode, i; int message_complete = 0, message_error; uint8_t header[2], payload_len[8], masking_key[4]; uint8_t * payload_data; size_t msg_len = 0; (*message) = NULL; do { message_error = 0; len = ulfius_websocket_recv_all(websocket_manager->sock, header, 2); // New frame has arrived if (len == 2) { if ((*message) == NULL) { *message = o_malloc(sizeof(struct _websocket_message)); (*message)->data_len = 0; (*message)->has_mask = 0; (*message)->opcode = header[0] & 0x0F; (*message)->data = NULL; } if ((header[1] & U_WEBSOCKET_LEN_MASK) <= 125) { msg_len = (header[1] & U_WEBSOCKET_LEN_MASK); } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 126) { len = ulfius_websocket_recv_all(websocket_manager->sock, payload_len, 2); if (len == 2) { msg_len = payload_len[1] | ((uint64_t)payload_len[0] << 8); } else if (len != -1) { message_error = 1; opcode = U_WEBSOCKET_OPCODE_ERROR; y_log_message(Y_LOG_LEVEL_ERROR, "Error reading websocket message length"); } } else if ((header[1] & U_WEBSOCKET_LEN_MASK) == 127) { len = ulfius_websocket_recv_all(websocket_manager->sock, payload_len, 8); if (len == 8) { msg_len = payload_len[7] | ((uint64_t)payload_len[6] << 8) | ((uint64_t)payload_len[5] << 16) | ((uint64_t)payload_len[4] << 24) | ((uint64_t)payload_len[3] << 32) | ((uint64_t)payload_len[2] << 40) | ((uint64_t)payload_len[1] << 48) | ((uint64_t)payload_len[0] << 54); } else if (len != -1) { message_error = 1; opcode = U_WEBSOCKET_OPCODE_ERROR; y_log_message(Y_LOG_LEVEL_ERROR, "Error reading websocket message length"); } } if (!message_error) { if (header[1] & U_WEBSOCKET_HAS_MASK) { (*message)->has_mask = 1; len = ulfius_websocket_recv_all(websocket_manager->sock, masking_key, 4); if (len != 4 && len != -1) { message_error = 1; opcode = U_WEBSOCKET_OPCODE_ERROR; y_log_message(Y_LOG_LEVEL_ERROR, "Error reading websocket for mask"); } if (!message_error) { if (msg_len > 0) { payload_data = o_malloc(msg_len*sizeof(uint8_t)); len = ulfius_websocket_recv_all(websocket_manager->sock, payload_data, msg_len); if (len == msg_len) { // If mask, decode message if ((*message)->has_mask) { (*message)->data = o_realloc((*message)->data, (msg_len+(*message)->data_len)*sizeof(uint8_t)); for (i = (*message)->data_len; i < msg_len; i++) { (*message)->data[i] = payload_data[i-(*message)->data_len] ^ masking_key[(i-(*message)->data_len)%4]; } } else { memcpy((*message)->data+(*message)->data_len, payload_data, msg_len); } (*message)->data_len += msg_len; } else if (len != -1) { message_error = 1; opcode = U_WEBSOCKET_OPCODE_ERROR; y_log_message(Y_LOG_LEVEL_ERROR, "Error reading websocket for payload_data"); } o_free(payload_data); } if (!message_error && (header[0] & U_WEBSOCKET_BIT_FIN)) { opcode = (*message)->opcode; time(&(*message)->datestamp); message_complete = 1; } } } else { message_error = 1; opcode = U_WEBSOCKET_OPCODE_ERROR; y_log_message(Y_LOG_LEVEL_ERROR, "Incoming message has no MASK flag, exiting"); } } } else if (len == 0) { // Socket closed opcode = U_WEBSOCKET_OPCODE_CLOSED; message_complete = 1; } else if (len != -1) { opcode = U_WEBSOCKET_OPCODE_ERROR; message_error = 1; y_log_message(Y_LOG_LEVEL_ERROR, "Error reading websocket for header, len is %d", len); } else { opcode = U_WEBSOCKET_OPCODE_NONE; message_complete = 1; } } while (!message_complete && !message_error); return opcode; } /** * Clear all data related to the websocket */ int ulfius_clear_websocket(struct _websocket * websocket) { if (websocket != NULL) { if (MHD_upgrade_action (websocket->urh, MHD_UPGRADE_ACTION_CLOSE) != MHD_YES) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending MHD_UPGRADE_ACTION_CLOSE frame to urh"); } ulfius_instance_remove_websocket_active(websocket->instance, websocket); ulfius_clear_websocket_manager(websocket->websocket_manager); ulfius_clean_request_full(websocket->request); o_free(websocket->websocket_manager); websocket->websocket_manager = NULL; o_free(websocket); return U_OK; } else { return U_ERROR_PARAMS; } } /** * Run the websocket manager in a separated detached thread */ void * ulfius_thread_websocket_manager_run(void * args) { struct _websocket * websocket = (struct _websocket *)args; if (websocket != NULL && websocket->websocket_manager_callback != NULL && websocket->websocket_manager != NULL) { websocket->websocket_manager_callback(websocket->request, websocket->websocket_manager, websocket->websocket_manager_user_data); // Websocket manager callback complete, set close signal websocket->websocket_manager->manager_closed = 1; websocket->websocket_manager->closing = 1; } pthread_exit(NULL); } /** * Generates a handhshake answer from the key given in parameter */ int ulfius_generate_handshake_answer(const char * key, char * out_digest) { gnutls_datum_t key_data; unsigned char encoded_key[32] = {0}; size_t encoded_key_size = 32, encoded_key_size_base64; int res, to_return = 0; key_data.data = (unsigned char*)msprintf("%s%s", key, U_WEBSOCKET_MAGIC_STRING); key_data.size = strlen((const char *)key_data.data); if (key_data.data != NULL && out_digest != NULL && (res = gnutls_fingerprint(GNUTLS_DIG_SHA1, &key_data, encoded_key, &encoded_key_size)) == GNUTLS_E_SUCCESS) { if (o_base64_encode(encoded_key, encoded_key_size, (unsigned char *)out_digest, &encoded_key_size_base64)) { to_return = 1; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error base64 encoding hashed key"); } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error getting sha1 signature for key"); } o_free(key_data.data); return to_return; } /** * Initialize a websocket message list * Return U_OK on success */ int ulfius_init_websocket_message_list(struct _websocket_message_list * message_list) { if (message_list != NULL) { message_list->len = 0; message_list->list = NULL; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Clear data of a websocket message list */ void ulfius_clear_websocket_message_list(struct _websocket_message_list * message_list) { size_t i; if (message_list != NULL) { for (i=0; i < message_list->len; i++) { ulfius_clear_websocket_message(message_list->list[i]); } o_free(message_list->list); } } /** * Clear data of a websocket message */ void ulfius_clear_websocket_message(struct _websocket_message * message) { if (message != NULL) { o_free(message->data); o_free(message); } } /** * 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) { if (message_list != NULL && message != NULL) { message_list->list = o_realloc(message_list->list, (message_list->len+1)*sizeof(struct _websocket_message *)); if (message_list->list != NULL) { message_list->list[message_list->len] = message; message_list->len++; return U_OK; } else { return U_ERROR_MEMORY; } } else { return U_ERROR_PARAMS; } } /** * 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) { size_t len; struct _websocket_message * message = NULL; if (message_list != NULL && message_list->len > 0) { message = message_list->list[0]; for (len=0; len < message_list->len-1; len++) { message_list->list[len] = message_list->list[len+1]; } message_list->list = o_realloc(message_list->list, (message_list->len-1)); message_list->len--; } return message; } /** * 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) { int ret, count = WEBSOCKET_MAX_CLOSE_TRY, poll_ret, ret_message = U_WEBSOCKET_OPCODE_NONE; struct _websocket_message * message; if (websocket_manager != NULL && websocket_manager->connected) { if (pthread_mutex_lock(&websocket_manager->write_lock)) { return U_ERROR; } if (opcode == U_WEBSOCKET_OPCODE_CLOSE) { // If message sent is U_WEBSOCKET_OPCODE_CLOSE, wait for the response for 2 s max, then close the connection if (pthread_mutex_lock(&websocket_manager->read_lock)) { pthread_mutex_unlock(&websocket_manager->write_lock); return U_ERROR; } ret = ulfius_websocket_send_message_nolock(websocket_manager, opcode, data_len, data); message = NULL; poll_ret = poll(&websocket_manager->fds, 1, U_WEBSOCKET_USEC_WAIT); if (poll_ret == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Error poll websocket read for close signal"); } else if (poll_ret > 0) { do { ret_message = ulfius_read_incoming_message(websocket_manager, &message); if (message != NULL) { if (ulfius_push_websocket_message(websocket_manager->message_list_incoming, message) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error pushing new websocket message in list"); } } } while (ret_message != U_WEBSOCKET_OPCODE_CLOSE && (count-- > 0)); } websocket_manager->closing = 1; pthread_mutex_unlock(&websocket_manager->read_lock); } else { ret = ulfius_websocket_send_message_nolock(websocket_manager, opcode, data_len, data); } pthread_mutex_unlock(&websocket_manager->write_lock); return ret; } else { return U_ERROR_PARAMS; } } /** * 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) { size_t frame_data_len; uint8_t * sent_data; int off; struct _websocket_message * my_message; if (websocket_manager != NULL && websocket_manager->connected && ( opcode == U_WEBSOCKET_OPCODE_TEXT || opcode == U_WEBSOCKET_OPCODE_BINARY || opcode == U_WEBSOCKET_OPCODE_CLOSE || opcode == U_WEBSOCKET_OPCODE_PING || opcode == U_WEBSOCKET_OPCODE_PONG ) && (data_len == 0 || data != NULL)) { frame_data_len = 2 + data_len; if (data_len > 65536) { frame_data_len += 8; } else if (data_len > 128) { frame_data_len += 2; } sent_data = o_malloc(frame_data_len + 1); my_message = o_malloc(sizeof(struct _websocket_message)); if (sent_data != NULL && my_message != NULL) { if (data_len > 0) { my_message->data = o_malloc(data_len*sizeof(char)); if (my_message->data == NULL) { o_free(sent_data); o_free(my_message); return U_ERROR_MEMORY; } } sent_data[0] = opcode|U_WEBSOCKET_BIT_FIN; my_message->opcode = opcode; my_message->has_mask = 0; memset(my_message->mask, 0, 4); my_message->data_len = data_len; if (data_len > 65536) { sent_data[1] = 127; sent_data[2] = (uint8_t)(data_len >> 54); sent_data[3] = (uint8_t)(data_len >> 48); sent_data[4] = (uint8_t)(data_len >> 40); sent_data[5] = (uint8_t)(data_len >> 32); sent_data[6] = (uint8_t)(data_len >> 24); sent_data[7] = (uint8_t)(data_len >> 16); sent_data[8] = (uint8_t)(data_len >> 8); sent_data[9] = (uint8_t)(data_len); off = 10; } else if (data_len > 125) { sent_data[1] = 126; sent_data[2] = (uint8_t)(data_len >> 8); sent_data[3] = (uint8_t)(data_len); off = 4; } else { sent_data[1] = (uint8_t)data_len; off = 2; } if (data_len > 0) { memcpy(sent_data + off, data, data_len); memcpy(my_message->data, data, data_len); } else { my_message->data = NULL; } time(&my_message->datestamp); ulfius_websocket_send_all(websocket_manager->sock, sent_data, frame_data_len); if (ulfius_push_websocket_message(websocket_manager->message_list_outcoming, my_message) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error pushing new websocket message in list"); } o_free(sent_data); return U_OK; } else { return U_ERROR_MEMORY; } } else { return U_ERROR_PARAMS; } } /** * 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) { ssize_t ret = 0, off; if (data != NULL && len > 0) { for (off = 0; off < len; off += ret) { ret = send(sock, &data[off], len - off, MSG_NOSIGNAL); if (ret < 0) { break; } } } } /** * 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) { return read(sock, data, len); } /** * 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) { char ** source_list = NULL, ** match_list = NULL; char * to_return = NULL; int i; if (match == NULL) { to_return = o_strdup(source); } else { if (source != NULL) { if (split_string(source, ",", &source_list) > 0 && split_string(match, ",", &match_list) > 0) { for (i=0; source_list[i] != NULL; i++) { if (string_array_has_trimmed_value((const char **)match_list, source_list[i])) { if (to_return == NULL) { to_return = o_strdup(trimwhitespace(source_list[i])); } else { char * tmp = msprintf("%s, %s", to_return, trimwhitespace(source_list[i])); o_free(to_return); to_return = tmp; } } } free_string_array(source_list); free_string_array(match_list); } } } return to_return; } /** * Close the websocket */ int ulfius_close_websocket(struct _websocket * websocket) { if (websocket != NULL && websocket->websocket_manager != NULL) { if (websocket->websocket_onclose_callback != NULL && websocket->websocket_manager->connected) { // Call websocket_onclose_callback if set websocket->websocket_onclose_callback(websocket->request, websocket->websocket_manager, websocket->websocket_onclose_user_data); } // If websocket is still open, send opcode 0x08 (close) if (websocket->websocket_manager->connected) { if (ulfius_websocket_send_message(websocket->websocket_manager, U_WEBSOCKET_OPCODE_CLOSE, 0, NULL) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Error sending close frame to websocket"); } } websocket->websocket_manager->connected = 0; return U_OK; } else { return U_ERROR_PARAMS; } } /** * Clear data of a websocket_manager */ void ulfius_clear_websocket_manager(struct _websocket_manager * websocket_manager) { if (websocket_manager != NULL) { pthread_mutex_destroy(&websocket_manager->read_lock); pthread_mutex_destroy(&websocket_manager->write_lock); ulfius_clear_websocket_message_list(websocket_manager->message_list_incoming); o_free(websocket_manager->message_list_incoming); websocket_manager->message_list_incoming = NULL; ulfius_clear_websocket_message_list(websocket_manager->message_list_outcoming); o_free(websocket_manager->message_list_outcoming); websocket_manager->message_list_outcoming = NULL; } } /** * 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) { if (instance != NULL && websocket != NULL) { ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = o_realloc(((struct _websocket_handler *)instance->websocket_handler)->websocket_active, (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active+1)*sizeof(struct _websocket *)); if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active != NULL) { ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active] = websocket; ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active++; return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Error allocating resources for instance->websocket_handler->websocket_active"); return U_ERROR_MEMORY; } } else { return U_ERROR_PARAMS; } } /** * 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) { size_t i, j; if (instance != NULL && ((struct _websocket_handler *)instance->websocket_handler)->websocket_active != NULL && websocket != NULL) { for (i=0; i<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active; i++) { if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active[i] == websocket) { if (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active > 1) { for (j=i; j<((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active-1; j++) { ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j] = ((struct _websocket_handler *)instance->websocket_handler)->websocket_active[j+1]; } ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = o_realloc(((struct _websocket_handler *)instance->websocket_handler)->websocket_active, (((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active-1)*sizeof(struct _websocket *)); if (((struct _websocket_handler *)instance->websocket_handler)->websocket_active == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Error allocating resources for instance->websocket_active"); return U_ERROR_MEMORY; } } else { o_free(((struct _websocket_handler *)instance->websocket_handler)->websocket_active); ((struct _websocket_handler *)instance->websocket_handler)->websocket_active = NULL; } ((struct _websocket_handler *)instance->websocket_handler)->nb_websocket_active--; pthread_mutex_lock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock); pthread_cond_broadcast(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_cond); pthread_mutex_unlock(&((struct _websocket_handler *)instance->websocket_handler)->websocket_close_lock); return U_OK; } } return U_ERROR_NOT_FOUND; } else { return U_ERROR_PARAMS; } } #endif ulfius-2.2.4/src/ulfius.c000066400000000000000000001570661322747500200153110ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * ulfius.c: framework functions definitions * * Copyright 2015-2017 Nicolas Mora * * 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 . * */ #include #include #include #include "u_private.h" #include "ulfius.h" /** * Fill a map with the key/values specified */ static int ulfius_fill_map(void * cls, enum MHD_ValueKind kind, const char * key, const char * value) { char * tmp; int res; if (cls == NULL || key == NULL) { // Invalid parameters y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error invalid parameters for ulfius_fill_map"); return MHD_NO; } else if (u_map_get(((struct _u_map *)cls), key) != NULL) { // u_map already has a value with this this key, appending value separated with a comma ',') tmp = msprintf("%s,%s", u_map_get(((struct _u_map *)cls), key), (value==NULL?"":value)); res = u_map_put(((struct _u_map *)cls), key, tmp); o_free(tmp); if (res == U_OK) { return MHD_YES; } else { return MHD_NO; } } else if (u_map_put(((struct _u_map *)cls), key, (value==NULL?"":value)) == U_OK) { return MHD_YES; } else { return MHD_NO; } } /** * ulfius_is_valid_endpoint * return true if the endpoind has valid parameters */ static int ulfius_is_valid_endpoint(const struct _u_endpoint * endpoint, int to_delete) { if (endpoint != NULL) { if (ulfius_equals_endpoints(endpoint, ulfius_empty_endpoint())) { // Should be the last endpoint of the list to close it return 1; } else if (endpoint->http_method == NULL) { return 0; } else if (!to_delete && endpoint->callback_function == NULL) { return 0; } else if (endpoint->url_prefix == NULL && endpoint->url_format == NULL) { return 0; } else { return 1; } } else { return 0; } } /** * ulfius_validate_endpoint_list * return true if endpoint_list has valid parameters */ static int ulfius_validate_endpoint_list(const struct _u_endpoint * endpoint_list, int nb_endpoints) { int i; if (endpoint_list != NULL) { for (i=0; i < nb_endpoints; i++) { if (i == 0 && ulfius_equals_endpoints(ulfius_empty_endpoint(), &endpoint_list[i])) { // One can not have an empty endpoint in the beginning of the list y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, no empty endpoint allowed in the beginning of the endpoint list"); return U_ERROR_PARAMS; } else if (!ulfius_is_valid_endpoint(&endpoint_list[i], 0)) { // One must set at least the parameters http_method, url_format and callback_function y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, endpoint at index %d has invalid parameters", i); return U_ERROR_PARAMS; } } return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, no endpoint list"); return U_ERROR_PARAMS; } } /** * ulfius_validate_instance * return true if u_instance has valid parameters */ static int ulfius_validate_instance(const struct _u_instance * u_instance) { if (u_instance == NULL || u_instance->port <= 0 || u_instance->port >= 65536 || ulfius_validate_endpoint_list(u_instance->endpoint_list, u_instance->nb_endpoints) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, instance or has invalid parameters"); return U_ERROR_PARAMS; } return U_OK; } /** * Internal method used to duplicate the full url before it's manipulated and modified by MHD */ static void * ulfius_uri_logger (void * cls, const char * uri) { struct connection_info_struct * con_info = o_malloc (sizeof (struct connection_info_struct)); if (con_info != NULL) { con_info->callback_first_iteration = 1; con_info->u_instance = NULL; con_info->request = o_malloc(sizeof(struct _u_request)); if (con_info->request == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info->request"); o_free(con_info); return NULL; } if (NULL == con_info->request || ulfius_init_request(con_info->request) != U_OK) { ulfius_clean_request_full(con_info->request); o_free(con_info); return NULL; } con_info->request->http_url = o_strdup(uri); if (con_info->request->http_url == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info->request->http_url"); ulfius_clean_request_full(con_info->request); o_free(con_info); return NULL; } con_info->max_post_param_size = 0; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info"); } return con_info; } /** * ulfius_get_body_from_response * Extract the body data from the response if any * Copy it in newly allocated response_buffer and set the size in response_buffer_len * return U_OK on success */ static int ulfius_get_body_from_response(struct _u_response * response, void ** response_buffer, size_t * response_buffer_len) { if (response == NULL || response_buffer == NULL || response_buffer_len == NULL) { return U_ERROR_PARAMS; } else { if (response->binary_body != NULL && response->binary_body_length > 0) { // The user sent a binary response *response_buffer = o_malloc(response->binary_body_length); if (*response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response->binary_body = o_strdup(ULFIUS_HTTP_ERROR_BODY); response->binary_body_length = strlen(ULFIUS_HTTP_ERROR_BODY); if (response->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); return U_ERROR_MEMORY; } } else { memcpy(*response_buffer, response->binary_body, response->binary_body_length); *response_buffer_len = response->binary_body_length; } } else { *response_buffer = NULL; *response_buffer_len = 0; } return U_OK; } } /** * mhd_request_completed * function used to clean data allocated after a web call is complete */ static void mhd_request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { struct connection_info_struct *con_info = *con_cls; if (NULL == con_info) { return; } if (NULL != con_info && con_info->has_post_processor && con_info->post_processor != NULL) { MHD_destroy_post_processor (con_info->post_processor); } ulfius_clean_request_full(con_info->request); u_map_clean(&con_info->map_url_initial); con_info->request = NULL; o_free(con_info); con_info = NULL; *con_cls = NULL; } /** * mhd_iterate_post_data * function used to iterate post parameters * if a parameter is larger than max_post_param_size, truncate it * return MHD_NO on error */ static 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) { struct connection_info_struct * con_info = coninfo_cls; size_t cur_size = size; char * data_dup, * filename_param; if (filename != NULL && con_info->u_instance != NULL && con_info->u_instance->file_upload_callback != NULL) { if (con_info->u_instance->file_upload_callback(con_info->request, key, filename, content_type, transfer_encoding, data, off, size, con_info->u_instance->file_upload_cls) == U_OK) { return MHD_YES; } else { return MHD_NO; } } else { data_dup = o_strndup(data, size); // Force value to end with a NULL character if (con_info->max_post_param_size > 0) { if (off > con_info->max_post_param_size) { return MHD_YES; } else if (off + size > con_info->max_post_param_size) { cur_size = con_info->max_post_param_size - off; } } if (filename != NULL) { filename_param = msprintf("%s_filename", key); if (u_map_put((struct _u_map *)con_info->request->map_post_body, filename_param, filename) != U_OK) { y_log_message(Y_LOG_LEVEL_ERROR, "ulfius - Error u_map_put filename value"); } o_free(filename_param); } if (cur_size > 0 && data_dup != NULL && u_map_put_binary((struct _u_map *)con_info->request->map_post_body, key, data_dup, off, cur_size + 1) == U_OK) { o_free(data_dup); return MHD_YES; } else { o_free(data_dup); return MHD_NO; } } } /** * ulfius_webservice_dispatcher * function executed by libmicrohttpd every time an HTTP call is made * return MHD_NO on error */ static 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) { struct _u_endpoint * endpoint_list = ((struct _u_instance *)cls)->endpoint_list, ** current_endpoint_list = NULL, * current_endpoint = NULL; struct connection_info_struct * con_info = * con_cls; int mhd_ret = MHD_NO, callback_ret = U_OK, i, close_loop = 0, inner_error = U_OK; #ifndef U_DISABLE_WEBSOCKET int upgrade_protocol = 0; #endif char * content_type, * auth_realm = NULL; struct _u_response * response = NULL; struct sockaddr * so_client; void * response_buffer = NULL; size_t response_buffer_len = 0; // Response variables struct MHD_Response * mhd_response = NULL; // Prepare for POST or PUT input data // Initialize the input maps if (con_info == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error con_info is NULL"); return MHD_NO; } if (con_info->u_instance == NULL) { con_info->u_instance = (struct _u_instance *)cls; } if (con_info->callback_first_iteration) { con_info->callback_first_iteration = 0; so_client = MHD_get_connection_info (connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS)->client_addr; con_info->has_post_processor = 0; con_info->max_post_param_size = ((struct _u_instance *)cls)->max_post_param_size; u_map_init(&con_info->map_url_initial); con_info->request->http_protocol = o_strdup(version); con_info->request->http_verb = o_strdup(method); con_info->request->client_address = o_malloc(sizeof(struct sockaddr)); if (con_info->request->client_address == NULL || con_info->request->http_verb == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating client_address or http_verb"); return MHD_NO; } memcpy(con_info->request->client_address, so_client, sizeof(struct sockaddr)); MHD_get_connection_values (connection, MHD_HEADER_KIND, ulfius_fill_map, con_info->request->map_header); MHD_get_connection_values (connection, MHD_GET_ARGUMENT_KIND, ulfius_fill_map, &con_info->map_url_initial); MHD_get_connection_values (connection, MHD_COOKIE_KIND, ulfius_fill_map, con_info->request->map_cookie); content_type = (char*)u_map_get_case(con_info->request->map_header, ULFIUS_HTTP_HEADER_CONTENT); // Set POST Processor if content-type is properly set if (content_type != NULL && (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) || 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))) { con_info->has_post_processor = 1; con_info->post_processor = MHD_create_post_processor (connection, ULFIUS_POSTBUFFERSIZE, mhd_iterate_post_data, (void *) con_info); if (NULL == con_info->post_processor) { ulfius_clean_request_full(con_info->request); con_info->request = NULL; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating post_processor"); return MHD_NO; } } return MHD_YES; } else if (*upload_data_size != 0) { size_t body_len = con_info->request->binary_body_length + *upload_data_size, upload_data_size_current = *upload_data_size; if (((struct _u_instance *)cls)->max_post_body_size > 0 && con_info->request->binary_body_length + *upload_data_size > ((struct _u_instance *)cls)->max_post_body_size) { body_len = ((struct _u_instance *)cls)->max_post_body_size; upload_data_size_current = ((struct _u_instance *)cls)->max_post_body_size - con_info->request->binary_body_length; } if (body_len >= con_info->request->binary_body_length) { con_info->request->binary_body = o_realloc(con_info->request->binary_body, body_len); if (con_info->request->binary_body == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for con_info->request->binary_body"); return MHD_NO; } else { memcpy((char*)con_info->request->binary_body + con_info->request->binary_body_length, upload_data, upload_data_size_current); con_info->request->binary_body_length += upload_data_size_current; // Handles request binary_body const char * content_type = u_map_get_case(con_info->request->map_header, ULFIUS_HTTP_HEADER_CONTENT); if (0 == o_strncmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, content_type, strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)) || 0 == o_strncmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, content_type, strlen(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA))) { MHD_post_process (con_info->post_processor, upload_data, *upload_data_size); } *upload_data_size = 0; return MHD_YES; } } else { return MHD_YES; } } else { // Check if the endpoint has one or more matches current_endpoint_list = ulfius_endpoint_match(method, url, endpoint_list); // Set to default_endpoint if no match if ((current_endpoint_list == NULL || current_endpoint_list[0] == NULL) && ((struct _u_instance *)cls)->default_endpoint != NULL && ((struct _u_instance *)cls)->default_endpoint->callback_function != NULL) { current_endpoint_list = o_realloc(current_endpoint_list, 2*sizeof(struct _u_endpoint *)); if (current_endpoint_list != NULL) { current_endpoint_list[0] = ((struct _u_instance *)cls)->default_endpoint; current_endpoint_list[1] = NULL; } } if (current_endpoint_list != NULL && current_endpoint_list[0] != NULL) { response = o_malloc(sizeof(struct _u_response)); if (response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating response"); mhd_ret = MHD_NO; } else if (ulfius_init_response(response) != U_OK) { o_free(response); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_init_response"); mhd_ret = MHD_NO; } else { // Add default headers (if any) to the response header maps if (((struct _u_instance *)cls)->default_headers != NULL && u_map_count(((struct _u_instance *)cls)->default_headers) > 0) { u_map_clean_full(response->map_header); response->map_header = u_map_copy(((struct _u_instance *)cls)->default_headers); } // Initialize auth variables con_info->request->auth_basic_user = MHD_basic_auth_get_username_password(connection, &con_info->request->auth_basic_password); for (i=0; current_endpoint_list[i] != NULL && !close_loop; i++) { current_endpoint = current_endpoint_list[i]; u_map_empty(con_info->request->map_url); u_map_copy_into(con_info->request->map_url, &con_info->map_url_initial); if (ulfius_parse_url(url, current_endpoint, con_info->request->map_url) != U_OK) { o_free(response); y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error parsing url: ", url); mhd_ret = MHD_NO; } // Run callback function with the input parameters filled for the current callback callback_ret = current_endpoint->callback_function(con_info->request, response, current_endpoint->user_data); if (response->stream_callback != NULL) { // Call the stream_callback function to build the response binary_body // A stram_callback is always the last one mhd_response = MHD_create_response_from_callback(response->stream_size, response->stream_block_size, response->stream_callback, response->stream_user_data, response->stream_callback_free); if (mhd_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_from_callback"); mhd_ret = MHD_NO; } else if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); mhd_ret = MHD_NO; } close_loop = 1; #ifndef U_DISABLE_WEBSOCKET } else if (((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback != NULL || ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback != NULL) { // if the session is a valid websocket request, // Initiate an UPGRADE session, // then run the websocket callback functions with initialized data if (NULL != o_strcasestr(u_map_get_case(con_info->request->map_header, "upgrade"), U_WEBSOCKET_UPGRADE_VALUE) && u_map_get(con_info->request->map_header, "Sec-WebSocket-Key") != NULL && u_map_get(con_info->request->map_header, "Origin") != NULL && 0 == o_strcmp(con_info->request->http_protocol, "HTTP/1.1") && 0 == o_strcmp(u_map_get(con_info->request->map_header, "Sec-WebSocket-Version"), "13") && NULL != o_strcasestr(u_map_get_case(con_info->request->map_header, "Connection"), "Upgrade") && 0 == o_strcmp(con_info->request->http_verb, "GET")) { // Check websocket_protocol and websocket_extensions to match ours char * extensions = ulfius_check_list_match(u_map_get(con_info->request->map_header, "Sec-WebSocket-Extensions"), ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions), * protocol = ulfius_check_list_match(u_map_get(con_info->request->map_header, "Sec-WebSocket-Protocol"), ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol); if (extensions != NULL && protocol != NULL) { char websocket_accept[32] = {0}; if (ulfius_generate_handshake_answer(u_map_get(con_info->request->map_header, "Sec-WebSocket-Key"), websocket_accept)) { struct _websocket * websocket = o_malloc(sizeof(struct _websocket)); if (websocket != NULL) { websocket->request = ulfius_duplicate_request(con_info->request); if (websocket->request != NULL) { websocket->instance = (struct _u_instance *)cls; websocket->websocket_protocol = ((struct _websocket_handle *)response->websocket_handle)->websocket_protocol; websocket->websocket_extensions = ((struct _websocket_handle *)response->websocket_handle)->websocket_extensions; websocket->websocket_manager_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_callback; websocket->websocket_manager_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_manager_user_data; websocket->websocket_incoming_message_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_message_callback; websocket->websocket_incoming_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_incoming_user_data; websocket->websocket_onclose_callback = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_callback; websocket->websocket_onclose_user_data = ((struct _websocket_handle *)response->websocket_handle)->websocket_onclose_user_data; websocket->tls = 0; mhd_response = MHD_create_response_for_upgrade(ulfius_start_websocket_cb, websocket); if (mhd_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_for_upgrade"); mhd_ret = MHD_NO; } else { MHD_add_response_header (mhd_response, MHD_HTTP_HEADER_UPGRADE, U_WEBSOCKET_UPGRADE_VALUE); MHD_add_response_header (mhd_response, "Sec-WebSocket-Accept", websocket_accept); if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); mhd_ret = MHD_NO; } else { ulfius_instance_add_websocket_active((struct _u_instance *)cls, websocket); upgrade_protocol = 1; } } } else { o_free(websocket); // Error building struct _websocket, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for websocket->request"); mhd_ret = MHD_NO; } else { response_buffer_len = strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); } } } else { // Error building struct _websocket, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); } } } else { // Error building ulfius_generate_handshake_answer, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); } } } else { response->status = MHD_HTTP_BAD_REQUEST; response_buffer = o_strdup(U_WEBSOCKET_BAD_REQUEST_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); } o_free(extensions); o_free(protocol); } else { response->status = MHD_HTTP_BAD_REQUEST; response_buffer = o_strdup(U_WEBSOCKET_BAD_REQUEST_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); } close_loop = 1; #endif } else { if (callback_ret == U_CALLBACK_CONTINUE && current_endpoint_list[i+1] == NULL) { // If callback_ret is U_CALLBACK_CONTINUE but callback function is the last one on the list callback_ret = U_CALLBACK_COMPLETE; } // Test callback_ret to know what to do switch (callback_ret) { case U_CALLBACK_CONTINUE: break; case U_CALLBACK_COMPLETE: close_loop = 1; if (ulfius_get_body_from_response(response, &response_buffer, &response_buffer_len) == U_OK) { // Build the response binary_body mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); if (mhd_response == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_create_response_from_buffer"); mhd_ret = MHD_NO; } else if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); mhd_ret = MHD_NO; } } else { // Error building response, sending error 500 response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); } } break; case U_CALLBACK_UNAUTHORIZED: close_loop = 1; // Wrong credentials, send status 401 and realm value if set if (ulfius_get_body_from_response(response, &response_buffer, &response_buffer_len) == U_OK) { mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); if (ulfius_set_response_header(mhd_response, response->map_header) == -1 || ulfius_set_response_cookie(mhd_response, response) == -1) { inner_error = U_ERROR_PARAMS; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers or cookies"); response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response->binary_body = o_strdup(ULFIUS_HTTP_ERROR_BODY); response->binary_body_length = strlen(ULFIUS_HTTP_ERROR_BODY); if (response->binary_body == NULL) { inner_error = U_ERROR_MEMORY; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body"); mhd_ret = MHD_NO; } } else { inner_error = U_CALLBACK_UNAUTHORIZED; } } else { // Error building response, sending error 500 response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { inner_error = U_ERROR_MEMORY; y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); inner_error = U_CALLBACK_UNAUTHORIZED; } } if (response->auth_realm != NULL) { auth_realm = response->auth_realm; } else if (((struct _u_instance *)cls)->default_auth_realm != NULL) { auth_realm = ((struct _u_instance *)cls)->default_auth_realm; } break; case U_CALLBACK_ERROR: default: close_loop = 1; response->status = MHD_HTTP_INTERNAL_SERVER_ERROR; response_buffer = o_strdup(ULFIUS_HTTP_ERROR_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = strlen(ULFIUS_HTTP_ERROR_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); } break; } } } if (mhd_response != NULL) { if (auth_realm != NULL && inner_error == U_CALLBACK_UNAUTHORIZED) { mhd_ret = MHD_queue_basic_auth_fail_response (connection, auth_realm, mhd_response); } else if (inner_error == U_CALLBACK_UNAUTHORIZED) { mhd_ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, mhd_response); #ifndef U_DISABLE_WEBSOCKET } else if (upgrade_protocol) { mhd_ret = MHD_queue_response (connection, MHD_HTTP_SWITCHING_PROTOCOLS, mhd_response); #endif } else { mhd_ret = MHD_queue_response (connection, response->status, mhd_response); } MHD_destroy_response (mhd_response); // Free Response parameters ulfius_clean_response_full(response); response = NULL; } } } else { response_buffer = o_strdup(ULFIUS_HTTP_NOT_FOUND_BODY); if (response_buffer == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response_buffer"); mhd_ret = MHD_NO; } else { response_buffer_len = strlen(ULFIUS_HTTP_NOT_FOUND_BODY); mhd_response = MHD_create_response_from_buffer (response_buffer_len, response_buffer, MHD_RESPMEM_MUST_FREE ); mhd_ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, mhd_response); MHD_destroy_response (mhd_response); } } o_free(current_endpoint_list); return mhd_ret; } } /** * ulfius_run_mhd_daemon * Starts a mhd daemon for the specified instance * return a pointer to the mhd_daemon on success, NULL on error * */ static struct MHD_Daemon * ulfius_run_mhd_daemon(struct _u_instance * u_instance, const char * key_pem, const char * cert_pem) { unsigned int mhd_flags = MHD_USE_THREAD_PER_CONNECTION; #ifdef DEBUG mhd_flags |= MHD_USE_DEBUG; #endif #if MHD_VERSION >= 0x00095300 mhd_flags |= MHD_USE_INTERNAL_POLLING_THREAD; #endif #ifndef U_DISABLE_WEBSOCKET mhd_flags |= MHD_ALLOW_UPGRADE; #endif if (u_instance->mhd_daemon == NULL) { struct MHD_OptionItem mhd_ops[6]; // Default options mhd_ops[0].option = MHD_OPTION_NOTIFY_COMPLETED; mhd_ops[0].value = (intptr_t)mhd_request_completed; mhd_ops[0].ptr_value = NULL; mhd_ops[1].option = MHD_OPTION_SOCK_ADDR; mhd_ops[1].value = 0; mhd_ops[1].ptr_value = (void *)u_instance->bind_address; mhd_ops[2].option = MHD_OPTION_URI_LOG_CALLBACK; mhd_ops[2].value = (intptr_t)ulfius_uri_logger; mhd_ops[2].ptr_value = NULL; mhd_ops[3].option = MHD_OPTION_END; mhd_ops[3].value = 0; mhd_ops[3].ptr_value = NULL; if (key_pem != NULL && cert_pem != NULL) { // HTTPS parameters mhd_flags |= MHD_USE_SSL; mhd_ops[3].option = MHD_OPTION_HTTPS_MEM_KEY; mhd_ops[3].value = 0; mhd_ops[3].ptr_value = (void*)key_pem; mhd_ops[4].option = MHD_OPTION_HTTPS_MEM_CERT; mhd_ops[4].value = 0; mhd_ops[4].ptr_value = (void*)cert_pem; mhd_ops[5].option = MHD_OPTION_END; mhd_ops[5].value = 0; mhd_ops[5].ptr_value = NULL; } return MHD_start_daemon ( mhd_flags, u_instance->port, NULL, NULL, &ulfius_webservice_dispatcher, (void *)u_instance, MHD_OPTION_ARRAY, mhd_ops, MHD_OPTION_END ); } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error, instance already started"); return NULL; } } /** * ulfius_start_framework * Initializes the framework and run the webservice based on the parameters given * return true if no error * * 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) { // Validate u_instance and endpoint_list that there is no mistake if (ulfius_validate_instance(u_instance) == U_OK) { u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, NULL, NULL); if (u_instance->mhd_daemon == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting"); u_instance->status = U_STATUS_ERROR; return U_ERROR_LIBMHD; } else { u_instance->status = U_STATUS_RUNNING; return U_OK; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_start_framework - error input parameters"); return U_ERROR_PARAMS; } } /** * 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) { // Validate u_instance and endpoint_list that there is no mistake if (ulfius_validate_instance(u_instance) == U_OK && key_pem != NULL && cert_pem != NULL) { u_instance->mhd_daemon = ulfius_run_mhd_daemon(u_instance, key_pem, cert_pem); if (u_instance->mhd_daemon == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error MHD_start_daemon, aborting"); u_instance->status = U_STATUS_ERROR; return U_ERROR_LIBMHD; } else { u_instance->status = U_STATUS_RUNNING; return U_OK; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "ulfius_start_secure_framework - error input parameters"); return U_ERROR_PARAMS; } } /** * 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) { if (u_instance != NULL && u_instance->mhd_daemon != NULL) { #ifndef U_DISABLE_WEBSOCKET int i; // Loop in all active websockets and send close signal for (i=((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active-1; i>=0; i--) { ((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active[i]->websocket_manager->closing = 1; } pthread_mutex_lock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); while (((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active > 0) { pthread_cond_wait(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, &((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); } pthread_mutex_unlock(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock); #endif MHD_stop_daemon (u_instance->mhd_daemon); u_instance->mhd_daemon = NULL; u_instance->status = U_STATUS_STOP; return U_OK; } else { u_instance->status = U_STATUS_ERROR; return U_ERROR_PARAMS; } } /** * 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) { if (source != NULL && dest != NULL) { dest->http_method = o_strdup(source->http_method); dest->url_prefix = o_strdup(source->url_prefix); dest->url_format = o_strdup(source->url_format); dest->callback_function = source->callback_function; dest->user_data = source->user_data; dest->priority = source->priority; if (ulfius_is_valid_endpoint(dest, 0)) { return U_OK; } else { return U_ERROR_MEMORY; } } return U_ERROR_PARAMS; } /** * duplicate_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) { struct _u_endpoint * to_return = NULL; int i; if (endpoint_list != NULL) { for (i=0; endpoint_list[i].http_method != NULL; i++) { if ((to_return = o_realloc(to_return, (i+1)*sizeof(struct _u_endpoint *))) == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for duplicate_endpoint_list.to_return"); return NULL; } else { ulfius_copy_endpoint(&to_return[i], &endpoint_list[i]); } } } return to_return; } /** * clean_endpoint * free allocated memory by an endpoint */ void ulfius_clean_endpoint(struct _u_endpoint * endpoint) { if (endpoint != NULL) { o_free(endpoint->http_method); o_free(endpoint->url_prefix); o_free(endpoint->url_format); endpoint->http_method = NULL; endpoint->url_prefix = NULL; endpoint->url_format = NULL; } } /** * ulfius_clean_endpoint_list * free allocated memory by an endpoint list */ void ulfius_clean_endpoint_list(struct _u_endpoint * endpoint_list) { int i; if (endpoint_list != NULL) { for (i=0; endpoint_list[i].http_method != NULL; i++) { ulfius_clean_endpoint(&endpoint_list[i]); } o_free(endpoint_list); } } /** * 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) { int res; if (u_instance != NULL && u_endpoint != NULL) { if (ulfius_is_valid_endpoint(u_endpoint, 0)) { if (u_instance->endpoint_list == NULL) { // No endpoint, create a list with 2 endpoints so the last one is an empty one u_instance->endpoint_list = o_malloc(2 * sizeof(struct _u_endpoint)); if (u_instance->endpoint_list == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, Error allocating memory for u_instance->endpoint_list"); return U_ERROR_MEMORY; } u_instance->nb_endpoints = 1; } else { u_instance->nb_endpoints++; u_instance->endpoint_list = o_realloc(u_instance->endpoint_list, (u_instance->nb_endpoints + 1) * sizeof(struct _u_endpoint)); if (u_instance->endpoint_list == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, Error reallocating memory for u_instance->endpoint_list"); return U_ERROR_MEMORY; } } res = ulfius_copy_endpoint(&u_instance->endpoint_list[u_instance->nb_endpoints - 1], u_endpoint); if (res != U_OK) { return res; } else { // Add empty endpoint at the end of the endpoint list ulfius_copy_endpoint(&u_instance->endpoint_list[u_instance->nb_endpoints], ulfius_empty_endpoint()); } return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, invalid struct _u_endpoint"); return U_ERROR_PARAMS; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, invalid parameters"); return U_ERROR_PARAMS; } return U_ERROR; } /** * 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 a struct _u_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) { int i, res; if (u_instance != NULL && u_endpoint_list != NULL) { for (i=0; !ulfius_equals_endpoints(u_endpoint_list[i], ulfius_empty_endpoint()); i++) { res = ulfius_add_endpoint(u_instance, u_endpoint_list[i]); if (res != U_OK) { return res; } } return U_OK; } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint_list, invalid parameters"); return U_ERROR_PARAMS; } return U_ERROR; } /** * 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) { int i, j, found = 0; if (u_instance != NULL && u_endpoint != NULL && !ulfius_equals_endpoints(u_endpoint, ulfius_empty_endpoint()) && ulfius_is_valid_endpoint(u_endpoint, 1)) { for (i=0; inb_endpoints; i++) { // Compare u_endpoint with u_instance->endpoint_list[i] if ((u_endpoint->http_method != NULL && 0 == o_strcmp(u_instance->endpoint_list[i].http_method, u_endpoint->http_method)) && ((u_instance->endpoint_list[i].url_prefix == NULL && u_endpoint->url_prefix == NULL) || (u_instance->endpoint_list[i].url_prefix != NULL && u_endpoint->url_prefix != NULL && 0 == o_strcmp(u_instance->endpoint_list[i].url_prefix, u_endpoint->url_prefix))) && ((u_instance->endpoint_list[i].url_format == NULL && u_endpoint->url_format == NULL) || (u_instance->endpoint_list[i].url_format != NULL && u_endpoint->url_format != NULL && 0 == o_strcmp(u_instance->endpoint_list[i].url_format, u_endpoint->url_format)))) { // It's a match! // Remove current endpoint and move the next ones to their previous index, then reduce the endpoint_list by 1 found = 1; o_free(u_instance->endpoint_list[i].http_method); o_free(u_instance->endpoint_list[i].url_prefix); o_free(u_instance->endpoint_list[i].url_format); for (j=i; jnb_endpoints; j++) { u_instance->endpoint_list[j] = u_instance->endpoint_list[j+1]; } u_instance->nb_endpoints--; u_instance->endpoint_list = o_realloc(u_instance->endpoint_list, (u_instance->nb_endpoints + 1)*sizeof(struct _u_endpoint)); if (u_instance->endpoint_list == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_add_endpoint, Error reallocating memory for u_instance->endpoint_list"); return U_ERROR_MEMORY; } } } if (found) { return U_OK; } else { return U_ERROR_NOT_FOUND; } } else { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - ulfius_remove_endpoint, invalid parameters"); return U_ERROR_PARAMS; } return U_ERROR; } /** * ulfius_empty_endpoint * return an empty endpoint that goes at the end of an endpoint list */ const struct _u_endpoint * ulfius_empty_endpoint() { static struct _u_endpoint empty_endpoint; empty_endpoint.http_method = NULL; empty_endpoint.url_prefix = NULL; empty_endpoint.url_format = NULL; empty_endpoint.callback_function = NULL; empty_endpoint.user_data = NULL; return &empty_endpoint; } /** * 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) { if (endpoint1 != NULL && endpoint2 != NULL) { if (endpoint1 == endpoint2) { return 1; } else if (o_strcmp(endpoint2->http_method, endpoint1->http_method) != 0) { return 0; } else if (o_strcmp(endpoint2->url_prefix, endpoint1->url_prefix) != 0) { return 0; } else if (o_strcmp(endpoint2->url_format, endpoint1->url_format) != 0) { return 0; } else { return 1; } } else { return 1; } } /** * 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, unsigned int 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) { struct _u_endpoint endpoint; if (u_instance != NULL) { endpoint.http_method = (char *)http_method; endpoint.url_prefix = (char *)url_prefix; endpoint.url_format = (char *)url_format; endpoint.priority = priority; endpoint.callback_function = callback_function; endpoint.user_data = user_data; return ulfius_add_endpoint(u_instance, &endpoint); } else { return U_ERROR_PARAMS; } } /** * 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) { struct _u_endpoint endpoint; if (u_instance != NULL) { endpoint.http_method = (char *)http_method; endpoint.url_prefix = (char *)url_prefix; endpoint.url_format = (char *)url_format; endpoint.callback_function = NULL; return ulfius_remove_endpoint(u_instance, &endpoint); } else { return U_ERROR_PARAMS; } } /** * ulfius_set_default_endpoint * Set the default endpoint * This endpoint will be called if no endpoint match the url called * 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 function, 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) { if (u_instance != NULL && callback_function != NULL) { if (u_instance->default_endpoint == NULL) { u_instance->default_endpoint = o_malloc(sizeof(struct _u_endpoint)); if (u_instance->default_endpoint == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_instance->default_endpoint"); return U_ERROR_MEMORY; } } u_instance->default_endpoint->http_method = NULL; u_instance->default_endpoint->url_prefix = NULL; u_instance->default_endpoint->url_format = NULL; u_instance->default_endpoint->callback_function = callback_function; u_instance->default_endpoint->user_data = user_data; u_instance->default_endpoint->priority = 0; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_set_upload_file_callback_function * * Set the callback function to handle file upload * Used to facilitate large files upload management * The callback function file_upload_callback will be called * multiple times, with the uploaded file in striped in parts * * Warning: If this function is used, all the uploaded files * for the instance will be managed via this function, and they * will no longer be available in the struct _u_request in the * ulfius callback function afterwards. * * Thanks to Thad Phetteplace for the help on this feature * * u_instance: pointer to a struct _u_instance that describe its port and bind address * file_upload_callback: Pointer to a callback function that will handle all file uploads * cls: a pointer that will be passed to file_upload_callback each tim it's called */ int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance, int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls), void * cls) { if (u_instance != NULL && file_upload_callback != NULL) { u_instance->file_upload_callback = file_upload_callback; u_instance->file_upload_cls = cls; return U_OK; } else { return U_ERROR_PARAMS; } } /** * ulfius_clean_instance * * Clean memory allocated by a struct _u_instance * */ void ulfius_clean_instance(struct _u_instance * u_instance) { if (u_instance != NULL) { ulfius_clean_endpoint_list(u_instance->endpoint_list); u_map_clean_full(u_instance->default_headers); o_free(u_instance->default_auth_realm); o_free(u_instance->default_endpoint); u_instance->endpoint_list = NULL; u_instance->default_headers = NULL; u_instance->default_auth_realm = NULL; u_instance->bind_address = NULL; u_instance->default_endpoint = NULL; #ifndef U_DISABLE_WEBSOCKET if (((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init && (pthread_mutex_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock) || pthread_cond_destroy(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond))) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error destroying websocket_close_lock or websocket_close_cond"); } o_free(u_instance->websocket_handler); u_instance->websocket_handler = NULL; #endif } } /** * ulfius_init_instance * * Initialize a struct _u_instance * with default values * port: tcp port to bind to, must be between 1 and 65535 * bind_address: IP address to listen to, optional, the reference is borrowed, the structure isn't copied * default_auth_realm: default realm to send to the client on authentication error * return U_OK on success */ int ulfius_init_instance(struct _u_instance * u_instance, unsigned int port, struct sockaddr_in * bind_address, const char * default_auth_realm) { if (u_instance != NULL && port > 0 && port < 65536) { u_instance->mhd_daemon = NULL; u_instance->status = U_STATUS_STOP; u_instance->port = port; u_instance->bind_address = bind_address; u_instance->default_auth_realm = o_strdup(default_auth_realm); u_instance->nb_endpoints = 0; u_instance->endpoint_list = NULL; u_instance->default_headers = o_malloc(sizeof(struct _u_map)); if (u_instance->default_headers == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_instance->default_headers"); ulfius_clean_instance(u_instance); return U_ERROR_MEMORY; } u_map_init(u_instance->default_headers); u_instance->default_endpoint = NULL; u_instance->max_post_param_size = 0; u_instance->max_post_body_size = 0; u_instance->file_upload_callback = NULL; u_instance->file_upload_cls = NULL; #ifndef U_DISABLE_WEBSOCKET u_instance->websocket_handler = o_malloc(sizeof(struct _websocket_handler)); if (u_instance->websocket_handler == NULL) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for u_instance->websocket_handler"); ulfius_clean_instance(u_instance); return U_ERROR_MEMORY; } ((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init = 0; ((struct _websocket_handler *)u_instance->websocket_handler)->nb_websocket_active = 0; ((struct _websocket_handler *)u_instance->websocket_handler)->websocket_active = NULL; if (pthread_mutex_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_lock, NULL) || pthread_cond_init(&((struct _websocket_handler *)u_instance->websocket_handler)->websocket_close_cond, NULL)) { y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error initializing websocket_close_lock or websocket_close_cond"); ulfius_clean_instance(u_instance); return U_ERROR_MEMORY; } ((struct _websocket_handler *)u_instance->websocket_handler)->pthread_init = 1; #else u_instance->websocket_handler = NULL; #endif return U_OK; } else { return U_ERROR_PARAMS; } } /** * free data allocated by ulfius functions */ void u_free(void * data) { o_free(data); } ulfius-2.2.4/src/ulfius.h000066400000000000000000001243061322747500200153050ustar00rootroot00000000000000/** * * Ulfius Framework * * REST framework library * * ulfius.h: public structures and functions declarations * * Copyright 2015-2017 Nicolas Mora * * 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 . * */ #ifndef __ULFIUS_H__ #define __ULFIUS_H__ /** External dependencies **/ #include #include #include #if (MHD_VERSION < 0x00095300) && !defined(U_DISABLE_WEBSOCKET) #define U_DISABLE_WEBSOCKET #endif /** Angharad libraries **/ #include #include #ifndef U_DISABLE_JANSSON #include #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.2.4 #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; unsigned int 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; unsigned int 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; unsigned int 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; int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls); void * file_upload_cls; }; /** * Structures used to facilitate data manipulations (internal) */ struct connection_info_struct { struct _u_instance * u_instance; 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 **********************************/ /** * free data allocated by ulfius functions */ void u_free(void * data); /** * 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, unsigned int 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); /** * ulfius_set_upload_file_callback_function * * Set the callback function to handle file upload * Used to facilitate large files upload management * The callback function file_upload_callback will be called * multiple times, with the uploaded file in striped in parts * * Warning: If this function is used, all the uploaded files * for the instance will be managed via this function, and they * will no longer be available in the struct _u_request in the * ulfius callback function afterwards. * * Thanks to Thad Phetteplace for the help on this feature * * u_instance: pointer to a struct _u_instance that describe its port and bind address * file_upload_callback: Pointer to a callback function that will handle all file uploads * cls: a pointer that will be passed to file_upload_callback each tim it's called */ int ulfius_set_upload_file_callback_function(struct _u_instance * u_instance, int (* file_upload_callback) (const struct _u_request * request, const char * key, const char * filename, const char * content_type, const char * transfer_encoding, const char * data, uint64_t off, size_t size, void * cls), void * cls); /*********************************** * 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, unsigned int 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 unsigned int 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 unsigned int 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 unsigned int 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 unsigned int 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 unsigned int 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 j_body to a request * return U_OK on success */ int ulfius_set_json_body_request(struct _u_request * request, json_t * j_body); /** * ulfius_set_json_body_response * Add a json_t j_body to a response * return U_OK on success */ int ulfius_set_json_body_response(struct _u_response * response, const unsigned int status, const json_t * j_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 /** 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; }; #endif // U_DISABLE_WEBSOCKET #endif // __ULFIUS_H__ ulfius-2.2.4/test/000077500000000000000000000000001322747500200140075ustar00rootroot00000000000000ulfius-2.2.4/test/Makefile000066400000000000000000000027601322747500200154540ustar00rootroot00000000000000# # Ulfius library # # Makefile used to build the tests # # Copyright 2017 Nicolas Mora # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU GENERAL PUBLIC LICENSE # License as published by the Free Software Foundation; # version 3 of the License. # # This program 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 program. If not, see . # CC=gcc CFLAGS=-Wall -D_REENTRANT -DDEBUG -g -O0 ULFIUS_LOCATION=../src LIBS=-lc -lorcania -lyder -lulfius -lcheck -lpthread -lm -lrt -lsubunit -L$(ULFIUS_LOCATION) all: test clean: rm -f *.o u_map core framework valgrind.txt libulfius.so: cd $(ULFIUS_LOCATION) && $(MAKE) debug u_map: u_map.c $(CC) $(CFLAGS) u_map.c -o u_map $(LIBS) test_u_map: libulfius.so u_map -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./u_map core: core.c $(CC) $(CFLAGS) core.c -o core $(LIBS) test_core: libulfius.so core -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./core framework: framework.c $(CC) $(CFLAGS) framework.c -o framework $(LIBS) test_framework: libulfius.so framework -LD_LIBRARY_PATH=$(ULFIUS_LOCATION):${LD_LIBRARY_PATH} ./framework test: test_u_map test_core test_framework ulfius-2.2.4/test/core.c000066400000000000000000000227521322747500200151130ustar00rootroot00000000000000/* Public domain, no copyright. Use at your own risk. */ #include #include #include #include #include #include #include #include #include "../src/ulfius.h" int callback_function_empty(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_CONTINUE; } START_TEST(test_ulfius_init_instance) { struct _u_instance u_instance; struct sockaddr_in listen; listen.sin_family = AF_INET; listen.sin_addr.s_addr = htonl (INADDR_ANY); ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, NULL), U_OK); ulfius_clean_instance(&u_instance); ck_assert_int_eq(ulfius_init_instance(&u_instance, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 99999, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, &listen, NULL), U_OK); ulfius_clean_instance(&u_instance); ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, "test_ulfius"), U_OK); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_endpoint) { struct _u_instance u_instance; struct _u_endpoint endpoint; endpoint.http_method = "nope"; endpoint.url_prefix = NULL; endpoint.url_format = NULL; endpoint.priority = 0; endpoint.callback_function = NULL; ck_assert_int_eq(ulfius_init_instance(&u_instance, 80, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.http_method = NULL; endpoint.url_prefix = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.url_prefix = NULL; endpoint.url_format = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.callback_function = &callback_function_empty; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.url_format = NULL; endpoint.url_prefix = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.url_prefix = NULL; endpoint.http_method = "nope"; ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_ERROR_PARAMS); endpoint.http_method = o_strdup("test0"); endpoint.url_prefix = o_strdup("test0"); endpoint.url_format = o_strdup("test0"); ck_assert_int_eq(ulfius_add_endpoint(&u_instance, &endpoint), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", NULL, NULL, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", NULL, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, NULL, "nope", 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", "nope", 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", NULL, "nope", 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", "nope", NULL, 0, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "nope", NULL, NULL, 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", NULL, 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, NULL, "nope", 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, NULL, "nope", "nope", 0, &callback_function_empty, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "test1", "test1", "test1", 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "test2", NULL, "test2", 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "test3", "test3", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_remove_endpoint(&u_instance, &endpoint), U_OK); ck_assert_int_eq(ulfius_remove_endpoint(&u_instance, &endpoint), U_ERROR_NOT_FOUND); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "nope", "nope", NULL), U_ERROR_NOT_FOUND); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "nope", NULL, "nope"), U_ERROR_NOT_FOUND); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "test1", "test1", "test1"), U_OK); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "test2", NULL, "test2"), U_OK); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "test3", "test3", NULL), U_OK); ck_assert_int_eq(ulfius_set_default_endpoint(&u_instance, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_set_default_endpoint(&u_instance, &callback_function_empty, NULL), U_OK); o_free(endpoint.http_method); o_free(endpoint.url_prefix); o_free(endpoint.url_format); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_start_instance) { struct _u_instance u_instance; ck_assert_int_eq(ulfius_start_framework(NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_start_secure_framework(NULL, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", NULL, "test", 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ck_assert_int_eq(ulfius_stop_framework(&u_instance), U_OK); ck_assert_int_eq(ulfius_start_secure_framework(&u_instance, "error", "error"), U_ERROR_LIBMHD); ck_assert_int_eq(ulfius_start_secure_framework(&u_instance, "-----BEGIN PRIVATE KEY-----\ MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDr90HrswgEmln/\ rXeNqYq0boIvas5wu27hmeHDdGGKtkCWIWGAo9GUy45xqsI4mDl3bOWS+pmb/3yi\ +nhe+BmYHvEqUFo1JfUcVMxaNEbdd9REytMjKdOS+kkLf++BBRoZI/g8DggIu+Ri\ dOSypk+pUECyQxROsyCrB/FgXuKbyC4QNl7fqZxMSpzw7jsWCZiwFv4pu8kMqzDG\ 2wTl/r/4STyK4Pj2TVa/JVzbZbH7VfcjT8MdMsXvKhlmPywjbqo70Hnmt3cnakYF\ X+07ncx/5mjYYd3eSFgiNXr7WNw2rhFKtfTUcjrqSw9FDxmHFWUU76mwJyUo02N9\ ViakSoQpAgMBAAECggEBAJp/VBwdJpzM6yxqyaJpZbXpvTeKuQw6zMjN1nIBG3SV\ DAjAZnSxziGcffGSmoQvt0CoflAT4MuxJkwXrwSPcUKWz9Sis82kwq4AH6TYIaYU\ NVmtazzUwAC1+2maJJjXXFUlpfy8Oypsy4ZjfvIxzmrPbuzI2t0Ej9kr5DDzL3BL\ CWQ/U7w7y4KC0Pnq1ueIzM+UJIfvI0ldUcXHWsAnjyQzwgFBC35qDOfDTw0YUJv+\ ElfFFcGYCA+9wlQyhM/zhAWqKgZ2mwAS6WykgbSc7j4NDjlmZwf4ZuTxbDUV1kBX\ pPH21snqO42CFpw9hRUAA0W0XydCIfUhH8/6tH9enQECgYEA+rM9f6cUk3c7aLWs\ hnauVqJuyGhgCkMyF9sSxgfcs87OVLNuGgaTIfwcT/7oxAY8G7sY44cbk1ZRhh7y\ 6kf01xqiJeXxBQei1qiJxMb2gukvpeY81s2Mg9og5d9qbEhLzp8TdiRJHxLIiGwF\ xOM69CpugKN4T0Zum7EBGeSvmBECgYEA8PRG5SRTE4JwzGtLuTbMbjYTqyEjXAkB\ zo33a92znA0EXEeLCl845EUgzUkSkeN/T/uyWRjj0hrPU99UaaXHt3bc+lrDHrc7\ WFAR3QoAfFFJPIqqwiHcBDdTeAozQ8IOqFIxspl72RukuRdeQR3EdfcF9TUZyUbU\ k8SuRioggpkCgYA2scgnA3KvwYGKlKgxJc9fQ0zcGDlrw8E4BymPXsO9zs6hGAxb\ TTfoYDJlGX361kli22zQpvdTK6/ZjQL+LfiyvTLHBeWRbVsPbfGwpp+9a9ZjYVnA\ m1OeqIYo4Jc9TICNcZMzYTM6vkRVzwtrKw//mQpGsmNbGEilWvaciZHtoQKBgQDo\ FDBQtir6SJIConm9/ETtBlLtai6Xj+lYnK6qC1DaxkLj6tjF9a9jVh3g/DfRopBW\ ZnSCkpGkJcR54Up5s35ofCkdTdxPsmaLihuaje6nztc+Y8VS1LAIs41GunRkF/5s\ KzbI8kIyfAitag+Toms+v93SLwIWNo27gh3lYOANSQKBgQDIidSO3fzB+jzJh7R0\ Yy9ADWbBsLxc8u+sBdxmZBGl+l4YZWNPlQsnsafwcpJWT3le6N7Ri3iuOZw9KiGe\ QDkc7olxUZZ3pshg+cOORK6jVE8v6FeUlLnxpeAWa4C4JDawGPTOBct6bVBl5sxi\ 7GaqDcEK1TSxc4cUaiiPDNNXQA==\ -----END PRIVATE KEY-----", "-----BEGIN CERTIFICATE-----\ MIIDhTCCAm2gAwIBAgIJANrO2RnCbURLMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV\ BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\ aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzA0MjgxNTA0\ NDVaFw0xODA0MjgxNTA0NDVaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l\ LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV\ BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOv3\ QeuzCASaWf+td42pirRugi9qznC7buGZ4cN0YYq2QJYhYYCj0ZTLjnGqwjiYOXds\ 5ZL6mZv/fKL6eF74GZge8SpQWjUl9RxUzFo0Rt131ETK0yMp05L6SQt/74EFGhkj\ +DwOCAi75GJ05LKmT6lQQLJDFE6zIKsH8WBe4pvILhA2Xt+pnExKnPDuOxYJmLAW\ /im7yQyrMMbbBOX+v/hJPIrg+PZNVr8lXNtlsftV9yNPwx0yxe8qGWY/LCNuqjvQ\ eea3dydqRgVf7TudzH/maNhh3d5IWCI1evtY3DauEUq19NRyOupLD0UPGYcVZRTv\ qbAnJSjTY31WJqRKhCkCAwEAAaNQME4wHQYDVR0OBBYEFPFfmGA3jO9koBZNGNZC\ T/dZHZyHMB8GA1UdIwQYMBaAFPFfmGA3jO9koBZNGNZCT/dZHZyHMAwGA1UdEwQF\ MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIc8Yuom4vz82izNEV+9bcCvuabcVwLH\ Qgpv5Nzy/W+1hDoqfMfKNwOSdUB7jZoDaNDG1WhjKGGCLTAx4Hx+q1LwUXvu4Bs1\ woocge65bl85h10l2TxxnlT5BIJezm5r3NiZSwOK2zxxIEyL4vh+b/xqQblBEkR3\ e4/A4Ugn9Egh8GdpF4klGp4MjjpRyAVI7BDaleAhvDSfPmm7ylHJ2y7CLI9ApOQY\ glwRuTmowAZQtaSiE1Ox7QtWj858HDzzTZyFWRG/MNqQptn7AMTPJv3DivNfDNPj\ fYxFAheH3CjryHqqR9DD+d9396W8mqEaUp+plMwSjpcTDSR4rEQkUJg=\ -----END CERTIFICATE-----"), U_OK); ck_assert_int_eq(ulfius_stop_framework(&u_instance), U_OK); ulfius_clean_instance(&u_instance); } END_TEST static Suite *ulfius_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Ulfius core function tests"); tc_core = tcase_create("test_ulfius_core"); tcase_add_test(tc_core, test_ulfius_init_instance); tcase_add_test(tc_core, test_endpoint); tcase_add_test(tc_core, test_ulfius_start_instance); tcase_set_timeout(tc_core, 30); suite_add_tcase(s, tc_core); return s; } int main(int argc, char *argv[]) { int number_failed; Suite *s; SRunner *sr; //y_init_logs("Ulfius", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting Ulfius core tests"); s = ulfius_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); //y_close_logs(); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } ulfius-2.2.4/test/framework.c000066400000000000000000000572651322747500200161670ustar00rootroot00000000000000/* Public domain, no copyright. Use at your own risk. */ #include #include #include #include #include #include #include #include #include "../src/ulfius.h" /** * decode a u_map into a string */ char * print_map(const struct _u_map * map) { char * line, * to_return = NULL; const char **keys, * value; int len, i; if (map != NULL) { keys = u_map_enum_keys(map); for (i=0; keys[i] != NULL; i++) { value = u_map_get(map, keys[i]); len = snprintf(NULL, 0, "key is %s, value is %s", keys[i], value); line = o_malloc((len+1)*sizeof(char)); snprintf(line, (len+1), "key is %s, value is %s", keys[i], value); if (to_return != NULL) { len = strlen(to_return) + strlen(line) + 1; to_return = o_realloc(to_return, (len+1)*sizeof(char)); if (strlen(to_return) > 0) { strcat(to_return, "\n"); } } else { to_return = o_malloc((strlen(line) + 1)*sizeof(char)); to_return[0] = 0; } strcat(to_return, line); o_free(line); } return to_return; } else { return NULL; } } int callback_function_empty(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_CONTINUE; } int callback_function_return_url(const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_string_body_response(response, 200, request->http_url); return U_CALLBACK_CONTINUE; } int callback_function_error(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_ERROR; } int callback_function_unauthorized(const struct _u_request * request, struct _u_response * response, void * user_data) { return U_CALLBACK_UNAUTHORIZED; } int callback_function_check_auth(const struct _u_request * request, struct _u_response * response, void * user_data) { if (o_strcmp("user", request->auth_basic_user) == 0 && o_strcmp("password", request->auth_basic_password) == 0) { return U_CALLBACK_CONTINUE; } else { return U_CALLBACK_UNAUTHORIZED; } } int callback_function_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * param3, * body; if (u_map_has_key(request->map_url, "param3")) { param3 = msprintf(", param3 is %s", u_map_get(request->map_url, "param3")); } else { param3 = o_strdup(""); } body = msprintf("param1 is %s, param2 is %s%s", u_map_get(request->map_url, "param1"), u_map_get(request->map_url, "param2"), param3); ulfius_set_string_body_response(response, 200, body); o_free(body); o_free(param3); return U_CALLBACK_CONTINUE; } int callback_function_body_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * body; body = msprintf("param1 is %s, param2 is %s", u_map_get(request->map_post_body, "param1"), u_map_get(request->map_post_body, "param2")); ulfius_set_string_body_response(response, 200, body); o_free(body); return U_CALLBACK_CONTINUE; } int callback_function_header_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * body; body = msprintf("param1 is %s, param2 is %s", u_map_get(request->map_header, "param1"), u_map_get(request->map_header, "param2")); ulfius_set_string_body_response(response, 200, body); o_free(body); return U_CALLBACK_CONTINUE; } int callback_function_cookie_param(const struct _u_request * request, struct _u_response * response, void * user_data) { char * body; body = msprintf("param1 is %s", u_map_get(request->map_cookie, "param1")); ulfius_set_string_body_response(response, 200, body); ulfius_add_cookie_to_response(response, "param2", "value_cookie", NULL, 100, "localhost", "/cookie", 0, 1); o_free(body); return U_CALLBACK_CONTINUE; } int callback_function_multiple_continue(const struct _u_request * request, struct _u_response * response, void * user_data) { if (response->binary_body != NULL) { char * body = msprintf("%.*s\n%s", response->binary_body_length, (char*)response->binary_body, request->http_url); ulfius_set_string_body_response(response, 200, body); o_free(body); } else { ulfius_set_string_body_response(response, 200, request->http_url); } return U_CALLBACK_CONTINUE; } int callback_function_multiple_complete(const struct _u_request * request, struct _u_response * response, void * user_data) { if (response->binary_body != NULL) { char * body = msprintf("%.*s\n%s", response->binary_body_length, (char*)response->binary_body, request->http_url); ulfius_set_string_body_response(response, 200, body); o_free(body); } else { ulfius_set_string_body_response(response, 200, request->http_url); } return U_CALLBACK_COMPLETE; } ssize_t stream_data (void * cls, uint64_t pos, char * buf, size_t max) { usleep(100); if (pos <= 100) { snprintf(buf, max, "%s %" PRIu64 "\n", (char *)cls, pos + 1); return strlen(buf); } else { return MHD_CONTENT_READER_END_OF_STREAM; } } void free_stream_data(void * cls) { o_free(cls); } int callback_function_stream (const struct _u_request * request, struct _u_response * response, void * user_data) { ulfius_set_stream_response(response, 200, stream_data, free_stream_data, U_STREAM_SIZE_UNKOWN, 32 * 1024, o_strdup("stream test")); return U_OK; } size_t my_write_body(void * contents, size_t size, size_t nmemb, void * user_data) { ck_assert_int_eq(o_strncmp((char *)contents, "stream test ", strlen("stream test ")), 0); ck_assert_int_ne(strtol((char *)contents + strlen("stream test "), NULL, 10), 0); return size * nmemb; } START_TEST(test_ulfius_simple_endpoint) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "POST", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "PUT", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "DELETE", "empty", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "url", NULL, 0, &callback_function_return_url, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "*", "url", NULL, 0, &callback_function_return_url, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "error", NULL, 0, &callback_function_error, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "unauthorized", NULL, 0, &callback_function_unauthorized, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "check_auth", NULL, 0, &callback_function_check_auth, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/nope"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("POST"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("PUT"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("DELETE"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("OPTION"); request.http_url = o_strdup("http://localhost:8080/empty"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/url"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/url", strlen("/url")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("OPTION"); request.http_url = o_strdup("http://localhost:8080/url"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/url", strlen("/url")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("test"); request.http_url = o_strdup("http://localhost:8080/url"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/url", strlen("/url")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/error"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 500); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/unauthorized"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 401); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/check_auth"); request.auth_basic_user = o_strdup("nope"); request.auth_basic_password = o_strdup("nope"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 401); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/check_auth"); request.auth_basic_user = o_strdup("user"); request.auth_basic_password = o_strdup("password"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_parameters) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "param", "/:param1/@param2/", 0, &callback_function_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "param", "/:param1/@param2/:param3/:param3", 0, &callback_function_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "POST", "param", NULL, 0, &callback_function_body_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "header", NULL, 0, &callback_function_header_param, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "cookie", NULL, 0, &callback_function_cookie_param, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/param/value1/value2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value1, param2 is value2", strlen("param1 is value1, param2 is value2")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/param/value1/value2/value3.1/value3.2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value1, param2 is value2, param3 is value3.1,value3.2", strlen("param1 is value1, param2 is value2, param3 is value3.1,value3.2")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_verb = o_strdup("POST"); request.http_url = o_strdup("http://localhost:8080/param/"); u_map_put(request.map_post_body, "param1", "value3"); u_map_put(request.map_post_body, "param2", "value4"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value3, param2 is value4", strlen("param1 is value3, param2 is value4")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/header"); u_map_put(request.map_header, "param1", "value5"); u_map_put(request.map_header, "param2", "value6"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value5, param2 is value6", strlen("param1 is value5, param2 is value6")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/cookie"); u_map_put(request.map_cookie, "param1", "value7"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "param1 is value7", strlen("param1 is value7")), 0); ck_assert_str_eq(u_map_get(response.map_header, "Set-Cookie"), "param2=value_cookie; Max-Age=100; Domain=localhost; Path=/cookie; HttpOnly"); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_injection) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "inject1", NULL, 0, &callback_function_empty, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject1"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "inject2", NULL, 0, &callback_function_empty, NULL), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ck_assert_int_eq(ulfius_remove_endpoint_by_val(&u_instance, "GET", "inject2", NULL), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/inject2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_multiple) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "*", 0, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "/:param1/*", 1, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "/:param1/:param2/*", 2, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple", "/:param1/:param2/:param3", 3, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "*", 0, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "/:param1/*", 1, &callback_function_multiple_complete, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "/:param1/:param2/*", 2, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "multiple_complete", "/:param1/:param2/:param3", 3, &callback_function_multiple_continue, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple", strlen("/multiple")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple/value1"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple/value1\n/multiple/value1", strlen("/multiple/value1\n/multiple/value1")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple/value1/value2"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple/value1/value2\n/multiple/value1/value2\n/multiple/value1/value2", strlen("/multiple/value1/value2\n/multiple/value1/value2\n/multiple/value1/value2")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple/value1/value2/value3"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3", strlen("/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3\n/multiple/value1/value2/value3")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/nope/value1/value2/value3/value4"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 404); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/multiple_complete/value1/value2/value3"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_request(&request, &response), U_OK); ck_assert_int_eq(response.status, 200); ck_assert_int_eq(o_strncmp(response.binary_body, "/multiple_complete/value1/value2/value3\n/multiple_complete/value1/value2/value3", strlen("/multiple_complete/value1/value2/value3\n/multiple_complete/value1/value2/value3")), 0); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_clean_instance(&u_instance); } END_TEST START_TEST(test_ulfius_endpoint_stream) { struct _u_instance u_instance; struct _u_request request; struct _u_response response; ck_assert_int_eq(ulfius_init_instance(&u_instance, 8080, NULL, NULL), U_OK); ck_assert_int_eq(ulfius_add_endpoint_by_val(&u_instance, "GET", "stream", NULL, 0, &callback_function_stream, NULL), U_OK); ck_assert_int_eq(ulfius_start_framework(&u_instance), U_OK); ulfius_init_request(&request); request.http_url = o_strdup("http://localhost:8080/stream"); ulfius_init_response(&response); ck_assert_int_eq(ulfius_send_http_streaming_request(&request, &response, my_write_body, NULL), U_OK); ck_assert_int_eq(response.status, 200); ulfius_clean_request(&request); ulfius_clean_response(&response); ulfius_clean_instance(&u_instance); } END_TEST static Suite *ulfius_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Ulfius framework function tests"); tc_core = tcase_create("test_ulfius_framework"); tcase_add_test(tc_core, test_ulfius_simple_endpoint); tcase_add_test(tc_core, test_ulfius_endpoint_parameters); tcase_add_test(tc_core, test_ulfius_endpoint_injection); tcase_add_test(tc_core, test_ulfius_endpoint_multiple); tcase_add_test(tc_core, test_ulfius_endpoint_stream); tcase_set_timeout(tc_core, 30); suite_add_tcase(s, tc_core); return s; } int main(int argc, char *argv[]) { int number_failed; Suite *s; SRunner *sr; y_init_logs("Ulfius", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting Ulfius core tests"); s = ulfius_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); y_close_logs(); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } ulfius-2.2.4/test/u_map.c000066400000000000000000000167111322747500200152620ustar00rootroot00000000000000/* Public domain, no copyright. Use at your own risk. */ #include #include #include #include #include #include #include "../src/ulfius.h" START_TEST(test_u_map_init) { struct _u_map map; ck_assert_int_eq(u_map_init(&map), U_OK); ck_assert_int_eq(u_map_init(NULL), U_ERROR_PARAMS); u_map_clean(&map); } END_TEST START_TEST(test_u_map_put) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key", "value"), U_OK); ck_assert_int_eq(u_map_put(&map, "key", NULL), U_OK); ck_assert_int_eq(u_map_put(&map, NULL, "value"), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put(&map, NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put(NULL, "key", "value"), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put(&map, "key", "new_value"), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "bkey", "value", 0, o_strlen("value")), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "bkey", NULL, 0, o_strlen("value")), U_OK); ck_assert_int_eq(u_map_put_binary(&map, NULL, "value", 0, o_strlen("value")), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put_binary(&map, NULL, NULL, 0, o_strlen("value")), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put_binary(NULL, "bkey", "value", 0, o_strlen("value")), U_ERROR_PARAMS); ck_assert_int_eq(u_map_put_binary(&map, "bkey", "new_value", 0, o_strlen("new_value")), U_OK); u_map_clean(&map); } END_TEST START_TEST(test_u_map_get) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key", "value"), U_OK); ck_assert_str_eq(u_map_get(&map, "key"), "value"); ck_assert_int_eq(u_map_get_length(&map, "key"), 6); ck_assert_ptr_eq(u_map_get(&map, "nope"), NULL); ck_assert_ptr_eq(u_map_get(NULL, "key"), NULL); ck_assert_int_eq(u_map_get_length(&map, "nope"), -1); ck_assert_int_eq(u_map_get_length(NULL, "key"), -1); ck_assert_str_eq(u_map_get_case(&map, "Key"), "value"); ck_assert_int_eq(u_map_get_case_length(&map, "kEy"), 6); ck_assert_ptr_eq(u_map_get_case(&map, "noPE"), NULL); ck_assert_ptr_eq(u_map_get_case(NULL, "keY"), NULL); ck_assert_int_eq(u_map_get_case_length(&map, "noPe"), -1); ck_assert_int_eq(u_map_get_case_length(NULL, "KEy"), -1); u_map_clean(&map); } END_TEST START_TEST(test_u_map_get_case) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key", "value"), U_OK); ck_assert_str_eq(u_map_get(&map, "key"), "value"); ck_assert_int_eq(u_map_get_case_length(&map, "key"), 6); ck_assert_ptr_eq(u_map_get_case(&map, "nope"), NULL); ck_assert_ptr_eq(u_map_get_case(NULL, "key"), NULL); ck_assert_int_eq(u_map_get_case_length(&map, "nope"), -1); ck_assert_int_eq(u_map_get_case_length(NULL, "key"), -1); u_map_clean(&map); } END_TEST START_TEST(test_u_map_enum) { struct _u_map map; const char ** enum_keys, ** enum_values; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_put(&map, "key4", "value4"), U_OK); enum_keys = u_map_enum_keys(&map); enum_values = u_map_enum_values(&map); ck_assert_ptr_ne(enum_keys, NULL); ck_assert_ptr_ne(enum_values, NULL); ck_assert_int_eq(u_map_count(&map), 4); u_map_clean(&map); } END_TEST START_TEST(test_u_map_has) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "key4", "value4", 0, o_strlen("value4")), U_OK); ck_assert_int_eq(u_map_has_key(&map, "key3"), 1); ck_assert_int_eq(u_map_has_key(&map, "nope"), 0); ck_assert_int_eq(u_map_has_value(&map, "value1"), 1); ck_assert_int_eq(u_map_has_value(&map, "nope"), 0); ck_assert_int_eq(u_map_has_value_binary(&map, "value4", o_strlen("value4")), 1); ck_assert_int_eq(u_map_has_value_binary(&map, "nope", o_strlen("nope")), 0); ck_assert_int_eq(u_map_has_key_case(&map, "Key3"), 1); ck_assert_int_eq(u_map_has_key_case(&map, "Nope"), 0); ck_assert_int_eq(u_map_has_value_case(&map, "ValuE1"), 1); ck_assert_int_eq(u_map_has_value_case(&map, "nOpe"), 0); u_map_clean(&map); } END_TEST START_TEST(test_u_map_remove) { struct _u_map map; u_map_init(&map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_put(&map, "key4", "value4"), U_OK); ck_assert_int_eq(u_map_put_binary(&map, "key5", "value5", 0, o_strlen("value5")), U_OK); ck_assert_int_eq(u_map_put(&map, "key6", "value6"), U_OK); ck_assert_int_eq(u_map_put(&map, "key7", "value7"), U_OK); ck_assert_int_eq(u_map_put(&map, "key8", "value8"), U_OK); ck_assert_int_eq(u_map_remove_from_key(&map, "key1"), U_OK); ck_assert_int_eq(u_map_remove_from_key(&map, "nope"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_key_case(&map, "Key2"), U_OK); ck_assert_int_eq(u_map_remove_from_key_case(&map, "nOPe"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_value(&map, "value3"), U_OK); ck_assert_int_eq(u_map_remove_from_value(&map, "nope"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_value_case(&map, "value4"), U_OK); ck_assert_int_eq(u_map_remove_from_value_case(&map, "nOPe"), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_from_value_binary(&map, "value5", o_strlen("value5")), U_OK); ck_assert_int_eq(u_map_remove_from_value_binary(&map, "nope", o_strlen("nope")), U_ERROR_NOT_FOUND); ck_assert_int_eq(u_map_remove_at(&map, 1), U_OK); ck_assert_int_eq(u_map_remove_at(&map, 10), U_ERROR_NOT_FOUND); u_map_clean(&map); } END_TEST START_TEST(test_u_map_copy_empty) { struct _u_map map, copy_map; u_map_init(&map); u_map_init(©_map); ck_assert_int_eq(u_map_put(&map, "key1", "value1"), U_OK); ck_assert_int_eq(u_map_put(&map, "key2", "value2"), U_OK); ck_assert_int_eq(u_map_put(&map, "key3", "value3"), U_OK); ck_assert_int_eq(u_map_copy_into(©_map, &map), U_OK); ck_assert_int_eq(u_map_copy_into(NULL, &map), U_ERROR_PARAMS); ck_assert_int_eq(u_map_copy_into(©_map, NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_copy_into(NULL, NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_count(&map), 3); ck_assert_int_eq(u_map_count(©_map), 3); ck_assert_int_eq(u_map_empty(©_map), U_OK); ck_assert_int_eq(u_map_empty(NULL), U_ERROR_PARAMS); ck_assert_int_eq(u_map_count(©_map), 0); u_map_clean(&map); u_map_clean(©_map); } END_TEST static Suite *ulfius_suite(void) { Suite *s; TCase *tc_core; s = suite_create("Ulfius struct _u_map function tests"); tc_core = tcase_create("test_ulfius_u_map"); tcase_add_test(tc_core, test_u_map_init); tcase_add_test(tc_core, test_u_map_put); tcase_add_test(tc_core, test_u_map_get); tcase_add_test(tc_core, test_u_map_get_case); tcase_add_test(tc_core, test_u_map_enum); tcase_add_test(tc_core, test_u_map_has); tcase_add_test(tc_core, test_u_map_remove); tcase_add_test(tc_core, test_u_map_copy_empty); tcase_set_timeout(tc_core, 30); suite_add_tcase(s, tc_core); return s; } int main(int argc, char *argv[]) { int number_failed; Suite *s; SRunner *sr; s = ulfius_suite(); sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }