marc 98/01/05 12:48:59
Modified: src Tag: APACHE_1_2_X mod_imap.c
Log:
SECURITY: Numerous changes to mod_imap in a general cleanup including
fixing a possible buffer overflow. This cleanup was done with 1.3
code as a basis.
Dean says:
This is a bit large, but that's deliberate because I took the opportunity
to do the crap that we've been wanting done to mod_imap.
- liberal use of const to help find stack assignments
- remove all constant sized char arrays except input[]; replaced by pool
string functions or by pointers into tokens inside the input[]
array
- in particular, the use of read_quoted() had a stack overrun potential.
Eliminated.
- These changes can chew memory when generating a menu. I don't care,
I'd rather have them do that than have them overrun the stack. It
shouldn't chew more than approx the size of the map file though.
- better error handling
Submitted by: Dean Gaudet
Reviewed by: Martin Kraemer, Mark J Cox, Marc Slemko, Randy Terbush
Revision Changes Path
No revision
No revision
1.21.2.3 +654 -623 apache/src/mod_imap.c
Index: mod_imap.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_imap.c,v
retrieving revision 1.21.2.2
retrieving revision 1.21.2.3
diff -u -r1.21.2.2 -r1.21.2.3
--- mod_imap.c 1997/11/05 11:43:14 1.21.2.2
+++ mod_imap.c 1998/01/05 20:48:58 1.21.2.3
@@ -20,7 +20,8 @@
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
- * prior written permission.
+ * prior written permission. For written permission, please contact
+ * [EMAIL PROTECTED]
*
* 5. Redistributions of any form whatsoever must retain the following
* acknowledgment:
@@ -96,8 +97,6 @@
#include "util_script.h"
#define IMAP_MAGIC_TYPE "application/x-httpd-imap"
-#define LARGEBUF 500
-#define SMALLBUF 256
#define MAXVERTS 100
#define X 0
#define Y 1
@@ -107,62 +106,65 @@
#define IMAP_BASE_DEFAULT "map"
#ifdef SUNOS4
-double strtod(); /* SunOS needed this */
+double strtod(); /* SunOS needed this */
#endif
module imap_module;
-typedef struct {
- char *imap_menu;
- char *imap_default;
- char *imap_base;
+typedef struct {
+ char *imap_menu;
+ char *imap_default;
+ char *imap_base;
} imap_conf_rec;
-void *create_imap_dir_config (pool *p, char *dummy) {
- imap_conf_rec *icr =
- (imap_conf_rec *)palloc(p, sizeof(imap_conf_rec));
-
- icr->imap_menu = NULL;
- icr->imap_default = NULL;
- icr->imap_base = NULL;
-
- return icr;
-}
-
-void *merge_imap_dir_configs (pool *p, void *basev, void *addv)
-{
- imap_conf_rec *new=(imap_conf_rec *)pcalloc (p, sizeof(imap_conf_rec));
- imap_conf_rec *base = (imap_conf_rec *)basev;
- imap_conf_rec *add = (imap_conf_rec *)addv;
-
- new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
- new->imap_default=add->imap_default ? add->imap_default :
base->imap_default;
- new->imap_base =add-> imap_base ? add->imap_base : base->imap_base;
-
- return new;
+static void *create_imap_dir_config(pool *p, char *dummy)
+{
+ imap_conf_rec *icr =
+ (imap_conf_rec *) palloc(p, sizeof(imap_conf_rec));
+
+ icr->imap_menu = NULL;
+ icr->imap_default = NULL;
+ icr->imap_base = NULL;
+
+ return icr;
+}
+
+static void *merge_imap_dir_configs(pool *p, void *basev, void *addv)
+{
+ imap_conf_rec *new = (imap_conf_rec *) pcalloc(p, sizeof(imap_conf_rec));
+ imap_conf_rec *base = (imap_conf_rec *) basev;
+ imap_conf_rec *add = (imap_conf_rec *) addv;
+
+ new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
+ new->imap_default = add->imap_default ? add->imap_default :
base->imap_default;
+ new->imap_base = add->imap_base ? add->imap_base : base->imap_base;
+
+ return new;
}
-command_rec imap_cmds[] = {
-{ "ImapMenu", set_string_slot,
- (void*)XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1,
- "the type of menu generated: none, formatted, semiformatted,
unformatted"},
-{ "ImapDefault", set_string_slot,
- (void*)XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1,
- "the action taken if no match: error, nocontent, referer, menu, URL" },
-{ "ImapBase", set_string_slot,
- (void*)XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1,
- "the base for all URL's: map, referer, URL (or start of)" },
-{ NULL }
+static command_rec imap_cmds[] =
+{
+ {"ImapMenu", set_string_slot,
+ (void *) XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1,
+ "the type of menu generated: none, formatted, semiformatted, unformatted"},
+ {"ImapDefault", set_string_slot,
+ (void *) XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1,
+ "the action taken if no match: error, nocontent, referer, menu, URL"},
+ {"ImapBase", set_string_slot,
+ (void *) XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1,
+ "the base for all URL's: map, referer, URL (or start of)"},
+ {NULL}
};
-int pointinrect(double point[2], double coords[MAXVERTS][2])
+static int pointinrect(const double point[2], const double
coords[MAXVERTS][2])
{
double max[2], min[2];
if (coords[0][X] > coords[1][X]) {
max[0] = coords[0][X];
min[0] = coords[1][X];
- } else {
+ }
+ else {
max[0] = coords[1][X];
min[0] = coords[0][X];
}
@@ -170,33 +172,35 @@
if (coords[0][Y] > coords[1][Y]) {
max[1] = coords[0][Y];
min[1] = coords[1][Y];
- } else {
+ }
+ else {
max[1] = coords[1][Y];
min[1] = coords[0][Y];
}
return ((point[X] >= min[0] && point[X] <= max[0]) &&
- (point[Y] >= min[1] && point[Y] <= max[1]));
+ (point[Y] >= min[1] && point[Y] <= max[1]));
}
-int pointincircle(double point[2], double coords[MAXVERTS][2])
+static int pointincircle(const double point[2], const double
coords[MAXVERTS][2])
{
- int radius1, radius2;
+ double radius1, radius2;
radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
- + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
-
+ + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
+
radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
- + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
+ + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
return (radius2 <= radius1);
}
-int pointinpoly(double point[2], double pgon[MAXVERTS][2])
+static int pointinpoly(const double point[2], const double pgon[MAXVERTS][2])
{
int i, numverts, inside_flag, xflag0;
int crossings;
- double *p, *stop;
+ double *p;
+ const double *stop;
double tx, ty, y;
for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++);
@@ -211,54 +215,54 @@
p = (double *) pgon + 1;
if ((y >= ty) != (*p >= ty)) {
- if ((xflag0 = (pgon[numverts - 1][X] >= tx)) == (*(double *) pgon >=
tx)) {
- if (xflag0)
- crossings++;
- }
- else {
- crossings += (pgon[numverts - 1][X] - (y - ty) *
- (*(double *) pgon - pgon[numverts - 1][X]) /
- (*p - y)) >= tx;
- }
+ if ((xflag0 = (pgon[numverts - 1][X] >= tx)) == (*(double *) pgon >=
tx)) {
+ if (xflag0)
+ crossings++;
+ }
+ else {
+ crossings += (pgon[numverts - 1][X] - (y - ty) *
+ (*(double *) pgon - pgon[numverts - 1][X]) /
+ (*p - y)) >= tx;
+ }
}
stop = pgon[numverts];
for (y = *p, p += 2; p < stop; y = *p, p += 2) {
-
- if (y >= ty) {
-
- while ((p < stop) && (*p >= ty))
- p += 2;
-
- if (p >= stop)
- break;
- if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
-
- if (xflag0)
- crossings++;
- }
- else {
- crossings += (*(p - 3) - (*(p - 2) - ty) *
- (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
- }
- }
- else {
- while ((p < stop) && (*p < ty))
- p += 2;
- if (p >= stop)
- break;
+ if (y >= ty) {
- if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
- if (xflag0)
- crossings++;
- }
- else {
- crossings += (*(p - 3) - (*(p - 2) - ty) *
- (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
- }
- }
+ while ((p < stop) && (*p >= ty))
+ p += 2;
+
+ if (p >= stop)
+ break;
+ if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
+
+ if (xflag0)
+ crossings++;
+ }
+ else {
+ crossings += (*(p - 3) - (*(p - 2) - ty) *
+ (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
+ }
+ }
+ else {
+ while ((p < stop) && (*p < ty))
+ p += 2;
+
+ if (p >= stop)
+ break;
+
+ if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
+ if (xflag0)
+ crossings++;
+ }
+ else {
+ crossings += (*(p - 3) - (*(p - 2) - ty) *
+ (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
+ }
+ }
}
inside_flag = crossings & 0x01;
@@ -266,580 +270,607 @@
}
-int is_closer(double point[2], double coords[MAXVERTS][2], double *closest)
+static int is_closer(const double point[2], const double
coords[MAXVERTS][2], double *closest)
{
- double dist_squared =((point[X] - coords[0][X]) * (point[X] -
coords[0][X]))
- + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y]));
+ double dist_squared = ((point[X] - coords[0][X]) * (point[X] -
coords[0][X]))
+ + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y]));
+
+ if (point[X] < 0 || point[Y] < 0)
+ return (0); /* don't mess around with negative
coordinates */
- if (point[X] < 0 || point[Y] < 0 )
- return(0); /* don't mess around with negative coordinates */
+ if (*closest < 0 || dist_squared < *closest) {
+ *closest = dist_squared;
+ return (1); /* if this is the first point or is the
closest yet
+ set 'closest' equal to this distance^2 */
+ }
- if ( *closest < 0 || dist_squared < *closest ) {
- *closest = dist_squared;
- return(1); /* if this is the first point or is the closest yet
- set 'closest' equal to this distance^2 */
- }
-
- return(0); /* if it's not the first or closest */
+ return (0); /* if it's not the first or closest */
}
-double get_x_coord(char *args)
+static double get_x_coord(const char *args)
{
- char *endptr; /* we want it non-null */
- double x_coord = -1; /* -1 is returned if no coordinate is given */
+ char *endptr; /* we want it non-null */
+ double x_coord = -1; /* -1 is returned if no coordinate is given
*/
- if (args == NULL)
- return(-1); /* in case we aren't passed anything */
+ if (args == NULL)
+ return (-1); /* in case we aren't passed anything */
- while( *args && !isdigit(*args) && *args != ',')
- args++; /* jump to the first digit, but not past a comma or end */
+ while (*args && !isdigit(*args) && *args != ',')
+ args++; /* jump to the first digit, but not past a
comma or end */
- x_coord = strtod(args, &endptr);
+ x_coord = strtod(args, &endptr);
- if (endptr > args) /* if a conversion was made */
- return(x_coord);
+ if (endptr > args) /* if a conversion was made */
+ return (x_coord);
- return(-1); /* else if no conversion was made, or if no args was given */
+ return (-1); /* else if no conversion was made, or if no
args was given */
}
-double get_y_coord(char *args)
+static double get_y_coord(const char *args)
{
- char *endptr; /* we want it non-null */
- char *start_of_y = NULL;
- double y_coord = -1; /* -1 is returned on error */
+ char *endptr; /* we want it non-null */
+ char *start_of_y = NULL;
+ double y_coord = -1; /* -1 is returned on error */
- if (args == NULL)
- return(-1); /* in case we aren't passed anything */
+ if (args == NULL)
+ return (-1); /* in case we aren't passed anything */
- start_of_y = strchr(args, ','); /* the comma */
+ start_of_y = strchr(args, ','); /* the comma */
- if (start_of_y) {
-
- start_of_y++; /* start looking at the character after the comma */
+ if (start_of_y) {
- while( *start_of_y && !isdigit(*start_of_y))
- start_of_y++; /* jump to the first digit, but not past the end */
+ start_of_y++; /* start looking at the character after the
comma */
- y_coord = strtod(start_of_y, &endptr);
+ while (*start_of_y && !isdigit(*start_of_y))
+ start_of_y++; /* jump to the first digit, but not past the
end */
+
+ y_coord = strtod(start_of_y, &endptr);
+
+ if (endptr > start_of_y)
+ return (y_coord);
+ }
- if (endptr > start_of_y)
- return(y_coord);
- }
-
- return(-1); /* if no conversion was made, or no comma was found in args
*/
+ return (-1); /* if no conversion was made, or no comma
was found in args */
}
-
-int read_quoted(char *string, char *quoted_part)
-{
- char *starting_pos = string;
-
- while ( isspace(*string) )
- string++; /* go along string until non-whitespace */
- if ( *string == '"' ) { /* if that character is a double quote */
+/* See if string has a "quoted part", and if so set *quoted_part to
+ * the first character of the quoted part, then hammer a \0 onto the
+ * trailing quote, and set *string to point at the first character
+ * past the second quote.
+ *
+ * Otherwise set *quoted_part to NULL, and leave *string alone.
+ */
+static void read_quoted(char **string, char **quoted_part)
+{
+ char *strp = *string;
- string++; /* step over it */
+ /* assume there's no quoted part */
+ *quoted_part = NULL;
- while ( *string && *string != '"' ) {
- *quoted_part++ = *string++; /* copy the quoted portion */
- }
+ while (isspace(*strp))
+ strp++; /* go along string until non-whitespace
*/
+
+ if (*strp == '"') { /* if that character is a double quote
*/
+ strp++; /* step over it */
+ *quoted_part = strp; /* note where the quoted part begins */
+
+ while (*strp && *strp != '"') {
+ ++strp; /* skip the quoted portion */
+ }
- *quoted_part = '\0'; /* end the string with a SNUL */
-
- string++; /* step over the last double quote */
- }
+ *strp = '\0'; /* end the string with a NUL */
- return(string - starting_pos); /* return the total characters read */
+ strp++; /* step over the last double quote */
+ *string = strp;
+ }
}
/*
- * url needs to point to a string with at least SMALLBUF memory allocated
+ * returns the mapped URL or NULL.
*/
-void imap_url(request_rec *r, char *base, char *value, char *url)
+static char *imap_url(request_rec *r, const char *base, const char *value)
{
/* translates a value into a URL. */
- int slen, clen;
- char *string_pos = NULL;
- char *directory = NULL;
- char *referer = NULL;
- char my_base[SMALLBUF] = {'\0'};
-
- if ( ! strcasecmp(value, "map" ) || ! strcasecmp(value, "menu") ) {
- if (r->server->port == DEFAULT_PORT ) {
- ap_snprintf(url, SMALLBUF,
- "http://%s%s", r->server->server_hostname, r->uri);
+ int slen, clen;
+ char *string_pos = NULL;
+ const char *string_pos_const = NULL;
+ char *directory = NULL;
+ char *referer = NULL;
+ char *my_base;
+
+ if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
+ return construct_url(r->pool, r->uri, r->server);
+ }
+
+ if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
+ return pstrdup(r->pool, value); /* these are handled
elsewhere, so just copy them */
+ }
+
+ if (!strcasecmp(value, "referer")) {
+ referer = table_get(r->headers_in, "Referer");
+ if (referer && *referer) {
+ return pstrdup(r->pool, referer);
+ }
+ else {
+ /* XXX: This used to do *value = '\0'; ... which is totally bogus
+ * because it hammers the passed in value, which can be a string
constant,
+ * or part of a config, or whatever. Total garbage. This works
around
+ * that without changing the rest of this code much
+ */
+ value = ""; /* if 'referer' but no referring page, null the
value */
+ }
+ }
+
+ string_pos_const = value;
+ while (isalpha(*string_pos_const))
+ string_pos_const++; /* go along the URL from the map until a
non-letter */
+ if (*string_pos_const == ':') {
+ /* if letters and then a colon (like http:) */
+ /* it's an absolute URL, so use it! */
+ return pstrdup(r->pool, value);
+ }
+
+ if (!base || !*base) {
+ if (value && *value) {
+ return pstrdup(r->pool, value); /* no base: use what is given */
+ }
+ /* no base, no value: pick a simple default */
+ return construct_url(r->pool, "/", r->server);
+ }
+
+ /* must be a relative URL to be combined with base */
+ if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3) ||
!strcmp(value, ".."))) {
+ log_reason("invalid base directive in map file: %s", r->uri, r);
+ return NULL;
+ }
+ my_base = pstrdup(r->pool, base);
+ string_pos = my_base;
+ while (*string_pos) {
+ if (*string_pos == '/' && *(string_pos + 1) == '/') {
+ string_pos += 2; /* if there are two slashes, jump over them
*/
+ continue;
+ }
+ if (*string_pos == '/') { /* the first single slash */
+ if (value[0] == '/') {
+ *string_pos = '\0';
+ } /* if the URL from the map starts from root,
end the
+ base URL string at the first single slash
*/
+ else {
+ directory = string_pos; /* save the start of the
directory portion */
+
+ string_pos = strrchr(string_pos, '/'); /* now reuse
string_pos */
+ string_pos++; /* step over that last slash */
+ *string_pos = '\0';
+ } /* but if the map url is relative, leave the
+ slash on the base (if there is one) */
+ break;
+ }
+ string_pos++; /* until we get to the end of my_base
without finding
+ a slash by itself */
+ }
+
+ while (!strncmp(value, "../", 3) || !strcmp(value, "..")) {
+
+ if (directory && (slen = strlen(directory))) {
+
+ /* for each '..', knock a directory off the end
+ by ending the string right at the last slash.
+ But only consider the directory portion: don't eat
+ into the server name. And only try if a directory
+ portion was found */
+
+ clen = slen - 1;
+
+ while ((slen - clen) == 1) {
+
+ if ((string_pos = strrchr(directory, '/')))
+ *string_pos = '\0';
+ clen = strlen(directory);
+ if (clen == 0)
+ break;
+ }
+
+ value += 2; /* jump over the '..' that we found in the
value */
+ }
+ else if (directory) {
+ log_reason("invalid directory name in map file: %s", r->uri, r);
+ return NULL;
+ }
+
+ if (!strncmp(value, "/../", 4) || !strcmp(value, "/.."))
+ value++; /* step over the '/' if there are more '..'
to do.
+ this way, we leave the starting '/' on
value after
+ the last '..', but get rid of it
otherwise */
+
+ } /* by this point, value does not start with
'..' */
+
+ if (value && *value) {
+ return pstrcat(r->pool, my_base, value, NULL);
+ }
+ return my_base;
+}
+
+static int imap_reply(request_rec *r, char *redirect)
+{
+ if (!strcasecmp(redirect, "error")) {
+ return SERVER_ERROR; /* they actually requested an error! */
+ }
+ if (!strcasecmp(redirect, "nocontent")) {
+ return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
+ }
+ if (redirect && *redirect) {
+ table_set(r->headers_out, "Location", redirect);
+ return REDIRECT; /* must be a URL, so redirect to it */
+ }
+ return SERVER_ERROR;
+}
+
+static void menu_header(request_rec *r, char *menu)
+{
+ r->content_type = "text/html";
+ send_http_header(r);
+ hard_timeout("send menu", r); /* killed in menu_footer */
+
+ rvputs(r, "<html><head>\n<title>Menu for ", r->uri,
+ "</title>\n</head><body>\n", NULL);
+
+ if (!strcasecmp(menu, "formatted")) {
+ rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
}
- else {
- ap_snprintf(url, SMALLBUF, "http://%s:%d%s",
r->server->server_hostname,
- r->server->port, r->uri);
+
+ return;
+}
+
+static void menu_blank(request_rec *r, char *menu)
+{
+ if (!strcasecmp(menu, "formatted")) {
+ rputs("\n", r);
+ }
+ if (!strcasecmp(menu, "semiformatted")) {
+ rputs("<br>\n", r);
+ }
+ if (!strcasecmp(menu, "unformatted")) {
+ rputs("\n", r);
+ }
+ return;
+}
+
+static void menu_comment(request_rec *r, char *menu, char *comment)
+{
+ if (!strcasecmp(menu, "formatted")) {
+ rputs("\n", r); /* print just a newline if 'formatted' */
+ }
+ if (!strcasecmp(menu, "semiformatted") && *comment) {
+ rvputs(r, comment, "\n", NULL);
+ }
+ if (!strcasecmp(menu, "unformatted") && *comment) {
+ rvputs(r, comment, "\n", NULL);
}
- return;
- }
+ return; /* comments are ignored in the 'formatted'
form */
+}
- if ( ! strcasecmp(value, "nocontent") || ! strcasecmp(value, "error") ) {
- strncpy(url, value, SMALLBUF-1);
- url[SMALLBUF-1] = '\0';
- return; /* these are handled elsewhere, so just copy them */
- }
-
- if ( ! strcasecmp(value, "referer" ) ) {
- referer = table_get(r->headers_in, "Referer");
- if ( referer && *referer ) {
- strncpy(url, referer, SMALLBUF-1);
- url[SMALLBUF-1] = '\0';
- return;
+static void menu_default(request_rec *r, char *menu, char *href, char *text)
+{
+ if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
+ return; /* don't print such lines, these aren'te
really href's */
+ }
+ if (!strcasecmp(menu, "formatted")) {
+ rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
"</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "semiformatted")) {
+ rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
"</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "unformatted")) {
+ rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
}
- else {
- *value = '\0'; /* if 'referer' but no referring page, null the value
*/
- }
- }
-
- string_pos = value;
- while ( isalpha(*string_pos) )
- string_pos++; /* go along the URL from the map until a non-letter */
- if ( *string_pos == ':' ) {
- strncpy(url, value, SMALLBUF-1); /* if letters and then a colon
(like http:) */
- url[SMALLBUF-1] = '\0';
- return; /* it's an absolute URL, so use it! */
- }
-
- if ( ! base || ! *base ) {
- if ( value && *value ) {
- strncpy(url, value, SMALLBUF-1); /* no base: use what is given */
- url[SMALLBUF-1] = '\0';
- }
- else {
- if (r->server->port == DEFAULT_PORT ) {
- ap_snprintf(url, SMALLBUF, "http://%s/", r->server->server_hostname);
- }
- if (r->server->port != DEFAULT_PORT ) {
- ap_snprintf(url, SMALLBUF, "http://%s:%d/",
- r->server->server_hostname, r->server->port);
- } /* no base, no value: pick a simple default */
- }
- return;
- }
-
- strncpy(my_base, base, sizeof(my_base)-1); /* must be a relative URL to
be combined with base */
- my_base[sizeof(my_base)-1] = '\0';
- if (strchr(my_base, '/') == NULL && (!strncmp(value, "../", 3) ||
!strcmp(value, "..")) ) {
- url[0] = '\0';
- log_reason("invalid base directive in map file", r->uri, r);
return;
- }
- string_pos = my_base;
- while (*string_pos) {
- if (*string_pos == '/' && *(string_pos+1) == '/') {
- string_pos += 2; /* if there are two slashes, jump over them */
- continue;
- }
- if (*string_pos == '/') { /* the first single slash */
- if ( value[0] == '/' ) {
- *string_pos = '\0';
- } /* if the URL from the map starts from root, end the
- base URL string at the first single slash */
- else {
- directory = string_pos; /* save the start of the directory portion */
+}
+
+static void menu_directive(request_rec *r, char *menu, char *href, char
*text)
+{
+ if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
+ return; /* don't print such lines, as this isn't
really an href */
+ }
+ if (!strcasecmp(menu, "formatted")) {
+ rvputs(r, "<pre> <a href=\"", href, "\">", text,
"</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "semiformatted")) {
+ rvputs(r, "<pre> <a href=\"", href, "\">", text,
"</a></pre>\n",
+ NULL);
+ }
+ if (!strcasecmp(menu, "unformatted")) {
+ rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
+ }
+ return;
+}
+
+static void menu_footer(request_rec *r)
+{
+ rputs("\n\n</body>\n</html>\n", r); /* finish the menu */
+ kill_timeout(r);
+}
- string_pos = strrchr(string_pos, '/'); /* now reuse string_pos */
- string_pos++; /* step over that last slash */
- *string_pos = '\0';
- } /* but if the map url is relative, leave the
- slash on the base (if there is one) */
- break;
- }
- string_pos++; /* until we get to the end of my_base without finding
- a slash by itself */
- }
-
- while ( ! strncmp(value, "../", 3) || ! strcmp(value, "..") ) {
-
- if (directory && (slen = strlen (directory))) {
-
- /* for each '..', knock a directory off the end
- by ending the string right at the last slash.
- But only consider the directory portion: don't eat
- into the server name. And only try if a directory
- portion was found */
-
- clen = slen - 1;
-
- while ((slen - clen) == 1) {
-
- if ((string_pos = strrchr(directory, '/')))
- *string_pos = '\0';
- clen = strlen (directory);
- if (clen == 0) break;
- }
-
- value += 2; /* jump over the '..' that we found in the value */
- } else if (directory) {
- url[0] = '\0';
- log_reason("invalid directory name in map file", r->uri, r);
- return;
- }
-
- if (! strncmp(value, "/../", 4) || ! strcmp(value, "/..") )
-
- value++; /* step over the '/' if there are more '..' to do.
- this way, we leave the starting '/' on value after
- the last '..', but get rid of it otherwise */
-
- } /* by this point, value does not start with '..' */
-
- if ( value && *value ) {
- ap_snprintf(url, SMALLBUF, "%s%s", my_base, value);
- }
- else {
- ap_snprintf(url, SMALLBUF, "%s", my_base);
- }
- return;
-}
-
-int imap_reply(request_rec *r, char *redirect)
-{
- if ( ! strcasecmp(redirect, "error") ) {
- return SERVER_ERROR; /* they actually requested an error! */
- }
- if ( ! strcasecmp(redirect, "nocontent") ) {
- return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
- }
- if (redirect && *redirect ) {
- table_set(r->headers_out, "Location", redirect);
- return REDIRECT; /* must be a URL, so redirect to it */
- }
- return SERVER_ERROR;
-}
-
-void menu_header(request_rec *r, char *menu)
-{
- r->content_type = "text/html";
- send_http_header(r);
- hard_timeout("send menu", r); /* killed in menu_footer */
-
- rvputs(r, "<html><head>\n<title>Menu for ", r->uri,
- "</title>\n</head><body>\n", NULL);
-
- if (!strcasecmp(menu, "formatted")) {
- rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
- }
-
- return;
-}
-
-void menu_blank(request_rec *r, char *menu)
-{
- if (! strcasecmp(menu, "formatted") ) {
- rputs("\n", r);
- }
- if (! strcasecmp(menu, "semiformatted") ) {
- rputs("<br>\n", r);
- }
- if (! strcasecmp(menu, "unformatted") ) {
- rputs("\n", r);
- }
- return;
-}
-
-void menu_comment(request_rec *r, char *menu, char *comment)
-{
- if (! strcasecmp(menu, "formatted") ) {
- rputs("\n", r); /* print just a newline if 'formatted' */
- }
- if (! strcasecmp(menu, "semiformatted") && *comment ) {
- rvputs(r, comment, "\n", NULL);
- }
- if (! strcasecmp(menu, "unformatted") && *comment ) {
- rvputs(r, comment, "\n", NULL);
- }
- return; /* comments are ignored in the 'formatted' form */
-}
-
-void menu_default(request_rec *r, char *menu, char *href, char *text)
-{
- if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) {
- return; /* don't print such lines, these aren'te really href's */
- }
- if ( ! strcasecmp(menu, "formatted" ) ) {
- rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n",
- NULL);
- }
- if ( ! strcasecmp(menu, "semiformatted" ) ) {
- rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n",
- NULL);
- }
- if ( ! strcasecmp(menu, "unformatted" ) ) {
- rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
- }
- return;
-}
-
-void menu_directive(request_rec *r, char *menu, char *href, char *text)
-{
- if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) {
- return; /* don't print such lines, as this isn't really an href */
- }
- if ( ! strcasecmp(menu, "formatted" ) ) {
- rvputs(r, "<pre> <a href=\"", href, "\">", text, "</a></pre>\n",
- NULL);
- }
- if ( ! strcasecmp(menu, "semiformatted" ) ) {
- rvputs(r, "<pre> <a href=\"", href, "\">", text, "</a></pre>\n",
- NULL);
- }
- if ( ! strcasecmp(menu, "unformatted" ) ) {
- rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
- }
- return;
-}
-
-void menu_footer(request_rec *r)
-{
- rputs("\n\n</body>\n</html>\n", r); /* finish the menu */
- kill_timeout(r);
-}
-
-int imap_handler(request_rec *r)
-{
- char input[LARGEBUF] = {'\0'};
- /* size of input can not be lowered without changing hard-coded
- * checks
+static int imap_handler(request_rec *r)
+{
+ char input[MAX_STRING_LEN];
+ char *directive;
+ char *value;
+ char *href_text;
+ char *base;
+ char *redirect;
+ char *mapdflt;
+ char *closest = NULL;
+ double closest_yet = -1;
+
+ double testpoint[2];
+ double pointarray[MAXVERTS + 1][2];
+ int vertex;
+
+ char *string_pos;
+ int showmenu = 0;
+
+ imap_conf_rec *icr = get_module_config(r->per_dir_config, &imap_module);
+
+ char *imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT;
+ char *imap_default = icr->imap_default
+ ? icr->imap_default : IMAP_DEFAULT_DEFAULT;
+ char *imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT;
+
+ FILE *imap;
+
+ if (r->method_number != M_GET)
+ return DECLINED;
+
+ imap = pfopen(r->pool, r->filename, "r");
+
+ if (!imap)
+ return NOT_FOUND;
+
+ base = imap_url(r, NULL, imap_base); /* set base according to
default */
+ if (!base)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ mapdflt = imap_url(r, NULL, imap_default); /* and default to global
default */
+ if (!mapdflt)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ testpoint[X] = get_x_coord(r->args);
+ testpoint[Y] = get_y_coord(r->args);
+
+ if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
+ (testpoint[X] == 0 && testpoint[Y] == 0)) {
+ /* if either is -1 or if both are zero (new Lynx) */
+ /* we don't have valid coordinates */
+ testpoint[X] = -1;
+ testpoint[Y] = -1;
+ if (strncasecmp(imap_menu, "none", 2))
+ showmenu = 1; /* show the menu _unless_ ImapMenu is 'none'
or 'no' */
+ }
+
+ if (showmenu) { /* send start of imagemap menu if we're
going to */
+ menu_header(r, imap_menu);
+ }
+
+ while (!cfg_getline(input, sizeof(input), imap)) {
+ if (!input[0]) {
+ if (showmenu) {
+ menu_blank(r, imap_menu);
+ }
+ continue;
+ }
+
+ if (input[0] == '#') {
+ if (showmenu) {
+ menu_comment(r, imap_menu, input + 1);
+ }
+ continue;
+ } /* blank lines and comments are ignored if
we aren't printing a menu */
+
+ /* find the first two space delimited fields, recall that
+ * cfg_getline has removed leading/trailing whitespace and
+ * compressed the other whitespace down to one space a piece
+ *
+ * note that we're tokenizing as we go... if we were to use the
+ * getword() class of functions we would end up allocating extra
+ * memory for every line of the map file
*/
- char href_text[SMALLBUF] = {'\0'};
- char base[SMALLBUF] = {'\0'};
- char redirect[SMALLBUF] = {'\0'};
- char directive[SMALLBUF] = {'\0'};
- char value[SMALLBUF] = {'\0'};
- char mapdflt[SMALLBUF] = {'\0'};
- char closest[SMALLBUF] = {'\0'};
- double closest_yet = -1;
-
- double testpoint[2] = { -1,-1 };
- double pointarray[MAXVERTS + 1][2] = { {-1,-1} };
- int vertex = 0;
-
- char *string_pos = NULL;
- int chars_read = 0;
- int showmenu = 0;
-
- imap_conf_rec *icr = get_module_config(r->per_dir_config, &imap_module);
-
- char *imap_menu = icr->imap_menu ?
- icr->imap_menu : IMAP_MENU_DEFAULT;
- char *imap_default = icr->imap_default ?
- icr->imap_default : IMAP_DEFAULT_DEFAULT;
- char *imap_base = icr->imap_base ?
- icr->imap_base : IMAP_BASE_DEFAULT;
-
- FILE *imap;
-
- if (r->method_number != M_GET) return DECLINED;
-
- imap = pfopen(r->pool, r->filename, "r");
-
- if ( ! imap )
- return NOT_FOUND;
-
- imap_url(r, NULL, imap_base, base); /* set base according to default
*/
- imap_url(r, NULL, imap_default, mapdflt); /* and default to global default
*/
-
- testpoint[X] = get_x_coord(r->args);
- testpoint[Y] = get_y_coord(r->args);
-
- if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
- (testpoint[X] == 0 && testpoint[Y] == 0) ) {
- /* if either is -1 or if both are zero (new Lynx) */
- /* we don't have valid coordinates */
- testpoint[X] = -1;
- testpoint[Y] = -1;
- if ( strncasecmp(imap_menu, "none", 2) )
- showmenu = 1; /* show the menu _unless_ ImapMenu is 'none' or 'no'
*/
- }
-
- if (showmenu) { /* send start of imagemap menu if we're going to */
- menu_header(r, imap_menu);
- }
-
- while (!cfg_getline(input, LARGEBUF, imap)) {
- string_pos = input; /* always start at the beginning of line */
-
- directive[0] = '\0';
- value[0] = '\0';
- href_text[0] = '\0';
- redirect[0] = '\0';
- chars_read = 0; /* clear these before using */
-
- if ( ! input[0] ) {
- if (showmenu) {
- menu_blank(r, imap_menu);
- }
- continue;
- }
-
- if ( input[0] == '#' ) {
- if (showmenu) {
- menu_comment(r, imap_menu, input + 1);
- }
- continue;
- } /* blank lines and comments are ignored if we aren't printing a menu */
-
-
- if (sscanf(input, "%255s %255s", directive, value) != 2) {
- continue; /* make sure we read two fields */
- }
- /* Now skip what we just read... we can't use ANSIism %n */
- while (!(isspace(*string_pos))) /* past directive */
- string_pos++;
- while (isspace(*string_pos)) /* and whitespace */
- string_pos++;
- while (!(isspace(*string_pos))) /* and value... have to watch it */
- string_pos++; /* can have punctuation and stuff */
-
- if ( ! strncasecmp(directive, "base", 4 ) ) { /* base, base_uri */
- imap_url(r, NULL, value, base);
- continue; /* base is never printed to a menu */
- }
-
- chars_read = read_quoted(string_pos, href_text);
- string_pos += chars_read; /* read the quoted href text if present */
-
- if ( ! strcasecmp(directive, "default" ) ) { /* default */
- imap_url(r, NULL, value, mapdflt);
- if (showmenu) { /* print the default if there's a menu */
- if (! *href_text) { /* if we didn't find a "href text" */
- strncpy(href_text, mapdflt, sizeof(href_text)-1); /* use the href
itself as text */
- href_text[sizeof(href_text)-1] = '\0';
+ string_pos = input;
+ if (!*string_pos) /* need at least two fields */
+ goto need_2_fields;
+
+ directive = string_pos;
+ while (*string_pos && *string_pos != ' ') /* past directive */
+ ++string_pos;
+ if (!*string_pos) /* need at least two fields */
+ goto need_2_fields;
+ *string_pos++ = '\0';
+
+ if (!*string_pos) /* need at least two fields */
+ goto need_2_fields;
+ value = string_pos;
+ while (*string_pos && *string_pos != ' ') /* past value */
+ ++string_pos;
+ if (*string_pos == ' ') {
+ *string_pos++ = '\0';
+ }
+ else {
+ /* end of input, don't advance past it */
+ *string_pos = '\0';
}
- imap_url(r, base, mapdflt, redirect);
- menu_default(r, imap_menu, redirect, href_text);
- }
- continue;
- }
-
- vertex = 0;
- while ( vertex < MAXVERTS &&
- sscanf(string_pos, "%lf, %lf",
- &pointarray[vertex][X], &pointarray[vertex][Y]) == 2)
- {
- /* Now skip what we just read... we can't use ANSIism %n */
- while(isspace(*string_pos)) /* past whitespace */
- string_pos++;
- while(isdigit(*string_pos)) /* and the 1st number */
- string_pos++;
- string_pos++; /* skip the ',' */
- while(isspace(*string_pos)) /* past any more whitespace */
- string_pos++;
- while(isdigit(*string_pos)) /* 2nd number */
- string_pos++;
- vertex++;
- } /* so long as there are more vertices to read, and
- we have room, read them in. We start where we left
- off of the last sscanf, not at the beginning.*/
-
- pointarray[vertex][X] = -1; /* signals the end of vertices */
+ if (!strncasecmp(directive, "base", 4)) { /* base, base_uri */
+ base = imap_url(r, NULL, value);
+ if (!base)
+ goto menu_bail;
+ continue; /* base is never printed to a menu */
+ }
+
+ read_quoted(&string_pos, &href_text);
+
+ if (!strcasecmp(directive, "default")) { /* default */
+ mapdflt = imap_url(r, NULL, value);
+ if (!mapdflt)
+ goto menu_bail;
+ if (showmenu) { /* print the default if there's a menu */
+ redirect = imap_url(r, base, mapdflt);
+ if (!redirect)
+ goto menu_bail;
+ menu_default(r, imap_menu, redirect, href_text ? href_text :
mapdflt);
+ }
+ continue;
+ }
+
+ vertex = 0;
+ while (vertex < MAXVERTS &&
+ sscanf(string_pos, "%lf%*[, ]%lf",
+ &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) {
+ /* Now skip what we just read... we can't use ANSIism %n */
+ while (isspace(*string_pos)) /* past whitespace */
+ string_pos++;
+ while (isdigit(*string_pos)) /* and the 1st number */
+ string_pos++;
+ string_pos++; /* skip the ',' */
+ while (isspace(*string_pos)) /* past any more whitespace
*/
+ string_pos++;
+ while (isdigit(*string_pos)) /* 2nd number */
+ string_pos++;
+ vertex++;
+ } /* so long as there are more vertices to
read, and
+ we have room, read them in. We start
where we left
+ off of the last sscanf, not at the
beginning. */
+
+ pointarray[vertex][X] = -1; /* signals the end of vertices */
+
+ if (showmenu) {
+ if (!href_text) {
+ read_quoted(&string_pos, &href_text); /* href text
could be here instead */
+ }
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ goto menu_bail;
+ menu_directive(r, imap_menu, redirect, href_text ? href_text :
value);
+ continue;
+ }
+ /* note that we don't make it past here if we are making a menu */
+
+ if (testpoint[X] == -1 || pointarray[0][X] == -1)
+ continue; /* don't try the following tests if
testpoints
+ are invalid, or if there are no
coordinates */
+
+ if (!strcasecmp(directive, "poly")) { /* poly */
+
+ if (pointinpoly(testpoint, pointarray)) {
+ pfclose(r->pool, imap);
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if (!strcasecmp(directive, "circle")) { /* circle */
+
+ if (pointincircle(testpoint, pointarray)) {
+ pfclose(r->pool, imap);
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if (!strcasecmp(directive, "rect")) { /* rect */
+
+ if (pointinrect(testpoint, pointarray)) {
+ pfclose(r->pool, imap);
+ redirect = imap_url(r, base, value);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if (!strcasecmp(directive, "point")) { /* point */
+
+ if (is_closer(testpoint, pointarray, &closest_yet)) {
+ closest = pstrdup(r->pool, value);
+ }
+
+ continue;
+ } /* move on to next line whether it's closest
or not */
+
+ } /* nothing matched, so we get another line!
*/
+
+ pfclose(r->pool, imap); /* we are done with the map file, so close
it */
+
+ if (showmenu) {
+ menu_footer(r); /* finish the menu and we are done */
+ return OK;
+ }
+
+ if (closest) { /* if a 'point' directive has been seen */
+ redirect = imap_url(r, base, closest);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+
+ if (mapdflt) { /* a default should be defined, even if only
'nocontent' */
+ redirect = imap_url(r, base, mapdflt);
+ if (!redirect)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return (imap_reply(r, redirect));
+ }
+
+ return SERVER_ERROR; /* If we make it this far, we failed. They
lose! */
+
+need_2_fields:
+ log_reason("all map file lines require at least two fields", r->uri, r);
+ /* fall through */
+menu_bail:
+ pfclose(r->pool, imap);
if (showmenu) {
- read_quoted(string_pos, href_text); /* href text could be here instead
*/
- if (! *href_text) { /* if we didn't find a "href text" */
- strncpy(href_text, value, sizeof(href_text)-1); /* use the href itself
in the menu */
- href_text[sizeof(href_text)-1] = '\0';
- }
- imap_url(r, base, value, redirect);
- menu_directive(r, imap_menu, redirect, href_text);
- continue;
- }
- /* note that we don't make it past here if we are making a menu */
-
- if (testpoint[X] == -1 || pointarray[0][X] == -1 )
- continue; /* don't try the following tests if testpoints
- are invalid, or if there are no coordinates */
-
- if ( ! strcasecmp(directive, "poly" ) ) { /* poly */
-
- if (pointinpoly (testpoint, pointarray) ) {
- pfclose(r->pool, imap);
- imap_url(r, base, value, redirect);
- return (imap_reply(r, redirect));
- }
- continue;
- }
-
- if ( ! strcasecmp(directive, "circle" ) ) { /* circle */
-
- if (pointincircle (testpoint, pointarray) ) {
- pfclose(r->pool, imap);
- imap_url(r, base, value, redirect);
- return (imap_reply(r, redirect));
- }
- continue;
- }
-
- if ( ! strcasecmp(directive, "rect" ) ) { /* rect */
-
- if (pointinrect (testpoint, pointarray) ) {
- pfclose(r->pool, imap);
- imap_url(r, base, value, redirect);
- return (imap_reply(r, redirect));
- }
- continue;
- }
-
- if ( ! strcasecmp(directive, "point" ) ) { /* point */
-
- if (is_closer(testpoint, pointarray, &closest_yet) ) {
- strncpy(closest, value, sizeof(closest)-1); /* if the closest point
yet save it */
- closest[sizeof(closest)-1] = '\0';
- }
-
- continue;
- } /* move on to next line whether it's closest or not */
-
- } /* nothing matched, so we get another line! */
-
- pfclose(r->pool, imap); /* we are done with the map file, so close it */
-
- if (showmenu) {
- menu_footer(r); /* finish the menu and we are done */
- return OK;
- }
-
- if (*closest) { /* if a 'point' directive has been seen */
- imap_url(r, base, closest, redirect);
- return (imap_reply(r, redirect));
- }
-
- if (*mapdflt ) { /* a default should be defined, even if only
'nocontent'*/
- imap_url(r, base, mapdflt, redirect);
- return(imap_reply(r, redirect));
- }
-
- return SERVER_ERROR; /* If we make it this far, we failed. They lose! */
-}
-
-
-handler_rec imap_handlers[] = {
-{ IMAP_MAGIC_TYPE, imap_handler },
-{ "imap-file", imap_handler },
-{ NULL }
+ /* There's not much else we can do ... we've already sent the headers
+ * to the client.
+ */
+ rputs("\n\n[an internal server error occured]\n", r);
+ menu_footer(r);
+ return OK;
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static handler_rec imap_handlers[] =
+{
+ {IMAP_MAGIC_TYPE, imap_handler},
+ {"imap-file", imap_handler},
+ {NULL}
};
-module imap_module = {
- STANDARD_MODULE_STUFF,
- NULL, /* initializer */
- create_imap_dir_config, /* dir config creater */
- merge_imap_dir_configs, /* dir merger --- default is to override */
- NULL, /* server config */
- NULL, /* merge server config */
- imap_cmds, /* command table */
- imap_handlers, /* handlers */
- NULL, /* filename translation */
- NULL, /* check_user_id */
- NULL, /* check auth */
- NULL, /* check access */
- NULL, /* type_checker */
- NULL, /* fixups */
- NULL, /* logger */
- NULL /* header parser */
+module imap_module =
+{
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_imap_dir_config, /* dir config creater */
+ merge_imap_dir_configs, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ imap_cmds, /* command table */
+ imap_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL, /* logger */
+ NULL /* header parser */
};