From: Fabian Keil Date: Mon, 2 Jun 2014 06:19:06 +0000 (+0000) Subject: Add support for external filters X-Git-Tag: v_3_0_22~137 X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=commitdiff_plain;h=7759e9a72652fe7e10f8596212865d8b2cd22ad4 Add support for external filters ... which allow to process the response body with a script or program written in any language the platform supports. External filters are enabled with +external-filter{} after they have been defined in one of the filter files with a header line starting with "EXTERNAL-FILTER:". For this to work, a temporary directory has to be specified using the newly-added temporary-directory directive. External filters are experimental and not expected to work on all platforms (yet). --- diff --git a/acconfig.h b/acconfig.h index d2f50a29..6e4022dd 100644 --- a/acconfig.h +++ b/acconfig.h @@ -161,6 +161,11 @@ */ #undef FEATURE_EXTENDED_HOST_PATTERNS +/* + * Allow filtering with scripts and programs. + */ +#undef FEATURE_EXTERNAL_FILTERS + /* * Keep connections alive if possible. */ diff --git a/actionlist.h b/actionlist.h index b8aac4ae..9a8f28b7 100644 --- a/actionlist.h +++ b/actionlist.h @@ -71,6 +71,9 @@ DEFINE_ACTION_STRING ("deanimate-gifs", ACTION_DEANIMATE, DEFINE_CGI_PARAM_RADIO ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE, "first", 0) DEFINE_CGI_PARAM_RADIO ("deanimate-gifs", ACTION_DEANIMATE, ACTION_STRING_DEANIMATE, "last", 1) DEFINE_ACTION_BOOL ("downgrade-http-version", ACTION_DOWNGRADE) +#ifdef FEATURE_EXTERNAL_FILTERS +DEFINE_ACTION_MULTI ("external-filter", ACTION_MULTI_EXTERNAL_FILTER) +#endif #ifdef FEATURE_FAST_REDIRECTS DEFINE_ACTION_STRING ("fast-redirects", ACTION_FAST_REDIRECTS, ACTION_STRING_FAST_REDIRECTS) DEFINE_CGI_PARAM_RADIO ("fast-redirects", ACTION_FAST_REDIRECTS, ACTION_STRING_FAST_REDIRECTS, "simple-check", 0) diff --git a/cgiedit.c b/cgiedit.c index cb824f9e..70f6b456 100644 --- a/cgiedit.c +++ b/cgiedit.c @@ -1,4 +1,4 @@ -const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.78 2013/11/24 14:22:51 fabiankeil Exp $"; +const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.79 2013/11/24 14:25:19 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $ @@ -243,6 +243,14 @@ static const struct filter_type_info filter_type_info[] = "server-header-tagger-all", "server_header_tagger_all", "E", "SERVER-HEADER-TAGGER" }, +#ifdef FEATURE_EXTERNAL_FILTERS + { + ACTION_MULTI_EXTERNAL_FILTER, + "external-content-filter-params", "external-filter", + "external-content-filter-all", "external_content_filter_all", + "E", "EXTERNAL-CONTENT-FILTER" + }, +#endif }; /* FIXME: Following non-static functions should be prototyped in .h or made static */ @@ -2820,6 +2828,10 @@ jb_err cgi_edit_actions_for_url(struct client_state *csp, } } +#ifndef FEATURE_EXTERNAL_FILTERS + if (!err) err = map_block_killer(exports, "external-content-filters"); +#endif + if (err) { edit_free_file(file); diff --git a/configure.in b/configure.in index 89ebb479..309cbb90 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. dnl -dnl $Id: configure.in,v 1.182 2014/05/20 11:55:09 fabiankeil Exp $ +dnl $Id: configure.in,v 1.183 2014/06/02 05:46:53 fabiankeil Exp $ dnl dnl Written by and Copyright (C) 2001-2010 the dnl Privoxy team. http://www.privoxy.org/ @@ -32,7 +32,7 @@ dnl ================================================================= dnl AutoConf Initialization dnl ================================================================= -AC_REVISION($Revision: 1.182 $) +AC_REVISION($Revision: 1.183 $) AC_INIT(jcc.c) if test ! -f config.h.in; then @@ -962,6 +962,12 @@ AC_ARG_ENABLE(extended-host-patterns, AC_DEFINE(FEATURE_EXTENDED_HOST_PATTERNS) fi]) +AC_ARG_ENABLE(external-filters, +[ --enable-external-filters Allow to filter content with scripts and programs. Experimental.], +[if test $enableval = yes; then + AC_DEFINE(FEATURE_EXTERNAL_FILTERS,1,[Define to 1 to allow to filter content with scripts and programs.]) +fi]) + AC_ARG_ENABLE(accept-filter, [ --enable-accept-filter Try to use accf_http(9) if supported.], [if test $enableval = yes; then diff --git a/errlog.c b/errlog.c index fe2ee38d..d679f617 100644 --- a/errlog.c +++ b/errlog.c @@ -1,4 +1,4 @@ -const char errlog_rcs[] = "$Id: errlog.c,v 1.117 2012/12/09 12:28:14 fabiankeil Exp $"; +const char errlog_rcs[] = "$Id: errlog.c,v 1.118 2014/05/05 09:51:19 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/errlog.c,v $ @@ -75,6 +75,9 @@ const char errlog_rcs[] = "$Id: errlog.c,v 1.117 2012/12/09 12:28:14 fabiankeil #include "errlog.h" #include "project.h" #include "jcc.h" +#ifdef FEATURE_EXTERNAL_FILTERS +#include "jbsockets.h" +#endif const char errlog_h_rcs[] = ERRLOG_H_VERSION; @@ -354,6 +357,10 @@ void init_error_log(const char *prog_name, const char *logfname) log_error(LOG_LEVEL_FATAL, "init_error_log(): can't open logfile: \'%s\'", logfname); } +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(3); +#endif + /* set logging to be completely unbuffered */ setbuf(fp, NULL); diff --git a/filters.c b/filters.c index fcca2669..290940d1 100644 --- a/filters.c +++ b/filters.c @@ -1,4 +1,4 @@ -const char filters_rcs[] = "$Id: filters.c,v 1.179 2013/12/24 13:32:51 fabiankeil Exp $"; +const char filters_rcs[] = "$Id: filters.c,v 1.180 2013/12/24 13:33:13 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/filters.c,v $ @@ -65,6 +65,7 @@ const char filters_rcs[] = "$Id: filters.c,v 1.179 2013/12/24 13:32:51 fabiankei #include "miscutil.h" #include "actions.h" #include "cgi.h" +#include "jcc.h" #include "list.h" #include "deanimate.h" #include "urlmatch.h" @@ -1732,6 +1733,229 @@ static char *pcrs_filter_response(struct client_state *csp) } +#ifdef FEATURE_EXTERNAL_FILTERS +/********************************************************************* + * + * Function : get_external_filter + * + * Description : Lookup the code to execute for an external filter. + * Masks the misuse of the re_filterfile_spec. + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : name = Name of the content filter to get + * + * Returns : A pointer to the requested code + * or NULL if the filter wasn't found + * + *********************************************************************/ +static const char *get_external_filter(const struct client_state *csp, + const char *name) +{ + struct re_filterfile_spec *external_filter; + + external_filter = get_filter(csp, name, FT_EXTERNAL_CONTENT_FILTER); + if (external_filter == NULL) + { + log_error(LOG_LEVEL_FATAL, + "Didn't find stuff to execute for external filter: %s", + name); + } + + return external_filter->patterns->first->str; + +} + + +/********************************************************************* + * + * Function : set_privoxy_variables + * + * Description : Sets a couple of privoxy-specific environment variables + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * + * Returns : N/A + * + *********************************************************************/ +static void set_privoxy_variables(const struct client_state *csp) +{ + int i; + struct { + const char *name; + const char *value; + } env[] = { + { "PRIVOXY_URL", csp->http->url }, + { "PRIVOXY_PATH", csp->http->path }, + { "PRIVOXY_HOST", csp->http->host }, + { "PRIVOXY_ORIGIN", csp->ip_addr_str }, + }; + + for (i = 0; i < SZ(env); i++) + { + if (setenv(env[i].name, env[i].value, 1)) + { + log_error(LOG_LEVEL_ERROR, "Failed to set %s=%s: %E", + env[i].name, env[i].value); + } + } +} + + +/********************************************************************* + * + * Function : execute_external_filter + * + * Description : Pipe content into external filter and return the output + * + * Parameters : + * 1 : csp = Current client state (buffers, headers, etc...) + * 2 : name = Name of the external filter to execute + * 3 : content = The original content to filter + * 4 : size = The size of the content buffer + * + * Returns : a pointer to the (newly allocated) modified buffer. + * or NULL if there were no hits or something went wrong + * + *********************************************************************/ +static char *execute_external_filter(const struct client_state *csp, + const char *name, char *content, size_t *size) +{ + char cmd[200]; + char file_name[FILENAME_MAX]; + FILE *fp; + char *filter_output; + int fd; + int ret; + size_t new_size; + const char *external_filter; + + if (csp->config->temporary_directory == NULL) + { + log_error(LOG_LEVEL_ERROR, + "No temporary-directory configured. Can't execute filter: %s", + name); + return NULL; + } + + external_filter = get_external_filter(csp, name); + + if (sizeof(file_name) < snprintf(file_name, sizeof(file_name), + "%s/privoxy-XXXXXXXX", csp->config->temporary_directory)) + { + log_error(LOG_LEVEL_ERROR, "temporary-directory path too long"); + return NULL; + } + + fd = mkstemp(file_name); + if (fd == -1) + { + log_error(LOG_LEVEL_ERROR, "mkstemp() failed to create %s: %E", file_name); + return NULL; + } + + fp = fdopen(fd, "w"); + if (fp == NULL) + { + log_error(LOG_LEVEL_ERROR, "fdopen() failed: %E"); + unlink(file_name); + return NULL; + } + + /* + * The size may be zero if a previous filter discarded everything. + * + * This isn't necessary unintentional, so we just don't try + * to fwrite() nothing and let the user deal with the rest. + */ + if ((*size != 0) && fwrite(content, *size, 1, fp) != 1) + { + log_error(LOG_LEVEL_ERROR, "fwrite(..., %d, 1, ..) failed: %E", *size); + unlink(file_name); + return NULL; + } + fclose(fp); + + if (sizeof(cmd) < snprintf(cmd, sizeof(cmd), "%s < %s", external_filter, file_name)) + { + log_error(LOG_LEVEL_ERROR, + "temporary-directory or external filter path too long"); + unlink(file_name); + return NULL; + } + + log_error(LOG_LEVEL_RE_FILTER, "Executing '%s': %s", name, cmd); + + /* + * The locking is necessary to prevent other threads + * from overwriting the environment variables before + * the popen fork. Afterwards this no longer matters. + */ + privoxy_mutex_lock(&external_filter_mutex); + set_privoxy_variables(csp); + fp = popen(cmd, "r"); + privoxy_mutex_unlock(&external_filter_mutex); + if (fp == NULL) + { + log_error(LOG_LEVEL_ERROR, "popen(\"%s\", \"r\") failed: %E", cmd); + unlink(file_name); + return NULL; + } + + filter_output = malloc_or_die(*size); + + new_size = 0; + while (!feof(fp) && !ferror(fp)) + { + size_t len; + /* Could be bigger ... */ + enum { READ_LENGTH = 2048 }; + + if (new_size + READ_LENGTH >= *size) + { + char *p; + + /* Could be considered wasteful if the content is 'large'. */ + *size = (*size != 0) ? *size * 2 : READ_LENGTH; + + p = realloc(filter_output, *size); + if (p == NULL) + { + log_error(LOG_LEVEL_ERROR, "Out of memory while reading " + "external filter output. Using what we got so far."); + break; + } + filter_output = p; + } + len = fread(&filter_output[new_size], 1, READ_LENGTH, fp); + if (len > 0) + { + new_size += len; + } + } + + ret = pclose(fp); + if (ret == -1) + { + log_error(LOG_LEVEL_ERROR, "Executing %s failed: %E", cmd); + } + else + { + log_error(LOG_LEVEL_RE_FILTER, + "Executing '%s' resulted in return value %d. " + "Read %d of up to %d bytes.", name, (ret >> 8), new_size, *size); + } + + unlink(file_name); + *size = new_size; + + return filter_output; + +} +#endif /* def FEATURE_EXTERNAL_FILTERS */ + + /********************************************************************* * * Function : gif_deanimate_response @@ -1798,7 +2022,8 @@ static char *gif_deanimate_response(struct client_state *csp) * Function : get_filter_function * * Description : Decides which content filter function has - * to be applied (if any). + * to be applied (if any). Only considers functions + * for internal filters which are mutually-exclusive. * * Parameters : * 1 : csp = Current client state (buffers, headers, etc...) @@ -1993,6 +2218,7 @@ static jb_err prepare_for_filtering(struct client_state *csp) *********************************************************************/ char *execute_content_filters(struct client_state *csp) { + char *content; filter_function_ptr content_filter; assert(content_filters_enabled(csp->action)); @@ -2023,8 +2249,32 @@ char *execute_content_filters(struct client_state *csp) } content_filter = get_filter_function(csp); + content = (content_filter != NULL) ? (*content_filter)(csp) : NULL; + +#ifdef FEATURE_EXTERNAL_FILTERS + if (!list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])) + { + struct list_entry *filtername; + size_t size = (size_t)csp->content_length; + + if (content == NULL) + { + content = csp->iob->cur; + size = (size_t)(csp->iob->eod - csp->iob->cur); + } + + for (filtername = csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]->first; + filtername ; filtername = filtername->next) + { + content = execute_external_filter(csp, filtername->str, content, &size); + } + csp->flags |= CSP_FLAG_MODIFIED; + csp->content_length = size; + } +#endif /* def FEATURE_EXTERNAL_FILTERS */ + + return content; - return ((*content_filter)(csp)); } @@ -2414,7 +2664,7 @@ int content_requires_filtering(struct client_state *csp) return TRUE; } - return FALSE; + return (!list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])); } @@ -2435,7 +2685,8 @@ int content_requires_filtering(struct client_state *csp) int content_filters_enabled(const struct current_action_spec *action) { return ((action->flags & ACTION_DEANIMATE) || - !list_is_empty(action->multi[ACTION_MULTI_FILTER])); + !list_is_empty(action->multi[ACTION_MULTI_FILTER]) || + !list_is_empty(action->multi[ACTION_MULTI_EXTERNAL_FILTER])); } diff --git a/jbsockets.c b/jbsockets.c index 0517ee08..fc79483b 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -1,4 +1,4 @@ -const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.123 2013/03/06 21:06:18 diem Exp $"; +const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.124 2013/03/20 11:30:05 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $ @@ -289,6 +289,10 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client } #endif +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif + #ifdef TCP_NODELAY { /* turn off TCP coalescence */ int mi = 1; @@ -494,6 +498,9 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli { flags |= O_NDELAY; fcntl(fd, F_SETFL, flags); +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ @@ -908,6 +915,10 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) fd = socket(AF_INET, SOCK_STREAM, 0); #endif /* def HAVE_RFC2553 */ +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif + #ifdef _WIN32 if (fd == JB_INVALID_SOCKET) #else @@ -1319,6 +1330,10 @@ int accept_connection(struct client_state * csp, jb_socket fds[]) } #endif +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(afd); +#endif + csp->cfd = afd; #ifdef HAVE_RFC2553 csp->ip_addr_str = malloc(NI_MAXHOST); @@ -1518,6 +1533,42 @@ int socket_is_still_alive(jb_socket sfd) } +#ifdef FEATURE_EXTERNAL_FILTERS +/********************************************************************* + * + * Function : mark_socket_for_close_on_execute + * + * Description : Marks a socket for close on execute. + * + * Used so that external filters have no direct + * access to sockets they shouldn't care about. + * + * Not implemented for all platforms. + * + * Parameters : + * 1 : fd = The socket to mark + * + * Returns : void. + * + *********************************************************************/ +void mark_socket_for_close_on_execute(jb_socket fd) +{ +#ifdef FEATURE_PTHREAD + int ret; + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (ret == -1) + { + log_error(LOG_LEVEL_ERROR, + "fcntl(%d, F_SETFD, FD_CLOEXEC) failed", fd); + } +#else +#warning "Sockets will be visible to external filters" +#endif +} +#endif /* def FEATURE_EXTERNAL_FILTERS */ + /* Local Variables: tab-width: 3 diff --git a/jbsockets.h b/jbsockets.h index 0064bb85..765c43f9 100644 --- a/jbsockets.h +++ b/jbsockets.h @@ -1,6 +1,6 @@ #ifndef JBSOCKETS_H_INCLUDED #define JBSOCKETS_H_INCLUDED -#define JBSOCKETS_H_VERSION "$Id: jbsockets.h,v 1.21 2012/10/12 11:17:48 fabiankeil Exp $" +#define JBSOCKETS_H_VERSION "$Id: jbsockets.h,v 1.22 2013/11/24 14:23:28 fabiankeil Exp $" /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jbsockets.h,v $ @@ -61,6 +61,10 @@ extern unsigned long resolve_hostname_to_ip(const char *host); extern int socket_is_still_alive(jb_socket sfd); +#ifdef FEATURE_EXTERNAL_FILTERS +extern void mark_socket_for_close_on_execute(jb_socket fd); +#endif + /* Revision control strings from this header and associated .c file */ extern const char jbsockets_rcs[]; extern const char jbsockets_h_rcs[]; diff --git a/jcc.c b/jcc.c index b1d275c3..9be9b12c 100644 --- a/jcc.c +++ b/jcc.c @@ -1,4 +1,4 @@ -const char jcc_rcs[] = "$Id: jcc.c,v 1.424 2013/03/01 17:38:34 fabiankeil Exp $"; +const char jcc_rcs[] = "$Id: jcc.c,v 1.425 2014/02/10 14:39:43 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.c,v $ @@ -182,6 +182,10 @@ privoxy_mutex_t log_mutex; privoxy_mutex_t log_init_mutex; privoxy_mutex_t connection_reuse_mutex; +#ifdef FEATURE_EXTERNAL_FILTERS +privoxy_mutex_t external_filter_mutex; +#endif + #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) privoxy_mutex_t resolver_mutex; #endif /* !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) */ @@ -3134,6 +3138,9 @@ static void initialize_mutexes(void) privoxy_mutex_init(&log_mutex); privoxy_mutex_init(&log_init_mutex); privoxy_mutex_init(&connection_reuse_mutex); +#ifdef FEATURE_EXTERNAL_FILTERS + privoxy_mutex_init(&external_filter_mutex); +#endif /* * XXX: The assumptions below are a bit naive @@ -3518,6 +3525,13 @@ int main(int argc, char **argv) close(fd); } +#ifdef FEATURE_EXTERNAL_FILTERS + for (fd = 0; fd < 3; fd++) + { + mark_socket_for_close_on_execute(fd); + } +#endif + chdir("/"); } /* -END- if (daemon_mode) */ diff --git a/jcc.h b/jcc.h index aa8d8299..9b07df97 100644 --- a/jcc.h +++ b/jcc.h @@ -1,6 +1,6 @@ #ifndef JCC_H_INCLUDED #define JCC_H_INCLUDED -#define JCC_H_VERSION "$Id: jcc.h,v 1.32 2011/11/06 11:48:23 fabiankeil Exp $" +#define JCC_H_VERSION "$Id: jcc.h,v 1.33 2013/11/24 14:23:28 fabiankeil Exp $" /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jcc.h,v $ @@ -79,6 +79,10 @@ extern privoxy_mutex_t log_mutex; extern privoxy_mutex_t log_init_mutex; extern privoxy_mutex_t connection_reuse_mutex; +#ifdef FEATURE_EXTERNAL_FILTERS +extern privoxy_mutex_t external_filter_mutex; +#endif + #ifndef HAVE_GMTIME_R extern privoxy_mutex_t gmtime_mutex; #endif /* ndef HAVE_GMTIME_R */ diff --git a/loadcfg.c b/loadcfg.c index 5b175a6c..b9a15c9f 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -1,4 +1,4 @@ -const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.138 2013/04/23 09:42:53 fabiankeil Exp $"; +const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.139 2013/11/24 14:25:19 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/loadcfg.c,v $ @@ -157,6 +157,7 @@ static struct file_list *current_configfile = NULL; #define hash_split_large_cgi_forms 671658948U /* "split-large-cgi-forms" */ #define hash_suppress_blocklists 1948693308U /* "suppress-blocklists" */ #define hash_templdir 11067889U /* "templdir" */ +#define hash_temporary_directory 1824125181U /* "temporary-directory" */ #define hash_tolerate_pipelining 1360286620U /* "tolerate-pipelining" */ #define hash_toggle 447966U /* "toggle" */ #define hash_trust_info_url 430331967U /* "trust-info-url" */ @@ -222,6 +223,9 @@ static void unload_configfile (void * data) freez(config->logdir); freez(config->templdir); freez(config->hostname); +#ifdef FEATURE_EXTERNAL_FILTERS + freez(config->temporary_directory); +#endif for (i = 0; i < MAX_LISTENING_SOCKETS; i++) { @@ -1364,6 +1368,16 @@ struct configuration_spec * load_config(void) config->templdir = make_path(NULL, arg); break; +#ifdef FEATURE_EXTERNAL_FILTERS +/* ************************************************************************* + * temporary-directory directory-name + * *************************************************************************/ + case hash_temporary_directory : + freez(config->temporary_directory); + config->temporary_directory = make_path(NULL, arg); + break; +#endif + /* ************************************************************************* * tolerate-pipelining (0|1) * *************************************************************************/ diff --git a/loaders.c b/loaders.c index 37b7d65b..a23ae781 100644 --- a/loaders.c +++ b/loaders.c @@ -1,4 +1,4 @@ -const char loaders_rcs[] = "$Id: loaders.c,v 1.96 2013/11/24 14:22:51 fabiankeil Exp $"; +const char loaders_rcs[] = "$Id: loaders.c,v 1.97 2013/11/24 14:25:19 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/loaders.c,v $ @@ -1170,6 +1170,12 @@ int load_one_re_filterfile(struct client_state *csp, int fileid) { new_filter = FT_SERVER_HEADER_TAGGER; } +#ifdef FEATURE_EXTERNAL_FILTERS + else if (strncmp(buf, "EXTERNAL-FILTER:", 16) == 0) + { + new_filter = FT_EXTERNAL_CONTENT_FILTER; + } +#endif /* * If this is the head of a new filter block, make it a @@ -1186,6 +1192,12 @@ int load_one_re_filterfile(struct client_state *csp, int fileid) { new_bl->name = chomp(buf + 7); } +#ifdef FEATURE_EXTERNAL_FILTERS + else if (new_filter == FT_EXTERNAL_CONTENT_FILTER) + { + new_bl->name = chomp(buf + 16); + } +#endif else { new_bl->name = chomp(buf + 21); @@ -1234,12 +1246,33 @@ int load_one_re_filterfile(struct client_state *csp, int fileid) continue; } - /* - * Else, save the expression, make it a pcrs_job - * and chain it into the current filter's joblist - */ +#ifdef FEATURE_EXTERNAL_FILTERS + if ((bl != NULL) && (bl->type == FT_EXTERNAL_CONTENT_FILTER)) + { + /* Save the code as "pattern", but do not compile anything. */ + if (bl->patterns->first != NULL) + { + log_error(LOG_LEVEL_FATAL, "External filter '%s' contains several jobss. " + "Did you forget to escape a line break?", + bl->name); + } + error = enlist(bl->patterns, buf); + if (JB_ERR_MEMORY == error) + { + log_error(LOG_LEVEL_FATAL, + "Out of memory while enlisting external filter code \'%s\' for filter %s.", + buf, bl->name); + } + freez(buf); + continue; + } +#endif if (bl != NULL) { + /* + * Save the expression, make it a pcrs_job + * and chain it into the current filter's joblist + */ error = enlist(bl->patterns, buf); if (JB_ERR_MEMORY == error) { diff --git a/project.h b/project.h index a1295097..f57381ae 100644 --- a/project.h +++ b/project.h @@ -1,7 +1,7 @@ #ifndef PROJECT_H_INCLUDED #define PROJECT_H_INCLUDED /** Version string. */ -#define PROJECT_H_VERSION "$Id: project.h,v 1.203 2013/11/24 14:26:39 fabiankeil Exp $" +#define PROJECT_H_VERSION "$Id: project.h,v 1.204 2014/05/26 10:46:45 fabiankeil Exp $" /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/project.h,v $ @@ -566,7 +566,9 @@ struct iob /** Index into current_action_spec::multi[] for server-header tags to apply. */ #define ACTION_MULTI_SERVER_HEADER_TAGGER 5 /** Number of multi-string actions. */ -#define ACTION_MULTI_COUNT 6 +#define ACTION_MULTI_EXTERNAL_FILTER 6 +/** Number of multi-string actions. */ +#define ACTION_MULTI_COUNT 7 /** @@ -1115,9 +1117,17 @@ enum filter_type FT_SERVER_HEADER_FILTER = 2, FT_CLIENT_HEADER_TAGGER = 3, FT_SERVER_HEADER_TAGGER = 4, +#ifdef FEATURE_EXTERNAL_FILTERS + FT_EXTERNAL_CONTENT_FILTER = 5, +#endif FT_INVALID_FILTER = 42, }; + +#ifdef FEATURE_EXTERNAL_FILTERS +#define MAX_FILTER_TYPES 6 +#else #define MAX_FILTER_TYPES 5 +#endif /** * This struct represents one filter (one block) from @@ -1246,6 +1256,11 @@ struct configuration_spec /** The directory for customized CGI templates. */ const char *templdir; +#ifdef FEATURE_EXTERNAL_FILTERS + /** The template used to create temporary files. */ + const char *temporary_directory; +#endif + /** The log file directory. */ const char *logdir; diff --git a/templates/edit-actions-for-url b/templates/edit-actions-for-url index 0c62e7e6..53825180 100644 --- a/templates/edit-actions-for-url +++ b/templates/edit-actions-for-url @@ -527,6 +527,24 @@ function show_limit_connect_opts(tf) Change HTTP/1.1 requests to HTTP/1.0. Only change if you know what you're doing! + + + +   + + + external-filter * + Filter the website through external scripts or programs. + You can use the radio buttons on this line to disable + all filters applied by previous rules, and/or you can enable or + disable the filters individually below. + + +@external-content-filter-params@ + +