Blame modules/mappers/mod_imagemap.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * This imagemap module started as a port of the original imagemap.c
Packit 90a5c9
 * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
Packit 90a5c9
 * This version includes the mapping algorithms found in version 1.3
Packit 90a5c9
 * of imagemap.c.
Packit 90a5c9
 *
Packit 90a5c9
 * Contributors to this code include:
Packit 90a5c9
 *
Packit 90a5c9
 * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
Packit 90a5c9
 *
Packit 90a5c9
 * Eric Haines, erich@eye.com
Packit 90a5c9
 * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
Packit 90a5c9
 *
Packit 90a5c9
 * Randy Terbush, randy@zyzzyva.com
Packit 90a5c9
 * port to Apache module format, "base_uri" and support for relative URLs
Packit 90a5c9
 *
Packit 90a5c9
 * James H. Cloos, Jr., cloos@jhcloos.com
Packit 90a5c9
 * Added point datatype, using code in NCSA's version 1.8 imagemap.c
Packit 90a5c9
 * program, as distributed with version 1.4.1 of their server.
Packit 90a5c9
 * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
Packit 90a5c9
 *
Packit 90a5c9
 * Nathan Kurz, nate@tripod.com
Packit 90a5c9
 * Rewrite/reorganization.  New handling of default, base and relative URLs.
Packit 90a5c9
 * New Configuration directives:
Packit 90a5c9
 *    ImapMenu {none, formatted, semiformatted, unformatted}
Packit 90a5c9
 *    ImapDefault {error, nocontent, referer, menu, URL}
Packit 90a5c9
 *    ImapBase {map, referer, URL}
Packit 90a5c9
 * Support for creating non-graphical menu added.  (backwards compatible):
Packit 90a5c9
 *    Old:  directive URL [x,y ...]
Packit 90a5c9
 *    New:  directive URL "Menu text" [x,y ...]
Packit 90a5c9
 *     or:  directive URL x,y ... "Menu text"
Packit 90a5c9
 * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
Packit 90a5c9
 *
Packit 90a5c9
 * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "apr.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_lib.h"
Packit 90a5c9
Packit 90a5c9
#define APR_WANT_STDIO          /* for sscanf() */
Packit 90a5c9
#define APR_WANT_STRFUNC
Packit 90a5c9
#include "apr_want.h"
Packit 90a5c9
Packit 90a5c9
#include "ap_config.h"
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_request.h"
Packit 90a5c9
#include "http_core.h"
Packit 90a5c9
#include "http_protocol.h"
Packit 90a5c9
#include "http_main.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "util_script.h"
Packit 90a5c9
#include "mod_core.h"
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
#define IMAP_MAGIC_TYPE "application/x-httpd-imap"
Packit 90a5c9
#define MAXVERTS 100
Packit 90a5c9
#define X 0
Packit 90a5c9
#define Y 1
Packit 90a5c9
Packit 90a5c9
#define IMAP_MENU_DEFAULT "formatted"
Packit 90a5c9
#define IMAP_DEFAULT_DEFAULT "nocontent"
Packit 90a5c9
#define IMAP_BASE_DEFAULT "map"
Packit 90a5c9
Packit 90a5c9
module AP_MODULE_DECLARE_DATA imagemap_module;
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    char *imap_menu;
Packit 90a5c9
    char *imap_default;
Packit 90a5c9
    char *imap_base;
