blob: 18723a6b108dece156e6c7ad98d4c39b40e7aaec [file] [log] [blame]
scientiamobiled0027ed2016-11-04 10:55:08 +01001#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 <proto/arg.h>
8#include <proto/log.h>
9#include <proto/proto_http.h>
10#include <proto/sample.h>
Willy Tarreaue5d31692016-11-08 18:47:25 +010011#include <proto/wurfl.h>
scientiamobiled0027ed2016-11-04 10:55:08 +010012#include <ebsttree.h>
13#include <ebmbtree.h>
scientiamobiled0027ed2016-11-04 10:55:08 +010014
Willy Tarreaue5d31692016-11-08 18:47:25 +010015#include <wurfl/wurfl.h>
16
scientiamobiled0027ed2016-11-04 10:55:08 +010017
18#ifdef WURFL_DEBUG
19inline static void ha_wurfl_log(char * message, ...)
20{
21 char logbuf[256];
22 va_list argp;
23
24 va_start(argp, message);
25 vsnprintf(logbuf, sizeof(logbuf), message, argp);
26 va_end(argp);
27 send_log(NULL, LOG_NOTICE, logbuf, NULL);
28}
29#else
30inline static void ha_wurfl_log(char * message, ...)
31{
32}
33#endif
34
35#define HA_WURFL_MAX_HEADER_LENGTH 1024
36
Willy Tarreaue5d31692016-11-08 18:47:25 +010037typedef char *(*PROP_CALLBACK_FUNC)(wurfl_handle wHandle, wurfl_device_handle dHandle);
38
39enum wurfl_data_type {
40 HA_WURFL_DATA_TYPE_UNKNOWN = 0,
41 HA_WURFL_DATA_TYPE_CAP = 100,
42 HA_WURFL_DATA_TYPE_VCAP = 200,
43 HA_WURFL_DATA_TYPE_PROPERTY = 300
44};
45
46typedef struct {
47 char *name;
48 enum wurfl_data_type type;
49 PROP_CALLBACK_FUNC func_callback;
50 struct ebmb_node nd;
51} wurfl_data_t;
52
scientiamobiled0027ed2016-11-04 10:55:08 +010053static const char HA_WURFL_MODULE_VERSION[] = "1.0";
54static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE";
55static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE";
56static const char HA_WURFL_TARGET_ACCURACY[] = "accuracy";
57static const char HA_WURFL_TARGET_PERFORMANCE[] = "performance";
58static const char HA_WURFL_PRIORITY_PLAIN[] = "plain";
59static const char HA_WURFL_PRIORITY_SIDELOADED_BROWSER[] = "sideloaded_browser";
60static const char HA_WURFL_MIN_ENGINE_VERSION_MANDATORY[] = "1.8.0.0";
61
62static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown";
63static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability";
64static const char HA_WURFL_DATA_TYPE_VCAP_STRING[] = "virtual_capability";
65static const char HA_WURFL_DATA_TYPE_PROPERTY_STRING[] = "property";
66
67static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh);
68static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
69static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle);
70static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle);
71static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
72static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle);
73static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle);
74static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle);
75static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle);
76static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle);
77static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle);
78static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle);
79
80// ordered property=>function map, suitable for binary search
81static const struct {
82 const char *name;
83 const char *(*func)(wurfl_handle wHandle, wurfl_device_handle dHandle);
84} wurfl_properties_function_map [] = {
85 {"wurfl_api_version", ha_wurfl_get_wurfl_api_version},
86 {"wurfl_engine_target", ha_wurfl_get_wurfl_engine_target},
87 {"wurfl_id", ha_wurfl_get_wurfl_id },
88 {"wurfl_info", ha_wurfl_get_wurfl_info },
89 {"wurfl_isdevroot", ha_wurfl_get_wurfl_isdevroot},
90 {"wurfl_last_load_time", ha_wurfl_get_wurfl_last_load_time},
91 {"wurfl_normalized_useragent", ha_wurfl_get_wurfl_normalized_useragent},
92 {"wurfl_useragent", ha_wurfl_get_wurfl_useragent},
93 {"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority },
94 {"wurfl_root_id", ha_wurfl_get_wurfl_root_id},
95};
96static const int HA_WURFL_PROPERTIES_NBR = 10;
97
98typedef struct {
99 struct list list;
100 wurfl_data_t data;
101} wurfl_information_t;
102
103typedef struct {
104 struct list list;
105 char *patch_file_path;
106} wurfl_patches_t;
107
108typedef struct {
109 struct sample *wsmp;
110 char header_value[HA_WURFL_MAX_HEADER_LENGTH + 1];
111} ha_wurfl_header_t;
112
113/*
114 * configuration parameters parsing functions
115 */
116static int ha_wurfl_cfg_data_file(char **args, int section_type, struct proxy *curpx,
117 struct proxy *defpx, const char *file, int line,
118 char **err)
119{
120
121 if (*(args[1]) == 0) {
122 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
123 return -1;
124 }
125
126 global.wurfl.data_file = strdup(args[1]);
127 return 0;
128}
129
130static int ha_wurfl_cfg_cache(char **args, int section_type, struct proxy *curpx,
131 struct proxy *defpx, const char *file, int line,
132 char **err)
133{
134 if (*(args[1]) == 0) {
135 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
136 return -1;
137 }
138
139 global.wurfl.cache_size = strdup(args[1]);
140 return 0;
141}
142
143static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy *curpx,
144 struct proxy *defpx, const char *file, int line,
145 char **err)
146{
147 if (*(args[1]) == 0) {
148 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
149 return -1;
150 }
151
152 if (!strcmp(args[1],HA_WURFL_TARGET_ACCURACY)) {
153 global.wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_ACCURACY;
154 return 0;
155 }
156
157 if (!strcmp(args[1],HA_WURFL_TARGET_PERFORMANCE)) {
158 global.wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_PERFORMANCE;
159 return 0;
160 }
161
162 memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_TARGET_PERFORMANCE, HA_WURFL_TARGET_ACCURACY);
163 return -1;
164}
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{
246 if (*(args[1]) == 0) {
247 memprintf(err, "WURFL: %s expects a value.\n", args[0]);
248 return -1;
249 }
250
251 if (!strcmp(args[1],HA_WURFL_PRIORITY_PLAIN)) {
252 global.wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT;
253 return 0;
254 }
255
256 if (!strcmp(args[1],HA_WURFL_PRIORITY_SIDELOADED_BROWSER)) {
257 global.wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_OVERRIDE_SIDELOADED_BROWSER_USERAGENT;
258 return 0;
259 }
260
261 memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_PRIORITY_PLAIN, HA_WURFL_PRIORITY_SIDELOADED_BROWSER);
262 return -1;
263}
264
265/*
266 * module init / deinit functions
267 */
268
269int ha_wurfl_init(void)
270{
271 wurfl_information_t *wi;
272 wurfl_patches_t *wp;
273 wurfl_data_t * wn;
274 int wurfl_result_code = WURFL_OK;
275 int len;
276
277 send_log(NULL, LOG_NOTICE, "WURFL: Loading module v.%s\n", HA_WURFL_MODULE_VERSION);
278 // creating WURFL handler
279 global.wurfl.handle = wurfl_create();
280
281 if (global.wurfl.handle == NULL) {
282 Warning("WURFL: Engine handler creation failed");
283 send_log(NULL, LOG_WARNING, "WURFL: Engine handler creation failed\n");
284 return -1;
285 }
286
287 send_log(NULL, LOG_NOTICE, "WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() );
288
289 // set wurfl data file
290 if (global.wurfl.data_file == NULL) {
291 Warning("WURFL: missing wurfl-data-file parameter in global configuration\n");
292 send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-data-file parameter in global configuration\n");
293 return -1;
294 }
295
296 if (wurfl_set_root(global.wurfl.handle, global.wurfl.data_file) != WURFL_OK) {
297 Warning("WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
298 send_log(NULL, LOG_WARNING, "WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
299 return -1;
300 }
301
302 send_log(NULL, LOG_NOTICE, "WURFL: Engine root file set to %s\n", global.wurfl.data_file);
303 // just a log to inform which separator char has to be used
304 send_log(NULL, LOG_NOTICE, "WURFL: Information list separator set to '%c'\n", global.wurfl.information_list_separator);
305
306 // load wurfl data needed ( and filter whose are supposed to be capabilities )
307 if (LIST_ISEMPTY(&global.wurfl.information_list)) {
308 Warning("WURFL: missing wurfl-information-list parameter in global configuration\n");
309 send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-information-list parameter in global configuration\n");
310 return -1;
311 } else {
312 // ebtree initialization
313 global.wurfl.btree = EB_ROOT;
314
315 // checking if informations are valid WURFL data ( cap, vcaps, properties )
316 list_for_each_entry(wi, &global.wurfl.information_list, list) {
317
318 // check if information is already loaded looking into btree
319 if (ebst_lookup(&global.wurfl.btree, wi->data.name) == NULL) {
320
321 if ((wi->data.func_callback = (PROP_CALLBACK_FUNC) ha_wurfl_get_property_callback(wi->data.name)) != NULL) {
322 wi->data.type = HA_WURFL_DATA_TYPE_PROPERTY;
323 ha_wurfl_log("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name);
324 } else if (wurfl_has_virtual_capability(global.wurfl.handle, wi->data.name)) {
325 wi->data.type = HA_WURFL_DATA_TYPE_VCAP;
326 ha_wurfl_log("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name);
327 } else {
328 // by default a cap type is assumed to be and we control it on engine load
329 wi->data.type = HA_WURFL_DATA_TYPE_CAP;
330
331 if (wurfl_add_requested_capability(global.wurfl.handle, wi->data.name) != WURFL_OK) {
332 Warning("WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
333 send_log(NULL, LOG_WARNING, "WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
334 return -1;
335 }
336
337 ha_wurfl_log("WURFL: [%s] treated as wurfl capability. Will check its validity later, on engine load\n",wi->data.name);
338 }
339
340 // ebtree insert here
341 len = strlen(wi->data.name);
342
343 wn = malloc(sizeof(wurfl_data_t) + len + 1);
344
345 if (wn == NULL) {
346 Warning("WURFL: Error allocating memory for information tree element.\n");
347 send_log(NULL, LOG_WARNING, "WURFL: Error allocating memory for information tree element.\n");
348 return -1;
349 }
350
351 wn->name = wi->data.name;
352 wn->type = wi->data.type;
353 wn->func_callback = wi->data.func_callback;
354 memcpy(wn->nd.key, wi->data.name, len);
355 wn->nd.key[len] = 0;
356
357 if (!ebst_insert(&global.wurfl.btree, &wn->nd)) {
358 Warning("WURFL: [%s] not inserted in btree\n",wn->name);
359 send_log(NULL, LOG_WARNING, "WURFL: [%s] not inserted in btree\n",wn->name);
360 return -1;
361 }
362
363 } else {
364 ha_wurfl_log("WURFL: [%s] already loaded\n",wi->data.name);
365 }
366
367 }
368
369 }
370
371 // filtering mandatory capabilities if engine version < 1.8.0.0
372 if (strcmp(wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY) < 0) {
373 wurfl_capability_enumerator_handle hmandatorycapabilityenumerator;
374 ha_wurfl_log("WURFL: Engine version %s < %s - Filtering mandatory capabilities\n", wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY);
375 hmandatorycapabilityenumerator = wurfl_get_mandatory_capability_enumerator(global.wurfl.handle);
376
377 while (wurfl_capability_enumerator_is_valid(hmandatorycapabilityenumerator)) {
378 char *name = (char *)wurfl_capability_enumerator_get_name(hmandatorycapabilityenumerator);
379
380 if (ebst_lookup(&global.wurfl.btree, name) == NULL) {
381
382 if (wurfl_add_requested_capability(global.wurfl.handle, name) != WURFL_OK) {
383 Warning("WURFL: Engine adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global.wurfl.handle));
384 send_log(NULL, LOG_WARNING, "WURFL: Adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global.wurfl.handle));
385 return -1;
386 }
387
388 ha_wurfl_log("WURFL: Mandatory capability [%s] added\n", name);
389 } else {
390 ha_wurfl_log("WURFL: Mandatory capability [%s] already filtered\n", name);
391 }
392
393 wurfl_capability_enumerator_move_next(hmandatorycapabilityenumerator);
394 }
395
396 wurfl_capability_enumerator_destroy(hmandatorycapabilityenumerator);
397 }
398
399 // adding WURFL patches if needed
400 if (!LIST_ISEMPTY(&global.wurfl.patch_file_list)) {
401
402 list_for_each_entry(wp, &global.wurfl.patch_file_list, list) {
403
404 if (wurfl_add_patch(global.wurfl.handle, wp->patch_file_path) != WURFL_OK) {
405 Warning("WURFL: Engine adding patch file failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
406 send_log(NULL, LOG_WARNING, "WURFL: Adding engine patch file failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
407 return -1;
408 }
409 send_log(NULL, LOG_NOTICE, "WURFL: Engine patch file added %s\n", wp->patch_file_path);
410
411 }
412
413 }
414
415 // setting cache provider if specified in cfg, otherwise let engine choose
416 if (global.wurfl.cache_size != NULL) {
417
418 if (strpbrk(global.wurfl.cache_size, ",") != NULL) {
419 wurfl_result_code = wurfl_set_cache_provider(global.wurfl.handle, WURFL_CACHE_PROVIDER_DOUBLE_LRU, global.wurfl.cache_size) ;
420 } else {
421
422 if (strcmp(global.wurfl.cache_size, "0")) {
423 wurfl_result_code = wurfl_set_cache_provider(global.wurfl.handle, WURFL_CACHE_PROVIDER_LRU, global.wurfl.cache_size) ;
424 } else {
425 wurfl_result_code = wurfl_set_cache_provider(global.wurfl.handle, WURFL_CACHE_PROVIDER_NONE, 0);
426 }
427
428 }
429
430 if (wurfl_result_code != WURFL_OK) {
431 Warning("WURFL: Setting cache to [%s] failed - %s\n", global.wurfl.cache_size, wurfl_get_error_message(global.wurfl.handle));
432 send_log(NULL, LOG_WARNING, "WURFL: Setting cache to [%s] failed - %s\n", global.wurfl.cache_size, wurfl_get_error_message(global.wurfl.handle));
433 return -1;
434 }
435
436 send_log(NULL, LOG_NOTICE, "WURFL: Cache set to [%s]\n", global.wurfl.cache_size);
437 }
438
439 // setting engine mode if specified in cfg, otherwise let engine choose
440 if (global.wurfl.engine_mode != -1) {
441
442 if (wurfl_set_engine_target(global.wurfl.handle, global.wurfl.engine_mode) != WURFL_OK ) {
443 Warning("WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
444 send_log(NULL, LOG_WARNING, "WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
445 return -1;
446 }
447
448 }
449
450 send_log(NULL, LOG_NOTICE, "WURFL: Engine target set to [%s]\n", (global.wurfl.engine_mode == WURFL_ENGINE_TARGET_HIGH_PERFORMANCE) ? (HA_WURFL_TARGET_PERFORMANCE) : (HA_WURFL_TARGET_ACCURACY) );
451
452 // setting ua priority if specified in cfg, otherwise let engine choose
453 if (global.wurfl.useragent_priority != -1) {
454
455 if (wurfl_set_useragent_priority(global.wurfl.handle, global.wurfl.useragent_priority) != WURFL_OK ) {
456 Warning("WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
457 send_log(NULL, LOG_WARNING, "WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
458 return -1;
459 }
460
461 }
462
463 send_log(NULL, LOG_NOTICE, "WURFL: Engine useragent priority set to [%s]\n", (global.wurfl.useragent_priority == WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT) ? (HA_WURFL_PRIORITY_PLAIN) : (HA_WURFL_PRIORITY_SIDELOADED_BROWSER) );
464
465 // loading WURFL engine
466 if (wurfl_load(global.wurfl.handle) != WURFL_OK) {
467 Warning("WURFL: Engine load failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
468 send_log(NULL, LOG_WARNING, "WURFL: Engine load failed - %s\n", wurfl_get_error_message(global.wurfl.handle));
469 return -1;
470 }
471
472 send_log(NULL, LOG_NOTICE, "WURFL: Engine loaded\n");
473 send_log(NULL, LOG_NOTICE, "WURFL: Module load completed\n");
474 return 0;
475}
476
477void ha_wurfl_deinit(void)
478{
479 wurfl_information_t *wi, *wi2;
480 wurfl_patches_t *wp, *wp2;
481
482 send_log(NULL, LOG_NOTICE, "WURFL: Unloading module v.%s\n", HA_WURFL_MODULE_VERSION);
483 wurfl_destroy(global.wurfl.handle);
484 global.wurfl.handle = NULL;
485 free(global.wurfl.data_file);
486 global.wurfl.data_file = NULL;
487 free(global.wurfl.cache_size);
488 global.wurfl.cache_size = NULL;
489
490 list_for_each_entry_safe(wi, wi2, &global.wurfl.information_list, list) {
491 LIST_DEL(&wi->list);
492 free(wi);
493 }
494
495 list_for_each_entry_safe(wp, wp2, &global.wurfl.patch_file_list, list) {
496 LIST_DEL(&wp->list);
497 free(wp);
498 }
499
500 send_log(NULL, LOG_NOTICE, "WURFL: Module unloaded\n");
501}
502
503static int ha_wurfl_get_all(const struct arg *args, struct sample *smp, const char *kw, void *private)
504{
505 wurfl_device_handle dHandle;
506 struct chunk *temp;
507 wurfl_information_t *wi;
508 ha_wurfl_header_t wh;
509
510 ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n");
511 wh.wsmp = smp;
512 dHandle = wurfl_lookup(global.wurfl.handle, &ha_wurfl_retrieve_header, &wh);
513
514 if (!dHandle) {
515 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global.wurfl.handle));
516 return 1;
517 }
518
519 temp = get_trash_chunk();
520 chunk_reset(temp);
521
522 list_for_each_entry(wi, &global.wurfl.information_list, list) {
523 chunk_appendf(temp, "%c", global.wurfl.information_list_separator);
524
525 switch(wi->data.type) {
526 case HA_WURFL_DATA_TYPE_UNKNOWN :
527 ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
528#ifdef WURFL_HEADER_WITH_DETAILS
529 // write WURFL property type and name before its value...
530 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name);
531#endif
532 break;
533 case HA_WURFL_DATA_TYPE_CAP :
534 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING);
535#ifdef WURFL_HEADER_WITH_DETAILS
536 // write WURFL property type and name before its value...
537 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name);
538#endif
539 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name));
540 break;
541 case HA_WURFL_DATA_TYPE_VCAP :
542 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING);
543#ifdef WURFL_HEADER_WITH_DETAILS
544 // write WURFL property type and name before its value...
545 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name);
546#endif
547 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name));
548 break;
549 case HA_WURFL_DATA_TYPE_PROPERTY :
550 ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_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_PROPERTY_STRING, wi->data.name);
554#endif
555 chunk_appendf(temp, "%s", wi->data.func_callback(global.wurfl.handle, dHandle));
556 break;
557 }
558
559 }
560
561 wurfl_device_destroy(dHandle);
562 smp->data.u.str.str = temp->str;
563 smp->data.u.str.len = temp->len;
564 return 1;
565}
566
567static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private)
568{
569 wurfl_device_handle dHandle;
570 struct chunk *temp;
571 wurfl_data_t *wn = NULL;
572 struct ebmb_node *node;
573 ha_wurfl_header_t wh;
574 int i = 0;
575
576 ha_wurfl_log("WURFL: starting ha_wurfl_get\n");
577 wh.wsmp = smp;
578 dHandle = wurfl_lookup(global.wurfl.handle, &ha_wurfl_retrieve_header, &wh);
579
580 if (!dHandle) {
581 ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global.wurfl.handle));
582 return 1;
583 }
584
585 temp = get_trash_chunk();
586 chunk_reset(temp);
587
588 while (args[i].data.str.str) {
589 chunk_appendf(temp, "%c", global.wurfl.information_list_separator);
590 node = ebst_lookup(&global.wurfl.btree, args[i].data.str.str);
591 wn = container_of(node, wurfl_data_t, nd);
592
593 if (wn) {
594
595 switch(wn->type) {
596 case HA_WURFL_DATA_TYPE_UNKNOWN :
597 ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING);
598#ifdef WURFL_HEADER_WITH_DETAILS
599 // write WURFL property type and name before its value...
600 chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name);
601#endif
602 break;
603 case HA_WURFL_DATA_TYPE_CAP :
604 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING);
605#ifdef WURFL_HEADER_WITH_DETAILS
606 // write WURFL property type and name before its value...
607 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name);
608#endif
609 chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name));
610 break;
611 case HA_WURFL_DATA_TYPE_VCAP :
612 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING);
613#ifdef WURFL_HEADER_WITH_DETAILS
614 // write WURFL property type and name before its value...
615 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name);
616#endif
617 chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name));
618 break;
619 case HA_WURFL_DATA_TYPE_PROPERTY :
620 ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_STRING);
621#ifdef WURFL_HEADER_WITH_DETAILS
622 // write WURFL property type and name before its value...
623 chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wn->name);
624#endif
625 chunk_appendf(temp, "%s", wn->func_callback(global.wurfl.handle, dHandle));
626 break;
627 }
628
629 } else {
630 ha_wurfl_log("WURFL: %s not in wurfl-information-list \n", args[i].data.str.str);
631 }
632
633 i++;
634 }
635
636 wurfl_device_destroy(dHandle);
637 smp->data.u.str.str = temp->str;
638 smp->data.u.str.len = temp->len;
639 return 1;
640}
641
642static struct cfg_kw_list wurflcfg_kws = {{ }, {
643 { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file },
644 { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator },
645 { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list },
646 { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list },
647 { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache },
648 { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode },
649 { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority },
650 { 0, NULL, NULL },
651 }
652};
653
654/* Note: must not be declared <const> as its list will be overwritten */
655static struct sample_fetch_kw_list fetch_kws = {ILH, {
656 { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
657 { "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 },
658 { NULL, NULL, 0, 0, 0 },
659 }
660};
661
662/* Note: must not be declared <const> as its list will be overwritten */
663static struct sample_conv_kw_list conv_kws = {ILH, {
664 { NULL, NULL, 0, 0, 0 },
665 }
666};
667
668__attribute__((constructor))
669static void __wurfl_init(void)
670{
671 /* register sample fetch and format conversion keywords */
672 sample_register_fetches(&fetch_kws);
673 sample_register_convs(&conv_kws);
674 cfg_register_keywords(&wurflcfg_kws);
675}
676
677// WURFL properties wrapper functions
678static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
679{
680 return wurfl_device_get_root_id(dHandle);
681}
682
683static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle)
684{
685 return wurfl_device_get_id(dHandle);
686}
687
688static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle)
689{
690 if (wurfl_device_is_actual_device_root(dHandle))
691 return HA_WURFL_ISDEVROOT_TRUE;
692 else
693 return HA_WURFL_ISDEVROOT_FALSE;
694}
695
696static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
697{
698 return wurfl_device_get_original_useragent(dHandle);
699}
700
701static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle)
702{
703 return wurfl_get_api_version();
704}
705
706static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle)
707{
708 return wurfl_get_engine_target_as_string(wHandle);
709}
710
711static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle)
712{
713 return wurfl_get_wurfl_info(wHandle);
714}
715
716static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle)
717{
718 return wurfl_get_last_load_time_as_string(wHandle);
719}
720
721static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle)
722{
723 return wurfl_device_get_normalized_useragent(dHandle);
724}
725
726static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle)
727{
728 return wurfl_get_useragent_priority_as_string(wHandle);
729}
730
731// call function for WURFL properties
732static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle)
733{
734 int position;
735 int begin = 0;
736 int end = HA_WURFL_PROPERTIES_NBR - 1;
737 int cond = 0;
738
739 while(begin <= end) {
740 position = (begin + end) / 2;
741
742 if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) {
743 ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name );
744 return wurfl_properties_function_map[position].func;
745 } else if(cond < 0)
746 begin = position + 1;
747 else
748 end = position - 1;
749
750 }
751
752 return NULL;
753}
754
755static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh)
756{
757 struct sample *smp;
758 struct hdr_idx *idx;
759 struct hdr_ctx ctx;
760 const struct http_msg *msg;
761 int header_len = HA_WURFL_MAX_HEADER_LENGTH;
762
763 ha_wurfl_log("WURFL: retrieve header request [%s]\n", header_name);
764 smp = ((ha_wurfl_header_t *)wh)->wsmp;
765 idx = &smp->strm->txn->hdr_idx;
766 msg = &smp->strm->txn->req;
767 ctx.idx = 0;
768
769 if (http_find_full_header2(header_name, strlen(header_name), msg->chn->buf->p, idx, &ctx) == 0)
770 return 0;
771
772 if (header_len > ctx.vlen)
773 header_len = ctx.vlen;
774
775 strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.line + ctx.val, header_len);
776 ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0';
777 ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value);
778 return ((ha_wurfl_header_t *)wh)->header_value;
779}