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