Packit 90a5c9
} imap_conf_rec;
Packit 90a5c9
Packit 90a5c9
static void *create_imap_dir_config(apr_pool_t *p, char *dummy)
Packit 90a5c9
{
Packit 90a5c9
    imap_conf_rec *icr =
Packit 90a5c9
    (imap_conf_rec *) apr_palloc(p, sizeof(imap_conf_rec));
Packit 90a5c9
Packit 90a5c9
    icr->imap_menu = NULL;
Packit 90a5c9
    icr->imap_default = NULL;
Packit 90a5c9
    icr->imap_base = NULL;
Packit 90a5c9
Packit 90a5c9
    return icr;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void *merge_imap_dir_configs(apr_pool_t *p, void *basev, void *addv)
Packit 90a5c9
{
Packit 90a5c9
    imap_conf_rec *new = (imap_conf_rec *) apr_palloc(p, sizeof(imap_conf_rec));
Packit 90a5c9
    imap_conf_rec *base = (imap_conf_rec *) basev;
Packit 90a5c9
    imap_conf_rec *add = (imap_conf_rec *) addv;
Packit 90a5c9
Packit 90a5c9
    new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
Packit 90a5c9
    new->imap_default = add->imap_default ? add->imap_default
Packit 90a5c9
                                          : base->imap_default;
Packit 90a5c9
    new->imap_base = add->imap_base ? add->imap_base : base->imap_base;
Packit 90a5c9
Packit 90a5c9
    return new;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static const command_rec imap_cmds[] =
Packit 90a5c9
{
Packit 90a5c9
    AP_INIT_TAKE1("ImapMenu", ap_set_string_slot,
Packit 90a5c9
                  (void *)APR_OFFSETOF(imap_conf_rec, imap_menu), OR_INDEXES,
Packit 90a5c9
                  "the type of menu generated: none, formatted, semiformatted, "
Packit 90a5c9
                  "unformatted"),
Packit 90a5c9
    AP_INIT_TAKE1("ImapDefault", ap_set_string_slot,
Packit 90a5c9
                  (void *)APR_OFFSETOF(imap_conf_rec, imap_default), OR_INDEXES,
Packit 90a5c9
                  "the action taken if no match: error, nocontent, referer, "
Packit 90a5c9
                  "menu, URL"),
Packit 90a5c9
    AP_INIT_TAKE1("ImapBase", ap_set_string_slot,
Packit 90a5c9
                  (void *)APR_OFFSETOF(imap_conf_rec, imap_base), OR_INDEXES,
Packit 90a5c9
                  "the base for all URL's: map, referer, URL (or start of)"),
Packit 90a5c9
    {NULL}
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
static int pointinrect(const double point[2], double coords[MAXVERTS][2])
Packit 90a5c9
{
Packit 90a5c9
    double max[2], min[2];
Packit 90a5c9
    if (coords[0][X] > coords[1][X]) {
Packit 90a5c9
        max[0] = coords[0][X];
Packit 90a5c9
        min[0] = coords[1][X];
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        max[0] = coords[1][X];
Packit 90a5c9
        min[0] = coords[0][X];
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (coords[0][Y] > coords[1][Y]) {
Packit 90a5c9
        max[1] = coords[0][Y];
Packit 90a5c9
        min[1] = coords[1][Y];
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        max[1] = coords[1][Y];
Packit 90a5c9
        min[1] = coords[0][Y];
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return ((point[X] >= min[0] && point[X] <= max[0]) &&
Packit 90a5c9
            (point[Y] >= min[1] && point[Y] <= max[1]));
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int pointincircle(const double point[2], double coords[MAXVERTS][2])
Packit 90a5c9
{
Packit 90a5c9
    double radius1, radius2;
Packit 90a5c9
Packit 90a5c9
    radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
Packit 90a5c9
        + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
Packit 90a5c9
Packit 90a5c9
    radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
Packit 90a5c9
        + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
Packit 90a5c9
Packit 90a5c9
    return (radius2 <= radius1);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
#define fmin(a,b) (((a)>(b))?(b):(a))
Packit 90a5c9
#define fmax(a,b) (((a)>(b))?(a):(b))
Packit 90a5c9
Packit 90a5c9
static int pointinpoly(const double point[2], double pgon[MAXVERTS][2])
Packit 90a5c9
{
Packit 90a5c9
    int i, numverts, crossings = 0;
Packit 90a5c9
    double x = point[X], y = point[Y];
Packit 90a5c9
Packit 90a5c9
    for (numverts = 0; numverts < MAXVERTS && pgon[numverts][X] != -1;
Packit 90a5c9
        numverts++) {
Packit 90a5c9
        /* just counting the vertexes */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    for (i = 0; i < numverts; i++) {
Packit 90a5c9
        double x1=pgon[i][X];
Packit 90a5c9
        double y1=pgon[i][Y];
Packit 90a5c9
        double x2=pgon[(i + 1) % numverts][X];
Packit 90a5c9
        double y2=pgon[(i + 1) % numverts][Y];
Packit 90a5c9
        double d=(y - y1) * (x2 - x1) - (x - x1) * (y2 - y1);
Packit 90a5c9
Packit 90a5c9
        if ((y1 >= y) != (y2 >= y)) {
Packit 90a5c9
            crossings +=y2 - y1 >= 0 ? d >= 0 : d <= 0;
Packit 90a5c9
        }
Packit 90a5c9
        if (!d && fmin(x1,x2) <= x && x <= fmax(x1,x2)
Packit 90a5c9
            && fmin(y1,y2) <= y && y <= fmax(y1,y2)) {
Packit 90a5c9
            return 1;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return crossings & 0x01;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static int is_closer(const double point[2], double coords[MAXVERTS][2],
Packit 90a5c9
                     double *closest)
Packit 90a5c9
{
Packit 90a5c9
    double dist_squared = ((point[X] - coords[0][X])
Packit 90a5c9
                           * (point[X] - coords[0][X]))
Packit 90a5c9
                          + ((point[Y] - coords[0][Y])
Packit 90a5c9
                             * (point[Y] - coords[0][Y]));
Packit 90a5c9
Packit 90a5c9
    if (point[X] < 0 || point[Y] < 0) {
Packit 90a5c9
        return (0);          /* don't mess around with negative coordinates */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (*closest < 0 || dist_squared < *closest) {
Packit 90a5c9
        *closest = dist_squared;
Packit 90a5c9
        return (1);          /* if this is the first point or is the closest yet
Packit 90a5c9
                                set 'closest' equal to this distance^2 */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return (0);              /* if it's not the first or closest */
Packit 90a5c9
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static double get_x_coord(const char *args)
Packit 90a5c9
{
Packit 90a5c9
    char *endptr;               /* we want it non-null */
Packit 90a5c9
    double x_coord = -1;        /* -1 is returned if no coordinate is given */
Packit 90a5c9
Packit 90a5c9
    if (args == NULL) {
Packit 90a5c9
        return (-1);            /* in case we aren't passed anything */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    while (*args && !apr_isdigit(*args) && *args != ',') {
Packit 90a5c9
        args++;                 /* jump to the first digit, but not past
Packit 90a5c9
                                   a comma or end */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    x_coord = strtod(args, &endptr);
Packit 90a5c9
Packit 90a5c9
    if (endptr > args) {        /* if a conversion was made */
Packit 90a5c9
        return (x_coord);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return (-1);                /* else if no conversion was made,
Packit 90a5c9
                                   or if no args was given */
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static double get_y_coord(const char *args)
Packit 90a5c9
{
Packit 90a5c9
    char *endptr;               /* we want it non-null */
Packit 90a5c9
    const char *start_of_y = NULL;
Packit 90a5c9
    double y_coord = -1;        /* -1 is returned on error */
Packit 90a5c9
Packit 90a5c9
    if (args == NULL) {
Packit 90a5c9
        return (-1);            /* in case we aren't passed anything */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    start_of_y = ap_strchr_c(args, ',');     /* the comma */
Packit 90a5c9
Packit 90a5c9
    if (start_of_y) {
Packit 90a5c9
Packit 90a5c9
        start_of_y++;           /* start looking at the character after
Packit 90a5c9
                                   the comma */
Packit 90a5c9
Packit 90a5c9
        while (*start_of_y && !apr_isdigit(*start_of_y)) {
Packit 90a5c9
            start_of_y++;       /* jump to the first digit, but not
Packit 90a5c9
                                   past the end */
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        y_coord = strtod(start_of_y, &endptr);
Packit 90a5c9
Packit 90a5c9
        if (endptr > start_of_y) {
Packit 90a5c9
            return (y_coord);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return (-1);                /* if no conversion was made, or
Packit 90a5c9
                                   no comma was found in args */
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/* See if string has a "quoted part", and if so set *quoted_part to
Packit 90a5c9
 * the first character of the quoted part, then hammer a \0 onto the
Packit 90a5c9
 * trailing quote, and set *string to point at the first character
Packit 90a5c9
 * past the second quote.
Packit 90a5c9
 *
Packit 90a5c9
 * Otherwise set *quoted_part to NULL, and leave *string alone.
Packit 90a5c9
 */
Packit 90a5c9
static void read_quoted(char **string, char **quoted_part)
Packit 90a5c9
{
Packit 90a5c9
    char *strp = *string;
Packit 90a5c9
Packit 90a5c9
    /* assume there's no quoted part */
Packit 90a5c9
    *quoted_part = NULL;
Packit 90a5c9
Packit 90a5c9
    while (apr_isspace(*strp)) {
Packit 90a5c9
        strp++;                 /* go along string until non-whitespace */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (*strp == '"') {         /* if that character is a double quote */
Packit 90a5c9
        strp++;                 /* step over it */
Packit 90a5c9
        *quoted_part = strp;    /* note where the quoted part begins */
Packit 90a5c9
Packit 90a5c9
        while (*strp && *strp != '"') {
Packit 90a5c9
            ++strp;             /* skip the quoted portion */
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        *strp = '\0';           /* end the string with a NUL */
Packit 90a5c9
Packit 90a5c9
        strp++;                 /* step over the last double quote */
Packit 90a5c9
        *string = strp;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * returns the mapped URL or NULL.
Packit 90a5c9
 */
Packit 90a5c9
static const char *imap_url(request_rec *r, const char *base, const char *value)
Packit 90a5c9
{
Packit 90a5c9
/* translates a value into a URL. */
Packit 90a5c9
    int slen, clen;
Packit 90a5c9
    char *string_pos = NULL;
Packit 90a5c9
    const char *string_pos_const = NULL;
Packit 90a5c9
    char *directory = NULL;
Packit 90a5c9
    const char *referer = NULL;
Packit 90a5c9
    char *my_base;
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) {
Packit 90a5c9
        return ap_construct_url(r->pool, r->uri, r);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) {
Packit 90a5c9
        return apr_pstrdup(r->pool, value);      /* these are handled elsewhere,
Packit 90a5c9
                                                so just copy them */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(value, "referer")) {
Packit 90a5c9
        referer = apr_table_get(r->headers_in, "Referer");
Packit 90a5c9
        if (referer && *referer) {
Packit 90a5c9
            return referer;
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            /* XXX:  This used to do *value = '\0'; ... which is totally bogus
Packit 90a5c9
             * because it hammers the passed in value, which can be a string
Packit 90a5c9
             * constant, or part of a config, or whatever.  Total garbage.
Packit 90a5c9
             * This works around that without changing the rest of this
Packit 90a5c9
             * code much
Packit 90a5c9
             */
Packit 90a5c9
            value = "";      /* if 'referer' but no referring page,
Packit 90a5c9
                                null the value */
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    string_pos_const = value;
Packit 90a5c9
    while (apr_isalpha(*string_pos_const)) {
Packit 90a5c9
        string_pos_const++;           /* go along the URL from the map
Packit 90a5c9
                                         until a non-letter */
Packit 90a5c9
    }
Packit 90a5c9
    if (*string_pos_const == ':') {
Packit 90a5c9
        /* if letters and then a colon (like http:) */
Packit 90a5c9
        /* it's an absolute URL, so use it! */
Packit 90a5c9
        return apr_pstrdup(r->pool, value);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!base || !*base) {
Packit 90a5c9
        if (value && *value) {
Packit 90a5c9
            return apr_pstrdup(r->pool, value); /* no base: use what is given */
Packit 90a5c9
        }
Packit 90a5c9
        /* no base, no value: pick a simple default */
Packit 90a5c9
        return ap_construct_url(r->pool, "/", r);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* must be a relative URL to be combined with base */
Packit 90a5c9
    if (ap_strchr_c(base, '/') == NULL && (!strncmp(value, "../", 3)
Packit 90a5c9
        || !strcmp(value, ".."))) {
Packit 90a5c9
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00677)
Packit 90a5c9
                    "invalid base directive in map file: %s", r->uri);
Packit 90a5c9
        return NULL;
Packit 90a5c9
    }
Packit 90a5c9
    my_base = apr_pstrdup(r->pool, base);
Packit 90a5c9
    string_pos = my_base;
Packit 90a5c9
    while (*string_pos) {
Packit 90a5c9
        if (*string_pos == '/' && *(string_pos + 1) == '/') {
Packit 90a5c9
            string_pos += 2;    /* if there are two slashes, jump over them */
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
        if (*string_pos == '/') {       /* the first single slash */
Packit 90a5c9
            if (value[0] == '/') {
Packit 90a5c9
                *string_pos = '\0';
Packit 90a5c9
            }                   /* if the URL from the map starts from root,
Packit 90a5c9
                                   end the base URL string at the first single
Packit 90a5c9
                                   slash */
Packit 90a5c9
            else {
Packit 90a5c9
                directory = string_pos;         /* save the start of
Packit 90a5c9
                                                   the directory portion */
Packit 90a5c9
Packit 90a5c9
                string_pos = strrchr(string_pos, '/');  /* now reuse
Packit 90a5c9
                                                           string_pos */
Packit 90a5c9
                string_pos++;   /* step over that last slash */
Packit 90a5c9
                *string_pos = '\0';
Packit 90a5c9
            }                   /* but if the map url is relative, leave the
Packit 90a5c9
                                   slash on the base (if there is one) */
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
        string_pos++;           /* until we get to the end of my_base without
Packit 90a5c9
                                   finding a slash by itself */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    while (!strncmp(value, "../", 3) || !strcmp(value, "..")) {
Packit 90a5c9
Packit 90a5c9
        if (directory && (slen = strlen(directory))) {
Packit 90a5c9
Packit 90a5c9
            /* for each '..',  knock a directory off the end
Packit 90a5c9
               by ending the string right at the last slash.
Packit 90a5c9
               But only consider the directory portion: don't eat
Packit 90a5c9
               into the server name.  And only try if a directory
Packit 90a5c9
               portion was found */
Packit 90a5c9
Packit 90a5c9
            clen = slen - 1;
Packit 90a5c9
Packit 90a5c9
            while ((slen - clen) == 1) {
Packit 90a5c9
Packit 90a5c9
                if ((string_pos = strrchr(directory, '/'))) {
Packit 90a5c9
                    *string_pos = '\0';
Packit 90a5c9
                }
Packit 90a5c9
                clen = strlen(directory);
Packit 90a5c9
                if (clen == 0) {
Packit 90a5c9
                    break;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            value += 2;         /* jump over the '..' that we found in the
Packit 90a5c9
                                   value */
Packit 90a5c9
        }
Packit 90a5c9
        else if (directory) {
Packit 90a5c9
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00678)
Packit 90a5c9
                        "invalid directory name in map file: %s", r->uri);
Packit 90a5c9
            return NULL;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) {
Packit 90a5c9
            value++;            /* step over the '/' if there are more '..'
Packit 90a5c9
                                   to do.  This way, we leave the starting
Packit 90a5c9
                                   '/' on value after the last '..', but get
Packit 90a5c9
                                   rid of it otherwise */
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
    }                           /* by this point, value does not start
Packit 90a5c9
                                   with '..' */
Packit 90a5c9
Packit 90a5c9
    if (value && *value) {
Packit 90a5c9
        return apr_pstrcat(r->pool, my_base, value, NULL);
Packit 90a5c9
    }
Packit 90a5c9
    return my_base;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int imap_reply(request_rec *r, const char *redirect)
Packit 90a5c9
{
Packit 90a5c9
    if (!strcasecmp(redirect, "error")) {
Packit 90a5c9
        /* they actually requested an error! */
Packit 90a5c9
        return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
    }
Packit 90a5c9
    if (!strcasecmp(redirect, "nocontent")) {
Packit 90a5c9
        /* tell the client to keep the page it has */
Packit 90a5c9
        return HTTP_NO_CONTENT;
Packit 90a5c9
    }
Packit 90a5c9
    if (redirect && *redirect) {
Packit 90a5c9
        /* must be a URL, so redirect to it */
Packit 90a5c9
        apr_table_setn(r->headers_out, "Location", redirect);
Packit 90a5c9
        return HTTP_MOVED_TEMPORARILY;
Packit 90a5c9
    }
Packit 90a5c9
    return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void menu_header(request_rec *r, char *menu)
Packit 90a5c9
{
Packit 90a5c9
    ap_set_content_type(r, "text/html; charset=ISO-8859-1");
Packit 90a5c9
Packit 90a5c9
    ap_rvputs(r, DOCTYPE_HTML_3_2, "<html><head>\n<title>Menu for ",
Packit 90a5c9
              ap_escape_html(r->pool, r->uri),
Packit 90a5c9
              "</title>\n</head><body>\n", NULL);
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(menu, "formatted")) {
Packit 90a5c9
        ap_rvputs(r, "

Menu for ",

Packit 90a5c9
                  ap_escape_html(r->pool, r->uri),
Packit 90a5c9
                  "\n
\n\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void menu_blank(request_rec *r, char *menu)
Packit 90a5c9
{
Packit 90a5c9
    if (!strcasecmp(menu, "formatted")) {
Packit 90a5c9
        ap_rputs("\n", r);
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "semiformatted")) {
Packit 90a5c9
        ap_rputs("
\n", r);
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "unformatted")) {
Packit 90a5c9
        ap_rputs("\n", r);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void menu_comment(request_rec *r, char *menu, char *comment)
Packit 90a5c9
{
Packit 90a5c9
    /* comments are ignored in the 'formatted' form */
Packit 90a5c9
    if (!strcasecmp(menu, "formatted")) {
Packit 90a5c9
        ap_rputs("\n", r);         /* print just a newline if 'formatted' */
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "semiformatted") && *comment) {
Packit 90a5c9
        ap_rvputs(r, comment, "\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "unformatted") && *comment) {
Packit 90a5c9
        ap_rvputs(r, comment, "\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void menu_default(request_rec *r, const char *menu, const char *href, const char *text)
Packit 90a5c9
{
Packit 90a5c9
    char *ehref, *etext;
Packit 90a5c9
    if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
Packit 90a5c9
        return;                 /* don't print such lines, these aren't
Packit 90a5c9
                                   really href's */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ehref = ap_escape_uri(r->pool, href);
Packit 90a5c9
    etext = ap_escape_html(r->pool, text);
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(menu, "formatted")) {
Packit 90a5c9
        ap_rvputs(r, "
(Default) ", etext,
Packit 90a5c9
                     "\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "semiformatted")) {
Packit 90a5c9
        ap_rvputs(r, "
(Default) ", etext,
Packit 90a5c9
               "\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "unformatted")) {
Packit 90a5c9
        ap_rvputs(r, "", etext, "", NULL);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void menu_directive(request_rec *r, const char *menu, const char *href, const char *text)
Packit 90a5c9
{
Packit 90a5c9
    char *ehref, *etext;
Packit 90a5c9
    if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) {
Packit 90a5c9
        return;                 /* don't print such lines, as this isn't
Packit 90a5c9
                                   really an href */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ehref = ap_escape_uri(r->pool, href);
Packit 90a5c9
    etext = ap_escape_html(r->pool, text);
Packit 90a5c9
Packit 90a5c9
    if (!strcasecmp(menu, "formatted")) {
Packit 90a5c9
        ap_rvputs(r, "
          ", etext,
Packit 90a5c9
               "\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "semiformatted")) {
Packit 90a5c9
        ap_rvputs(r, "
          ", etext,
Packit 90a5c9
               "\n", NULL);
Packit 90a5c9
    }
Packit 90a5c9
    else if (!strcasecmp(menu, "unformatted")) {
Packit 90a5c9
        ap_rvputs(r, "", etext, "", NULL);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void menu_footer(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    ap_rputs("\n\n</body>\n</html>\n", r);         /* finish the menu */
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int imap_handler_internal(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    char input[MAX_STRING_LEN];
Packit 90a5c9
    char *directive;
Packit 90a5c9
    char *value;
Packit 90a5c9
    char *href_text;
Packit 90a5c9
    const char *base;
Packit 90a5c9
    const char *redirect;
Packit 90a5c9
    const char *mapdflt;
Packit 90a5c9
    char *closest = NULL;
Packit 90a5c9
    double closest_yet = -1;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
Packit 90a5c9
    double testpoint[2];
Packit 90a5c9
    double pointarray[MAXVERTS + 1][2];
Packit 90a5c9
    int vertex;
Packit 90a5c9
Packit 90a5c9
    char *string_pos;
Packit 90a5c9
    int showmenu = 0;
Packit 90a5c9
Packit 90a5c9
    imap_conf_rec *icr;
Packit 90a5c9
Packit 90a5c9
    char *imap_menu;
Packit 90a5c9
    char *imap_default;
Packit 90a5c9
    char *imap_base;
Packit 90a5c9
Packit 90a5c9
    ap_configfile_t *imap;
Packit 90a5c9
Packit 90a5c9
    icr = ap_get_module_config(r->per_dir_config, &imagemap_module);
Packit 90a5c9
Packit 90a5c9
    imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT;
Packit 90a5c9
    imap_default = icr->imap_default
Packit 90a5c9
      ?  icr->imap_default : IMAP_DEFAULT_DEFAULT;
Packit 90a5c9
    imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT;
Packit 90a5c9
Packit 90a5c9
    status = ap_pcfg_openfile(&imap, r->pool, r->filename);
Packit 90a5c9
Packit 90a5c9
    if (status != APR_SUCCESS) {
Packit 90a5c9
        return HTTP_NOT_FOUND;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    base = imap_url(r, NULL, imap_base);         /* set base according
Packit 90a5c9
                                                    to default */
Packit 90a5c9
    if (!base) {
Packit 90a5c9
        return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
    }
Packit 90a5c9
    mapdflt = imap_url(r, NULL, imap_default);   /* and default to
Packit 90a5c9
                                                    global default */
Packit 90a5c9
    if (!mapdflt) {
Packit 90a5c9
        return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    testpoint[X] = get_x_coord(r->args);
Packit 90a5c9
    testpoint[Y] = get_y_coord(r->args);
Packit 90a5c9
Packit 90a5c9
    if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
Packit 90a5c9
        (testpoint[X] == 0 && testpoint[Y] == 0)) {
Packit 90a5c9
        /* if either is -1 or if both are zero (new Lynx) */
Packit 90a5c9
        /* we don't have valid coordinates */
Packit 90a5c9
        testpoint[X] = -1;
Packit 90a5c9
        testpoint[Y] = -1;
Packit 90a5c9
        if (strncasecmp(imap_menu, "none", 2)) {
Packit 90a5c9
            showmenu = 1;       /* show the menu _unless_ ImapMenu is
Packit 90a5c9
                                   'none' or 'no' */
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (showmenu) {             /* send start of imagemap menu if
Packit 90a5c9
                                   we're going to */
Packit 90a5c9
        menu_header(r, imap_menu);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    while (!ap_cfg_getline(input, sizeof(input), imap)) {
Packit 90a5c9
        if (!input[0]) {
Packit 90a5c9
            if (showmenu) {
Packit 90a5c9
                menu_blank(r, imap_menu);
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (input[0] == '#') {
Packit 90a5c9
            if (showmenu) {
Packit 90a5c9
                menu_comment(r, imap_menu, input + 1);
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }                       /* blank lines and comments are ignored
Packit 90a5c9
                                   if we aren't printing a menu */
Packit 90a5c9
Packit 90a5c9
        /* find the first two space delimited fields, recall that
Packit 90a5c9
         * ap_cfg_getline has removed leading/trailing whitespace.
Packit 90a5c9
         *
Packit 90a5c9
         * note that we're tokenizing as we go... if we were to use the
Packit 90a5c9
         * ap_getword() class of functions we would end up allocating extra
Packit 90a5c9
         * memory for every line of the map file
Packit 90a5c9
         */
Packit 90a5c9
        string_pos = input;
Packit 90a5c9
        if (!*string_pos) {   /* need at least two fields */
Packit 90a5c9
            goto need_2_fields;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        directive = string_pos;
Packit 90a5c9
        while (*string_pos && !apr_isspace(*string_pos)) {   /* past directive */
Packit 90a5c9
            ++string_pos;
Packit 90a5c9
        }
Packit 90a5c9
        if (!*string_pos) {   /* need at least two fields */
Packit 90a5c9
            goto need_2_fields;
Packit 90a5c9
        }
Packit 90a5c9
        *string_pos++ = '\0';
Packit 90a5c9
Packit 90a5c9
        if (!*string_pos) {   /* need at least two fields */
Packit 90a5c9
            goto need_2_fields;
Packit 90a5c9
        }
Packit 90a5c9
        while (apr_isspace(*string_pos)) { /* past whitespace */
Packit 90a5c9
            ++string_pos;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        value = string_pos;
Packit 90a5c9
        while (*string_pos && !apr_isspace(*string_pos)) {   /* past value */
Packit 90a5c9
            ++string_pos;
Packit 90a5c9
        }
Packit 90a5c9
        if (apr_isspace(*string_pos)) {
Packit 90a5c9
            *string_pos++ = '\0';
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            /* end of input, don't advance past it */
Packit 90a5c9
            *string_pos = '\0';
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strncasecmp(directive, "base", 4)) {       /* base, base_uri */
Packit 90a5c9
            base = imap_url(r, NULL, value);
Packit 90a5c9
            if (!base) {
Packit 90a5c9
                goto menu_bail;
Packit 90a5c9
            }
Packit 90a5c9
            continue;           /* base is never printed to a menu */
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        read_quoted(&string_pos, &href_text);
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(directive, "default")) {        /* default */
Packit 90a5c9
            mapdflt = imap_url(r, NULL, value);
Packit 90a5c9
            if (!mapdflt) {
Packit 90a5c9
                goto menu_bail;
Packit 90a5c9
            }
Packit 90a5c9
            if (showmenu) {     /* print the default if there's a menu */
Packit 90a5c9
                redirect = imap_url(r, base, mapdflt);
Packit 90a5c9
                if (!redirect) {
Packit 90a5c9
                    goto menu_bail;
Packit 90a5c9
                }
Packit 90a5c9
                menu_default(r, imap_menu, redirect,
Packit 90a5c9
                             href_text ? href_text : mapdflt);
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        vertex = 0;
Packit 90a5c9
        while (vertex < MAXVERTS &&
Packit 90a5c9
               sscanf(string_pos, "%lf%*[, ]%lf",
Packit 90a5c9
                      &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) {
Packit 90a5c9
            /* Now skip what we just read... we can't use ANSIism %n */
Packit 90a5c9
            while (apr_isspace(*string_pos)) {      /* past whitespace */
Packit 90a5c9
                string_pos++;
Packit 90a5c9
            }
Packit 90a5c9
            while (apr_isdigit(*string_pos)) {      /* and the 1st number */
Packit 90a5c9
                string_pos++;
Packit 90a5c9
            }
Packit 90a5c9
            string_pos++;       /* skip the ',' */
Packit 90a5c9
            while (apr_isspace(*string_pos)) {      /* past any more whitespace */
Packit 90a5c9
                string_pos++;
Packit 90a5c9
            }
Packit 90a5c9
            while (apr_isdigit(*string_pos)) {      /* 2nd number */
Packit 90a5c9
                string_pos++;
Packit 90a5c9
            }
Packit 90a5c9
            vertex++;
Packit 90a5c9
        }                       /* so long as there are more vertices to
Packit 90a5c9
                                   read, and we have room, read them in.
Packit 90a5c9
                                   We start where we left off of the last
Packit 90a5c9
                                   sscanf, not at the beginning. */
Packit 90a5c9
Packit 90a5c9
        pointarray[vertex][X] = -1;     /* signals the end of vertices */
Packit 90a5c9
Packit 90a5c9
        if (showmenu) {
Packit 90a5c9
            if (!href_text) {
Packit 90a5c9
                read_quoted(&string_pos, &href_text);     /* href text could
Packit 90a5c9
                                                             be here instead */
Packit 90a5c9
            }
Packit 90a5c9
            redirect = imap_url(r, base, value);
Packit 90a5c9
            if (!redirect) {
Packit 90a5c9
                goto menu_bail;
Packit 90a5c9
            }
Packit 90a5c9
            menu_directive(r, imap_menu, redirect,
Packit 90a5c9
                           href_text ? href_text : value);
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
        /* note that we don't make it past here if we are making a menu */
Packit 90a5c9
Packit 90a5c9
        if (testpoint[X] == -1 || pointarray[0][X] == -1) {
Packit 90a5c9
            continue;           /* don't try the following tests if testpoints
Packit 90a5c9
                                   are invalid, or if there are no
Packit 90a5c9
                                   coordinates */
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(directive, "poly")) {   /* poly */
Packit 90a5c9
Packit 90a5c9
            if (pointinpoly(testpoint, pointarray)) {
Packit 90a5c9
                ap_cfg_closefile(imap);
Packit 90a5c9
                redirect = imap_url(r, base, value);
Packit 90a5c9
                if (!redirect) {
Packit 90a5c9
                    return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
                }
Packit 90a5c9
                return (imap_reply(r, redirect));
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(directive, "circle")) {         /* circle */
Packit 90a5c9
Packit 90a5c9
            if (pointincircle(testpoint, pointarray)) {
Packit 90a5c9
                ap_cfg_closefile(imap);
Packit 90a5c9
                redirect = imap_url(r, base, value);
Packit 90a5c9
                if (!redirect) {
Packit 90a5c9
                    return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
                }
Packit 90a5c9
                return (imap_reply(r, redirect));
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(directive, "rect")) {   /* rect */
Packit 90a5c9
Packit 90a5c9
            if (pointinrect(testpoint, pointarray)) {
Packit 90a5c9
                ap_cfg_closefile(imap);
Packit 90a5c9
                redirect = imap_url(r, base, value);
Packit 90a5c9
                if (!redirect) {
Packit 90a5c9
                    return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
                }
Packit 90a5c9
                return (imap_reply(r, redirect));
Packit 90a5c9
            }
Packit 90a5c9
            continue;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (!strcasecmp(directive, "point")) {  /* point */
Packit 90a5c9
Packit 90a5c9
            if (is_closer(testpoint, pointarray, &closest_yet)) {
Packit 90a5c9
                closest = apr_pstrdup(r->pool, value);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            continue;
Packit 90a5c9
        }                       /* move on to next line whether it's
Packit 90a5c9
                                   closest or not */
Packit 90a5c9
Packit 90a5c9
    }                           /* nothing matched, so we get another line! */
Packit 90a5c9
Packit 90a5c9
    ap_cfg_closefile(imap);        /* we are done with the map file; close it */
Packit 90a5c9
Packit 90a5c9
    if (showmenu) {
Packit 90a5c9
        menu_footer(r);         /* finish the menu and we are done */
Packit 90a5c9
        return OK;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (closest) {             /* if a 'point' directive has been seen */
Packit 90a5c9
        redirect = imap_url(r, base, closest);
Packit 90a5c9
        if (!redirect) {
Packit 90a5c9
            return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
        }
Packit 90a5c9
        return (imap_reply(r, redirect));
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (mapdflt) {             /* a default should be defined, even if
Packit 90a5c9
                                  only 'nocontent' */
Packit 90a5c9
        redirect = imap_url(r, base, mapdflt);
Packit 90a5c9
        if (!redirect) {
Packit 90a5c9
            return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
        }
Packit 90a5c9
        return (imap_reply(r, redirect));
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return HTTP_INTERNAL_SERVER_ERROR;        /* If we make it this far,
Packit 90a5c9
                                                 we failed. They lose! */
Packit 90a5c9
Packit 90a5c9
need_2_fields:
Packit 90a5c9
    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00679)
Packit 90a5c9
                "map file %s, line %d syntax error: requires at "
Packit 90a5c9
                "least two fields", r->uri, imap->line_number);
Packit 90a5c9
    /* fall through */
Packit 90a5c9
menu_bail:
Packit 90a5c9
    ap_cfg_closefile(imap);
Packit 90a5c9
    if (showmenu) {
Packit 90a5c9
        /* There's not much else we can do ... we've already sent the headers
Packit 90a5c9
         * to the client.
Packit 90a5c9
         */
Packit 90a5c9
        ap_rputs("\n\n[an internal server error occured]\n", r);
Packit 90a5c9
        menu_footer(r);
Packit 90a5c9
        return OK;
Packit 90a5c9
    }
Packit 90a5c9
    return HTTP_INTERNAL_SERVER_ERROR;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int imap_handler(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    /* Optimization: skip the allocation of large local variables on the
Packit 90a5c9
     * stack (in imap_handler_internal()) on requests that aren't using
Packit 90a5c9
     * imagemaps
Packit 90a5c9
     */
Packit 90a5c9
    if (r->method_number != M_GET || (strcmp(r->handler,IMAP_MAGIC_TYPE)
Packit 90a5c9
                                      && strcmp(r->handler, "imap-file"))) {
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        return imap_handler_internal(r);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void register_hooks(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    ap_hook_handler(imap_handler,NULL,NULL,APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(imagemap) =
Packit 90a5c9
{
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    create_imap_dir_config,     /* dir config creater */
Packit 90a5c9
    merge_imap_dir_configs,     /* dir merger --- default is to override */
Packit 90a5c9
    NULL,                       /* server config */
Packit 90a5c9
    NULL,                       /* merge server config */
Packit 90a5c9
    imap_cmds,                  /* command apr_table_t */
Packit 90a5c9
    register_hooks              /* register hooks */
Packit 90a5c9
};