]> granicus.if.org Git - apache/blob - modules/mappers/mod_imap.c
This is a huge change to the configure system. Basically, this name space
[apache] / modules / mappers / mod_imap.c
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 #include <string.h>
103
104 #define IMAP_MAGIC_TYPE "application/x-httpd-imap"
105 #define MAXVERTS 100
106 #define X 0
107 #define Y 1
108
109 #define IMAP_MENU_DEFAULT "formatted"
110 #define IMAP_DEFAULT_DEFAULT "nocontent"
111 #define IMAP_BASE_DEFAULT "map"
112
113 #ifdef SUNOS4
114 double strtod();                /* SunOS needed this */
115 #endif
116
117 module MODULE_VAR_EXPORT imap_module;
118
119 typedef struct {
120     char *imap_menu;
121     char *imap_default;
122     char *imap_base;
123 } imap_conf_rec;
124
125 static void *create_imap_dir_config(ap_context_t *p, char *dummy)
126 {
127     imap_conf_rec *icr =
128     (imap_conf_rec *) ap_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 static void *merge_imap_dir_configs(ap_context_t *p, void *basev, void *addv)
138 {
139     imap_conf_rec *new = (imap_conf_rec *) ap_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
145                                           : base->imap_default;
146     new->imap_base = add->imap_base ? add->imap_base : base->imap_base;
147
148     return new;
149 }
150
151
152 static const command_rec imap_cmds[] =
153 {
154     {"ImapMenu", ap_set_string_slot,
155      (void *) XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1,
156  "the type of menu generated: none, formatted, semiformatted, unformatted"},
157     {"ImapDefault", ap_set_string_slot,
158      (void *) XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1,
159      "the action taken if no match: error, nocontent, referer, menu, URL"},
160     {"ImapBase", ap_set_string_slot,
161      (void *) XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1,
162      "the base for all URL's: map, referer, URL (or start of)"},
163     {NULL}
164 };
165
166 static int pointinrect(const double point[2], double coords[MAXVERTS][2])
167 {
168     double max[2], min[2];
169     if (coords[0][X] > coords[1][X]) {
170         max[0] = coords[0][X];
171         min[0] = coords[1][X];
172     }
173     else {
174         max[0] = coords[1][X];
175         min[0] = coords[0][X];
176     }
177
178     if (coords[0][Y] > coords[1][Y]) {
179         max[1] = coords[0][Y];
180         min[1] = coords[1][Y];
181     }
182     else {
183         max[1] = coords[1][Y];
184         min[1] = coords[0][Y];
185     }
186
187     return ((point[X] >= min[0] && point[X] <= max[0]) &&
188             (point[Y] >= min[1] && point[Y] <= max[1]));
189 }
190
191 static int pointincircle(const double point[2], double coords[MAXVERTS][2])
192 {
193     double radius1, radius2;
194
195     radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
196         + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
197
198     radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
199         + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
200
201     return (radius2 <= radius1);
202 }
203
204 #define fmin(a,b) (((a)>(b))?(b):(a))
205 #define fmax(a,b) (((a)>(b))?(a):(b))
206
207 static int pointinpoly(const double point[2], double pgon[MAXVERTS][2])
208 {
209     int i, numverts, crossings = 0;
210     double x = point[X], y = point[Y];
211
212     for (numverts = 0; pgon[numverts][X] != -1 && numverts < MAXVERTS;
213         numverts++) {
214         /* just counting the vertexes */
215     }
216
217     for (i = 0; i < numverts; i++) {
218         double x1=pgon[i][X];
219         double y1=pgon[i][Y];
220         double x2=pgon[(i + 1) % numverts][X];
221         double y2=pgon[(i + 1) % numverts][Y];
222         double d=(y - y1) * (x2 - x1) - (x - x1) * (y2 - y1);
223
224         if ((y1 >= y) != (y2 >= y)) {
225             crossings +=y2 - y1 >= 0 ? d >= 0 : d <= 0;
226         }
227         if (!d && fmin(x1,x2) <= x && x <= fmax(x1,x2)
228             && fmin(y1,y2) <= y && y <= fmax(y1,y2)) {
229             return 1;
230         }
231     }
232     return crossings & 0x01;
233 }
234
235
236 static int is_closer(const double point[2], double coords[MAXVERTS][2],
237                      double *closest)
238 {
239     double dist_squared = ((point[X] - coords[0][X])
240                            * (point[X] - coords[0][X]))
241                           + ((point[Y] - coords[0][Y])
242                              * (point[Y] - coords[0][Y]));
243
244     if (point[X] < 0 || point[Y] < 0) {
245         return (0);          /* don't mess around with negative coordinates */
246     }
247
248     if (*closest < 0 || dist_squared < *closest) {
249         *closest = dist_squared;
250         return (1);          /* if this is the first point or is the closest yet
251                                 set 'closest' equal to this distance^2 */
252     }
253
254     return (0);              /* if it's not the first or closest */
255
256 }
257
258 static double get_x_coord(const char *args)
259 {
260     char *endptr;               /* we want it non-null */
261     double x_coord = -1;        /* -1 is returned if no coordinate is given */
262
263     if (args == NULL) {
264         return (-1);            /* in case we aren't passed anything */
265     }
266
267     while (*args && !ap_isdigit(*args) && *args != ',') {
268         args++;                 /* jump to the first digit, but not past
269                                    a comma or end */
270     }
271
272     x_coord = strtod(args, &endptr);
273
274     if (endptr > args) {        /* if a conversion was made */
275         return (x_coord);
276     }
277
278     return (-1);                /* else if no conversion was made,
279                                    or if no args was given */
280 }
281
282 static double get_y_coord(const char *args)
283 {
284     char *endptr;               /* we want it non-null */
285     char *start_of_y = NULL;
286     double y_coord = -1;        /* -1 is returned on error */
287
288     if (args == NULL) {
289         return (-1);            /* in case we aren't passed anything */
290     }
291
292     start_of_y = strchr(args, ',');     /* the comma */
293
294     if (start_of_y) {
295
296         start_of_y++;           /* start looking at the character after
297                                    the comma */
298
299         while (*start_of_y && !ap_isdigit(*start_of_y)) {
300             start_of_y++;       /* jump to the first digit, but not
301                                    past the end */
302         }
303
304         y_coord = strtod(start_of_y, &endptr);
305
306         if (endptr > start_of_y) {
307             return (y_coord);
308         }
309     }
310
311     return (-1);                /* if no conversion was made, or
312                                    no comma was found in args */
313 }
314
315
316 /* See if string has a "quoted part", and if so set *quoted_part to
317  * the first character of the quoted part, then hammer a \0 onto the
318  * trailing quote, and set *string to point at the first character
319  * past the second quote.
320  *
321  * Otherwise set *quoted_part to NULL, and leave *string alone.
322  */
323 static void read_quoted(char **string, char **quoted_part)
324 {
325     char *strp = *string;
326
327     /* assume there's no quoted part */
328     *quoted_part = NULL;
329
330     while (ap_isspace(*strp)) {
331         strp++;                 /* go along string until non-whitespace */
332     }
333
334     if (*strp == '"') {         /* if that character is a double quote */
335         strp++;                 /* step over it */
336         *quoted_part = strp;    /* note where the quoted part begins */
337
338         while (*strp && *strp != '"') {
339             ++strp;             /* skip the quoted portion */
340         }
341
342         *strp = '\0';           /* end the string with a NUL */
343
344         strp++;                 /* step over the last double quote */
345         *string = strp;
346     }
347 }
348
349 /*
350  * returns the mapped URL or NULL.
351  */
352 static char *imap_url(request_rec *r, const char *base, const char *value)
353 {
354 /* translates a value into a URL. */
355     int slen, clen;
356     char *string_pos = NULL;
357     const char *string_pos_const = NULL;
358     char *directory = NULL;
359     const char *referer = NULL;
360     char *my_base;
361
362     if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
363         return ap_construct_url(r->pool, r->uri, r);
364     }
365
366     if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
367         return ap_pstrdup(r->pool, value);      /* these are handled elsewhere,
368                                                 so just copy them */
369     }
370
371     if (!strcasecmp(value, "referer")) {
372         referer = ap_table_get(r->headers_in, "Referer");
373         if (referer && *referer) {
374             return ap_pstrdup(r->pool, referer);
375         }
376         else {
377             /* XXX:  This used to do *value = '\0'; ... which is totally bogus
378              * because it hammers the passed in value, which can be a string
379              * constant, or part of a config, or whatever.  Total garbage.
380              * This works around that without changing the rest of this
381              * code much
382              */
383             value = "";      /* if 'referer' but no referring page,
384                                 null the value */
385         }
386     }
387
388     string_pos_const = value;
389     while (ap_isalpha(*string_pos_const)) {
390         string_pos_const++;           /* go along the URL from the map
391                                          until a non-letter */
392     }
393     if (*string_pos_const == ':') {
394         /* if letters and then a colon (like http:) */
395         /* it's an absolute URL, so use it! */
396         return ap_pstrdup(r->pool, value);
397     }
398
399     if (!base || !*base) {
400         if (value && *value) {
401             return ap_pstrdup(r->pool, value); /* no base: use what is given */
402         }
403         /* no base, no value: pick a simple default */
404         return ap_construct_url(r->pool, "/", r);
405     }
406
407     /* must be a relative URL to be combined with base */
408     if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3)
409         || !strcmp(value, ".."))) {
410         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
411                     "invalid base directive in map file: %s", r->uri);
412         return NULL;
413     }
414     my_base = ap_pstrdup(r->pool, base);
415     string_pos = my_base;
416     while (*string_pos) {
417         if (*string_pos == '/' && *(string_pos + 1) == '/') {
418             string_pos += 2;    /* if there are two slashes, jump over them */
419             continue;
420         }
421         if (*string_pos == '/') {       /* the first single slash */
422             if (value[0] == '/') {
423                 *string_pos = '\0';
424             }                   /* if the URL from the map starts from root,
425                                    end the base URL string at the first single
426                                    slash */
427             else {
428                 directory = string_pos;         /* save the start of
429                                                    the directory portion */
430
431                 string_pos = strrchr(string_pos, '/');  /* now reuse
432                                                            string_pos */
433                 string_pos++;   /* step over that last slash */
434                 *string_pos = '\0';
435             }                   /* but if the map url is relative, leave the
436                                    slash on the base (if there is one) */
437             break;
438         }
439         string_pos++;           /* until we get to the end of my_base without
440                                    finding a slash by itself */
441     }
442
443     while (!strncmp(value, "../", 3) || !strcmp(value, "..")) {
444
445         if (directory && (slen = strlen(directory))) {
446
447             /* for each '..',  knock a directory off the end 
448                by ending the string right at the last slash.
449                But only consider the directory portion: don't eat
450                into the server name.  And only try if a directory
451                portion was found */
452
453             clen = slen - 1;
454
455             while ((slen - clen) == 1) {
456
457                 if ((string_pos = strrchr(directory, '/'))) {
458                     *string_pos = '\0';
459                 }
460                 clen = strlen(directory);
461                 if (clen == 0) {
462                     break;
463                 }
464             }
465
466             value += 2;         /* jump over the '..' that we found in the
467                                    value */
468         }
469         else if (directory) {
470             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
471                         "invalid directory name in map file: %s", r->uri);
472             return NULL;
473         }
474
475         if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) {
476             value++;            /* step over the '/' if there are more '..'
477                                    to do.  This way, we leave the starting
478                                    '/' on value after the last '..', but get
479                                    rid of it otherwise */
480         }
481
482     }                           /* by this point, value does not start
483                                    with '..' */
484
485     if (value && *value) {
486         return ap_pstrcat(r->pool, my_base, value, NULL);
487     }
488     return my_base;
489 }
490
491 static int imap_reply(request_rec *r, char *redirect)
492 {
493     if (!strcasecmp(redirect, "error")) {
494         return SERVER_ERROR;    /* they actually requested an error! */
495     }
496     if (!strcasecmp(redirect, "nocontent")) {
497         return HTTP_NO_CONTENT; /* tell the client to keep the page it has */
498     }
499     if (redirect && *redirect) {
500         ap_table_setn(r->headers_out, "Location", redirect);
501         return REDIRECT;        /* must be a URL, so redirect to it */
502     }
503     return SERVER_ERROR;
504 }
505
506 static void menu_header(request_rec *r, char *menu)
507 {
508     r->content_type = "text/html";
509     ap_send_http_header(r);
510
511
512     ap_rvputs(r, DOCTYPE_HTML_3_2, "<html><head>\n<title>Menu for ", r->uri,
513            "</title>\n</head><body>\n", NULL);
514
515     if (!strcasecmp(menu, "formatted")) {
516         ap_rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
517     }
518
519     return;
520 }
521
522 static void menu_blank(request_rec *r, char *menu)
523 {
524     if (!strcasecmp(menu, "formatted")) {
525         ap_rputs("\n", r);
526     }
527     if (!strcasecmp(menu, "semiformatted")) {
528         ap_rputs("<br>\n", r);
529     }
530     if (!strcasecmp(menu, "unformatted")) {
531         ap_rputs("\n", r);
532     }
533     return;
534 }
535
536 static void menu_comment(request_rec *r, char *menu, char *comment)
537 {
538     if (!strcasecmp(menu, "formatted")) {
539         ap_rputs("\n", r);         /* print just a newline if 'formatted' */
540     }
541     if (!strcasecmp(menu, "semiformatted") && *comment) {
542         ap_rvputs(r, comment, "\n", NULL);
543     }
544     if (!strcasecmp(menu, "unformatted") && *comment) {
545         ap_rvputs(r, comment, "\n", NULL);
546     }
547     return;                     /* comments are ignored in the
548                                    'formatted' form */
549 }
550
551 static void menu_default(request_rec *r, char *menu, char *href, char *text)
552 {
553     if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
554         return;                 /* don't print such lines, these aren't
555                                    really href's */
556     }
557     if (!strcasecmp(menu, "formatted")) {
558         ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
559                "</a></pre>\n", NULL);
560     }
561     if (!strcasecmp(menu, "semiformatted")) {
562         ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
563                "</a></pre>\n", NULL);
564     }
565     if (!strcasecmp(menu, "unformatted")) {
566         ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
567     }
568     return;
569 }
570
571 static void menu_directive(request_rec *r, char *menu, char *href, char *text)
572 {
573     if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
574         return;                 /* don't print such lines, as this isn't
575                                    really an href */
576     }
577     if (!strcasecmp(menu, "formatted")) {
578         ap_rvputs(r, "<pre>          <a href=\"", href, "\">", text,
579                "</a></pre>\n", NULL);
580     }
581     if (!strcasecmp(menu, "semiformatted")) {
582         ap_rvputs(r, "<pre>          <a href=\"", href, "\">", text,
583                "</a></pre>\n", NULL);
584     }
585     if (!strcasecmp(menu, "unformatted")) {
586         ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
587     }
588     return;
589 }
590
591 static void menu_footer(request_rec *r)
592 {
593     ap_rputs("\n\n</body>\n</html>\n", r);         /* finish the menu */
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     ap_status_t status;
608
609     double testpoint[2];
610     double pointarray[MAXVERTS + 1][2];
611     int vertex;
612
613     char *string_pos;
614     int showmenu = 0;
615
616     imap_conf_rec *icr = ap_get_module_config(r->per_dir_config, &imap_module);
617
618     char *imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT;
619     char *imap_default = icr->imap_default
620                             ?  icr->imap_default : IMAP_DEFAULT_DEFAULT;
621     char *imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT;
622
623     configfile_t *imap; 
624
625     if (r->method_number != M_GET) {
626         return DECLINED;
627     }
628
629     status = ap_pcfg_openfile(&imap, r->pool, r->filename);
630
631     if (status != APR_SUCCESS) {
632         return NOT_FOUND;
633     }
634
635     base = imap_url(r, NULL, imap_base);         /* set base according
636                                                     to default */
637     if (!base) {
638         return HTTP_INTERNAL_SERVER_ERROR;
639     }
640     mapdflt = imap_url(r, NULL, imap_default);   /* and default to
641                                                     global default */
642     if (!mapdflt) {
643         return HTTP_INTERNAL_SERVER_ERROR;
644     }
645
646     testpoint[X] = get_x_coord(r->args);
647     testpoint[Y] = get_y_coord(r->args);
648
649     if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
650         (testpoint[X] == 0 && testpoint[Y] == 0)) {
651         /* if either is -1 or if both are zero (new Lynx) */
652         /* we don't have valid coordinates */
653         testpoint[X] = -1;
654         testpoint[Y] = -1;
655         if (strncasecmp(imap_menu, "none", 2)) {
656             showmenu = 1;       /* show the menu _unless_ ImapMenu is
657                                    'none' or 'no' */
658         }
659     }
660
661     if (showmenu) {             /* send start of imagemap menu if
662                                    we're going to */
663         menu_header(r, imap_menu);
664     }
665
666     while (!ap_cfg_getline(input, sizeof(input), imap)) {
667         if (!input[0]) {
668             if (showmenu) {
669                 menu_blank(r, imap_menu);
670             }
671             continue;
672         }
673
674         if (input[0] == '#') {
675             if (showmenu) {
676                 menu_comment(r, imap_menu, input + 1);
677             }
678             continue;
679         }                       /* blank lines and comments are ignored
680                                    if we aren't printing a menu */
681
682         /* find the first two space delimited fields, recall that
683          * ap_cfg_getline has removed leading/trailing whitespace.
684          *
685          * note that we're tokenizing as we go... if we were to use the
686          * ap_getword() class of functions we would end up allocating extra
687          * memory for every line of the map file
688          */
689         string_pos = input;
690         if (!*string_pos) {             /* need at least two fields */
691             goto need_2_fields;
692         }
693
694         directive = string_pos;
695         while (*string_pos && !ap_isspace(*string_pos)) {       /* past directive */
696             ++string_pos;
697         }
698         if (!*string_pos) {             /* need at least two fields */
699             goto need_2_fields;
700         }
701         *string_pos++ = '\0';
702
703         if (!*string_pos) {             /* need at least two fields */
704             goto need_2_fields;
705         }
706         while(*string_pos && ap_isspace(*string_pos)) { /* past whitespace */
707             ++string_pos;
708         }
709
710         value = string_pos;
711         while (*string_pos && !ap_isspace(*string_pos)) {       /* past value */
712             ++string_pos;
713         }
714         if (ap_isspace(*string_pos)) {
715             *string_pos++ = '\0';
716         }
717         else {
718             /* end of input, don't advance past it */
719             *string_pos = '\0';
720         }
721
722         if (!strncasecmp(directive, "base", 4)) {       /* base, base_uri */
723             base = imap_url(r, NULL, value);
724             if (!base) {
725                 goto menu_bail;
726             }
727             continue;           /* base is never printed to a menu */
728         }
729
730         read_quoted(&string_pos, &href_text);
731
732         if (!strcasecmp(directive, "default")) {        /* default */
733             mapdflt = imap_url(r, NULL, value);
734             if (!mapdflt) {
735                 goto menu_bail;
736             }
737             if (showmenu) {     /* print the default if there's a menu */
738                 redirect = imap_url(r, base, mapdflt);
739                 if (!redirect) {
740                     goto menu_bail;
741                 }
742                 menu_default(r, imap_menu, redirect,
743                              href_text ? href_text : mapdflt);
744             }
745             continue;
746         }
747
748         vertex = 0;
749         while (vertex < MAXVERTS &&
750                sscanf(string_pos, "%lf%*[, ]%lf",
751                       &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) {
752             /* Now skip what we just read... we can't use ANSIism %n */
753             while (ap_isspace(*string_pos)) {      /* past whitespace */
754                 string_pos++;
755             }
756             while (ap_isdigit(*string_pos)) {      /* and the 1st number */
757                 string_pos++;
758             }
759             string_pos++;       /* skip the ',' */
760             while (ap_isspace(*string_pos)) {      /* past any more whitespace */
761                 string_pos++;
762             }
763             while (ap_isdigit(*string_pos)) {      /* 2nd number */
764                 string_pos++;
765             }
766             vertex++;
767         }                       /* so long as there are more vertices to
768                                    read, and we have room, read them in.
769                                    We start where we left off of the last
770                                    sscanf, not at the beginning. */
771
772         pointarray[vertex][X] = -1;     /* signals the end of vertices */
773
774         if (showmenu) {
775             if (!href_text) {
776                 read_quoted(&string_pos, &href_text);     /* href text could
777                                                              be here instead */
778             }
779             redirect = imap_url(r, base, value);
780             if (!redirect) {
781                 goto menu_bail;
782             }
783             menu_directive(r, imap_menu, redirect,
784                            href_text ? href_text : value);
785             continue;
786         }
787         /* note that we don't make it past here if we are making a menu */
788
789         if (testpoint[X] == -1 || pointarray[0][X] == -1) {
790             continue;           /* don't try the following tests if testpoints
791                                    are invalid, or if there are no
792                                    coordinates */
793         }
794
795         if (!strcasecmp(directive, "poly")) {   /* poly */
796
797             if (pointinpoly(testpoint, pointarray)) {
798                 ap_cfg_closefile(imap);
799                 redirect = imap_url(r, base, value);
800                 if (!redirect) {
801                     return HTTP_INTERNAL_SERVER_ERROR;
802                 }
803                 return (imap_reply(r, redirect));
804             }
805             continue;
806         }
807
808         if (!strcasecmp(directive, "circle")) {         /* circle */
809
810             if (pointincircle(testpoint, pointarray)) {
811                 ap_cfg_closefile(imap);
812                 redirect = imap_url(r, base, value);
813                 if (!redirect) {
814                     return HTTP_INTERNAL_SERVER_ERROR;
815                 }
816                 return (imap_reply(r, redirect));
817             }
818             continue;
819         }
820
821         if (!strcasecmp(directive, "rect")) {   /* rect */
822
823             if (pointinrect(testpoint, pointarray)) {
824                 ap_cfg_closefile(imap);
825                 redirect = imap_url(r, base, value);
826                 if (!redirect) {
827                     return HTTP_INTERNAL_SERVER_ERROR;
828                 }
829                 return (imap_reply(r, redirect));
830             }
831             continue;
832         }
833
834         if (!strcasecmp(directive, "point")) {  /* point */
835
836             if (is_closer(testpoint, pointarray, &closest_yet)) {
837                 closest = ap_pstrdup(r->pool, value);
838             }
839
840             continue;
841         }                       /* move on to next line whether it's
842                                    closest or not */
843
844     }                           /* nothing matched, so we get another line! */
845
846     ap_cfg_closefile(imap);        /* we are done with the map file; close it */
847
848     if (showmenu) {
849         menu_footer(r);         /* finish the menu and we are done */
850         return OK;
851     }
852
853     if (closest) {             /* if a 'point' directive has been seen */
854         redirect = imap_url(r, base, closest);
855         if (!redirect) {
856             return HTTP_INTERNAL_SERVER_ERROR;
857         }
858         return (imap_reply(r, redirect));
859     }
860
861     if (mapdflt) {             /* a default should be defined, even if
862                                   only 'nocontent' */
863         redirect = imap_url(r, base, mapdflt);
864         if (!redirect) {
865             return HTTP_INTERNAL_SERVER_ERROR;
866         }
867         return (imap_reply(r, redirect));
868     }
869
870     return HTTP_INTERNAL_SERVER_ERROR;        /* If we make it this far,
871                                                  we failed. They lose! */
872
873 need_2_fields:
874     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
875                 "map file %s, line %d syntax error: requires at "
876                 "least two fields", r->uri, imap->line_number);
877     /* fall through */
878 menu_bail:
879     ap_cfg_closefile(imap);
880     if (showmenu) {
881         /* There's not much else we can do ... we've already sent the headers
882          * to the client.
883          */
884         ap_rputs("\n\n[an internal server error occured]\n", r);
885         menu_footer(r);
886         return OK;
887     }
888     return HTTP_INTERNAL_SERVER_ERROR;
889 }
890
891
892 static const handler_rec imap_handlers[] =
893 {
894     {IMAP_MAGIC_TYPE, imap_handler},
895     {"imap-file", imap_handler},
896     {NULL}
897 };
898
899 module MODULE_VAR_EXPORT imap_module =
900 {
901     STANDARD20_MODULE_STUFF,
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 ap_table_t */
907     imap_handlers,              /* handlers */
908     NULL                        /* register hooks */
909 };