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