*/
#undef FEATURE_EXTENDED_HOST_PATTERNS
+/*
+ * Allow filtering with scripts and programs.
+ */
+#undef FEATURE_EXTERNAL_FILTERS
+
/*
* Keep connections alive if possible.
*/
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)
-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 $
"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 */
}
}
+#ifndef FEATURE_EXTERNAL_FILTERS
+ if (!err) err = map_block_killer(exports, "external-content-filters");
+#endif
+
if (err)
{
edit_free_file(file);
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/
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
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
-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 $
#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;
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);
-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 $
#include "miscutil.h"
#include "actions.h"
#include "cgi.h"
+#include "jcc.h"
#include "list.h"
#include "deanimate.h"
#include "urlmatch.h"
}
+#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
* 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...)
*********************************************************************/
char *execute_content_filters(struct client_state *csp)
{
+ char *content;
filter_function_ptr content_filter;
assert(content_filters_enabled(csp->action));
}
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));
}
return TRUE;
}
- return FALSE;
+ return (!list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]));
}
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]));
}
-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 $
}
#endif
+#ifdef FEATURE_EXTERNAL_FILTERS
+ mark_socket_for_close_on_execute(fd);
+#endif
+
#ifdef TCP_NODELAY
{ /* turn off TCP coalescence */
int mi = 1;
{
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__) */
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
}
#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);
}
+#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
#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 $
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[];
-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 $
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) */
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
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) */
#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 $
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 */
-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 $
#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" */
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++)
{
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)
* *************************************************************************/
-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 $
{
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
{
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);
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)
{
#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 $
/** 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
/**
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
/** 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;
<td>Change HTTP/1.1 requests to HTTP/1.0. Only change if you know
what you're doing!</td>
</tr>
+
+<!-- @if-external-content-filters-start -->
+ <tr class="bg1" align="left" valign="top">
+ <td class="en1"> </td>
+ <td class="dis1" align="center" valign="middle"><input type="radio"
+ name="external_content_filter_all" id="external_content_filter_all_n" value="N" @external-content-filter-all-n@ ></td>
+ <td class="noc1" align="center" valign="middle"><input type="radio"
+ name="external_content_filter_all" id="external_content_filter_all_x" value="X" @external-content-filter-all-x@ ></td>
+ <td class="action"><a href="@user-manual@@actions-help-prefix@EXTERNAL_FILTER">external-filter</a> *</td>
+ <td>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.</td>
+ </tr>
+
+@external-content-filter-params@
+<!-- if-external-content-filters-end@ -->
+
<tr class="bg1" align="left" valign="top">
<td class="en1" align="center" valign="middle"><input type="radio"
name="fast_redirects" value="Y" @fast-redirects-y@