home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_imap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  29.8 KB  |  920 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 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. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /*
  59.  * This imagemap module started as a port of the original imagemap.c
  60.  * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
  61.  * This version includes the mapping algorithms found in version 1.3
  62.  * of imagemap.c.
  63.  *
  64.  * Contributors to this code include:
  65.  *
  66.  * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
  67.  *
  68.  * Eric Haines, erich@eye.com
  69.  * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
  70.  *
  71.  * Randy Terbush, randy@zyzzyva.com
  72.  * port to Apache module format, "base_uri" and support for relative URLs
  73.  * 
  74.  * James H. Cloos, Jr., cloos@jhcloos.com
  75.  * Added point datatype, using code in NCSA's version 1.8 imagemap.c
  76.  * program, as distributed with version 1.4.1 of their server.
  77.  * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
  78.  *
  79.  * Nathan Kurz, nate@tripod.com
  80.  * Rewrite/reorganization.  New handling of default, base and relative URLs.  
  81.  * New Configuration directives:
  82.  *    ImapMenu {none, formatted, semiformatted, unformatted}
  83.  *    ImapDefault {error, nocontent, referer, menu, URL}
  84.  *    ImapBase {map, referer, URL}
  85.  * Support for creating non-graphical menu added.  (backwards compatible):
  86.  *    Old:  directive URL [x,y ...]
  87.  *    New:  directive URL "Menu text" [x,y ...]
  88.  *     or:  directive URL x,y ... "Menu text"
  89.  * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
  90.  *
  91.  * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
  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 MAXVERTS 100
  105. #define X 0
  106. #define Y 1
  107.  
  108. #define IMAP_MENU_DEFAULT "formatted"
  109. #define IMAP_DEFAULT_DEFAULT "nocontent"
  110. #define IMAP_BASE_DEFAULT "map"
  111.  
  112. #ifdef SUNOS4
  113. double strtod();                /* SunOS needed this */
  114. #endif
  115.  
  116. module MODULE_VAR_EXPORT imap_module;
  117.  
  118. typedef struct {
  119.     char *imap_menu;
  120.     char *imap_default;
  121.     char *imap_base;
  122. } imap_conf_rec;
  123.  
  124. static void *create_imap_dir_config(pool *p, char *dummy)
  125. {
  126.     imap_conf_rec *icr =
  127.     (imap_conf_rec *) ap_palloc(p, sizeof(imap_conf_rec));
  128.  
  129.     icr->imap_menu = NULL;
  130.     icr->imap_default = NULL;
  131.     icr->imap_base = NULL;
  132.  
  133.     return icr;
  134. }
  135.  
  136. static void *merge_imap_dir_configs(pool *p, void *basev, void *addv)
  137. {
  138.     imap_conf_rec *new = (imap_conf_rec *) ap_pcalloc(p, sizeof(imap_conf_rec));
  139.     imap_conf_rec *base = (imap_conf_rec *) basev;
  140.     imap_conf_rec *add = (imap_conf_rec *) addv;
  141.  
  142.     new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
  143.     new->imap_default = add->imap_default ? add->imap_default
  144.                                           : base->imap_default;
  145.     new->imap_base = add->imap_base ? add->imap_base : base->imap_base;
  146.  
  147.     return new;
  148. }
  149.  
  150.  
  151. static const command_rec imap_cmds[] =
  152. {
  153.     {"ImapMenu", ap_set_string_slot,
  154.      (void *) XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1,
  155.  "the type of menu generated: none, formatted, semiformatted, unformatted"},
  156.     {"ImapDefault", ap_set_string_slot,
  157.      (void *) XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1,
  158.      "the action taken if no match: error, nocontent, referer, menu, URL"},
  159.     {"ImapBase", ap_set_string_slot,
  160.      (void *) XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1,
  161.      "the base for all URL's: map, referer, URL (or start of)"},
  162.     {NULL}
  163. };
  164.  
  165. static int pointinrect(const double point[2], double coords[MAXVERTS][2])
  166. {
  167.     double max[2], min[2];
  168.     if (coords[0][X] > coords[1][X]) {
  169.         max[0] = coords[0][X];
  170.         min[0] = coords[1][X];
  171.     }
  172.     else {
  173.         max[0] = coords[1][X];
  174.         min[0] = coords[0][X];
  175.     }
  176.  
  177.     if (coords[0][Y] > coords[1][Y]) {
  178.         max[1] = coords[0][Y];
  179.         min[1] = coords[1][Y];
  180.     }
  181.     else {
  182.         max[1] = coords[1][Y];
  183.         min[1] = coords[0][Y];
  184.     }
  185.  
  186.     return ((point[X] >= min[0] && point[X] <= max[0]) &&
  187.             (point[Y] >= min[1] && point[Y] <= max[1]));
  188. }
  189.  
  190. static int pointincircle(const double point[2], double coords[MAXVERTS][2])
  191. {
  192.     double radius1, radius2;
  193.  
  194.     radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
  195.         + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
  196.  
  197.     radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
  198.         + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
  199.  
  200.     return (radius2 <= radius1);
  201. }
  202.  
  203. #define fmin(a,b) (((a)>(b))?(b):(a))
  204. #define fmax(a,b) (((a)>(b))?(a):(b))
  205.  
  206. static int pointinpoly(const double point[2], double pgon[MAXVERTS][2])
  207. {
  208.     int i, numverts, crossings = 0;
  209.     double x = point[X], y = point[Y];
  210.  
  211.     for (numverts = 0; pgon[numverts][X] != -1 && numverts < MAXVERTS;
  212.     numverts++) {
  213.     /* just counting the vertexes */
  214.     }
  215.  
  216.     for (i = 0; i < numverts; i++) {
  217.         double x1=pgon[i][X];
  218.         double y1=pgon[i][Y];
  219.         double x2=pgon[(i + 1) % numverts][X];
  220.         double y2=pgon[(i + 1) % numverts][Y];
  221.         double d=(y - y1) * (x2 - x1) - (x - x1) * (y2 - y1);
  222.  
  223.         if ((y1 >= y) != (y2 >= y)) {
  224.         crossings +=y2 - y1 >= 0 ? d >= 0 : d <= 0;
  225.     }
  226.         if (!d && fmin(x1,x2) <= x && x <= fmax(x1,x2)
  227.         && fmin(y1,y2) <= y && y <= fmax(y1,y2)) {
  228.         return 1;
  229.     }
  230.     }
  231.     return crossings & 0x01;
  232. }
  233.  
  234.  
  235. static int is_closer(const double point[2], double coords[MAXVERTS][2],
  236.                      double *closest)
  237. {
  238.     double dist_squared = ((point[X] - coords[0][X])
  239.                            * (point[X] - coords[0][X]))
  240.                           + ((point[Y] - coords[0][Y])
  241.                              * (point[Y] - coords[0][Y]));
  242.  
  243.     if (point[X] < 0 || point[Y] < 0) {
  244.         return (0);          /* don't mess around with negative coordinates */
  245.     }
  246.  
  247.     if (*closest < 0 || dist_squared < *closest) {
  248.         *closest = dist_squared;
  249.         return (1);          /* if this is the first point or is the closest yet
  250.                                 set 'closest' equal to this distance^2 */
  251.     }
  252.  
  253.     return (0);              /* if it's not the first or closest */
  254.  
  255. }
  256.  
  257. static double get_x_coord(const char *args)
  258. {
  259.     char *endptr;               /* we want it non-null */
  260.     double x_coord = -1;        /* -1 is returned if no coordinate is given */
  261.  
  262.     if (args == NULL) {
  263.         return (-1);            /* in case we aren't passed anything */
  264.     }
  265.  
  266.     while (*args && !ap_isdigit(*args) && *args != ',') {
  267.         args++;                 /* jump to the first digit, but not past
  268.                                    a comma or end */
  269.     }
  270.  
  271.     x_coord = strtod(args, &endptr);
  272.  
  273.     if (endptr > args) {        /* if a conversion was made */
  274.         return (x_coord);
  275.     }
  276.  
  277.     return (-1);                /* else if no conversion was made,
  278.                                    or if no args was given */
  279. }
  280.  
  281. static double get_y_coord(const char *args)
  282. {
  283.     char *endptr;               /* we want it non-null */
  284.     char *start_of_y = NULL;
  285.     double y_coord = -1;        /* -1 is returned on error */
  286.  
  287.     if (args == NULL) {
  288.         return (-1);            /* in case we aren't passed anything */
  289.     }
  290.  
  291.     start_of_y = strchr(args, ',');     /* the comma */
  292.  
  293.     if (start_of_y) {
  294.  
  295.         start_of_y++;           /* start looking at the character after
  296.                                    the comma */
  297.  
  298.         while (*start_of_y && !ap_isdigit(*start_of_y)) {
  299.             start_of_y++;       /* jump to the first digit, but not
  300.                                    past the end */
  301.     }
  302.  
  303.         y_coord = strtod(start_of_y, &endptr);
  304.  
  305.         if (endptr > start_of_y) {
  306.             return (y_coord);
  307.     }
  308.     }
  309.  
  310.     return (-1);                /* if no conversion was made, or
  311.                                    no comma was found in args */
  312. }
  313.  
  314.  
  315. /* See if string has a "quoted part", and if so set *quoted_part to
  316.  * the first character of the quoted part, then hammer a \0 onto the
  317.  * trailing quote, and set *string to point at the first character
  318.  * past the second quote.
  319.  *
  320.  * Otherwise set *quoted_part to NULL, and leave *string alone.
  321.  */
  322. static void read_quoted(char **string, char **quoted_part)
  323. {
  324.     char *strp = *string;
  325.  
  326.     /* assume there's no quoted part */
  327.     *quoted_part = NULL;
  328.  
  329.     while (ap_isspace(*strp)) {
  330.         strp++;                   /* go along string until non-whitespace */
  331.     }
  332.  
  333.     if (*strp == '"') {           /* if that character is a double quote */
  334.         strp++;                   /* step over it */
  335.     *quoted_part = strp;      /* note where the quoted part begins */
  336.  
  337.         while (*strp && *strp != '"') {
  338.         ++strp;        /* skip the quoted portion */
  339.         }
  340.  
  341.         *strp = '\0';        /* end the string with a NUL */
  342.  
  343.         strp++;                   /* step over the last double quote */
  344.     *string = strp;
  345.     }
  346. }
  347.  
  348. /*
  349.  * returns the mapped URL or NULL.
  350.  */
  351. static char *imap_url(request_rec *r, const char *base, const char *value)
  352. {
  353. /* translates a value into a URL. */
  354.     int slen, clen;
  355.     char *string_pos = NULL;
  356.     const char *string_pos_const = NULL;
  357.     char *directory = NULL;
  358.     const char *referer = NULL;
  359.     char *my_base;
  360.  
  361.     if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
  362.     return ap_construct_url(r->pool, r->uri, r);
  363.     }
  364.  
  365.     if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
  366.         return ap_pstrdup(r->pool, value);      /* these are handled elsewhere,
  367.                                                 so just copy them */
  368.     }
  369.  
  370.     if (!strcasecmp(value, "referer")) {
  371.         referer = ap_table_get(r->headers_in, "Referer");
  372.         if (referer && *referer) {
  373.         return ap_pstrdup(r->pool, referer);
  374.         }
  375.         else {
  376.         /* XXX:  This used to do *value = '\0'; ... which is totally bogus
  377.          * because it hammers the passed in value, which can be a string
  378.              * constant, or part of a config, or whatever.  Total garbage.
  379.              * This works around that without changing the rest of this
  380.              * code much
  381.              */
  382.             value = "";      /* if 'referer' but no referring page,
  383.                                 null the value */
  384.         }
  385.     }
  386.  
  387.     string_pos_const = value;
  388.     while (ap_isalpha(*string_pos_const)) {
  389.     string_pos_const++;           /* go along the URL from the map
  390.                                          until a non-letter */
  391.     }
  392.     if (*string_pos_const == ':') {
  393.     /* if letters and then a colon (like http:) */
  394.     /* it's an absolute URL, so use it! */
  395.     return ap_pstrdup(r->pool, value);
  396.     }
  397.  
  398.     if (!base || !*base) {
  399.         if (value && *value) {
  400.         return ap_pstrdup(r->pool, value); /* no base: use what is given */
  401.         }
  402.     /* no base, no value: pick a simple default */
  403.     return ap_construct_url(r->pool, "/", r);
  404.     }
  405.  
  406.     /* must be a relative URL to be combined with base */
  407.     if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3)
  408.         || !strcmp(value, ".."))) {
  409.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  410.                     "invalid base directive in map file: %s", r->uri);
  411.         return NULL;
  412.     }
  413.     my_base = ap_pstrdup(r->pool, base);
  414.     string_pos = my_base;
  415.     while (*string_pos) {
  416.         if (*string_pos == '/' && *(string_pos + 1) == '/') {
  417.             string_pos += 2;    /* if there are two slashes, jump over them */
  418.             continue;
  419.         }
  420.         if (*string_pos == '/') {       /* the first single slash */
  421.             if (value[0] == '/') {
  422.                 *string_pos = '\0';
  423.             }                   /* if the URL from the map starts from root,
  424.                                    end the base URL string at the first single
  425.                                    slash */
  426.             else {
  427.                 directory = string_pos;         /* save the start of
  428.                                                    the directory portion */
  429.  
  430.                 string_pos = strrchr(string_pos, '/');  /* now reuse
  431.                                                            string_pos */
  432.                 string_pos++;   /* step over that last slash */
  433.                 *string_pos = '\0';
  434.             }                   /* but if the map url is relative, leave the
  435.                                    slash on the base (if there is one) */
  436.             break;
  437.         }
  438.         string_pos++;           /* until we get to the end of my_base without
  439.                                    finding a slash by itself */
  440.     }
  441.  
  442.     while (!strncmp(value, "../", 3) || !strcmp(value, "..")) {
  443.  
  444.         if (directory && (slen = strlen(directory))) {
  445.  
  446.             /* for each '..',  knock a directory off the end 
  447.                by ending the string right at the last slash.
  448.                But only consider the directory portion: don't eat
  449.                into the server name.  And only try if a directory
  450.                portion was found */
  451.  
  452.             clen = slen - 1;
  453.  
  454.             while ((slen - clen) == 1) {
  455.  
  456.                 if ((string_pos = strrchr(directory, '/'))) {
  457.                     *string_pos = '\0';
  458.         }
  459.                 clen = strlen(directory);
  460.                 if (clen == 0) {
  461.                     break;
  462.         }
  463.             }
  464.  
  465.             value += 2;         /* jump over the '..' that we found in the
  466.                                    value */
  467.         }
  468.         else if (directory) {
  469.             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  470.                         "invalid directory name in map file: %s", r->uri);
  471.             return NULL;
  472.         }
  473.  
  474.         if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) {
  475.             value++;            /* step over the '/' if there are more '..'
  476.                                    to do.  This way, we leave the starting
  477.                                    '/' on value after the last '..', but get
  478.                                    rid of it otherwise */
  479.     }
  480.  
  481.     }                           /* by this point, value does not start
  482.                                    with '..' */
  483.  
  484.     if (value && *value) {
  485.     return ap_pstrcat(r->pool, my_base, value, NULL);
  486.     }
  487.     return my_base;
  488. }
  489.  
  490. static int imap_reply(request_rec *r, char *redirect)
  491. {
  492.     if (!strcasecmp(redirect, "error")) {
  493.         return SERVER_ERROR;    /* they actually requested an error! */
  494.     }
  495.     if (!strcasecmp(redirect, "nocontent")) {
  496.         return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
  497.     }
  498.     if (redirect && *redirect) {
  499.         ap_table_setn(r->headers_out, "Location", redirect);
  500.         return REDIRECT;        /* must be a URL, so redirect to it */
  501.     }
  502.     return SERVER_ERROR;
  503. }
  504.  
  505. static void menu_header(request_rec *r, char *menu)
  506. {
  507.     r->content_type = "text/html";
  508.     ap_send_http_header(r);
  509.     ap_hard_timeout("send menu", r);       /* killed in menu_footer */
  510.  
  511.     ap_rvputs(r, "<html><head>\n<title>Menu for ", r->uri,
  512.            "</title>\n</head><body>\n", NULL);
  513.  
  514.     if (!strcasecmp(menu, "formatted")) {
  515.         ap_rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
  516.     }
  517.  
  518.     return;
  519. }
  520.  
  521. static void menu_blank(request_rec *r, char *menu)
  522. {
  523.     if (!strcasecmp(menu, "formatted")) {
  524.         ap_rputs("\n", r);
  525.     }
  526.     if (!strcasecmp(menu, "semiformatted")) {
  527.         ap_rputs("<br>\n", r);
  528.     }
  529.     if (!strcasecmp(menu, "unformatted")) {
  530.         ap_rputs("\n", r);
  531.     }
  532.     return;
  533. }
  534.  
  535. static void menu_comment(request_rec *r, char *menu, char *comment)
  536. {
  537.     if (!strcasecmp(menu, "formatted")) {
  538.         ap_rputs("\n", r);         /* print just a newline if 'formatted' */
  539.     }
  540.     if (!strcasecmp(menu, "semiformatted") && *comment) {
  541.         ap_rvputs(r, comment, "\n", NULL);
  542.     }
  543.     if (!strcasecmp(menu, "unformatted") && *comment) {
  544.         ap_rvputs(r, comment, "\n", NULL);
  545.     }
  546.     return;                     /* comments are ignored in the
  547.                                    'formatted' form */
  548. }
  549.  
  550. static void menu_default(request_rec *r, char *menu, char *href, char *text)
  551. {
  552.     if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
  553.         return;                 /* don't print such lines, these aren't
  554.                                    really href's */
  555.     }
  556.     if (!strcasecmp(menu, "formatted")) {
  557.         ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
  558.                "</a></pre>\n", NULL);
  559.     }
  560.     if (!strcasecmp(menu, "semiformatted")) {
  561.         ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
  562.                "</a></pre>\n", NULL);
  563.     }
  564.     if (!strcasecmp(menu, "unformatted")) {
  565.         ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
  566.     }
  567.     return;
  568. }
  569.  
  570. static void menu_directive(request_rec *r, char *menu, char *href, char *text)
  571. {
  572.     if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
  573.         return;                 /* don't print such lines, as this isn't
  574.                                    really an href */
  575.     }
  576.     if (!strcasecmp(menu, "formatted")) {
  577.         ap_rvputs(r, "<pre>          <a href=\"", href, "\">", text,
  578.                "</a></pre>\n", NULL);
  579.     }
  580.     if (!strcasecmp(menu, "semiformatted")) {
  581.         ap_rvputs(r, "<pre>          <a href=\"", href, "\">", text,
  582.                "</a></pre>\n", NULL);
  583.     }
  584.     if (!strcasecmp(menu, "unformatted")) {
  585.         ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
  586.     }
  587.     return;
  588. }
  589.  
  590. static void menu_footer(request_rec *r)
  591. {
  592.     ap_rputs("\n\n</body>\n</html>\n", r);         /* finish the menu */
  593.     ap_kill_timeout(r);
  594. }
  595.  
  596. static int imap_handler(request_rec *r)
  597. {
  598.     char input[MAX_STRING_LEN];
  599.     char *directive;
  600.     char *value;
  601.     char *href_text;
  602.     char *base;
  603.     char *redirect;
  604.     char *mapdflt;
  605.     char *closest = NULL;
  606.     double closest_yet = -1;
  607.  
  608.     double testpoint[2];
  609.     double pointarray[MAXVERTS + 1][2];
  610.     int vertex;
  611.  
  612.     char *string_pos;
  613.     int showmenu = 0;
  614.  
  615.     imap_conf_rec *icr = ap_get_module_config(r->per_dir_config, &imap_module);
  616.  
  617.     char *imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT;
  618.     char *imap_default = icr->imap_default
  619.                 ?  icr->imap_default : IMAP_DEFAULT_DEFAULT;
  620.     char *imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT;
  621.  
  622.     configfile_t *imap; 
  623.  
  624.     if (r->method_number != M_GET) {
  625.     return DECLINED;
  626.     }
  627.  
  628.     imap = ap_pcfg_openfile(r->pool, r->filename);
  629.  
  630.     if (!imap) {
  631.         return NOT_FOUND;
  632.     }
  633.  
  634.     base = imap_url(r, NULL, imap_base);         /* set base according
  635.                                                     to default */
  636.     if (!base) {
  637.     return HTTP_INTERNAL_SERVER_ERROR;
  638.     }
  639.     mapdflt = imap_url(r, NULL, imap_default);   /* and default to
  640.                                                     global default */
  641.     if (!mapdflt) {
  642.     return HTTP_INTERNAL_SERVER_ERROR;
  643.     }
  644.  
  645.     testpoint[X] = get_x_coord(r->args);
  646.     testpoint[Y] = get_y_coord(r->args);
  647.  
  648.     if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
  649.         (testpoint[X] == 0 && testpoint[Y] == 0)) {
  650.         /* if either is -1 or if both are zero (new Lynx) */
  651.         /* we don't have valid coordinates */
  652.         testpoint[X] = -1;
  653.         testpoint[Y] = -1;
  654.         if (strncasecmp(imap_menu, "none", 2)) {
  655.             showmenu = 1;       /* show the menu _unless_ ImapMenu is
  656.                                    'none' or 'no' */
  657.     }
  658.     }
  659.  
  660.     if (showmenu) {             /* send start of imagemap menu if
  661.                                    we're going to */
  662.         menu_header(r, imap_menu);
  663.     }
  664.  
  665.     while (!ap_cfg_getline(input, sizeof(input), imap)) {
  666.         if (!input[0]) {
  667.             if (showmenu) {
  668.                 menu_blank(r, imap_menu);
  669.             }
  670.             continue;
  671.         }
  672.  
  673.         if (input[0] == '#') {
  674.             if (showmenu) {
  675.                 menu_comment(r, imap_menu, input + 1);
  676.             }
  677.             continue;
  678.         }                       /* blank lines and comments are ignored
  679.                                    if we aren't printing a menu */
  680.  
  681.     /* find the first two space delimited fields, recall that
  682.      * ap_cfg_getline has removed leading/trailing whitespace.
  683.      *
  684.      * note that we're tokenizing as we go... if we were to use the
  685.      * ap_getword() class of functions we would end up allocating extra
  686.      * memory for every line of the map file
  687.      */
  688.         string_pos = input;
  689.     if (!*string_pos) {        /* need at least two fields */
  690.         goto need_2_fields;
  691.     }
  692.  
  693.     directive = string_pos;
  694.     while (*string_pos && !ap_isspace(*string_pos)) {    /* past directive */
  695.         ++string_pos;
  696.     }
  697.     if (!*string_pos) {        /* need at least two fields */
  698.         goto need_2_fields;
  699.     }
  700.     *string_pos++ = '\0';
  701.  
  702.     if (!*string_pos) {        /* need at least two fields */
  703.         goto need_2_fields;
  704.     }
  705.     while(*string_pos && ap_isspace(*string_pos)) { /* past whitespace */
  706.         ++string_pos;
  707.     }
  708.  
  709.     value = string_pos;
  710.     while (*string_pos && !ap_isspace(*string_pos)) {    /* past value */
  711.         ++string_pos;
  712.     }
  713.     if (ap_isspace(*string_pos)) {
  714.         *string_pos++ = '\0';
  715.     }
  716.     else {
  717.         /* end of input, don't advance past it */
  718.         *string_pos = '\0';
  719.     }
  720.  
  721.         if (!strncasecmp(directive, "base", 4)) {       /* base, base_uri */
  722.             base = imap_url(r, NULL, value);
  723.         if (!base) {
  724.         goto menu_bail;
  725.         }
  726.             continue;           /* base is never printed to a menu */
  727.         }
  728.  
  729.         read_quoted(&string_pos, &href_text);
  730.  
  731.         if (!strcasecmp(directive, "default")) {        /* default */
  732.             mapdflt = imap_url(r, NULL, value);
  733.         if (!mapdflt) {
  734.         goto menu_bail;
  735.         }
  736.             if (showmenu) {     /* print the default if there's a menu */
  737.                 redirect = imap_url(r, base, mapdflt);
  738.         if (!redirect) {
  739.             goto menu_bail;
  740.         }
  741.                 menu_default(r, imap_menu, redirect,
  742.                              href_text ? href_text : mapdflt);
  743.             }
  744.             continue;
  745.         }
  746.  
  747.         vertex = 0;
  748.         while (vertex < MAXVERTS &&
  749.                sscanf(string_pos, "%lf%*[, ]%lf",
  750.                       &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) {
  751.             /* Now skip what we just read... we can't use ANSIism %n */
  752.             while (ap_isspace(*string_pos)) {      /* past whitespace */
  753.                 string_pos++;
  754.         }
  755.             while (ap_isdigit(*string_pos)) {      /* and the 1st number */
  756.                 string_pos++;
  757.         }
  758.             string_pos++;       /* skip the ',' */
  759.             while (ap_isspace(*string_pos)) {      /* past any more whitespace */
  760.                 string_pos++;
  761.         }
  762.             while (ap_isdigit(*string_pos)) {      /* 2nd number */
  763.                 string_pos++;
  764.         }
  765.             vertex++;
  766.         }                       /* so long as there are more vertices to
  767.                                    read, and we have room, read them in.
  768.                                    We start where we left off of the last
  769.                                    sscanf, not at the beginning. */
  770.  
  771.         pointarray[vertex][X] = -1;     /* signals the end of vertices */
  772.  
  773.         if (showmenu) {
  774.         if (!href_text) {
  775.         read_quoted(&string_pos, &href_text);     /* href text could
  776.                                                              be here instead */
  777.         }
  778.             redirect = imap_url(r, base, value);
  779.         if (!redirect) {
  780.         goto menu_bail;
  781.         }
  782.             menu_directive(r, imap_menu, redirect,
  783.                            href_text ? href_text : value);
  784.             continue;
  785.         }
  786.         /* note that we don't make it past here if we are making a menu */
  787.  
  788.         if (testpoint[X] == -1 || pointarray[0][X] == -1) {
  789.             continue;           /* don't try the following tests if testpoints
  790.                                    are invalid, or if there are no
  791.                                    coordinates */
  792.     }
  793.  
  794.         if (!strcasecmp(directive, "poly")) {   /* poly */
  795.  
  796.             if (pointinpoly(testpoint, pointarray)) {
  797.         ap_cfg_closefile(imap);
  798.                 redirect = imap_url(r, base, value);
  799.         if (!redirect) {
  800.             return HTTP_INTERNAL_SERVER_ERROR;
  801.         }
  802.                 return (imap_reply(r, redirect));
  803.             }
  804.             continue;
  805.         }
  806.  
  807.         if (!strcasecmp(directive, "circle")) {         /* circle */
  808.  
  809.             if (pointincircle(testpoint, pointarray)) {
  810.         ap_cfg_closefile(imap);
  811.                 redirect = imap_url(r, base, value);
  812.         if (!redirect) {
  813.             return HTTP_INTERNAL_SERVER_ERROR;
  814.         }
  815.                 return (imap_reply(r, redirect));
  816.             }
  817.             continue;
  818.         }
  819.  
  820.         if (!strcasecmp(directive, "rect")) {   /* rect */
  821.  
  822.             if (pointinrect(testpoint, pointarray)) {
  823.         ap_cfg_closefile(imap);
  824.                 redirect = imap_url(r, base, value);
  825.         if (!redirect) {
  826.             return HTTP_INTERNAL_SERVER_ERROR;
  827.         }
  828.                 return (imap_reply(r, redirect));
  829.             }
  830.             continue;
  831.         }
  832.  
  833.         if (!strcasecmp(directive, "point")) {  /* point */
  834.  
  835.             if (is_closer(testpoint, pointarray, &closest_yet)) {
  836.         closest = ap_pstrdup(r->pool, value);
  837.             }
  838.  
  839.             continue;
  840.         }                       /* move on to next line whether it's
  841.                                    closest or not */
  842.  
  843.     }                           /* nothing matched, so we get another line! */
  844.  
  845.     ap_cfg_closefile(imap);        /* we are done with the map file; close it */
  846.  
  847.     if (showmenu) {
  848.         menu_footer(r);         /* finish the menu and we are done */
  849.         return OK;
  850.     }
  851.  
  852.     if (closest) {             /* if a 'point' directive has been seen */
  853.         redirect = imap_url(r, base, closest);
  854.     if (!redirect) {
  855.         return HTTP_INTERNAL_SERVER_ERROR;
  856.     }
  857.         return (imap_reply(r, redirect));
  858.     }
  859.  
  860.     if (mapdflt) {             /* a default should be defined, even if
  861.                                   only 'nocontent' */
  862.         redirect = imap_url(r, base, mapdflt);
  863.     if (!redirect) {
  864.         return HTTP_INTERNAL_SERVER_ERROR;
  865.     }
  866.         return (imap_reply(r, redirect));
  867.     }
  868.  
  869.     return HTTP_INTERNAL_SERVER_ERROR;        /* If we make it this far,
  870.                                                  we failed. They lose! */
  871.  
  872. need_2_fields:
  873.     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  874.         "map file %s, line %d syntax error: requires at "
  875.                 "least two fields", r->uri, imap->line_number);
  876.     /* fall through */
  877. menu_bail:
  878.     ap_cfg_closefile(imap);
  879.     if (showmenu) {
  880.     /* There's not much else we can do ... we've already sent the headers
  881.      * to the client.
  882.      */
  883.     ap_rputs("\n\n[an internal server error occured]\n", r);
  884.     menu_footer(r);
  885.     return OK;
  886.     }
  887.     return HTTP_INTERNAL_SERVER_ERROR;
  888. }
  889.  
  890.  
  891. static const handler_rec imap_handlers[] =
  892. {
  893.     {IMAP_MAGIC_TYPE, imap_handler},
  894.     {"imap-file", imap_handler},
  895.     {NULL}
  896. };
  897.  
  898. module MODULE_VAR_EXPORT imap_module =
  899. {
  900.     STANDARD_MODULE_STUFF,
  901.     NULL,                       /* initializer */
  902.     create_imap_dir_config,     /* dir config creater */
  903.     merge_imap_dir_configs,     /* dir merger --- default is to override */
  904.     NULL,                       /* server config */
  905.     NULL,                       /* merge server config */
  906.     imap_cmds,                  /* command table */
  907.     imap_handlers,              /* handlers */
  908.     NULL,                       /* filename translation */
  909.     NULL,                       /* check_user_id */
  910.     NULL,                       /* check auth */
  911.     NULL,                       /* check access */
  912.     NULL,                       /* type_checker */
  913.     NULL,                       /* fixups */
  914.     NULL,                       /* logger */
  915.     NULL,                       /* header parser */
  916.     NULL,                       /* child_init */
  917.     NULL,                       /* child_exit */
  918.     NULL                        /* post read-request */
  919. };
  920.