Hi All, Willy,

please find attached to this email the 6 patches that cover various areas
of restyling of
the WURFL device detection feature for HAProxy. All patches can be back
ported to 1.9 if necessary.
Last patch is a dummy WURFL library that can be used to build/run haproxy
compiled with the USE_WURFL option to make easier checking for any build
problem in the future.
We'll try to do the same and make sure that the module does not break
builds again as happened in the past.

-Paul

-- 
Paul Stephen Borile
Director, WURFL Product Development - Italy
Scientiamobile Inc -  paul.bor...@scientiamobile.com +39 348 7108474
Milano Office : +39 02 620227260
skype: paulstephenborile
"Reducing complexity and size must be the goal in every step."  Niklaus
Wirth
From d04f6a5d94f59c043a3819e7626981c41c6c2779 Mon Sep 17 00:00:00 2001
From: paulborile <paul.bor...@gmail.com>
Date: Thu, 18 Apr 2019 11:57:04 +0200
Subject: [PATCH 3/6] CLEANUP: wurfl: removed deprecated methods

last 2 major releases of libwurfl included a complete review of engine options with
the result of deprecating many features. The patch removes unecessary code and fixes
the documentation.
Can be backported on any version of haproxy.
---
 doc/WURFL-device-detection.txt |   9 +--
 doc/configuration.txt          |  37 ++----------
 examples/wurfl-example.cfg     |  10 +---
 src/wurfl.c                    | 106 +++------------------------------
 4 files changed, 15 insertions(+), 147 deletions(-)

diff --git a/doc/WURFL-device-detection.txt b/doc/WURFL-device-detection.txt
index c9686151..0d61db0a 100644
--- a/doc/WURFL-device-detection.txt
+++ b/doc/WURFL-device-detection.txt
@@ -23,25 +23,18 @@ These are the supported WURFL directives (see doc/configuration.txt) :
    virtual capabilities, property names we plan to use in injected headers)
 - wurfl-information-list-separator <char> (character that will be
    used to separate values in a response header, ',' by default).
-- wurfl-engine-mode <string> (Sets the WURFL engine target. You can choose
-   between "accuracy" and "performance","performance" by default)
 - wurfl-cache-size <string> (Sets the WURFL caching strategy)
 - wurfl-patch-file [<file path>] (Sets the paths to custom WURFL patch files)
 
 Sample configuration :
 
     global
-	wurfl-data-file /usr/share/wurfl/wurfl-eval.xml
+	wurfl-data-file /usr/share/wurfl/wurfl.zip
 
 	wurfl-information-list wurfl_id model_name
 
 	#wurfl-information-list-separator |
 
-	wurfl-engine-mode performance
-	#wurfl-engine-mode accuracy
-
-	## double LRU cache
-	wurfl-cache-size 100000,30000
 	## single LRU cache
 	#wurfl-cache-size 100000
 	## no cache
diff --git a/doc/configuration.txt b/doc/configuration.txt
index ccf614fd..5b99da47 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -599,9 +599,7 @@ The following keywords are supported in the "global" section :
    - wurfl-data-file
    - wurfl-information-list
    - wurfl-information-list-separator
-   - wurfl-engine-mode
    - wurfl-cache-size
-   - wurfl-useragent-priority
 
  * Performance tuning
    - max-spread-checks
@@ -1246,10 +1244,6 @@ wurfl-information-list [<capability>]*
   - wurfl_api_version           Contains a string representing the currently
                                 used Libwurfl API version.
 
-  - wurfl_engine_target         Contains a string representing the currently
-                                set WURFL Engine Target. Possible values are
-                                "HIGH_ACCURACY", "HIGH_PERFORMANCE", "INVALID".
-
   - wurfl_info                  A string containing information on the parsed
                                 wurfl.xml and its full path.
 
@@ -1258,8 +1252,6 @@ wurfl-information-list [<capability>]*
 
   - wurfl_normalized_useragent  The normalized useragent.
 
-  - wurfl_useragent_priority    The user agent priority used by WURFL.
-
   Please note that this option is only available when haproxy has been compiled
   with USE_WURFL=1.
 
@@ -1277,36 +1269,15 @@ wurfl-patch-file [<file path>]
   Please note that this option is only available when haproxy has been compiled
   with USE_WURFL=1.
 
-wurfl-engine-mode { accuracy | performance }
-  Sets the WURFL engine target. You can choose between 'accuracy' or
-  'performance' targets. In performance mode, desktop web browser detection is
-  done programmatically without referencing the WURFL data. As a result, most
-  desktop web browsers are returned as generic_web_browser WURFL ID for
-  performance. If either performance or accuracy are not defined, performance
-  mode is enabled by default.
-
-  Please note that this option is only available when haproxy has been compiled
-  with USE_WURFL=1.
-
-wurfl-cache-size <U>[,<D>]
-  Sets the WURFL caching strategy. Here <U> is the Useragent cache size, and
-  <D> is the internal device cache size. There are three possibilities here :
+wurfl-cache-size <size>
+  Sets the WURFL Useragent cache size. For faster lookups, already processed user
+  agents are kept in a LRU cache :
   - "0"     : no cache is used.
-  - <U>     : the Single LRU cache is used, the size is expressed in elements.
-  - <U>,<D> : the Double LRU cache is used, both sizes are in elements. This is
-              the highest performing option.
+  - <size>  : size of lru cache in elements.
 
   Please note that this option is only available when haproxy has been compiled
   with USE_WURFL=1.
 
