blob: 5de33b4a8d0c12cc048fc5c2b14f171715fc6492 [file] [log] [blame]
Willy Tarreaub3cc9f22019-04-19 16:03:32 +02001#include <stdio.h>
2#include <stdarg.h>
3
4#include <common/cfgparse.h>
5#include <common/chunk.h>
6#include <common/buffer.h>
7#include <common/errors.h>
8#include <common/initcall.h>
9#include <types/global.h>
10#include <proto/arg.h>
11#include <proto/log.h>
12#include <proto/proto_http.h>
13#include <proto/sample.h>
14#include <ebsttree.h>
15#include <ebmbtree.h>
16
17#include <wurfl/wurfl.h>
18
19static struct {
20 char *data_file; /* the WURFL data file */
21 char *cache_size; /* the WURFL cache parameters */
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020022 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,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020030 .information_list_separator = ',',
31 .information_list = LIST_HEAD_INIT(global_wurfl.information_list),
32 .patch_file_list = LIST_HEAD_INIT(global_wurfl.patch_file_list),
33 .handle = NULL,
34};
35
36#ifdef WURFL_DEBUG
37inline static void ha_wurfl_log(char * message, ...)
38{
39 char logbuf[256];
40 va_list argp;
41
42 va_start(argp, message);
43 vsnprintf(logbuf, sizeof(logbuf), message, argp);
44 va_end(argp);
45 send_log(NULL, LOG_NOTICE, logbuf, NULL);
46}
47#else
48inline static void ha_wurfl_log(char * message, ...)
49{
50}
51#endif
52
53#define HA_WURFL_MAX_HEADER_LENGTH 1024
54
55typedef char *(*PROP_CALLBACK_FUNC)(wurfl_handle wHandle, wurfl_device_handle dHandle);
56
57enum wurfl_data_type {
58 HA_WURFL_DATA_TYPE_UNKNOWN = 0,
59 HA_WURFL_DATA_TYPE_CAP = 100,
60 HA_WURFL_DATA_TYPE_VCAP = 200,
61 HA_WURFL_DATA_TYPE_PROPERTY = 300
62};
63
64typedef struct {
65 char *name;
66 enum wurfl_data_type type;
67 PROP_CALLBACK_FUNC func_callback;
68 struct ebmb_node nd;
69} wurfl_data_t;
70
71static const char HA_WURFL_MODULE_VERSION[] = "1.0";
72static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE";
73static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020074
75static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown";
76static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability";
77static const char HA_WURFL_DATA_TYPE_VCAP_STRING[] = "virtual_capability";
78static const char HA_WURFL_DATA_TYPE_PROPERTY_STRING[] = "property";
79
80static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh);
81static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
82static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
83static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle);
84static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
85static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle);
86static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle);
87static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle);
88static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle);
89static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
90static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle);
91static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle);
92
93// ordered property=>function map, suitable for binary search
94static const struct {
95 const char *name;
96 const char *(*func)(wurfl_handle wHandle, wurfl_device_handle dHandle);
97} wurfl_properties_function_map [] = {
98 {"wurfl_api_version", ha_wurfl_get_wurfl_api_version},
paulborilebad132c2019-04-18 11:57:04 +020099 {"wurfl_engine_target", ha_wurfl_get_wurfl_engine_target}, // kept for backward conf file compat
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200100 {"wurfl_id", ha_wurfl_get_wurfl_id },
101 {"wurfl_info", ha_wurfl_get_wurfl_info },
102 {"wurfl_isdevroot", ha_wurfl_get_wurfl_isdevroot},
103 {"wurfl_last_load_time", ha_wurfl_get_wurfl_last_load_time},
104 {"wurfl_normalized_useragent", ha_wurfl_get_wurfl_normalized_useragent},
105 {"wurfl_useragent", ha_wurfl_get_wurfl_useragent},
paulborilebad132c2019-04-18 11:57:04 +0200106 {"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority }, // kept for backward conf file compat
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200107 {"wurfl_root_id", ha_wurfl_get_wurfl_root_id},
108};
109static const int HA_WURFL_PROPERTIES_NBR = 10;
110
111typedef struct {
112 struct list list;
113 wurfl_data_t data;
114} wurfl_information_t;
115
116typedef struct {
117 struct list list;
118 char *patch_file_path;
119} wurfl_patches_t;
120
121typedef struct {
122 struct sample *wsmp;
123 char header_value[HA_WURFL_MAX_HEADER_LENGTH + 1];
124} ha_wurfl_header_t;
125
126/*
127 * configuration parameters parsing functions
128 */
129static int ha_wurfl_cfg_data_file(char **args, int section_type, struct proxy *curpx,
130 struct proxy *defpx, const char *file, int line,
131 char **err)
132{
133
134 if (*(args[1]) == 0) {
135 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
136 return -1;
137 }
138
139 global_wurfl.data_file = strdup(args[1]);
140 return 0;
141}
142
143static int ha_wurfl_cfg_cache(char **args, int section_type, struct proxy *curpx,
144 struct proxy *defpx, const char *file, int line,
145 char **err)
146{
147 if (*(args[1]) == 0) {
148 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
149 return -1;
150 }
151
152 global_wurfl.cache_size = strdup(args[1]);
153 return 0;
154}
155
156static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy *curpx,
157 struct proxy *defpx, const char *file, int line,
158 char **err)
159{
paulborilebad132c2019-04-18 11:57:04 +0200160 // kept for backward conf file compat
161 return 0;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200162}
163
164static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx,
165 struct proxy *defpx, const char *file, int line,
166 char **err)
167{
168 if (*(args[1]) == 0) {
169 memprintf(err, "WURFL: %s expects a single character.\n", args[0]);
170 return -1;
171 }
172
173 if (strlen(args[1]) > 1) {
174 memprintf(err, "WURFL: %s expects a single character, got %s.\n", args[0], args[1]);
175 return -1;
176 }
177
178 global_wurfl.information_list_separator = *args[1];
179 return 0;
180}
181
182static int ha_wurfl_cfg_information_list(char **args, int section_type, struct proxy *curpx,
183 struct proxy *defpx, const char *file, int line,
184 char **err)
185{
186 int argIdx = 1;
187 wurfl_information_t *wi;
188
189 if (*(args[argIdx]) == 0) {
190 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
191 return -1;
192 }
193
194 while (*(args[argIdx])) {
195 wi = calloc(1, sizeof(*wi));
196
197 if (wi == NULL) {
198 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
199 return -1;
200 }
201
202 wi->data.name = strdup(args[argIdx]);
203 wi->data.type = HA_WURFL_DATA_TYPE_UNKNOWN;
204 wi->data.func_callback = NULL;
205 LIST_ADDQ(&global_wurfl.information_list, &wi->list);
206 ++argIdx;
207 }
208
209 return 0;
210}
211
212static int ha_wurfl_cfg_patch_file_list(char **args, int section_type, struct proxy *curpx,
213 struct proxy *defpx, const char *file, int line,
214 char **err)
215{
216 int argIdx = 1;
217 wurfl_patches_t *wp;
218
219 if (*(args[argIdx]) == 0) {
220 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
221 return -1;
222 }
223
224 while (*(args[argIdx])) {
225 wp = calloc(1, sizeof(*wp));
226
227 if (wp == NULL) {
228 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
229 return -1;
230 }
231
232 wp->patch_file_path = strdup(args[argIdx]);
233 LIST_ADDQ(&global_wurfl.patch_file_list, &wp->list);
234 ++argIdx;
235 }
236
237 return 0;
238}
239
240static int ha_wurfl_cfg_useragent_priority(char **args, int section_type, struct proxy *curpx,
241 struct proxy *defpx, const char *file, int line,
242 char **err)
243{
paulborilebad132c2019-04-18 11:57:04 +0200244 // this feature is deprecated, keeping only not to break compatibility
245 // with old configuration files.
246 return 0;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200247}
248
249/*
250 * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
251 */
252
253static int ha_wurfl_init(void)
254{
255 wurfl_information_t *wi;
256 wurfl_patches_t *wp;
257 wurfl_data_t * wn;
258 int wurfl_result_code = WURFL_OK;
259 int len;
260
261 send_log(NULL, LOG_NOTICE, "WURFL: Loading module v.%s\n", HA_WURFL_MODULE_VERSION);
262 // creating WURFL handler
263 global_wurfl.handle = wurfl_create();
264
265 if (global_wurfl.handle == NULL) {
266 ha_warning("WURFL: Engine handler creation failed");
267 send_log(NULL, LOG_WARNING, "WURFL: Engine handler creation failed\n");
268 return ERR_WARN;
269 }
270
271 send_log(NULL, LOG_NOTICE, "WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() );
272
273 // set wurfl data file
274 if (global_wurfl.data_file == NULL) {
275 ha_warning("WURFL: missing wurfl-data-file parameter in global configuration\n");
276 send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-data-file parameter in global configuration\n");
277 return ERR_WARN;
278 }
279
280 if (global.nbthread > 1) {
281 ha_alert("WURFL: multithreading is not supported for now.\n");
282 return (ERR_FATAL | ERR_ALERT);
283 }
284
285 if (wurfl_set_root(global_wurfl.handle, global_wurfl.data_file) != WURFL_OK) {
286 ha_warning("WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
287 send_log(NULL, LOG_WARNING, "WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
288 return ERR_WARN;
289 }
290
291 send_log(NULL, LOG_NOTICE, "WURFL: Engine root file set to %s\n", global_wurfl.data_file);
292 // just a log to inform which separator char has to be used
293 send_log(NULL, LOG_NOTICE, "WURFL: Information list separator set to '%c'\n", global_wurfl.information_list_separator);
294
295 // load wurfl data needed ( and filter whose are supposed to be capabilities )
296 if (LIST_ISEMPTY(&global_wurfl.information_list)) {
297 ha_warning("WURFL: missing wurfl-information-list parameter in global configuration\n");
298 send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-information-list parameter in global configuration\n");
299 return ERR_WARN;
300 } else {
301 // ebtree initialization
302 global_wurfl.btree = EB_ROOT;
303
304 // checking if informations are valid WURFL data ( cap, vcaps, properties )
305 list_for_each_entry(wi, &global_wurfl.information_list, list) {
306 // check if information is already loaded looking into btree
307 if (ebst_lookup(&global_wurfl.btree, wi->data.name) == NULL) {
308 if ((wi->data.func_callback = (PROP_CALLBACK_FUNC) ha_wurfl_get_property_callback(wi->data.name)) != NULL) {
309 wi->data.type = HA_WURFL_DATA_TYPE_PROPERTY;
310 ha_wurfl_log("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name);
311 } else if (wurfl_has_virtual_capability(global_wurfl.handle, wi->data.name)) {
312 wi->data.type = HA_WURFL_DATA_TYPE_VCAP;
313 ha_wurfl_log("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name);
314 } else {
315 // by default a cap type is assumed to be and we control it on engine load
316 wi->data.type = HA_WURFL_DATA_TYPE_CAP;
317
318 if (wurfl_add_requested_capability(global_wurfl.handle, wi->data.name) != WURFL_OK) {
319 ha_warning("WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
320 send_log(NULL, LOG_WARNING, "WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
321 return ERR_WARN;
322 }
323
324 ha_wurfl_log("WURFL: [%s] treated as wurfl capability. Will check its validity later, on engine load\n",wi->data.name);
325 }
326
327 // ebtree insert here
328 len = strlen(wi->data.name);
329
330 wn = malloc(sizeof(wurfl_data_t) + len + 1);
331
332 if (wn == NULL) {
333 ha_warning("WURFL: Error allocating memory for information tree element.\n");
334 send_log(NULL, LOG_WARNING, "WURFL: Error allocating memory for information tree element.\n");
335 return ERR_WARN;
336 }
337
338 wn->name = wi->data.name;
339 wn->type = wi->data.type;
340 wn->func_callback = wi->data.func_callback;
341 memcpy(wn->nd.key, wi->data.name, len);
342 wn->nd.key[len] = 0;
343
344 if (!ebst_insert(&global_wurfl.btree, &wn->nd)) {
345 ha_warning("WURFL: [%s] not inserted in btree\n",wn->name);
346 send_log(NULL, LOG_WARNING, "WURFL: [%s] not inserted in btree\n",wn->name);
347 return ERR_WARN;
348 }
349
350 } else {
351 ha_wurfl_log("WURFL: [%s] already loaded\n",wi->data.name);
352 }
353
354 }
355
356 }
357
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200358
359 // adding WURFL patches if needed
360 if (!LIST_ISEMPTY(&global_wurfl.patch_file_list)) {
361
362 list_for_each_entry(wp, &global_wurfl.patch_file_list, list) {
363 if (wurfl_add_patch(global_wurfl.handle, wp->patch_file_path) != WURFL_OK) {
364 ha_warning("WURFL: Engine adding patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
365 send_log(NULL, LOG_WARNING, "WURFL: Adding engine patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
366 return ERR_WARN;
367 }
368 send_log(NULL, LOG_NOTICE, "WURFL: Engine patch file added %s\n", wp->patch_file_path);
369
370 }
371
372 }
373
374 // setting cache provider if specified in cfg, otherwise let engine choose
375 if (global_wurfl.cache_size != NULL) {
376 if (strpbrk(global_wurfl.cache_size, ",") != NULL) {
377 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_DOUBLE_LRU, global_wurfl.cache_size) ;
378 } else {
379 if (strcmp(global_wurfl.cache_size, "0")) {
380 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_LRU, global_wurfl.cache_size) ;
381 } else {
382 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_NONE, 0);
383 }
384
385 }
386
387 if (wurfl_result_code != WURFL_OK) {
388 ha_warning("WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle));
389 send_log(NULL, LOG_WARNING, "WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle));
390 return ERR_WARN;
391 }
392
393 send_log(NULL, LOG_NOTICE, "WURFL: Cache set to [%s]\n", global_wurfl.cache_size);
394 }
395
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200396 // loading WURFL engine
397 if (wurfl_load(global_wurfl.handle) != WURFL_OK) {
398 ha_warning("WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
399 send_log(NULL, LOG_WARNING, "WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
400 return ERR_WARN;
401 }
402
403 send_log(NULL, LOG_NOTICE, "WURFL: Engine loaded\n");
404 send_log(NULL, LOG_NOTICE, "WURFL: Module load completed\n");
405 return 0;
406}
407
408static void ha_wurfl_deinit(void)
409{
410 wurfl_information_t *wi, *wi2;
411 wurfl_patches_t *wp, *wp2;
412
413 send_log(NULL, LOG_NOTICE, "WURFL: Unloading module v.%s\n", HA_WURFL_MODULE_VERSION);
414 wurfl_destroy(global_wurfl.handle);
415 global_wurfl.handle = NULL;
416 free(global_wurfl.data_file);
417 global_wurfl.data_file = NULL;
418 free(global_wurfl.cache_size);
419 global_wurfl.cache_size = NULL;
420
421 list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) {
422 LIST_DEL(&wi->list);
423 free(wi);
424 }
425
426 list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) {
427 LIST_DEL(&wp->list);
428 free(wp);
429 }
430
431 send_log(NULL, LOG_NOTICE, "WURFL: Module unloaded\n");
432}
433
434static int ha_wurfl_get_all(const struct arg *args, struct sample *smp, const char *kw, void *private)
435{
436 wurfl_device_handle dHandle;
437 struct buffer *temp;
438 wurfl_information_t *wi;
439 ha_wurfl_header_t wh;
440
441 ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n");
442 wh.wsmp = smp;
443 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
444
445 if (!dHandle) {
446 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
447 return 1;
448 }
449
450 temp = get_trash_chunk();
451 chunk_reset(temp);
452
453 list_for_each_entry(wi, &global_wurfl.information_list, list) {
454 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
455
456 switch(wi->data.type) {
457 case HA_WURFL_DATA_TYPE_UNKNOWN :
458 ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
459#ifdef WURFL_HEADER_WITH_DETAILS
460 // write WURFL property type and name before its value...
461 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name);
462#endif
463 break;
464 case HA_WURFL_DATA_TYPE_CAP :
465 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING);
466#ifdef WURFL_HEADER_WITH_DETAILS
467 // write WURFL property type and name before its value...
468 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name);
469#endif
470 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name));
471 break;
472 case HA_WURFL_DATA_TYPE_VCAP :
473 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING);
474#ifdef WURFL_HEADER_WITH_DETAILS
475 // write WURFL property type and name before its value...
476 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name);
477#endif
478 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name));
479 break;
480 case HA_WURFL_DATA_TYPE_PROPERTY :
481 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
482#ifdef WURFL_HEADER_WITH_DETAILS
483 // write WURFL property type and name before its value...
484 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wi->data.name);
485#endif
486 chunk_appendf(temp, "%s", wi->data.func_callback(global_wurfl.handle, dHandle));
487 break;
488 }
489
490 }
491
492 wurfl_device_destroy(dHandle);
493 smp->data.u.str.area = temp->area;
494 smp->data.u.str.data = temp->data;
495 return 1;
496}
497
498static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private)
499{
500 wurfl_device_handle dHandle;
501 struct buffer *temp;
502 wurfl_data_t *wn = NULL;
503 struct ebmb_node *node;
504 ha_wurfl_header_t wh;
505 int i = 0;
506
507 ha_wurfl_log("WURFL: starting ha_wurfl_get\n");
508 wh.wsmp = smp;
509 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
510
511 if (!dHandle) {
512 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
513 return 1;
514 }
515
516 temp = get_trash_chunk();
517 chunk_reset(temp);
518
519 while (args[i].data.str.area) {
520 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
521 node = ebst_lookup(&global_wurfl.btree, args[i].data.str.area);
522 wn = container_of(node, wurfl_data_t, nd);
523
524 if (wn) {
525
526 switch(wn->type) {
527 case HA_WURFL_DATA_TYPE_UNKNOWN :
528 ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
529#ifdef WURFL_HEADER_WITH_DETAILS
530 // write WURFL property type and name before its value...
531 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name);
532#endif
533 break;
534 case HA_WURFL_DATA_TYPE_CAP :
535 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING);
536#ifdef WURFL_HEADER_WITH_DETAILS
537 // write WURFL property type and name before its value...
538 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name);
539#endif
540 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name));
541 break;
542 case HA_WURFL_DATA_TYPE_VCAP :
543 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING);
544#ifdef WURFL_HEADER_WITH_DETAILS
545 // write WURFL property type and name before its value...
546 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name);
547#endif
548 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name));
549 break;
550 case HA_WURFL_DATA_TYPE_PROPERTY :
551 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_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_PROPERTY_STRING, wn->name);
555#endif
556 chunk_appendf(temp, "%s", wn->func_callback(global_wurfl.handle, dHandle));
557 break;
558 }
559
560 } else {
561 ha_wurfl_log("WURFL: %s not in wurfl-information-list \n",
562 args[i].data.str.area);
563 }
564
565 i++;
566 }
567
568 wurfl_device_destroy(dHandle);
569 smp->data.u.str.area = temp->area;
570 smp->data.u.str.data = temp->data;
571 return 1;
572}
573
574static struct cfg_kw_list wurflcfg_kws = {{ }, {
575 { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file },
576 { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator },
577 { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list },
578 { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list },
579 { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache },
580 { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode },
581 { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority },
582 { 0, NULL, NULL },
583 }
584};
585
586INITCALL1(STG_REGISTER, cfg_register_keywords, &wurflcfg_kws);
587
588/* Note: must not be declared <const> as its list will be overwritten */
589static struct sample_fetch_kw_list fetch_kws = {ILH, {
590 { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
591 { "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 },
592 { NULL, NULL, 0, 0, 0 },
593 }
594};
595
596INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws);
597
598/* Note: must not be declared <const> as its list will be overwritten */
599static struct sample_conv_kw_list conv_kws = {ILH, {
600 { NULL, NULL, 0, 0, 0 },
601 }
602};
603
604INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
605
606// WURFL properties wrapper functions
607static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
608{
609 return wurfl_device_get_root_id(dHandle);
610}
611
612static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
613{
614 return wurfl_device_get_id(dHandle);
615}
616
617static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle)
618{
619 if (wurfl_device_is_actual_device_root(dHandle))
620 return HA_WURFL_ISDEVROOT_TRUE;
621 else
622 return HA_WURFL_ISDEVROOT_FALSE;
623}
624
625static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
626{
627 return wurfl_device_get_original_useragent(dHandle);
628}
629
630static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle)
631{
632 return wurfl_get_api_version();
633}
634
635static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle)
636{
paulborilebad132c2019-04-18 11:57:04 +0200637 return "default";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200638}
639
640static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle)
641{
642 return wurfl_get_wurfl_info(wHandle);
643}
644
645static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle)
646{
647 return wurfl_get_last_load_time_as_string(wHandle);
648}
649
650static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
651{
652 return wurfl_device_get_normalized_useragent(dHandle);
653}
654
655static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle)
656{
paulborilebad132c2019-04-18 11:57:04 +0200657 return "default";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200658}
659
660// call function for WURFL properties
661static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle)
662{
663 int position;
664 int begin = 0;
665 int end = HA_WURFL_PROPERTIES_NBR - 1;
666 int cond = 0;
667
668 while(begin <= end) {
669 position = (begin + end) / 2;
670
671 if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) {
672 ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name );
673 return wurfl_properties_function_map[position].func;
674 } else if(cond < 0)
675 begin = position + 1;
676 else
677 end = position - 1;
678
679 }
680
681 return NULL;
682}
683
684static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh)
685{
686 struct sample *smp;
687 struct hdr_idx *idx;
688 struct hdr_ctx ctx;
689 const struct http_msg *msg;
690 int header_len = HA_WURFL_MAX_HEADER_LENGTH;
691
692 ha_wurfl_log("WURFL: retrieve header request [%s]\n", header_name);
693 smp = ((ha_wurfl_header_t *)wh)->wsmp;
694 idx = &smp->strm->txn->hdr_idx;
695 msg = &smp->strm->txn->req;
696 ctx.idx = 0;
697
paulborile59d50142019-04-17 12:20:42 +0200698 if (http_find_full_header2(header_name, strlen(header_name), ci_head(msg->chn), idx, &ctx) == 0)
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200699 return 0;
700
701 if (header_len > ctx.vlen)
702 header_len = ctx.vlen;
703
704 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.line + ctx.val, header_len);
705 ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0';
706 ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value);
707 return ((ha_wurfl_header_t *)wh)->header_value;
708}
709
Willy Tarreaub5188232019-04-19 16:28:53 +0200710static void ha_wurfl_register_build_options()
711{
712 const char *ver = wurfl_get_api_version();
713 char *ptr = NULL;
714
715 memprintf(&ptr, "Built with WURFL support (%sversion %s)",
716 strcmp(ver, "1.11.2.100") ? "" : "dummy library ",
717 ver);
718 hap_register_build_opts(ptr, 1);
719}
720
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200721REGISTER_POST_CHECK(ha_wurfl_init);
722REGISTER_POST_DEINIT(ha_wurfl_deinit);
Willy Tarreaub5188232019-04-19 16:28:53 +0200723INITCALL0(STG_REGISTER, ha_wurfl_register_build_options);