dgaudet     98/01/07 14:23:34

  Modified:    src/modules/standard mod_imap.c
  Log:
  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
  
  Reviewed by:  Jim Jagielski, Martin Kraemer
  
  Revision  Changes    Path
  1.37      +165 -153  apachen/src/modules/standard/mod_imap.c
  
  Index: mod_imap.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/modules/standard/mod_imap.c,v
  retrieving revision 1.36
  retrieving revision 1.37
  diff -u -r1.36 -r1.37
  --- mod_imap.c        1998/01/07 16:46:50     1.36
  +++ mod_imap.c        1998/01/07 22:23:33     1.37
  @@ -97,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
  @@ -159,7 +157,7 @@
       {NULL}
   };
   
  -static 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]) {
  @@ -184,7 +182,7 @@
               (point[Y] >= min[1] && point[Y] <= max[1]));
   }
   
  -static int pointincircle(double point[2], double coords[MAXVERTS][2])
  +static int pointincircle(const double point[2], const double 
coords[MAXVERTS][2])
   {
       double radius1, radius2;
   
  @@ -197,11 +195,12 @@
       return (radius2 <= radius1);
   }
   
  -static 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++);
  @@ -271,7 +270,7 @@
   }
   
   
  -static 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]));
  @@ -289,7 +288,7 @@
   
   }
   
  -static 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 
*/
  @@ -308,7 +307,7 @@
       return (-1);                /* else if no conversion was made, or if no 
args was given */
   }
   
  -static 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;
  @@ -336,107 +335,98 @@
   }
   
   
  -static int read_quoted(char *string, char *quoted_part)
  +/* 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 *starting_pos = string;
  +    char *strp = *string;
   
  -    while (isspace(*string))
  -        string++;               /* go along string until non-whitespace */
  +    /* assume there's no quoted part */
  +    *quoted_part = NULL;
   
  -    if (*string == '"') {       /* if that character is a double quote */
  +    while (isspace(*strp))
  +        strp++;                      /* go along string until non-whitespace 
*/
   
  -        string++;               /* step over it */
  +    if (*strp == '"') {              /* if that character is a double quote 
*/
  +        strp++;                      /* step over it */
  +     *quoted_part = strp;    /* note where the quoted part begins */
   
  -        while (*string && *string != '"') {
  -            *quoted_part++ = *string++;         /* copy the quoted portion */
  +        while (*strp && *strp != '"') {
  +         ++strp;             /* skip the quoted portion */
           }
   
  -        *quoted_part = '\0';    /* end the string with a SNUL */
  +        *strp = '\0';        /* end the string with a NUL */
   
  -        string++;               /* step over the last double quote */
  +        strp++;                      /* step over the last double quote */
  +     *string = strp;
       }
  -
  -    return (string - starting_pos);     /* return the total characters read 
*/
   }
   
   /*
  - * url needs to point to a string with at least SMALLBUF memory allocated
  + * returns the mapped URL or NULL.
    */
  -static 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;
  +    const char *string_pos_const = NULL;
       char *directory = NULL;
       char *referer = NULL;
  -    char my_base[SMALLBUF] =
  -    {'\0'};
  +    char *my_base;
   
       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);
  -        }
  -        else {
  -            ap_snprintf(url, SMALLBUF, "http://%s:%d%s";, 
r->server->server_hostname,
  -                        r->server->port, r->uri);
  -        }
  -        return;
  +     return construct_url(r->pool, r->uri, r->server);
       }
   
       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 */
  +        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) {
  -            strncpy(url, referer, SMALLBUF - 1);
  -            url[SMALLBUF - 1] = '\0';
  -            return;
  +         return pstrdup(r->pool, referer);
           }
           else {
  -            *value = '\0';      /* if 'referer' but no referring page, null 
the value */
  +         /* 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 = 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! */
  +    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) {
  -            strncpy(url, value, SMALLBUF - 1);  /* no base: use what is 
given */
  -            url[SMALLBUF - 1] = '\0';
  +         return pstrdup(r->pool, value); /* no base: use what is given */
           }
  -        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;
  +     /* 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 */
  -    strncpy(my_base, base, sizeof(my_base) - 1);
  -    my_base[sizeof(my_base) - 1] = '\0';
  -    if (strchr(my_base, '/') == NULL && (!strncmp(value, "../", 3) || 
!strcmp(value, ".."))) {
  -        url[0] = '\0';
  +    if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3) || 
