/*****************************************************************************/
/*
Descr.c
This module implements a full multi-threaded, AST-driven, asynchronous
description generator for a file.
This is not a full task in the sense of a file transfer. This module merely
determines a file description. For an HTML file this is by looking for the
contents of a
or tag pair. For a plain text file,
the first non-blank line. If no internal description can be determined the
buffer is made an empty string.
This module never returns a valid status and ALWAYS calls the supplied next
task (AST) function. This should check for a generated error message to
determine is there were any problems.
VERSION HISTORY
---------------
06-OCT-2010 MGD DescriptionError() !&S to %X!8XL
05-OCT-2002 MGD refine VMS security profile usage
27-APR-2002 MGD make SYSPRV enabled ASTs asynchronous
04-AUG-2001 MGD support module WATCHing
02-JAN-2000 MGD no significant modifications for ODS-5 (no NAM block)
20-NOV-1999 MGD increase buffer size to 2048 (in part to allow for longer
"lines", also to accomodate fixed, 512 byte, FTPed records).
19-SEP-1998 MGD improve granularity of file open, connect, close
25-OCT-1997 MGD DescriptionEscape() now includes only printable characters
17-AUG-1997 MGD message database,
SYSUAF-authenticated users security-profile
01-FEB-1997 MGD HTTPd version 4
23-MAY-1996 MGD functionality generalized, moved from directory module
*/
/*****************************************************************************/
#ifdef WASD_VMS_V7
#undef _VMS__V6__SOURCE
#define _VMS__V6__SOURCE
#undef __VMS_VER
#define __VMS_VER 70000000
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif
/* standard C header files */
#include
#include
#include
/* VMS related header files */
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* application related header files */
#include "wasd.h"
#define WASD_MODULE "DESCR"
/********************/
/* external storage */
/********************/
extern int ToLowerCase[],
ToUpperCase[];
extern char SoftwareID[];
extern CONFIG_STRUCT Config;
extern MSG_STRUCT Msgs;
extern WATCH_STRUCT Watch;
/*****************************************************************************/
/*
*/
Description
(
REQUEST_STRUCT *rqptr,
REQUEST_AST NextTaskFunction,
char *FileName,
char *DescriptionBuffer,
int SizeOfDescriptionBuffer,
int ContentTypes
)
{
int status;
char *cptr, *sptr, *zptr;
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"Description() !&A !AZ", NextTaskFunction, FileName);
if (ERROR_REPORTED (rqptr))
{
/* previous error, cause threaded processing to unravel */
SysDclAst (NextTaskFunction, rqptr);
return;
}
/* set up the task structure (possibly multiple serially) */
if (rqptr->DescrTaskPtr)
{
tkptr = rqptr->DescrTaskPtr;
memset (tkptr, 0, sizeof(DESCR_TASK));
}
else
{
rqptr->DescrTaskPtr = tkptr = (DESCR_TASK*)
VmGetHeap (rqptr, sizeof(DESCR_TASK));
}
tkptr->NextTaskFunction = NextTaskFunction;
/* setup using the calling function's buffer space */
*(tkptr->DescriptionBufferPtr = DescriptionBuffer) = '\0';
tkptr->SizeOfDescriptionBuffer = SizeOfDescriptionBuffer;
/* indicate no description could be generated */
tkptr->DescriptionBufferPtr[0] = DESCRIPTION_IMPOSSIBLE;
cptr = FileName;
zptr = (sptr = tkptr->FileName) + sizeof(tkptr->FileName);
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
if (sptr >= zptr)
{
ErrorGeneralOverflow (rqptr, FI_LI);
DescriptionEnd (rqptr);
return;
}
*sptr = '\0';
tkptr->FileNameLength = sptr - tkptr->FileName;
/***************/
/* description */
/***************/
/* using the pointer from above find the file type */
while (sptr > tkptr->FileName && *sptr != '.') sptr--;
cptr = ConfigContentType (NULL, sptr);
if (TOLO(cptr[0]) == 't' &&
ConfigSameContentType (cptr, "text/", 5))
{
if ((ContentTypes & DESCRIPTION_TEXT_HTML ||
ContentTypes & DESCRIPTION_ALL) &&
ConfigSameContentType (cptr, "text/html", -1))
{
tkptr->TextHtmlFile = true;
DescriptionOpen (rqptr);
return;
}
else
if ((ContentTypes & DESCRIPTION_TEXT_PLAIN ||
ContentTypes & DESCRIPTION_ALL) &&
ConfigSameContentType (cptr, "text/plain", -1))
{
tkptr->TextHtmlFile = false;
DescriptionOpen (rqptr);
return;
}
}
/* declare the next task */
SysDclAst (tkptr->NextTaskFunction, rqptr);
}
/*****************************************************************************/
/*
*/
DescriptionEnd (REQUEST_STRUCT *rqptr)
{
int status;
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
/* retrieve the pointer to the task structure */
tkptr = rqptr->DescrTaskPtr;
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "DescriptionEnd() !&F !AZ",
&DescriptionEnd, tkptr->DescriptionBufferPtr);
if (tkptr->FileFab.fab$w_ifi)
{
tkptr->FileFab.fab$l_fop |= FAB$M_ASY;
sys$close (&tkptr->FileFab, &DescriptionCloseAst, &DescriptionCloseAst);
}
else
SysDclAst (tkptr->NextTaskFunction, rqptr);
}
/*****************************************************************************/
/*
AST delivered after asynchronous close in DescriptionEnd().
*/
DescriptionCloseAst (struct FAB *FabPtr)
{
/*********/
/* begin */
/*********/
if (WATCH_MOD)
{
REQUEST_STRUCT *rqptr;
rqptr = FabPtr->fab$l_ctx;
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DescriptionCloseAst() !&F sts!&S stv:!&S",
&DescriptionCloseAst, FabPtr->fab$l_sts, FabPtr->fab$l_stv);
}
DescriptionEnd (FabPtr->fab$l_ctx);
}
/*****************************************************************************/
/*
*/
DescriptionOpen (REQUEST_STRUCT *rqptr)
{
int status;
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DescriptionOpen() !&F !AZ !UL",
&DescriptionOpen,
rqptr->DescrTaskPtr->FileName,
rqptr->DescrTaskPtr->TextHtmlFile);
tkptr = rqptr->DescrTaskPtr;
tkptr->FileFab = cc$rms_fab;
tkptr->FileFab.fab$l_ctx = rqptr;
tkptr->FileFab.fab$b_fac = FAB$M_GET;
tkptr->FileFab.fab$l_fna = tkptr->FileName;
tkptr->FileFab.fab$b_fns = tkptr->FileNameLength;
tkptr->FileFab.fab$b_shr = FAB$M_SHRGET;
AuthAccessEnable (rqptr, tkptr->FileName, AUTH_ACCESS_READ);
tkptr->FileFab.fab$l_fop |= FAB$M_ASY;
sys$open (&tkptr->FileFab, &DescriptionOpenAst, &DescriptionOpenAst);
AuthAccessEnable (rqptr, 0, 0);
}
/*****************************************************************************/
/*
AST called from DescriptionOpen() when asynchronous open completes. Initiate
an asynchronous RAB connect.
*/
DescriptionOpenAst (struct FAB *FabPtr)
{
int status,
MultiBlockCount;
REQUEST_STRUCT *rqptr;
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
rqptr = FabPtr->fab$l_ctx;
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DescriptionOpenAst() !&F sts:!&S stv:!&S",
&DescriptionOpenAst, FabPtr->fab$l_sts, FabPtr->fab$l_stv);
#if WATCH_MOD
HttpdCheckPriv (FI_LI);
#endif /* WATCH_MOD */
tkptr = rqptr->DescrTaskPtr;
if (VMSnok (status = FabPtr->fab$l_sts))
{
DescriptionError (rqptr, status, __LINE__);
DescriptionEnd (rqptr);
return;
}
tkptr->FileRab = cc$rms_rab;
tkptr->FileRab.rab$l_ctx = rqptr;
tkptr->FileRab.rab$l_fab = &tkptr->FileFab;
/* 2 buffers and read ahead performance option */
tkptr->FileRab.rab$b_mbf = 2;
tkptr->FileRab.rab$l_rop = RAB$M_RAH | RAB$M_ASY;
tkptr->FileRab.rab$l_ubf = tkptr->ReadBuffer;
tkptr->FileRab.rab$w_usz = sizeof(tkptr->ReadBuffer)-1;
sys$connect (&tkptr->FileRab, &DescriptionConnectAst,
&DescriptionConnectAst);
}
/*****************************************************************************/
/*
AST called from FileOpenAst() when asynchronous RAB connect completes.
Initiate stream-LF file conversion if the file meets requirements. If
suitable for caching then initiate a cache load and begin reading the file
contents.
*/
DescriptionConnectAst (struct RAB *RabPtr)
{
int status;
REQUEST_STRUCT *rqptr;
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
rqptr = RabPtr->rab$l_ctx;
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DescriptionConnectAst() !&F sts:!&S stv:!&S",
&DescriptionConnectAst, RabPtr->rab$l_sts, RabPtr->rab$l_stv);
tkptr = rqptr->DescrTaskPtr;
if (VMSnok (status = RabPtr->rab$l_sts))
{
DescriptionError (rqptr, status, __LINE__);
DescriptionEnd (rqptr);
return;
}
*(tkptr->DescriptionPtr = tkptr->DescriptionBufferPtr) = '\0';
tkptr->DescriptionRetrieved = false;
tkptr->DescriptionLineCount = 0;
/* queue up a read of the first record in the file */
if (tkptr->TextHtmlFile)
sys$get (&tkptr->FileRab, &DescriptionHtmlRecord,
&DescriptionHtmlRecord);
else
sys$get (&tkptr->FileRab, &DescriptionPlainRecord,
&DescriptionPlainRecord);
}
/*****************************************************************************/
/*
This is an AST completion routine called each time sys$get() completes. It
returns with the next record, end-of-file, or some other error. Examine the
record looking for text comprising the first of either the
element or a heading element (e.g. ), retrieving this and using it
as the file description.
*/
DescriptionHtmlRecord (struct RAB *RabPtr)
{
int status;
char *dptr, *rptr, *zptr;
REQUEST_STRUCT *rqptr;
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
rqptr = RabPtr->rab$l_ctx;
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DescriptionHtmlRecord() !&F sts:!&S stv:!&S !UL",
&DescriptionHtmlRecord, RabPtr->rab$l_sts,
RabPtr->rab$l_stv, RabPtr->rab$w_rsz);
tkptr = rqptr->DescrTaskPtr;
if (VMSnok (tkptr->FileRab.rab$l_sts))
{
if (tkptr->FileRab.rab$l_sts == RMS$_EOF)
{
/***************/
/* end-of-file */
/***************/
DescriptionEnd (rqptr);
return;
}
/**********************/
/* error reading file */
/**********************/
DescriptionError (rqptr, tkptr->FileRab.rab$l_sts, __LINE__);
DescriptionEnd (rqptr);
return;
}
tkptr->DescriptionLineCount++;
tkptr->FileRab.rab$l_ubf[tkptr->FileRab.rab$w_rsz] = '\0';
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchDataDump (tkptr->FileRab.rab$l_ubf, tkptr->FileRab.rab$w_rsz);
/****************************************/
/* look for the or element */
/****************************************/
/*
'zptr' points at the last character of the description storage.
It prevents non-closed or structures from running off
the end of the storage area.
*/
dptr = tkptr->DescriptionPtr;
zptr = tkptr->DescriptionBufferPtr + tkptr->SizeOfDescriptionBuffer - 1;
/*
Add a space for any record boundary occuring in the title,
except if it's a leading space (i.e. first after the leading quote).
*/
if (tkptr->DescriptionInside &&
dptr > tkptr->DescriptionBufferPtr+1 &&
dptr < zptr)
*dptr++ = ' ';
for (rptr = tkptr->FileRab.rab$l_ubf; *rptr; rptr++)
{
if (*rptr == '<')
{
tkptr->DescriptionOpeningTagCount++;
if (TOUP(rptr[1]) == 'T' && strsame (rptr+2, "ITLE>", 5))
{
tkptr->DescriptionInside = true;
rptr += 6;
tkptr->DescriptionClosingTagCount++;
continue;
}
if (TOUP(rptr[1]) == 'H' && isdigit(rptr[2]) && rptr[3] == '>')
{
tkptr->DescriptionInside = true;
rptr += 3;
tkptr->DescriptionClosingTagCount++;
continue;
}
if (rptr[1] == '/')
{
if (TOUP(rptr[2]) == 'T' && strsame (rptr+3, "ITLE>", 5))
{
tkptr->DescriptionInside = false;
tkptr->DescriptionRetrieved = true;
rptr += 7;
tkptr->DescriptionClosingTagCount++;
/* suppress any trailing white-space */
if (dptr > tkptr->DescriptionBufferPtr+1) dptr--;
while (dptr > tkptr->DescriptionBufferPtr+1 &&
ISLWS(*dptr)) dptr--;
*++dptr = '\0';
continue;
}
if (TOUP(rptr[2]) == 'H' && isdigit(rptr[3]) && rptr[4] == '>')
{
tkptr->DescriptionInside = false;
tkptr->DescriptionRetrieved = true;
rptr += 4;
tkptr->DescriptionClosingTagCount++;
/* suppress any trailing white-space */
if (dptr > tkptr->DescriptionBufferPtr+1) dptr--;
while (dptr > tkptr->DescriptionBufferPtr+1 &&
ISLWS(*dptr)) dptr--;
*++dptr = '\0';
continue;
}
}
}
else
if (*rptr == '>')
{
tkptr->DescriptionClosingTagCount++;
continue;
}
/* don't scan the line further if we've found what we're looking for */
if (tkptr->DescriptionRetrieved) break;
/* if we're currently inside a tag name (between "<" and ">") */
if (tkptr->DescriptionOpeningTagCount > tkptr->DescriptionClosingTagCount)
continue;
/* suppress leading white-space */
if (tkptr->DescriptionInside &&
!(dptr == tkptr->DescriptionBufferPtr && ISLWS(*rptr)) &&
isprint (*rptr) &&
dptr < zptr)
*dptr++ = *rptr;
}
/* terminate the title/description */
if (dptr > tkptr->DescriptionBufferPtr) *dptr = '\0';
tkptr->DescriptionPtr = dptr;
if (tkptr->DescriptionRetrieved ||
tkptr->DescriptionLineCount > Config.cfDir.DescriptionLines)
{
/************************************/
/* title found, or out-of-patience! */
/************************************/
DescriptionEnd (rqptr);
return;
}
else
{
/*****************/
/* still looking */
/*****************/
/* queue another read, completion AST back to this function again */
sys$get (&tkptr->FileRab, &DescriptionHtmlRecord,
&DescriptionHtmlRecord);
}
}
/*****************************************************************************/
/*
This is an AST completion routine called each time sys$get() completes. It
returns with the next record, end-of-file, or some other error.
*/
DescriptionPlainRecord (struct RAB *RabPtr)
{
int status;
char *dptr, *rptr, *zptr;
REQUEST_STRUCT *rqptr;
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
rqptr = RabPtr->rab$l_ctx;
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DescriptionPlainRecord() !&F sts:!&S stv:!&S !UL",
&DescriptionPlainRecord, RabPtr->rab$l_sts,
RabPtr->rab$l_stv, RabPtr->rab$w_rsz);
tkptr = rqptr->DescrTaskPtr;
if (VMSnok (tkptr->FileRab.rab$l_sts))
{
if (tkptr->FileRab.rab$l_sts == RMS$_EOF)
{
/***************/
/* end-of-file */
/***************/
DescriptionEnd (rqptr);
return;
}
/**********************/
/* error reading file */
/**********************/
{
DescriptionError (rqptr, tkptr->FileRab.rab$l_sts, __LINE__);
DescriptionEnd (rqptr);
return;
}
}
tkptr->DescriptionLineCount++;
tkptr->FileRab.rab$l_ubf[tkptr->FileRab.rab$w_rsz] = '\0';
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchDataDump (tkptr->FileRab.rab$l_ubf, tkptr->FileRab.rab$w_rsz);
/************************************/
/* look for first plain-text record */
/************************************/
for (rptr = tkptr->FileRab.rab$l_ubf; *rptr && !isalnum(*rptr); rptr++);
if (!isalnum(*rptr))
{
if (tkptr->DescriptionLineCount <= Config.cfDir.DescriptionLines)
{
/* queue another read, completion AST back to this function again */
sys$get (&tkptr->FileRab, &DescriptionPlainRecord,
&DescriptionPlainRecord);
return;
}
/********************/
/* out-of-patience! */
/********************/
DescriptionEnd (rqptr);
return;
}
/*******************/
/* get description */
/*******************/
dptr = tkptr->DescriptionBufferPtr;
zptr = tkptr->DescriptionBufferPtr + tkptr->SizeOfDescriptionBuffer - 1;
while (*rptr && dptr < zptr) *dptr++ = *rptr++;
*dptr = '\0';
DescriptionEscape (rqptr);
DescriptionEnd (rqptr);
}
/*****************************************************************************/
/*
Make sure we only meaningful and HTML-acceptable characters into the
description.
*/
DescriptionEscape (REQUEST_STRUCT *rqptr)
{
BOOL Changed;
char *cptr, *sptr, *zptr;
DESCR_TASK *tkptr;
char Buffer [256];
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "DescriptionEscape()");
/* retrieve the pointer to the task structure */
tkptr = rqptr->DescrTaskPtr;
Changed = false;
if (tkptr->SizeOfDescriptionBuffer > sizeof(Buffer))
zptr = (sptr = Buffer) + tkptr->SizeOfDescriptionBuffer - 1;
else
zptr = (sptr = Buffer) + sizeof(Buffer)-1;
for (cptr = tkptr->DescriptionBufferPtr; *cptr && sptr+6 < zptr; cptr++)
{
switch (*cptr)
{
case '<' :
memcpy (sptr, "<", 4); sptr += 4; Changed = true; break;
case '>' :
memcpy (sptr, ">", 4); sptr += 4; Changed = true; break;
case '&' :
memcpy (sptr, "&", 5); sptr += 5; Changed = true; break;
case '\"' :
*sptr++ = '\''; Changed = true; break;
default:
if (isprint(*cptr)) *sptr++ = *cptr;
}
}
*sptr++ = '\0';
if (Changed) memcpy (tkptr->DescriptionBufferPtr, Buffer, sptr - Buffer);
}
/*****************************************************************************/
/*
Put the hexadecimal VMS status value into the description buffer as an error
indication.
*/
DescriptionError
(
REQUEST_STRUCT *rqptr,
int StatusValue,
int SourceLineNumber
)
{
static $DESCRIPTOR (ErrorFaoDsc,
"[Error %X!8XL]\0");
static $DESCRIPTOR (BufferDsc, "");
DESCR_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DescriptionError() !&S", StatusValue);
tkptr = rqptr->DescrTaskPtr;
BufferDsc.dsc$a_pointer = tkptr->DescriptionBufferPtr;
BufferDsc.dsc$w_length = tkptr->SizeOfDescriptionBuffer;
sys$fao (&ErrorFaoDsc, 0, &BufferDsc, StatusValue, SourceLineNumber);
}
/*****************************************************************************/