1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
60 * This imagemap module started as a port of the original imagemap.c
61 * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
62 * This version includes the mapping algorithms found in version 1.3
65 * Contributors to this code include:
67 * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
69 * Eric Haines, erich@eye.com
70 * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
72 * Randy Terbush, randy@zyzzyva.com
73 * port to Apache module format, "base_uri" and support for relative URLs
75 * James H. Cloos, Jr., cloos@jhcloos.com
76 * Added point datatype, using code in NCSA's version 1.8 imagemap.c
77 * program, as distributed with version 1.4.1 of their server.
78 * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
80 * Nathan Kurz, nate@tripod.com
81 * Rewrite/reorganization. New handling of default, base and relative URLs.
82 * New Configuration directives:
83 * ImapMenu {none, formatted, semiformatted, unformatted}
84 * ImapDefault {error, nocontent, referer, menu, URL}
85 * ImapBase {map, referer, URL}
86 * Support for creating non-graphical menu added. (backwards compatible):
87 * Old: directive URL [x,y ...]
88 * New: directive URL "Menu text" [x,y ...]
89 * or: directive URL x,y ... "Menu text"
90 * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
92 * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
96 #include "apr_strings.h"
99 #include <stdio.h> /* for sscanf() */
102 #include "ap_config.h"
104 #include "http_config.h"
105 #include "http_request.h"
106 #include "http_core.h"
107 #include "http_protocol.h"
108 #include "http_main.h"
109 #include "http_log.h"
110 #include "util_script.h"
114 #ifdef HAVE_STRINGS_H
118 #define IMAP_MAGIC_TYPE "application/x-httpd-imap"
123 #define IMAP_MENU_DEFAULT "formatted"
124 #define IMAP_DEFAULT_DEFAULT "nocontent"
125 #define IMAP_BASE_DEFAULT "map"
128 double strtod(); /* SunOS needed this */
131 module AP_MODULE_DECLARE_DATA imap_module;
139 static void *create_imap_dir_config(apr_pool_t *p, char *dummy)
142 (imap_conf_rec *) apr_palloc(p, sizeof(imap_conf_rec));
144 icr->imap_menu = NULL;
145 icr->imap_default = NULL;
146 icr->imap_base = NULL;
151 static void *merge_imap_dir_configs(apr_pool_t *p, void *basev, void *addv)
153 imap_conf_rec *new = (imap_conf_rec *) apr_pcalloc(p, sizeof(imap_conf_rec));
154 imap_conf_rec *base = (imap_conf_rec *) basev;
155 imap_conf_rec *add = (imap_conf_rec *) addv;
157 new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
158 new->imap_default = add->imap_default ? add->imap_default
159 : base->imap_default;
160 new->imap_base = add->imap_base ? add->imap_base : base->imap_base;
166 static const command_rec imap_cmds[] =
168 AP_INIT_TAKE1("ImapMenu", ap_set_string_slot,
169 (void *) XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES,
170 "the type of menu generated: none, formatted, semiformatted, "
172 AP_INIT_TAKE1("ImapDefault", ap_set_string_slot,
173 (void *) XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES,
174 "the action taken if no match: error, nocontent, referer, "
176 AP_INIT_TAKE1("ImapBase", ap_set_string_slot,
177 (void *) XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES,
178 "the base for all URL's: map, referer, URL (or start of)"),
182 static int pointinrect(const double point[2], double coords[MAXVERTS][2])
184 double max[2], min[2];
185 if (coords[0][X] > coords[1][X]) {
186 max[0] = coords[0][X];
187 min[0] = coords[1][X];
190 max[0] = coords[1][X];
191 min[0] = coords[0][X];
194 if (coords[0][Y] > coords[1][Y]) {
195 max[1] = coords[0][Y];
196 min[1] = coords[1][Y];
199 max[1] = coords[1][Y];
200 min[1] = coords[0][Y];
203 return ((point[X] >= min[0] && point[X] <= max[0]) &&
204 (point[Y] >= min[1] && point[Y] <= max[1]));
207 static int pointincircle(const double point[2], double coords[MAXVERTS][2])
209 double radius1, radius2;
211 radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
212 + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
214 radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
215 + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
217 return (radius2 <= radius1);
220 #define fmin(a,b) (((a)>(b))?(b):(a))
221 #define fmax(a,b) (((a)>(b))?(a):(b))
223 static int pointinpoly(const double point[2], double pgon[MAXVERTS][2])
225 int i, numverts, crossings = 0;
226 double x = point[X], y = point[Y];
228 for (numverts = 0; pgon[numverts][X] != -1 && numverts < MAXVERTS;
230 /* just counting the vertexes */
233 for (i = 0; i < numverts; i++) {
234 double x1=pgon[i][X];
235 double y1=pgon[i][Y];
236 double x2=pgon[(i + 1) % numverts][X];
237 double y2=pgon[(i + 1) % numverts][Y];
238 double d=(y - y1) * (x2 - x1) - (x - x1) * (y2 - y1);
240 if ((y1 >= y) != (y2 >= y)) {
241 crossings +=y2 - y1 >= 0 ? d >= 0 : d <= 0;
243 if (!d && fmin(x1,x2) <= x && x <= fmax(x1,x2)
244 && fmin(y1,y2) <= y && y <= fmax(y1,y2)) {
248 return crossings & 0x01;
252 static int is_closer(const double point[2], double coords[MAXVERTS][2],
255 double dist_squared = ((point[X] - coords[0][X])
256 * (point[X] - coords[0][X]))
257 + ((point[Y] - coords[0][Y])
258 * (point[Y] - coords[0][Y]));
260 if (point[X] < 0 || point[Y] < 0) {
261 return (0); /* don't mess around with negative coordinates */
264 if (*closest < 0 || dist_squared < *closest) {
265 *closest = dist_squared;
266 return (1); /* if this is the first point or is the closest yet
267 set 'closest' equal to this distance^2 */
270 return (0); /* if it's not the first or closest */
274 static double get_x_coord(const char *args)
276 char *endptr; /* we want it non-null */
277 double x_coord = -1; /* -1 is returned if no coordinate is given */
280 return (-1); /* in case we aren't passed anything */
283 while (*args && !apr_isdigit(*args) && *args != ',') {
284 args++; /* jump to the first digit, but not past
288 x_coord = strtod(args, &endptr);
290 if (endptr > args) { /* if a conversion was made */
294 return (-1); /* else if no conversion was made,
295 or if no args was given */
298 static double get_y_coord(const char *args)
300 char *endptr; /* we want it non-null */
301 const char *start_of_y = NULL;
302 double y_coord = -1; /* -1 is returned on error */
305 return (-1); /* in case we aren't passed anything */
308 start_of_y = ap_strchr_c(args, ','); /* the comma */
312 start_of_y++; /* start looking at the character after
315 while (*start_of_y && !apr_isdigit(*start_of_y)) {
316 start_of_y++; /* jump to the first digit, but not
320 y_coord = strtod(start_of_y, &endptr);
322 if (endptr > start_of_y) {
327 return (-1); /* if no conversion was made, or
328 no comma was found in args */
332 /* See if string has a "quoted part", and if so set *quoted_part to
333 * the first character of the quoted part, then hammer a \0 onto the
334 * trailing quote, and set *string to point at the first character
335 * past the second quote.
337 * Otherwise set *quoted_part to NULL, and leave *string alone.
339 static void read_quoted(char **string, char **quoted_part)
341 char *strp = *string;
343 /* assume there's no quoted part */
346 while (apr_isspace(*strp)) {
347 strp++; /* go along string until non-whitespace */
350 if (*strp == '"') { /* if that character is a double quote */
351 strp++; /* step over it */
352 *quoted_part = strp; /* note where the quoted part begins */
354 while (*strp && *strp != '"') {
355 ++strp; /* skip the quoted portion */
358 *strp = '\0'; /* end the string with a NUL */
360 strp++; /* step over the last double quote */
366 * returns the mapped URL or NULL.
368 static char *imap_url(request_rec *r, const char *base, const char *value)
370 /* translates a value into a URL. */
372 char *string_pos = NULL;
373 const char *string_pos_const = NULL;
374 char *directory = NULL;
375 const char *referer = NULL;
378 if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
379 return ap_construct_url(r->pool, r->uri, r);
382 if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
383 return apr_pstrdup(r->pool, value); /* these are handled elsewhere,
387 if (!strcasecmp(value, "referer")) {
388 referer = apr_table_get(r->headers_in, "Referer");
389 if (referer && *referer) {
390 return apr_pstrdup(r->pool, referer);
393 /* XXX: This used to do *value = '\0'; ... which is totally bogus
394 * because it hammers the passed in value, which can be a string
395 * constant, or part of a config, or whatever. Total garbage.
396 * This works around that without changing the rest of this
399 value = ""; /* if 'referer' but no referring page,
404 string_pos_const = value;
405 while (apr_isalpha(*string_pos_const)) {
406 string_pos_const++; /* go along the URL from the map
407 until a non-letter */
409 if (*string_pos_const == ':') {
410 /* if letters and then a colon (like http:) */
411 /* it's an absolute URL, so use it! */
412 return apr_pstrdup(r->pool, value);
415 if (!base || !*base) {
416 if (value && *value) {
417 return apr_pstrdup(r->pool, value); /* no base: use what is given */
419 /* no base, no value: pick a simple default */
420 return ap_construct_url(r->pool, "/", r);
423 /* must be a relative URL to be combined with base */
424 if (ap_strchr_c(base, '/') == NULL && (!strncmp(value, "../", 3)
425 || !strcmp(value, ".."))) {
426 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
427 "invalid base directive in map file: %s", r->uri);
430 my_base = apr_pstrdup(r->pool, base);
431 string_pos = my_base;
432 while (*string_pos) {
433 if (*string_pos == '/' && *(string_pos + 1) == '/') {
434 string_pos += 2; /* if there are two slashes, jump over them */
437 if (*string_pos == '/') { /* the first single slash */
438 if (value[0] == '/') {
440 } /* if the URL from the map starts from root,
441 end the base URL string at the first single
444 directory = string_pos; /* save the start of
445 the directory portion */
447 string_pos = strrchr(string_pos, '/'); /* now reuse
449 string_pos++; /* step over that last slash */
451 } /* but if the map url is relative, leave the
452 slash on the base (if there is one) */
455 string_pos++; /* until we get to the end of my_base without
456 finding a slash by itself */
459 while (!strncmp(value, "../", 3) || !strcmp(value, "..")) {
461 if (directory && (slen = strlen(directory))) {
463 /* for each '..', knock a directory off the end
464 by ending the string right at the last slash.
465 But only consider the directory portion: don't eat
466 into the server name. And only try if a directory
471 while ((slen - clen) == 1) {
473 if ((string_pos = strrchr(directory, '/'))) {
476 clen = strlen(directory);
482 value += 2; /* jump over the '..' that we found in the
485 else if (directory) {
486 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
487 "invalid directory name in map file: %s", r->uri);
491 if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) {
492 value++; /* step over the '/' if there are more '..'
493 to do. This way, we leave the starting
494 '/' on value after the last '..', but get
495 rid of it otherwise */
498 } /* by this point, value does not start
501 if (value && *value) {
502 return apr_pstrcat(r->pool, my_base, value, NULL);
507 static int imap_reply(request_rec *r, char *redirect)
509 if (!strcasecmp(redirect, "error")) {
510 /* they actually requested an error! */
511 return HTTP_INTERNAL_SERVER_ERROR;
513 if (!strcasecmp(redirect, "nocontent")) {
514 /* tell the client to keep the page it has */
515 return HTTP_NO_CONTENT;
517 if (redirect && *redirect) {
518 /* must be a URL, so redirect to it */
519 apr_table_setn(r->headers_out, "Location", redirect);
520 return HTTP_MOVED_TEMPORARILY;
522 return HTTP_INTERNAL_SERVER_ERROR;
525 static void menu_header(request_rec *r, char *menu)
527 r->content_type = "text/html";
528 ap_send_http_header(r);
531 ap_rvputs(r, DOCTYPE_HTML_3_2, "<html><head>\n<title>Menu for ", r->uri,
532 "</title>\n</head><body>\n", NULL);
534 if (!strcasecmp(menu, "formatted")) {
535 ap_rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL);
541 static void menu_blank(request_rec *r, char *menu)
543 if (!strcasecmp(menu, "formatted")) {
546 if (!strcasecmp(menu, "semiformatted")) {
547 ap_rputs("<br>\n", r);
549 if (!strcasecmp(menu, "unformatted")) {
555 static void menu_comment(request_rec *r, char *menu, char *comment)
557 if (!strcasecmp(menu, "formatted")) {
558 ap_rputs("\n", r); /* print just a newline if 'formatted' */
560 if (!strcasecmp(menu, "semiformatted") && *comment) {
561 ap_rvputs(r, comment, "\n", NULL);
563 if (!strcasecmp(menu, "unformatted") && *comment) {
564 ap_rvputs(r, comment, "\n", NULL);
566 return; /* comments are ignored in the
570 static void menu_default(request_rec *r, char *menu, char *href, char *text)
572 if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
573 return; /* don't print such lines, these aren't
576 if (!strcasecmp(menu, "formatted")) {
577 ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
578 "</a></pre>\n", NULL);
580 if (!strcasecmp(menu, "semiformatted")) {
581 ap_rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text,
582 "</a></pre>\n", NULL);
584 if (!strcasecmp(menu, "unformatted")) {
585 ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
590 static void menu_directive(request_rec *r, char *menu, char *href, char *text)
592 if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
593 return; /* don't print such lines, as this isn't
596 if (!strcasecmp(menu, "formatted")) {
597 ap_rvputs(r, "<pre> <a href=\"", href, "\">", text,
598 "</a></pre>\n", NULL);
600 if (!strcasecmp(menu, "semiformatted")) {
601 ap_rvputs(r, "<pre> <a href=\"", href, "\">", text,
602 "</a></pre>\n", NULL);
604 if (!strcasecmp(menu, "unformatted")) {
605 ap_rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL);
610 static void menu_footer(request_rec *r)
612 ap_rputs("\n\n</body>\n</html>\n", r); /* finish the menu */
615 static int imap_handler(const char *handler,request_rec *r)
617 char input[MAX_STRING_LEN];
624 char *closest = NULL;
625 double closest_yet = -1;
629 double pointarray[MAXVERTS + 1][2];
643 if (r->method_number != M_GET || (strcmp(handler,IMAP_MAGIC_TYPE)
644 && strcmp(handler, "imap-file")))
647 icr = ap_get_module_config(r->per_dir_config, &imap_module);
649 imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT;
650 imap_default = icr->imap_default
651 ? icr->imap_default : IMAP_DEFAULT_DEFAULT;
652 imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT;
654 status = ap_pcfg_openfile(&imap, r->pool, r->filename);
656 if (status != APR_SUCCESS) {
657 return HTTP_NOT_FOUND;
660 base = imap_url(r, NULL, imap_base); /* set base according
663 return HTTP_INTERNAL_SERVER_ERROR;
665 mapdflt = imap_url(r, NULL, imap_default); /* and default to
668 return HTTP_INTERNAL_SERVER_ERROR;
671 testpoint[X] = get_x_coord(r->args);
672 testpoint[Y] = get_y_coord(r->args);
674 if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
675 (testpoint[X] == 0 && testpoint[Y] == 0)) {
676 /* if either is -1 or if both are zero (new Lynx) */
677 /* we don't have valid coordinates */
680 if (strncasecmp(imap_menu, "none", 2)) {
681 showmenu = 1; /* show the menu _unless_ ImapMenu is
686 if (showmenu) { /* send start of imagemap menu if
688 menu_header(r, imap_menu);
691 while (!ap_cfg_getline(input, sizeof(input), imap)) {
694 menu_blank(r, imap_menu);
699 if (input[0] == '#') {
701 menu_comment(r, imap_menu, input + 1);
704 } /* blank lines and comments are ignored
705 if we aren't printing a menu */
707 /* find the first two space delimited fields, recall that
708 * ap_cfg_getline has removed leading/trailing whitespace.
710 * note that we're tokenizing as we go... if we were to use the
711 * ap_getword() class of functions we would end up allocating extra
712 * memory for every line of the map file
715 if (!*string_pos) { /* need at least two fields */
719 directive = string_pos;
720 while (*string_pos && !apr_isspace(*string_pos)) { /* past directive */
723 if (!*string_pos) { /* need at least two fields */
726 *string_pos++ = '\0';
728 if (!*string_pos) { /* need at least two fields */
731 while(*string_pos && apr_isspace(*string_pos)) { /* past whitespace */
736 while (*string_pos && !apr_isspace(*string_pos)) { /* past value */
739 if (apr_isspace(*string_pos)) {
740 *string_pos++ = '\0';
743 /* end of input, don't advance past it */
747 if (!strncasecmp(directive, "base", 4)) { /* base, base_uri */
748 base = imap_url(r, NULL, value);
752 continue; /* base is never printed to a menu */
755 read_quoted(&string_pos, &href_text);
757 if (!strcasecmp(directive, "default")) { /* default */
758 mapdflt = imap_url(r, NULL, value);
762 if (showmenu) { /* print the default if there's a menu */
763 redirect = imap_url(r, base, mapdflt);
767 menu_default(r, imap_menu, redirect,
768 href_text ? href_text : mapdflt);
774 while (vertex < MAXVERTS &&
775 sscanf(string_pos, "%lf%*[, ]%lf",
776 &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) {
777 /* Now skip what we just read... we can't use ANSIism %n */
778 while (apr_isspace(*string_pos)) { /* past whitespace */
781 while (apr_isdigit(*string_pos)) { /* and the 1st number */
784 string_pos++; /* skip the ',' */
785 while (apr_isspace(*string_pos)) { /* past any more whitespace */
788 while (apr_isdigit(*string_pos)) { /* 2nd number */
792 } /* so long as there are more vertices to
793 read, and we have room, read them in.
794 We start where we left off of the last
795 sscanf, not at the beginning. */
797 pointarray[vertex][X] = -1; /* signals the end of vertices */
801 read_quoted(&string_pos, &href_text); /* href text could
804 redirect = imap_url(r, base, value);
808 menu_directive(r, imap_menu, redirect,
809 href_text ? href_text : value);
812 /* note that we don't make it past here if we are making a menu */
814 if (testpoint[X] == -1 || pointarray[0][X] == -1) {
815 continue; /* don't try the following tests if testpoints
816 are invalid, or if there are no
820 if (!strcasecmp(directive, "poly")) { /* poly */
822 if (pointinpoly(testpoint, pointarray)) {
823 ap_cfg_closefile(imap);
824 redirect = imap_url(r, base, value);
826 return HTTP_INTERNAL_SERVER_ERROR;
828 return (imap_reply(r, redirect));
833 if (!strcasecmp(directive, "circle")) { /* circle */
835 if (pointincircle(testpoint, pointarray)) {
836 ap_cfg_closefile(imap);
837 redirect = imap_url(r, base, value);
839 return HTTP_INTERNAL_SERVER_ERROR;
841 return (imap_reply(r, redirect));
846 if (!strcasecmp(directive, "rect")) { /* rect */
848 if (pointinrect(testpoint, pointarray)) {
849 ap_cfg_closefile(imap);
850 redirect = imap_url(r, base, value);
852 return HTTP_INTERNAL_SERVER_ERROR;
854 return (imap_reply(r, redirect));
859 if (!strcasecmp(directive, "point")) { /* point */
861 if (is_closer(testpoint, pointarray, &closest_yet)) {
862 closest = apr_pstrdup(r->pool, value);
866 } /* move on to next line whether it's
869 } /* nothing matched, so we get another line! */
871 ap_cfg_closefile(imap); /* we are done with the map file; close it */
874 menu_footer(r); /* finish the menu and we are done */
878 if (closest) { /* if a 'point' directive has been seen */
879 redirect = imap_url(r, base, closest);
881 return HTTP_INTERNAL_SERVER_ERROR;
883 return (imap_reply(r, redirect));
886 if (mapdflt) { /* a default should be defined, even if
888 redirect = imap_url(r, base, mapdflt);
890 return HTTP_INTERNAL_SERVER_ERROR;
892 return (imap_reply(r, redirect));
895 return HTTP_INTERNAL_SERVER_ERROR; /* If we make it this far,
896 we failed. They lose! */
899 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
900 "map file %s, line %d syntax error: requires at "
901 "least two fields", r->uri, imap->line_number);
904 ap_cfg_closefile(imap);
906 /* There's not much else we can do ... we've already sent the headers
909 ap_rputs("\n\n[an internal server error occured]\n", r);
913 return HTTP_INTERNAL_SERVER_ERROR;
916 static void register_hooks(void)
918 ap_hook_handler(imap_handler,NULL,NULL,AP_HOOK_MIDDLE);
921 module AP_MODULE_DECLARE_DATA imap_module =
923 STANDARD20_MODULE_STUFF,
924 create_imap_dir_config, /* dir config creater */
925 merge_imap_dir_configs, /* dir merger --- default is to override */
926 NULL, /* server config */
927 NULL, /* merge server config */
928 imap_cmds, /* command apr_table_t */
929 register_hooks /* register hooks */