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 82module AP_MODULE_DECLARE_DATA imagemap_module; 83 84typedef struct { 85 char *imap_menu; 86 char *imap_default; 87 char *imap_base; 88} imap_conf_rec; 89 90static 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 102static 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 117static 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 133static 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 158static 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 174static 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 203static 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 225static 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 249static 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 */ 290static 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 */ 319static 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 458static 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 476static 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 493static 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 507static 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 522static 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 547static 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 572static void menu_footer(request_rec *r) 573{ 574 ap_rputs("\n\n</body>\n</html>\n", r); /* finish the menu */ 575} 576 577static 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 856need_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 */ 861menu_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 874static 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 889static void register_hooks(apr_pool_t *p) 890{ 891 ap_hook_handler(imap_handler,NULL,NULL,APR_HOOK_MIDDLE); 892} 893 894AP_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