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