blob: 0349a50f9967101efeab18bd9ce8e3a0f538f798 [file] [log] [blame]
scientiamobiled0027ed2016-11-04 10:55:08 +01001#include <stdio.h>
2#include <stdarg.h>
3
4#include <common/cfgparse.h>
5#include <common/chunk.h>
6#include <common/buffer.h>
Willy Tarreaudc2ed472016-12-21 20:20:17 +01007#include <common/errors.h>
scientiamobiled0027ed2016-11-04 10:55:08 +01008#include <proto/arg.h>
9#include <proto/log.h>
10#include <proto/proto_http.h>
11#include <proto/sample.h>
12#include <ebsttree.h>
13#include <ebmbtree.h>
scientiamobiled0027ed2016-11-04 10:55:08 +010014
Willy Tarreaue5d31692016-11-08 18:47:25 +010015#include <wurfl/wurfl.h>
16
Willy Tarreau350c1c62016-12-21 14:57:34 +010017static struct {
18 char *data_file; /* the WURFL data file */
19 char *cache_size; /* the WURFL cache parameters */
20 int engine_mode; /* the WURFL engine mode */
21 int useragent_priority; /* the WURFL ua priority */
22 struct list patch_file_list; /* the list of WURFL patch file to use */
23 char information_list_separator; /* the separator used in request to separate values */
24 struct list information_list; /* the list of WURFL data to return into request */
25 void *handle; /* the handle to WURFL engine */
26 struct eb_root btree; /* btree containing info (name/type) on WURFL data to return */
27} global_wurfl = {
28 .data_file = NULL,
29 .cache_size = NULL,
30 .engine_mode = -1,
31 .useragent_priority = -1,
32 .information_list_separator = ',',
33 .information_list = LIST_HEAD_INIT(global_wurfl.information_list),
34 .patch_file_list = LIST_HEAD_INIT(global_wurfl.patch_file_list),
35 .handle = NULL,
36};
scientiamobiled0027ed2016-11-04 10:55:08 +010037
38#ifdef WURFL_DEBUG
39inline static void ha_wurfl_log(char * message, ...)
40{
41 char logbuf[256];
42 va_list argp;
43
44 va_start(argp, message);
45 vsnprintf(logbuf, sizeof(logbuf), message, argp);
46 va_end(argp);
47 send_log(NULL, LOG_NOTICE, logbuf, NULL);
48}
49#else
50inline static void ha_wurfl_log(char * message, ...)
51{
52}
53#endif
54
55#define HA_WURFL_MAX_HEADER_LENGTH 1024
56
Willy Tarreaue5d31692016-11-08 18:47:25 +010057typedef char *(*PROP_CALLBACK_FUNC)(wurfl_handle wHandle, wurfl_device_handle dHandle);
58
59enum wurfl_data_type {
60 HA_WURFL_DATA_TYPE_UNKNOWN = 0,
61 HA_WURFL_DATA_TYPE_CAP = 100,
62 HA_WURFL_DATA_TYPE_VCAP = 200,
63 HA_WURFL_DATA_TYPE_PROPERTY = 300
64};
65
66typedef struct {
67 char *name;
68 enum wurfl_data_type type;
69 PROP_CALLBACK_FUNC func_callback;
70 struct ebmb_node nd;
71} wurfl_data_t;
72
scientiamobiled0027ed2016-11-04 10:55:08 +010073static const char HA_WURFL_MODULE_VERSION[] = "1.0";
74static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE";
75static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE";
76static const char HA_WURFL_TARGET_ACCURACY[] = "accuracy";
77static const char HA_WURFL_TARGET_PERFORMANCE[] = "performance";
78static const char HA_WURFL_PRIORITY_PLAIN[] = "plain";
79static const char HA_WURFL_PRIORITY_SIDELOADED_BROWSER[] = "sideloaded_browser";
80static const char HA_WURFL_MIN_ENGINE_VERSION_MANDATORY[] = "1.8.0.0";
81
82static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown";
83static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability";
84static const char HA_WURFL_DATA_TYPE_VCAP_STRING[] = "virtual_capability";
85static const char HA_WURFL_DATA_TYPE_PROPERTY_STRING[] = "property";
86
87static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh);
88static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
89static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
90static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle);
91static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
92static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle);
93static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle);
94static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle);
95static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle);
96static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
97static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle);
98static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle);
99
100// ordered property=>function map, suitable for binary search
101static const struct {
102 const char *name;
103 const char *(*func)(wurfl_handle wHandle, wurfl_device_handle dHandle);
104} wurfl_properties_function_map [] = {
105 {"wurfl_api_version", ha_wurfl_get_wurfl_api_version},
106 {"wurfl_engine_target", ha_wurfl_get_wurfl_engine_target},
107 {"wurfl_id", ha_wurfl_get_wurfl_id },
108 {"wurfl_info", ha_wurfl_get_wurfl_info },
109 {"wurfl_isdevroot", ha_wurfl_get_wurfl_isdevroot},
110 {"wurfl_last_load_time", ha_wurfl_get_wurfl_last_load_time},
111 {"wurfl_normalized_useragent", ha_wurfl_get_wurfl_normalized_useragent},
112 {"wurfl_useragent", ha_wurfl_get_wurfl_useragent},
113 {"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority },
114 {"wurfl_root_id", ha_wurfl_get_wurfl_root_id},
115};
116static const int HA_WURFL_PROPERTIES_NBR = 10;
117
118typedef struct {
119 struct list list;
120 wurfl_data_t data;
121} wurfl_information_t;
122
123typedef struct {
124 struct list list;
125 char *patch_file_path;
126} wurfl_patches_t;
127
128typedef struct {
129 struct sample *wsmp;
130 char header_value[HA_WURFL_MAX_HEADER_LENGTH + 1];
131} ha_wurfl_header_t;
132
133/*
134 * configuration parameters parsing functions
135 */
136static int ha_wurfl_cfg_data_file(char **args, int section_type, struct proxy *curpx,
137 struct proxy *defpx, const char *file, int line,
138 char **err)
139{
140
141 if (*(args[1]) == 0) {
142 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
143 return -1;
144 }
145
Willy Tarreau350c1c62016-12-21 14:57:34 +0100146 global_wurfl.data_file = strdup(args[1]);
scientiamobiled0027ed2016-11-04 10:55:08 +0100147 return 0;
148}
149
150static int ha_wurfl_cfg_cache(char **args, int section_type, struct proxy *curpx,
151 struct proxy *defpx, const char *file, int line,
152 char **err)
153{
154 if (*(args[1]) == 0) {
155 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
156 return -1;
157 }
158
Willy Tarreau350c1c62016-12-21 14:57:34 +0100159 global_wurfl.cache_size = strdup(args[1]);
scientiamobiled0027ed2016-11-04 10:55:08 +0100160 return 0;
161}
162
163static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy *curpx,
164 struct proxy *defpx, const char *file, int line,
165 char **err)
166{
167 if (*(args[1]) == 0) {
168 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
169 return -1;
170 }
171
172 if (!strcmp(args[1],HA_WURFL_TARGET_ACCURACY)) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100173 global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_ACCURACY;
scientiamobiled0027ed2016-11-04 10:55:08 +0100174 return 0;
175 }
176
177 if (!strcmp(args[1],HA_WURFL_TARGET_PERFORMANCE)) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100178 global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_PERFORMANCE;
scientiamobiled0027ed2016-11-04 10:55:08 +0100179 return 0;
180 }
181
182 memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_TARGET_PERFORMANCE, HA_WURFL_TARGET_ACCURACY);
183 return -1;
184}
185
186static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx,
187 struct proxy *defpx, const char *file, int line,
188 char **err)
189{
190 if (*(args[1]) == 0) {
191 memprintf(err, "WURFL: %s expects a single character.\n", args[0]);
192 return -1;
193 }
194
195 if (strlen(args[1]) > 1) {
196 memprintf(err, "WURFL: %s expects a single character, got %s.\n", args[0], args[1]);
197 return -1;
198 }
199
Willy Tarreau350c1c62016-12-21 14:57:34 +0100200 global_wurfl.information_list_separator = *args[1];
scientiamobiled0027ed2016-11-04 10:55:08 +0100201 return 0;
202}
203
204static int ha_wurfl_cfg_information_list(char **args, int section_type, struct proxy *curpx,
205 struct proxy *defpx, const char *file, int line,
206 char **err)
207{
208 int argIdx = 1;
209 wurfl_information_t *wi;
210
211 if (*(args[argIdx]) == 0) {
212 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
213 return -1;
214 }
215
216 while (*(args[argIdx])) {
217 wi = calloc(1, sizeof(*wi));
218
219 if (wi == NULL) {
220 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
221 return -1;
222 }
223
224 wi->data.name = strdup(args[argIdx]);
225 wi->data.type = HA_WURFL_DATA_TYPE_UNKNOWN;
226 wi->data.func_callback = NULL;
Willy Tarreau350c1c62016-12-21 14:57:34 +0100227 LIST_ADDQ(&global_wurfl.information_list, &wi->list);
scientiamobiled0027ed2016-11-04 10:55:08 +0100228 ++argIdx;
229 }
230
231 return 0;
232}
233
234static int ha_wurfl_cfg_patch_file_list(char **args, int section_type, struct proxy *curpx,
235 struct proxy *defpx, const char *file, int line,
236 char **err)
237{
238 int argIdx = 1;
239 wurfl_patches_t *wp;
240
241 if (*(args[argIdx]) == 0) {
242 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
243 return -1;
244 }
245
246 while (*(args[argIdx])) {
247 wp = calloc(1, sizeof(*wp));
248
249 if (wp == NULL) {
250 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
251 return -1;
252 }
253
254 wp->patch_file_path = strdup(args[argIdx]);
Willy Tarreau350c1c62016-12-21 14:57:34 +0100255 LIST_ADDQ(&global_wurfl.patch_file_list, &wp->list);
scientiamobiled0027ed2016-11-04 10:55:08 +0100256 ++argIdx;
257 }
258
259 return 0;
260}
261
262static int ha_wurfl_cfg_useragent_priority(char **args, int section_type, struct proxy *curpx,
263 struct proxy *defpx, const char *file, int line,
264 char **err)
265{
266 if (*(args[1]) == 0) {
267 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
268 return -1;
269 }
270
271 if (!strcmp(args[1],HA_WURFL_PRIORITY_PLAIN)) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100272 global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT;
scientiamobiled0027ed2016-11-04 10:55:08 +0100273 return 0;
274 }
275
276 if (!strcmp(args[1],HA_WURFL_PRIORITY_SIDELOADED_BROWSER)) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100277 global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_OVERRIDE_SIDELOADED_BROWSER_USERAGENT;
scientiamobiled0027ed2016-11-04 10:55:08 +0100278 return 0;
279 }
280
281 memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_PRIORITY_PLAIN, HA_WURFL_PRIORITY_SIDELOADED_BROWSER);
282 return -1;
283}
284
285/*
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100286 * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
scientiamobiled0027ed2016-11-04 10:55:08 +0100287 */
288
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100289static int ha_wurfl_init(void)
scientiamobiled0027ed2016-11-04 10:55:08 +0100290{
291 wurfl_information_t *wi;
292 wurfl_patches_t *wp;
293 wurfl_data_t * wn;
294 int wurfl_result_code = WURFL_OK;
295 int len;
296
297 send_log(NULL, LOG_NOTICE, "WURFL: Loading module v.%s\n", HA_WURFL_MODULE_VERSION);
298 // creating WURFL handler
Willy Tarreau350c1c62016-12-21 14:57:34 +0100299 global_wurfl.handle = wurfl_create();
scientiamobiled0027ed2016-11-04 10:55:08 +0100300
Willy Tarreau350c1c62016-12-21 14:57:34 +0100301 if (global_wurfl.handle == NULL) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100302 Warning("WURFL: Engine handler creation failed");
303 send_log(NULL, LOG_WARNING, "WURFL: Engine handler creation failed\n");
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100304 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100305 }
306
307 send_log(NULL, LOG_NOTICE, "WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() );
308
309 // set wurfl data file
Willy Tarreau350c1c62016-12-21 14:57:34 +0100310 if (global_wurfl.data_file == NULL) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100311 Warning("WURFL: missing wurfl-data-file parameter in global configuration\n");
312 send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-data-file parameter in global configuration\n");
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100313 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100314 }
315
Christopher Faulete8ca4342017-10-25 17:23:02 +0200316 if (global.nbthread > 1) {
317 Alert("WURFL: multithreading is not supported for now.\n");
318 return (ERR_FATAL | ERR_ALERT);
319 }
320
Willy Tarreau350c1c62016-12-21 14:57:34 +0100321 if (wurfl_set_root(global_wurfl.handle, global_wurfl.data_file) != WURFL_OK) {
322 Warning("WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
323 send_log(NULL, LOG_WARNING, "WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100324 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100325 }
326
Willy Tarreau350c1c62016-12-21 14:57:34 +0100327 send_log(NULL, LOG_NOTICE, "WURFL: Engine root file set to %s\n", global_wurfl.data_file);
scientiamobiled0027ed2016-11-04 10:55:08 +0100328 // just a log to inform which separator char has to be used
Willy Tarreau350c1c62016-12-21 14:57:34 +0100329 send_log(NULL, LOG_NOTICE, "WURFL: Information list separator set to '%c'\n", global_wurfl.information_list_separator);
scientiamobiled0027ed2016-11-04 10:55:08 +0100330
331 // load wurfl data needed ( and filter whose are supposed to be capabilities )
Willy Tarreau350c1c62016-12-21 14:57:34 +0100332 if (LIST_ISEMPTY(&global_wurfl.information_list)) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100333 Warning("WURFL: missing wurfl-information-list parameter in global configuration\n");
334 send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-information-list parameter in global configuration\n");
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100335 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100336 } else {
337 // ebtree initialization
Willy Tarreau350c1c62016-12-21 14:57:34 +0100338 global_wurfl.btree = EB_ROOT;
scientiamobiled0027ed2016-11-04 10:55:08 +0100339
340 // checking if informations are valid WURFL data ( cap, vcaps, properties )
Willy Tarreau350c1c62016-12-21 14:57:34 +0100341 list_for_each_entry(wi, &global_wurfl.information_list, list) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100342 // check if information is already loaded looking into btree
Willy Tarreau350c1c62016-12-21 14:57:34 +0100343 if (ebst_lookup(&global_wurfl.btree, wi->data.name) == NULL) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100344 if ((wi->data.func_callback = (PROP_CALLBACK_FUNC) ha_wurfl_get_property_callback(wi->data.name)) != NULL) {
345 wi->data.type = HA_WURFL_DATA_TYPE_PROPERTY;
346 ha_wurfl_log("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name);
Willy Tarreau350c1c62016-12-21 14:57:34 +0100347 } else if (wurfl_has_virtual_capability(global_wurfl.handle, wi->data.name)) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100348 wi->data.type = HA_WURFL_DATA_TYPE_VCAP;
349 ha_wurfl_log("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name);
350 } else {
351 // by default a cap type is assumed to be and we control it on engine load
352 wi->data.type = HA_WURFL_DATA_TYPE_CAP;
353
Willy Tarreau350c1c62016-12-21 14:57:34 +0100354 if (wurfl_add_requested_capability(global_wurfl.handle, wi->data.name) != WURFL_OK) {
355 Warning("WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
356 send_log(NULL, LOG_WARNING, "WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100357 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100358 }
359
360 ha_wurfl_log("WURFL: [%s] treated as wurfl capability. Will check its validity later, on engine load\n",wi->data.name);
361 }
362
363 // ebtree insert here
364 len = strlen(wi->data.name);
365
366 wn = malloc(sizeof(wurfl_data_t) + len + 1);
367
368 if (wn == NULL) {
369 Warning("WURFL: Error allocating memory for information tree element.\n");
370 send_log(NULL, LOG_WARNING, "WURFL: Error allocating memory for information tree element.\n");
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100371 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100372 }
373
374 wn->name = wi->data.name;
375 wn->type = wi->data.type;
376 wn->func_callback = wi->data.func_callback;
377 memcpy(wn->nd.key, wi->data.name, len);
378 wn->nd.key[len] = 0;
379
Willy Tarreau350c1c62016-12-21 14:57:34 +0100380 if (!ebst_insert(&global_wurfl.btree, &wn->nd)) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100381 Warning("WURFL: [%s] not inserted in btree\n",wn->name);
382 send_log(NULL, LOG_WARNING, "WURFL: [%s] not inserted in btree\n",wn->name);
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100383 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100384 }
385
386 } else {
387 ha_wurfl_log("WURFL: [%s] already loaded\n",wi->data.name);
388 }
389
390 }
391
392 }
393
394 // filtering mandatory capabilities if engine version < 1.8.0.0
395 if (strcmp(wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY) < 0) {
396 wurfl_capability_enumerator_handle hmandatorycapabilityenumerator;
397 ha_wurfl_log("WURFL: Engine version %s < %s - Filtering mandatory capabilities\n", wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY);
Willy Tarreau350c1c62016-12-21 14:57:34 +0100398 hmandatorycapabilityenumerator = wurfl_get_mandatory_capability_enumerator(global_wurfl.handle);
scientiamobiled0027ed2016-11-04 10:55:08 +0100399
400 while (wurfl_capability_enumerator_is_valid(hmandatorycapabilityenumerator)) {
401 char *name = (char *)wurfl_capability_enumerator_get_name(hmandatorycapabilityenumerator);
402
Willy Tarreau350c1c62016-12-21 14:57:34 +0100403 if (ebst_lookup(&global_wurfl.btree, name) == NULL) {
404 if (wurfl_add_requested_capability(global_wurfl.handle, name) != WURFL_OK) {
405 Warning("WURFL: Engine adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle));
406 send_log(NULL, LOG_WARNING, "WURFL: Adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100407 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100408 }
409
410 ha_wurfl_log("WURFL: Mandatory capability [%s] added\n", name);
411 } else {
412 ha_wurfl_log("WURFL: Mandatory capability [%s] already filtered\n", name);
413 }
414
415 wurfl_capability_enumerator_move_next(hmandatorycapabilityenumerator);
416 }
417
418 wurfl_capability_enumerator_destroy(hmandatorycapabilityenumerator);
419 }
420
421 // adding WURFL patches if needed
Willy Tarreau350c1c62016-12-21 14:57:34 +0100422 if (!LIST_ISEMPTY(&global_wurfl.patch_file_list)) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100423
Willy Tarreau350c1c62016-12-21 14:57:34 +0100424 list_for_each_entry(wp, &global_wurfl.patch_file_list, list) {
425 if (wurfl_add_patch(global_wurfl.handle, wp->patch_file_path) != WURFL_OK) {
426 Warning("WURFL: Engine adding patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
427 send_log(NULL, LOG_WARNING, "WURFL: Adding engine patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100428 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100429 }
430 send_log(NULL, LOG_NOTICE, "WURFL: Engine patch file added %s\n", wp->patch_file_path);
431
432 }
433
434 }
435
436 // setting cache provider if specified in cfg, otherwise let engine choose
Willy Tarreau350c1c62016-12-21 14:57:34 +0100437 if (global_wurfl.cache_size != NULL) {
438 if (strpbrk(global_wurfl.cache_size, ",") != NULL) {
439 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_DOUBLE_LRU, global_wurfl.cache_size) ;
scientiamobiled0027ed2016-11-04 10:55:08 +0100440 } else {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100441 if (strcmp(global_wurfl.cache_size, "0")) {
442 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_LRU, global_wurfl.cache_size) ;
scientiamobiled0027ed2016-11-04 10:55:08 +0100443 } else {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100444 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_NONE, 0);
scientiamobiled0027ed2016-11-04 10:55:08 +0100445 }
446
447 }
448
449 if (wurfl_result_code != WURFL_OK) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100450 Warning("WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle));
451 send_log(NULL, LOG_WARNING, "WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100452 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100453 }
454
Willy Tarreau350c1c62016-12-21 14:57:34 +0100455 send_log(NULL, LOG_NOTICE, "WURFL: Cache set to [%s]\n", global_wurfl.cache_size);
scientiamobiled0027ed2016-11-04 10:55:08 +0100456 }
457
458 // setting engine mode if specified in cfg, otherwise let engine choose
Willy Tarreau350c1c62016-12-21 14:57:34 +0100459 if (global_wurfl.engine_mode != -1) {
460 if (wurfl_set_engine_target(global_wurfl.handle, global_wurfl.engine_mode) != WURFL_OK ) {
461 Warning("WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
462 send_log(NULL, LOG_WARNING, "WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100463 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100464 }
465
466 }
467
Willy Tarreau350c1c62016-12-21 14:57:34 +0100468 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) );
scientiamobiled0027ed2016-11-04 10:55:08 +0100469
470 // setting ua priority if specified in cfg, otherwise let engine choose
Willy Tarreau350c1c62016-12-21 14:57:34 +0100471 if (global_wurfl.useragent_priority != -1) {
472 if (wurfl_set_useragent_priority(global_wurfl.handle, global_wurfl.useragent_priority) != WURFL_OK ) {
473 Warning("WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
474 send_log(NULL, LOG_WARNING, "WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100475 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100476 }
477
478 }
479
Willy Tarreau350c1c62016-12-21 14:57:34 +0100480 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) );
scientiamobiled0027ed2016-11-04 10:55:08 +0100481
482 // loading WURFL engine
Willy Tarreau350c1c62016-12-21 14:57:34 +0100483 if (wurfl_load(global_wurfl.handle) != WURFL_OK) {
484 Warning("WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
485 send_log(NULL, LOG_WARNING, "WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100486 return ERR_WARN;
scientiamobiled0027ed2016-11-04 10:55:08 +0100487 }
488
489 send_log(NULL, LOG_NOTICE, "WURFL: Engine loaded\n");
490 send_log(NULL, LOG_NOTICE, "WURFL: Module load completed\n");
491 return 0;
492}
493
Willy Tarreau800f93f2016-12-21 20:52:38 +0100494static void ha_wurfl_deinit(void)
scientiamobiled0027ed2016-11-04 10:55:08 +0100495{
496 wurfl_information_t *wi, *wi2;
497 wurfl_patches_t *wp, *wp2;
498
499 send_log(NULL, LOG_NOTICE, "WURFL: Unloading module v.%s\n", HA_WURFL_MODULE_VERSION);
Willy Tarreau350c1c62016-12-21 14:57:34 +0100500 wurfl_destroy(global_wurfl.handle);
501 global_wurfl.handle = NULL;
502 free(global_wurfl.data_file);
503 global_wurfl.data_file = NULL;
504 free(global_wurfl.cache_size);
505 global_wurfl.cache_size = NULL;
scientiamobiled0027ed2016-11-04 10:55:08 +0100506
Willy Tarreau350c1c62016-12-21 14:57:34 +0100507 list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100508 LIST_DEL(&wi->list);
509 free(wi);
510 }
511
Willy Tarreau350c1c62016-12-21 14:57:34 +0100512 list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) {
scientiamobiled0027ed2016-11-04 10:55:08 +0100513 LIST_DEL(&wp->list);
514 free(wp);
515 }
516
517 send_log(NULL, LOG_NOTICE, "WURFL: Module unloaded\n");
518}
519
520static int ha_wurfl_get_all(const struct arg *args, struct sample *smp, const char *kw, void *private)
521{
522 wurfl_device_handle dHandle;
523 struct chunk *temp;
524 wurfl_information_t *wi;
525 ha_wurfl_header_t wh;
526
527 ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n");
528 wh.wsmp = smp;
Willy Tarreau350c1c62016-12-21 14:57:34 +0100529 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
scientiamobiled0027ed2016-11-04 10:55:08 +0100530
531 if (!dHandle) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100532 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
scientiamobiled0027ed2016-11-04 10:55:08 +0100533 return 1;
534 }
535
536 temp = get_trash_chunk();
537 chunk_reset(temp);
538
Willy Tarreau350c1c62016-12-21 14:57:34 +0100539 list_for_each_entry(wi, &global_wurfl.information_list, list) {
540 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
scientiamobiled0027ed2016-11-04 10:55:08 +0100541
542 switch(wi->data.type) {
543 case HA_WURFL_DATA_TYPE_UNKNOWN :
544 ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
545#ifdef WURFL_HEADER_WITH_DETAILS
546 // write WURFL property type and name before its value...
547 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name);
548#endif
549 break;
550 case HA_WURFL_DATA_TYPE_CAP :
551 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING);
552#ifdef WURFL_HEADER_WITH_DETAILS
553 // write WURFL property type and name before its value...
554 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name);
555#endif
556 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name));
557 break;
558 case HA_WURFL_DATA_TYPE_VCAP :
559 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING);
560#ifdef WURFL_HEADER_WITH_DETAILS
561 // write WURFL property type and name before its value...
562 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name);
563#endif
564 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name));
565 break;
566 case HA_WURFL_DATA_TYPE_PROPERTY :
567 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
568#ifdef WURFL_HEADER_WITH_DETAILS
569 // write WURFL property type and name before its value...
570 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wi->data.name);
571#endif
Willy Tarreau350c1c62016-12-21 14:57:34 +0100572 chunk_appendf(temp, "%s", wi->data.func_callback(global_wurfl.handle, dHandle));
scientiamobiled0027ed2016-11-04 10:55:08 +0100573 break;
574 }
575
576 }
577
578 wurfl_device_destroy(dHandle);
579 smp->data.u.str.str = temp->str;
580 smp->data.u.str.len = temp->len;
581 return 1;
582}
583
584static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private)
585{
586 wurfl_device_handle dHandle;
587 struct chunk *temp;
588 wurfl_data_t *wn = NULL;
589 struct ebmb_node *node;
590 ha_wurfl_header_t wh;
591 int i = 0;
592
593 ha_wurfl_log("WURFL: starting ha_wurfl_get\n");
594 wh.wsmp = smp;
Willy Tarreau350c1c62016-12-21 14:57:34 +0100595 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
scientiamobiled0027ed2016-11-04 10:55:08 +0100596
597 if (!dHandle) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100598 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
scientiamobiled0027ed2016-11-04 10:55:08 +0100599 return 1;
600 }
601
602 temp = get_trash_chunk();
603 chunk_reset(temp);
604
605 while (args[i].data.str.str) {
Willy Tarreau350c1c62016-12-21 14:57:34 +0100606 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
607 node = ebst_lookup(&global_wurfl.btree, args[i].data.str.str);
scientiamobiled0027ed2016-11-04 10:55:08 +0100608 wn = container_of(node, wurfl_data_t, nd);
609
610 if (wn) {
611
612 switch(wn->type) {
613 case HA_WURFL_DATA_TYPE_UNKNOWN :
614 ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
615#ifdef WURFL_HEADER_WITH_DETAILS
616 // write WURFL property type and name before its value...
617 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name);
618#endif
619 break;
620 case HA_WURFL_DATA_TYPE_CAP :
621 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING);
622#ifdef WURFL_HEADER_WITH_DETAILS
623 // write WURFL property type and name before its value...
624 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name);
625#endif
626 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name));
627 break;
628 case HA_WURFL_DATA_TYPE_VCAP :
629 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING);
630#ifdef WURFL_HEADER_WITH_DETAILS
631 // write WURFL property type and name before its value...
632 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name);
633#endif
634 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name));
635 break;
636 case HA_WURFL_DATA_TYPE_PROPERTY :
637 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
638#ifdef WURFL_HEADER_WITH_DETAILS
639 // write WURFL property type and name before its value...
640 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wn->name);
641#endif
Willy Tarreau350c1c62016-12-21 14:57:34 +0100642 chunk_appendf(temp, "%s", wn->func_callback(global_wurfl.handle, dHandle));
scientiamobiled0027ed2016-11-04 10:55:08 +0100643 break;
644 }
645
646 } else {
647 ha_wurfl_log("WURFL: %s not in wurfl-information-list \n", args[i].data.str.str);
648 }
649
650 i++;
651 }
652
653 wurfl_device_destroy(dHandle);
654 smp->data.u.str.str = temp->str;
655 smp->data.u.str.len = temp->len;
656 return 1;
657}
658
659static struct cfg_kw_list wurflcfg_kws = {{ }, {
660 { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file },
661 { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator },
662 { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list },
663 { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list },
664 { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache },
665 { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode },
666 { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority },
667 { 0, NULL, NULL },
668 }
669};
670
671/* Note: must not be declared <const> as its list will be overwritten */
672static struct sample_fetch_kw_list fetch_kws = {ILH, {
673 { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
674 { "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 },
675 { NULL, NULL, 0, 0, 0 },
676 }
677};
678
679/* Note: must not be declared <const> as its list will be overwritten */
680static struct sample_conv_kw_list conv_kws = {ILH, {
681 { NULL, NULL, 0, 0, 0 },
682 }
683};
684
685__attribute__((constructor))
686static void __wurfl_init(void)
687{
688 /* register sample fetch and format conversion keywords */
689 sample_register_fetches(&fetch_kws);
690 sample_register_convs(&conv_kws);
691 cfg_register_keywords(&wurflcfg_kws);
Willy Tarreau770042d2016-12-21 18:47:13 +0100692 hap_register_build_opts("Built with WURFL support.", 0);
Willy Tarreaudc2ed472016-12-21 20:20:17 +0100693 hap_register_post_check(ha_wurfl_init);
Willy Tarreau800f93f2016-12-21 20:52:38 +0100694 hap_register_post_deinit(ha_wurfl_deinit);
scientiamobiled0027ed2016-11-04 10:55:08 +0100695}
696
697// WURFL properties wrapper functions
698static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
699{
700 return wurfl_device_get_root_id(dHandle);
701}
702
703static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
704{
705 return wurfl_device_get_id(dHandle);
706}
707
708static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle)
709{
710 if (wurfl_device_is_actual_device_root(dHandle))
711 return HA_WURFL_ISDEVROOT_TRUE;
712 else
713 return HA_WURFL_ISDEVROOT_FALSE;
714}
715
716static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
717{
718 return wurfl_device_get_original_useragent(dHandle);
719}
720
721static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle)
722{
723 return wurfl_get_api_version();
724}
725
726static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle)
727{
728 return wurfl_get_engine_target_as_string(wHandle);
729}
730
731static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle)
732{
733 return wurfl_get_wurfl_info(wHandle);
734}
735
736static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle)
737{
738 return wurfl_get_last_load_time_as_string(wHandle);
739}
740
741static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
742{
743 return wurfl_device_get_normalized_useragent(dHandle);
744}
745
746static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle)
747{
748 return wurfl_get_useragent_priority_as_string(wHandle);
749}
750
751// call function for WURFL properties
752static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle)
753{
754 int position;
755 int begin = 0;
756 int end = HA_WURFL_PROPERTIES_NBR - 1;
757 int cond = 0;
758
759 while(begin <= end) {
760 position = (begin + end) / 2;
761
762 if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) {
763 ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name );
764 return wurfl_properties_function_map[position].func;
765 } else if(cond < 0)
766 begin = position + 1;
767 else
768 end = position - 1;
769
770 }
771
772 return NULL;
773}
774
775static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh)
776{
777 struct sample *smp;
778 struct hdr_idx *idx;
779 struct hdr_ctx ctx;
780 const struct http_msg *msg;
781 int header_len = HA_WURFL_MAX_HEADER_LENGTH;
782
783 ha_wurfl_log("WURFL: retrieve header request [%s]\n", header_name);
784 smp = ((ha_wurfl_header_t *)wh)->wsmp;
785 idx = &smp->strm->txn->hdr_idx;
786 msg = &smp->strm->txn->req;
787 ctx.idx = 0;
788
789 if (http_find_full_header2(header_name, strlen(header_name), msg->chn->buf->p, idx, &ctx) == 0)
790 return 0;
791
792 if (header_len > ctx.vlen)
793 header_len = ctx.vlen;
794
795 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.line + ctx.val, header_len);
796 ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0';
797 ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value);
798 return ((ha_wurfl_header_t *)wh)->header_value;
799}