summaryrefslogtreecommitdiffstats
path: root/addons/wurfl
diff options
context:
space:
mode:
Diffstat (limited to 'addons/wurfl')
-rw-r--r--addons/wurfl/dummy/Makefile13
-rw-r--r--addons/wurfl/dummy/dummy-wurfl.c126
-rw-r--r--addons/wurfl/dummy/wurfl/wurfl.h409
-rw-r--r--addons/wurfl/wurfl.c779
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);