home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 25 / CDROM25.iso / Share / linux / apache / contrib / modules / mod_phantomimap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-11  |  31.4 KB  |  1,048 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1997 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission.
  24.  *
  25.  * 5. Redistributions of any form whatsoever must retain the following
  26.  *    acknowledgment:
  27.  *    "This product includes software developed by the Apache Group
  28.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  29.  *
  30.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  31.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  32.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  33.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  34.  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  38.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  41.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  * ====================================================================
  43.  *
  44.  * This software consists of voluntary contributions made by many
  45.  * individuals on behalf of the Apache Group and was originally based
  46.  * on public domain software written at the National Center for
  47.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  48.  * For more information on the Apache Group and the Apache HTTP server
  49.  * project, please see <http://www.apache.org/>.
  50.  *
  51.  */
  52.  
  53. /*
  54.  * This imagemap module started as a port of the original imagemap.c
  55.  * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
  56.  * This version includes the mapping algorithms found in version 1.3
  57.  * of imagemap.c.
  58.  *
  59.  * Contributors to this code include:
  60.  *
  61.  * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
  62.  *
  63.  * Eric Haines, erich@eye.com
  64.  * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
  65.  *
  66.  * Randy Terbush, randy@zyzzyva.com
  67.  * port to Apache module format, "base_uri" and support for relative URLs
  68.  * 
  69.  * James H. Cloos, Jr., cloos@jhcloos.com
  70.  * Added point datatype, using code in NCSA's version 1.8 imagemap.c
  71.  * program, as distributed with version 1.4.1 of their server.
  72.  * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
  73.  *
  74.  * Nathan Kurz, nate@tripod.com
  75.  * Rewrite/reorganization.  New handling of default, base and relative URLs.  
  76.  * New Configuration directives:
  77.  *    ImapMenu {none, formatted, semiformatted, unformatted}
  78.  *    ImapDefault {error, nocontent, referer, menu, URL}
  79.  *    ImapBase {map, referer, URL}
  80.  * Support for creating non-graphical menu added.  (backwards compatible):
  81.  *    Old:  directive URL [x,y ...]
  82.  *    New:  directive URL "Menu text" [x,y ...]
  83.  *     or:  directive URL x,y ... "Menu text"
  84.  * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
  85.  *
  86.  * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
  87.  *
  88.  * Tim Baverstock, warwick@mmm.co.uk, Interpreted <AREA> tags to .map format.
  89.  *  Added phantom .map file support and <MAP NAME=""> support.
  90.  *  Refer to index.html.map to read index.html's first <MAP></MAP> element.
  91.  *  Refer to index.html.something.map to read <MAP NAME="something"></MAP>
  92.  */
  93.  
  94. #include "httpd.h"
  95. #include "http_config.h"
  96. #include "http_request.h"
  97. #include "http_core.h"
  98. #include "http_protocol.h"
  99. #include "http_main.h"
  100. #include "http_log.h"
  101. #include "util_script.h"
  102.  
  103. #define IMAP_MAGIC_TYPE "application/x-httpd-imap"
  104. #define LARGEBUF 500
  105. #define SMALLBUF 256
  106. #define MAXVERTS 100
  107. #define X 0
  108. #define Y 1
  109.  
  110. #define IMAP_MENU_DEFAULT "formatted"
  111. #define IMAP_DEFAULT_DEFAULT "nocontent"
  112. #define IMAP_BASE_DEFAULT "map"
  113.  
  114. #ifdef SUNOS4
  115. double strtod();   /* SunOS needed this */
  116. #endif
  117.  
  118. module imap_module;
  119.  
  120. typedef struct { 
  121.   char *imap_menu;
  122.   char *imap_default;
  123.   char *imap_base;
  124. } imap_conf_rec;
  125.  
  126. void *create_imap_dir_config (pool *p, char *dummy) { 
  127.   imap_conf_rec *icr = 
  128.     (imap_conf_rec *)palloc(p, sizeof(imap_conf_rec));
  129.  
  130.   icr->imap_menu = NULL;
  131.   icr->imap_default = NULL;
  132.   icr->imap_base = NULL;
  133.  
  134.   return icr;
  135. }
  136.  
  137. void *merge_imap_dir_configs (pool *p, void *basev, void *addv)
  138. {
  139.   imap_conf_rec *new=(imap_conf_rec *)pcalloc (p, sizeof(imap_conf_rec));
  140.   imap_conf_rec *base = (imap_conf_rec *)basev;
  141.   imap_conf_rec *add = (imap_conf_rec *)addv;
  142.  
  143.   new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
  144.   new->imap_default=add->imap_default ? add->imap_default : base->imap_default;
  145.   new->imap_base =add-> imap_base ? add->imap_base : base->imap_base;
  146.  
  147.   return new;
  148. }
  149.  
  150.  
  151. command_rec imap_cmds[] = {
  152. { "ImapMenu", set_string_slot, 
  153.     (void*)XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1,
  154.     "the type of menu generated: none, formatted, semiformatted, unformatted"},
  155. { "ImapDefault", set_string_slot, 
  156.     (void*)XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1,
  157.     "the action taken if no match: error, nocontent, referer, menu, URL" },
  158. { "ImapBase", set_string_slot, 
  159.     (void*)XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1,
  160.     "the base for all URL's: map, referer, URL (or start of)" },
  161. { NULL }
  162. };
  163.  
  164. int pointinrect(double point[2], double coords[MAXVERTS][2])
  165. {
  166.     double max[2], min[2];
  167.     if (coords[0][X] > coords[1][X]) {
  168.         max[0] = coords[0][X];
  169.         min[0] = coords[1][X];
  170.     } else {
  171.         max[0] = coords[1][X];
  172.         min[0] = coords[0][X];
  173.     }
  174.  
  175.     if (coords[0][Y] > coords[1][Y]) {
  176.         max[1] = coords[0][Y];
  177.         min[1] = coords[1][Y];
  178.     } else {
  179.         max[1] = coords[1][Y];
  180.         min[1] = coords[0][Y];
  181.     }
  182.  
  183.     return ((point[X] >= min[0] && point[X] <= max[0]) &&
  184.         (point[Y] >= min[1] && point[Y] <= max[1]));
  185. }
  186.  
  187. int pointincircle(double point[2], double coords[MAXVERTS][2])
  188. {
  189.     int radius1, radius2;
  190.  
  191.     radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
  192.     + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
  193.     
  194.     radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
  195.     + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
  196.  
  197.     return (radius2 <= radius1);
  198. }
  199.  
  200. int pointinpoly(double point[2], double pgon[MAXVERTS][2])
  201. {
  202.     int i, numverts, inside_flag, xflag0;
  203.     int crossings;
  204.     double *p, *stop;
  205.     double tx, ty, y;
  206.  
  207.     for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++);
  208.  
  209.     numverts = i;
  210.     crossings = 0;
  211.  
  212.     tx = point[X];
  213.     ty = point[Y];
  214.     y = pgon[numverts - 1][Y];
  215.  
  216.     p = (double *) pgon + 1;
  217.     if ((y >= ty) != (*p >= ty)) {
  218.  
  219.     if ((xflag0 = (pgon[numverts - 1][X] >= tx)) == (*(double *) pgon >= tx)) {
  220.         if (xflag0)
  221.         crossings++;
  222.     }
  223.     else {
  224.         crossings += (pgon[numverts - 1][X] - (y - ty) *
  225.               (*(double *) pgon - pgon[numverts - 1][X]) /
  226.               (*p - y)) >= tx;
  227.     }
  228.     }
  229.  
  230.     stop = pgon[numverts];
  231.  
  232.     for (y = *p, p += 2; p < stop; y = *p, p += 2) {
  233.     
  234.     if (y >= ty) {
  235.         
  236.         while ((p < stop) && (*p >= ty))
  237.         p += 2;
  238.         
  239.         if (p >= stop)
  240.         break;
  241.         if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
  242.         
  243.         if (xflag0)
  244.             crossings++;
  245.         }
  246.         else {
  247.         crossings += (*(p - 3) - (*(p - 2) - ty) *
  248.                   (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
  249.         }
  250.     }
  251.     else {
  252.         while ((p < stop) && (*p < ty))
  253.         p += 2;
  254.  
  255.         if (p >= stop)
  256.         break;
  257.  
  258.         if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
  259.         if (xflag0)
  260.             crossings++;
  261.         }
  262.         else {
  263.         crossings += (*(p - 3) - (*(p - 2) - ty) *
  264.                   (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
  265.         }
  266.     }
  267.     }
  268.  
  269.     inside_flag = crossings & 0x01;
  270.     return (inside_flag);
  271. }
  272.  
  273.  
  274. int is_closer(double point[2], double coords[MAXVERTS][2], double *closest)
  275. {
  276.   double dist_squared =((point[X] - coords[0][X]) * (point[X] - coords[0][X]))
  277.          + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y]));
  278.  
  279.   if (point[X] < 0 || point[Y] < 0 ) 
  280.     return(0);          /* don't mess around with negative coordinates */
  281.  
  282.   if ( *closest < 0 || dist_squared < *closest ) {
  283.     *closest = dist_squared;
  284.     return(1);         /* if this is the first point or is the closest yet
  285.               set 'closest' equal to this distance^2 */
  286.   }
  287.   
  288.   return(0);           /* if it's not the first or closest */
  289.  
  290. }
  291.  
  292. double get_x_coord(char *args) 
  293. {
  294.   char *endptr;           /* we want it non-null */
  295.   double x_coord = -1;    /* -1 is returned if no coordinate is given */
  296.  
  297.   if (args == NULL)
  298.     return(-1);           /* in case we aren't passed anything */
  299.  
  300.   while( *args && !isdigit(*args) && *args != ',') 
  301.     args++;   /* jump to the first digit, but not past a comma or end */
  302.  
  303.   x_coord = strtod(args, &endptr);
  304.  
  305.   if (endptr > args)   /* if a conversion was made */
  306.     return(x_coord); 
  307.  
  308.   return(-1);  /* else if no conversion was made, or if no args was given */
  309. }
  310.  
  311. double get_y_coord(char *args) 
  312. {
  313.   char *endptr;        /* we want it non-null */
  314.   char *start_of_y = NULL;
  315.   double y_coord = -1;    /* -1 is returned on error */
  316.  
  317.   if (args == NULL)
  318.     return(-1);           /* in case we aren't passed anything */
  319.  
  320.   start_of_y = strchr(args, ',');  /* the comma */
  321.  
  322.   if (start_of_y) {
  323.     
  324.     start_of_y++;    /* start looking at the character after the comma */
  325.  
  326.     while( *start_of_y && !isdigit(*start_of_y))  
  327.       start_of_y++;  /* jump to the first digit, but not past the end */
  328.  
  329.     y_coord = strtod(start_of_y, &endptr);
  330.  
  331.     if (endptr > start_of_y) 
  332.       return(y_coord); 
  333.   }
  334.   
  335.   return(-1);   /* if no conversion was made, or no comma was found in args */
  336. }
  337.   
  338.  
  339. int read_quoted(char *string, char *quoted_part)
  340.   char *starting_pos = string;
  341.   
  342.   while ( isspace(*string) )
  343.     string++;    /* go along string until non-whitespace */
  344.  
  345.   if ( *string == '"' ) { /* if that character is a double quote */
  346.  
  347.     string++;  /* step over it */
  348.  
  349.     while ( *string && *string != '"' ) {
  350.       *quoted_part++ = *string++;  /* copy the quoted portion */
  351.     }
  352.  
  353.     *quoted_part = '\0';  /* end the string with a SNUL */
  354.     
  355.     string++;  /* step over the last double quote */
  356.   }
  357.  
  358.   return(string - starting_pos); /* return the total characters read */
  359. }
  360.  
  361. /*
  362.  * url needs to point to a string with at least SMALLBUF memory allocated
  363.  */
  364. void imap_url(request_rec *r, char *base, char *value, char *url) 
  365. {
  366. /* translates a value into a URL. */
  367.   int slen, clen;
  368.   char *string_pos = NULL;
  369.   char *directory = NULL;
  370.   char *referer = NULL;
  371.   char my_base[SMALLBUF] = {'\0'};
  372.  
  373.   if ( ! strcasecmp(value, "map" ) || ! strcasecmp(value, "menu") ) {
  374.     if (r->server->port == DEFAULT_PORT ) { 
  375.       ap_snprintf(url, SMALLBUF,
  376.         "http://%s%s", r->server->server_hostname, r->uri);
  377.     }
  378.     else {
  379.       ap_snprintf(url, SMALLBUF, "http://%s:%d%s", r->server->server_hostname,
  380.           r->server->port, r->uri);      
  381.     }
  382.     return;  
  383.   }
  384.  
  385.   if ( ! strcasecmp(value, "nocontent") || ! strcasecmp(value, "error") ) {
  386.     strncpy(url, value, SMALLBUF-1);
  387.     url[SMALLBUF-1] = '\0';
  388.     return;    /* these are handled elsewhere, so just copy them */
  389.   }
  390.  
  391.   if ( ! strcasecmp(value, "referer" ) ) {
  392.     referer = table_get(r->headers_in, "Referer");
  393.     if ( referer && *referer ) {
  394.       strncpy(url, referer, SMALLBUF-1);
  395.       url[SMALLBUF-1] = '\0';
  396.       return;
  397.     }
  398.     else {
  399.       *value = '\0';  /* if 'referer' but no referring page, null the value */
  400.     }                 
  401.   }         
  402.  
  403.   string_pos = value;
  404.   while ( isalpha(*string_pos) )
  405.     string_pos++;    /* go along the URL from the map until a non-letter */
  406.   if ( *string_pos == ':' ) { 
  407.     strncpy(url, value, SMALLBUF-1);        /* if letters and then a colon (like http:) */
  408.     url[SMALLBUF-1] = '\0';
  409.     return;                    /* it's an absolute URL, so use it! */
  410.   }
  411.  
  412.   if ( ! base || ! *base ) {
  413.     if ( value && *value ) {  
  414.       strncpy(url, value, SMALLBUF-1);   /* no base: use what is given */
  415.       url[SMALLBUF-1] = '\0';
  416.     }         
  417.     else {                  
  418.       if (r->server->port == DEFAULT_PORT ) {  
  419.     ap_snprintf(url, SMALLBUF, "http://%s/", r->server->server_hostname);
  420.       }            
  421.       if (r->server->port != DEFAULT_PORT ) {
  422.     ap_snprintf(url, SMALLBUF, "http://%s:%d/",
  423.         r->server->server_hostname, r->server->port);
  424.       }                     /* no base, no value: pick a simple default */
  425.     }
  426.     return;  
  427.   }
  428.  
  429.   strncpy(my_base, base, sizeof(my_base)-1);  /* must be a relative URL to be combined with base */
  430.   my_base[sizeof(my_base)-1] = '\0';
  431.   if (strchr(my_base, '/') == NULL && (!strncmp(value, "../", 3) || !strcmp(value, "..")) ) {
  432.     url[0] = '\0';
  433.     log_reason("invalid base directive in map file", r->uri, r);
  434.     return;
  435.   }
  436.   string_pos = my_base; 
  437.   while (*string_pos) {  
  438.     if (*string_pos == '/' && *(string_pos+1) == '/') {
  439.       string_pos += 2;  /* if there are two slashes, jump over them */
  440.       continue;
  441.     }
  442.     if (*string_pos == '/') {  /* the first single slash */
  443.     if ( value[0] == '/' ) {
  444.       *string_pos = '\0';  
  445.     }              /* if the URL from the map starts from root, end the
  446.               base URL string at the first single slash */
  447.     else {
  448.       directory = string_pos; /* save the start of the directory portion */
  449.  
  450.       string_pos = strrchr(string_pos, '/');  /* now reuse string_pos */
  451.       string_pos++;  /* step over that last slash */
  452.       *string_pos = '\0';
  453.     }              /* but if the map url is relative, leave the
  454.             slash on the base (if there is one) */
  455.     break;
  456.       }
  457.     string_pos++;   /* until we get to the end of my_base without finding
  458.                a slash by itself */
  459.   }
  460.  
  461.   while ( ! strncmp(value, "../", 3) || ! strcmp(value, "..") ) { 
  462.  
  463.       if (directory && (slen = strlen (directory))) {
  464.  
  465.       /* for each '..',  knock a directory off the end 
  466.          by ending the string right at the last slash.
  467.          But only consider the directory portion: don't eat
  468.          into the server name.  And only try if a directory
  469.          portion was found */    
  470.       
  471.       clen = slen - 1;
  472.     
  473.       while ((slen - clen) == 1) {
  474.     
  475.           if ((string_pos = strrchr(directory, '/')))
  476.           *string_pos = '\0';
  477.           clen = strlen (directory);
  478.           if (clen == 0) break;
  479.       }
  480.  
  481.       value += 2;      /* jump over the '..' that we found in the value */
  482.       }
  483.       
  484.       if (! strncmp(value, "/../", 4) || ! strcmp(value, "/..") )
  485.  
  486.       value++;       /* step over the '/' if there are more '..' to do.
  487.                this way, we leave the starting '/' on value after
  488.                the last '..', but get rid of it otherwise */ 
  489.      
  490.   }                   /* by this point, value does not start with '..' */
  491.  
  492.   if ( value && *value ) {
  493.     ap_snprintf(url, SMALLBUF, "%s%s", my_base, value);   
  494.   }
  495.   else {
  496.     ap_snprintf(url, SMALLBUF, "%s", my_base);   
  497.   }
  498.   return;
  499. }
  500.  
  501. int imap_reply(request_rec *r, char *redirect)
  502.   if ( ! strcasecmp(redirect, "error") ) {
  503.     return SERVER_ERROR;  /* they actually requested an error! */
  504.   }
  505.   if ( ! strcasecmp(redirect, "nocontent") ) {
  506.     return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
  507.   }
  508.   if (redirect && *redirect ) { 
  509.     table_set(r->headers_out, "Location", redirect);
  510.     return REDIRECT;      /* must be a URL, so redirect to it */
  511.   }    
  512.   return SERVER_ERROR;
  513. }
  514.  
  515. void menu_header(request_rec *r, char *menu)
  516. {
  517.   r->content_type = "text/html";
  518.   send_http_header(r);
  519.   hard_timeout("send menu", r);   /* killed in menu_footer */
  520.  
  521.   rvputs(r, "<html><head>\n<title>Menu for ", r->uri,
  522.         "</title>\n</head><body>\n", NULL);
  523.  
  524.   if (!strcasecmp(menu, "formatted")) {
  525.     rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
  526.   } 
  527.  
  528.   return;
  529. }
  530.  
  531. void menu_blank(request_rec *r, char *menu)
  532. {
  533.   if (! strcasecmp(menu, "formatted") ) {
  534.     rputs("\n", r);
  535.   }
  536.   if (! strcasecmp(menu, "semiformatted") ) {
  537.     rputs("<br>\n", r);
  538.   }
  539.   if (! strcasecmp(menu, "unformatted") ) {
  540.     rputs("\n", r);  
  541.   }
  542.   return;  
  543. }
  544.  
  545. void menu_comment(request_rec *r, char *menu, char *comment)
  546. {
  547.   if (! strcasecmp(menu, "formatted") ) {
  548.     rputs("\n", r);  /* print just a newline if 'formatted' */
  549.   }
  550.   if (! strcasecmp(menu, "semiformatted") && *comment ) {
  551.     rvputs(r, comment, "\n", NULL);
  552.   }             
  553.   if (! strcasecmp(menu, "unformatted") && *comment ) {
  554.     rvputs(r, comment, "\n", NULL);
  555.   }             
  556.   return;    /* comments are ignored in the 'formatted' form */
  557. }
  558.  
  559. void menu_default(request_rec *r, char *menu, char *href, char *text)
  560. {
  561.   if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) {
  562.     return;   /* don't print such lines, these aren'te really href's */
  563.   }
  564.   if ( ! strcasecmp(menu, "formatted" ) ) {
  565.     rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n",
  566.        NULL);
  567.   }
  568.   if ( ! strcasecmp(menu, "semiformatted" ) ) {
  569.     rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n",
  570.        NULL);
  571.   }
  572.   if ( ! strcasecmp(menu, "unformatted" ) ) {
  573.     rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
  574.   }
  575.   return;
  576. }
  577.  
  578. void menu_directive(request_rec *r, char *menu, char *href, char *text)
  579. {
  580.   if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) {
  581.     return;   /* don't print such lines, as this isn't really an href */
  582.   }
  583.   if ( ! strcasecmp(menu, "formatted" ) ) {
  584.     rvputs(r, "<pre>          <a href=\"", href, "\">", text, "</a></pre>\n",
  585.        NULL);
  586.   }
  587.   if ( ! strcasecmp(menu, "semiformatted" ) ) {
  588.     rvputs(r, "<pre>          <a href=\"", href, "\">", text, "</a></pre>\n",
  589.        NULL);
  590.   }
  591.   if ( ! strcasecmp(menu, "unformatted" ) ) {
  592.     rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
  593.   }
  594.   return;
  595. }
  596.  
  597. void menu_footer(request_rec *r)
  598. {
  599.   rputs("\n\n</body>\n</html>\n", r);  /* finish the menu */
  600.   kill_timeout(r);
  601. }
  602.  
  603. /* -------------------------------------------------------------- */
  604.  
  605. /* Return length of this quotedstring/barevalue, and its startpointer */
  606.  
  607. int len_attr ( char *found, char **from ) {
  608.     char rej[]=" \t\n\r>";
  609.     if ( !found || !*found || !from ) return 0;
  610.     if ( *found=='"' || *found=='\'' ) {
  611.         rej[0]=*found; rej[1]=0; *from=found+1;
  612.     }
  613.     else
  614.         *from=found;
  615.     return strcspn(*from,rej);
  616. }
  617.  
  618. /* Find *find, with optional =; return something interesting or NULL */
  619.  
  620. char *find_attr ( char *from, char *find, char equals ) {
  621.     int findlen=strlen(find);
  622.     while ( *from && !isalpha(*from) ) from++;
  623.     while ( *from && strlen(from)>=findlen ) {
  624.         if ( !strncasecmp(from,find,findlen)
  625.              && !isalpha(from[findlen]) ) {        /* isalpha's not really right */
  626.             char *p=from+findlen;
  627.             while ( isspace(*p) ) p++;
  628.             if ( equals ) {
  629.                 if ( *p == '=' ) {
  630.                     p++;
  631.                     while ( isspace(*p) ) p++;
  632.                     return p;
  633.                 }
  634.             }
  635.             else {
  636.                 return p;
  637.             }
  638.         }
  639.         if ( *from ) from++;    /* Eat a char (okayed in while(*from)) */
  640.         while ( isalpha(*from) ) from++;
  641.         while ( isspace(*from) ) from++;
  642.         if ( *from == '=' ) {
  643.             int len; char *p;
  644.             from++;
  645.             while ( isspace(*from) ) from++;
  646.             len=len_attr(from,&p);
  647.             if ( ( *from=='"' || *from=='\'' ) && p[len+1] ) len++;
  648.             from=p+len;
  649.         }
  650.         while ( isspace(*from) ) from++;
  651.     }
  652.     return NULL;
  653. }
  654.  
  655. /* Find *find after *from; copy its attribute to *to, with <nul> term */
  656.  
  657. char *copy_attr ( char *from, char *find, char *to ) {
  658.     char *found=find_attr(from,find,(to?'=':0));
  659.     int len=len_attr(found,&from);
  660.     if ( !len || len>=SMALLBUF ) return NULL;    /* Huh? */
  661.     strncpy(to,from,len); to[len]=0;
  662.     return found;
  663. }
  664.  
  665. /* -------------------------------------------------------------- */
  666.  
  667. int imap_handler(request_rec *r)
  668. {
  669.   char input[LARGEBUF] = {'\0'};
  670.     /* size of input can not be lowered without changing hard-coded
  671.      * checks
  672.      */
  673.   char href_text[SMALLBUF] = {'\0'};
  674.   char base[SMALLBUF] = {'\0'};
  675.   char redirect[SMALLBUF] = {'\0'};
  676.   char directive[SMALLBUF] = {'\0'};
  677.   char value[SMALLBUF] = {'\0'};
  678.   char mapdflt[SMALLBUF] = {'\0'};
  679.   char closest[SMALLBUF] = {'\0'};
  680.   double closest_yet = -1;
  681.  
  682.   double testpoint[2] = { -1,-1 }; 
  683.   double pointarray[MAXVERTS + 1][2] = { {-1,-1} };
  684.   int vertex = 0;
  685.  
  686.   char usemap_only = 0;
  687.   char searching_for_map = 0;
  688.   char usemap_name[SMALLBUF] = {'\0'};
  689.  
  690.   char *string_pos = NULL;
  691.   int chars_read = 0;
  692.   int showmenu = 0;
  693.  
  694.   imap_conf_rec *icr = get_module_config(r->per_dir_config, &imap_module);
  695.  
  696.   char *imap_menu = icr->imap_menu ? 
  697.     icr->imap_menu : IMAP_MENU_DEFAULT;
  698.   char *imap_default = icr->imap_default ? 
  699.     icr->imap_default : IMAP_DEFAULT_DEFAULT;
  700.   char *imap_base = icr->imap_base ?
  701.     icr->imap_base : IMAP_BASE_DEFAULT;
  702.  
  703.   FILE *imap = pfopen(r->pool, r->filename, "r"); 
  704.  
  705. /* Phantom .map files:  
  706.  *    Since imap can now read <AREA> tags, it makes sense to permit it to
  707.  *  read .html files as .map files. Further, since .html files may well
  708.  *  contain multiple <MAP>s, it should be possible to specify which <MAP>
  709.  *  to read.
  710.  *    Specify as index.html.mapname.map or index.html.map: in absence of
  711.  *  mapname, the file will only be read to the first </MAP>
  712.  */
  713.   if ( ! imap && strrchr(r->filename,'.') ) {
  714.     char *p;
  715.     strcpy(input, r->filename);
  716.     *strrchr(input,'.')=0;        /* Remove .map */
  717.     if ( (p=strrchr(input,'.')) ) {
  718.         imap = pfopen(r->pool, input, "r");
  719.         if ( ! imap ) {                /* Not file.html.map */
  720.           strcpy(usemap_name,p+1); *p=0;        /* Try file.html.mapname.map */
  721.           if ( strrchr(input,'.') )
  722.               imap = pfopen(r->pool, input, "r");
  723.         }
  724.     }
  725.     usemap_only=1;        /* Only read <AREA> tags */
  726.     searching_for_map=1;        /* Skip until a suitable <MAP> tag */
  727.   }
  728.   
  729.   if ( ! imap ) 
  730.     return NOT_FOUND;
  731.  
  732.   imap_url(r, NULL, imap_base, base);       /* set base according to default */
  733.   imap_url(r, NULL, imap_default, mapdflt); /* and default to global default */
  734.  
  735.   testpoint[X] = get_x_coord(r->args);
  736.   testpoint[Y] = get_y_coord(r->args);
  737.  
  738.   if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
  739.       (testpoint[X] == 0  && testpoint[Y] == 0) ) {
  740.               /* if either is -1 or if both are zero (new Lynx) */
  741.               /* we don't have valid coordinates */
  742.     testpoint[X] = -1;
  743.     testpoint[Y] = -1;
  744.     if ( strncasecmp(imap_menu, "none", 2) )
  745.       showmenu = 1;    /* show the menu _unless_ ImapMenu is 'none' or 'no' */
  746.   }
  747.  
  748.   if (showmenu) {        /* send start of imagemap menu if we're going to */
  749.     menu_header(r, imap_menu);
  750.   }
  751.  
  752.   while (!cfg_getline(input, LARGEBUF, imap)) {
  753.     string_pos = input;   /* always start at the beginning of line */
  754.  
  755.     directive[0] = '\0';
  756.     value[0] = '\0';  
  757.     href_text[0] = '\0';
  758.     redirect[0] = '\0';
  759.     chars_read = 0; /* clear these before using */
  760.  
  761.     if ( ! input[0] ) {     
  762.       if (showmenu) {
  763.     menu_blank(r, imap_menu);
  764.       }
  765.       continue;                           
  766.     }
  767.  
  768.     if ( input[0] == '#' ) {
  769.       if (showmenu) {
  770.     menu_comment(r, imap_menu, input + 1); 
  771.       }           
  772.       continue;
  773.     } /* blank lines and comments are ignored if we aren't printing a menu */
  774.  
  775.  
  776.  
  777.     if (!strncasecmp(input,"<MAP",4)) {    /* Assume it's a MAP tag on one line */
  778.         usemap_only=1;
  779.         searching_for_map=1;
  780.         if ( ! copy_attr(input,"NAME",directive) ) {
  781.         continue;
  782.         }
  783.         if ( !*usemap_name || !strcmp(usemap_name,directive) ) {
  784.         searching_for_map=0;
  785.         }
  786.         continue;
  787.     }
  788.     else if (!strncasecmp(input,"</MAP",4)) {    /* Assume it's a /MAP tag on one line */
  789.         if ( !searching_for_map ) break;
  790.         searching_for_map=1;
  791.         continue;
  792.     }
  793.  
  794.     if ( searching_for_map ) continue;
  795.     
  796.     if (!strncasecmp(input,"<AREA",5)) {    /* Assume it's an AREA tag on one line */
  797.         /* Translate this into a .map format (gets smaller :) */
  798.         usemap_only=1;
  799.         if ( ! copy_attr(input,"SHAPE",directive) ) {
  800.             
  801.             /* Define a CONTINUE which breaks out of nested loops, on error */
  802. #define CONTINUE goto AREA_CONTINUE
  803. AREA_CONTINUE:
  804.             continue;
  805.             
  806.         }
  807.         if ( ! copy_attr(input,"HREF",value) ) {
  808.             if ( ! find_attr(input,"NOHREF",0) ) CONTINUE;
  809.             strcpy(value,"nocontent");
  810.         }
  811.         { char
  812.             *coords=find_attr(input,"COORDS",'='),
  813.             *alt=find_attr(input,"ALT",'=');
  814.             
  815.             if ( alt && alt < coords ) {    /* "alt" coords appeared first  */
  816.                 char *p; int len=len_attr(alt,&p);
  817.                 input[0]='"';
  818.                 memmove(input+1,p,len);
  819.                 strcpy(input+len+1,"\" ");
  820.                 alt=NULL;
  821.             }
  822.             else
  823.                 input[0]=0;    /* Ripe for strcat() */
  824.             string_pos=input+strlen(input);
  825.             if ( coords ) {            /* Coords. Minor surgery for circles */
  826.                 char quotes=(*coords=='"' || *coords=='\'');
  827.                 char *p=coords+quotes;
  828.                 char *p2;
  829.                 if ( !strcasecmp(directive,"CIRCLE") ) {
  830.                     long x,y,r;
  831.                     p2=p; x=strtol(p,&p,10); if ( p2==p ) CONTINUE;
  832.                     while ( isspace(*p) ) p++; if ( *p++!=',' ) CONTINUE;
  833.                     p2=p; y=strtol(p,&p,10); if ( p2==p ) CONTINUE;
  834.                     while ( isspace(*p) ) p++; if ( *p++!=',' ) CONTINUE;
  835.                     p2=p; r=strtol(p,&p,10); if ( p2==p ) CONTINUE;
  836.                     if ( quotes ) {
  837.                         while ( isspace(*p) ) p++;
  838.                         if ( *p!=*coords ) CONTINUE;
  839.                     }
  840.                     else {
  841.                         if ( *p!='>' || *p!=' ' ) CONTINUE;
  842.                     }
  843.                     /* XX This is dodgy in a totally pathological case */
  844.                     sprintf(string_pos,"%ld,%ld %ld,%ld ",x,y,x,y+r);
  845.                     string_pos+=strlen(string_pos);
  846.                 } 
  847.                 else {    /* Not circle - take n,n,n,n -> n,n n,n */
  848.                     long x,y;
  849.                     do {
  850.                         p2=p; x=strtol(p,&p,10); if ( p2==p ) CONTINUE;
  851.                         while ( isspace(*p) ) p++; if ( *p++!=',' ) CONTINUE;
  852.                         p2=p; y=strtol(p,&p,10); if ( p2==p ) CONTINUE;
  853.                         while ( isspace(*p) ) p++;
  854.                         sprintf(string_pos,"%ld,%ld ",x,y);
  855.                         string_pos+=strlen(string_pos);
  856.                     } while ( *p++==',' );
  857.                     p--;
  858.                     if ( quotes ) {
  859.                         while ( isspace(*p) ) p++;
  860.                         if ( *p!=*coords ) CONTINUE;
  861.                     }
  862.                     else {
  863.                         if ( *p!='>' || *p!=' ' ) CONTINUE;
  864.                     }
  865.                 } /* else (directive!="circle") */
  866.             } /* if (coords) */
  867.             if ( alt ) {        /* coords "alt" */
  868.                 char *p; int len=len_attr(alt,&p);
  869.                 *string_pos++='"';
  870.                 memmove(string_pos,p,len);
  871.                 strcpy(string_pos+len,"\"");
  872.             }
  873.         }
  874.         string_pos=input;
  875.         
  876.         /* Don't need this any more */
  877. #undef CONTINUE
  878.     }
  879.  
  880.  
  881.  
  882.     else {
  883.     
  884.     if ( usemap_only ) {
  885.         continue;    /* Don't read anything if we're in HTML mode */
  886.     }
  887.  
  888.     if (sscanf(input, "%255s %255s", directive, value) != 2) {
  889.       continue;                           /* make sure we read two fields */
  890.     }
  891.     /* Now skip what we just read... we can't use ANSIism %n */
  892.     while (!(isspace(*string_pos)))    /* past directive */
  893.         string_pos++;
  894.     while (isspace(*string_pos))    /* and whitespace */
  895.         string_pos++;
  896.     while (!(isspace(*string_pos)))    /* and value... have to watch it */
  897.         string_pos++;        /* can have punctuation and stuff */
  898.     }
  899.     
  900.     if ( ! strncasecmp(directive, "base", 4 ) ) {       /* base, base_uri */
  901.       imap_url(r, NULL, value, base);
  902.       continue; /* base is never printed to a menu */
  903.     }    
  904.  
  905.     chars_read = read_quoted(string_pos, href_text);
  906.     string_pos += chars_read;      /* read the quoted href text if present */
  907.  
  908.     if ( ! strcasecmp(directive, "default" ) ) {        /* default */
  909.       imap_url(r, NULL, value, mapdflt);
  910.       if (showmenu) {              /* print the default if there's a menu */
  911.     if (! *href_text) {           /* if we didn't find a "href text" */
  912.       strncpy(href_text, mapdflt, sizeof(href_text)-1); /* use the href itself as text */
  913.       href_text[sizeof(href_text)-1] = '\0';
  914.     }
  915.     imap_url(r, base, mapdflt, redirect); 
  916.     menu_default(r, imap_menu, redirect, href_text);
  917.       }
  918.       continue;
  919.     }
  920.  
  921.     vertex = 0;
  922.     while ( vertex < MAXVERTS &&  
  923.      sscanf(string_pos, "%lf, %lf",
  924.      &pointarray[vertex][X], &pointarray[vertex][Y])   == 2)
  925.     {
  926.     /* Now skip what we just read... we can't use ANSIism %n */
  927.     while(isspace(*string_pos))    /* past whitespace */
  928.         string_pos++;
  929.     while(isdigit(*string_pos))    /* and the 1st number */
  930.         string_pos++;
  931.     string_pos++;            /* skip the ',' */
  932.     while(isspace(*string_pos))    /* past any more whitespace */
  933.         string_pos++;
  934.     while(isdigit(*string_pos))    /* 2nd number */
  935.         string_pos++;
  936.     vertex++;
  937.     }                /* so long as there are more vertices to read, and
  938.             we have room, read them in.  We start where we left
  939.             off of the last sscanf, not at the beginning.*/
  940.                   
  941.     pointarray[vertex][X] = -1;  /* signals the end of vertices */
  942.  
  943.     if (showmenu) {
  944.       read_quoted(string_pos, href_text); /* href text could be here instead */
  945.       if (! *href_text) {           /* if we didn't find a "href text" */
  946.     strncpy(href_text, value, sizeof(href_text)-1);  /* use the href itself in the menu */
  947.     href_text[sizeof(href_text)-1] = '\0';
  948.       }
  949.       imap_url(r, base, value, redirect); 
  950.       menu_directive(r, imap_menu, redirect, href_text);
  951.       continue;
  952.     }
  953.     /* note that we don't make it past here if we are making a menu */
  954.  
  955.     if (testpoint[X] == -1 || pointarray[0][X] == -1 )
  956.       continue;    /* don't try the following tests if testpoints
  957.             are invalid, or if there are no coordinates */
  958.  
  959.     if ( ! strcasecmp(directive, "poly" ) ) {        /* poly */
  960.  
  961.       if (pointinpoly (testpoint, pointarray) ) {
  962.     pfclose(r->pool, imap); 
  963.     imap_url(r, base, value, redirect);     
  964.     return (imap_reply(r, redirect));
  965.       }
  966.       continue;
  967.     }
  968.  
  969.     if ( ! strcasecmp(directive, "circle" ) ) {        /* circle */
  970.     
  971.       if (pointincircle (testpoint, pointarray) ) {
  972.     pfclose(r->pool, imap); 
  973.     imap_url(r, base, value, redirect);     
  974.     return (imap_reply(r, redirect));
  975.       }
  976.       continue;
  977.     }
  978.     
  979.     if ( ! strcasecmp(directive, "rect" ) ) {        /* rect */
  980.       
  981.       if (pointinrect (testpoint, pointarray) ) {
  982.     pfclose(r->pool, imap); 
  983.     imap_url(r, base, value, redirect);     
  984.     return (imap_reply(r, redirect));
  985.       }
  986.       continue;
  987.     }
  988.     
  989.     if ( ! strcasecmp(directive, "point" ) ) {         /* point */
  990.       
  991.       if (is_closer(testpoint, pointarray, &closest_yet) ) {
  992.     strncpy(closest, value, sizeof(closest)-1);  /* if the closest point yet save it */
  993.     closest[sizeof(closest)-1] = '\0';
  994.       }
  995.       
  996.       continue;    
  997.     }     /* move on to next line whether it's closest or not */
  998.     
  999.   }       /* nothing matched, so we get another line! */
  1000.  
  1001.   pfclose(r->pool, imap);   /* we are done with the map file, so close it */
  1002.  
  1003.   if (showmenu) {
  1004.     menu_footer(r);   /* finish the menu and we are done */
  1005.     return OK;                
  1006.   }
  1007.  
  1008.   if (*closest) {    /* if a 'point' directive has been seen */
  1009.     imap_url(r, base, closest, redirect);     
  1010.     return (imap_reply(r, redirect));
  1011.   }    
  1012.  
  1013.   if (*mapdflt ) {   /* a default should be defined, even if only 'nocontent'*/
  1014.     imap_url(r, base, mapdflt, redirect);
  1015.     return(imap_reply(r, redirect));
  1016.   }    
  1017.  
  1018.   return SERVER_ERROR;   /* If we make it this far, we failed. They lose! */
  1019. }
  1020.  
  1021.  
  1022. handler_rec imap_handlers[] = {
  1023. { IMAP_MAGIC_TYPE, imap_handler },
  1024. { "imap-file", imap_handler },
  1025. { NULL }
  1026. };
  1027.  
  1028. module imap_module = {
  1029.    STANDARD_MODULE_STUFF,
  1030.    NULL,            /* initializer */
  1031.    create_imap_dir_config,    /* dir config creater */
  1032.    merge_imap_dir_configs,    /* dir merger --- default is to override */
  1033.    NULL,            /* server config */
  1034.    NULL,            /* merge server config */
  1035.    imap_cmds,            /* command table */
  1036.    imap_handlers,        /* handlers */
  1037.    NULL,            /* filename translation */
  1038.    NULL,            /* check_user_id */
  1039.    NULL,            /* check auth */
  1040.    NULL,            /* check access */
  1041.    NULL,            /* type_checker */
  1042.    NULL,            /* fixups */
  1043.    NULL,            /* logger */
  1044.    NULL                /* header parser */
  1045. };
  1046.