blob: 8ec17836d15fdc019fc960212786407b9e1746c6 [file] [log] [blame]
Willy Tarreaub3cc9f22019-04-19 16:03:32 +02001#include <stdio.h>
2#include <stdarg.h>
3
Willy Tarreau4c7e4b72020-05-27 12:58:42 +02004#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +02005#include <haproxy/arg.h>
Willy Tarreau2741c8c2020-06-02 11:28:02 +02006#include <haproxy/buf-t.h>
Willy Tarreau6be78492020-06-05 00:00:29 +02007#include <haproxy/cfgparse.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +02008#include <haproxy/chunk.h>
Willy Tarreau8d366972020-05-27 16:10:29 +02009#include <haproxy/errors.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020010#include <haproxy/global.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +020011#include <haproxy/http_ana.h>
Willy Tarreau126ba3a2020-06-04 18:26:43 +020012#include <haproxy/http_fetch.h>
Willy Tarreau87735332020-06-04 09:08:41 +020013#include <haproxy/http_htx.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020014#include <haproxy/log.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020015#include <haproxy/sample.h>
Willy Tarreau8d2b7772020-05-27 10:58:19 +020016#include <import/ebmbtree.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020017#include <import/ebsttree.h>
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020018
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,
Willy Tarreau01825162021-03-09 09:53:46 +0100132 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200133 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,
Willy Tarreau01825162021-03-09 09:53:46 +0100146 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200147 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,
Willy Tarreau01825162021-03-09 09:53:46 +0100159 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200160 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,
Willy Tarreau01825162021-03-09 09:53:46 +0100167 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200168 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,
Willy Tarreau01825162021-03-09 09:53:46 +0100185 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200186 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,
Willy Tarreau01825162021-03-09 09:53:46 +0100215 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200216 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,
Willy Tarreau01825162021-03-09 09:53:46 +0100243 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200244 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)
Christopher Fauletfc633b62020-11-06 15:24:23 +0100266 return ERR_NONE;
Willy Tarreauaabbe6a2019-05-22 14:01:22 +0200267
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");
Christopher Fauletfc633b62020-11-06 15:24:23 +0100398 return ERR_NONE;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200399}
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;
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100409 ha_free(&global_wurfl.data_file);
410 ha_free(&global_wurfl.cache_size);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200411
412 list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) {
413 LIST_DEL(&wi->list);
414 free(wi);
415 }
416
417 list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) {
418 LIST_DEL(&wp->list);
419 free(wp);
420 }
421
422 send_log(NULL, LOG_NOTICE, "WURFL: Module unloaded\n");
423}
424
425static int ha_wurfl_get_all(const struct arg *args, struct sample *smp, const char *kw, void *private)
426{
427 wurfl_device_handle dHandle;
428 struct buffer *temp;
429 wurfl_information_t *wi;
430 ha_wurfl_header_t wh;
mbellomi2c077002019-05-21 17:15:07 +0200431 struct channel *chn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200432 struct htx *htx;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200433
434 ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n");
mbellomi2c077002019-05-21 17:15:07 +0200435
436 chn = (smp->strm ? &smp->strm->req : NULL);
Christopher Fauletb565e722020-05-05 11:52:13 +0200437 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200438 if (!htx)
439 return 0;
mbellomi2c077002019-05-21 17:15:07 +0200440
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200441 wh.wsmp = smp;
mbellomi2c077002019-05-21 17:15:07 +0200442
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200443 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
444
mbellomi98969812019-05-21 16:41:24 +0200445 temp = get_trash_chunk();
446 chunk_reset(temp);
447
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200448 if (!dHandle) {
449 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 +0200450 goto wurfl_get_all_completed;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200451 }
452
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200453 list_for_each_entry(wi, &global_wurfl.information_list, list) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200454
455 switch(wi->data.type) {
456 case HA_WURFL_DATA_TYPE_UNKNOWN :
457 ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
458#ifdef WURFL_HEADER_WITH_DETAILS
459 // write WURFL property type and name before its value...
460 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name);
461#endif
462 break;
463 case HA_WURFL_DATA_TYPE_CAP :
464 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING);
465#ifdef WURFL_HEADER_WITH_DETAILS
466 // write WURFL property type and name before its value...
467 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name);
468#endif
469 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name));
470 break;
471 case HA_WURFL_DATA_TYPE_VCAP :
472 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING);
473#ifdef WURFL_HEADER_WITH_DETAILS
474 // write WURFL property type and name before its value...
475 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name);
476#endif
477 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name));
478 break;
479 case HA_WURFL_DATA_TYPE_PROPERTY :
480 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
481#ifdef WURFL_HEADER_WITH_DETAILS
482 // write WURFL property type and name before its value...
483 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wi->data.name);
484#endif
485 chunk_appendf(temp, "%s", wi->data.func_callback(global_wurfl.handle, dHandle));
486 break;
487 }
488
mbellomie9fedf52019-05-21 16:29:22 +0200489 // append wurfl-information-list-separator
490 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200491 }
492
mbellomi98969812019-05-21 16:41:24 +0200493wurfl_get_all_completed:
494
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200495 wurfl_device_destroy(dHandle);
496 smp->data.u.str.area = temp->area;
497 smp->data.u.str.data = temp->data;
mbellomie9fedf52019-05-21 16:29:22 +0200498
499 // remove trailing wurfl-information-list-separator
500 if (temp->data) {
501 temp->area[temp->data] = '\0';
502 --smp->data.u.str.data;
503 }
504
mbellomi2c077002019-05-21 17:15:07 +0200505 smp->data.type = SMP_T_STR;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200506 return 1;
507}
508
509static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private)
510{
511 wurfl_device_handle dHandle;
512 struct buffer *temp;
513 wurfl_data_t *wn = NULL;
514 struct ebmb_node *node;
515 ha_wurfl_header_t wh;
516 int i = 0;
mbellomi2c077002019-05-21 17:15:07 +0200517 struct channel *chn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200518 struct htx *htx;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200519
520 ha_wurfl_log("WURFL: starting ha_wurfl_get\n");
mbellomi2c077002019-05-21 17:15:07 +0200521
522 chn = (smp->strm ? &smp->strm->req : NULL);
Christopher Fauletb565e722020-05-05 11:52:13 +0200523 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200524 if (!htx)
525 return 0;
mbellomi2c077002019-05-21 17:15:07 +0200526
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200527 wh.wsmp = smp;
mbellomi2c077002019-05-21 17:15:07 +0200528
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200529 dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh);
530
mbellomi98969812019-05-21 16:41:24 +0200531 temp = get_trash_chunk();
532 chunk_reset(temp);
533
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200534 if (!dHandle) {
535 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 +0200536 goto wurfl_get_completed;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200537 }
538
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200539 while (args[i].data.str.area) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200540 node = ebst_lookup(&global_wurfl.btree, args[i].data.str.area);
mbellomid173e932019-05-21 15:32:48 +0200541
542 if (node) {
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200543
mbellomid173e932019-05-21 15:32:48 +0200544 wn = container_of(node, wurfl_data_t, nd);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200545
546 switch(wn->type) {
547 case HA_WURFL_DATA_TYPE_UNKNOWN :
548 ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
549#ifdef WURFL_HEADER_WITH_DETAILS
550 // write WURFL property type and name before its value...
551 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name);
552#endif
553 break;
554 case HA_WURFL_DATA_TYPE_CAP :
555 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING);
556#ifdef WURFL_HEADER_WITH_DETAILS
557 // write WURFL property type and name before its value...
558 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name);
559#endif
560 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name));
561 break;
562 case HA_WURFL_DATA_TYPE_VCAP :
563 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING);
564#ifdef WURFL_HEADER_WITH_DETAILS
565 // write WURFL property type and name before its value...
566 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name);
567#endif
568 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name));
569 break;
570 case HA_WURFL_DATA_TYPE_PROPERTY :
571 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
572#ifdef WURFL_HEADER_WITH_DETAILS
573 // write WURFL property type and name before its value...
574 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wn->name);
575#endif
576 chunk_appendf(temp, "%s", wn->func_callback(global_wurfl.handle, dHandle));
577 break;
578 }
579
mbellomie9fedf52019-05-21 16:29:22 +0200580 // append wurfl-information-list-separator
581 chunk_appendf(temp, "%c", global_wurfl.information_list_separator);
582
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200583 } else {
584 ha_wurfl_log("WURFL: %s not in wurfl-information-list \n",
585 args[i].data.str.area);
586 }
587
588 i++;
589 }
590
mbellomi98969812019-05-21 16:41:24 +0200591wurfl_get_completed:
592
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200593 wurfl_device_destroy(dHandle);
594 smp->data.u.str.area = temp->area;
595 smp->data.u.str.data = temp->data;
mbellomie9fedf52019-05-21 16:29:22 +0200596
597 // remove trailing wurfl-information-list-separator
598 if (temp->data) {
599 temp->area[temp->data] = '\0';
600 --smp->data.u.str.data;
601 }
602
mbellomi2c077002019-05-21 17:15:07 +0200603 smp->data.type = SMP_T_STR;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200604 return 1;
605}
606
607static struct cfg_kw_list wurflcfg_kws = {{ }, {
608 { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file },
609 { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator },
610 { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list },
611 { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list },
612 { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache },
613 { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode },
614 { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority },
615 { 0, NULL, NULL },
616 }
617};
618
619INITCALL1(STG_REGISTER, cfg_register_keywords, &wurflcfg_kws);
620
621/* Note: must not be declared <const> as its list will be overwritten */
622static struct sample_fetch_kw_list fetch_kws = {ILH, {
623 { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
624 { "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 },
625 { NULL, NULL, 0, 0, 0 },
626 }
627};
628
629INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws);
630
631/* Note: must not be declared <const> as its list will be overwritten */
632static struct sample_conv_kw_list conv_kws = {ILH, {
633 { NULL, NULL, 0, 0, 0 },
634 }
635};
636
637INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
638
639// WURFL properties wrapper functions
640static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
641{
mbellomif9ea1e22019-05-21 15:51:46 +0200642 if (wurfl_device_get_root_id(dHandle))
643 return wurfl_device_get_root_id(dHandle);
644 else
645 return "";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200646}
647
648static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
649{
650 return wurfl_device_get_id(dHandle);
651}
652
653static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle)
654{
655 if (wurfl_device_is_actual_device_root(dHandle))
656 return HA_WURFL_ISDEVROOT_TRUE;
657 else
658 return HA_WURFL_ISDEVROOT_FALSE;
659}
660
661static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
662{
663 return wurfl_device_get_original_useragent(dHandle);
664}
665
666static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle)
667{
668 return wurfl_get_api_version();
669}
670
671static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle)
672{
paulborilebad132c2019-04-18 11:57:04 +0200673 return "default";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200674}
675
676static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle)
677{
678 return wurfl_get_wurfl_info(wHandle);
679}
680
681static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle)
682{
683 return wurfl_get_last_load_time_as_string(wHandle);
684}
685
686static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
687{
688 return wurfl_device_get_normalized_useragent(dHandle);
689}
690
691static const char *ha_wurfl_get_wurfl_useragent_priority (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
696// call function for WURFL properties
697static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle)
698{
699 int position;
700 int begin = 0;
701 int end = HA_WURFL_PROPERTIES_NBR - 1;
702 int cond = 0;
703
704 while(begin <= end) {
705 position = (begin + end) / 2;
706
707 if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) {
708 ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name );
709 return wurfl_properties_function_map[position].func;
710 } else if(cond < 0)
711 begin = position + 1;
712 else
713 end = position - 1;
714
715 }
716
717 return NULL;
718}
719
720static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh)
721{
722 struct sample *smp;
mbellomi2c077002019-05-21 17:15:07 +0200723 struct channel *chn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200724 struct htx *htx;
725 struct http_hdr_ctx ctx;
726 struct ist name;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200727 int header_len = HA_WURFL_MAX_HEADER_LENGTH;
728
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200729 smp = ((ha_wurfl_header_t *)wh)->wsmp;
mbellomi2c077002019-05-21 17:15:07 +0200730 chn = (smp->strm ? &smp->strm->req : NULL);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200731
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200732 ha_wurfl_log("WURFL: retrieve header (HTX) request [%s]\n", header_name);
mbellomi2c077002019-05-21 17:15:07 +0200733
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200734 //the header is searched from the beginning
735 ctx.blk = NULL;
mbellomi2c077002019-05-21 17:15:07 +0200736
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500737 // We could skip this check since ha_wurfl_retrieve_header is called from inside
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200738 // ha_wurfl_get()/ha_wurfl_get_all() that already perform the same check
739 // We choose to keep it in case ha_wurfl_retrieve_header will be called directly
Christopher Fauletb565e722020-05-05 11:52:13 +0200740 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200741 if (!htx) {
742 return NULL;
743 }
mbellomi2c077002019-05-21 17:15:07 +0200744
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100745 name = ist2((char *)header_name, strlen(header_name));
mbellomi2c077002019-05-21 17:15:07 +0200746
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200747 // If 4th param is set, it works on full-line headers in whose comma is not a delimiter but is
748 // part of the syntax
749 if (!http_find_header(htx, name, &ctx, 1)) {
750 return NULL;
751 }
mbellomi2c077002019-05-21 17:15:07 +0200752
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200753 if (header_len > ctx.value.len)
754 header_len = ctx.value.len;
mbellomi2c077002019-05-21 17:15:07 +0200755
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200756 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.value.ptr, header_len);
mbellomi2c077002019-05-21 17:15:07 +0200757
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200758 ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0';
mbellomi2c077002019-05-21 17:15:07 +0200759
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200760 ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value);
761 return ((ha_wurfl_header_t *)wh)->header_value;
762}
763
Willy Tarreaub5188232019-04-19 16:28:53 +0200764static void ha_wurfl_register_build_options()
765{
766 const char *ver = wurfl_get_api_version();
767 char *ptr = NULL;
768
769 memprintf(&ptr, "Built with WURFL support (%sversion %s)",
770 strcmp(ver, "1.11.2.100") ? "" : "dummy library ",
771 ver);
772 hap_register_build_opts(ptr, 1);
773}
774
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200775REGISTER_POST_CHECK(ha_wurfl_init);
776REGISTER_POST_DEINIT(ha_wurfl_deinit);
Willy Tarreaub5188232019-04-19 16:28:53 +0200777INITCALL0(STG_REGISTER, ha_wurfl_register_build_options);