blob: b7eb1fef852cd084ae3a004e9d03d6e02a8b7123 [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>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020012#include <proto/http_ana.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);
Miroslav Zagoracf0eb3732019-10-14 17:15:56 +020047 send_log(NULL, LOG_NOTICE, "%s", logbuf);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020048}
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
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500297 // checking if information is valid WURFL data ( cap, vcaps, properties )
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200298 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;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200434 struct htx *htx;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200435
436 ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n");
mbellomi2c077002019-05-21 17:15:07 +0200437
438 chn = (smp->strm ? &smp->strm->req : NULL);
Christopher Fauletb565e722020-05-05 11:52:13 +0200439 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200440 if (!htx)
441 return 0;
mbellomi2c077002019-05-21 17:15:07 +0200442
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200443 wh.wsmp = smp;
mbellomi2c077002019-05-21 17:15:07 +0200444
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200445 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
446
mbellomi98969812019-05-21 16:41:24 +0200447 temp = get_trash_chunk();
448 chunk_reset(temp);
449
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200450 if (!dHandle) {
451 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 +0200452 goto wurfl_get_all_completed;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200453 }
454
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200455 list_for_each_entry(wi, &global_wurfl.information_list, list) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200456
457 switch(wi->data.type) {
458 case HA_WURFL_DATA_TYPE_UNKNOWN :
459 ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
460#ifdef WURFL_HEADER_WITH_DETAILS
461 // write WURFL property type and name before its value...
462 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name);
463#endif
464 break;
465 case HA_WURFL_DATA_TYPE_CAP :
466 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING);
467#ifdef WURFL_HEADER_WITH_DETAILS
468 // write WURFL property type and name before its value...
469 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name);
470#endif
471 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name));
472 break;
473 case HA_WURFL_DATA_TYPE_VCAP :
474 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING);
475#ifdef WURFL_HEADER_WITH_DETAILS
476 // write WURFL property type and name before its value...
477 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name);
478#endif
479 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name));
480 break;
481 case HA_WURFL_DATA_TYPE_PROPERTY :
482 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
483#ifdef WURFL_HEADER_WITH_DETAILS
484 // write WURFL property type and name before its value...
485 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wi->data.name);
486#endif
487 chunk_appendf(temp, "%s", wi->data.func_callback(global_wurfl.handle, dHandle));
488 break;
489 }
490
mbellomie9fedf52019-05-21 16:29:22 +0200491 // append wurfl-information-list-separator
492 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200493 }
494
mbellomi98969812019-05-21 16:41:24 +0200495wurfl_get_all_completed:
496
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200497 wurfl_device_destroy(dHandle);
498 smp->data.u.str.area = temp->area;
499 smp->data.u.str.data = temp->data;
mbellomie9fedf52019-05-21 16:29:22 +0200500
501 // remove trailing wurfl-information-list-separator
502 if (temp->data) {
503 temp->area[temp->data] = '\0';
504 --smp->data.u.str.data;
505 }
506
mbellomi2c077002019-05-21 17:15:07 +0200507 smp->data.type = SMP_T_STR;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200508 return 1;
509}
510
511static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private)
512{
513 wurfl_device_handle dHandle;
514 struct buffer *temp;
515 wurfl_data_t *wn = NULL;
516 struct ebmb_node *node;
517 ha_wurfl_header_t wh;
518 int i = 0;
mbellomi2c077002019-05-21 17:15:07 +0200519 struct channel *chn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200520 struct htx *htx;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200521
522 ha_wurfl_log("WURFL: starting ha_wurfl_get\n");
mbellomi2c077002019-05-21 17:15:07 +0200523
524 chn = (smp->strm ? &smp->strm->req : NULL);
Christopher Fauletb565e722020-05-05 11:52:13 +0200525 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200526 if (!htx)
527 return 0;
mbellomi2c077002019-05-21 17:15:07 +0200528
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200529 wh.wsmp = smp;
mbellomi2c077002019-05-21 17:15:07 +0200530
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200531 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
532
mbellomi98969812019-05-21 16:41:24 +0200533 temp = get_trash_chunk();
534 chunk_reset(temp);
535
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200536 if (!dHandle) {
537 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 +0200538 goto wurfl_get_completed;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200539 }
540
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200541 while (args[i].data.str.area) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200542 node = ebst_lookup(&global_wurfl.btree, args[i].data.str.area);
mbellomid173e932019-05-21 15:32:48 +0200543
544 if (node) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200545
mbellomid173e932019-05-21 15:32:48 +0200546 wn = container_of(node, wurfl_data_t, nd);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200547
548 switch(wn->type) {
549 case HA_WURFL_DATA_TYPE_UNKNOWN :
550 ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
551#ifdef WURFL_HEADER_WITH_DETAILS
552 // write WURFL property type and name before its value...
553 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name);
554#endif
555 break;
556 case HA_WURFL_DATA_TYPE_CAP :
557 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING);
558#ifdef WURFL_HEADER_WITH_DETAILS
559 // write WURFL property type and name before its value...
560 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name);
561#endif
562 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name));
563 break;
564 case HA_WURFL_DATA_TYPE_VCAP :
565 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING);
566#ifdef WURFL_HEADER_WITH_DETAILS
567 // write WURFL property type and name before its value...
568 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name);
569#endif
570 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name));
571 break;
572 case HA_WURFL_DATA_TYPE_PROPERTY :
573 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
574#ifdef WURFL_HEADER_WITH_DETAILS
575 // write WURFL property type and name before its value...
576 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wn->name);
577#endif
578 chunk_appendf(temp, "%s", wn->func_callback(global_wurfl.handle, dHandle));
579 break;
580 }
581
mbellomie9fedf52019-05-21 16:29:22 +0200582 // append wurfl-information-list-separator
583 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
584
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200585 } else {
586 ha_wurfl_log("WURFL: %s not in wurfl-information-list \n",
587 args[i].data.str.area);
588 }
589
590 i++;
591 }
592
mbellomi98969812019-05-21 16:41:24 +0200593wurfl_get_completed:
594
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200595 wurfl_device_destroy(dHandle);
596 smp->data.u.str.area = temp->area;
597 smp->data.u.str.data = temp->data;
mbellomie9fedf52019-05-21 16:29:22 +0200598
599 // remove trailing wurfl-information-list-separator
600 if (temp->data) {
601 temp->area[temp->data] = '\0';
602 --smp->data.u.str.data;
603 }
604
mbellomi2c077002019-05-21 17:15:07 +0200605 smp->data.type = SMP_T_STR;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200606 return 1;
607}
608
609static struct cfg_kw_list wurflcfg_kws = {{ }, {
610 { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file },
611 { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator },
612 { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list },
613 { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list },
614 { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache },
615 { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode },
616 { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority },
617 { 0, NULL, NULL },
618 }
619};
620
621INITCALL1(STG_REGISTER, cfg_register_keywords, &wurflcfg_kws);
622
623/* Note: must not be declared <const> as its list will be overwritten */
624static struct sample_fetch_kw_list fetch_kws = {ILH, {
625 { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
626 { "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 },
627 { NULL, NULL, 0, 0, 0 },
628 }
629};
630
631INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws);
632
633/* Note: must not be declared <const> as its list will be overwritten */
634static struct sample_conv_kw_list conv_kws = {ILH, {
635 { NULL, NULL, 0, 0, 0 },
636 }
637};
638
639INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
640
641// WURFL properties wrapper functions
642static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
643{
mbellomif9ea1e22019-05-21 15:51:46 +0200644 if (wurfl_device_get_root_id(dHandle))
645 return wurfl_device_get_root_id(dHandle);
646 else
647 return "";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200648}
649
650static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
651{
652 return wurfl_device_get_id(dHandle);
653}
654
655static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle)
656{
657 if (wurfl_device_is_actual_device_root(dHandle))
658 return HA_WURFL_ISDEVROOT_TRUE;
659 else
660 return HA_WURFL_ISDEVROOT_FALSE;
661}
662
663static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
664{
665 return wurfl_device_get_original_useragent(dHandle);
666}
667
668static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle)
669{
670 return wurfl_get_api_version();
671}
672
673static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle)
674{
paulborilebad132c2019-04-18 11:57:04 +0200675 return "default";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200676}
677
678static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle)
679{
680 return wurfl_get_wurfl_info(wHandle);
681}
682
683static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle)
684{
685 return wurfl_get_last_load_time_as_string(wHandle);
686}
687
688static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
689{
690 return wurfl_device_get_normalized_useragent(dHandle);
691}
692
693static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle)
694{
paulborilebad132c2019-04-18 11:57:04 +0200695 return "default";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200696}
697
698// call function for WURFL properties
699static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle)
700{
701 int position;
702 int begin = 0;
703 int end = HA_WURFL_PROPERTIES_NBR - 1;
704 int cond = 0;
705
706 while(begin <= end) {
707 position = (begin + end) / 2;
708
709 if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) {
710 ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name );
711 return wurfl_properties_function_map[position].func;
712 } else if(cond < 0)
713 begin = position + 1;
714 else
715 end = position - 1;
716
717 }
718
719 return NULL;
720}
721
722static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh)
723{
724 struct sample *smp;
mbellomi2c077002019-05-21 17:15:07 +0200725 struct channel *chn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200726 struct htx *htx;
727 struct http_hdr_ctx ctx;
728 struct ist name;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200729 int header_len = HA_WURFL_MAX_HEADER_LENGTH;
730
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200731 smp = ((ha_wurfl_header_t *)wh)->wsmp;
mbellomi2c077002019-05-21 17:15:07 +0200732 chn = (smp->strm ? &smp->strm->req : NULL);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200733
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200734 ha_wurfl_log("WURFL: retrieve header (HTX) request [%s]\n", header_name);
mbellomi2c077002019-05-21 17:15:07 +0200735
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200736 //the header is searched from the beginning
737 ctx.blk = NULL;
mbellomi2c077002019-05-21 17:15:07 +0200738
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500739 // We could skip this check since ha_wurfl_retrieve_header is called from inside
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200740 // ha_wurfl_get()/ha_wurfl_get_all() that already perform the same check
741 // We choose to keep it in case ha_wurfl_retrieve_header will be called directly
Christopher Fauletb565e722020-05-05 11:52:13 +0200742 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200743 if (!htx) {
744 return NULL;
745 }
mbellomi2c077002019-05-21 17:15:07 +0200746
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200747 name.ptr = (char *)header_name;
748 name.len = strlen(header_name);
mbellomi2c077002019-05-21 17:15:07 +0200749
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200750 // If 4th param is set, it works on full-line headers in whose comma is not a delimiter but is
751 // part of the syntax
752 if (!http_find_header(htx, name, &ctx, 1)) {
753 return NULL;
754 }
mbellomi2c077002019-05-21 17:15:07 +0200755
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200756 if (header_len > ctx.value.len)
757 header_len = ctx.value.len;
mbellomi2c077002019-05-21 17:15:07 +0200758
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200759 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.value.ptr, header_len);
mbellomi2c077002019-05-21 17:15:07 +0200760
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200761 ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0';
mbellomi2c077002019-05-21 17:15:07 +0200762
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200763 ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value);
764 return ((ha_wurfl_header_t *)wh)->header_value;
765}
766
Willy Tarreaub5188232019-04-19 16:28:53 +0200767static void ha_wurfl_register_build_options()
768{
769 const char *ver = wurfl_get_api_version();
770 char *ptr = NULL;
771
772 memprintf(&ptr, "Built with WURFL support (%sversion %s)",
773 strcmp(ver, "1.11.2.100") ? "" : "dummy library ",
774 ver);
775 hap_register_build_opts(ptr, 1);
776}
777
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200778REGISTER_POST_CHECK(ha_wurfl_init);
779REGISTER_POST_DEINIT(ha_wurfl_deinit);
Willy Tarreaub5188232019-04-19 16:28:53 +0200780INITCALL0(STG_REGISTER, ha_wurfl_register_build_options);