ib_status_t DLL_PUBLIC ib_logevent_field_add(ib_logevent_t *le, const char *name) { IB_FTRACE_INIT(); char *name_copy; ib_status_t rc; assert(le != NULL); if (le->fields == NULL) { rc = ib_list_create(&le->fields, le->mp); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } name_copy = ib_mpool_memdup(le->mp, name, strlen(name) + 1); rc = ib_list_push(le->fields, name_copy); IB_FTRACE_RET_STATUS(rc); }
ib_status_t DLL_PUBLIC ib_logevent_tag_add(ib_logevent_t *le, const char *tag) { IB_FTRACE_INIT(); char *tag_copy; ib_status_t rc; assert(le != NULL); if (le->tags == NULL) { rc = ib_list_create(&le->tags, le->mp); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } tag_copy = ib_mpool_memdup(le->mp, tag, strlen(tag) + 1); rc = ib_list_push(le->tags, tag_copy); IB_FTRACE_RET_STATUS(rc); }
ib_status_t ib_site_create(ib_site_t **psite, ib_engine_t *ib, const char *name) { IB_FTRACE_INIT(ib_site_create); ib_mpool_t *pool = ib->config_mp; ib_status_t rc; /* Create the main structure in the config memory pool */ *psite = (ib_site_t *)ib_mpool_calloc(pool, 1, sizeof(**psite)); if (*psite == NULL) { rc = IB_EALLOC; IB_FTRACE_RET_STATUS(rc); } (*psite)->ib = ib; (*psite)->mp = pool; (*psite)->name = (const char *)ib_mpool_memdup(pool, name, strlen(name)+1); /* Remaining fields are NULL via calloc. */ IB_FTRACE_RET_STATUS(IB_OK); }
ib_status_t ib_site_loc_create(ib_site_t *site, ib_loc_t **ploc, const char *path) { IB_FTRACE_INIT(ib_site_loc_create); ib_loc_t *loc; ib_status_t rc; if (ploc != NULL) { *ploc = NULL; } /* Create a list if this is the first item. */ if (site->locations == NULL) { rc = ib_list_create(&site->locations, site->mp); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } /* Create the location structure in the site memory pool */ loc = (ib_loc_t *)ib_mpool_calloc(site->mp, 1, sizeof(*loc)); if (loc == NULL) { rc = IB_EALLOC; IB_FTRACE_RET_STATUS(rc); } loc->site = site; loc->path = path; loc->path = (const char *)ib_mpool_memdup(site->mp, path, strlen(path)+1); if (ploc != NULL) { *ploc = loc; } rc = ib_list_push(site->locations, (void *)loc); IB_FTRACE_RET_STATUS(rc); }
/** * Internal compilation of the modpcre pattern. * * @param[in] ib IronBee engine for logging. * @param[in] pool The memory pool to allocate memory out of. * @param[out] pcre_cpatt Struct containing the compilation. * @param[in] patt The uncompiled pattern to match. * @param[out] errptr Pointer to an error message describing the failure. * @param[out] errorffset The location of the failure, if this fails. * @returns IronBee status. IB_EINVAL if the pattern is invalid, * IB_EALLOC if memory allocation fails or IB_OK. */ static ib_status_t pcre_compile_internal(ib_engine_t *ib, ib_mpool_t *pool, modpcre_cpatt_t **pcre_cpatt, const char *patt, const char **errptr, int *erroffset) { IB_FTRACE_INIT(); /* Compiled pattern. */ pcre *cpatt = NULL; /* Compiled pattern size. Used to copy cpatt. */ size_t cpatt_sz; /* Extra data structure. This contains the study_data pointer. */ pcre_extra *edata = NULL; /* Size of edata->study_data. */ size_t study_data_sz; /* Is the compiled regex jit-compiled? This impacts how it is executed. */ int is_jit; /* How cpatt is produced. */ const int compile_flags = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; #ifdef PCRE_HAVE_JIT /* Determine the success of a call. */ int rc; /* Determines if the pcre compilation was successful with pcre_jit. */ int pcre_jit_ret; /* How edata is produced if we are using JIT. */ const int study_flags = PCRE_STUDY_JIT_COMPILE; #else /* How edata is produced if we are not using JIT. */ const int study_flags = 0; #endif /* PCRE_HAVE_JIT */ cpatt = pcre_compile(patt, compile_flags, errptr, erroffset, NULL); if (*errptr != NULL) { ib_util_log_error("PCRE compile error for \"%s\": %s at offset %d", patt, *errptr, *erroffset); IB_FTRACE_RET_STATUS(IB_EINVAL); } edata = pcre_study(cpatt, study_flags, errptr); #ifdef PCRE_HAVE_JIT if(*errptr != NULL) { pcre_free(cpatt); ib_util_log_error("PCRE-JIT study failed: %s", *errptr); IB_FTRACE_RET_STATUS(IB_EINVAL); } /* The check to see if JIT compilation was a success changed in 8.20RC1 now uses pcre_fullinfo see doc/pcrejit.3 */ rc = pcre_fullinfo(cpatt, edata, PCRE_INFO_JIT, &pcre_jit_ret); if (rc != 0) { ib_log_error(ib, "PCRE-JIT failed to get pcre_fullinfo"); is_jit = 0; } else if (pcre_jit_ret != 1) { ib_log_info(ib, "PCRE-JIT compiler does not support: %s", patt); ib_log_info(ib, "It will fallback to the normal PCRE"); is_jit = 0; } else { /* Assume pcre_jit_ret == 1. */ is_jit = 1; } #else if(*errptr != NULL) { pcre_free(cpatt); ib_log_info(ib, "PCRE study failed: %s", *errptr); } is_jit = 0; #endif /*PCRE_HAVE_JIT*/ /* Compute the size of the populated values of cpatt. */ pcre_fullinfo(cpatt, edata, PCRE_INFO_SIZE, &cpatt_sz); if (edata != NULL) { pcre_fullinfo(cpatt, edata, PCRE_INFO_STUDYSIZE, &study_data_sz); } else { study_data_sz = 0; } /** * Below is only allocation and copy operations to pass the PCRE results * back to the output variable pcre_cpatt. */ *pcre_cpatt = (modpcre_cpatt_t *)ib_mpool_alloc(pool, sizeof(**pcre_cpatt)); if (*pcre_cpatt == NULL) { pcre_free(cpatt); pcre_free(edata); ib_log_error(ib, "Failed to allocate pcre_cpatt of size: %zd", sizeof(**pcre_cpatt)); IB_FTRACE_RET_STATUS(IB_EALLOC); } (*pcre_cpatt)->is_jit = is_jit; (*pcre_cpatt)->cpatt_sz = cpatt_sz; (*pcre_cpatt)->study_data_sz = study_data_sz; /* Copy pattern. */ (*pcre_cpatt)->patt = ib_mpool_strdup(pool, patt); if ((*pcre_cpatt)->patt == NULL) { pcre_free(cpatt); pcre_free(edata); ib_log_error(ib, "Failed to duplicate pattern string: %s", patt); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Copy compiled pattern. */ (*pcre_cpatt)->cpatt = ib_mpool_memdup(pool, cpatt, cpatt_sz); pcre_free(cpatt); if ((*pcre_cpatt)->cpatt == NULL) { pcre_free(edata); ib_log_error(ib, "Failed to duplicate pattern of size: %zd", cpatt_sz); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Copy extra data (study data). */ if (edata != NULL) { /* Copy edata. */ (*pcre_cpatt)->edata = ib_mpool_memdup(pool, edata, sizeof(*edata)); if ((*pcre_cpatt)->edata == NULL) { pcre_free(edata); ib_log_error(ib, "Failed to duplicate edata."); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Copy edata->study_data. */ (*pcre_cpatt)->edata->study_data = ib_mpool_memdup(pool, edata->study_data, study_data_sz); pcre_free(edata); if ((*pcre_cpatt)->edata->study_data == NULL) { ib_log_error(ib, "Failed to study data of size: %zd", study_data_sz); IB_FTRACE_RET_STATUS(IB_EALLOC); } } else { (*pcre_cpatt)->edata = NULL; } IB_FTRACE_RET_STATUS(IB_OK); }
/** * Internal compilation of the dfa pattern. * * The major difference in this compilation from that of a normal pcre pattern * is that it does not use PCRE_JIT because it is intended for use * on streaming data. Streaming data is delivered in chunks * and partial matches are found. Doing partial matches and resumes * disable JIT optimizations and some a few other normal optimizations. * * @param[in] pool The memory pool to allocate memory out of. * @param[out] pcre_cpatt Struct containing the compilation. * @param[in] patt The uncompiled pattern to match. * @param[out] errptr Pointer to an error message describing the failure. * @param[out] errorffset The location of the failure, if this fails. * @returns IronBee status. IB_EINVAL if the pattern is invalid, * IB_EALLOC if memory allocation fails or IB_OK. */ static ib_status_t dfa_compile_internal(ib_mpool_t *pool, dfa_rule_data_t **dfa_cpatt, const char *patt, const char **errptr, int *erroffset) { IB_FTRACE_INIT(); /* Compiled pattern. */ pcre *cpatt = NULL; /* Compiled pattern size. Used to copy cpatt. */ size_t cpatt_sz; /* Extra data structure. This contains the study_data pointer. */ pcre_extra *edata = NULL; /* Size of edata->study_data. */ size_t study_data_sz; /* How cpatt is produced. */ const int compile_flags = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; /* How edata is produced if we are not using JIT. */ const int study_flags = 0; cpatt = pcre_compile(patt, compile_flags, errptr, erroffset, NULL); if (*errptr != NULL) { ib_util_log_error("PCRE compile error for \"%s\": %s at offset %d", patt, *errptr, *erroffset); IB_FTRACE_RET_STATUS(IB_EINVAL); } edata = pcre_study(cpatt, study_flags, errptr); if(*errptr != NULL) { pcre_free(cpatt); ib_util_log_error("PCRE study failed: %s", *errptr); } /* Compute the size of the populated values of cpatt. */ pcre_fullinfo(cpatt, edata, PCRE_INFO_SIZE, &cpatt_sz); if (edata != NULL) { pcre_fullinfo(cpatt, edata, PCRE_INFO_STUDYSIZE, &study_data_sz); } else { study_data_sz = 0; } /** * Below is only allocation and copy operations to pass the PCRE results * back to the output variable dfa_cpatt. */ *dfa_cpatt = (dfa_rule_data_t *)ib_mpool_alloc(pool, sizeof(**dfa_cpatt)); if (*dfa_cpatt == NULL) { pcre_free(cpatt); pcre_free(edata); IB_FTRACE_RET_STATUS(IB_EALLOC); } (*dfa_cpatt)->cpatt_sz = cpatt_sz; (*dfa_cpatt)->study_data_sz = study_data_sz; /* Copy pattern. */ (*dfa_cpatt)->patt = ib_mpool_strdup(pool, patt); if ((*dfa_cpatt)->patt == NULL) { pcre_free(cpatt); pcre_free(edata); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Copy compiled pattern. */ (*dfa_cpatt)->cpatt = ib_mpool_memdup(pool, cpatt, cpatt_sz); pcre_free(cpatt); if ((*dfa_cpatt)->cpatt == NULL) { pcre_free(edata); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Copy extra data (study data). */ if (edata != NULL) { /* Copy edata. */ (*dfa_cpatt)->edata = ib_mpool_memdup(pool, edata, sizeof(*edata)); if ((*dfa_cpatt)->edata == NULL) { pcre_free(edata); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Copy edata->study_data. */ (*dfa_cpatt)->edata->study_data = ib_mpool_memdup(pool, edata->study_data, study_data_sz); pcre_free(edata); if ((*dfa_cpatt)->edata->study_data == NULL) { IB_FTRACE_RET_STATUS(IB_EALLOC); } } else { (*dfa_cpatt)->edata = NULL; } IB_FTRACE_RET_STATUS(IB_OK); }
/** * Internal compilation of the modpcre pattern. * * @param[in] ib IronBee engine for logging. * @param[in] pool The memory pool to allocate memory out of. * @param[in] config Module configuration * @param[in] is_dfa Set to true for DFA * @param[out] pcpdata Pointer to new struct containing the compilation. * @param[in] patt The uncompiled pattern to match. * @param[out] errptr Pointer to an error message describing the failure. * @param[out] erroffset The location of the failure, if this fails. * * @returns IronBee status. IB_EINVAL if the pattern is invalid, * IB_EALLOC if memory allocation fails or IB_OK. */ static ib_status_t pcre_compile_internal(ib_engine_t *ib, ib_mpool_t *pool, const modpcre_cfg_t *config, bool is_dfa, modpcre_cpat_data_t **pcpdata, const char *patt, const char **errptr, int *erroffset) { assert(ib != NULL); assert(pool != NULL); assert(config != NULL); assert(pcpdata != NULL); assert(patt != NULL); /* Pattern data structure we'll create */ modpcre_cpat_data_t *cpdata; /* Compiled pattern. */ pcre *cpatt = NULL; /* Compiled pattern size. Used to copy cpatt. */ size_t cpatt_sz; /* Extra data structure. This contains the study_data pointer. */ pcre_extra *edata = NULL; /* Size of edata->study_data. */ size_t study_data_sz; /* How cpatt is produced. */ const int compile_flags = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; /* Are we using JIT? */ bool use_jit = !is_dfa; #ifdef PCRE_HAVE_JIT if (config->use_jit == 0) { use_jit = false; } /* Do we want to be using JIT? */ const bool want_jit = use_jit; #else use_jit = false; #endif /* PCRE_HAVE_JIT */ cpatt = pcre_compile(patt, compile_flags, errptr, erroffset, NULL); if (*errptr != NULL) { ib_log_error(ib, "PCRE compile error for \"%s\": %s at offset %d", patt, *errptr, *erroffset); return IB_EINVAL; } if (config->study) { if (use_jit) { #ifdef PCRE_HAVE_JIT edata = pcre_study(cpatt, PCRE_STUDY_JIT_COMPILE, errptr); if (*errptr != NULL) { pcre_free(cpatt); use_jit = false; ib_log_warning(ib, "PCRE-JIT study failed: %s", *errptr); } #endif } else { edata = pcre_study(cpatt, 0, errptr); if (*errptr != NULL) { pcre_free(cpatt); ib_log_error(ib, "PCRE study failed: %s", *errptr); } } } else if (use_jit) { ib_log_warning(ib, "PCRE: Disabling JIT because study disabled"); use_jit = false; } #ifdef PCRE_HAVE_JIT /* The check to see if JIT compilation was a success changed in 8.20RC1 now uses pcre_fullinfo see doc/pcrejit.3 */ if (use_jit) { int rc; int pcre_jit_ret; rc = pcre_fullinfo(cpatt, edata, PCRE_INFO_JIT, &pcre_jit_ret); if (rc != 0) { ib_log_error(ib, "PCRE-JIT failed to get pcre_fullinfo"); use_jit = false; } else if (pcre_jit_ret != 1) { ib_log_info(ib, "PCRE-JIT compiler does not support: %s", patt); use_jit = false; } else { /* Assume pcre_jit_ret == 1. */ /* Do nothing */ } } if (want_jit && !use_jit) { ib_log_info(ib, "Falling back to normal PCRE"); } #endif /*PCRE_HAVE_JIT*/ /* Compute the size of the populated values of cpatt. */ pcre_fullinfo(cpatt, edata, PCRE_INFO_SIZE, &cpatt_sz); if (edata != NULL) { pcre_fullinfo(cpatt, edata, PCRE_INFO_STUDYSIZE, &study_data_sz); } else { study_data_sz = 0; } /** * Below is only allocation and copy operations to pass the PCRE results * back to the output variable cpdata. */ cpdata = (modpcre_cpat_data_t *)ib_mpool_calloc(pool, sizeof(*cpdata), 1); if (cpdata == NULL) { pcre_free(cpatt); pcre_free(edata); ib_log_error(ib, "Failed to allocate cpdata of size: %zd", sizeof(*cpdata)); return IB_EALLOC; } cpdata->is_dfa = is_dfa; cpdata->is_jit = use_jit; cpdata->cpatt_sz = cpatt_sz; cpdata->study_data_sz = study_data_sz; /* Copy pattern. */ cpdata->patt = ib_mpool_strdup(pool, patt); if (cpdata->patt == NULL) { pcre_free(cpatt); pcre_free(edata); ib_log_error(ib, "Failed to duplicate pattern string: %s", patt); return IB_EALLOC; } /* Copy compiled pattern. */ cpdata->cpatt = ib_mpool_memdup(pool, cpatt, cpatt_sz); pcre_free(cpatt); if (cpdata->cpatt == NULL) { pcre_free(edata); ib_log_error(ib, "Failed to duplicate pattern of size: %zd", cpatt_sz); return IB_EALLOC; } ib_log_debug(ib, "PCRE copied cpatt @ %p -> %p (%zd bytes)", (void *)cpatt, (void *)cpdata->cpatt, cpatt_sz); /* Copy extra data (study data). */ if (edata != NULL) { /* Copy edata. */ cpdata->edata = ib_mpool_memdup(pool, edata, sizeof(*edata)); if (cpdata->edata == NULL) { pcre_free(edata); ib_log_error(ib, "Failed to duplicate edata."); return IB_EALLOC; } /* Copy edata->study_data. */ if (edata->study_data != NULL) { cpdata->edata->study_data = ib_mpool_memdup(pool, edata->study_data, study_data_sz); if (cpdata->edata->study_data == NULL) { ib_log_error(ib, "Failed to study data of size: %zd", study_data_sz); pcre_free(edata); return IB_EALLOC; } } pcre_free(edata); } else { cpdata->edata = ib_mpool_calloc(pool, 1, sizeof(*edata)); if (cpdata->edata == NULL) { pcre_free(edata); ib_log_error(ib, "Failed to allocate edata."); return IB_EALLOC; } } /* Set the PCRE limits for non-DFA patterns */ if (! is_dfa) { cpdata->edata->flags |= (PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION); cpdata->edata->match_limit = (unsigned long)config->match_limit; cpdata->edata->match_limit_recursion = (unsigned long)config->match_limit_recursion; cpdata->dfa_ws_size = 0; } else { cpdata->edata->match_limit = 0U; cpdata->edata->match_limit_recursion = 0U; cpdata->dfa_ws_size = (int)config->dfa_workspace_size; } /* Set stack limits for JIT */ if (cpdata->is_jit) { #ifdef PCRE_HAVE_JIT if (config->jit_stack_start == 0U) { cpdata->jit_stack_start = PCRE_JIT_STACK_START_MULT * config->match_limit_recursion; } else { cpdata->jit_stack_start = (int)config->jit_stack_start; } if (config->jit_stack_max == 0U) { cpdata->jit_stack_max = PCRE_JIT_STACK_MAX_MULT * config->match_limit_recursion; } else { cpdata->jit_stack_max = (int)config->jit_stack_max; } #endif } else { cpdata->jit_stack_start = 0; cpdata->jit_stack_max = 0; } ib_log_trace(ib, "Compiled pcre pattern \"%s\": " "cpatt=%p edata=%p limit=%ld rlimit=%ld study=%p " "dfa=%s dfa-ws-sz=%d " "jit=%s jit-stack: start=%d max=%d", patt, (void *)cpdata->cpatt, (void *)cpdata->edata, cpdata->edata->match_limit, cpdata->edata->match_limit_recursion, cpdata->edata->study_data, cpdata->is_dfa ? "yes" : "no", cpdata->dfa_ws_size, cpdata->is_jit ? "yes" : "no", cpdata->jit_stack_start, cpdata->jit_stack_max); *pcpdata = cpdata; return IB_OK; }
/********************************* * Helper Functions *********************************/ static ib_status_t sqli_create_pattern_set_from_file( sqli_pattern_set_t **out_ps, const char *path, ib_mpool_t *mp ) { assert(out_ps != NULL); assert(path != NULL); assert(mp != NULL); ib_status_t rc; FILE *fp = NULL; char *buffer = NULL; size_t buffer_size = 0; ib_list_t *items = NULL; ib_list_node_t *n = NULL; ib_mpool_t *tmp = NULL; sqli_pattern_set_t *ps = NULL; size_t i = 0; /* Temporary memory pool for this function only. */ rc = ib_mpool_create(&tmp, "sqli tmp", NULL); assert(rc == IB_OK); assert(tmp != NULL); fp = fopen(path, "r"); if (fp == NULL) { goto fail; } rc = ib_list_create(&items, tmp); assert(rc == IB_OK); assert(items != NULL); for (;;) { char *buffer_copy; int read = getline(&buffer, &buffer_size, fp); if (read == -1) { if (! feof(fp)) { fclose(fp); goto fail; } else { break; } } buffer_copy = ib_mpool_memdup(mp, buffer, read); assert(buffer_copy != NULL); while (buffer_copy[read-1] == '\n' || buffer_copy[read-1] == '\r') { buffer_copy[read-1] = '\0'; --read; } rc = ib_list_push(items, (void *)buffer_copy); assert(rc == IB_OK); } fclose(fp); ps = ib_mpool_alloc(mp, sizeof(*ps)); assert(ps != NULL); ps->num_patterns = ib_list_elements(items); ps->patterns = ib_mpool_alloc(mp, ps->num_patterns * sizeof(*ps->patterns)); assert(ps->patterns != NULL); i = 0; IB_LIST_LOOP(items, n) { ps->patterns[i] = ib_list_node_data(n); ++i; }
/* * Expand a string from the given hash-like object, ex version. See expand.h. */ ib_status_t ib_expand_str_gen_ex(ib_mpool_t *mp, const char *str, size_t str_len, const char *prefix, const char *suffix, bool nul, bool recurse, ib_expand_lookup_fn_t lookup_fn, const void *lookup_data, char **result, size_t *result_len) { IB_FTRACE_INIT(); ib_status_t rc; size_t pre_len; /* Prefix string length */ size_t suf_len = SIZE_MAX; /* Suffix string length */ const char *buf = str; /* Current buffer */ size_t buflen = str_len; /* Length of the buffer */ /* Sanity checks */ assert(mp != NULL); assert(str != NULL); assert(prefix != NULL); assert(suffix != NULL); assert(lookup_fn != NULL); assert(result != NULL); assert(result_len != NULL); /* Initialize the result to NULL */ *result = NULL; *result_len = 0; /* Validate prefix and suffix */ if ( (*prefix == '\0') || (*suffix == '\0') ) { IB_FTRACE_RET_STATUS(IB_EINVAL); } /* Compute prefix length */ pre_len = strlen(prefix); assert(pre_len != 0); /* Check for minimum string length */ if (str_len < (pre_len+1) ) { *result = (char *)ib_mpool_memdup(mp, str, str_len); *result_len = str_len; IB_FTRACE_RET_STATUS(IB_OK); } /* Loop until there is nothing more to find. */ while (1) { const char *pre = NULL; /* Pointer to found prefix string */ size_t pre_off = 0;/* Offset of prefix in the string */ const char *suf = NULL; /* Pointer to found suffix string */ const char *name; /* Pointer to name between pre and suffix */ size_t namelen; /* Length of the name */ char *new; /* New buffer */ size_t newlen; /* Length of new buffer */ const char *iptr; /* Initial block (up to the prefix) */ size_t ilen; /* Length of the initial block */ const char *fptr; /* Final block (after the suffix) */ size_t flen; /* Length of the final block */ ib_field_t *f; /* Field */ size_t slen; /* Length of the buffer to search */ /* Look for the last prefix in the string with a matching suffix */ slen = buflen; while ( (pre == NULL) && (slen >= pre_len) ) { if (recurse) { pre = ib_strrstr_ex(buf, slen, prefix, pre_len); } else { pre = ib_strstr_ex(buf, slen, prefix, pre_len); } if (pre == NULL) { break; } /* Lazy compute suffix length */ if (suf_len == SIZE_MAX) { suf_len = strlen(suffix); assert (suf_len != 0); } /* And the next matching suffix */ pre_off = pre - buf; suf = ib_strstr_ex(pre+pre_len, buflen - (pre_off + pre_len), suffix, suf_len); if ( recurse && (suf == NULL) ) { slen = (pre - buf); pre = NULL; } } /* Did we find a matching pair? */ if ( (pre == NULL) || (suf == NULL) ) { break; } /* The name is the block between the two */ name = (pre + pre_len); namelen = (suf - pre) - pre_len; /* Length of the initial block */ iptr = buf; ilen = (pre - buf); /* The final block */ fptr = (suf + suf_len); flen = (buf + buflen ) - fptr; /* Zero length name? Expand it to "" */ if (namelen == 0) { rc = join2(mp, iptr, ilen, fptr, flen, true, &new, &newlen); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } buf = new; buflen = newlen; continue; } /* Search the hash */ rc = lookup_fn(lookup_data, name, namelen, &f); if (rc == IB_ENOENT) { /* Not in the hash; replace with "" */ rc = join2(mp, iptr, ilen, fptr, flen, true, &new, &newlen); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } buf = new; buflen = newlen; continue; }
ib_status_t ib_tfn_data_get_ex( ib_engine_t *ib, ib_data_t *data, const char *name, size_t nlen, const ib_field_t **pf, const char *tfn ) { assert(data != NULL); char *fullname; size_t fnlen; size_t tlen; ib_status_t rc; /* No tfn just means a normal get. */ if (tfn == NULL) { rc = ib_data_get_ex(data, name, nlen, (void *)pf); return rc; } /* Build the full name with tfn: "name.t(tfn)" */ tlen = strlen(tfn); fnlen = nlen + tlen + 4; /* Additional ".t()" bytes */ fullname = (char *)ib_mpool_alloc(ib_data_pool(data), fnlen); memcpy(fullname, name, nlen); memcpy(fullname + nlen, ".t(", fnlen - nlen); memcpy(fullname + nlen + 3, tfn, fnlen - nlen - 3); fullname[fnlen - 1] = ')'; /* See if there is already a transformed version, otherwise * one needs to be created. */ rc = ib_data_get_ex(data, fullname, fnlen, (void *)pf); if (rc == IB_ENOENT) { const char *tname; size_t i; ib_field_t *new_pf; /* Get the non-tfn field. */ rc = ib_data_get_ex(data, name, nlen, &new_pf); if (rc != IB_OK) { return rc; } /* Currently this only works for string type fields. */ if ( (new_pf->type != IB_FTYPE_NULSTR) && (new_pf->type != IB_FTYPE_BYTESTR)) { return IB_EINVAL; } /* Copy the field, noting the tfn. */ rc = ib_field_copy(&new_pf, ib_data_pool(data), fullname, fnlen, new_pf); if (rc != IB_OK) { return rc; } new_pf->tfn = (char *)ib_mpool_memdup(ib_data_pool(data), tfn, tlen + 1); /* Transform. */ tname = tfn; for (i = 0; i <= tlen; ++i) { ib_tfn_t *t; ib_flags_t flags; if ((tfn[i] == ',') || (i == tlen)) { size_t len = (tfn + i) - tname; rc = ib_tfn_lookup_ex(ib, tname, len, &t); if (rc == IB_OK) { rc = ib_tfn_transform(ib, ib_data_pool(data), t, new_pf, (const ib_field_t**) &new_pf, &flags); if (rc != IB_OK) { /// @todo What to do here? Fail or ignore? } } else { /// @todo What to do here? Fail or ignore? } tname = tfn + i + 1; } } /* Store the transformed field. */ rc = ib_data_set(data, new_pf, name, nlen); if (rc != IB_OK) { return rc; } *pf = new_pf; } return rc; }