diff options
Diffstat (limited to 'addons/wurfl')
-rw-r--r-- | addons/wurfl/dummy/Makefile | 13 | ||||
-rw-r--r-- | addons/wurfl/dummy/dummy-wurfl.c | 126 | ||||
-rw-r--r-- | addons/wurfl/dummy/wurfl/wurfl.h | 409 | ||||
-rw-r--r-- | addons/wurfl/wurfl.c | 779 |
4 files changed, 1327 insertions, 0 deletions
diff --git a/addons/wurfl/dummy/Makefile b/addons/wurfl/dummy/Makefile new file mode 100644 index 0000000..df08288 --- /dev/null +++ b/addons/wurfl/dummy/Makefile @@ -0,0 +1,13 @@ +# makefile for dummy wurfl library +# builds shared library +# installs it in /usr/lib/ with header file wurfl.h in /usr/include/wurfl +# +# install needs to be run as root + +build: libwurfl.a + +libwurfl.a: dummy-wurfl.o + ar rv $@ $< + +clean: + rm -rf *.a *.o diff --git a/addons/wurfl/dummy/dummy-wurfl.c b/addons/wurfl/dummy/dummy-wurfl.c new file mode 100644 index 0000000..0d5f068 --- /dev/null +++ b/addons/wurfl/dummy/dummy-wurfl.c @@ -0,0 +1,126 @@ +/* + * InFuze C API - HAPROXY Dummy library version of include + * + * Author : Paul Stephen Borile, Mon Apr 8, 2019 + * Copyright (c) ScientiaMobile, Inc. + * http://www.scientiamobile.com + * + * This is a dummy implementation of the wurfl C API that builds and runs + * like the normal API simply without returning device detection data + * + * + */ + +#include "wurfl/wurfl.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + +const char *wurfl_get_api_version(void) +{ + return "1.11.2.100"; // 100 indicates the dummy +} + +wurfl_handle wurfl_create(void) +{ + return (void*) 0xbeffa; +} + +void wurfl_destroy(wurfl_handle handle) +{ + return; +} + +wurfl_error wurfl_set_root(wurfl_handle hwurfl, const char* root) +{ + return WURFL_OK; +} +wurfl_error wurfl_add_patch(wurfl_handle hwurfl, const char *patch) +{ + return WURFL_OK; +} + +wurfl_error wurfl_add_requested_capability(wurfl_handle hwurfl, const char *requested_capability) +{ + return WURFL_OK; +} + +const char *wurfl_get_error_message(wurfl_handle hwurfl) +{ + return "wurfl dummy library error message"; +} + +int wurfl_has_virtual_capability(wurfl_handle hwurfl, const char *virtual_capability) +{ + return 0; +} + +wurfl_error wurfl_set_cache_provider(wurfl_handle hwurfl, wurfl_cache_provider cache_provider, const char *config) +{ + return WURFL_OK; +} + +wurfl_error wurfl_load(wurfl_handle hwurfl) +{ + return WURFL_OK; +} + +wurfl_device_handle wurfl_lookup(wurfl_handle hwurfl, wurfl_header_retrieve_callback header_retrieve_callback, const void *header_retrieve_callback_data) +{ + // call callback, on a probably existing header + const char *hvalue = header_retrieve_callback("User-Agent", header_retrieve_callback_data); + // and on a non existing one + hvalue = header_retrieve_callback("Non-Existing-Header", header_retrieve_callback_data); + (void)hvalue; + return (void *) 0xdeffa; +} + +const char *wurfl_device_get_capability(wurfl_device_handle hwurfldevice, const char *capability) +{ + return "dummy_cap_val"; +} + +const char *wurfl_device_get_virtual_capability(wurfl_device_handle hwurfldevice, const char *capability) +{ + return "dummy_vcap_val"; +} + +void wurfl_device_destroy(wurfl_device_handle handle) +{ + return; +} + +const char *wurfl_device_get_id(wurfl_device_handle hwurfldevice) +{ + return "generic_dummy_device"; +} + +const char *wurfl_device_get_root_id(wurfl_device_handle hwurfldevice) +{ + return "generic_dummy_device"; +} + +const char *wurfl_device_get_original_useragent(wurfl_device_handle hwurfldevice) +{ + return "original_useragent"; +} +const char *wurfl_device_get_normalized_useragent(wurfl_device_handle hwurfldevice) +{ + return "normalized_useragent"; +} +int wurfl_device_is_actual_device_root(wurfl_device_handle hwurfldevice) +{ + return 1; +} + +const char *wurfl_get_wurfl_info(wurfl_handle hwurfl) +{ + return "dummy wurfl info"; +} + +const char *wurfl_get_last_load_time_as_string(wurfl_handle hwurfl) +{ + return "dummy wurfl last load time"; +} + +#pragma GCC diagnostic pop diff --git a/addons/wurfl/dummy/wurfl/wurfl.h b/addons/wurfl/dummy/wurfl/wurfl.h new file mode 100644 index 0000000..7659561 --- /dev/null +++ b/addons/wurfl/dummy/wurfl/wurfl.h @@ -0,0 +1,409 @@ +/* + * InFuze C API - HAPROXY Dummy library version of include + * + * Copyright (c) ScientiaMobile, Inc. + * http://www.scientiamobile.com + * + * This software package is the property of ScientiaMobile Inc. and is distributed under + * a dual licensing scheme: + * + * 1) commercially according to a contract between the Licensee and ScientiaMobile Inc. (Licensor). + * If you represent the Licensee, please refer to the licensing agreement which has been signed + * between the two parties. If you do not represent the Licensee, you are not authorized to use + * this software in any way. + * + * 2) LGPL when used in the context of the HAProxy project with the purpose of testing compatibility + * of HAProxy with ScientiaMobile software. + * + */ + +#ifndef _WURFL_H_ +#define _WURFL_H_ + +#include <time.h> + +#if defined (__GNUC__) || defined (__clang__) +#define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define DEPRECATED __declspec(deprecated) +#else +#pragma message("WARNING: You need to implement DEPRECATED for this compiler") +#define DEPRECATED +#endif + +// WURFL error enumeration +typedef enum { + WURFL_OK = 0, //!< no error + WURFL_ERROR_INVALID_HANDLE = 1, //!< handle passed to the function is invalid + WURFL_ERROR_ALREADY_LOAD = 2, //!< wurfl_load has already been invoked on the specific wurfl_handle + WURFL_ERROR_FILE_NOT_FOUND = 3, //!< file not found during wurfl_load or remote data file update + WURFL_ERROR_UNEXPECTED_END_OF_FILE = 4, //!< unexpected end of file or parsing error during wurfl_load + WURFL_ERROR_INPUT_OUTPUT_FAILURE = 5, //!< error reading stream during wurfl_load or updater accessing local updated data file + WURFL_ERROR_DEVICE_NOT_FOUND = 6, //!< specified device is missing + WURFL_ERROR_CAPABILITY_NOT_FOUND = 7, //!< specified capability is missing + WURFL_ERROR_INVALID_CAPABILITY_VALUE = 8, //!< invalid capability value + WURFL_ERROR_VIRTUAL_CAPABILITY_NOT_FOUND = 9, //!< specified virtual capability is missing + WURFL_ERROR_CANT_LOAD_CAPABILITY_NOT_FOUND = 10, //!< specified capability is missing + WURFL_ERROR_CANT_LOAD_VIRTUAL_CAPABILITY_NOT_FOUND = 11, //!< specified virtual capability is missing + WURFL_ERROR_EMPTY_ID = 12, //!< missing id in searching device + WURFL_ERROR_CAPABILITY_GROUP_NOT_FOUND = 13, //!< specified capability is missing in its group + WURFL_ERROR_CAPABILITY_GROUP_MISMATCH = 14, //!< specified capability mismatch in its group + WURFL_ERROR_DEVICE_ALREADY_DEFINED = 15, //!< specified device is already defined + WURFL_ERROR_USERAGENT_ALREADY_DEFINED = 16, //!< specified user agent is already defined + WURFL_ERROR_DEVICE_HIERARCHY_CIRCULAR_REFERENCE = 17, //!< circular reference in device hierarchy + WURFL_ERROR_UNKNOWN = 18, //!< unknown error + WURFL_ERROR_INVALID_USERAGENT_PRIORITY = 19, //!< specified override sideloaded browser user agent configuration not valid + WURFL_ERROR_INVALID_PARAMETER = 20, //!< invalid parameter + WURFL_ERROR_INVALID_CACHE_SIZE = 21, //!< specified an invalid cache size, 0 or a negative value. + WURFL_ERROR_XML_CONSISTENCY = 22, //!< WURFL data file is out of date or wrong - some needed device_id/capability is missing + WURFL_ERROR_INTERNAL = 23, //!< internal error. If this is an updater issue, please enable and check updater log using wurfl_updater_set_log_path() + WURFL_ERROR_VIRTUAL_CAPABILITY_NOT_AVAILABLE = 24, //!< the requested virtual capability has not been licensed + WURFL_ERROR_MISSING_USERAGENT = 25, // an XML device definition without mandatory UA has been detected + WURFL_ERROR_XML_PARSE = 26, // the XML data file is malformed + WURFL_ERROR_UPDATER_INVALID_DATA_URL = 27, // updater data URL is missing or invalid (note: only .zip and .gz formats allowed) + WURFL_ERROR_UPDATER_INVALID_LICENSE = 28, // client license is invalid, expired etc + WURFL_ERROR_UPDATER_NETWORK_ERROR = 29, // updater request returned an HTTP response != 200, or SSL error, etc. Please enable and check updater log using wurfl_updater_set_log_path() + WURFL_ERROR_ENGINE_NOT_INITIALIZED = 30, // prerequisite for executing an update is that the engine has been initialized (i.e., wurfl_load() has been called) + WURFL_ERROR_UPDATER_ALREADY_RUNNING = 31, // wurfl_updater_start() can be called just once, when the updater is not running + WURFL_ERROR_UPDATER_NOT_RUNNING = 32, // wurfl_updater_stop() can be called just once, when the updater is running + WURFL_ERROR_UPDATER_TOO_MANY_REQUESTS = 33, // Updater encountered HTTP 429 error + WURFL_ERROR_UPDATER_CMDLINE_DOWNLOADER_UNAVAILABLE = 34, // Curl executable not found. Please check path, etc + WURFL_ERROR_UPDATER_TIMEDOUT = 35, // Curl operation timed out. + WURFL_ERROR_ROOT_NOT_SET = 36, // set_root() must be called before any load() / reload() and update attempt + WURFL_ERROR_WRONG_ENGINE_TARGET = 37, // set_engine_target() was called with a wrong/unrecognized parameter + // new errors added in + + WURFL_ERROR_CANNOT_FILTER_STATIC_CAP = 38, + WURFL_ENGINE_UNABLE_TO_ALLOCATE_MEMORY = 39, + WURFL_ENGINE_NOT_LOADED = 40, + WURFL_ERROR_UPDATER_CANNOT_START_THREAD = 41, + WURFL_ERROR_ENUM_EMPTY_SET = 42, + + // update when adding errors + WURFL_ERROR_LAST = 43 +} wurfl_error; + +typedef enum { + WURFL_ENGINE_TARGET_HIGH_ACCURACY = 0, + WURFL_ENGINE_TARGET_HIGH_PERFORMANCE = 1, + WURFL_ENGINE_TARGET_DEFAULT = 2, + WURFL_ENGINE_TARGET_FAST_DESKTOP_BROWSER_MATCH = 3, +} wurfl_engine_target; + +typedef enum { + WURFL_USERAGENT_PRIORITY_OVERRIDE_SIDELOADED_BROWSER_USERAGENT, + WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT, + WURFL_USERAGENT_PRIORITY_INVALID, +} wurfl_useragent_priority; + +typedef enum { + WURFL_CACHE_PROVIDER_NONE, + WURFL_CACHE_PROVIDER_LRU, + WURFL_CACHE_PROVIDER_DOUBLE_LRU, +} wurfl_cache_provider; + +typedef enum { + WURFL_MATCH_TYPE_EXACT = 0, + WURFL_MATCH_TYPE_CONCLUSIVE = 1, + WURFL_MATCH_TYPE_RECOVERY = 2, + WURFL_MATCH_TYPE_CATCHALL = 3, + WURFL_MATCH_TYPE_HIGHPERFORMANCE = 4, // deprecated. See hereunder. + WURFL_MATCH_TYPE_NONE = 5, + WURFL_MATCH_TYPE_CACHED = 6, + WURFL_MATCH_TYPE_FAST_DESKTOP_BROWSER_MATCH = 7 +} wurfl_match_type; + + +typedef enum { + WURFL_UPDATER_FREQ_DAILY = 0, + WURFL_UPDATER_FREQ_WEEKLY = 1, +} wurfl_updater_frequency; + + +#ifdef __cplusplus +extern "C" { +#endif + +// typedef struct _we_h * wurfl_handle; +// typedef struct _en_t * wurfl_enum_handle; +// typedef struct _en_t * wurfl_device_capability_enumerator_handle; +// typedef struct _en_t * wurfl_capability_enumerator_handle; +// typedef struct _en_t * wurfl_device_id_enumerator_handle; +// typedef struct _md_t * wurfl_device_handle; + +typedef void * wurfl_handle; +typedef void * wurfl_enum_handle; +typedef void * wurfl_device_capability_enumerator_handle; +typedef void * wurfl_capability_enumerator_handle; +typedef void * wurfl_device_id_enumerator_handle; +typedef void * wurfl_device_handle; + +const char *wurfl_get_api_version(void); +wurfl_handle wurfl_create(void); +void wurfl_destroy(wurfl_handle handle); + +// NEW : enable/set api logfile +wurfl_error wurfl_set_log_path(wurfl_handle hwurfl, const char *log_path); +// allow writing user stuff on logs : mesg will be prepended by a "USER LOG :" string +wurfl_error wurfl_log_print(wurfl_handle hwurfl, char *msg); + +// Errors + +const char *wurfl_get_error_message(wurfl_handle hwurfl); +wurfl_error wurfl_get_error_code(wurfl_handle hwurfl); +int wurfl_has_error_message(wurfl_handle hwurfl); +// deprecated +void wurfl_clear_error_message(wurfl_handle hwurfl); + +const char *wurfl_get_wurfl_info(wurfl_handle hwurfl); +wurfl_error wurfl_set_root(wurfl_handle hwurfl, const char* root); +wurfl_error wurfl_add_patch(wurfl_handle hwurfl, const char *patch); +wurfl_error wurfl_add_requested_capability(wurfl_handle hwurfl, const char *requested_capability); +DEPRECATED wurfl_error wurfl_set_engine_target(wurfl_handle hwurfl, wurfl_engine_target target); +DEPRECATED wurfl_engine_target wurfl_get_engine_target(wurfl_handle hwurfl); +DEPRECATED const char *wurfl_get_engine_target_as_string(wurfl_handle hwurfl); +DEPRECATED wurfl_error wurfl_set_useragent_priority(wurfl_handle hwurfl, wurfl_useragent_priority useragent_priority); +DEPRECATED wurfl_useragent_priority wurfl_get_useragent_priority(wurfl_handle hwurfl); +DEPRECATED const char *wurfl_get_useragent_priority_as_string(wurfl_handle hwurfl); +wurfl_error wurfl_set_cache_provider(wurfl_handle hwurfl, wurfl_cache_provider cache_provider, const char *config); +wurfl_error wurfl_load(wurfl_handle hwurfl); +struct tm *wurfl_get_last_load_time(wurfl_handle hwurfl); +const char *wurfl_get_last_load_time_as_string(wurfl_handle hwurfl); +int wurfl_has_capability(wurfl_handle hwurfl, const char *capability); +int wurfl_has_virtual_capability(wurfl_handle hwurfl, const char *virtual_capability); + +/* + * enumerators + */ + +/* + * new enumerators implementation + * + * a selector is used to indicate which enumerator we needed + WURFL_ENUM_VIRTUAL_CAPABILITIES, WURFL_ENUM_STATIC_CAPABILITIES, WURFL_ENUM_MANDATORY_CAPABILITIES, WURFL_ENUM_WURFLID, + */ + +typedef enum { + WURFL_ENUM_STATIC_CAPABILITIES, + WURFL_ENUM_VIRTUAL_CAPABILITIES, + WURFL_ENUM_MANDATORY_CAPABILITIES, + WURFL_ENUM_WURFLID, +} wurfl_enum_type; + +wurfl_enum_handle wurfl_enum_create(wurfl_handle, wurfl_enum_type); +const char *wurfl_enum_get_name(wurfl_enum_handle handle); +int wurfl_enum_is_valid(wurfl_enum_handle handle); +void wurfl_enum_move_next(wurfl_enum_handle handle); +void wurfl_enum_destroy(wurfl_enum_handle handle); + +/* deprecated enumerators */ +// virtual caps +//DEPRECATED wurfl_capability_enumerator_handle wurfl_get_virtual_capability_enumerator(wurfl_handle hwurfl); +wurfl_capability_enumerator_handle wurfl_get_virtual_capability_enumerator(wurfl_handle hwurfl); + +// all mandatories +//DEPRECATED wurfl_capability_enumerator_handle wurfl_get_mandatory_capability_enumerator(wurfl_handle hwurfl); +wurfl_capability_enumerator_handle wurfl_get_mandatory_capability_enumerator(wurfl_handle hwurfl); + +// all capabilities +//DEPRECATED wurfl_capability_enumerator_handle wurfl_get_capability_enumerator(wurfl_handle hwurfl); +wurfl_capability_enumerator_handle wurfl_get_capability_enumerator(wurfl_handle hwurfl); +//DEPRECATED const char *wurfl_capability_enumerator_get_name(wurfl_capability_enumerator_handle hwurflcapabilityenumeratorhandle); +const char *wurfl_capability_enumerator_get_name(wurfl_capability_enumerator_handle hwurflcapabilityenumeratorhandle); +//DEPRECATED int wurfl_capability_enumerator_is_valid(wurfl_capability_enumerator_handle handle); +int wurfl_capability_enumerator_is_valid(wurfl_capability_enumerator_handle handle); +//DEPRECATED void wurfl_capability_enumerator_move_next(wurfl_capability_enumerator_handle handle); +void wurfl_capability_enumerator_move_next(wurfl_capability_enumerator_handle handle); +//DEPRECATED void wurfl_capability_enumerator_destroy(wurfl_capability_enumerator_handle handle); +void wurfl_capability_enumerator_destroy(wurfl_capability_enumerator_handle handle); + +// device id enumerator +//DEPRECATED wurfl_device_id_enumerator_handle wurfl_get_device_id_enumerator(wurfl_handle hwurfl); +wurfl_device_id_enumerator_handle wurfl_get_device_id_enumerator(wurfl_handle hwurfl); +//DEPRECATED const char *wurfl_device_id_enumerator_get_device_id(wurfl_device_id_enumerator_handle hwurfldeviceidenumeratorhandle); +const char *wurfl_device_id_enumerator_get_device_id(wurfl_device_id_enumerator_handle hwurfldeviceidenumeratorhandle); +//DEPRECATED int wurfl_device_id_enumerator_is_valid(wurfl_device_id_enumerator_handle handle); +int wurfl_device_id_enumerator_is_valid(wurfl_device_id_enumerator_handle handle); +//DEPRECATED void wurfl_device_id_enumerator_move_next(wurfl_device_id_enumerator_handle handle); +void wurfl_device_id_enumerator_move_next(wurfl_device_id_enumerator_handle handle); +//DEPRECATED void wurfl_device_id_enumerator_destroy(wurfl_device_id_enumerator_handle handle); +void wurfl_device_id_enumerator_destroy(wurfl_device_id_enumerator_handle handle); + +/* + * deprecated device enumerators + */ + +//DEPRECATED wurfl_device_capability_enumerator_handle wurfl_device_get_capability_enumerator(wurfl_device_handle hwurfldevice); +wurfl_device_capability_enumerator_handle wurfl_device_get_capability_enumerator(wurfl_device_handle hwurfldevice); +//DEPRECATED wurfl_device_capability_enumerator_handle wurfl_device_get_virtual_capability_enumerator(wurfl_device_handle hwurfldevice); +wurfl_device_capability_enumerator_handle wurfl_device_get_virtual_capability_enumerator(wurfl_device_handle hwurfldevice); +//DEPRECATED const char *wurfl_device_capability_enumerator_get_name(wurfl_device_capability_enumerator_handle); +const char *wurfl_device_capability_enumerator_get_name(wurfl_device_capability_enumerator_handle); +//DEPRECATED int wurfl_device_capability_enumerator_is_valid(wurfl_device_capability_enumerator_handle); +int wurfl_device_capability_enumerator_is_valid(wurfl_device_capability_enumerator_handle); +//DEPRECATED void wurfl_device_capability_enumerator_move_next(wurfl_device_capability_enumerator_handle); +void wurfl_device_capability_enumerator_move_next(wurfl_device_capability_enumerator_handle); +//DEPRECATED void wurfl_device_capability_enumerator_destroy(wurfl_device_capability_enumerator_handle); +void wurfl_device_capability_enumerator_destroy(wurfl_device_capability_enumerator_handle); + +//DEPRECATED const char *wurfl_device_capability_enumerator_get_value(wurfl_device_capability_enumerator_handle); +const char *wurfl_device_capability_enumerator_get_value(wurfl_device_capability_enumerator_handle); +//DEPRECATED int wurfl_device_capability_enumerator_get_value_as_int(wurfl_device_capability_enumerator_handle hwurfldevicecapabilityenumeratorhandle); +int wurfl_device_capability_enumerator_get_value_as_int(wurfl_device_capability_enumerator_handle hwurfldevicecapabilityenumeratorhandle); +//DEPRECATED int wurfl_device_capability_enumerator_get_value_as_bool(wurfl_device_capability_enumerator_handle hwurfldevicecapabilityenumeratorhandle); +int wurfl_device_capability_enumerator_get_value_as_bool(wurfl_device_capability_enumerator_handle hwurfldevicecapabilityenumeratorhandle); + + +/* + * Device lookup methods + */ + +typedef const char *(*wurfl_header_retrieve_callback)(const char *header_name, const void *callback_data); + +wurfl_device_handle wurfl_lookup(wurfl_handle hwurfl, wurfl_header_retrieve_callback header_retrieve_callback, const void *header_retrieve_callback_data); +wurfl_device_handle wurfl_lookup_useragent(wurfl_handle hwurfl, const char *useragent); +wurfl_device_handle wurfl_get_device(wurfl_handle hwurfl, const char *deviceid); +wurfl_device_handle wurfl_get_device_with_headers(wurfl_handle hwurfl, const char *deviceid, wurfl_header_retrieve_callback header_retrieve_callback, const void *header_retrieve_callback_data); + +/* + * device related methods + */ + +const char *wurfl_device_get_id(wurfl_device_handle hwurfldevice); +const char *wurfl_device_get_root_id(wurfl_device_handle hwurfldevice); +const char *wurfl_device_get_useragent(wurfl_device_handle hwurfldevice); +const char *wurfl_device_get_original_useragent(wurfl_device_handle hwurfldevice); +const char *wurfl_device_get_normalized_useragent(wurfl_device_handle hwurfldevice); +int wurfl_device_is_actual_device_root(wurfl_device_handle hwurfldevice); +wurfl_match_type wurfl_device_get_match_type(wurfl_device_handle hwurfldevice); +const char *wurfl_device_get_matcher_name(wurfl_device_handle hwurfldevice); +const char *wurfl_device_get_bucket_matcher_name(wurfl_device_handle hwurfldevice); +void wurfl_device_destroy(wurfl_device_handle handle); + + +/* + * static capability, virtual capability methods + */ + +int wurfl_device_has_capability(wurfl_device_handle hwurfldevice, const char *capability); + +const char *wurfl_device_get_capability(wurfl_device_handle hwurfldevice, const char *capability); +int wurfl_device_get_capability_as_int(wurfl_device_handle hwurfldevice, const char *capability); +int wurfl_device_get_capability_as_bool(wurfl_device_handle hwurfldevice, const char *capability); + +int wurfl_device_has_virtual_capability(wurfl_device_handle hwurfldevice, const char *capability); + +const char *wurfl_device_get_virtual_capability(wurfl_device_handle hwurfldevice, const char *capability); +int wurfl_device_get_virtual_capability_as_int(wurfl_device_handle hwurfldevice, const char *capability); +int wurfl_device_get_virtual_capability_as_bool(wurfl_device_handle hwurfldevice, const char *capability); + +/* + * static capability, virtual capability NEW methods + */ + +const char *wurfl_device_get_static_cap(wurfl_device_handle hwdev, const char *cap, wurfl_error *err); +int wurfl_device_get_static_cap_as_int(wurfl_device_handle hwdev, const char *cap, wurfl_error *err); +int wurfl_device_get_static_cap_as_bool(wurfl_device_handle hwdev, const char *cap, wurfl_error *err); + +const char *wurfl_device_get_virtual_cap(wurfl_device_handle hwdev, const char *vcap, wurfl_error *err); +int wurfl_device_get_virtual_cap_as_int(wurfl_device_handle hwdev, const char *vcap, wurfl_error *err); +int wurfl_device_get_virtual_cap_as_bool(wurfl_device_handle hwdev, const char *vcap, wurfl_error *err); + +/* + * Updater methods + */ + +// Instruct the updater module to log to file any operation/error. If not used, the updater will not log anything. +// Returns: WURLF_OK if no errors, WURFL_ERROR_INPUT_OUTPUT_FAILURE if the log file cannot be created (no write access rights?) +// or if you try to reopen the log file anywhere else, i.e. this call can be made just once, any attempt to reopen a different log file will fail. +wurfl_error wurfl_updater_set_log_path(wurfl_handle hwurfl, const char *log_path); + +// Set remote data file URL for downloading via internal updater. Will execute various validation tests +// eventually returning WURFL_ERROR_UPDATER_XXX errors for various error conditions and logging detailed infos if +// update logger is enabled. +wurfl_error wurfl_updater_set_data_url(wurfl_handle hwurfl, const char *data_url); + +// Set the updater frequency of automatic updates. Will run a background task with given update frequency. +wurfl_error wurfl_updater_set_data_frequency(wurfl_handle hwurfl, wurfl_updater_frequency freq); + +// Set updater timeouts. +// There are two timeouts, both in milliseconds : connection timeout and operation timeout. +// The values are mapped to CURL --connect-timeout and --max-time parameters +// (after millisecs-to-secs conversion). Note that CURL sub millisecond timeouts don't work for +// lack of a way to specify decimal values for timeout to curl (using 0.05 for example fails to work +// on docker machines with "POSIX" locale installed. +// Connection timeout has a default value of 10 seconds (10000 ms) and refers only to connection phase. Passing 0 will use CURL value "no timeout used". +// Data transfer timeout has a default value of 600 seconds (600000 ms). Passing 0 will use CURL default value "no timeout used" +// So, pass 0 to either parameter to set it to "no timeout used" +// Pass -1 to either parameter to use default values (10 secs, 600 secs) +// The specified timeouts (if any) are used just in the synchronous (i.e., wurfl_updater_runonce()) API call. +// The asynchronous background updater always runs with default (CURL) timeouts (i.e., it will wait "as long as needed" for a new data file to be downloaded) +wurfl_error wurfl_updater_set_data_url_timeouts(wurfl_handle hwurfl, int connection_timeout, int data_transfer_timeout); + +// Call a synchronous update. This is a blocking call and will execute the whole process +// of downloading the new data file, checking for correctness, replacing the data file and restarting the engine. +// Will keep all old configurations (patches, cache, etc) +// Returns WURLF_OK if no errors, +// or WURFL_ERROR_UPDATER_XXX errors for various error conditions, eventually logging detailed infos if +// update logger is enabled. +wurfl_error wurfl_updater_runonce(wurfl_handle hwurfl); + +// Start the asynchronous update thread. Can be called just once when the updater is stopped; +// Subsequent/wrong calls will return WURFL_ERROR_UPDATER_ALREADY_RUNNING +// Will also return WURFL_ERROR_UPDATER_XXX errors for various initialization error conditions (see above), eventually logging detailed infos if +// update logger is enabled. +// On success will return WURLF_OK +wurfl_error wurfl_updater_start(wurfl_handle hwurfl); + +// Stop the asynchronous update thread. Can be called just once when the updater is started; +// Subsequent/wrong calls will return WURFL_ERROR_UPDATER_NOT_RUNNING. +// On success will return WURLF_OK +wurfl_error wurfl_updater_stop(wurfl_handle hwurfl); + +// Reload and reboot the engine with the given data file. Basically, the same process of a wurfl_updater_runonce but without the file download. +// Will synchronously load the new root testing for errors, restart the engine with the new data file and overwrite the old data file with the new one. +// Will keep old configuration (patches, cache, etc) +// Preconditions: wurfl_set_root() and wurfl_load() must have been called and the new root must be of the same kind (i.e, same extension) as the actual root +// You can force a reload of the actual set_root() file passing NULL as the newroot +wurfl_error wurfl_updater_reload_root(wurfl_handle hwurfl, const char *newroot); + +// Alternative API for passing headers to lookup functions + +// An opaque type representing a name/value headers map +// You can create, fill and destroy this object directly. +typedef struct _ih_h * wurfl_important_header_handle; +wurfl_important_header_handle wurfl_important_header_create(wurfl_handle); +wurfl_error wurfl_important_header_set(wurfl_important_header_handle, const char *name, const char *value); +void wurfl_important_header_destroy(wurfl_important_header_handle); + +// Alternative lookup functions using the above wurfl_important_header_handle object. +// Once called, you can destroy the wurfl_important_header_handle object. Headers values are cached internally in the wurfl_device_handle. +wurfl_device_handle wurfl_lookup_with_important_header(wurfl_handle, wurfl_important_header_handle); +wurfl_device_handle wurfl_get_device_with_important_header(wurfl_handle, const char *deviceid, wurfl_important_header_handle); + +// Enumerator of all headers that should be passed to a lookup function. Returns a null-termninated list of const char* +// +// Example usage: +// +// const char** importantHeadersNames = wurfl_get_important_header_names(); +// int i = 0; +// while (importantHeadersNames[i]) +// { +// printf("important header %i: %s\n", i, headerNames[i]); +// i++; +// } +const char **wurfl_get_important_header_names(void); + +// classic WURFL iterator version of the enumerator hereabove. +typedef void *wurfl_important_header_enumerator_handle; +wurfl_important_header_enumerator_handle wurfl_get_important_header_enumerator(wurfl_handle hwurfl); +void wurfl_important_header_enumerator_destroy(wurfl_important_header_enumerator_handle); +const char *wurfl_important_header_enumerator_get_value(wurfl_important_header_enumerator_handle); +int wurfl_important_header_enumerator_is_valid(wurfl_important_header_enumerator_handle); +void wurfl_important_header_enumerator_move_next(wurfl_important_header_enumerator_handle); + +#ifdef __cplusplus +} +#endif + +#endif // _WURFL_H_ diff --git a/addons/wurfl/wurfl.c b/addons/wurfl/wurfl.c new file mode 100644 index 0000000..4df6473 --- /dev/null +++ b/addons/wurfl/wurfl.c @@ -0,0 +1,779 @@ +#include <stdio.h> +#include <stdarg.h> + +#include <import/ebmbtree.h> +#include <import/ebsttree.h> + +#include <haproxy/api.h> +#include <haproxy/arg.h> +#include <haproxy/buf-t.h> +#include <haproxy/cfgparse.h> +#include <haproxy/chunk.h> +#include <haproxy/errors.h> +#include <haproxy/global.h> +#include <haproxy/http_ana.h> +#include <haproxy/http_fetch.h> +#include <haproxy/http_htx.h> +#include <haproxy/log.h> +#include <haproxy/sample.h> +#include <haproxy/tools.h> + +#include <wurfl/wurfl.h> + +static struct { + char *data_file; /* the WURFL data file */ + char *cache_size; /* the WURFL cache parameters */ + struct list patch_file_list; /* the list of WURFL patch file to use */ + char information_list_separator; /* the separator used in request to separate values */ + struct list information_list; /* the list of WURFL data to return into request */ + void *handle; /* the handle to WURFL engine */ + struct eb_root btree; /* btree containing info (name/type) on WURFL data to return */ +} global_wurfl = { + .data_file = NULL, + .cache_size = NULL, + .information_list_separator = ',', + .information_list = LIST_HEAD_INIT(global_wurfl.information_list), + .patch_file_list = LIST_HEAD_INIT(global_wurfl.patch_file_list), + .handle = NULL, +}; + +#ifdef WURFL_DEBUG +inline static void ha_wurfl_log(char * message, ...) +{ + char logbuf[256]; + va_list argp; + + va_start(argp, message); + vsnprintf(logbuf, sizeof(logbuf), message, argp); + va_end(argp); + send_log(NULL, LOG_NOTICE, "%s", logbuf); +} +#else +inline static void ha_wurfl_log(char * message, ...) +{ +} +#endif + +#define HA_WURFL_MAX_HEADER_LENGTH 1024 + +typedef char *(*PROP_CALLBACK_FUNC)(wurfl_handle wHandle, wurfl_device_handle dHandle); + +enum wurfl_data_type { + HA_WURFL_DATA_TYPE_UNKNOWN = 0, + HA_WURFL_DATA_TYPE_CAP = 100, + HA_WURFL_DATA_TYPE_VCAP = 200, + HA_WURFL_DATA_TYPE_PROPERTY = 300 +}; + +typedef struct { + char *name; + enum wurfl_data_type type; + PROP_CALLBACK_FUNC func_callback; + struct ebmb_node nd; +} wurfl_data_t; + +static const char HA_WURFL_MODULE_VERSION[] = "2.0"; +static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE"; +static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE"; + +static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown"; +static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability"; +static const char HA_WURFL_DATA_TYPE_VCAP_STRING[] = "virtual_capability"; +static const char HA_WURFL_DATA_TYPE_PROPERTY_STRING[] = "property"; + +static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh); +static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle); +static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle); + +// ordered property=>function map, suitable for binary search +static const struct { + const char *name; + const char *(*func)(wurfl_handle wHandle, wurfl_device_handle dHandle); +} wurfl_properties_function_map [] = { + {"wurfl_api_version", ha_wurfl_get_wurfl_api_version}, + {"wurfl_engine_target", ha_wurfl_get_wurfl_engine_target}, // kept for backward conf file compat + {"wurfl_id", ha_wurfl_get_wurfl_id }, + {"wurfl_info", ha_wurfl_get_wurfl_info }, + {"wurfl_isdevroot", ha_wurfl_get_wurfl_isdevroot}, + {"wurfl_last_load_time", ha_wurfl_get_wurfl_last_load_time}, + {"wurfl_normalized_useragent", ha_wurfl_get_wurfl_normalized_useragent}, + {"wurfl_root_id", ha_wurfl_get_wurfl_root_id}, + {"wurfl_useragent", ha_wurfl_get_wurfl_useragent}, + {"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority }, // kept for backward conf file compat +}; +static const int HA_WURFL_PROPERTIES_NBR = 10; + +typedef struct { + struct list list; + wurfl_data_t data; +} wurfl_information_t; + +typedef struct { + struct list list; + char *patch_file_path; +} wurfl_patches_t; + +typedef struct { + struct sample *wsmp; + char header_value[HA_WURFL_MAX_HEADER_LENGTH + 1]; +} ha_wurfl_header_t; + +/* + * configuration parameters parsing functions + */ +static int ha_wurfl_cfg_data_file(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + + if (*(args[1]) == 0) { + memprintf(err, "WURFL: %s expects a value.\n", args[0]); + return -1; + } + + global_wurfl.data_file = strdup(args[1]); + return 0; +} + +static int ha_wurfl_cfg_cache(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + if (*(args[1]) == 0) { + memprintf(err, "WURFL: %s expects a value.\n", args[0]); + return -1; + } + + global_wurfl.cache_size = strdup(args[1]); + return 0; +} + +static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + // kept for backward conf file compat + return 0; +} + +static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + if (*(args[1]) == 0) { + memprintf(err, "WURFL: %s expects a single character.\n", args[0]); + return -1; + } + + if (strlen(args[1]) > 1) { + memprintf(err, "WURFL: %s expects a single character, got %s.\n", args[0], args[1]); + return -1; + } + + global_wurfl.information_list_separator = *args[1]; + return 0; +} + +static int ha_wurfl_cfg_information_list(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + int argIdx = 1; + wurfl_information_t *wi; + + if (*(args[argIdx]) == 0) { + memprintf(err, "WURFL: %s expects a value.\n", args[0]); + return -1; + } + + while (*(args[argIdx])) { + wi = calloc(1, sizeof(*wi)); + + if (wi == NULL) { + memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]); + return -1; + } + + wi->data.name = strdup(args[argIdx]); + wi->data.type = HA_WURFL_DATA_TYPE_UNKNOWN; + wi->data.func_callback = NULL; + LIST_APPEND(&global_wurfl.information_list, &wi->list); + ++argIdx; + } + + return 0; +} + +static int ha_wurfl_cfg_patch_file_list(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + int argIdx = 1; + wurfl_patches_t *wp; + + if (*(args[argIdx]) == 0) { + memprintf(err, "WURFL: %s expects a value.\n", args[0]); + return -1; + } + + while (*(args[argIdx])) { + wp = calloc(1, sizeof(*wp)); + + if (wp == NULL) { + memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]); + return -1; + } + + wp->patch_file_path = strdup(args[argIdx]); + LIST_APPEND(&global_wurfl.patch_file_list, &wp->list); + ++argIdx; + } + + return 0; +} + +static int ha_wurfl_cfg_useragent_priority(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + // this feature is deprecated, keeping only not to break compatibility + // with old configuration files. + return 0; +} + +/* + * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*. + */ + +static int ha_wurfl_init(void) +{ + wurfl_information_t *wi; + wurfl_patches_t *wp; + wurfl_data_t * wn; + int wurfl_result_code = WURFL_OK; + int len; + + // wurfl-data-file not configured, WURFL is not used so don't try to + // configure it. + if (global_wurfl.data_file == NULL) + return ERR_NONE; + + ha_notice("WURFL: Loading module v.%s\n", HA_WURFL_MODULE_VERSION); + // creating WURFL handler + global_wurfl.handle = wurfl_create(); + + if (global_wurfl.handle == NULL) { + ha_warning("WURFL: Engine handler creation failed\n"); + return ERR_WARN; + } + + ha_notice("WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() ); + + // set wurfl data file + if (wurfl_set_root(global_wurfl.handle, global_wurfl.data_file) != WURFL_OK) { + ha_warning("WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); + return ERR_WARN; + } + + ha_notice("WURFL: Engine root file set to %s\n", global_wurfl.data_file); + // just a log to inform which separator char has to be used + ha_notice("WURFL: Information list separator set to '%c'\n", global_wurfl.information_list_separator); + + // load wurfl data needed ( and filter whose are supposed to be capabilities ) + if (LIST_ISEMPTY(&global_wurfl.information_list)) { + ha_warning("WURFL: missing wurfl-information-list parameter in global configuration\n"); + return ERR_WARN; + } else { + // ebtree initialization + global_wurfl.btree = EB_ROOT; + + // checking if information is valid WURFL data ( cap, vcaps, properties ) + list_for_each_entry(wi, &global_wurfl.information_list, list) { + // check if information is already loaded looking into btree + if (ebst_lookup(&global_wurfl.btree, wi->data.name) == NULL) { + if ((wi->data.func_callback = (PROP_CALLBACK_FUNC) ha_wurfl_get_property_callback(wi->data.name)) != NULL) { + wi->data.type = HA_WURFL_DATA_TYPE_PROPERTY; +#ifdef WURFL_DEBUG + ha_notice("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name); +#endif + } else if (wurfl_has_virtual_capability(global_wurfl.handle, wi->data.name)) { + wi->data.type = HA_WURFL_DATA_TYPE_VCAP; +#ifdef WURFL_DEBUG + ha_notice("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name); +#endif + } else { + // by default a cap type is assumed to be and we control it on engine load + wi->data.type = HA_WURFL_DATA_TYPE_CAP; + + if (wurfl_add_requested_capability(global_wurfl.handle, wi->data.name) != WURFL_OK) { + ha_warning("WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); + return ERR_WARN; + } + + ha_notice("WURFL: [%s] treated as wurfl capability. Will check its validity later, on engine load\n",wi->data.name); + } + + // ebtree insert here + len = strlen(wi->data.name); + + wn = malloc(sizeof(wurfl_data_t) + len + 1); + + if (wn == NULL) { + ha_warning("WURFL: Error allocating memory for information tree element.\n"); + return ERR_WARN; + } + + wn->name = wi->data.name; + wn->type = wi->data.type; + wn->func_callback = wi->data.func_callback; + memcpy(wn->nd.key, wi->data.name, len); + wn->nd.key[len] = 0; + + if (!ebst_insert(&global_wurfl.btree, &wn->nd)) { + ha_warning("WURFL: [%s] not inserted in btree\n",wn->name); + return ERR_WARN; + } + + } else { +#ifdef WURFL_DEBUG + ha_notice("WURFL: [%s] already loaded\n",wi->data.name); +#endif + } + + } + + } + + + // adding WURFL patches if needed + if (!LIST_ISEMPTY(&global_wurfl.patch_file_list)) { + + list_for_each_entry(wp, &global_wurfl.patch_file_list, list) { + if (wurfl_add_patch(global_wurfl.handle, wp->patch_file_path) != WURFL_OK) { + ha_warning("WURFL: Engine adding patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); + return ERR_WARN; + } + ha_notice("WURFL: Engine patch file added %s\n", wp->patch_file_path); + + } + + } + + // setting cache provider if specified in cfg, otherwise let engine choose + if (global_wurfl.cache_size != NULL) { + if (strpbrk(global_wurfl.cache_size, ",") != NULL) { + wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_DOUBLE_LRU, global_wurfl.cache_size) ; + } else { + if (strcmp(global_wurfl.cache_size, "0")) { + wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_LRU, global_wurfl.cache_size) ; + } else { + wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_NONE, 0); + } + + } + + if (wurfl_result_code != WURFL_OK) { + ha_warning("WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle)); + return ERR_WARN; + } + + ha_notice("WURFL: Cache set to [%s]\n", global_wurfl.cache_size); + } + + // loading WURFL engine + if (wurfl_load(global_wurfl.handle) != WURFL_OK) { + ha_warning("WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); + return ERR_WARN; + } + + ha_notice("WURFL: Engine loaded\n"); + ha_notice("WURFL: Module load completed\n"); + return ERR_NONE; +} + +static void ha_wurfl_deinit(void) +{ + wurfl_information_t *wi, *wi2; + wurfl_patches_t *wp, *wp2; + + send_log(NULL, LOG_NOTICE, "WURFL: Unloading module v.%s\n", HA_WURFL_MODULE_VERSION); + wurfl_destroy(global_wurfl.handle); + global_wurfl.handle = NULL; + ha_free(&global_wurfl.data_file); + ha_free(&global_wurfl.cache_size); + + list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) { + LIST_DELETE(&wi->list); + free(wi); + } + + list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) { + LIST_DELETE(&wp->list); + free(wp); + } + + send_log(NULL, LOG_NOTICE, "WURFL: Module unloaded\n"); +} + +static int ha_wurfl_get_all(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + wurfl_device_handle dHandle; + struct buffer *temp; + wurfl_information_t *wi; + ha_wurfl_header_t wh; + struct channel *chn; + struct htx *htx; + + ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n"); + + chn = (smp->strm ? &smp->strm->req : NULL); + htx = smp_prefetch_htx(smp, chn, NULL, 1); + if (!htx) + return 0; + + wh.wsmp = smp; + + dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh); + + temp = get_trash_chunk(); + chunk_reset(temp); + + if (!dHandle) { + ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle)); + goto wurfl_get_all_completed; + } + + list_for_each_entry(wi, &global_wurfl.information_list, list) { + + switch(wi->data.type) { + case HA_WURFL_DATA_TYPE_UNKNOWN : + ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name); +#endif + break; + case HA_WURFL_DATA_TYPE_CAP : + ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name); +#endif + chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name)); + break; + case HA_WURFL_DATA_TYPE_VCAP : + ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name); +#endif + chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name)); + break; + case HA_WURFL_DATA_TYPE_PROPERTY : + ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wi->data.name); +#endif + chunk_appendf(temp, "%s", wi->data.func_callback(global_wurfl.handle, dHandle)); + break; + } + + // append wurfl-information-list-separator + chunk_appendf(temp, "%c", global_wurfl.information_list_separator); + } + +wurfl_get_all_completed: + + wurfl_device_destroy(dHandle); + smp->data.u.str.area = temp->area; + smp->data.u.str.data = temp->data; + + // remove trailing wurfl-information-list-separator + if (temp->data) { + temp->area[temp->data] = '\0'; + --smp->data.u.str.data; + } + + smp->data.type = SMP_T_STR; + return 1; +} + +static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + wurfl_device_handle dHandle; + struct buffer *temp; + wurfl_data_t *wn = NULL; + struct ebmb_node *node; + ha_wurfl_header_t wh; + int i = 0; + struct channel *chn; + struct htx *htx; + + ha_wurfl_log("WURFL: starting ha_wurfl_get\n"); + + chn = (smp->strm ? &smp->strm->req : NULL); + htx = smp_prefetch_htx(smp, chn, NULL, 1); + if (!htx) + return 0; + + wh.wsmp = smp; + + dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh); + + temp = get_trash_chunk(); + chunk_reset(temp); + + if (!dHandle) { + ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle)); + goto wurfl_get_completed; + } + + while (args[i].data.str.area) { + node = ebst_lookup(&global_wurfl.btree, args[i].data.str.area); + + if (node) { + + wn = container_of(node, wurfl_data_t, nd); + + switch(wn->type) { + case HA_WURFL_DATA_TYPE_UNKNOWN : + ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name); +#endif + break; + case HA_WURFL_DATA_TYPE_CAP : + ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name); +#endif + chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name)); + break; + case HA_WURFL_DATA_TYPE_VCAP : + ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name); +#endif + chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name)); + break; + case HA_WURFL_DATA_TYPE_PROPERTY : + ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_STRING); +#ifdef WURFL_HEADER_WITH_DETAILS + // write WURFL property type and name before its value... + chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wn->name); +#endif + chunk_appendf(temp, "%s", wn->func_callback(global_wurfl.handle, dHandle)); + break; + } + + // append wurfl-information-list-separator + chunk_appendf(temp, "%c", global_wurfl.information_list_separator); + + } else { + ha_wurfl_log("WURFL: %s not in wurfl-information-list \n", + args[i].data.str.area); + } + + i++; + } + +wurfl_get_completed: + + wurfl_device_destroy(dHandle); + smp->data.u.str.area = temp->area; + smp->data.u.str.data = temp->data; + + // remove trailing wurfl-information-list-separator + if (temp->data) { + temp->area[temp->data] = '\0'; + --smp->data.u.str.data; + } + + smp->data.type = SMP_T_STR; + return 1; +} + +static struct cfg_kw_list wurflcfg_kws = {{ }, { + { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file }, + { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator }, + { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list }, + { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list }, + { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache }, + { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode }, + { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority }, + { 0, NULL, NULL }, + } +}; + +INITCALL1(STG_REGISTER, cfg_register_keywords, &wurflcfg_kws); + +/* Note: must not be declared <const> as its list will be overwritten */ +static struct sample_fetch_kw_list fetch_kws = {ILH, { + { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV }, + { "wurfl-get", ha_wurfl_get, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV }, + { NULL, NULL, 0, 0, 0 }, + } +}; + +INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws); + +/* Note: must not be declared <const> as its list will be overwritten */ +static struct sample_conv_kw_list conv_kws = {ILH, { + { NULL, NULL, 0, 0, 0 }, + } +}; + +INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws); + +// WURFL properties wrapper functions +static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + if (wurfl_device_get_root_id(dHandle)) + return wurfl_device_get_root_id(dHandle); + else + return ""; +} + +static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return wurfl_device_get_id(dHandle); +} + +static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + if (wurfl_device_is_actual_device_root(dHandle)) + return HA_WURFL_ISDEVROOT_TRUE; + else + return HA_WURFL_ISDEVROOT_FALSE; +} + +static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return wurfl_device_get_original_useragent(dHandle); +} + +static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return wurfl_get_api_version(); +} + +static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return "default"; +} + +static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return wurfl_get_wurfl_info(wHandle); +} + +static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return wurfl_get_last_load_time_as_string(wHandle); +} + +static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return wurfl_device_get_normalized_useragent(dHandle); +} + +static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + return "default"; +} + +// call function for WURFL properties +static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle) +{ + int position; + int begin = 0; + int end = HA_WURFL_PROPERTIES_NBR - 1; + int cond = 0; + + while(begin <= end) { + position = (begin + end) / 2; + + if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) { + ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name ); + return wurfl_properties_function_map[position].func; + } else if(cond < 0) + begin = position + 1; + else + end = position - 1; + + } + + return NULL; +} + +static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh) +{ + struct sample *smp; + struct channel *chn; + struct htx *htx; + struct http_hdr_ctx ctx; + struct ist name; + int header_len = HA_WURFL_MAX_HEADER_LENGTH; + + smp = ((ha_wurfl_header_t *)wh)->wsmp; + chn = (smp->strm ? &smp->strm->req : NULL); + + ha_wurfl_log("WURFL: retrieve header (HTX) request [%s]\n", header_name); + + //the header is searched from the beginning + ctx.blk = NULL; + + // We could skip this check since ha_wurfl_retrieve_header is called from inside + // ha_wurfl_get()/ha_wurfl_get_all() that already perform the same check + // We choose to keep it in case ha_wurfl_retrieve_header will be called directly + htx = smp_prefetch_htx(smp, chn, NULL, 1); + if (!htx) { + return NULL; + } + + name = ist2((char *)header_name, strlen(header_name)); + + // If 4th param is set, it works on full-line headers in whose comma is not a delimiter but is + // part of the syntax + if (!http_find_header(htx, name, &ctx, 1)) { + return NULL; + } + + if (header_len > ctx.value.len) + header_len = ctx.value.len; + + strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.value.ptr, header_len); + + ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0'; + + ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value); + return ((ha_wurfl_header_t *)wh)->header_value; +} + +static void ha_wurfl_register_build_options() +{ + const char *ver = wurfl_get_api_version(); + char *ptr = NULL; + + memprintf(&ptr, "Built with WURFL support (%sversion %s)", + strcmp(ver, "1.11.2.100") ? "" : "dummy library ", + ver); + hap_register_build_opts(ptr, 1); +} + +REGISTER_POST_CHECK(ha_wurfl_init); +REGISTER_POST_DEINIT(ha_wurfl_deinit); +INITCALL0(STG_REGISTER, ha_wurfl_register_build_options); |