!strcmp(value, ".."))) {
           aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
                       "invalid base directive in map file: %s", r->uri);
  -        return;
  +        return NULL;
       }
  +    my_base = pstrdup(r->pool, base);
       string_pos = my_base;
       while (*string_pos) {
           if (*string_pos == '/' && *(string_pos + 1) == '/') {
  @@ -486,10 +476,9 @@
               value += 2;         /* jump over the '..' that we found in the 
value */
           }
           else if (directory) {
  -            url[0] = '\0';
               aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
                           "invalid directory name in map file: %s", r->uri);
  -            return;
  +            return NULL;
           }
   
           if (!strncmp(value, "/../", 4) || !strcmp(value, "/.."))
  @@ -500,12 +489,9 @@
       }                           /* 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 pstrcat(r->pool, my_base, value, NULL);
       }
  -    return;
  +    return my_base;
   }
   
   static int imap_reply(request_rec *r, char *redirect)
  @@ -613,46 +599,29 @@
   
   static int imap_handler(request_rec *r)
   {
  -    char input[LARGEBUF] =
  -    {'\0'};
  -    /* size of input can not be lowered without changing hard-coded
  -     * checks
  -     */
  -    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'};
  +    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] =
  -    {-1, -1};
  -    double pointarray[MAXVERTS + 1][2] =
  -    {
  -        {-1, -1}};
  -    int vertex = 0;
  +    double testpoint[2];
  +    double pointarray[MAXVERTS + 1][2];
  +    int vertex;
   
  -    char *string_pos = NULL;
  -    int chars_read = 0;
  +    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;
  +    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;
   
       configfile_t *imap; 
   
  @@ -664,8 +633,12 @@
       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 */
  +    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);
  @@ -684,15 +657,7 @@
           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 */
  -
  +    while (!cfg_getline(input, sizeof(input), imap)) {
           if (!input[0]) {
               if (showmenu) {
                   menu_blank(r, imap_menu);
  @@ -707,35 +672,56 @@
               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 */
  +     /* 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
  +      */
  +        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';
  +     }
   
           if (!strncasecmp(directive, "base", 4)) {       /* base, base_uri */
  -            imap_url(r, NULL, value, base);
  +            base = imap_url(r, NULL, value);
  +         if (!base)
  +             goto menu_bail;
               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 */
  +        read_quoted(&string_pos, &href_text);
   
           if (!strcasecmp(directive, "default")) {        /* default */
  -            imap_url(r, NULL, value, mapdflt);
  +            mapdflt = imap_url(r, NULL, value);
  +         if (!mapdflt)
  +             goto menu_bail;
               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';
  -                }
  -                imap_url(r, base, mapdflt, redirect);
  -                menu_default(r, imap_menu, redirect, href_text);
  +                redirect = imap_url(r, base, mapdflt);
  +             if (!redirect)
  +                 goto menu_bail;
  +                menu_default(r, imap_menu, redirect, href_text ? href_text : 
mapdflt);
               }
               continue;
           }
  @@ -762,13 +748,13 @@
           pointarray[vertex][X] = -1;     /* signals the end of vertices */
   
           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);
  +         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 */
  @@ -781,7 +767,9 @@
   
               if (pointinpoly(testpoint, pointarray)) {
                cfg_closefile(imap);
  -                imap_url(r, base, value, redirect);
  +                redirect = imap_url(r, base, value);
  +             if (!redirect)
  +                 return HTTP_INTERNAL_SERVER_ERROR;
                   return (imap_reply(r, redirect));
               }
               continue;
  @@ -791,7 +779,9 @@
   
               if (pointincircle(testpoint, pointarray)) {
                cfg_closefile(imap);
  -                imap_url(r, base, value, redirect);
  +                redirect = imap_url(r, base, value);
  +             if (!redirect)
  +                 return HTTP_INTERNAL_SERVER_ERROR;
                   return (imap_reply(r, redirect));
               }
               continue;
  @@ -801,7 +791,9 @@
   
               if (pointinrect(testpoint, pointarray)) {
                cfg_closefile(imap);
  -                imap_url(r, base, value, redirect);
  +                redirect = imap_url(r, base, value);
  +             if (!redirect)
  +                 return HTTP_INTERNAL_SERVER_ERROR;
                   return (imap_reply(r, redirect));
               }
               continue;
  @@ -810,8 +802,7 @@
           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';
  +             closest = pstrdup(r->pool, value);
               }
   
               continue;
  @@ -826,17 +817,38 @@
           return OK;
       }
   
  -    if (*closest) {             /* if a 'point' directive has been seen */
  -        imap_url(r, base, closest, redirect);
  +    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' */
  -        imap_url(r, base, mapdflt, 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:
  +    aplog_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
  +             "map file %s, line %d syntax error: requires at least two 
fields",
  +             r->uri, imap->line_number);
  +    /* fall through */
  +menu_bail:
  +    cfg_closefile(imap);
  +    if (showmenu) {
  +     /* 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;
   }
   
   
  
  
  

Reply via email to