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 */ };