blob: 27e673d0214cbcf5cc47ea8fe17f8cb443e70a8d [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>
mbellomi2c077002019-05-21 17:15:07 +020013#include <proto/http_fetch.h>
14#include <proto/http_htx.h>
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020015#include <proto/sample.h>
16#include <ebsttree.h>
17#include <ebmbtree.h>
18
19#include <wurfl/wurfl.h>
20
21static struct {
22 char *data_file; /* the WURFL data file */
23 char *cache_size; /* the WURFL cache parameters */
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020024 struct list patch_file_list; /* the list of WURFL patch file to use */
25 char information_list_separator; /* the separator used in request to separate values */
26 struct list information_list; /* the list of WURFL data to return into request */
27 void *handle; /* the handle to WURFL engine */
28 struct eb_root btree; /* btree containing info (name/type) on WURFL data to return */
29} global_wurfl = {
30 .data_file = NULL,
31 .cache_size = NULL,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020032 .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};
37
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
57typedef 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
mbellomiae4fcf12019-05-21 17:15:59 +020073static const char HA_WURFL_MODULE_VERSION[] = "2.0";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020074static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE";
75static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020076
77static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown";
78static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability";
79static const char HA_WURFL_DATA_TYPE_VCAP_STRING[] = "virtual_capability";
80static const char HA_WURFL_DATA_TYPE_PROPERTY_STRING[] = "property";
81
82static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh);
83static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
84static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
85static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle);
86static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
87static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle);
88static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle);
89static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle);
90static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle);
91static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
92static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle);
93static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle);
94
95// ordered property=>function map, suitable for binary search
96static const struct {
97 const char *name;
98 const char *(*func)(wurfl_handle wHandle, wurfl_device_handle dHandle);
99} wurfl_properties_function_map [] = {
100 {"wurfl_api_version", ha_wurfl_get_wurfl_api_version},
paulborilebad132c2019-04-18 11:57:04 +0200101 {"wurfl_engine_target", ha_wurfl_get_wurfl_engine_target}, // kept for backward conf file compat
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200102 {"wurfl_id", ha_wurfl_get_wurfl_id },
103 {"wurfl_info", ha_wurfl_get_wurfl_info },
104 {"wurfl_isdevroot", ha_wurfl_get_wurfl_isdevroot},
105 {"wurfl_last_load_time", ha_wurfl_get_wurfl_last_load_time},
106 {"wurfl_normalized_useragent", ha_wurfl_get_wurfl_normalized_useragent},
mbellomif9ea1e22019-05-21 15:51:46 +0200107 {"wurfl_root_id", ha_wurfl_get_wurfl_root_id},
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200108 {"wurfl_useragent", ha_wurfl_get_wurfl_useragent},
paulborilebad132c2019-04-18 11:57:04 +0200109 {"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority }, // kept for backward conf file compat
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200110};
111static const int HA_WURFL_PROPERTIES_NBR = 10;
112
113typedef struct {
114 struct list list;
115 wurfl_data_t data;
116} wurfl_information_t;
117
118typedef struct {
119 struct list list;
120 char *patch_file_path;
121} wurfl_patches_t;
122
123typedef struct {
124 struct sample *wsmp;
125 char header_value[HA_WURFL_MAX_HEADER_LENGTH + 1];
126} ha_wurfl_header_t;
127
128/*
129 * configuration parameters parsing functions
130 */
131static int ha_wurfl_cfg_data_file(char **args, int section_type, struct proxy *curpx,
132 struct proxy *defpx, const char *file, int line,
133 char **err)
134{
135
136 if (*(args[1]) == 0) {
137 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
138 return -1;
139 }
140
141 global_wurfl.data_file = strdup(args[1]);
142 return 0;
143}
144
145static int ha_wurfl_cfg_cache(char **args, int section_type, struct proxy *curpx,
146 struct proxy *defpx, const char *file, int line,
147 char **err)
148{
149 if (*(args[1]) == 0) {
150 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
151 return -1;
152 }
153
154 global_wurfl.cache_size = strdup(args[1]);
155 return 0;
156}
157
158static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy *curpx,
159 struct proxy *defpx, const char *file, int line,
160 char **err)
161{
paulborilebad132c2019-04-18 11:57:04 +0200162 // kept for backward conf file compat
163 return 0;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200164}
165
166static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx,
167 struct proxy *defpx, const char *file, int line,
168 char **err)
169{
170 if (*(args[1]) == 0) {
171 memprintf(err, "WURFL: %s expects a single character.\n", args[0]);
172 return -1;
173 }
174
175 if (strlen(args[1]) > 1) {
176 memprintf(err, "WURFL: %s expects a single character, got %s.\n", args[0], args[1]);
177 return -1;
178 }
179
180 global_wurfl.information_list_separator = *args[1];
181 return 0;
182}
183
184static int ha_wurfl_cfg_information_list(char **args, int section_type, struct proxy *curpx,
185 struct proxy *defpx, const char *file, int line,
186 char **err)
187{
188 int argIdx = 1;
189 wurfl_information_t *wi;
190
191 if (*(args[argIdx]) == 0) {
192 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
193 return -1;
194 }
195
196 while (*(args[argIdx])) {
197 wi = calloc(1, sizeof(*wi));
198
199 if (wi == NULL) {
200 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
201 return -1;
202 }
203
204 wi->data.name = strdup(args[argIdx]);
205 wi->data.type = HA_WURFL_DATA_TYPE_UNKNOWN;
206 wi->data.func_callback = NULL;
207 LIST_ADDQ(&global_wurfl.information_list, &wi->list);
208 ++argIdx;
209 }
210
211 return 0;
212}
213
214static int ha_wurfl_cfg_patch_file_list(char **args, int section_type, struct proxy *curpx,
215 struct proxy *defpx, const char *file, int line,
216 char **err)
217{
218 int argIdx = 1;
219 wurfl_patches_t *wp;
220
221 if (*(args[argIdx]) == 0) {
222 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
223 return -1;
224 }
225
226 while (*(args[argIdx])) {
227 wp = calloc(1, sizeof(*wp));
228
229 if (wp == NULL) {
230 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
231 return -1;
232 }
233
234 wp->patch_file_path = strdup(args[argIdx]);
235 LIST_ADDQ(&global_wurfl.patch_file_list, &wp->list);
236 ++argIdx;
237 }
238
239 return 0;
240}
241
242static int ha_wurfl_cfg_useragent_priority(char **args, int section_type, struct proxy *curpx,
243 struct proxy *defpx, const char *file, int line,
244 char **err)
245{
paulborilebad132c2019-04-18 11:57:04 +0200246 // this feature is deprecated, keeping only not to break compatibility
247 // with old configuration files.
248 return 0;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200249}
250
251/*
252 * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
253 */
254
255static int ha_wurfl_init(void)
256{
257 wurfl_information_t *wi;
258 wurfl_patches_t *wp;
259 wurfl_data_t * wn;
260 int wurfl_result_code = WURFL_OK;
261 int len;
262
Willy Tarreauaabbe6a2019-05-22 14:01:22 +0200263 // wurfl-data-file not configured, WURFL is not used so don't try to
264 // configure it.
265 if (global_wurfl.data_file == NULL)
266 return 0;
267
mbellomi4304e302019-05-21 16:11:42 +0200268 ha_notice("WURFL: Loading module v.%s\n", HA_WURFL_MODULE_VERSION);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200269 // creating WURFL handler
270 global_wurfl.handle = wurfl_create();
271
272 if (global_wurfl.handle == NULL) {
mbellomi4304e302019-05-21 16:11:42 +0200273 ha_warning("WURFL: Engine handler creation failed\n");
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200274 return ERR_WARN;
275 }
276
mbellomi4304e302019-05-21 16:11:42 +0200277 ha_notice("WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() );
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200278
279 // set wurfl data file
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200280 if (wurfl_set_root(global_wurfl.handle, global_wurfl.data_file) != WURFL_OK) {
281 ha_warning("WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200282 return ERR_WARN;
283 }
284
mbellomi4304e302019-05-21 16:11:42 +0200285 ha_notice("WURFL: Engine root file set to %s\n", global_wurfl.data_file);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200286 // just a log to inform which separator char has to be used
mbellomi4304e302019-05-21 16:11:42 +0200287 ha_notice("WURFL: Information list separator set to '%c'\n", global_wurfl.information_list_separator);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200288
289 // load wurfl data needed ( and filter whose are supposed to be capabilities )
290 if (LIST_ISEMPTY(&global_wurfl.information_list)) {
291 ha_warning("WURFL: missing wurfl-information-list parameter in global configuration\n");
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200292 return ERR_WARN;
293 } else {
294 // ebtree initialization
295 global_wurfl.btree = EB_ROOT;
296
297 // checking if informations are valid WURFL data ( cap, vcaps, properties )
298 list_for_each_entry(wi, &global_wurfl.information_list, list) {
299 // check if information is already loaded looking into btree
300 if (ebst_lookup(&global_wurfl.btree, wi->data.name) == NULL) {
301 if ((wi->data.func_callback = (PROP_CALLBACK_FUNC) ha_wurfl_get_property_callback(wi->data.name)) != NULL) {
302 wi->data.type = HA_WURFL_DATA_TYPE_PROPERTY;
mbellomi4304e302019-05-21 16:11:42 +0200303#ifdef WURFL_DEBUG
304 ha_notice("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name);
305#endif
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200306 } else if (wurfl_has_virtual_capability(global_wurfl.handle, wi->data.name)) {
307 wi->data.type = HA_WURFL_DATA_TYPE_VCAP;
mbellomi4304e302019-05-21 16:11:42 +0200308#ifdef WURFL_DEBUG
309 ha_notice("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name);
310#endif
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200311 } else {
312 // by default a cap type is assumed to be and we control it on engine load
313 wi->data.type = HA_WURFL_DATA_TYPE_CAP;
314
315 if (wurfl_add_requested_capability(global_wurfl.handle, wi->data.name) != WURFL_OK) {
316 ha_warning("WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200317 return ERR_WARN;
318 }
319
mbellomi4304e302019-05-21 16:11:42 +0200320 ha_notice("WURFL: [%s] treated as wurfl capability. Will check its validity later, on engine load\n",wi->data.name);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200321 }
322
323 // ebtree insert here
324 len = strlen(wi->data.name);
325
326 wn = malloc(sizeof(wurfl_data_t) + len + 1);
327
328 if (wn == NULL) {
329 ha_warning("WURFL: Error allocating memory for information tree element.\n");
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200330 return ERR_WARN;
331 }
332
333 wn->name = wi->data.name;
334 wn->type = wi->data.type;
335 wn->func_callback = wi->data.func_callback;
336 memcpy(wn->nd.key, wi->data.name, len);
337 wn->nd.key[len] = 0;
338
339 if (!ebst_insert(&global_wurfl.btree, &wn->nd)) {
340 ha_warning("WURFL: [%s] not inserted in btree\n",wn->name);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200341 return ERR_WARN;
342 }
343
344 } else {
mbellomi4304e302019-05-21 16:11:42 +0200345#ifdef WURFL_DEBUG
346 ha_notice("WURFL: [%s] already loaded\n",wi->data.name);
347#endif
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200348 }
349
350 }
351
352 }
353
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200354
355 // adding WURFL patches if needed
356 if (!LIST_ISEMPTY(&global_wurfl.patch_file_list)) {
357
358 list_for_each_entry(wp, &global_wurfl.patch_file_list, list) {
359 if (wurfl_add_patch(global_wurfl.handle, wp->patch_file_path) != WURFL_OK) {
360 ha_warning("WURFL: Engine adding patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200361 return ERR_WARN;
362 }
mbellomi4304e302019-05-21 16:11:42 +0200363 ha_notice("WURFL: Engine patch file added %s\n", wp->patch_file_path);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200364
365 }
366
367 }
368
369 // setting cache provider if specified in cfg, otherwise let engine choose
370 if (global_wurfl.cache_size != NULL) {
371 if (strpbrk(global_wurfl.cache_size, ",") != NULL) {
372 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_DOUBLE_LRU, global_wurfl.cache_size) ;
373 } else {
374 if (strcmp(global_wurfl.cache_size, "0")) {
375 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_LRU, global_wurfl.cache_size) ;
376 } else {
377 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_NONE, 0);
378 }
379
380 }
381
382 if (wurfl_result_code != WURFL_OK) {
383 ha_warning("WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200384 return ERR_WARN;
385 }
386
mbellomi4304e302019-05-21 16:11:42 +0200387 ha_notice("WURFL: Cache set to [%s]\n", global_wurfl.cache_size);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200388 }
389
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200390 // loading WURFL engine
391 if (wurfl_load(global_wurfl.handle) != WURFL_OK) {
392 ha_warning("WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200393 return ERR_WARN;
394 }
395
mbellomi4304e302019-05-21 16:11:42 +0200396 ha_notice("WURFL: Engine loaded\n");
397 ha_notice("WURFL: Module load completed\n");
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200398 return 0;
399}
400
401static void ha_wurfl_deinit(void)
402{
403 wurfl_information_t *wi, *wi2;
404 wurfl_patches_t *wp, *wp2;
405
406 send_log(NULL, LOG_NOTICE, "WURFL: Unloading module v.%s\n", HA_WURFL_MODULE_VERSION);
407 wurfl_destroy(global_wurfl.handle);
408 global_wurfl.handle = NULL;
409 free(global_wurfl.data_file);
410 global_wurfl.data_file = NULL;
411 free(global_wurfl.cache_size);
412 global_wurfl.cache_size = NULL;
413
414 list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) {
415 LIST_DEL(&wi->list);
416 free(wi);
417 }
418
419 list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) {
420 LIST_DEL(&wp->list);
421 free(wp);
422 }
423
424 send_log(NULL, LOG_NOTICE, "WURFL: Module unloaded\n");
425}
426
427static int ha_wurfl_get_all(const struct arg *args, struct sample *smp, const char *kw, void *private)
428{
429 wurfl_device_handle dHandle;
430 struct buffer *temp;
431 wurfl_information_t *wi;
432 ha_wurfl_header_t wh;
mbellomi2c077002019-05-21 17:15:07 +0200433 struct channel *chn;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200434
435 ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n");
mbellomi2c077002019-05-21 17:15:07 +0200436
437 chn = (smp->strm ? &smp->strm->req : NULL);
438
439 if (smp->px->options2 & PR_O2_USE_HTX) {
440 /* HTX version */
441 struct htx *htx = smp_prefetch_htx(smp, chn, 1);
442
443 if (!htx) {
444 return 0;
445 }
446
447 } else {
448 /* Legacy version */
449 CHECK_HTTP_MESSAGE_FIRST(chn);
450 }
451
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200452 wh.wsmp = smp;
mbellomi2c077002019-05-21 17:15:07 +0200453
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200454 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
455
mbellomi98969812019-05-21 16:41:24 +0200456 temp = get_trash_chunk();
457 chunk_reset(temp);
458
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200459 if (!dHandle) {
460 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
mbellomi98969812019-05-21 16:41:24 +0200461 goto wurfl_get_all_completed;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200462 }
463
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200464 list_for_each_entry(wi, &global_wurfl.information_list, list) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200465
466 switch(wi->data.type) {
467 case HA_WURFL_DATA_TYPE_UNKNOWN :
468 ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
469#ifdef WURFL_HEADER_WITH_DETAILS
470 // write WURFL property type and name before its value...
471 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name);
472#endif
473 break;
474 case HA_WURFL_DATA_TYPE_CAP :
475 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING);
476#ifdef WURFL_HEADER_WITH_DETAILS
477 // write WURFL property type and name before its value...
478 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name);
479#endif
480 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name));
481 break;
482 case HA_WURFL_DATA_TYPE_VCAP :
483 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING);
484#ifdef WURFL_HEADER_WITH_DETAILS
485 // write WURFL property type and name before its value...
486 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name);
487#endif
488 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name));
489 break;
490 case HA_WURFL_DATA_TYPE_PROPERTY :
491 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
492#ifdef WURFL_HEADER_WITH_DETAILS
493 // write WURFL property type and name before its value...
494 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wi->data.name);
495#endif
496 chunk_appendf(temp, "%s", wi->data.func_callback(global_wurfl.handle, dHandle));
497 break;
498 }
499
mbellomie9fedf52019-05-21 16:29:22 +0200500 // append wurfl-information-list-separator
501 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200502 }
503
mbellomi98969812019-05-21 16:41:24 +0200504wurfl_get_all_completed:
505
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200506 wurfl_device_destroy(dHandle);
507 smp->data.u.str.area = temp->area;
508 smp->data.u.str.data = temp->data;
mbellomie9fedf52019-05-21 16:29:22 +0200509
510 // remove trailing wurfl-information-list-separator
511 if (temp->data) {
512 temp->area[temp->data] = '\0';
513 --smp->data.u.str.data;
514 }
515
mbellomi2c077002019-05-21 17:15:07 +0200516 smp->data.type = SMP_T_STR;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200517 return 1;
518}
519
520static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private)
521{
522 wurfl_device_handle dHandle;
523 struct buffer *temp;
524 wurfl_data_t *wn = NULL;
525 struct ebmb_node *node;
526 ha_wurfl_header_t wh;
527 int i = 0;
mbellomi2c077002019-05-21 17:15:07 +0200528 struct channel *chn;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200529
530 ha_wurfl_log("WURFL: starting ha_wurfl_get\n");
mbellomi2c077002019-05-21 17:15:07 +0200531
532 chn = (smp->strm ? &smp->strm->req : NULL);
533
534 if (smp->px->options2 & PR_O2_USE_HTX) {
535 /* HTX version */
536 struct htx *htx = smp_prefetch_htx(smp, chn, 1);
537
538 if (!htx) {
539 return 0;
540 }
541
542 } else {
543 /* Legacy version */
544 CHECK_HTTP_MESSAGE_FIRST(chn);
545 }
546
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200547 wh.wsmp = smp;
mbellomi2c077002019-05-21 17:15:07 +0200548
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200549 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
550
mbellomi98969812019-05-21 16:41:24 +0200551 temp = get_trash_chunk();
552 chunk_reset(temp);
553
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200554 if (!dHandle) {
555 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle));
mbellomi98969812019-05-21 16:41:24 +0200556 goto wurfl_get_completed;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200557 }
558
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200559 while (args[i].data.str.area) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200560 node = ebst_lookup(&global_wurfl.btree, args[i].data.str.area);
mbellomid173e932019-05-21 15:32:48 +0200561
562 if (node) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200563
mbellomid173e932019-05-21 15:32:48 +0200564 wn = container_of(node, wurfl_data_t, nd);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200565
566 switch(wn->type) {
567 case HA_WURFL_DATA_TYPE_UNKNOWN :
568 ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
569#ifdef WURFL_HEADER_WITH_DETAILS
570 // write WURFL property type and name before its value...
571 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name);
572#endif
573 break;
574 case HA_WURFL_DATA_TYPE_CAP :
575 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING);
576#ifdef WURFL_HEADER_WITH_DETAILS
577 // write WURFL property type and name before its value...
578 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name);
579#endif
580 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name));
581 break;
582 case HA_WURFL_DATA_TYPE_VCAP :
583 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING);
584#ifdef WURFL_HEADER_WITH_DETAILS
585 // write WURFL property type and name before its value...
586 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name);
587#endif
588 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name));
589 break;
590 case HA_WURFL_DATA_TYPE_PROPERTY :
591 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
592#ifdef WURFL_HEADER_WITH_DETAILS
593 // write WURFL property type and name before its value...
594 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wn->name);
595#endif
596 chunk_appendf(temp, "%s", wn->func_callback(global_wurfl.handle, dHandle));
597 break;
598 }
599
mbellomie9fedf52019-05-21 16:29:22 +0200600 // append wurfl-information-list-separator
601 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
602
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200603 } else {
604 ha_wurfl_log("WURFL: %s not in wurfl-information-list \n",
605 args[i].data.str.area);
606 }
607
608 i++;
609 }
610
mbellomi98969812019-05-21 16:41:24 +0200611wurfl_get_completed:
612
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200613 wurfl_device_destroy(dHandle);
614 smp->data.u.str.area = temp->area;
615 smp->data.u.str.data = temp->data;
mbellomie9fedf52019-05-21 16:29:22 +0200616
617 // remove trailing wurfl-information-list-separator
618 if (temp->data) {
619 temp->area[temp->data] = '\0';
620 --smp->data.u.str.data;
621 }
622
mbellomi2c077002019-05-21 17:15:07 +0200623 smp->data.type = SMP_T_STR;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200624 return 1;
625}
626
627static struct cfg_kw_list wurflcfg_kws = {{ }, {
628 { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file },
629 { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator },
630 { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list },
631 { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list },
632 { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache },
633 { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode },
634 { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority },
635 { 0, NULL, NULL },
636 }
637};
638
639INITCALL1(STG_REGISTER, cfg_register_keywords, &wurflcfg_kws);
640
641/* Note: must not be declared <const> as its list will be overwritten */
642static struct sample_fetch_kw_list fetch_kws = {ILH, {
643 { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
644 { "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 },
645 { NULL, NULL, 0, 0, 0 },
646 }
647};
648
649INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws);
650
651/* Note: must not be declared <const> as its list will be overwritten */
652static struct sample_conv_kw_list conv_kws = {ILH, {
653 { NULL, NULL, 0, 0, 0 },
654 }
655};
656
657INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
658
659// WURFL properties wrapper functions
660static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
661{
mbellomif9ea1e22019-05-21 15:51:46 +0200662 if (wurfl_device_get_root_id(dHandle))
663 return wurfl_device_get_root_id(dHandle);
664 else
665 return "";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200666}
667
668static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
669{
670 return wurfl_device_get_id(dHandle);
671}
672
673static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle)
674{
675 if (wurfl_device_is_actual_device_root(dHandle))
676 return HA_WURFL_ISDEVROOT_TRUE;
677 else
678 return HA_WURFL_ISDEVROOT_FALSE;
679}
680
681static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
682{
683 return wurfl_device_get_original_useragent(dHandle);
684}
685
686static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle)
687{
688 return wurfl_get_api_version();
689}
690
691static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle)
692{
paulborilebad132c2019-04-18 11:57:04 +0200693 return "default";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200694}
695
696static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle)
697{
698 return wurfl_get_wurfl_info(wHandle);
699}
700
701static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle)
702{
703 return wurfl_get_last_load_time_as_string(wHandle);
704}
705
706static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
707{
708 return wurfl_device_get_normalized_useragent(dHandle);
709}
710
711static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle)
712{
paulborilebad132c2019-04-18 11:57:04 +0200713 return "default";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200714}
715
716// call function for WURFL properties
717static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle)
718{
719 int position;
720 int begin = 0;
721 int end = HA_WURFL_PROPERTIES_NBR - 1;
722 int cond = 0;
723
724 while(begin <= end) {
725 position = (begin + end) / 2;
726
727 if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) {
728 ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name );
729 return wurfl_properties_function_map[position].func;
730 } else if(cond < 0)
731 begin = position + 1;
732 else
733 end = position - 1;
734
735 }
736
737 return NULL;
738}
739
740static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh)
741{
742 struct sample *smp;
mbellomi2c077002019-05-21 17:15:07 +0200743 struct channel *chn;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200744 int header_len = HA_WURFL_MAX_HEADER_LENGTH;
745
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200746 smp = ((ha_wurfl_header_t *)wh)->wsmp;
mbellomi2c077002019-05-21 17:15:07 +0200747 chn = (smp->strm ? &smp->strm->req : NULL);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200748
mbellomi2c077002019-05-21 17:15:07 +0200749 if (smp->px->options2 & PR_O2_USE_HTX) {
750 /* HTX version */
751 struct htx *htx;
752 struct http_hdr_ctx ctx;
753 struct ist name;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200754
mbellomi2c077002019-05-21 17:15:07 +0200755 ha_wurfl_log("WURFL: retrieve header (HTX) request [%s]\n", header_name);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200756
mbellomi2c077002019-05-21 17:15:07 +0200757 //the header is searched from the beginning
758 ctx.blk = NULL;
759
760 // We could skip this chek since ha_wurfl_retrieve_header is called from inside
761 // ha_wurfl_get()/ha_wurfl_get_all() that already perform the same check
762 // We choose to keep it in case ha_wurfl_retrieve_header will be called directly
763 htx = smp_prefetch_htx(smp, chn, 1);
764 if (!htx) {
765 return NULL;
766 }
767
768 name.ptr = (char *)header_name;
769 name.len = strlen(header_name);
770
771 // If 4th param is set, it works on full-line headers in whose comma is not a delimiter but is
772 // part of the syntax
773 if (!http_find_header(htx, name, &ctx, 1)) {
774 return NULL;
775 }
776
777 if (header_len > ctx.value.len)
778 header_len = ctx.value.len;
779
780 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.value.ptr, header_len);
781
782 } else {
783 /* Legacy version */
784 struct http_txn *txn;
785 struct hdr_idx *idx;
786 struct hdr_ctx ctx;
787 int res;
788
789 ha_wurfl_log("WURFL: retrieve header (legacy) request [%s]\n", header_name);
790
791 // We could skip this chek since ha_wurfl_retrieve_header is called from inside
792 // ha_wurfl_get()/ha_wurfl_get_all() that already perform the same check
793 // We choose to keep it in case ha_wurfl_retrieve_header will be called directly
794 // This is a version of CHECK_HTTP_MESSAGE_FIRST(chn) which returns NULL in case of error
795 res = smp_prefetch_http(smp->px, smp->strm, smp->opt, (chn), smp, 1);
796 if (res <= 0) {
797 return NULL;
798 }
799
800 txn = smp->strm->txn;
801 idx = &txn->hdr_idx;
802
803 ctx.idx = 0;
804
805 if (http_find_full_header2(header_name, strlen(header_name), ci_head(chn), idx, &ctx) == 0)
806 return NULL;
807
808 if (header_len > ctx.vlen)
809 header_len = ctx.vlen;
810
811 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.line + ctx.val, header_len);
812
813 }
814
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200815 ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0';
mbellomi2c077002019-05-21 17:15:07 +0200816
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200817 ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value);
818 return ((ha_wurfl_header_t *)wh)->header_value;
819}
820
Willy Tarreaub5188232019-04-19 16:28:53 +0200821static void ha_wurfl_register_build_options()
822{
823 const char *ver = wurfl_get_api_version();
824 char *ptr = NULL;
825
826 memprintf(&ptr, "Built with WURFL support (%sversion %s)",
827 strcmp(ver, "1.11.2.100") ? "" : "dummy library ",
828 ver);
829 hap_register_build_opts(ptr, 1);
830}
831
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200832REGISTER_POST_CHECK(ha_wurfl_init);
833REGISTER_POST_DEINIT(ha_wurfl_deinit);
Willy Tarreaub5188232019-04-19 16:28:53 +0200834INITCALL0(STG_REGISTER, ha_wurfl_register_build_options);