#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct { char *data_file; /* the WURFL data file */ char *cache_size; /* the WURFL cache parameters */ int engine_mode; /* the WURFL engine mode */ int useragent_priority; /* the WURFL ua priority */ struct list patch_file_list; /* the list of WURFL patch file to use */ char information_list_separator; /* the separator used in request to separate values */ struct list information_list; /* the list of WURFL data to return into request */ void *handle; /* the handle to WURFL engine */ struct eb_root btree; /* btree containing info (name/type) on WURFL data to return */ } global_wurfl = { .data_file = NULL, .cache_size = NULL, .engine_mode = -1, .useragent_priority = -1, .information_list_separator = ',', .information_list = LIST_HEAD_INIT(global_wurfl.information_list), .patch_file_list = LIST_HEAD_INIT(global_wurfl.patch_file_list), .handle = NULL, }; #ifdef WURFL_DEBUG inline static void ha_wurfl_log(char * message, ...) { char logbuf[256]; va_list argp; va_start(argp, message); vsnprintf(logbuf, sizeof(logbuf), message, argp); va_end(argp); send_log(NULL, LOG_NOTICE, logbuf, NULL); } #else inline static void ha_wurfl_log(char * message, ...) { } #endif #define HA_WURFL_MAX_HEADER_LENGTH 1024 typedef char *(*PROP_CALLBACK_FUNC)(wurfl_handle wHandle, wurfl_device_handle dHandle); enum wurfl_data_type { HA_WURFL_DATA_TYPE_UNKNOWN = 0, HA_WURFL_DATA_TYPE_CAP = 100, HA_WURFL_DATA_TYPE_VCAP = 200, HA_WURFL_DATA_TYPE_PROPERTY = 300 }; typedef struct { char *name; enum wurfl_data_type type; PROP_CALLBACK_FUNC func_callback; struct ebmb_node nd; } wurfl_data_t; static const char HA_WURFL_MODULE_VERSION[] = "1.0"; static const char HA_WURFL_ISDEVROOT_FALSE[] = "FALSE"; static const char HA_WURFL_ISDEVROOT_TRUE[] = "TRUE"; static const char HA_WURFL_TARGET_ACCURACY[] = "accuracy"; static const char HA_WURFL_TARGET_PERFORMANCE[] = "performance"; static const char HA_WURFL_PRIORITY_PLAIN[] = "plain"; static const char HA_WURFL_PRIORITY_SIDELOADED_BROWSER[] = "sideloaded_browser"; static const char HA_WURFL_MIN_ENGINE_VERSION_MANDATORY[] = "1.8.0.0"; static const char HA_WURFL_DATA_TYPE_UNKNOWN_STRING[] = "unknown"; static const char HA_WURFL_DATA_TYPE_CAP_STRING[] = "capability"; static const char HA_WURFL_DATA_TYPE_VCAP_STRING[] = "virtual_capability"; static const char HA_WURFL_DATA_TYPE_PROPERTY_STRING[] = "property"; static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh); static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle); static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle); // ordered property=>function map, suitable for binary search static const struct { const char *name; const char *(*func)(wurfl_handle wHandle, wurfl_device_handle dHandle); } wurfl_properties_function_map [] = { {"wurfl_api_version", ha_wurfl_get_wurfl_api_version}, {"wurfl_engine_target", ha_wurfl_get_wurfl_engine_target}, {"wurfl_id", ha_wurfl_get_wurfl_id }, {"wurfl_info", ha_wurfl_get_wurfl_info }, {"wurfl_isdevroot", ha_wurfl_get_wurfl_isdevroot}, {"wurfl_last_load_time", ha_wurfl_get_wurfl_last_load_time}, {"wurfl_normalized_useragent", ha_wurfl_get_wurfl_normalized_useragent}, {"wurfl_useragent", ha_wurfl_get_wurfl_useragent}, {"wurfl_useragent_priority", ha_wurfl_get_wurfl_useragent_priority }, {"wurfl_root_id", ha_wurfl_get_wurfl_root_id}, }; static const int HA_WURFL_PROPERTIES_NBR = 10; typedef struct { struct list list; wurfl_data_t data; } wurfl_information_t; typedef struct { struct list list; char *patch_file_path; } wurfl_patches_t; typedef struct { struct sample *wsmp; char header_value[HA_WURFL_MAX_HEADER_LENGTH + 1]; } ha_wurfl_header_t; /* * configuration parameters parsing functions */ static int ha_wurfl_cfg_data_file(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "WURFL: %s expects a value.\n", args[0]); return -1; } global_wurfl.data_file = strdup(args[1]); return 0; } static int ha_wurfl_cfg_cache(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "WURFL: %s expects a value.\n", args[0]); return -1; } global_wurfl.cache_size = strdup(args[1]); return 0; } static int ha_wurfl_cfg_engine_mode(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "WURFL: %s expects a value.\n", args[0]); return -1; } if (!strcmp(args[1],HA_WURFL_TARGET_ACCURACY)) { global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_ACCURACY; return 0; } if (!strcmp(args[1],HA_WURFL_TARGET_PERFORMANCE)) { global_wurfl.engine_mode = WURFL_ENGINE_TARGET_HIGH_PERFORMANCE; return 0; } memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_TARGET_PERFORMANCE, HA_WURFL_TARGET_ACCURACY); return -1; } static int ha_wurfl_cfg_information_list_separator(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "WURFL: %s expects a single character.\n", args[0]); return -1; } if (strlen(args[1]) > 1) { memprintf(err, "WURFL: %s expects a single character, got %s.\n", args[0], args[1]); return -1; } global_wurfl.information_list_separator = *args[1]; return 0; } static int ha_wurfl_cfg_information_list(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { int argIdx = 1; wurfl_information_t *wi; if (*(args[argIdx]) == 0) { memprintf(err, "WURFL: %s expects a value.\n", args[0]); return -1; } while (*(args[argIdx])) { wi = calloc(1, sizeof(*wi)); if (wi == NULL) { memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]); return -1; } wi->data.name = strdup(args[argIdx]); wi->data.type = HA_WURFL_DATA_TYPE_UNKNOWN; wi->data.func_callback = NULL; LIST_ADDQ(&global_wurfl.information_list, &wi->list); ++argIdx; } return 0; } static int ha_wurfl_cfg_patch_file_list(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { int argIdx = 1; wurfl_patches_t *wp; if (*(args[argIdx]) == 0) { memprintf(err, "WURFL: %s expects a value.\n", args[0]); return -1; } while (*(args[argIdx])) { wp = calloc(1, sizeof(*wp)); if (wp == NULL) { memprintf(err, "WURFL: Error allocating memory for %s element.\n", args[0]); return -1; } wp->patch_file_path = strdup(args[argIdx]); LIST_ADDQ(&global_wurfl.patch_file_list, &wp->list); ++argIdx; } return 0; } static int ha_wurfl_cfg_useragent_priority(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "WURFL: %s expects a value.\n", args[0]); return -1; } if (!strcmp(args[1],HA_WURFL_PRIORITY_PLAIN)) { global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_USE_PLAIN_USERAGENT; return 0; } if (!strcmp(args[1],HA_WURFL_PRIORITY_SIDELOADED_BROWSER)) { global_wurfl.useragent_priority = WURFL_USERAGENT_PRIORITY_OVERRIDE_SIDELOADED_BROWSER_USERAGENT; return 0; } memprintf(err, "WURFL: %s valid values are %s or %s.\n", args[0], HA_WURFL_PRIORITY_PLAIN, HA_WURFL_PRIORITY_SIDELOADED_BROWSER); return -1; } /* * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*. */ static int ha_wurfl_init(void) { wurfl_information_t *wi; wurfl_patches_t *wp; wurfl_data_t * wn; int wurfl_result_code = WURFL_OK; int len; send_log(NULL, LOG_NOTICE, "WURFL: Loading module v.%s\n", HA_WURFL_MODULE_VERSION); // creating WURFL handler global_wurfl.handle = wurfl_create(); if (global_wurfl.handle == NULL) { ha_warning("WURFL: Engine handler creation failed"); send_log(NULL, LOG_WARNING, "WURFL: Engine handler creation failed\n"); return ERR_WARN; } send_log(NULL, LOG_NOTICE, "WURFL: Engine handler created - API version %s\n", wurfl_get_api_version() ); // set wurfl data file if (global_wurfl.data_file == NULL) { ha_warning("WURFL: missing wurfl-data-file parameter in global configuration\n"); send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-data-file parameter in global configuration\n"); return ERR_WARN; } if (global.nbthread > 1) { ha_alert("WURFL: multithreading is not supported for now.\n"); return (ERR_FATAL | ERR_ALERT); } if (wurfl_set_root(global_wurfl.handle, global_wurfl.data_file) != WURFL_OK) { ha_warning("WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: Engine setting root file failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } send_log(NULL, LOG_NOTICE, "WURFL: Engine root file set to %s\n", global_wurfl.data_file); // just a log to inform which separator char has to be used send_log(NULL, LOG_NOTICE, "WURFL: Information list separator set to '%c'\n", global_wurfl.information_list_separator); // load wurfl data needed ( and filter whose are supposed to be capabilities ) if (LIST_ISEMPTY(&global_wurfl.information_list)) { ha_warning("WURFL: missing wurfl-information-list parameter in global configuration\n"); send_log(NULL, LOG_WARNING, "WURFL: missing wurfl-information-list parameter in global configuration\n"); return ERR_WARN; } else { // ebtree initialization global_wurfl.btree = EB_ROOT; // checking if informations are valid WURFL data ( cap, vcaps, properties ) list_for_each_entry(wi, &global_wurfl.information_list, list) { // check if information is already loaded looking into btree if (ebst_lookup(&global_wurfl.btree, wi->data.name) == NULL) { if ((wi->data.func_callback = (PROP_CALLBACK_FUNC) ha_wurfl_get_property_callback(wi->data.name)) != NULL) { wi->data.type = HA_WURFL_DATA_TYPE_PROPERTY; ha_wurfl_log("WURFL: [%s] is a valid wurfl data [property]\n",wi->data.name); } else if (wurfl_has_virtual_capability(global_wurfl.handle, wi->data.name)) { wi->data.type = HA_WURFL_DATA_TYPE_VCAP; ha_wurfl_log("WURFL: [%s] is a valid wurfl data [virtual capability]\n",wi->data.name); } else { // by default a cap type is assumed to be and we control it on engine load wi->data.type = HA_WURFL_DATA_TYPE_CAP; if (wurfl_add_requested_capability(global_wurfl.handle, wi->data.name) != WURFL_OK) { ha_warning("WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: capability filtering failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } ha_wurfl_log("WURFL: [%s] treated as wurfl capability. Will check its validity later, on engine load\n",wi->data.name); } // ebtree insert here len = strlen(wi->data.name); wn = malloc(sizeof(wurfl_data_t) + len + 1); if (wn == NULL) { ha_warning("WURFL: Error allocating memory for information tree element.\n"); send_log(NULL, LOG_WARNING, "WURFL: Error allocating memory for information tree element.\n"); return ERR_WARN; } wn->name = wi->data.name; wn->type = wi->data.type; wn->func_callback = wi->data.func_callback; memcpy(wn->nd.key, wi->data.name, len); wn->nd.key[len] = 0; if (!ebst_insert(&global_wurfl.btree, &wn->nd)) { ha_warning("WURFL: [%s] not inserted in btree\n",wn->name); send_log(NULL, LOG_WARNING, "WURFL: [%s] not inserted in btree\n",wn->name); return ERR_WARN; } } else { ha_wurfl_log("WURFL: [%s] already loaded\n",wi->data.name); } } } // filtering mandatory capabilities if engine version < 1.8.0.0 if (strcmp(wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY) < 0) { wurfl_capability_enumerator_handle hmandatorycapabilityenumerator; ha_wurfl_log("WURFL: Engine version %s < %s - Filtering mandatory capabilities\n", wurfl_get_api_version(), HA_WURFL_MIN_ENGINE_VERSION_MANDATORY); hmandatorycapabilityenumerator = wurfl_get_mandatory_capability_enumerator(global_wurfl.handle); while (wurfl_capability_enumerator_is_valid(hmandatorycapabilityenumerator)) { char *name = (char *)wurfl_capability_enumerator_get_name(hmandatorycapabilityenumerator); if (ebst_lookup(&global_wurfl.btree, name) == NULL) { if (wurfl_add_requested_capability(global_wurfl.handle, name) != WURFL_OK) { ha_warning("WURFL: Engine adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: Adding mandatory capability [%s] failed - %s\n", name, wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } ha_wurfl_log("WURFL: Mandatory capability [%s] added\n", name); } else { ha_wurfl_log("WURFL: Mandatory capability [%s] already filtered\n", name); } wurfl_capability_enumerator_move_next(hmandatorycapabilityenumerator); } wurfl_capability_enumerator_destroy(hmandatorycapabilityenumerator); } // adding WURFL patches if needed if (!LIST_ISEMPTY(&global_wurfl.patch_file_list)) { list_for_each_entry(wp, &global_wurfl.patch_file_list, list) { if (wurfl_add_patch(global_wurfl.handle, wp->patch_file_path) != WURFL_OK) { ha_warning("WURFL: Engine adding patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: Adding engine patch file failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } send_log(NULL, LOG_NOTICE, "WURFL: Engine patch file added %s\n", wp->patch_file_path); } } // setting cache provider if specified in cfg, otherwise let engine choose if (global_wurfl.cache_size != NULL) { if (strpbrk(global_wurfl.cache_size, ",") != NULL) { wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_DOUBLE_LRU, global_wurfl.cache_size) ; } else { if (strcmp(global_wurfl.cache_size, "0")) { wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_LRU, global_wurfl.cache_size) ; } else { wurfl_result_code = wurfl_set_cache_provider(global_wurfl.handle, WURFL_CACHE_PROVIDER_NONE, 0); } } if (wurfl_result_code != WURFL_OK) { ha_warning("WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: Setting cache to [%s] failed - %s\n", global_wurfl.cache_size, wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } send_log(NULL, LOG_NOTICE, "WURFL: Cache set to [%s]\n", global_wurfl.cache_size); } // setting engine mode if specified in cfg, otherwise let engine choose if (global_wurfl.engine_mode != -1) { if (wurfl_set_engine_target(global_wurfl.handle, global_wurfl.engine_mode) != WURFL_OK ) { ha_warning("WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: Setting engine target failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } } 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) ); // setting ua priority if specified in cfg, otherwise let engine choose if (global_wurfl.useragent_priority != -1) { if (wurfl_set_useragent_priority(global_wurfl.handle, global_wurfl.useragent_priority) != WURFL_OK ) { ha_warning("WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: Setting engine useragent priority failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } } 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) ); // loading WURFL engine if (wurfl_load(global_wurfl.handle) != WURFL_OK) { ha_warning("WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); send_log(NULL, LOG_WARNING, "WURFL: Engine load failed - %s\n", wurfl_get_error_message(global_wurfl.handle)); return ERR_WARN; } send_log(NULL, LOG_NOTICE, "WURFL: Engine loaded\n"); send_log(NULL, LOG_NOTICE, "WURFL: Module load completed\n"); return 0; } static void ha_wurfl_deinit(void) { wurfl_information_t *wi, *wi2; wurfl_patches_t *wp, *wp2; send_log(NULL, LOG_NOTICE, "WURFL: Unloading module v.%s\n", HA_WURFL_MODULE_VERSION); wurfl_destroy(global_wurfl.handle); global_wurfl.handle = NULL; free(global_wurfl.data_file); global_wurfl.data_file = NULL; free(global_wurfl.cache_size); global_wurfl.cache_size = NULL; list_for_each_entry_safe(wi, wi2, &global_wurfl.information_list, list) { LIST_DEL(&wi->list); free(wi); } list_for_each_entry_safe(wp, wp2, &global_wurfl.patch_file_list, list) { LIST_DEL(&wp->list); free(wp); } send_log(NULL, LOG_NOTICE, "WURFL: Module unloaded\n"); } static int ha_wurfl_get_all(const struct arg *args, struct sample *smp, const char *kw, void *private) { wurfl_device_handle dHandle; struct buffer *temp; wurfl_information_t *wi; ha_wurfl_header_t wh; ha_wurfl_log("WURFL: starting ha_wurfl_get_all\n"); wh.wsmp = smp; dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh); if (!dHandle) { ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle)); return 1; } temp = get_trash_chunk(); chunk_reset(temp); list_for_each_entry(wi, &global_wurfl.information_list, list) { chunk_appendf(temp, "%c", global_wurfl.information_list_separator); switch(wi->data.type) { case HA_WURFL_DATA_TYPE_UNKNOWN : ha_wurfl_log("WURFL: %s is of an %s type\n", wi->data.name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wi->data.name); #endif break; case HA_WURFL_DATA_TYPE_CAP : ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_CAP_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wi->data.name); #endif chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wi->data.name)); break; case HA_WURFL_DATA_TYPE_VCAP : ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_VCAP_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wi->data.name); #endif chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wi->data.name)); break; case HA_WURFL_DATA_TYPE_PROPERTY : ha_wurfl_log("WURFL: %s is a %s\n", wi->data.name, HA_WURFL_DATA_TYPE_PROPERTY_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wi->data.name); #endif chunk_appendf(temp, "%s", wi->data.func_callback(global_wurfl.handle, dHandle)); break; } } wurfl_device_destroy(dHandle); smp->data.u.str.area = temp->area; smp->data.u.str.data = temp->data; return 1; } static int ha_wurfl_get(const struct arg *args, struct sample *smp, const char *kw, void *private) { wurfl_device_handle dHandle; struct buffer *temp; wurfl_data_t *wn = NULL; struct ebmb_node *node; ha_wurfl_header_t wh; int i = 0; ha_wurfl_log("WURFL: starting ha_wurfl_get\n"); wh.wsmp = smp; dHandle = wurfl_lookup(global_wurfl.handle, &ha_wurfl_retrieve_header, &wh); if (!dHandle) { ha_wurfl_log("WURFL: unable to retrieve device from request %s\n", wurfl_get_error_message(global_wurfl.handle)); return 1; } temp = get_trash_chunk(); chunk_reset(temp); while (args[i].data.str.area) { chunk_appendf(temp, "%c", global_wurfl.information_list_separator); node = ebst_lookup(&global_wurfl.btree, args[i].data.str.area); wn = container_of(node, wurfl_data_t, nd); if (wn) { switch(wn->type) { case HA_WURFL_DATA_TYPE_UNKNOWN : ha_wurfl_log("WURFL: %s is of an %s type\n", wn->name, HA_WURFL_DATA_TYPE_UNKNOWN_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s", HA_WURFL_DATA_TYPE_UNKNOWN_STRING, wn->name); #endif break; case HA_WURFL_DATA_TYPE_CAP : ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_CAP_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_CAP_STRING, wn->name); #endif chunk_appendf(temp, "%s", wurfl_device_get_capability(dHandle, wn->name)); break; case HA_WURFL_DATA_TYPE_VCAP : ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_VCAP_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_VCAP_STRING, wn->name); #endif chunk_appendf(temp, "%s", wurfl_device_get_virtual_capability(dHandle, wn->name)); break; case HA_WURFL_DATA_TYPE_PROPERTY : ha_wurfl_log("WURFL: %s is a %s\n", wn->name, HA_WURFL_DATA_TYPE_PROPERTY_STRING); #ifdef WURFL_HEADER_WITH_DETAILS // write WURFL property type and name before its value... chunk_appendf(temp, "%s=%s=", HA_WURFL_DATA_TYPE_PROPERTY_STRING, wn->name); #endif chunk_appendf(temp, "%s", wn->func_callback(global_wurfl.handle, dHandle)); break; } } else { ha_wurfl_log("WURFL: %s not in wurfl-information-list \n", args[i].data.str.area); } i++; } wurfl_device_destroy(dHandle); smp->data.u.str.area = temp->area; smp->data.u.str.data = temp->data; return 1; } static struct cfg_kw_list wurflcfg_kws = {{ }, { { CFG_GLOBAL, "wurfl-data-file", ha_wurfl_cfg_data_file }, { CFG_GLOBAL, "wurfl-information-list-separator", ha_wurfl_cfg_information_list_separator }, { CFG_GLOBAL, "wurfl-information-list", ha_wurfl_cfg_information_list }, { CFG_GLOBAL, "wurfl-patch-file", ha_wurfl_cfg_patch_file_list }, { CFG_GLOBAL, "wurfl-cache-size", ha_wurfl_cfg_cache }, { CFG_GLOBAL, "wurfl-engine-mode", ha_wurfl_cfg_engine_mode }, { CFG_GLOBAL, "wurfl-useragent-priority", ha_wurfl_cfg_useragent_priority }, { 0, NULL, NULL }, } }; INITCALL1(STG_REGISTER, cfg_register_keywords, &wurflcfg_kws); /* Note: must not be declared as its list will be overwritten */ static struct sample_fetch_kw_list fetch_kws = {ILH, { { "wurfl-get-all", ha_wurfl_get_all, 0, NULL, SMP_T_STR, SMP_USE_HRQHV }, { "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 }, { NULL, NULL, 0, 0, 0 }, } }; INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws); /* Note: must not be declared as its list will be overwritten */ static struct sample_conv_kw_list conv_kws = {ILH, { { NULL, NULL, 0, 0, 0 }, } }; INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws); // WURFL properties wrapper functions static const char *ha_wurfl_get_wurfl_root_id (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_device_get_root_id(dHandle); } static const char *ha_wurfl_get_wurfl_id (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_device_get_id(dHandle); } static const char *ha_wurfl_get_wurfl_isdevroot (wurfl_handle wHandle, wurfl_device_handle dHandle) { if (wurfl_device_is_actual_device_root(dHandle)) return HA_WURFL_ISDEVROOT_TRUE; else return HA_WURFL_ISDEVROOT_FALSE; } static const char *ha_wurfl_get_wurfl_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_device_get_original_useragent(dHandle); } static const char *ha_wurfl_get_wurfl_api_version (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_get_api_version(); } static const char *ha_wurfl_get_wurfl_engine_target (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_get_engine_target_as_string(wHandle); } static const char *ha_wurfl_get_wurfl_info (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_get_wurfl_info(wHandle); } static const char *ha_wurfl_get_wurfl_last_load_time (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_get_last_load_time_as_string(wHandle); } static const char *ha_wurfl_get_wurfl_normalized_useragent (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_device_get_normalized_useragent(dHandle); } static const char *ha_wurfl_get_wurfl_useragent_priority (wurfl_handle wHandle, wurfl_device_handle dHandle) { return wurfl_get_useragent_priority_as_string(wHandle); } // call function for WURFL properties static const char *(*ha_wurfl_get_property_callback(char *name)) (wurfl_handle wHandle, wurfl_device_handle dHandle) { int position; int begin = 0; int end = HA_WURFL_PROPERTIES_NBR - 1; int cond = 0; while(begin <= end) { position = (begin + end) / 2; if((cond = strcmp(wurfl_properties_function_map[position].name, name)) == 0) { ha_wurfl_log("WURFL: ha_wurfl_get_property_callback match %s\n", wurfl_properties_function_map[position].name ); return wurfl_properties_function_map[position].func; } else if(cond < 0) begin = position + 1; else end = position - 1; } return NULL; } static const char *ha_wurfl_retrieve_header(const char *header_name, const void *wh) { struct sample *smp; struct hdr_idx *idx; struct hdr_ctx ctx; const struct http_msg *msg; int header_len = HA_WURFL_MAX_HEADER_LENGTH; ha_wurfl_log("WURFL: retrieve header request [%s]\n", header_name); smp = ((ha_wurfl_header_t *)wh)->wsmp; idx = &smp->strm->txn->hdr_idx; msg = &smp->strm->txn->req; ctx.idx = 0; if (http_find_full_header2(header_name, strlen(header_name), ci_head(msg->chn), idx, &ctx) == 0) return 0; if (header_len > ctx.vlen) header_len = ctx.vlen; strncpy(((ha_wurfl_header_t *)wh)->header_value, ctx.line + ctx.val, header_len); ((ha_wurfl_header_t *)wh)->header_value[header_len] = '\0'; ha_wurfl_log("WURFL: retrieve header request returns [%s]\n", ((ha_wurfl_header_t *)wh)->header_value); return ((ha_wurfl_header_t *)wh)->header_value; } static void ha_wurfl_register_build_options() { const char *ver = wurfl_get_api_version(); char *ptr = NULL; memprintf(&ptr, "Built with WURFL support (%sversion %s)", strcmp(ver, "1.11.2.100") ? "" : "dummy library ", ver); hap_register_build_opts(ptr, 1); } REGISTER_POST_CHECK(ha_wurfl_init); REGISTER_POST_DEINIT(ha_wurfl_deinit); INITCALL0(STG_REGISTER, ha_wurfl_register_build_options);