mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-08 00:23:14 +00:00
* Merged version 2.1 of GoAhead webserver. This update was submitted by Antti P Miettinen <antti.p.miettinen@nokia.com>. * NOTES, base64.c, ejIntrn.h, emfdb.c, emfdb.h, md5.h, md5c.c, um.c, um.h: New files. * wbase64.c: Removed. * Makefile.am, asp.c, balloc.c, default.c, ej.h, ejlex.c, ejparse.c, form.c, h.c, handler.c, mime.c, misc.c, ringq.c, rom.c, security.c, socket.c, sym.c, uemf.c, uemf.h, url.c, value.c, webcomp.c, webmain.c, webpage.c, webrom.c, webs.c, webs.h, websuemf.c, wsIntrn.h: Modified.
403 lines
9.2 KiB
C
403 lines
9.2 KiB
C
/*
|
|
* default.c -- Default URL handler. Includes support for ASP.
|
|
*
|
|
* Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
|
|
*
|
|
* See the file "license.txt" for usage and redistribution license requirements
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
/******************************** Description *********************************/
|
|
|
|
/*
|
|
* This module provides default URL handling and Active Server Page support.
|
|
*
|
|
* In many cases we don't check the return code of calls to websWrite as
|
|
* it is easier, smaller and non-fatal to continue even when the requesting
|
|
* browser has gone away.
|
|
*/
|
|
|
|
/********************************* Includes ***********************************/
|
|
|
|
#include "wsIntrn.h"
|
|
|
|
/*********************************** Locals ***********************************/
|
|
|
|
static char_t *websDefaultPage; /* Default page name */
|
|
static char_t *websDefaultDir; /* Default Web page directory */
|
|
|
|
/**************************** Forward Declarations ****************************/
|
|
|
|
static void websDefaultWriteEvent(webs_t wp);
|
|
|
|
/*********************************** Code *************************************/
|
|
/*
|
|
* Process a default URL request. This will validate the URL and handle "../"
|
|
* and will provide support for Active Server Pages. As the handler is the
|
|
* last handler to run, it always indicates that it has handled the URL
|
|
* by returning 1.
|
|
*/
|
|
|
|
int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
|
|
char_t *url, char_t *path, char_t *query)
|
|
{
|
|
websStatType sbuf;
|
|
char_t *lpath, *tmp, *date;
|
|
int bytes, flags, nchars;
|
|
|
|
a_assert(websValid(wp));
|
|
a_assert(url && *url);
|
|
a_assert(path);
|
|
a_assert(query);
|
|
|
|
/*
|
|
* Validate the URL and ensure that ".."s don't give access to unwanted files
|
|
*/
|
|
flags = websGetRequestFlags(wp);
|
|
|
|
if (websValidateUrl(wp, path) < 0) {
|
|
websError(wp, 500, T("Invalid URL %s"), url);
|
|
return 1;
|
|
}
|
|
lpath = websGetRequestLpath(wp);
|
|
nchars = gstrlen(lpath) - 1;
|
|
if (lpath[nchars] == '/' || lpath[nchars] == '\\') {
|
|
lpath[nchars] = '\0';
|
|
}
|
|
|
|
/*
|
|
* If the file is a directory, redirect using the nominated default page
|
|
*/
|
|
if (websPageIsDirectory(lpath)) {
|
|
nchars = gstrlen(path);
|
|
if (path[nchars-1] == '/' || path[nchars-1] == '\\') {
|
|
path[--nchars] = '\0';
|
|
}
|
|
nchars += gstrlen(websDefaultPage) + 2;
|
|
fmtAlloc(&tmp, nchars, T("%s/%s"), path, websDefaultPage);
|
|
websRedirect(wp, tmp);
|
|
bfreeSafe(B_L, tmp);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Open the document. Stat for later use.
|
|
*/
|
|
if (websPageOpen(wp, lpath, path, SOCKET_RDONLY | SOCKET_BINARY,
|
|
0666) < 0) {
|
|
websError(wp, 400,
|
|
T("Cannot open URL <b>%s</b>"), url);
|
|
return 1;
|
|
}
|
|
if (websPageStat(wp, lpath, path, &sbuf) < 0) {
|
|
websError(wp, 400, T("Cannot stat page for URL <b>%s</b>"),
|
|
url);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* If the page has not been modified since the user last received it and it
|
|
* is not dynamically generated each time (ASP), then optimize request by
|
|
* sending a 304 Use local copy response
|
|
*/
|
|
websStats.localHits++;
|
|
#if WEBS_IF_MODIFIED_SUPPORT
|
|
if (flags & WEBS_IF_MODIFIED && !(flags & WEBS_ASP)) {
|
|
if (sbuf.mtime <= wp->since) {
|
|
websWrite(wp, T("HTTP/1.0 304 Use local copy\r\n"));
|
|
|
|
/*
|
|
* by license terms the following line of code must
|
|
* not be modified.
|
|
*/
|
|
websWrite(wp, T("Server: %s\r\n"), WEBS_NAME);
|
|
|
|
if (flags & WEBS_KEEP_ALIVE) {
|
|
websWrite(wp, T("Connection: keep-alive\r\n"));
|
|
}
|
|
websWrite(wp, T("\r\n"));
|
|
websSetRequestFlags(wp, flags |= WEBS_HEADER_DONE);
|
|
websDone(wp, 304);
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Output the normal HTTP response header
|
|
*/
|
|
if ((date = websGetDateString(NULL)) != NULL) {
|
|
websWrite(wp, T("HTTP/1.0 200 OK\r\nDate: %s\r\n"), date);
|
|
|
|
/*
|
|
* By license terms the following line of code must not be modified.
|
|
*/
|
|
websWrite(wp, T("Server: %s\r\n"), WEBS_NAME);
|
|
bfree(B_L, date);
|
|
}
|
|
flags |= WEBS_HEADER_DONE;
|
|
|
|
/*
|
|
* If this is an ASP request, ensure the remote browser doesn't cache it.
|
|
* Send back both HTTP/1.0 and HTTP/1.1 cache control directives
|
|
*/
|
|
if (flags & WEBS_ASP) {
|
|
bytes = 0;
|
|
websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
|
|
|
|
} else {
|
|
if ((date = websGetDateString(&sbuf)) != NULL) {
|
|
websWrite(wp, T("Last-modified: %s\r\n"), date);
|
|
bfree(B_L, date);
|
|
}
|
|
bytes = sbuf.size;
|
|
}
|
|
|
|
if (bytes) {
|
|
websWrite(wp, T("Content-length: %d\r\n"), bytes);
|
|
websSetRequestBytes(wp, bytes);
|
|
}
|
|
websWrite(wp, T("Content-type: %s\r\n"), websGetRequestType(wp));
|
|
|
|
if ((flags & WEBS_KEEP_ALIVE) && !(flags & WEBS_ASP)) {
|
|
websWrite(wp, T("Connection: keep-alive\r\n"));
|
|
}
|
|
websWrite(wp, T("\r\n"));
|
|
|
|
/*
|
|
* All done if the browser did a HEAD request
|
|
*/
|
|
if (flags & WEBS_HEAD_REQUEST) {
|
|
websDone(wp, 200);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Evaluate ASP requests
|
|
*/
|
|
if (flags & WEBS_ASP) {
|
|
if (websAspRequest(wp, lpath) < 0) {
|
|
return 1;
|
|
}
|
|
websDone(wp, 200);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef WEBS_SSL_SUPPORT
|
|
if (wp->flags & WEBS_SECURE) {
|
|
websDefaultWriteEvent(wp);
|
|
} else {
|
|
websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent);
|
|
}
|
|
#else
|
|
/*
|
|
* For normal web documents, return the data via background write
|
|
*/
|
|
websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Validate the URL path and process ".." path segments. Return -1 if the URL
|
|
* is bad.
|
|
*/
|
|
|
|
int websValidateUrl(webs_t wp, char_t *path)
|
|
{
|
|
char_t *parts[64]; /* Array of ptr's to URL parts */
|
|
char_t *token, *dir, *lpath;
|
|
int i, len, npart;
|
|
|
|
a_assert(websValid(wp));
|
|
a_assert(path);
|
|
|
|
dir = websGetRequestDir(wp);
|
|
if (dir == NULL || *dir == '\0') {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Copy the string so we don't destroy the original
|
|
*/
|
|
path = bstrdup(B_L, path);
|
|
websDecodeUrl(path, path, gstrlen(path));
|
|
|
|
len = npart = 0;
|
|
parts[0] = NULL;
|
|
token = gstrtok(path, T("/"));
|
|
|
|
/*
|
|
* Look at each directory segment and process "." and ".." segments
|
|
* Don't allow the browser to pop outside the root web.
|
|
*/
|
|
while (token != NULL) {
|
|
if (gstrcmp(token, T("..")) == 0) {
|
|
if (npart > 0) {
|
|
npart--;
|
|
}
|
|
|
|
} else if (gstrcmp(token, T(".")) != 0) {
|
|
parts[npart] = token;
|
|
len += gstrlen(token) + 1;
|
|
npart++;
|
|
}
|
|
token = gstrtok(NULL, T("/"));
|
|
}
|
|
|
|
/*
|
|
* Create local path for document. Need extra space all "/" and null.
|
|
*/
|
|
if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0')) {
|
|
lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t));
|
|
gstrcpy(lpath, dir);
|
|
|
|
for (i = 0; i < npart; i++) {
|
|
gstrcat(lpath, T("/"));
|
|
gstrcat(lpath, parts[i]);
|
|
}
|
|
websSetRequestLpath(wp, lpath);
|
|
bfree(B_L, path);
|
|
bfree(B_L, lpath);
|
|
|
|
} else {
|
|
bfree(B_L, path);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Do output back to the browser in the background. This is a socket
|
|
* write handler.
|
|
*/
|
|
|
|
static void websDefaultWriteEvent(webs_t wp)
|
|
{
|
|
int len, wrote, flags, bytes, written;
|
|
char *buf;
|
|
|
|
a_assert(websValid(wp));
|
|
|
|
flags = websGetRequestFlags(wp);
|
|
|
|
websMarkTime(wp);
|
|
|
|
wrote = bytes = 0;
|
|
written = websGetRequestWritten(wp);
|
|
|
|
/*
|
|
* We only do this for non-ASP documents
|
|
*/
|
|
if ( !(flags & WEBS_ASP)) {
|
|
bytes = websGetRequestBytes(wp);
|
|
/*
|
|
* Note: websWriteDataNonBlock may return less than we wanted. It will
|
|
* return -1 on a socket error
|
|
*/
|
|
if ((buf = balloc(B_L, PAGE_READ_BUFSIZE)) == NULL) {
|
|
websError(wp, 200, T("Can't get memory"));
|
|
}
|
|
else {
|
|
while ((len = websPageReadData(wp, buf, PAGE_READ_BUFSIZE)) > 0) {
|
|
if ((wrote = websWriteDataNonBlock(wp, buf, len)) < 0) {
|
|
break;
|
|
}
|
|
written += wrote;
|
|
if (wrote != len) {
|
|
websPageSeek(wp, - (len - wrote));
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* Safety. If we are at EOF, we must be done
|
|
*/
|
|
if (len == 0) {
|
|
a_assert(written >= bytes);
|
|
written = bytes;
|
|
}
|
|
bfree(B_L, buf);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We're done if an error, or all bytes output
|
|
*/
|
|
websSetRequestWritten(wp, written);
|
|
if (wrote < 0 || written >= bytes) {
|
|
websDone(wp, 200);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Closing down. Free resources.
|
|
*/
|
|
|
|
void websDefaultClose()
|
|
{
|
|
if (websDefaultPage) {
|
|
bfree(B_L, websDefaultPage);
|
|
websDefaultPage = NULL;
|
|
}
|
|
if (websDefaultDir) {
|
|
bfree(B_L, websDefaultDir);
|
|
websDefaultDir = NULL;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Get the default page for URL requests ending in "/"
|
|
*/
|
|
|
|
char_t *websGetDefaultPage()
|
|
{
|
|
return websDefaultPage;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Get the default web directory
|
|
*/
|
|
|
|
char_t *websGetDefaultDir()
|
|
{
|
|
return websDefaultDir;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Set the default page for URL requests ending in "/"
|
|
*/
|
|
|
|
void websSetDefaultPage(char_t *page)
|
|
{
|
|
a_assert(page && *page);
|
|
|
|
if (websDefaultPage) {
|
|
bfree(B_L, websDefaultPage);
|
|
}
|
|
websDefaultPage = bstrdup(B_L, page);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Set the default web directory
|
|
*/
|
|
|
|
void websSetDefaultDir(char_t *dir)
|
|
{
|
|
a_assert(dir && *dir);
|
|
if (websDefaultDir) {
|
|
bfree(B_L, websDefaultDir);
|
|
}
|
|
websDefaultDir = bstrdup(B_L, dir);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|