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