mod_imap.c revision ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dc
/* Copyright 2000-2004 Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This imagemap module started as a port of the original imagemap.c
* written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
* This version includes the mapping algorithms found in version 1.3
* of imagemap.c.
*
* Contributors to this code include:
*
* Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
*
* Eric Haines, erich@eye.com
* "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
*
* Randy Terbush, randy@zyzzyva.com
* port to Apache module format, "base_uri" and support for relative URLs
*
* James H. Cloos, Jr., cloos@jhcloos.com
* Added point datatype, using code in NCSA's version 1.8 imagemap.c
* program, as distributed with version 1.4.1 of their server.
* The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
*
* Nathan Kurz, nate@tripod.com
* Rewrite/reorganization. New handling of default, base and relative URLs.
* New Configuration directives:
* ImapMenu {none, formatted, semiformatted, unformatted}
* ImapDefault {error, nocontent, referer, menu, URL}
* ImapBase {map, referer, URL}
* Support for creating non-graphical menu added. (backwards compatible):
* Old: directive URL [x,y ...]
* New: directive URL "Menu text" [x,y ...]
* or: directive URL x,y ... "Menu text"
* Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
*
* Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
*/
#include "apr.h"
#include "apr_strings.h"
#include "apr_lib.h"
#define APR_WANT_STDIO /* for sscanf() */
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
#include "mod_core.h"
#define IMAP_MAGIC_TYPE "application/x-httpd-imap"
#define MAXVERTS 100
#define X 0
#define Y 1
#define IMAP_MENU_DEFAULT "formatted"
#define IMAP_DEFAULT_DEFAULT "nocontent"
#define IMAP_BASE_DEFAULT "map"
#ifdef SUNOS4
double strtod(); /* SunOS needed this */
#endif
typedef struct {
char *imap_menu;
char *imap_default;
char *imap_base;
{
imap_conf_rec *icr =
return icr;
}
{
: base->imap_default;
return new;
}
static const command_rec imap_cmds[] =
{
"the type of menu generated: none, formatted, semiformatted, "
"unformatted"),
"the action taken if no match: error, nocontent, referer, "
"menu, URL"),
"the base for all URL's: map, referer, URL (or start of)"),
{NULL}
};
{
}
else {
}
}
else {
}
}
{
}
#define fmin(a,b) (((a)>(b))?(b):(a))
#define fmax(a,b) (((a)>(b))?(a):(b))
{
numverts++) {
/* just counting the vertexes */
}
for (i = 0; i < numverts; i++) {
}
return 1;
}
}
return crossings & 0x01;
}
double *closest)
{
return (0); /* don't mess around with negative coordinates */
}
*closest = dist_squared;
return (1); /* if this is the first point or is the closest yet
set 'closest' equal to this distance^2 */
}
return (0); /* if it's not the first or closest */
}
static double get_x_coord(const char *args)
{
char *endptr; /* we want it non-null */
return (-1); /* in case we aren't passed anything */
}
args++; /* jump to the first digit, but not past
a comma or end */
}
return (x_coord);
}
return (-1); /* else if no conversion was made,
or if no args was given */
}
static double get_y_coord(const char *args)
{
char *endptr; /* we want it non-null */
const char *start_of_y = NULL;
return (-1); /* in case we aren't passed anything */
}
if (start_of_y) {
start_of_y++; /* start looking at the character after
the comma */
start_of_y++; /* jump to the first digit, but not
past the end */
}
if (endptr > start_of_y) {
return (y_coord);
}
}
return (-1); /* if no conversion was made, or
no comma was found in args */
}
/* See if string has a "quoted part", and if so set *quoted_part to
* the first character of the quoted part, then hammer a \0 onto the
* trailing quote, and set *string to point at the first character
* past the second quote.
*
* Otherwise set *quoted_part to NULL, and leave *string alone.
*/
{
/* assume there's no quoted part */
*quoted_part = NULL;
while (apr_isspace(*strp)) {
strp++; /* go along string until non-whitespace */
}
strp++; /* step over it */
++strp; /* skip the quoted portion */
}
strp++; /* step over the last double quote */
}
}
/*
* returns the mapped URL or NULL.
*/
{
/* translates a value into a URL. */
char *string_pos = NULL;
const char *string_pos_const = NULL;
char *my_base;
}
so just copy them */
}
}
else {
/* XXX: This used to do *value = '\0'; ... which is totally bogus
* because it hammers the passed in value, which can be a string
* constant, or part of a config, or whatever. Total garbage.
* This works around that without changing the rest of this
* code much
*/
null the value */
}
}
while (apr_isalpha(*string_pos_const)) {
string_pos_const++; /* go along the URL from the map
until a non-letter */
}
if (*string_pos_const == ':') {
/* if letters and then a colon (like http:) */
/* it's an absolute URL, so use it! */
}
}
/* no base, no value: pick a simple default */
}
/* must be a relative URL to be combined with base */
"invalid base directive in map file: %s", r->uri);
return NULL;
}
while (*string_pos) {
continue;
}
if (value[0] == '/') {
*string_pos = '\0';
} /* if the URL from the map starts from root,
end the base URL string at the first single
slash */
else {
the directory portion */
string_pos */
string_pos++; /* step over that last slash */
*string_pos = '\0';
} /* but if the map url is relative, leave the
slash on the base (if there is one) */
break;
}
string_pos++; /* until we get to the end of my_base without
finding a slash by itself */
}
/* for each '..', knock a directory off the end
by ending the string right at the last slash.
But only consider the directory portion: don't eat
into the server name. And only try if a directory
portion was found */
*string_pos = '\0';
}
if (clen == 0) {
break;
}
}
value */
}
else if (directory) {
"invalid directory name in map file: %s", r->uri);
return NULL;
}
value++; /* step over the '/' if there are more '..'
to do. This way, we leave the starting
'/' on value after the last '..', but get
rid of it otherwise */
}
} /* by this point, value does not start
with '..' */
}
return my_base;
}
{
/* they actually requested an error! */
return HTTP_INTERNAL_SERVER_ERROR;
}
/* tell the client to keep the page it has */
return HTTP_NO_CONTENT;
}
/* must be a URL, so redirect to it */
return HTTP_MOVED_TEMPORARILY;
}
return HTTP_INTERNAL_SERVER_ERROR;
}
{
ap_set_content_type(r, "text/html");
"</title>\n</head><body>\n", NULL);
}
return;
}
{
ap_rputs("\n", r);
}
ap_rputs("<br />\n", r);
}
ap_rputs("\n", r);
}
return;
}
{
}
}
}
return; /* comments are ignored in the
'formatted' form */
}
{
return; /* don't print such lines, these aren't
really href's */
}
"</a></pre>\n", NULL);
}
"</a></pre>\n", NULL);
}
}
return;
}
{
return; /* don't print such lines, as this isn't
really an href */
}
"</a></pre>\n", NULL);
}
"</a></pre>\n", NULL);
}
}
return;
}
static void menu_footer(request_rec *r)
{
}
static int imap_handler_internal(request_rec *r)
{
char input[MAX_STRING_LEN];
char *directive;
char *value;
char *href_text;
char *base;
char *redirect;
char *mapdflt;
double closest_yet = -1;
double testpoint[2];
int vertex;
char *string_pos;
int showmenu = 0;
char *imap_menu;
char *imap_default;
char *imap_base;
if (status != APR_SUCCESS) {
return HTTP_NOT_FOUND;
}
to default */
if (!base) {
return HTTP_INTERNAL_SERVER_ERROR;
}
global default */
if (!mapdflt) {
return HTTP_INTERNAL_SERVER_ERROR;
}
/* if either is -1 or if both are zero (new Lynx) */
/* we don't have valid coordinates */
testpoint[X] = -1;
testpoint[Y] = -1;
'none' or 'no' */
}
}
if (showmenu) { /* send start of imagemap menu if
we're going to */
menu_header(r, imap_menu);
}
if (!input[0]) {
if (showmenu) {
menu_blank(r, imap_menu);
}
continue;
}
if (input[0] == '#') {
if (showmenu) {
}
continue;
} /* blank lines and comments are ignored
if we aren't printing a menu */
/* find the first two space delimited fields, recall that
*
* note that we're tokenizing as we go... if we were to use the
* ap_getword() class of functions we would end up allocating extra
* memory for every line of the map file
*/
string_pos = input;
if (!*string_pos) { /* need at least two fields */
goto need_2_fields;
}
++string_pos;
}
if (!*string_pos) { /* need at least two fields */
goto need_2_fields;
}
*string_pos++ = '\0';
if (!*string_pos) { /* need at least two fields */
goto need_2_fields;
}
++string_pos;
}
value = string_pos;
++string_pos;
}
if (apr_isspace(*string_pos)) {
*string_pos++ = '\0';
}
else {
/* end of input, don't advance past it */
*string_pos = '\0';
}
if (!base) {
goto menu_bail;
}
continue; /* base is never printed to a menu */
}
if (!mapdflt) {
goto menu_bail;
}
if (showmenu) { /* print the default if there's a menu */
if (!redirect) {
goto menu_bail;
}
}
continue;
}
vertex = 0;
/* Now skip what we just read... we can't use ANSIism %n */
string_pos++;
}
string_pos++;
}
string_pos++; /* skip the ',' */
string_pos++;
}
string_pos++;
}
vertex++;
} /* so long as there are more vertices to
read, and we have room, read them in.
We start where we left off of the last
sscanf, not at the beginning. */
if (showmenu) {
if (!href_text) {
be here instead */
}
if (!redirect) {
goto menu_bail;
}
continue;
}
/* note that we don't make it past here if we are making a menu */
continue; /* don't try the following tests if testpoints
are invalid, or if there are no
coordinates */
}
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
continue;
}
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
continue;
}
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
continue;
}
}
continue;
} /* move on to next line whether it's
closest or not */
} /* nothing matched, so we get another line! */
if (showmenu) {
menu_footer(r); /* finish the menu and we are done */
return OK;
}
if (closest) { /* if a 'point' directive has been seen */
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
if (mapdflt) { /* a default should be defined, even if
only 'nocontent' */
if (!redirect) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return (imap_reply(r, redirect));
}
return HTTP_INTERNAL_SERVER_ERROR; /* If we make it this far,
we failed. They lose! */
"map file %s, line %d syntax error: requires at "
/* fall through */
if (showmenu) {
/* There's not much else we can do ... we've already sent the headers
* to the client.
*/
ap_rputs("\n\n[an internal server error occured]\n", r);
menu_footer(r);
return OK;
}
return HTTP_INTERNAL_SERVER_ERROR;
}
static int imap_handler(request_rec *r)
{
/* Optimization: skip the allocation of large local variables on the
* stack (in imap_handler_internal()) on requests that aren't using
* imagemaps
*/
return DECLINED;
}
else {
return imap_handler_internal(r);
}
}
static void register_hooks(apr_pool_t *p)
{
}
{
create_imap_dir_config, /* dir config creater */
merge_imap_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
imap_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};