-wurfl-useragent-priority { plain | sideloaded_browser }
-  Tells WURFL if it should prioritize use of the plain user agent ('plain')
-  over the default sideloaded browser user agent ('sideloaded_browser').
-
-  Please note that this option is only available when haproxy has been compiled
-  with USE_WURFL=1.
-
-
 3.2. Performance tuning
 -----------------------
 
diff --git a/examples/wurfl-example.cfg b/examples/wurfl-example.cfg
index ad651a8c..e5aa01d3 100644
--- a/examples/wurfl-example.cfg
+++ b/examples/wurfl-example.cfg
@@ -7,19 +7,11 @@
 global
 
 	# The WURFL data file
-	wurfl-data-file		/usr/share/wurfl/wurfl-eval.xml
+	wurfl-data-file		/usr/share/wurfl/wurfl.zip
 
 	# WURFL patches definition (as much as needed, patches will be applied in the same order as specified in this conf file)
 	#wurfl-patch-file	/path/to/patch1.xml;
 
-	# WURFL engine target: one of the following (default is performance)
-	wurfl-engine-mode	performance
-	#wurfl-engine-mode	accuracy
-
-	# WURFL cache: one of the following
-	## double LRU cache
-	wurfl-cache-size	100000,30000
-	## single LRU cache
 	#wurfl-cache-size	100000
 	## no cache
 	#wurfl-cache-size	0
