CLEANUP: wurfl: remove dead, broken and unmaintained code

Since the "wurfl" device detection engine was merged slightly more than
two years ago (2016-11-04), it never received a single fix nor update.
For almost two years it didn't receive even the minimal review or changes
needed to be compatible with threads, and it's remained build-broken for
about the last 9 months, consecutive to the last buffer API changes,
without anyone ever noticing! When asked on the list, nobody confirmed
using it :

   https://www.mail-archive.com/haproxy@formilux.org/msg32516.html

And obviously nobody even cared to verify that it did still build. So we
are left with this broken code with no user and no maintainer. It might
even suffer from remotely exploitable vulnerabilities without anyone
being able to check if it presents any risk. It's a pain to update each
time there is an API change because it doesn't build as it depends on
external libraries that are not publicly accessible, leading to careful
blind changes. It slows down the whole project. This situation is not
acceptable at all.

It's time to cure the problem where it is. This patch removes all this
dead, non-buildable, non-working code. If anyone ever decides to use it,
which I seriously doubt based on history, it could be reintegrated, but
this time the following guarantees will be required :
  - someone has to step up as a maintainer and have his name listed in
    the MAINTAINERS file (I should have been more careful last time).
    This person will take the sole blame for all issues and will be
    responsible for fixing the bugs and incompatibilities affecting
    this code, and for making it evolve to follow regular internal API
    updates.

  - support building on a standard distro with automated tools (i.e. no
    more "click on this site, register your e-mail and download an
    archive then figure how to place this into your build system").
    Dummy libs are OK though as long as they allow the mainline code to
    build and start.

  - multi-threaded support must be fixed. I mean seriously, not worked
    around with a check saying "please disable threads, we've been busy
    fishing for the last two years".

This may be backported to 1.9 given that the code has never worked there
either, thus at least we're certain nobody will miss it.
This commit is contained in:
Willy Tarreau 2019-03-05 13:35:35 +01:00
parent 756d97f205
commit 8e5e1e7bf0
6 changed files with 0 additions and 1032 deletions

View File

@ -320,7 +320,6 @@ 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

View File

@ -46,7 +46,6 @@
# 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
@ -753,24 +752,6 @@ 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

View File

@ -1,68 +0,0 @@
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

View File

@ -596,12 +596,6 @@ 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
@ -1217,95 +1211,6 @@ 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
-----------------------

View File

@ -1,49 +0,0 @@
#
# 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

View File

@ -1,800 +0,0 @@
#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.");