blob: 4df64739025481eb6b1fd2f0e39f347816a98257 [file] [log] [blame]
Willy Tarreaub3cc9f22019-04-19 16:03:32 +02001#include <stdio.h>
2#include <stdarg.h>
3
Willy Tarreau2bd86282021-05-08 12:56:20 +02004#include <import/ebmbtree.h>
5#include <import/ebsttree.h>
6
Willy Tarreau4c7e4b72020-05-27 12:58:42 +02007#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +02008#include <haproxy/arg.h>
Willy Tarreau2741c8c2020-06-02 11:28:02 +02009#include <haproxy/buf-t.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020010#include <haproxy/cfgparse.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020011#include <haproxy/chunk.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020012#include <haproxy/errors.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020013#include <haproxy/global.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +020014#include <haproxy/http_ana.h>
Willy Tarreau126ba3a2020-06-04 18:26:43 +020015#include <haproxy/http_fetch.h>
Willy Tarreau87735332020-06-04 09:08:41 +020016#include <haproxy/http_htx.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020017#include <haproxy/log.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020018#include <haproxy/sample.h>
Willy Tarreau2bd86282021-05-08 12:56:20 +020019#include <haproxy/tools.h>
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020020
21#include <wurfl/wurfl.h>
22
23static struct {
24 char *data_file; /* the WURFL data file */
25 char *cache_size; /* the WURFL cache parameters */
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020026 struct list patch_file_list; /* the list of WURFL patch file to use */
27 char information_list_separator; /* the separator used in request to separate values */
28 struct list information_list; /* the list of WURFL data to return into request */
29 void *handle; /* the handle to WURFL engine */
30 struct eb_root btree; /* btree containing info (name/type) on WURFL data to return */
31} global_wurfl = {
32 .data_file = NULL,
33 .cache_size = NULL,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020034 .information_list_separator = ',',
35 .information_list = LIST_HEAD_INIT(global_wurfl.information_list),
36 .patch_file_list = LIST_HEAD_INIT(global_wurfl.patch_file_list),
37 .handle = NULL,
38};
39
40#ifdef WURFL_DEBUG
41inline static void ha_wurfl_log(char * message, ...)
42{
43 char logbuf[256];
44 va_list argp;
45
46 va_start(argp, message);
47 vsnprintf(logbuf, sizeof(logbuf), message, argp);
48 va_end(argp);
Miroslav Zagoracf0eb3732019-10-14 17:15:56 +020049 send_log(NULL, LOG_NOTICE, "%s", logbuf);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020050}
51#else
52inline static void ha_wurfl_log(char * message, ...)
53{
54}
55#endif
56
57#define HA_WURFL_MAX_HEADER_LENGTH 1024
58
59typedef char *(*PROP_CALLBACK_FUNC)(wurfl_handle wHandle, wurfl_device_handle dHandle);
60
61enum wurfl_data_type {
62 HA_WURFL_DATA_TYPE_UNKNOWN = 0,
63 HA_WURFL_DATA_TYPE_CAP = 100,
64 HA_WURFL_DATA_TYPE_VCAP = 200,
65 HA_WURFL_DATA_TYPE_PROPERTY = 300
66};
67
68typedef struct {
69 char *name;
70 enum wurfl_data_type type;
71 PROP_CALLBACK_FUNC func_callback;
72 struct ebmb_node nd;
73} wurfl_data_t;
74
mbellomiae4fcf12019-05-21 17:15:59 +020075static const char HA_WURFL_MODULE_VERSION[] = "2.0";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020076static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE";
77static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE";
Willy Tarreaub3cc9f22019-04-19 16:03:32 +020078
79static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown";
80static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability";
81static const char HA_WURFL_DATA_TYPE_VCAP_STRING[] = "virtual_capability";
82static const char HA_WURFL_DATA_TYPE_PROPERTY_STRING[] = "property";
83
84static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh);
85static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
86static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
87static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle);
88static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
89static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle);
90static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle);
91static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle);
92static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle);
93static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
94static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle);
95static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle);
96
97// ordered property=>function map, suitable for binary search
98static const struct {
99 const char *name;
100 const char *(*func)(wurfl_handle wHandle, wurfl_device_handle dHandle);
101} wurfl_properties_function_map [] = {
102 {"wurfl_api_version", ha_wurfl_get_wurfl_api_version},
paulborilebad132c2019-04-18 11:57:04 +0200103 {"wurfl_engine_target", ha_wurfl_get_wurfl_engine_target}, // kept for backward conf file compat
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200104 {"wurfl_id", ha_wurfl_get_wurfl_id },
105 {"wurfl_info", ha_wurfl_get_wurfl_info },
106 {"wurfl_isdevroot", ha_wurfl_get_wurfl_isdevroot},
107 {"wurfl_last_load_time", ha_wurfl_get_wurfl_last_load_time},
108 {"wurfl_normalized_useragent", ha_wurfl_get_wurfl_normalized_useragent},
mbellomif9ea1e22019-05-21 15:51:46 +0200109 {"wurfl_root_id", ha_wurfl_get_wurfl_root_id},
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200110 {"wurfl_useragent", ha_wurfl_get_wurfl_useragent},
paulborilebad132c2019-04-18 11:57:04 +0200111 {"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority }, // kept for backward conf file compat
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200112};
113static const int HA_WURFL_PROPERTIES_NBR = 10;
114
115typedef struct {
116 struct list list;
117 wurfl_data_t data;
118} wurfl_information_t;
119
120typedef struct {
121 struct list list;
122 char *patch_file_path;
123} wurfl_patches_t;
124
125typedef struct {
126 struct sample *wsmp;
127 char header_value[HA_WURFL_MAX_HEADER_LENGTH + 1];
128} ha_wurfl_header_t;
129
130/*
131 * configuration parameters parsing functions
132 */
133static int ha_wurfl_cfg_data_file(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100134 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200135 char **err)
136{
137
138 if (*(args[1]) == 0) {
139 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
140 return -1;
141 }
142
143 global_wurfl.data_file = strdup(args[1]);
144 return 0;
145}
146
147static int ha_wurfl_cfg_cache(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100148 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200149 char **err)
150{
151 if (*(args[1]) == 0) {
152 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
153 return -1;
154 }
155
156 global_wurfl.cache_size = strdup(args[1]);
157 return 0;
158}
159
160static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100161 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200162 char **err)
163{
paulborilebad132c2019-04-18 11:57:04 +0200164 // kept for backward conf file compat
165 return 0;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200166}
167
168static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100169 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200170 char **err)
171{
172 if (*(args[1]) == 0) {
173 memprintf(err, "WURFL: %s expects a single character.\n", args[0]);
174 return -1;
175 }
176
177 if (strlen(args[1]) > 1) {
178 memprintf(err, "WURFL: %s expects a single character, got %s.\n", args[0], args[1]);
179 return -1;
180 }
181
182 global_wurfl.information_list_separator = *args[1];
183 return 0;
184}
185
186static int ha_wurfl_cfg_information_list(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100187 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200188 char **err)
189{
190 int argIdx = 1;
191 wurfl_information_t *wi;
192
193 if (*(args[argIdx]) == 0) {
194 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
195 return -1;
196 }
197
198 while (*(args[argIdx])) {
199 wi = calloc(1, sizeof(*wi));
200
201 if (wi == NULL) {
202 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
203 return -1;
204 }
205
206 wi->data.name = strdup(args[argIdx]);
207 wi->data.type = HA_WURFL_DATA_TYPE_UNKNOWN;
208 wi->data.func_callback = NULL;
Willy Tarreau2b718102021-04-21 07:32:39 +0200209 LIST_APPEND(&global_wurfl.information_list, &wi->list);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200210 ++argIdx;
211 }
212
213 return 0;
214}
215
216static int ha_wurfl_cfg_patch_file_list(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100217 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200218 char **err)
219{
220 int argIdx = 1;
221 wurfl_patches_t *wp;
222
223 if (*(args[argIdx]) == 0) {
224 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
225 return -1;
226 }
227
228 while (*(args[argIdx])) {
229 wp = calloc(1, sizeof(*wp));
230
231 if (wp == NULL) {
232 memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]);
233 return -1;
234 }
235
236 wp->patch_file_path = strdup(args[argIdx]);
Willy Tarreau2b718102021-04-21 07:32:39 +0200237 LIST_APPEND(&global_wurfl.patch_file_list, &wp->list);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200238 ++argIdx;
239 }
240
241 return 0;
242}
243
244static int ha_wurfl_cfg_useragent_priority(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100245 const struct proxy *defpx, const char *file, int line,
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200246 char **err)
247{
paulborilebad132c2019-04-18 11:57:04 +0200248 // this feature is deprecated, keeping only not to break compatibility
249 // with old configuration files.
250 return 0;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200251}
252
253/*
254 * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
255 */
256
257static int ha_wurfl_init(void)
258{
259 wurfl_information_t *wi;
260 wurfl_patches_t *wp;
261 wurfl_data_t * wn;
262 int wurfl_result_code = WURFL_OK;
263 int len;
264
Willy Tarreauaabbe6a2019-05-22 14:01:22 +0200265 // wurfl-data-file not configured, WURFL is not used so don't try to
266 // configure it.
267 if (global_wurfl.data_file == NULL)
Christopher Fauletfc633b62020-11-06 15:24:23 +0100268 return ERR_NONE;
Willy Tarreauaabbe6a2019-05-22 14:01:22 +0200269
mbellomi4304e302019-05-21 16:11:42 +0200270 ha_notice("WURFL: Loading module v.%s\n", HA_WURFL_MODULE_VERSION);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200271 // creating WURFL handler
272 global_wurfl.handle = wurfl_create();
273
274 if (global_wurfl.handle == NULL) {
mbellomi4304e302019-05-21 16:11:42 +0200275 ha_warning("WURFL: Engine handler creation failed\n");
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200276 return ERR_WARN;
277 }
278
mbellomi4304e302019-05-21 16:11:42 +0200279 ha_notice("WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() );
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200280
281 // set wurfl data file
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200282 if (wurfl_set_root(global_wurfl.handle, global_wurfl.data_file) != WURFL_OK) {
283 ha_warning("WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200284 return ERR_WARN;
285 }
286
mbellomi4304e302019-05-21 16:11:42 +0200287 ha_notice("WURFL: Engine root file set to %s\n", global_wurfl.data_file);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200288 // just a log to inform which separator char has to be used
mbellomi4304e302019-05-21 16:11:42 +0200289 ha_notice("WURFL: Information list separator set to '%c'\n", global_wurfl.information_list_separator);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200290
291 // load wurfl data needed ( and filter whose are supposed to be capabilities )
292 if (LIST_ISEMPTY(&global_wurfl.information_list)) {
293 ha_warning("WURFL: missing wurfl-information-list parameter in global configuration\n");
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200294 return ERR_WARN;
295 } else {
296 // ebtree initialization
297 global_wurfl.btree = EB_ROOT;
298
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +0500299 // checking if information is valid WURFL data ( cap, vcaps, properties )
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200300 list_for_each_entry(wi, &global_wurfl.information_list, list) {
301 // check if information is already loaded looking into btree
302 if (ebst_lookup(&global_wurfl.btree, wi->data.name) == NULL) {
303 if ((wi->data.func_callback = (PROP_CALLBACK_FUNC) ha_wurfl_get_property_callback(wi->data.name)) != NULL) {
304 wi->data.type = HA_WURFL_DATA_TYPE_PROPERTY;
mbellomi4304e302019-05-21 16:11:42 +0200305#ifdef WURFL_DEBUG
306 ha_notice("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name);
307#endif
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200308 } else if (wurfl_has_virtual_capability(global_wurfl.handle, wi->data.name)) {
309 wi->data.type = HA_WURFL_DATA_TYPE_VCAP;
mbellomi4304e302019-05-21 16:11:42 +0200310#ifdef WURFL_DEBUG
311 ha_notice("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name);
312#endif
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200313 } else {
314 // by default a cap type is assumed to be and we control it on engine load
315 wi->data.type = HA_WURFL_DATA_TYPE_CAP;
316
317 if (wurfl_add_requested_capability(global_wurfl.handle, wi->data.name) != WURFL_OK) {
318 ha_warning("WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200319 return ERR_WARN;
320 }
321
mbellomi4304e302019-05-21 16:11:42 +0200322 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 +0200323 }
324
325 // ebtree insert here
326 len = strlen(wi->data.name);
327
328 wn = malloc(sizeof(wurfl_data_t) + len + 1);
329
330 if (wn == NULL) {
331 ha_warning("WURFL: Error allocating memory for information tree element.\n");
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200332 return ERR_WARN;
333 }
334
335 wn->name = wi->data.name;
336 wn->type = wi->data.type;
337 wn->func_callback = wi->data.func_callback;
338 memcpy(wn->nd.key, wi->data.name, len);
339 wn->nd.key[len] = 0;
340
341 if (!ebst_insert(&global_wurfl.btree, &wn->nd)) {
342 ha_warning("WURFL: [%s] not inserted in btree\n",wn->name);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200343 return ERR_WARN;
344 }
345
346 } else {
mbellomi4304e302019-05-21 16:11:42 +0200347#ifdef WURFL_DEBUG
348 ha_notice("WURFL: [%s] already loaded\n",wi->data.name);
349#endif
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200350 }
351
352 }
353
354 }
355
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200356
357 // adding WURFL patches if needed
358 if (!LIST_ISEMPTY(&global_wurfl.patch_file_list)) {
359
360 list_for_each_entry(wp, &global_wurfl.patch_file_list, list) {
361 if (wurfl_add_patch(global_wurfl.handle, wp->patch_file_path) != WURFL_OK) {
362 ha_warning("WURFL: Engine adding patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200363 return ERR_WARN;
364 }
mbellomi4304e302019-05-21 16:11:42 +0200365 ha_notice("WURFL: Engine patch file added %s\n", wp->patch_file_path);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200366
367 }
368
369 }
370
371 // setting cache provider if specified in cfg, otherwise let engine choose
372 if (global_wurfl.cache_size != NULL) {
373 if (strpbrk(global_wurfl.cache_size, ",") != NULL) {
374 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_DOUBLE_LRU, global_wurfl.cache_size) ;
375 } else {
376 if (strcmp(global_wurfl.cache_size, "0")) {
377 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_LRU, global_wurfl.cache_size) ;
378 } else {
379 wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_NONE, 0);
380 }
381
382 }
383
384 if (wurfl_result_code != WURFL_OK) {
385 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 +0200386 return ERR_WARN;
387 }
388
mbellomi4304e302019-05-21 16:11:42 +0200389 ha_notice("WURFL: Cache set to [%s]\n", global_wurfl.cache_size);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200390 }
391
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200392 // loading WURFL engine
393 if (wurfl_load(global_wurfl.handle) != WURFL_OK) {
394 ha_warning("WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle));
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200395 return ERR_WARN;
396 }
397
mbellomi4304e302019-05-21 16:11:42 +0200398 ha_notice("WURFL: Engine loaded\n");
399 ha_notice("WURFL: Module load completed\n");
Christopher Fauletfc633b62020-11-06 15:24:23 +0100400 return ERR_NONE;
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200401}
402
403static void ha_wurfl_deinit(void)
404{
405 wurfl_information_t *wi, *wi2;
406 wurfl_patches_t *wp, *wp2;
407
408 send_log(NULL, LOG_NOTICE, "WURFL: Unloading module v.%s\n", HA_WURFL_MODULE_VERSION);
409 wurfl_destroy(global_wurfl.handle);
410 global_wurfl.handle = NULL;
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100411 ha_free(&global_wurfl.data_file);
412 ha_free(&global_wurfl.cache_size);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200413
414 list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200415 LIST_DELETE(&wi->list);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200416 free(wi);
417 }
418
419 list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200420 LIST_DELETE(&wp->list);
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200421 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
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100747 name = ist2((char *)header_name, strlen(header_name));
mbellomi2c077002019-05-21 17:15:07 +0200748
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200749 // If 4th param is set, it works on full-line headers in whose comma is not a delimiter but is
750 // part of the syntax
751 if (!http_find_header(htx, name, &ctx, 1)) {
752 return NULL;
753 }
mbellomi2c077002019-05-21 17:15:07 +0200754
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200755 if (header_len > ctx.value.len)
756 header_len = ctx.value.len;
mbellomi2c077002019-05-21 17:15:07 +0200757
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200758 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.value.ptr, header_len);
mbellomi2c077002019-05-21 17:15:07 +0200759
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200760 ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0';
mbellomi2c077002019-05-21 17:15:07 +0200761
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200762 ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value);
763 return ((ha_wurfl_header_t *)wh)->header_value;
764}
765
Willy Tarreaub5188232019-04-19 16:28:53 +0200766static void ha_wurfl_register_build_options()
767{
768 const char *ver = wurfl_get_api_version();
769 char *ptr = NULL;
770
771 memprintf(&ptr, "Built with WURFL support (%sversion %s)",
772 strcmp(ver, "1.11.2.100") ? "" : "dummy library ",
773 ver);
774 hap_register_build_opts(ptr, 1);
775}
776
Willy Tarreaub3cc9f22019-04-19 16:03:32 +0200777REGISTER_POST_CHECK(ha_wurfl_init);
778REGISTER_POST_DEINIT(ha_wurfl_deinit);
Willy Tarreaub5188232019-04-19 16:28:53 +0200779INITCALL0(STG_REGISTER, ha_wurfl_register_build_options);