diff --git a/src/wurfl.c b/src/wurfl.c
index bc0994a3..e05a10af 100644
--- a/src/wurfl.c
+++ b/src/wurfl.c
@@ -19,8 +19,6 @@
 static struct {
 	char *data_file; /* the WURFL data file */
 	char *cache_size; /* the WURFL cache parameters */
-	int engine_mode; /* the WURFL engine mode */
-	int useragent_priority; /* the WURFL ua priority */
 	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 */
@@ -29,8 +27,6 @@ static struct {
 } global_wurfl = {
 	.data_file = NULL,
 	.cache_size = NULL,
-	.engine_mode = -1,
-	.useragent_priority = -1,
 	.information_list_separator = ',',
 	.information_list = LIST_HEAD_INIT(global_wurfl.information_list),
 	.patch_file_list = LIST_HEAD_INIT(global_wurfl.patch_file_list),
@@ -75,11 +71,6 @@ typedef struct {
 static const char HA_WURFL_MODULE_VERSION[] = "1.0";
 static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE";
 static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE";
-static const char HA_WURFL_TARGET_ACCURACY[] = "accuracy";
-static const char HA_WURFL_TARGET_PERFORMANCE[] = "performance";
-static const char HA_WURFL_PRIORITY_PLAIN[] = "plain";
-static const char HA_WURFL_PRIORITY_SIDELOADED_BROWSER[] = "sideloaded_browser";
-static const char HA_WURFL_MIN_ENGINE_VERSION_MANDATORY[] = "1.8.0.0";
 
 static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown";
 static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability";
@@ -105,14 +96,14 @@ static const struct {
 	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},
+	{"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_useragent", ha_wurfl_get_wurfl_useragent},
-	{"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority },
+	{"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority }, // kept for backward conf file compat
 	{"wurfl_root_id", ha_wurfl_get_wurfl_root_id},
 };
 static const int HA_WURFL_PROPERTIES_NBR = 10;
@@ -166,23 +157,8 @@ static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy
                                     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;
-	}
-
-	if (!strcmp(args[1],HA_WURFL_TARGET_ACCURACY)) {
-		global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_ACCURACY;
-		return 0;
-	}
-
-	if (!strcmp(args[1],HA_WURFL_TARGET_PERFORMANCE)) {
-		global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_PERFORMANCE;
-		return 0;
-	}
-
-	memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_TARGET_PERFORMANCE, HA_WURFL_TARGET_ACCURACY);
-	return -1;
+	// kept for backward conf file compat
+	return 0;
 }
 
 static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx,
@@ -265,23 +241,9 @@ static int ha_wurfl_cfg_useragent_priority(char **args, int section_type, struct
                                            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;
-	}
-
-	if (!strcmp(args[1],HA_WURFL_PRIORITY_PLAIN)) {
-		global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT;
-		return 0;
-	}
-
-	if (!strcmp(args[1],HA_WURFL_PRIORITY_SIDELOADED_BROWSER)) {
-		global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_OVERRIDE_SIDELOADED_BROWSER_USERAGENT;
-		return 0;
-	}
-
-	memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_PRIORITY_PLAIN, HA_WURFL_PRIORITY_SIDELOADED_BROWSER);
-	return -1;
+	// this feature is deprecated, keeping only not to break compatibility
+	// with old configuration files.
+	return 0;
 }
 
 /*
@@ -393,32 +355,6 @@ static int ha_wurfl_init(void)
 
 	}
 
-	// filtering mandatory capabilities if engine version < 1.8.0.0
-	if (strcmp(wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY) < 0) {
-		wurfl_capability_enumerator_handle hmandatorycapabilityenumerator;
-		ha_wurfl_log("WURFL: Engine version %s < %s - Filtering mandatory capabilities\n", wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY);
-		hmandatorycapabilityenumerator = wurfl_get_mandatory_capability_enumerator(global_wurfl.handle);
-
-		while (wurfl_capability_enumerator_is_valid(hmandatorycapabilityenumerator)) {
-			char *name = (char *)wurfl_capability_enumerator_get_name(hmandatorycapabilityenumerator);
-
-			if (ebst_lookup(&global_wurfl.btree, name) == NULL) {
-				if (wurfl_add_requested_capability(global_wurfl.handle, name) != WURFL_OK) {
-					ha_warning("WURFL: Engine adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle));
-					send_log(NULL, LOG_WARNING, "WURFL: Adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle));
-					return ERR_WARN;
-				}
-
-				ha_wurfl_log("WURFL: Mandatory capability [%s] added\n", name);
-			} else {
-				ha_wurfl_log("WURFL: Mandatory capability [%s] already filtered\n", name);
-			}
-
-			wurfl_capability_enumerator_move_next(hmandatorycapabilityenumerator);
-		}
-
-		wurfl_capability_enumerator_destroy(hmandatorycapabilityenumerator);
-	}
 
 	// adding WURFL patches if needed
 	if (!LIST_ISEMPTY(&global_wurfl.patch_file_list)) {
@@ -457,30 +393,6 @@ static int ha_wurfl_init(void)
 		send_log(NULL, LOG_NOTICE, "WURFL: Cache set to [%s]\n", global_wurfl.cache_size);
 	}
 
-	// setting engine mode if specified in cfg, otherwise let engine choose
-	if (global_wurfl.engine_mode != -1) {
-		if (wurfl_set_engine_target(global_wurfl.handle, global_wurfl.engine_mode) != WURFL_OK ) {
-			ha_warning("WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
-			send_log(NULL, LOG_WARNING, "WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
-			return ERR_WARN;
-		}
-
-	}
-
-	send_log(NULL, LOG_NOTICE, "WURFL: Engine target set to [%s]\n", (global_wurfl.engine_mode == WURFL_ENGINE_TARGET_HIGH_PERFORMANCE) ? (HA_WURFL_TARGET_PERFORMANCE) : (HA_WURFL_TARGET_ACCURACY) );
-
-	// setting ua priority if specified in cfg, otherwise let engine choose
-	if (global_wurfl.useragent_priority != -1) {
-		if (wurfl_set_useragent_priority(global_wurfl.handle, global_wurfl.useragent_priority) != WURFL_OK ) {
-			ha_warning("WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
-			send_log(NULL, LOG_WARNING, "WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
-			return ERR_WARN;
-		}
-
-	}
-
-	send_log(NULL, LOG_NOTICE, "WURFL: Engine useragent priority set to [%s]\n", (global_wurfl.useragent_priority == WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT) ? (HA_WURFL_PRIORITY_PLAIN) : (HA_WURFL_PRIORITY_SIDELOADED_BROWSER) );
-
 	// 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));
@@ -722,7 +634,7 @@ static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_d
 
 static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle)
 {
-	return wurfl_get_engine_target_as_string(wHandle);
+	return "default";
 }
 
 static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle)
@@ -742,7 +654,7 @@ static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle
 
 static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle)
 {
-	return wurfl_get_useragent_priority_as_string(wHandle);
+	return "default";
 }
 
 // call function for WURFL properties
-- 
2.17.1

From acd533ec7d12c4d07523e76fe7067c786d0435bf Mon Sep 17 00:00:00 2001
From: paulborile <paul.bor...@gmail.com>
Date: Thu, 18 Apr 2019 12:17:47 +0200
Subject: [PATCH 4/6] DOC: wurfl: added point of contact in MAINTAINERS file

---
 MAINTAINERS | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index df7cc336..9a46d748 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -82,3 +82,7 @@ Note: every change around the locking or synchronization point will require
       approval from one of these maintainers. Problems which only appear when
       nbthread is greater than 1 and which disappear otherwise are also
       relevant.
+
+ScientiaMobile WURFL Device Detection
+Maintainer: Paul Borile, Massimiliano Bellomi <wurfl-haproxy-supp...@scientiamobile.com>
+Files: src/wurfl.c
-- 
2.17.1

From a7bf666380e94de56ef6314a8679ef7c939c81f3 Mon Sep 17 00:00:00 2001
From: paulborile <paul.bor...@gmail.com>
Date: Thu, 18 Apr 2019 12:18:54 +0200
Subject: [PATCH 5/6] MINOR: wurfl: enabled multithreading mode

Initially excluded multithreaded mode is completely supported (libwurfl is fully MT safe).
Internal tests now are run also with multithreading enabled.
---
 src/wurfl.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/wurfl.c b/src/wurfl.c
index e05a10af..118e5844 100644
--- a/src/wurfl.c
+++ b/src/wurfl.c
@@ -277,11 +277,6 @@ static int ha_wurfl_init(void)
 		return ERR_WARN;
 	}
 
-	if (global.nbthread > 1) {
-		ha_alert("WURFL: multithreading is not supported for now.\n");
-		return (ERR_FATAL | ERR_ALERT);
-	}
-
 	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));
 		send_log(NULL, LOG_WARNING, "WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
-- 
2.17.1

From 4803f2e89e9c9cbf47b15aed4613a14342d7f2e2 Mon Sep 17 00:00:00 2001
From: paulborile <paul.bor...@gmail.com>
Date: Wed, 17 Apr 2019 12:20:42 +0200
Subject: [PATCH 2/6] BUILD: wurfl: build fix for 1.9/2.0 code base

---
 src/wurfl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/wurfl.c b/src/wurfl.c
index 75dcf004..bc0994a3 100644
--- a/src/wurfl.c
+++ b/src/wurfl.c
@@ -783,7 +783,7 @@ static const char *ha_wurfl_retrieve_header(const char *header_name, const void
 	msg = &smp->strm->txn->req;
 	ctx.idx = 0;
 
-	if (http_find_full_header2(header_name, strlen(header_name), msg->chn->buf->p, idx, &ctx) == 0)
+	if (http_find_full_header2(header_name, strlen(header_name), ci_head(msg->chn), idx, &ctx) == 0)
 		return 0;
 
 	if (header_len > ctx.vlen)
-- 
2.17.1

From c7e23917f63dd91f4fb8472ad3682e1e8e5267d3 Mon Sep 17 00:00:00 2001
From: paulborile <paul.bor...@gmail.com>
Date: Tue, 9 Apr 2019 16:18:13 +0200
Subject: [PATCH 1/6] Revert "CLEANUP: wurfl: remove dead, broken and
 unmaintained code"

This reverts commit 8e5e1e7bf000f2603a085c76be118214d22c55b4.
---
 INSTALL                        |   1 +
 Makefile                       |  19 +
 doc/WURFL-device-detection.txt |  68 +++
 doc/configuration.txt          |  95 ++++
 examples/wurfl-example.cfg     |  49 ++
 src/wurfl.c                    | 800 +++++++++++++++++++++++++++++++++
 6 files changed, 1032 insertions(+)
 create mode 100644 doc/WURFL-device-detection.txt
 create mode 100644 examples/wurfl-example.cfg
 create mode 100644 src/wurfl.c

diff --git a/INSTALL b/INSTALL
index cf3f1dfb..8086d154 100644
--- a/INSTALL
+++ b/INSTALL
@@ -320,6 +320,7 @@ following files :
 
     doc/DeviceAtlas-device-detection.txt   for DeviceAtlas
     doc/51Degrees-device-detection.txt     for 51Degrees
+    doc/WURFL-device-detection.txt         for Scientiamobile WURFL
 
 
 4.9) Miscellaneous
diff --git a/Makefile b/Makefile
index c515a754..69364d75 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,7 @@
 #   USE_RT               : enable it if your system requires -lrt. Automatic on Linux.
 #   USE_DEVICEATLAS      : enable DeviceAtlas api.
 #   USE_51DEGREES        : enable third party device detection library from 51Degrees
+#   USE_WURFL            : enable WURFL detection library from Scientiamobile
 #   USE_SYSTEMD          : enable sd_notify() support.
 #
 # Options can be forced by specifying "USE_xxx=1" or can be disabled by using
@@ -752,6 +753,24 @@ BUILD_OPTIONS   += $(call ignore_implicit,USE_51DEGREES)
 OPTIONS_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB)) -lm
 endif
 
+ifneq ($(USE_WURFL),)
+# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
+# to WURFL headers and libraries if needed.
+WURFL_SRC =
+WURFL_INC = $(WURFL_SRC)
+WURFL_LIB = $(WURFL_SRC)
+OPTIONS_OBJS    += src/wurfl.o
+OPTIONS_CFLAGS  += -DUSE_WURFL $(if $(WURFL_INC),-I$(WURFL_INC))
+ifneq ($(WURFL_DEBUG),)
+OPTIONS_CFLAGS  += -DWURFL_DEBUG
+endif
+ifneq ($(WURFL_HEADER_WITH_DETAILS),)
+OPTIONS_CFLAGS  += -DWURFL_HEADER_WITH_DETAILS
+endif
+BUILD_OPTIONS   += $(call ignore_implicit,USE_WURFL)
+OPTIONS_LDFLAGS += $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
+endif
+
 ifneq ($(USE_SYSTEMD),)
 BUILD_OPTIONS   += $(call ignore_implicit,USE_SYSTEMD)
 OPTIONS_CFLAGS  += -DUSE_SYSTEMD
diff --git a/doc/WURFL-device-detection.txt b/doc/WURFL-device-detection.txt
new file mode 100644
index 00000000..c9686151
--- /dev/null
+++ b/doc/WURFL-device-detection.txt
@@ -0,0 +1,68 @@
+Scientiamobile WURFL Device Detection
+-------------------------------------
+
+You can also include WURFL for inbuilt device detection enabling attributes.
+
+WURFL is a high-performance and low-memory footprint mobile device detection
+software component that can quickly and accurately detect over 500 capabilities
+of visiting devices. It can differentiate between portable mobile devices, desktop devices,
+SmartTVs and any other types of devices on which a web browser can be installed.
+
+In order to add WURFL device detection support, you would need to download Scientiamobile
+InFuze C API and install it on your system. Refer to www.scientiamobile.com to obtain a valid
+InFuze license.
+Compile haproxy as shown :
+
+    $ make TARGET=<target> USE_WURFL=1
+
+Optionally WURFL_DEBUG=1 may be set to increase logs verbosity
+
+These are the supported WURFL directives (see doc/configuration.txt) :
+- wurfl-data-file <path to WURFL data file>
+- wurfl-information-list [<string>] (list of WURFL capabilities,
+   virtual capabilities, property names we plan to use in injected headers)
+- wurfl-information-list-separator <char> (character that will be
+   used to separate values in a response header, ',' by default).
+- wurfl-engine-mode <string> (Sets the WURFL engine target. You can choose
+   between "accuracy" and "performance","performance" by default)
+- wurfl-cache-size <string> (Sets the WURFL caching strategy)
+- wurfl-patch-file [<file path>] (Sets the paths to custom WURFL patch files)
+
+Sample configuration :
+
+    global
+	wurfl-data-file /usr/share/wurfl/wurfl-eval.xml
+
+	wurfl-information-list wurfl_id model_name
+
+	#wurfl-information-list-separator |
+
+	wurfl-engine-mode performance
+	#wurfl-engine-mode accuracy
+
+	## double LRU cache
+	wurfl-cache-size 100000,30000
+	## single LRU cache
+	#wurfl-cache-size 100000
+	## no cache
+	#wurfl-cache-size 0
+
+	#wurfl-patch-file <paths to custom patch files>
+
+    ...
+    frontend
+	bind *:8888
+	default_backend servers
+
+There are two distinct methods available to transmit the WURFL data downstream
+to the target application:
+
+All data listed in wurfl-information-list
+
+    http-request set-header X-WURFL-All %[wurfl-get-all()]
+
+A subset of data listed in wurfl-information-list
+
+    http-request set-header X-WURFL-Properties %[wurfl-get(wurfl_id,is_tablet)]
+
+Please find more information about WURFL and the detection methods at https://www.scientiamobile.com
diff --git a/doc/configuration.txt b/doc/configuration.txt
index e66bb228..ccf614fd 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -596,6 +596,12 @@ The following keywords are supported in the "global" section :
    - 51degrees-property-name-list
    - 51degrees-property-separator
    - 51degrees-cache-size
+   - wurfl-data-file
+   - wurfl-information-list
+   - wurfl-information-list-separator
+   - wurfl-engine-mode
+   - wurfl-cache-size
+   - wurfl-useragent-priority
 
  * Performance tuning
    - max-spread-checks
@@ -1211,6 +1217,95 @@ description <text>
   Please note that this option is only available when haproxy has been
   compiled with USE_51DEGREES.
 
+wurfl-data-file <file path>
+  The path of the WURFL data file to provide device detection services. The
+  file should be accessible by HAProxy with relevant permissions.
+
+  Please note that this option is only available when haproxy has been compiled
+  with USE_WURFL=1.
+
+wurfl-information-list [<capability>]*
+  A space-delimited list of WURFL capabilities, virtual capabilities, property
+  names we plan to use in injected headers. A full list of capability and
+  virtual capability names is available on the Scientiamobile website :
+
+      https://www.scientiamobile.com/wurflCapability
+
+  Valid WURFL properties are:
+  - wurfl_id                    Contains the device ID of the matched device.
+
+  - wurfl_root_id               Contains the device root ID of the matched
+                                device.
+
+  - wurfl_isdevroot             Tells if the matched device is a root device.
+                                Possible values are "TRUE" or "FALSE".
+
+  - wurfl_useragent             The original useragent coming with this
+                                particular web request.
+
+  - wurfl_api_version           Contains a string representing the currently
+                                used Libwurfl API version.
+
+  - wurfl_engine_target         Contains a string representing the currently
+                                set WURFL Engine Target. Possible values are
+                                "HIGH_ACCURACY", "HIGH_PERFORMANCE", "INVALID".
+
+  - wurfl_info                  A string containing information on the parsed
+                                wurfl.xml and its full path.
+
+  - wurfl_last_load_time        Contains the UNIX timestamp of the last time
+                                WURFL has been loaded successfully.
+
+  - wurfl_normalized_useragent  The normalized useragent.
+
+  - wurfl_useragent_priority    The user agent priority used by WURFL.
+
+  Please note that this option is only available when haproxy has been compiled
+  with USE_WURFL=1.
+
+wurfl-information-list-separator <char>
+  A char that will be used to separate values in a response header containing
+  WURFL results. If not set that a comma (',') will be used by default.
+
+  Please note that this option is only available when haproxy has been compiled
+  with USE_WURFL=1.
+
+wurfl-patch-file [<file path>]
+  A list of WURFL patch file paths. Note that patches are loaded during startup
+  thus before the chroot.
+
+  Please note that this option is only available when haproxy has been compiled
+  with USE_WURFL=1.
+
+wurfl-engine-mode { accuracy | performance }
+  Sets the WURFL engine target. You can choose between 'accuracy' or
+  'performance' targets. In performance mode, desktop web browser detection is
+  done programmatically without referencing the WURFL data. As a result, most
+  desktop web browsers are returned as generic_web_browser WURFL ID for
+  performance. If either performance or accuracy are not defined, performance
+  mode is enabled by default.
+
+  Please note that this option is only available when haproxy has been compiled
+  with USE_WURFL=1.
+
+wurfl-cache-size <U>[,<D>]
+  Sets the WURFL caching strategy. Here <U> is the Useragent cache size, and
+  <D> is the internal device cache size. There are three possibilities here :
+  - "0"     : no cache is used.
+  - <U>     : the Single LRU cache is used, the size is expressed in elements.
+  - <U>,<D> : the Double LRU cache is used, both sizes are in elements. This is
+              the highest performing option.
+
+  Please note that this option is only available when haproxy has been compiled
+  with USE_WURFL=1.
+
+wurfl-useragent-priority { plain | sideloaded_browser }
+  Tells WURFL if it should prioritize use of the plain user agent ('plain')
+  over the default sideloaded browser user agent ('sideloaded_browser').
+
+  Please note that this option is only available when haproxy has been compiled
+  with USE_WURFL=1.
+
 
 3.2. Performance tuning
 -----------------------
diff --git a/examples/wurfl-example.cfg b/examples/wurfl-example.cfg
new file mode 100644
index 00000000..ad651a8c
--- /dev/null
+++ b/examples/wurfl-example.cfg
@@ -0,0 +1,49 @@
+#
+# This is an example of how to configure HAProxy to be used with WURFL Device Detection module.
+#
+# HAProxy needs to be compiled with support for this. See README section 1.3
+#
+
+global
+
+	# The WURFL data file
+	wurfl-data-file		/usr/share/wurfl/wurfl-eval.xml
+
+	# WURFL patches definition (as much as needed, patches will be applied in the same order as specified in this conf file)
+	#wurfl-patch-file	/path/to/patch1.xml;
+
+	# WURFL engine target: one of the following (default is performance)
+	wurfl-engine-mode	performance
+	#wurfl-engine-mode	accuracy
+
+	# WURFL cache: one of the following
+	## double LRU cache
+	wurfl-cache-size	100000,30000
+	## single LRU cache
+	#wurfl-cache-size	100000
+	## no cache
+	#wurfl-cache-size	0
+
+	wurfl-information-list-separator |
+
+	# list of WURFL capabilities, virtual capabilities, property names planned to be used in injected headers
+	wurfl-information-list wurfl_id model_name
+
+defaults
+	mode http
+	timeout connect		30s
+	timeout client		30s
+	timeout server		30s
+
+frontend TheFrontend
+	bind			192.168.1.22:80
+	default_backend		TheBackend
+
+	# inject a header called X-Wurfl-All with all the WURFL informations listed in wurfl-information-list
+	http-request set-header X-Wurfl-All %[wurfl-get-all()]
+
+	# inject a header called X-WURFL-PROPERTIES with the "wurfl_id" information (should be listed in wurfl-information-list)
+	#http-request set-header X-WURFL-PROPERTIES %[wurfl-get(wurfl_id)]
+
+backend TheBackend
+	server			TheWebServer 192.168.0.40:80
diff --git a/src/wurfl.c b/src/wurfl.c
new file mode 100644
index 00000000..75dcf004
--- /dev/null
+++ b/src/wurfl.c
@@ -0,0 +1,800 @@
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <common/cfgparse.h>
+#include <common/chunk.h>
+#include <common/buffer.h>
+#include <common/errors.h>
+#include <common/initcall.h>
+#include <types/global.h>
+#include <proto/arg.h>
+#include <proto/log.h>
+#include <proto/proto_http.h>
+#include <proto/sample.h>
+#include <ebsttree.h>
+#include <ebmbtree.h>
+
+#include <wurfl/wurfl.h>
+
+static struct {
+	char *data_file; /* the WURFL data file */
+	char *cache_size; /* the WURFL cache parameters */
+	int engine_mode; /* the WURFL engine mode */
+	int useragent_priority; /* the WURFL ua priority */
+	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,
+	.engine_mode = -1,
+	.useragent_priority = -1,
+	.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, logbuf, NULL);
+}
+#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[] = "1.0";
+static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE";
+static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE";
+static const char HA_WURFL_TARGET_ACCURACY[] = "accuracy";
+static const char HA_WURFL_TARGET_PERFORMANCE[] = "performance";
+static const char HA_WURFL_PRIORITY_PLAIN[] = "plain";
+static const char HA_WURFL_PRIORITY_SIDELOADED_BROWSER[] = "sideloaded_browser";
+static const char HA_WURFL_MIN_ENGINE_VERSION_MANDATORY[] = "1.8.0.0";
+
+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},
+	{"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_useragent", ha_wurfl_get_wurfl_useragent},
+	{"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority },
+	{"wurfl_root_id", ha_wurfl_get_wurfl_root_id},
+};
+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,
+                                  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,
+                              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,
+                                    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;
+	}
+
+	if (!strcmp(args[1],HA_WURFL_TARGET_ACCURACY)) {
+		global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_ACCURACY;
+		return 0;
+	}
+
+	if (!strcmp(args[1],HA_WURFL_TARGET_PERFORMANCE)) {
+		global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_PERFORMANCE;
+		return 0;
+	}
+
+	memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_TARGET_PERFORMANCE, HA_WURFL_TARGET_ACCURACY);
+	return -1;
+}
+
+static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx,
+                                                   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,
+                                         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_ADDQ(&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,
+                                        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_ADDQ(&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,
+                                           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;
+	}
+
+	if (!strcmp(args[1],HA_WURFL_PRIORITY_PLAIN)) {
+		global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT;
+		return 0;
+	}
+
+	if (!strcmp(args[1],HA_WURFL_PRIORITY_SIDELOADED_BROWSER)) {
+		global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_OVERRIDE_SIDELOADED_BROWSER_USERAGENT;
+		return 0;
+	}
+
+	memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_PRIORITY_PLAIN, HA_WURFL_PRIORITY_SIDELOADED_BROWSER);
+	return -1;
+}
+
+/*
+ * 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;
+
+	send_log(NULL, LOG_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");
+		send_log(NULL, LOG_WARNING, "WURFL: Engine handler creation failed\n");
+		return ERR_WARN;
+	}
+
+	send_log(NULL, LOG_NOTICE, "WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() );
+
+	// set wurfl data file
+	if (global_wurfl.data_file == NULL) {
+		ha_warning("WURFL: missing wurfl-data-file parameter in global configuration\n");
+		send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-data-file parameter in global configuration\n");
+		return ERR_WARN;
+	}
+
+	if (global.nbthread > 1) {
+		ha_alert("WURFL: multithreading is not supported for now.\n");
+		return (ERR_FATAL | ERR_ALERT);
+	}
+
+	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));
+		send_log(NULL, LOG_WARNING, "WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+		return ERR_WARN;
+	}
+
+	send_log(NULL, LOG_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
+	send_log(NULL, LOG_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");
+		send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-information-list parameter in global configuration\n");
+		return ERR_WARN;
+	} else {
+		// ebtree initialization
+		global_wurfl.btree = EB_ROOT;
+
+		// checking if informations are 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;
+					ha_wurfl_log("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name);
+				} else if (wurfl_has_virtual_capability(global_wurfl.handle, wi->data.name)) {
+					wi->data.type = HA_WURFL_DATA_TYPE_VCAP;
+					ha_wurfl_log("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name);
+				} 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));
+						send_log(NULL, LOG_WARNING, "WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+						return ERR_WARN;
+					}
+
+					ha_wurfl_log("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");
+					send_log(NULL, LOG_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);
+					send_log(NULL, LOG_WARNING, "WURFL: [%s] not inserted in btree\n",wn->name);
+					return ERR_WARN;
+				}
+
+			} else {
+				ha_wurfl_log("WURFL: [%s] already loaded\n",wi->data.name);
+			}
+
+		}
+
+	}
+
+	// filtering mandatory capabilities if engine version < 1.8.0.0
+	if (strcmp(wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY) < 0) {
+		wurfl_capability_enumerator_handle hmandatorycapabilityenumerator;
+		ha_wurfl_log("WURFL: Engine version %s < %s - Filtering mandatory capabilities\n", wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY);
+		hmandatorycapabilityenumerator = wurfl_get_mandatory_capability_enumerator(global_wurfl.handle);
+
+		while (wurfl_capability_enumerator_is_valid(hmandatorycapabilityenumerator)) {
+			char *name = (char *)wurfl_capability_enumerator_get_name(hmandatorycapabilityenumerator);
+
+			if (ebst_lookup(&global_wurfl.btree, name) == NULL) {
+				if (wurfl_add_requested_capability(global_wurfl.handle, name) != WURFL_OK) {
+					ha_warning("WURFL: Engine adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle));
+					send_log(NULL, LOG_WARNING, "WURFL: Adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle));
+					return ERR_WARN;
+				}
+
+				ha_wurfl_log("WURFL: Mandatory capability [%s] added\n", name);
+			} else {
+				ha_wurfl_log("WURFL: Mandatory capability [%s] already filtered\n", name);
+			}
+
+			wurfl_capability_enumerator_move_next(hmandatorycapabilityenumerator);
+		}
+
+		wurfl_capability_enumerator_destroy(hmandatorycapabilityenumerator);
+	}
+
+	// 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));
+				send_log(NULL, LOG_WARNING, "WURFL: Adding engine patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+				return ERR_WARN;
+			}
+			send_log(NULL, LOG_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));
+			send_log(NULL, LOG_WARNING, "WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle));
+			return ERR_WARN;
+		}
+
+		send_log(NULL, LOG_NOTICE, "WURFL: Cache set to [%s]\n", global_wurfl.cache_size);
+	}
+
+	// setting engine mode if specified in cfg, otherwise let engine choose
+	if (global_wurfl.engine_mode != -1) {
+		if (wurfl_set_engine_target(global_wurfl.handle, global_wurfl.engine_mode) != WURFL_OK ) {
+			ha_warning("WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+			send_log(NULL, LOG_WARNING, "WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+			return ERR_WARN;
+		}
+
+	}
+
+	send_log(NULL, LOG_NOTICE, "WURFL: Engine target set to [%s]\n", (global_wurfl.engine_mode == WURFL_ENGINE_TARGET_HIGH_PERFORMANCE) ? (HA_WURFL_TARGET_PERFORMANCE) : (HA_WURFL_TARGET_ACCURACY) );
+
+	// setting ua priority if specified in cfg, otherwise let engine choose
+	if (global_wurfl.useragent_priority != -1) {
+		if (wurfl_set_useragent_priority(global_wurfl.handle, global_wurfl.useragent_priority) != WURFL_OK ) {
+			ha_warning("WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+			send_log(NULL, LOG_WARNING, "WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+			return ERR_WARN;
+		}
+
+	}
+
+	send_log(NULL, LOG_NOTICE, "WURFL: Engine useragent priority set to [%s]\n", (global_wurfl.useragent_priority == WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT) ? (HA_WURFL_PRIORITY_PLAIN) : (HA_WURFL_PRIORITY_SIDELOADED_BROWSER) );
+
+	// 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));
+		send_log(NULL, LOG_WARNING, "WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
+		return ERR_WARN;
+	}
+
+	send_log(NULL, LOG_NOTICE, "WURFL: Engine loaded\n");
+	send_log(NULL, LOG_NOTICE, "WURFL: Module load completed\n");
+	return 0;
+}
+
+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;
+	free(global_wurfl.data_file);
+	global_wurfl.data_file = NULL;
+	free(global_wurfl.cache_size);
+	global_wurfl.cache_size = NULL;
+
+	list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) {
+		LIST_DEL(&wi->list);
+		free(wi);
+	}
+
+	list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) {
+		LIST_DEL(&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;
+
+	ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n");
+	wh.wsmp = smp;
+	dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
+
+	if (!dHandle) {
+		ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
+		return 1;
+	}
+
+	temp = get_trash_chunk();
+	chunk_reset(temp);
+
+	list_for_each_entry(wi, &global_wurfl.information_list, list) {
+		chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
+
+		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;
+		}
+
+	}
+
+	wurfl_device_destroy(dHandle);
+	smp->data.u.str.area = temp->area;
+	smp->data.u.str.data = temp->data;
+	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;
+
+	ha_wurfl_log("WURFL: starting ha_wurfl_get\n");
+	wh.wsmp = smp;
+	dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
+
+	if (!dHandle) {
+		ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
+		return 1;
+	}
+
+	temp = get_trash_chunk();
+	chunk_reset(temp);
+
+	while (args[i].data.str.area) {
+		chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
+		node = ebst_lookup(&global_wurfl.btree, args[i].data.str.area);
+		wn = container_of(node, wurfl_data_t, nd);
+
+		if (wn) {
+
+			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;
+			}
+
+		} else {
+			ha_wurfl_log("WURFL: %s not in wurfl-information-list \n",
+				     args[i].data.str.area);
+		}
+
+		i++;
+	}
+
+	wurfl_device_destroy(dHandle);
+	smp->data.u.str.area = temp->area;
+	smp->data.u.str.data = temp->data;
+	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)
+{
+	return wurfl_device_get_root_id(dHandle);
+}
+
+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 wurfl_get_engine_target_as_string(wHandle);
+}
+
+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 wurfl_get_useragent_priority_as_string(wHandle);
+}
+
+// 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 hdr_idx *idx;
+	struct hdr_ctx ctx;
+	const struct http_msg *msg;
+	int header_len = HA_WURFL_MAX_HEADER_LENGTH;
+
+	ha_wurfl_log("WURFL: retrieve header request [%s]\n", header_name);
+	smp =  ((ha_wurfl_header_t *)wh)->wsmp;
+	idx = &smp->strm->txn->hdr_idx;
+	msg = &smp->strm->txn->req;
+	ctx.idx = 0;
+
+	if (http_find_full_header2(header_name, strlen(header_name), msg->chn->buf->p, idx, &ctx) == 0)
+		return 0;
+
+	if (header_len > ctx.vlen)
+		header_len = ctx.vlen;
+
+	strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.line + ctx.val, 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;
+}
+
+REGISTER_POST_CHECK(ha_wurfl_init);
+REGISTER_POST_DEINIT(ha_wurfl_deinit);
+REGISTER_BUILD_OPTS("Built with WURFL support.");
-- 
2.17.1

From 7b30fca2c94f799da82c685a724b0a241788131e Mon Sep 17 00:00:00 2001
From: paulborile <paul.bor...@gmail.com>
Date: Thu, 18 Apr 2019 12:31:25 +0200
Subject: [PATCH 6/6] MINOR: contrib: dummy wurfl library

This is dummy version of the Scientiamobile WURFL C API that can be used to successfully build/run haproxy
compiled with USE_WURFL=1.
It is marked as version 1.11.2.100 to distinguish it from any real version of the lib.
Run "make install" to build and install header file wurfl.h under /usr/include/wurfl/wurfl.h and libwurfl.0.1.100.so
under /usr/lib (same locations as for the real libwurfl so that in case of upgrade everything should work)
---
 contrib/wurfl/dummy-wurfl.c | 121 +++++++++++
 contrib/wurfl/makefile      |  21 ++
 contrib/wurfl/wurfl.h       | 404 ++++++++++++++++++++++++++++++++++++
 3 files changed, 546 insertions(+)
 create mode 100644 contrib/wurfl/dummy-wurfl.c
 create mode 100644 contrib/wurfl/makefile
 create mode 100644 contrib/wurfl/wurfl.h

diff --git a/contrib/wurfl/dummy-wurfl.c b/contrib/wurfl/dummy-wurfl.c
new file mode 100644
index 00000000..8c14569e
--- /dev/null
+++ b/contrib/wurfl/dummy-wurfl.c
@@ -0,0 +1,121 @@
+/*
+ * 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.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)
+{
+  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)
+{
+
+}
+const char *wurfl_device_get_normalized_useragent(wurfl_device_handle hwurfldevice)
+{
+
+}
+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/contrib/wurfl/makefile b/contrib/wurfl/makefile
new file mode 100644
index 00000000..56ee0f8c
--- /dev/null
+++ b/contrib/wurfl/makefile
@@ -0,0 +1,21 @@
+# 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: dummy-wurfl.o
+	$(CC) -fPIC -fvisibility=hidden -Wl,-Bsymbolic -Wl,--exclude-libs -Wl,ALL -shared -Wl,-soname \
+	-Wl,libwurfl.so.0 -o libwurfl.so.0.1.100 dummy-wurfl.o
+	rm -rf libwurfl.so ; ln -s libwurfl.so.0.1.10 libwurfl.so
+	rm -rf libwurfl.so.0 ; ln -s libwurfl.so.0.1.10 libwurfl.so.0
+
+install:
+	make build
+	mkdir -p /usr/include/wurfl; cp wurfl.h /usr/include/wurfl
+	cp libwurfl.so.0.1.100 /usr/lib
+	rm -rf /usr/lib/libwurfl.so ; ln -s /usr/lib/libwurfl.so.0.1.100 /usr/lib/libwurfl.so
+	rm -rf /usr/lib/libwurfl.so.0 ; ln -s /usr/lib/libwurfl.so.0.1.100 /usr/lib/libwurfl.so.0
+
+clean:
+	rm -rf *.so* *.o
diff --git a/contrib/wurfl/wurfl.h b/contrib/wurfl/wurfl.h
new file mode 100644
index 00000000..ca512dae
--- /dev/null
+++ b/contrib/wurfl/wurfl.h
@@ -0,0 +1,404 @@
+/*
+ * 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 licensed
+ * 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.
+ *
+ */
+
+#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 miliseconds : 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_
-- 
2.17.1

Reply via email to