/********************************************************************* * * Function : pcrs_execute_single_command * * Description : Apply single pcrs command to the subject. * The subject itself is left untouched, memory for the result * is malloc()ed and it is the caller's responsibility to free * the result when it's no longer needed. * * Parameters : * 1 : subject = the subject (== original) string * 2 : pcrs_command = the pcrs command as string (s@foo@bar@) * 3 : hits = int* for returning the number of modifications * * Returns : NULL in case of errors, otherwise the * result of the pcrs command. * *********************************************************************/ char *pcrs_execute_single_command(const char *subject, const char *pcrs_command, int *hits) { size_t size; char *result = NULL; pcrs_job *job; assert(subject); assert(pcrs_command); *hits = 0; size = strlen(subject); job = pcrs_compile_command(pcrs_command, hits); if (NULL != job) { *hits = pcrs_execute(job, subject, size, &result, &size); if (*hits < 0) { freez(result); } pcrs_free_job(job); } return result; }
/********************************************************************* * * Function : pcrs_compile_dynamic_command * * Description : Takes a dynamic pcrs command, fills in the * values of the variables and compiles it. * * Parameters : * 1 : pcrs_command = The dynamic pcrs command to compile * 2 : v = NULL terminated array of variables and their values. * 3 : error = pcrs error code * * Returns : NULL in case of hard errors, otherwise the * compiled pcrs job. * *********************************************************************/ pcrs_job *pcrs_compile_dynamic_command(char *pcrs_command, const struct pcrs_variable v[], int *error) { char buf[PCRS_BUFFER_SIZE]; const char *original_pcrs_command = pcrs_command; char *pcrs_command_tmp = NULL; pcrs_job *job = NULL; int truncation = 0; char d; int ret; while ((NULL != v->name) && (NULL != pcrs_command)) { assert(NULL != v->value); if (NULL == strstr(pcrs_command, v->name)) { /* * Skip the substitution if the variable * name isn't part of the pattern. */ v++; continue; } /* Use pcrs to replace the variable with its value. */ d = pcrs_get_delimiter(v->value); if ('\0' == d) { /* No proper delimiter found */ *error = PCRS_ERR_CMDSYNTAX; return NULL; } /* * Variable names are supposed to contain alpha * numerical characters plus '_' only. */ assert(NULL == strchr(v->name, d)); ret = snprintf(buf, sizeof(buf), "s%c\\$%s%c%s%cgT", d, v->name, d, v->value, d); assert(ret >= 0); if (ret >= sizeof(buf)) { /* * Value didn't completely fit into buffer, * overwrite the end of the substitution text * with a truncation message and close the pattern * properly. */ const size_t trailer_size = sizeof(warning) + 3; /* 3 for d + "gT" */ char *trailer_start = buf + sizeof(buf) - trailer_size; ret = snprintf(trailer_start, trailer_size, "%s%cgT", warning, d); assert(ret == trailer_size - 1); assert(sizeof(buf) == strlen(buf) + 1); truncation = 1; } pcrs_command_tmp = pcrs_execute_single_command(pcrs_command, buf, error); if (NULL == pcrs_command_tmp) { return NULL; } if (pcrs_command != original_pcrs_command) { freez(pcrs_command); } pcrs_command = pcrs_command_tmp; v++; } job = pcrs_compile_command(pcrs_command, error); if (pcrs_command != original_pcrs_command) { freez(pcrs_command); } if (truncation) { *error = PCRS_WARN_TRUNCATION; } return job; }
/********************************************************************* * * Function : load_one_re_filterfile * * Description : Load a re_filterfile. * Generate a chained list of re_filterfile_spec's from * the "FILTER: " blocks, compiling all their substitutions * into chained lists of pcrs_job structs. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) * * Returns : 0 => Ok, everything else is an error. * *********************************************************************/ int load_one_re_filterfile(struct client_state *csp, int fileid) { FILE *fp; struct re_filterfile_spec *new_bl, *bl = NULL; struct file_list *fs; char buf[BUFFER_SIZE]; int error; unsigned long linenum = 0; pcrs_job *dummy, *lastjob = NULL; /* * No need to reload if unchanged */ if (!check_file_changed(current_re_filterfile[fileid], csp->config->re_filterfile[fileid], &fs)) { if (csp) { csp->rlist[fileid] = current_re_filterfile[fileid]; } return(0); } if (!fs) { goto load_re_filterfile_error; } /* * Open the file or fail */ if ((fp = fopen(csp->config->re_filterfile[fileid], "r")) == NULL) { goto load_re_filterfile_error; } /* * Read line by line */ while (read_config_line(buf, sizeof(buf), fp, &linenum) != NULL) { int new_filter = NO_NEW_FILTER; if (strncmp(buf, "FILTER:", 7) == 0) { new_filter = FT_CONTENT_FILTER; } else if (strncmp(buf, "SERVER-HEADER-FILTER:", 21) == 0) { new_filter = FT_SERVER_HEADER_FILTER; } else if (strncmp(buf, "CLIENT-HEADER-FILTER:", 21) == 0) { new_filter = FT_CLIENT_HEADER_FILTER; } else if (strncmp(buf, "CLIENT-HEADER-TAGGER:", 21) == 0) { new_filter = FT_CLIENT_HEADER_TAGGER; } else if (strncmp(buf, "SERVER-HEADER-TAGGER:", 21) == 0) { new_filter = FT_SERVER_HEADER_TAGGER; } /* * If this is the head of a new filter block, make it a * re_filterfile spec of its own and chain it to the list: */ if (new_filter != NO_NEW_FILTER) { new_bl = (struct re_filterfile_spec *)zalloc(sizeof(*bl)); if (new_bl == NULL) { goto load_re_filterfile_error; } if (new_filter == FT_CONTENT_FILTER) { new_bl->name = chomp(buf + 7); } else { new_bl->name = chomp(buf + 21); } new_bl->type = new_filter; /* * If a filter description is available, * encode it to HTML and save it. */ if (NULL != (new_bl->description = strpbrk(new_bl->name, " \t"))) { *new_bl->description++ = '\0'; new_bl->description = html_encode(chomp(new_bl->description)); if (NULL == new_bl->description) { new_bl->description = strdup("Out of memory while encoding this filter's description to HTML"); } } else { new_bl->description = strdup("No description available for this filter"); } new_bl->name = strdup(chomp(new_bl->name)); /* * If this is the first filter block, chain it * to the file_list rather than its (nonexistant) * predecessor */ if (fs->f == NULL) { fs->f = new_bl; } else { assert(NULL != bl); bl->next = new_bl; } bl = new_bl; log_error(LOG_LEVEL_RE_FILTER, "Reading in filter \"%s\" (\"%s\")", bl->name, bl->description); continue; } /* * Else, save the expression, make it a pcrs_job * and chain it into the current filter's joblist */ if (bl != NULL) { error = enlist(bl->patterns, buf); if (JB_ERR_MEMORY == error) { log_error(LOG_LEVEL_FATAL, "Out of memory while enlisting re_filter job \'%s\' for filter %s.", buf, bl->name); } assert(JB_ERR_OK == error); if (pcrs_job_is_dynamic(buf)) { /* * Dynamic pattern that might contain variables * and has to be recompiled for every request */ if (bl->joblist != NULL) { pcrs_free_joblist(bl->joblist); bl->joblist = NULL; } bl->dynamic = 1; log_error(LOG_LEVEL_RE_FILTER, "Adding dynamic re_filter job \'%s\' to filter %s succeeded.", buf, bl->name); continue; } else if (bl->dynamic) { /* * A previous job was dynamic and as we * recompile the whole filter anyway, it * makes no sense to compile this job now. */ log_error(LOG_LEVEL_RE_FILTER, "Adding static re_filter job \'%s\' to dynamic filter %s succeeded.", buf, bl->name); continue; } if ((dummy = pcrs_compile_command(buf, &error)) == NULL) { log_error(LOG_LEVEL_ERROR, "Adding re_filter job \'%s\' to filter %s failed with error %d.", buf, bl->name, error); continue; } else { if (bl->joblist == NULL) { bl->joblist = dummy; } else if (NULL != lastjob) { lastjob->next = dummy; } lastjob = dummy; log_error(LOG_LEVEL_RE_FILTER, "Adding re_filter job \'%s\' to filter %s succeeded.", buf, bl->name); } } else { log_error(LOG_LEVEL_ERROR, "Ignoring job %s outside filter block in %s, line %d", buf, csp->config->re_filterfile[fileid], linenum); } } fclose(fp); /* * Schedule the now-obsolete old data for unloading */ if ( NULL != current_re_filterfile[fileid] ) { current_re_filterfile[fileid]->unloader = unload_re_filterfile; } /* * Chain this file into the global list of loaded files */ fs->next = files->next; files->next = fs; current_re_filterfile[fileid] = fs; if (csp) { csp->rlist[fileid] = fs; } return( 0 ); load_re_filterfile_error: log_error(LOG_LEVEL_FATAL, "can't load re_filterfile '%s': %E", csp->config->re_filterfile[fileid]); return(-1); }