/*****************************************************************************
/*
Support.c
Miscellaneous support functions for HTTPd.
VERSION HISTORY
---------------
12-JUL-2021 MGD rework UTC/GMT processing
GenerateUniqueId() algorithm changed
bugfix; HttpIsoTime() timezone parsing
06-MAR-2021 MGD HttpGetTime()
14-NOV-2020 MGD migrate from ulong[2] to int64 for 64 bit quantities
26-JUL-2018 MGD SysTrnLnm() moved from dyanamic to static return
23-DEC-2017 MGD HttpIsoTimeString()
30-OCT-2017 MGD ThisLongAgo()
04-MAY-2017 MGD HtmlMetaInfo() MS Edge
#include
#include
#include
#include
/* VMS related header files */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* lib$cvts_from_internal_time() does not exist with V7.3-2 or earlier */
#include
#include
#define CVT$K_VAX_F 0
#define CVT$K_IEEE_S 4
/* application related header files */
#include "wasd.h"
#define WASD_MODULE "SUPPORT"
/******************/
/* global storage */
/******************/
#define RFC_1123_DATE yup
const int64 DeltaOneDay = DELTA64_ONE_DAY,
DeltaOneHour = DELTA64_ONE_HR,
Delta01Sec = DELTA64_ONE_SEC,
Delta02Sec = DELTA64_TWO_SEC,
Delta05Sec = DELTA64_FIVE_SEC,
Delta10Sec = DELTA64_TEN_SEC,
Delta30Sec = DELTA64_THIRTY_SEC,
Delta60Sec = DELTA64_ONE_MIN,
Delta001mSec = DELTA64_ONE_MSEC,
Delta010mSec = DELTA64_TEN_MSEC,
Delta100mSec = DELTA64_HUN_MSEC,
Delta001uSec = DELTA64_ONE_USEC,
Delta010uSec = DELTA64_TEN_USEC,
Delta100uSec = DELTA64_HUN_USEC;
int64 TimeUtcDelta64;
BOOL TimeAheadOfGmt;
char TimeGmtString [16],
TimeZoneString [16];
char *DayName [] =
{ "", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday" };
char *MonthName [] =
{ "", "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" };
/* these arrays are used with the TOUP() and TOLO() macros defined in WASD.H */
int ToUpperCase [] =
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,123,124,125,126,127,
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
};
int ToLowerCase [] =
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
};
/********************/
/* external storage */
/********************/
extern BOOL AccountingZeroOnStartup,
CliSoftwareID,
MonitorEnabled;
extern int CharsetCount,
EfnWait,
ServerPort;
extern ulong SysPrvMask[];
extern char ErrorSanityCheck[],
HttpdName[],
HttpdVersion[],
ServerHostName[],
SoftwareID[];
extern ACCOUNTING_STRUCT *AccountingPtr;
extern CONFIG_STRUCT Config;
extern HTTPD_GBLSEC *HttpdGblSecPtr;
extern HTTPD_PROCESS HttpdProcess;
extern MSG_STRUCT Msgs;
extern WATCH_STRUCT Watch;
/*****************************************************************************/
/*
Declare an AST, exit if there is any problem ... must have our ASTs!
*/
void SysDclAst
(
void *Address,
ulong Parameter
)
{
int status;
/*********/
/* begin */
/*********/
if (0 && WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"SysDclAst() !&A(!&X)", Address, Parameter);
status = sys$dclast (Address, Parameter, 0);
if (VMSok (status)) return;
if (status == SS$_EXQUOTA)
{
/* no ASTs means not much of anything else can happen so just exit! */
sys$canexh(0);
/* make the message a little more meaningful */
sys$exit (SS$_EXASTLM);
}
else
ErrorExitVmsStatus (status, "sys$dclast()", FI_LI);
}
/*****************************************************************************/
/*
Generic list handling function. Add entry to head of list.
*/
LIST_ENTRY* ListAddHead
(
LIST_HEAD *lptr,
LIST_ENTRY *eptr,
ulong DataType
)
{
/*********/
/* begin */
/*********/
if (!lptr->HeadPtr)
{
/* empty list */
lptr->HeadPtr = lptr->TailPtr = eptr;
eptr->PrevPtr = eptr->NextPtr = NULL;
if (DataType) eptr->DataType = DataType;
lptr->EntryCount++;
return (eptr);
}
else
{
/* non-empty list */
eptr->PrevPtr = NULL;
eptr->NextPtr = lptr->HeadPtr;
eptr->NextPtr->PrevPtr = lptr->HeadPtr = eptr;
if (DataType) eptr->DataType = DataType;
lptr->EntryCount++;
return (eptr);
}
}
/*****************************************************************************/
/*
Generic list handling function. Add entry to tail of list.
*/
LIST_ENTRY* ListAddTail
(
LIST_HEAD *lptr,
LIST_ENTRY *eptr,
ulong DataType
)
{
/*********/
/* begin */
/*********/
if (!lptr->HeadPtr)
{
/* empty list */
lptr->HeadPtr = lptr->TailPtr = eptr;
eptr->PrevPtr = eptr->NextPtr = NULL;
if (DataType) eptr->DataType = DataType;
lptr->EntryCount++;
return (eptr);
}
else
{
/* non-empty list */
eptr->NextPtr = NULL;
eptr->PrevPtr = lptr->TailPtr;
eptr->PrevPtr->NextPtr = lptr->TailPtr = eptr;
if (DataType) eptr->DataType = DataType;
lptr->EntryCount++;
return (eptr);
}
}
/*****************************************************************************/
/*
Generic list handling function. Add entry 2 "in front of" entry 1.
*/
LIST_ENTRY* ListAddBefore
(
LIST_HEAD *lptr,
LIST_ENTRY *e1ptr,
LIST_ENTRY *e2ptr,
ulong DataType
)
{
/*********/
/* begin */
/*********/
if (lptr->HeadPtr == e1ptr)
{
/* at head of list */
e1ptr->PrevPtr = e2ptr;
e2ptr->PrevPtr = NULL;
e2ptr->NextPtr = e1ptr;
lptr->HeadPtr = e2ptr;
if (DataType) e2ptr->DataType = DataType;
lptr->EntryCount++;
return (e2ptr);
}
else
{
/* not at head of list */
if (e1ptr->PrevPtr)
{
e2ptr->PrevPtr = e1ptr->PrevPtr;
e2ptr->PrevPtr->NextPtr = e2ptr;
}
e1ptr->PrevPtr = e2ptr;
e2ptr->NextPtr = e1ptr;
if (DataType) e2ptr->DataType = DataType;
lptr->EntryCount++;
return (e2ptr);
}
}
/*****************************************************************************/
/*
Generic list handling function. Remove entry from list. Check first that
entry looks legitimate, return NULL if not!
*/
LIST_ENTRY* ListRemove
(
LIST_HEAD *lptr,
LIST_ENTRY *eptr
)
{
/*********/
/* begin */
/*********/
if (!eptr->PrevPtr)
{
/* at head of list */
if (!eptr->NextPtr)
{
/* only entry in list */
if (lptr->HeadPtr != eptr || lptr->TailPtr != eptr) return (NULL);
lptr->HeadPtr = lptr->TailPtr = NULL;
if (lptr->EntryCount) lptr->EntryCount--;
return (eptr);
}
else
{
/* remove from head of list */
if (lptr->HeadPtr != eptr) return (NULL);
eptr->NextPtr->PrevPtr = NULL;
lptr->HeadPtr = eptr->NextPtr;
if (lptr->EntryCount) lptr->EntryCount--;
return (eptr);
}
}
else
{
/* not at head of list */
if (!eptr->NextPtr)
{
/* at tail of list */
if (lptr->TailPtr != eptr) return (NULL);
eptr->PrevPtr->NextPtr = NULL;
lptr->TailPtr = eptr->PrevPtr;
if (lptr->EntryCount) lptr->EntryCount--;
return (eptr);
}
else
{
/* somewhere in the middle! */
if (eptr->PrevPtr->NextPtr != eptr ||
eptr->NextPtr->PrevPtr != eptr) return (NULL);
eptr->PrevPtr->NextPtr = eptr->NextPtr;
eptr->NextPtr->PrevPtr = eptr->PrevPtr;
if (lptr->EntryCount) lptr->EntryCount--;
return (eptr);
}
}
}
/*****************************************************************************/
/*
Generic list handling function. If not already there move it to the head.
*/
#ifdef __DECC
#pragma inline(ListMoveHead)
#endif
LIST_ENTRY* ListMoveHead
(
LIST_HEAD *lptr,
LIST_ENTRY *eptr
)
{
/*********/
/* begin */
/*********/
/* move entry to the head of the cache list */
if (lptr->HeadPtr != eptr)
{
ListRemove (lptr, eptr);
ListAddHead (lptr, eptr, 0);
}
return (eptr);
}
/*****************************************************************************/
/*
Generic list handling function. If not already there move it to the tail.
*/
#ifdef __DECC
#pragma inline(ListMoveTail)
#endif
LIST_ENTRY* ListMoveTail
(
LIST_HEAD *lptr,
LIST_ENTRY *eptr
)
{
/*********/
/* begin */
/*********/
/* move entry to the head of the cache list */
if (lptr->TailPtr != eptr)
{
ListRemove (lptr, eptr);
ListAddTail (lptr, eptr, 0);
}
return (eptr);
}
/*****************************************************************************/
/*
Generic list handling function. Remove the head entry.
*/
#ifdef __DECC
#pragma inline(ListRemoveHead)
#endif
LIST_ENTRY* ListRemoveHead (LIST_HEAD *lptr)
{
LIST_ENTRY *eptr;
/*********/
/* begin */
/*********/
if (eptr = lptr->HeadPtr) ListRemove (lptr, eptr);
return (eptr);
}
/*****************************************************************************/
/*
Generic list handling function. Remove the tail entry.
*/
#ifdef __DECC
#pragma inline(ListRemoveTail)
#endif
LIST_ENTRY* ListRemoveTail (LIST_HEAD *lptr)
{
LIST_ENTRY *eptr;
/*********/
/* begin */
/*********/
if (eptr = lptr->TailPtr) ListRemove (lptr, eptr);
return (eptr);
}
/*****************************************************************************/
/*
Generic list handling function. Check if a specific entry is in specific list.
*/
LIST_ENTRY* ListCheck
(
LIST_HEAD *lptr,
LIST_ENTRY *eptr
)
{
LIST_ENTRY *teptr;
/*********/
/* begin */
/*********/
fprintf (stdout, "ListCheck() %d %d %d %d\n",
lptr, lptr->HeadPtr, lptr->TailPtr, lptr->EntryCount);
for (teptr = lptr->HeadPtr; teptr; teptr = teptr->NextPtr)
if (teptr == eptr) return (eptr);
return (NULL);
}
/*****************************************************************************/
/*
list the entries in the list.
*/
ListDebug (LIST_HEAD* lptr)
{
LIST_ENTRY *eptr;
/*********/
/* begin */
/*********/
for (eptr = lptr->HeadPtr; eptr; eptr = eptr->NextPtr)
fprintf (stdout, "%d <- %d -> %d\n", eptr->PrevPtr, eptr, eptr->NextPtr);
}
/*****************************************************************************/
/*
Generate the appropriate server signature returned the supplied buffer.
*/
char* ServerSignature
(
REQUEST_STRUCT *rqptr,
char *BufferPtr,
int BufferSize
)
{
int status,
ServerPortNumber;
char *cptr, *sptr, *zptr,
*ServerHostNamePtr;
char MsgBuffer [256],
EmailBuffer [256];
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER, "ServerSignature()");
if (!Config.cfServer.Signature)
{
BufferPtr[0] = '\0';
return (BufferPtr);
}
if (rqptr)
{
ServerHostNamePtr = rqptr->ServicePtr->ServerHostName;
ServerPortNumber = rqptr->ServicePtr->ServerPort;
}
else
{
ServerHostNamePtr = ServerHostName;
ServerPortNumber = ServerPort;
}
if (Config.cfServer.Signature == CONFIG_SERVER_SIGNATURE_EMAIL &&
Config.cfServer.AdminEmail[0])
{
status = FaoToBuffer (EmailBuffer, sizeof(EmailBuffer), NULL,
"!AZ",
Config.cfServer.AdminEmail, ServerHostNamePtr);
if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
}
else
EmailBuffer[0] = '\0';
if (CliSoftwareID)
{
cptr = MsgFor(rqptr, MSG_STATUS_SIGNATURE);
zptr = (sptr = MsgBuffer) + sizeof(MsgBuffer)-1;
while (*cptr && *cptr != '!' && sptr < zptr) *sptr++ = *cptr++;
if (strsame (cptr, "!AZ/", 4))
{
cptr += 4;
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
*sptr = '\0';
status = FaoToBuffer (BufferPtr, BufferSize, NULL, MsgBuffer,
SoftwareID,
EmailBuffer[0] ? EmailBuffer : ServerHostNamePtr,
ServerPortNumber);
if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
return (BufferPtr);
}
ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}
status = FaoToBuffer (BufferPtr, BufferSize, NULL,
MsgFor(rqptr, MSG_STATUS_SIGNATURE),
HttpdName, HttpdVersion,
EmailBuffer[0] ? EmailBuffer : ServerHostNamePtr,
ServerPortNumber);
if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
return (BufferPtr);
}
/*****************************************************************************/
/*
Create an HTTP format local time string in the storage pointed at by
'TimeString', e.g. "Fri, 25 Aug 1995 17:32:40" (RFC1123). If 'Time64Ptr' is
null the time string represents the current time. If it points to a quadword,
VMS time value the string represents that time. 'TimeString' must point to
storage large enough for 31 characters.
*/
int HttpLocalTimeString
(
char *TimeString,
int64 *Time64Ptr
)
{
/* alternate formats for time string */
#ifdef RFC_1123_DATE
/* day, dd mmm yyyy hh:mm */
static $DESCRIPTOR (HttpTimeFaoDsc, "!3AZ, !2ZW !3AZ !4ZW !2ZW:!2ZW:!2ZW");
#else
/* weekday, dd-mmm-yyyy hh:mm */
static $DESCRIPTOR (HttpTimeFaoDsc, "!3AZ, !2ZW-!3AZ-!4ZW !2ZW:!2ZW:!2ZW");
#endif
static $DESCRIPTOR (TimeStringDsc, "");
int status;
int64 time64;
ushort Length;
ushort time7 [7];
ulong DayOfWeek;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER, "HttpLocalTimeString()");
if (Time64Ptr)
time64 = *(UINT64PTR)Time64Ptr;
else
sys$gettim (&time64);
status = sys$numtim (&time7, &time64);
if (VMSnok (status = lib$day_of_week (&time64, &DayOfWeek)))
return (status);
/* set the descriptor address and size of the resultant time string */
TimeStringDsc.dsc$w_length = 25;
TimeStringDsc.dsc$a_pointer = TimeString;
status = sys$fao (&HttpTimeFaoDsc, &Length, &TimeStringDsc,
DayName[DayOfWeek], time7[2], MonthName[time7[1]],
time7[0], time7[3], time7[4], time7[5]);
if (VMSnok (status) || status == SS$_BUFFEROVF)
TimeString[0] = '\0';
else
TimeString[Length] = '\0';
return (status);
}
/*****************************************************************************/
/*
Create an HTTP format Greenwich Mean Time (UTC) time string in the storage
pointed at by 'TimeString', e.g. "Fri, 25 Aug 1995 17:32:40 GMT" (RFC1123).
This must be at least 30 characters capacity. If 'Time64Ptr' is NULL the
time string represents the current time. If it points to a quadword, VMS time
value the string represents that time.
*/
int HttpGmTimeString
(
char *TimeString,
int64 *Time64Ptr
)
{
/* alternate formats for time string */
#ifdef RFC_1123_DATE
/* day, dd mmm yyyy hh:mm */
static $DESCRIPTOR (HttpTimeFaoDsc,
"!3AZ, !2ZW !3AZ !4ZW !2ZW:!2ZW:!2ZW GMT");
#else
/* weekday, dd-mmm-yyyy hh:mm */
static $DESCRIPTOR (HttpTimeFaoDsc,
"!3AZ, !2ZW-!3AZ-!4ZW !2ZW:!2ZW:!2ZW GMT");
#endif
static $DESCRIPTOR (TimeStringDsc, "");
int status;
int64 time64;
ushort Length;
ushort time7 [7];
ulong DayOfWeek;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER, "HttpGmTimeString()");
if (Time64Ptr)
time64 = *(UINT64PTR)Time64Ptr;
else
sys$gettim (&time64);
TimeAdjustToGMT (&time64);
status = sys$numtim (&time7, &time64);
if (VMSnok (status = lib$day_of_week (&time64, &DayOfWeek)))
return (status);
/* set the descriptor address and size of the resultant time string */
TimeStringDsc.dsc$w_length = 29;
TimeStringDsc.dsc$a_pointer = TimeString;
status = sys$fao (&HttpTimeFaoDsc, &Length, &TimeStringDsc,
DayName[DayOfWeek], time7[2], MonthName[time7[1]],
time7[0], time7[3], time7[4], time7[5]);
if (VMSnok (status) || status == SS$_BUFFEROVF)
TimeString[0] = '\0';
else
TimeString[Length] = '\0';
return (status);
}
/*****************************************************************************/
/*
Create an ISO 8601 format UTC (GMT) basic time string in the storage pointed at
by 'TimeString', e.g. "19950825T173240.34Z+0000", or separated local time
string, e.g. "1995-08-25T17:32:40.34Z+0930". This must be at least 32
characters capacity. If 'Time64Ptr' is NULL the time string represents the
current time. If it points to a quadword, VMS time value the string represents
that time.
*/
int HttpIsoTimeString
(
BOOL LocalTimeZone,
char *TimeString,
int64 *Time64Ptr
)
{
static $DESCRIPTOR (UtcTimeFaoDsc,
"!4ZL!2ZL!2ZLT!2ZL!2ZL!2ZL.!2ZL+0000");
static $DESCRIPTOR (LocalTimeFaoDsc,
"!4ZL-!2ZL-!2ZLT!2ZL:!2ZL:!2ZL.!2ZL!AZ");
static $DESCRIPTOR (TimeStringDsc, "");
int status;
int64 time64;
ushort Length;
ushort time7 [7];
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER, "HttpIsoTimeString()");
if (Time64Ptr)
time64 = *(UINT64PTR)Time64Ptr;
else
sys$gettim (&time64);
if (!LocalTimeZone) TimeAdjustToGMT (&time64);
status = sys$numtim (&time7, &time64);
/* set the descriptor address and size of the resultant time string */
TimeStringDsc.dsc$w_length = 31;
TimeStringDsc.dsc$a_pointer = TimeString;
if (LocalTimeZone)
status = sys$fao (&LocalTimeFaoDsc, &Length, &TimeStringDsc,
time7[0], time7[1], time7[2],
time7[3], time7[4], time7[5], time7[6],
TimeZoneString);
else
status = sys$fao (&UtcTimeFaoDsc, &Length, &TimeStringDsc,
time7[0], time7[1], time7[2],
time7[3], time7[4], time7[5], time7[6]);
if (VMSnok (status) || status == SS$_BUFFEROVF)
TimeString[0] = '\0';
else
TimeString[Length] = '\0';
return (status);
}
/*****************************************************************************/
/*
Given a string such as "Fri, 25 Aug 1995 17:32:40 GMT" (RFC1123),
or "Friday, 25-Aug-1995 17:32:40 GMT" (RFC 1036),
or "1995-08-25T17:32:40.34Z+0000"" (ISO 8601),
create an internal, local, binary time.
For purposes such a if-modified-since. The RFC specifies RFC1123 but some
recently use ISO 8601. This function tries the 1123 format first and then the
8601.
*/
int HttpGetTime
(
char *TimeString,
int64 *Time64Ptr
)
{
int status;
/*********/
/* begin */
/*********/
if (VMSnok (status = HttpGmTime (TimeString, Time64Ptr)))
status = HttpIsoTime (TimeString, Time64Ptr);
return (status);
}
/*****************************************************************************/
/*
Given a string such as "Fri, 25 Aug 1995 17:32:40 GMT" (RFC1123), or "Friday,
25-Aug-1995 17:32:40 GMT" (RFC 1036), create an internal, local, binary time
with the current GMT offset. See complementary function HttpGmTimeString().
*/
int HttpGmTime
(
char *TimeString,
int64 *Time64Ptr
)
{
static char *MonthName [] =
{ "", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
int status;
ushort Length;
ushort time7 [7] = { 0,0,0,0,0,0,0 };
ulong DayOfWeek;
char *tptr;
/*********/
/* begin */
/*********/
tptr = TimeString;
/* hunt straight for the comma after the weekday name! */
while (*tptr && *tptr != ',') tptr++;
if (*tptr) tptr++;
/* span white space between weekday name and date */
while (*tptr && ISLWS(*tptr)) tptr++;
if (!*tptr) return (SS$_IVTIME);
/* get the date and then skip to month name */
if (isdigit(*tptr)) time7[2] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
while (*tptr && (*tptr == '-' || ISLWS(*tptr))) tptr++;
if (!*tptr) return (SS$_IVTIME);
/* get the month number from the name and skip to the year */
for (time7[1] = 1; time7[1] <= 12; time7[1]++)
if (strsame (tptr, MonthName[time7[1]], 3)) break;
if (time7[1] > 12) return (SS$_IVTIME);
while (isalpha(*tptr)) tptr++;
while (*tptr && (*tptr == '-' || ISLWS(*tptr))) tptr++;
if (!*tptr) return (SS$_IVTIME);
/* get the year and then skip to the hour */
if (isdigit(*tptr))
{
time7[0] = atoi (tptr);
if (time7[0] <= 99)
{
/* for older browsers supplying 2 digit years, some Y2K insurance! */
if (time7[0] >= 70)
time7[0] += 1900;
else
time7[0] += 2000;
}
}
while (*tptr && isdigit(*tptr)) tptr++;
while (*tptr && ISLWS(*tptr)) tptr++;
if (!*tptr) return (SS$_IVTIME);
/* get the hour, minute and second */
if (isdigit(*tptr)) time7[3] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (isdigit(*tptr)) time7[4] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (isdigit(*tptr)) time7[5] = atoi (tptr);
while (*tptr && isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (!*tptr) return (SS$_IVTIME);
/* the only thing remaining should be the "GMT" */
while (*tptr && ISLWS(*tptr)) tptr++;
if (!MATCH3 (tptr, "GMT")) return (SS$_IVTIME);
/*******************************************/
/* convert what looks like legitimate GMT! */
/*******************************************/
status = lib$cvt_vectim (&time7, Time64Ptr);
if (VMSnok (status)) return (status);
TimeAdjustFromGMT (Time64Ptr);
return (SS$_NORMAL);
}
/*****************************************************************************/
/*
Given an ISO 8601 format time string (e.g. "1995-08-25T17:32:40.34Z+0000")
create an internal, local, binary time. See complementary function
HttpIsoTimeString().
*/
int HttpIsoTime
(
char *TimeString,
int64 *Time64Ptr
)
{
int64 time64;
int year, month, day, hour, min, sec, frac, ohour, omin, status, zoffset;
ushort time7 [7];
char offset;
char *tptr;
/*********/
/* begin */
/*********/
*Time64Ptr = 0;
frac = ohour = omin = offset = zoffset = 0;
tptr = TimeString;
/* yyyy mm dd */
if (!isdigit(*tptr)) return (SS$_IVTIME);
if (sscanf (tptr, "%4d", &year) != 1) return (SS$_IVTIME);
while (isdigit(*tptr)) tptr++;
if (*tptr == '-') tptr++;
if (!isdigit(*tptr)) return (SS$_IVTIME);
if (sscanf (tptr, "%2d", &month) != 1) return (SS$_IVTIME);
while (isdigit(*tptr)) tptr++;
if (*tptr == '-') tptr++;
if (!isdigit(*tptr)) return (SS$_IVTIME);
if (sscanf (tptr, "%2d", &day) != 1) return (SS$_IVTIME);
while (isdigit(*tptr)) tptr++;
/* hh mm ss */
if (*tptr == 'T') tptr++;
if (!isdigit(*tptr)) return (SS$_IVTIME);
if (sscanf (tptr, "%2d", &hour) != 1) return (SS$_IVTIME);
while (isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (!isdigit(*tptr)) return (SS$_IVTIME);
if (sscanf (tptr, "%2d", &min) != 1) return (SS$_IVTIME);
while (isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (!isdigit(*tptr)) return (SS$_IVTIME);
if (sscanf (tptr, "%2d", &sec) != 1) return (SS$_IVTIME);
while (isdigit(*tptr)) tptr++;
/* fraction */
if (*tptr == '.')
{
tptr++;
if (!isdigit(*tptr)) return (SS$_IVTIME);
if (sscanf (tptr, "%d", &frac) != 1) return (SS$_IVTIME);
while (isdigit(*tptr)) tptr++;
}
/* zulu offset */
if (*tptr == 'Z') tptr++;
if (*tptr == '+')
offset = *tptr++;
else
if (*tptr == '-')
offset = *tptr++;
if (isdigit(*tptr))
{
if (sscanf (tptr, "%2d", &ohour) != 1) return (SS$_IVTIME);
if (isdigit(*tptr)) tptr++;
if (isdigit(*tptr)) tptr++;
if (*tptr == ':') tptr++;
if (sscanf (tptr, "%2d", &omin) != 1) return (SS$_IVTIME);
zoffset = (((ohour * 3600) + (omin * 60)) * Delta01Sec);
}
time7[0] = year;
time7[1] = month;
time7[2] = day;
time7[3] = hour;
time7[4] = min;
time7[5] = sec;
time7[6] = frac;
status = lib$cvt_vectim (&time7, &time64);
if (VMSnok (status)) return (status);
if (offset == '+')
time64 -= zoffset;
else
if (offset == '-')
time64 += zoffset;
*Time64Ptr = time64;
return (status);
}
/*****************************************************************************/
/*
Translate the logical WASD_UTC, WASD_GMT or HTTPD$GMT (defined to be something
like "+10:30" or "-01:15") and convert it into a delta time and store in
'TimeUtcDelta64'. If these are not present use the UTC time structure. And if
this doesn't make sense (for whatever reason) use SYS$TIMEZONE_DIFFERENTIAL.
*/
void TimeSetHttpdUTC ()
{
int hh, mm, offset, status;
uint64 time128 [2];
ushort time13 [13];
char *cptr, *sptr;
/*********/
/* begin */
/*********/
if (!(cptr = SysTrnLnm (WASD_UTC)))
if (!(cptr = SysTrnLnm (WASD_GMT)))
cptr = SysTrnLnm (HTTPD$GMT);
if (cptr)
{
sptr = cptr;
if (*cptr == '+' || *cptr == '-') cptr++;
hh = atoi(cptr);
while (isdigit(*cptr)) cptr++;
while (!isdigit(*cptr)) cptr++;
mm = atoi(cptr);
if (hh >= 0 && hh <= 23 && mm >= 0 && hh <= 59)
{
offset = hh * 60 + mm;
if (offset >= 0 && offset <= 1440)
{
if (*sptr == '-') offset = -offset;
TimeUtcDelta64 = (int64)offset * TIME64_ONE_MIN;
}
else
cptr = NULL;
if (!(*sptr == '+' || *sptr == '-')) sptr = "+";
}
else
cptr = NULL;
}
if (!cptr)
{
/* use system UTC time */
sys$getutc (&time128);
sys$numutc (&time13, &time128);
/* the timezone offset in minutes */
offset = time13[12];
TimeUtcDelta64 = (int64)offset * TIME64_ONE_MIN;
if (!TimeUtcDelta64) TimeUtcDelta64 = DELTA64_TEN_MSEC;
if (offset >= 0)
sptr = "+";
else
{
sptr = "-";
offset = -offset;
}
hh = offset / 60;
mm = offset % 60;
}
if (offset > 1440)
{
/* if doesn't make sense fall back to this hoary old approach */
if (cptr = SysTrnLnm ("SYS$TIMEZONE_DIFFERENTIAL"))
{
offset = atoi(cptr) / 60;
if (offset <= 1440 || offset >= -1440)
{
TimeUtcDelta64 = (int64)offset * TIME64_ONE_MIN;
if (!TimeUtcDelta64) TimeUtcDelta64 = DELTA64_TEN_MSEC;
if (offset >= 0)
sptr = "+";
else
{
sptr = "-";
offset = -offset;
}
hh = offset / 60;
mm = offset % 60;
}
}
}
sprintf (TimeGmtString, "%c%02.02d:%02.02d", *sptr, hh, mm);
sprintf (TimeZoneString, "%c%02.02d%02.02d", *sptr, hh, mm);
}
/*****************************************************************************/
/*
Adjust pointed-to time to GMT from local. /TimeUtcDelta64/ will either be
positive 64 bits (i.e. VMS binary time) if ahead of GMT or negative 64 bits if
behind (or zero if on GMT). So to move to GMT the delta to GMT is subtracted
from the supplied time.
*/
void TimeAdjustToGMT (int64 *Time64Ptr)
{
/*********/
/* begin */
/*********/
*Time64Ptr = *Time64Ptr - TimeUtcDelta64;
}
/*****************************************************************************/
/*
Adjust pointed-to time from GMT to local. Same as above only /TimeUtcDelta64/
is added to the supplied time.
*/
void TimeAdjustFromGMT (int64 *Time64Ptr)
{
/*********/
/* begin */
/*********/
*Time64Ptr = *Time64Ptr + TimeUtcDelta64;
}
/*****************************************************************************/
/*
*/
char* DigitDayTime (int64 *Time64Ptr)
{
static char TimeString [16];
static $DESCRIPTOR (TimeFaoDsc, "!2ZL !2ZL:!2ZL:!2ZL\0");
static $DESCRIPTOR (TimeStringDsc, TimeString);
int status;
int64 time64;
ushort time7 [7];
/*********/
/* begin */
/*********/
if (!Time64Ptr) sys$gettim (Time64Ptr = &time64);
if (VMSnok (status = sys$numtim (&time7, Time64Ptr)))
return ("*ERROR*");
if (VMSnok (sys$fao (&TimeFaoDsc, 0, &TimeStringDsc,
time7[2], time7[3], time7[4], time7[5])))
return ("*ERROR*");
return (TimeString);
}
/*****************************************************************************/
/*
Return a pointer to the name of the day of the week for the supplied binary
time. If a NULL pointer to the binary time then return current day of week.
*/
char* DayOfWeekName (int64 *Time64Ptr)
{
int status;
ulong DayOfWeek;
int64 time64;
/*********/
/* begin */
/*********/
if (!Time64Ptr) sys$gettim (Time64Ptr = &time64);
if (VMSnok (status = lib$day_of_week (Time64Ptr, &DayOfWeek)))
DayOfWeek = 0;
return (DayName[DayOfWeek]);
}
/*****************************************************************************/
/*
HTTP/1.0 and HTTP/1.1 requests. If the content length has changed (HTTP/1.0 de
facto standard), or if it has been modified since the specified date and time
then return /true/ indicating that the object has been modified. If not
modified then create a 304 HTTP header and return /false/ to indicate the file
should not be sent. With VMS' fractions of a second, add one second to the
specified 'since' time to ensure a reliable comparison.
Implements defacto HTTP/1.0 persistent connections. Currently we only provide
"keep-alive" response if we're sending a file in binary mode and know its
precise length. An accurate "Content-Length:" field is vital for the client's
correct reception of the transmitted data. The only other time a "keep-alive"
response can be provided is HERE, where a 304 header is returned (it has a
content length of precisely zero!)
*/
BOOL HttpIfModifiedSince
(
REQUEST_STRUCT *rqptr,
int64 *RdtTime64Ptr,
int LastContentLength
)
{
int64 time64;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"HttpIfModifiedSince() !UL !UL !%D",
LastContentLength, rqptr->rqHeader.IfModifiedSinceLength,
&rqptr->rqTime.IfModifiedSinceTime64);
if (!rqptr->rqTime.IfModifiedSinceTime64) return (true);
if (rqptr->rqHeader.HttpVersion == HTTP_VERSION_1_0 &&
rqptr->rqHeader.IfModifiedSinceLength >= 0 &&
LastContentLength >= 0 &&
rqptr->rqHeader.IfModifiedSinceLength != LastContentLength)
return (true);
/* slide a second to help ensure a reliable result */
time64 = *(INT64PTR)RdtTime64Ptr - TIME64_ONE_SEC;
if (time64 < rqptr->rqTime.IfModifiedSinceTime64)
{
/* not modified (note the zero content length!) */
ResponseHeader (rqptr, 304, NULL, 0, NULL, NULL);
return (false);
}
return (true);
}
/*****************************************************************************/
/*
HTTP/1.1 if-not-modified-since request. If it has NOT been modified since the
specified date and time then return /true/ indicating that the object has not
been modified. If modified then create a 412 HTTP header (precondition failed)
and return /false/ to indicate the request should not be processed. With VMS'
fractions of a second, add one second to the specified 'since' time to ensure a
reliable comparison.
*/
BOOL HttpIfUnModifiedSince
(
REQUEST_STRUCT *rqptr,
int64 *RdtTime64Ptr
)
{
int64 time64;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"HttpIfUnModifiedSince() !%D",
&rqptr->rqTime.IfModifiedSinceTime64);
if (!rqptr->rqTime.IfUnModifiedSinceTime64) return (true);
/* slide a second to help ensure a reliable result */
time64 = *(INT64PTR)RdtTime64Ptr + TIME64_ONE_SEC;
if (time64 > rqptr->rqTime.IfUnModifiedSinceTime64)
{
/* modified, precondition failed (note the zero content length!) */
ResponseHeader (rqptr, 412, NULL, 0, NULL, NULL);
return (true);
}
/* if not modified! */
return (false);
}
/*****************************************************************************/
/*
HTTP/1.1 if-range not modified since.
*/
void HttpIfRange
(
REQUEST_STRUCT *rqptr,
int64 *RdtTime64Ptr
)
{
int64 time64;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"HttpIfRange() !%D", &rqptr->rqTime.IfRangeBeginTime64);
if (!rqptr->rqTime.IfRangeBeginTime64) return;
/* slide a second to help ensure a reliable result */
time64 = *(INT64PTR)RdtTime64Ptr + TIME64_ONE_SEC;
if (time64 > rqptr->rqTime.IfModifiedSinceTime64)
{
/* modified, cancel any range data */
if (rqptr->rqHeader.RangeBytePtr) rqptr->rqHeader.RangeBytePtr->Total = 0;
}
}
/*****************************************************************************/
/*
Return a reasonable HTTP status from the supplied VMS status.
*/
int VmsToHttpStatus (int VmsStatus)
{
int HttpStatus;
/*********/
/* begin */
/*********/
if (VmsStatus == RMS$_DNF ||
VmsStatus == RMS$_DEV ||
VmsStatus == RMS$_FNF ||
VmsStatus == RMS$_NMF ||
VmsStatus == SS$_NOSUCHDEV ||
VmsStatus == SS$_NOSUCHFILE)
HttpStatus = 404;
else
if (VmsStatus == RMS$_SYN ||
VmsStatus == RMS$_FNM ||
VmsStatus == RMS$_TYP ||
VmsStatus == RMS$_VER ||
VmsStatus == RMS$_DIR ||
VmsStatus == SS$_BADFILENAME ||
VmsStatus == SS$_BADFILEVER ||
VmsStatus == SS$_BADIRECTORY)
HttpStatus = 403;
else
if (VmsStatus == RMS$_FLK)
HttpStatus = 423;
else
if (VmsStatus == SS$_DIRNOTEMPTY ||
VmsStatus == SS$_EXDISKQUOTA)
HttpStatus = 409;
else
if (VmsStatus == RMS$_PRV ||
VmsStatus == SS$_NOPRIV)
HttpStatus = 403;
else
if (VmsStatus == SS$_CREATED)
HttpStatus = 201;
else
if (VmsStatus & 1)
HttpStatus = 200;
else
HttpStatus = 500;
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"VmsToHttpStatus() !&S !UL",
VmsStatus, HttpStatus);
return (HttpStatus);
}
/*****************************************************************************/
/*
Return the a pointer to abbreviated meaning of the supplied HTTP status code.
These are typical of those included on the response header status line.
*/
char *HttpStatusCodeText (int StatusCode)
{
/*********/
/* begin */
/*********/
switch (StatusCode)
{
case 100 : return ("Continue"); /* (HTTP/1.1) */
case 101 : return ("Switching Protocols"); /* (HTTP/1.1) */
case 102 : return ("Processing"); /* (WebDAV - RFC2581) */
case 200 : return ("OK");
case 201 : return ("Created");
case 202 : return ("Accepted");
case 203 : return ("Non-authoritative"); /* (HTTP/1.1) */
case 204 : return ("No Content");
case 205 : return ("Reset Content"); /* (HTTP/1.1) */
case 206 : return ("Partial Content"); /* (HTTP/1.1) */
case 207 : return ("Multistatus"); /* (WebDAV - RFC2581) */
case 300 : return ("Multiple Choices"); /* (HTTP/1.1) */
case 301 : return ("Moved Permanently");
case 302 : return ("Found (Moved Temporarily)");
case 303 : return ("See Other"); /* (HTTP/1.1) */
case 304 : return ("Not Modified");
case 305 : return ("Use Proxy"); /* (HTTP/1.1) */
case 306 : return ("[reserved]");
case 307 : return ("Temporary Redirect"); /* (HTTP/1.1) */
case 400 : return ("Bad Request");
case 401 : return ("Authorization Required");
case 402 : return ("Payment Required"); /* (HTTP/1.1) */
case 403 : return ("Forbidden");
case 404 : return ("Not Found");
case 405 : return ("Method Not Allowed"); /* (HTTP/1.1) */
case 406 : return ("Not Acceptable"); /* (HTTP/1.1) */
case 407 : return ("Proxy Authentication Required"); /* (HTTP/1.1) */
case 408 : return ("Request Timeout"); /* (HTTP/1.1) */
case 409 : return ("Conflict"); /* (HTTP/1.1) */
case 410 : return ("Gone"); /* (HTTP/1.1) */
case 411 : return ("Length Required"); /* (HTTP/1.1) */
case 412 : return ("Precondition Failed"); /* (HTTP/1.1) */
case 413 : return ("Request Entity Too Large"); /* (HTTP/1.1) */
case 414 : return ("Request URI Too Long"); /* (HTTP/1.1) */
case 415 : return ("Unsupported Media Type"); /* (HTTP/1.1) */
case 416 : return ("Requested Range Not Satisfiable"); /* (HTTP/1.1) */
case 417 : return ("Expectation Failed"); /* (HTTP/1.1) */
case 418 : return ("I'm a teapot"); /* (RFC2324) */
case 421 : return ("Misdirected Request"); /* (HTTP/2 - RFC7540) */
case 422 : return ("Unprocessable Entity"); /* (WebDAV - RFC2581) */
case 423 : return ("Locked"); /* (WebDAV - RFC2581) */
case 424 : return ("Failed Dependency"); /* (WebDAV - RFC2581) */
case 426 : return ("Upgrade Required"); /* (RFC2847) */
case 500 : return ("Internal Error");
case 501 : return ("Not Implemented");
case 502 : return ("Bad Gateway");
case 503 : return ("Service Unavailable");
case 504 : return ("Gateway Timeout"); /* (HTTP/1.1) */
case 505 : return ("HTTP Version Not Supported"); /* (HTTP/1.1) */
case 507 : return ("Insufficient Storage"); /* (WebDAV - RFC2581) */
default : return ("Unknown Code!");
}
}
/*****************************************************************************/
/*
Return the a pointer an explanation of the supplied HTTP status code. These
are supplied from the message database can be are used in error messages, etc.
*/
char *HttpStatusCodeExplanation
(
REQUEST_STRUCT *rqptr,
int StatusCode
)
{
/*********/
/* begin */
/*********/
switch (StatusCode)
{
/* continue (HTTP/1.1) */
case 100 : return (MsgFor(rqptr,MSG_HTTP_100));
/* switching protocols (HTTP/1.1) */
case 101 : return (MsgFor(rqptr,MSG_HTTP_101));
/* processing (WedDAV - RFC2518) */
case 102 : return ("processing");
/* success */
case 200 : return (MsgFor(rqptr,MSG_HTTP_200));
/* created */
case 201 : return (MsgFor(rqptr,MSG_HTTP_201));
/* accepted */
case 202 : return (MsgFor(rqptr,MSG_HTTP_202));
/* non-authoritative (HTTP/1.1) */
case 203 : return (MsgFor(rqptr,MSG_HTTP_203));
/* no content */
case 204 : return (MsgFor(rqptr,MSG_HTTP_204));
/* reset content (HTTP/1.1) */
case 205 : return (MsgFor(rqptr,MSG_HTTP_205));
/* partial content (HTTP/1.1) */
case 206 : return (MsgFor(rqptr,MSG_HTTP_206));
/* multi-status (WedDAV - RFC2518) */
case 207 : return ("multi-status");
/* multiple choices (HTTP/1.1) */
case 300 : return (MsgFor(rqptr,MSG_HTTP_300));
/* moved permanently */
case 301 : return (MsgFor(rqptr,MSG_HTTP_301));
/* moved temporarily */
case 302 : return (MsgFor(rqptr,MSG_HTTP_302));
/* see other (HTTP/1.1) */
case 303 : return (MsgFor(rqptr,MSG_HTTP_303));
/* not modified */
case 304 : return (MsgFor(rqptr,MSG_HTTP_304));
/* use proxy (HTTP/1.1) */
case 305 : return (MsgFor(rqptr,MSG_HTTP_305));
/* reserved (WedDAV - RFC2518) */
case 306 : return ("reserved");
/* temporary redirect (WedDAV - RFC2518) */
case 307 : return ("temporary redirect");
/* bad request */
case 400 : return (MsgFor(rqptr,MSG_HTTP_400));
/* authorization required */
case 401 : return (MsgFor(rqptr,MSG_HTTP_401));
/* payment required */
case 402 : return (MsgFor(rqptr,MSG_HTTP_402));
/* forbidden */
case 403 : return (MsgFor(rqptr,MSG_HTTP_403));
/* not found */
case 404 : return (MsgFor(rqptr,MSG_HTTP_404));
/* method not allowed (HTTP/1.1) */
case 405 : return (MsgFor(rqptr,MSG_HTTP_405));
/* not acceptable (HTTP/1.1) */
case 406 : return (MsgFor(rqptr,MSG_HTTP_406));
/* proxy authentication required (HTTP/1.1) */
case 407 : return (MsgFor(rqptr,MSG_HTTP_407));
/* request timeout (HTTP/1.1) */
case 408 : return (MsgFor(rqptr,MSG_HTTP_408));
/* resource conflict (HTTP/1.1) */
case 409 : return (MsgFor(rqptr,MSG_HTTP_409));
/* gone (HTTP/1.1) */
case 410 : return (MsgFor(rqptr,MSG_HTTP_410));
/* length required (HTTP/1.1) */
case 411 : return (MsgFor(rqptr,MSG_HTTP_411));
/* precondition failed (HTTP/1.1) */
case 412 : return (MsgFor(rqptr,MSG_HTTP_412));
/* request entity too large (HTTP/1.1) */
case 413 : return (MsgFor(rqptr,MSG_HTTP_413));
/* request-URI too long (HTTP/1.1) */
case 414 : return (MsgFor(rqptr,MSG_HTTP_414));
/* unsupported media type (HTTP/1.1) */
case 415 : return (MsgFor(rqptr,MSG_HTTP_415));
/* requested range not satisfiable (HTTP/1.1) */
case 416 : return (MsgFor(rqptr,MSG_HTTP_416));
/* expectation failed (HTTP/1.1) */
case 417 : return (MsgFor(rqptr,MSG_HTTP_417));
/* misdirected request (HTTP/2 - RFC7540) */
case 421 : return ("misdirected request");
/* unprocessable entity (WedDAV - RFC2518) */
case 422 : return ("unprocessable entity");
/* locked (WedDAV - RFC2518) */
case 423 : return ("locked");
/* internal error */
case 500 : return (MsgFor(rqptr,MSG_HTTP_500));
/* not implemented */
case 501 : return (MsgFor(rqptr,MSG_HTTP_501));
/* bad gateway */
case 502 : return (MsgFor(rqptr,MSG_HTTP_502));
/* service unavailable */
case 503 : return (MsgFor(rqptr,MSG_HTTP_503));
/* gateway timeout (HTTP/1.1) */
case 504 : return (MsgFor(rqptr,MSG_HTTP_504));
/* HTTP version not supported (HTTP/1.1) */
case 505 : return (MsgFor(rqptr,MSG_HTTP_505));
/* unknown code! */
default :
return ("The server is reporting an UNKNOWN status code!");
}
}
/*****************************************************************************/
/*
Returns a pointer to a VmGetHeap()ed string of tags for an
internally generated HTML document.
*/
char* HtmlMetaInfo
(
REQUEST_STRUCT *rqptr,
char *VmsInfoPtr
)
{
int status;
ulong FaoVector [16];
ulong *vecptr;
ushort Length;
char *MetaPtr;
char Buffer [2048];
/*********/
/* begin */
/*********/
vecptr = &FaoVector;
*vecptr++ = SoftwareID;
*vecptr++ = rqptr->rqTime.GmDateTime;
if (rqptr->RemoteUser[0])
{
*vecptr++ = "\n";
*vecptr++ = rqptr->RemoteUser;
*vecptr++ = rqptr->rqAuth.RealmDescrPtr;
*vecptr++ = rqptr->ServicePtr->ServerHostPort;
}
else
{
*vecptr++ = "\n";
*vecptr++ = rqptr->ServicePtr->ServerHostPort;
}
if (VmsInfoPtr && VmsInfoPtr[0])
{
if (Config.cfReport.MetaInfoEnabled)
{
*vecptr++ = "\n!&@";
*vecptr++ = VmsInfoPtr;
if (rqptr->PathOds)
{
*vecptr++ = "\n";
switch (rqptr->PathOds)
{
case MAPURL_PATH_ODS_2 : *vecptr++ = "2"; break;
case MAPURL_PATH_ODS_5 : *vecptr++ = "5"; break;
case MAPURL_PATH_ODS_ADS : *vecptr++ = "ADS"; break;
case MAPURL_PATH_ODS_PWK : *vecptr++ = "PWK"; break;
case MAPURL_PATH_ODS_SMB : *vecptr++ = "SMB"; break;
case MAPURL_PATH_ODS_SRI : *vecptr++ = "SRI"; break;
default : *vecptr++ = "?";
}
}
else
*vecptr++ = "";
}
else
*vecptr++ = "";
}
else
*vecptr++ = "";
status = FaolToBuffer (Buffer, sizeof(Buffer), &Length,
"\n\
\n\
!&@\
!&@\
\n",
&FaoVector);
if (VMSnok (status) || status == SS$_BUFFEROVF)
{
ErrorNoticed (rqptr, status, NULL, FI_LI);
return ("\n");
}
MetaPtr = VmGetHeap (rqptr, Length+1);
memcpy (MetaPtr, Buffer, Length+1);
return (MetaPtr);
}
/*****************************************************************************/
/*
Return a string containing a duration string.
Durations are in standard (VMS) 100nS units.
If less than one minute provide as 'seconds.micro[milli]seconds'.
If a minute or greater as '[ddd-]hh.mm.ss'.
*/
char* DurationString
(
REQUEST_STRUCT *rqptr,
int64 *Time64Ptr
)
{
static int bidx;
static buf [4][32];
static $DESCRIPTOR (DateFaoDsc, "!13%D\0");
static $DESCRIPTOR (TimeFaoDsc, "!8%T\0");
static $DESCRIPTOR (TimeDsc, "");
int64 time64;
double dsecs;
char *sptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"DurationString() !&SQ !%T",
Time64Ptr, Time64Ptr);
if (rqptr)
sptr = VmGetHeap (rqptr, 32);
else
{
sptr = buf[bidx++];
if (bidx >= 4) bidx = 0;
}
time64 = *Time64Ptr;
if (!time64)
sptr = "0.0s";
else
if (time64 >= DELTA64_ONE_SEC)
{
/* less than one second, give in milliseconds */
dsecs = (double)time64;
if (time64 > DELTA64_TEN_MSEC)
sprintf (sptr, "%.3fms", dsecs / (double)DELTA64_ONE_MSEC);
else
if (time64 > DELTA64_HUN_MSEC)
sprintf (sptr, "%.1fms", dsecs / (double)DELTA64_ONE_MSEC);
else
sprintf (sptr, "%.0fms", dsecs / (double)DELTA64_ONE_MSEC);
}
else
if (time64 >= DELTA64_ONE_MIN)
{
/* less than one minute, give in seconds and fractions */
dsecs = (double)time64;
if (time64 > DELTA64_TEN_SEC)
sprintf (sptr, "%.3fs", dsecs / (double)DELTA64_ONE_SEC);
else
sprintf (sptr, "%.2fs", dsecs / (double)DELTA64_ONE_SEC);
}
else
if (time64 >= DELTA64_ONE_DAY)
{
/* less than twenty-four hours */
TimeDsc.dsc$a_pointer = sptr;
TimeDsc.dsc$w_length = 32;
sys$fao (&TimeFaoDsc, 0, &TimeDsc, Time64Ptr);
}
else
{
/* greater than twenty-four hours */
TimeDsc.dsc$a_pointer = sptr;
TimeDsc.dsc$w_length = 32;
sys$fao (&DateFaoDsc, 0, &TimeDsc, Time64Ptr);
sptr[4] = 160; /* ISO8859-1 non-breaking space */
while (*sptr == ' ') sptr++;
}
return (sptr);
}
/*****************************************************************************/
/*
Return an allocated string containing the average duration.
*/
char* AverageDurationString
(
REQUEST_STRUCT *rqptr,
int64 *Time64Ptr,
ulong Count
)
{
int status;
int64 time64;
float fcount;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD__OTHER))
WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER,
"AverageDurationString() !&X !&X !%T",
Time64Ptr[0], Time64Ptr[1], Time64Ptr);
time64 = 0;
if (Count)
{
time64 = *(INT64PTR)Time64Ptr;
fcount = (float)Count;
fcount = 1.0 / fcount;
/* could use lib$mults_delta_time() only it doesn't exist on V7.3 :-( */
status = cvt$convert_float (&fcount, CVT$K_IEEE_S,
&fcount, CVT$K_VAX_F, 0);
if (VMSnok (status))
{
ErrorNoticed (rqptr, status, NULL, FI_LI);
time64 = 0;
}
status = lib$multf_delta_time (&fcount, &time64);
if (VMSnok (status))
{
ErrorNoticed (rqptr, status, NULL, FI_LI);
time64 = 0;
}
}
return (DurationString (rqptr, &time64));
}
/*****************************************************************************/
/*
Format a string containing how many seconds, minutes, hours, days ago from the
current time. For example; "15s", "10m", "17h", "152d". Return VMS status.
|AgoBuf| should be at least 16 chars.
*/
void ThisLongAgo
(
int64 *Time64Ptr,
char *AgoBuf
)
{
static ulong LibDeltaSecs = LIB$K_DELTA_SECONDS;
static $DESCRIPTOR (AgoFaoDsc, "!UL!AZ\0");
static $DESCRIPTOR (AgoBufDsc, "");
int status;
ulong SecsAgo;
int64 NowTime64;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER, "ThisLongAgo()");
*(USHORTPTR)AgoBuf = '0\0';
if (!*Time64Ptr) return;
sys$gettim (&NowTime64);
SecsAgo = (NowTime64 - *Time64Ptr) / TIME64_ONE_SEC;
AgoBufDsc.dsc$a_pointer = AgoBuf;
AgoBufDsc.dsc$w_length = 16;
status = SS$_NORMAL;
if (SecsAgo >= (60*60*24))
status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo / (60*60*24), "d");
else
if (SecsAgo >= (60*60))
status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo / (60*60), "h");
else
if (SecsAgo >= 60)
status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo / 60, "m");
else
if (SecsAgo > 1)
status = sys$fao (&AgoFaoDsc, 0, &AgoBufDsc, SecsAgo, "s");
else
strcpy (AgoBuf, "now");
}
/*****************************************************************************/
/*
Put into the supplied buffer the time with the year removed (e.g. 31-OCT
23:01:15"). The buffer should have at least 16 bytes capacity.
*/
void TimeSansYear
(
ulong *Time64Ptr,
char *Buffer
)
{
static $DESCRIPTOR (TimeFaoDsc, "!20%D\0");
static char TimeBuf [32];
static $DESCRIPTOR (TimeBufDsc, TimeBuf);
int status;
char *cptr, *sptr;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER, "ThisLongAgo() !8XL", Time64Ptr);
if (!*(UINT64PTR)Time64Ptr)
{
strcpy (Buffer, "(none)");
return;
}
Buffer[0] = '\0';
status = sys$fao (&TimeFaoDsc, 0, &TimeBufDsc, Time64Ptr);
if (VMSnok (status)) return;
sptr = Buffer;
for (cptr = TimeBuf; *cptr && *cptr != '-'; *sptr++ = *cptr++);
for (*sptr++ = *cptr++; *cptr && *cptr != '-'; *sptr++ = *cptr++);
while (*cptr && *cptr != ' ') cptr++;
while (*cptr) *sptr++ = *cptr++;
*sptr = '\0';
}
/*****************************************************************************/
/*
Convert a VMS quadword, binary time to a Unix-style time component structure.
*/
BOOL TimeVmsToUnix
(
int64 *Time64Ptr,
struct tm *TmPtr
)
{
static ulong LibDayOfWeek = LIB$K_DAY_OF_WEEK;
static ulong LibDayOfYear = LIB$K_DAY_OF_YEAR;
int status;
ulong DayOfWeek,
DayOfYear;
ushort time7 [7];
int64 time64;
/*********/
/* begin */
/*********/
if (!Time64Ptr) sys$gettim (Time64Ptr = &time64);
if (VMSnok (status = sys$numtim (&time7, Time64Ptr)))
return (status);
lib$cvt_from_internal_time (&LibDayOfWeek, &DayOfWeek, Time64Ptr);
lib$cvt_from_internal_time (&LibDayOfYear, &DayOfYear, Time64Ptr);
TmPtr->tm_sec = time7[5];
TmPtr->tm_min = time7[4];
TmPtr->tm_hour = time7[3];
TmPtr->tm_mday = time7[2];
TmPtr->tm_mon = time7[1] - 1;
TmPtr->tm_year = time7[0] - 1900;
TmPtr->tm_wday = DayOfWeek;
TmPtr->tm_yday = DayOfYear;
TmPtr->tm_isdst = 0;
return (SS$_NORMAL);
}
/****************************************************************************/
/*
generate a standard VMS protection string from the mask. 'String' must be at
least 28 bytes (better use 32 :^) Returns the length of the string.
*/
int FormatProtection
(
ushort pmask,
char *String
)
{
char *sptr;
/*********/
/* begin */
/*********/
sptr = String;
if (!(pmask & 0x0001)) *sptr++ = 'R';
if (!(pmask & 0x0002)) *sptr++ = 'W';
if (!(pmask & 0x0004)) *sptr++ = 'E';
if (!(pmask & 0x0008)) *sptr++ = 'D';
*sptr++ = ',';
if (!(pmask & 0x0010)) *sptr++ = 'R';
if (!(pmask & 0x0020)) *sptr++ = 'W';
if (!(pmask & 0x0040)) *sptr++ = 'E';
if (!(pmask & 0x0080)) *sptr++ = 'D';
*sptr++ = ',';
if (!(pmask & 0x0100)) *sptr++ = 'R';
if (!(pmask & 0x0200)) *sptr++ = 'W';
if (!(pmask & 0x0400)) *sptr++ = 'E';
if (!(pmask & 0x0800)) *sptr++ = 'D';
*sptr++ = ',';
if (!(pmask & 0x1000)) *sptr++ = 'R';
if (!(pmask & 0x2000)) *sptr++ = 'W';
if (!(pmask & 0x4000)) *sptr++ = 'E';
if (!(pmask & 0x8000)) *sptr++ = 'D';
*sptr = '\0';
return (sptr - String);
}
/*****************************************************************************/
/*
Just return the process' current BYTLM quota.
*/
GetJpiBytLm ()
{
static ulong Pid = 0;
static ulong JpiBytLm;
static VMS_ITEM_LIST3 JpiItems [] =
{
{ sizeof(JpiBytLm), JPI$_BYTLM, &JpiBytLm, 0 },
{0,0,0,0}
};
int status;
IO_SB IOsb;
/*********/
/* begin */
/*********/
status = sys$getjpiw (EfnWait, &Pid, 0, &JpiItems, &IOsb, 0, 0);
if (VMSok (status)) status = IOsb.Status;
if (VMSok (status)) return (JpiBytLm);
ErrorExitVmsStatus (status, NULL, FI_LI);
}
/*****************************************************************************/
/*
Returns a pointer to a dynamic string containing the client host details as
"name (address)" or just "address".
*/
char* ClientHostString (REQUEST_STRUCT *rqptr)
{
static $DESCRIPTOR (AddressFaoDsc, "!AZ\0");
static $DESCRIPTOR (NameAddressFaoDsc, "!AZ (!AZ)\0");
ushort Length;
char *cptr;
char Buffer [256];
$DESCRIPTOR (BufferDsc, Buffer);
/*********/
/* begin */
/*********/
if (!strcmp (rqptr->ClientPtr->Lookup.HostName,
&rqptr->ClientPtr->IpAddressString))
sys$fao (&AddressFaoDsc, &Length, &BufferDsc,
&rqptr->ClientPtr->IpAddressString);
else
sys$fao (&NameAddressFaoDsc, &Length, &BufferDsc,
rqptr->ClientPtr->Lookup.HostName,
&rqptr->ClientPtr->IpAddressString);
cptr = VmGetHeap (rqptr, Length);
strcpy (cptr, Buffer);
return (cptr);
}
/*****************************************************************************/
/*
*/
char* UserAtClient (REQUEST_STRUCT *rqptr)
{
char *cptr, *sptr, *zptr;
char StringBuffer [256];
/*********/
/* begin */
/*********/
if (!rqptr->RemoteUser[0]) return (rqptr->ClientPtr->Lookup.HostName);
zptr = (sptr = StringBuffer) + sizeof(StringBuffer)-1;
for (cptr = rqptr->RemoteUser; *cptr; cptr++)
if (!isalnum(*cptr) && *cptr != '-' && *cptr != '_') break;
if (*cptr) *sptr++ = '\"';
for (cptr = rqptr->RemoteUser;
*cptr && sptr < zptr;
*sptr++ = *cptr++);
if (StringBuffer[0] == '\"' && sptr < zptr) *sptr++ = '\"';
if (rqptr->rqAuth.RealmPtr)
{
if (sptr < zptr) *sptr++ = '.';
for (cptr = rqptr->rqAuth.RealmPtr;
*cptr && sptr < zptr;
*sptr++ = *cptr++);
}
if (sptr < zptr) *sptr++ = '@';
for (cptr = rqptr->ClientPtr->Lookup.HostName;
*cptr && sptr < zptr;
*sptr++ = *cptr++);
*sptr = '\0';
cptr = VmGetHeap (rqptr, sptr-StringBuffer);
strcpy (cptr, StringBuffer);
return (cptr);
}
/****************************************************************************/
/*
Given a buffer of UTF-8 convert in-situ to 8 bit ASCII. Ignore non- 8 bit
ASCII characters. End-of-string is indicated by text-length not a
null-character, however the resultant string is nulled. If supplied the 8 bit
character 'SubsChar' is substituted for any non 8 bit code in the string.
Return the number of converted characters. Return -1 if there is an error.
The string is mangled if an error.
*/
int ConvertFromUtf8
(
char *UtfPtr,
int UtfCount,
char SubsChar
)
{
uchar ch;
uchar *cptr, *sptr, *zptr;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"ConvertFromUtf8() !&C !#H", SubsChar, UtfCount, UtfPtr);
if (UtfCount == -1) UtfCount = strlen(UtfPtr);
if (UtfCount < 0) return (-1);
/* is there a potentially UTF-8 bit pattern here? */
for (zptr = (cptr = (uchar*)UtfPtr) + UtfCount; cptr < zptr; cptr++)
if ((*cptr & 0xc0) == 0xc0) break;
/* return if no UTF-8 conversion necessary (i.e. all 7 bit characters) */
if (cptr >= zptr) return (UtfCount);
if (*cptr == 0xff) return (cptr - (uchar*)UtfPtr);
sptr = cptr;
while (cptr < zptr)
{
if ((*cptr & 0xf8) == 0xf0)
{
/* four byte sequence */
if (++cptr >= zptr) goto utf8_nbg;
if ((*cptr & 0xc0) != 0x80) goto utf8_nbg;
if (++cptr >= zptr) goto utf8_nbg;
if ((*cptr & 0xc0) != 0x80) goto utf8_nbg;
if (++cptr >= zptr) goto utf8_nbg;
if ((*cptr & 0xc0) != 0x80) goto utf8_nbg;
if (SubsChar) *sptr++ = SubsChar;
}
else
if ((*cptr & 0xf0) == 0xe0)
{
/* three byte sequence */
if (++cptr >= zptr) goto utf8_nbg;
if ((*cptr & 0xc0) != 0x80) goto utf8_nbg;
if (++cptr >= zptr) goto utf8_nbg;
if ((*cptr & 0xc0) != 0x80) goto utf8_nbg;
if (SubsChar) *sptr++ = SubsChar;
}
else
if ((*cptr & 0xe0) == 0xc0)
{
/* two byte sequence */
if (*cptr & 0x1c)
{
/* out-of-range character */
if (++cptr >= zptr) goto utf8_nbg;
if ((*cptr & 0xc0) != 0x80) goto utf8_nbg;
if (++cptr >= zptr) goto utf8_nbg;
if (SubsChar) *sptr++ = SubsChar;
}
else
{
/* 8 bit ASCII 128 to 255 */
ch = (*cptr & 0x03) << 6;
if (++cptr >= zptr) goto utf8_nbg;
if ((*cptr & 0xc0) != 0x80) goto utf8_nbg;
ch |= *cptr & 0x3f;
*sptr++ = ch;
cptr++;
}
}
else
{
/* 8 bit ASCII 0 to 127 */
*sptr++ = *cptr++;
}
}
*sptr = '\0';
return (sptr - (uchar*)UtfPtr);
utf8_nbg:
return (-1);
}
/****************************************************************************/
/*
Given a buffer of 8 bit ASCII text convert it in-situ to UTF-8. Worst-case the
buffer space needs to be two times in size (i.e. every character has the hi bit
set requiring a leading UTF-8 byte). End-of-string is indicated by text-length
not a null-character, however the resultant string is nulled. Return the
length of the converted string. Return -1 if the buffer space will be too
small.
*/
int ConvertToUtf8
(
char *TextPtr,
int TextLength,
int SizeOfText
)
{
int HiBitCount = 0;
char *cptr, *sptr, *zptr;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"ConvertFromUtf8() !!UL !#H", SizeOfText, TextLength, TextPtr);
if (TextLength == -1) TextLength = strlen(TextPtr);
for (zptr = (cptr = TextPtr) + TextLength; cptr < zptr; cptr++)
if (*cptr & 0x80) HiBitCount++;
if (!HiBitCount) return (TextLength);
if (TextLength + HiBitCount >= SizeOfText - 1) return (-1);
sptr = (cptr = (zptr = TextPtr) + TextLength - 1) + HiBitCount + 1;
*sptr-- = '\0';
while (cptr >= zptr)
{
if (*cptr & 0x80)
{
*sptr-- = (*cptr & 0x3f) | 0x80;
*sptr-- = ((*cptr-- & 0xc0) >> 6) | 0xc0;
}
else
*sptr-- = *cptr--;
}
return (TextLength + HiBitCount);
}
/****************************************************************************/
/*
For UTF-8 encoded file names hex-escape characters that cause issues. This is
intended to feed into RMS services such as $PARSE. Return zero if the original
string can be used. Return minus one if the buffer overflowed. Return the
number of characters in the escaped string when the buffer should be used.
*/
int ConvertUtf8ToEscape
(
char *FileSpec,
char *BufferPtr,
int BufferSize
)
{
static char HexDigit [] = "0123456789ABCDEF";
static char ScratchBuffer [ODS_MAX_FILE_NAME_LENGTH+1];
char *cptr, *sptr, *zptr;
/*********/
/* begin */
/*********/
if (WATCH_MODULE(WATCH_MOD__OTHER))
WatchThis (WATCHALL, WATCH_MOD__OTHER,
"ConvertUtf8ToEscape() !&Z", FileSpec);
for (cptr = FileSpec; *cptr; cptr++)
if (*(uchar*)cptr >= 0x80 && *(uchar*)cptr <= 0x9f)
break;
if (!*cptr) return (0);
if (BufferPtr == FileSpec)
{
/* reusing the file spec buffer so put it into a scratch buffer */
zptr = (sptr = ScratchBuffer) + sizeof(ScratchBuffer)-1;
for (cptr = FileSpec; *cptr && sptr < zptr; *sptr++ = *cptr++);
if (sptr >= zptr)
{
BufferPtr[0] = '\0';
return (-1);
}
*sptr = '\0';
cptr = ScratchBuffer;
}
else
cptr = FileSpec;
zptr = (sptr = BufferPtr) + BufferSize - 1;
while (*cptr && sptr < zptr)
{
if (*(uchar*)cptr >= 0x80 && *(uchar*)cptr <= 0x9f)
{
*sptr++ = '^';
if (sptr < zptr) *sptr++ = HexDigit[((uchar)*cptr & 0xf0) >> 4];
if (sptr < zptr) *sptr++ = HexDigit[(uchar)*cptr & 0x0f];
cptr++;
}
else
*sptr++ = *cptr++;
}
if (sptr >= zptr)
{
BufferPtr[0] = '\0';
return (-1);
}
*sptr = '\0';
if (WATCH_MODULE(WATCH_MOD_ODS))
WatchThis (WATCHALL, WATCH_MOD_ODS, "!&Z", BufferPtr);
return (sptr - BufferPtr);
}
/*****************************************************************************/
/*
Generate a unique request identifier. Based on the description and code of the
Apache Group's "mod_unique" module. One change has been made to allow the ID
to be used as part of VMS file names, the substitution of '@' for '_'.
*/
char* GenerateUniqueId (REQUEST_STRUCT *rqptr)
{
static uint64 UniqueId [3];
static char UniqueIdString [32];
/*********/
/* begin */
/*********/
sys$gettim (UniqueId[0]);
UniqueId[1] = HttpdProcess.Pid;
UniqueId[2] = UniqueId[2] * 69069 + 1;
Md5HexString (UniqueId, sizeof(UniqueId), UniqueIdString);
UniqueIdString[UNIQUE_ID_SIZE] = '\0';
return (UniqueIdString);
}
/*****************************************************************************/
/*
Support version 10 and pre-version-10 logical and file naming conventions.
Look for one of multiple (usually just two but can be more) possible logical
names. When one is successfully translated return a pointer to it's null
terminated name. Otherwise test for a subsequent and return a pointer to it if
found. If none is found then return NoneFound if zero or positive, return the
LogicalName if minus one. The constants should be #defined in the manner of
"?WASD_CONFIG_GLOBAL\0HTTPD$CONFIG\0".
*/
char* v10orPrev10
(
char *LogicalName,
int NoneFound
)
{
static ushort Length;
static char ValueString [128];
static $DESCRIPTOR (LogicalNameDsc, "");
static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV");
static VMS_ITEM_LIST3 LnmItems [] =
{
{ sizeof(ValueString)-1, LNM$_STRING, ValueString, &Length },
{ 0,0,0,0 }
};
int count, status;
char *cptr, *sptr;
/*********/
/* begin */
/*********/
if (!LogicalName || LogicalName[0] != '?') return (LogicalName);
count = 0;
/* stop at any logical name delimiting colon (perhaps of a file name) */
for (sptr = cptr = LogicalName+1; *sptr && *sptr != ':'; sptr++);
while (*cptr)
{
LogicalNameDsc.dsc$a_pointer = cptr;
LogicalNameDsc.dsc$w_length = sptr - cptr;
status = sys$trnlnm (0, &LnmFileDevDsc, &LogicalNameDsc, 0, &LnmItems);
if (VMSok (status))
{
if (count)
FaoToStdout ("%HTTPD-W-DEPRECATED, change !AZ to !AZ (soon!!)\n",
cptr, LogicalName+1);
return (cptr);
}
count++;
/* scan past any logical-delimiting colon */
while (*sptr) sptr++;
/* stop at any logical name delimiting colon (perhaps of a file name) */
for (cptr = (sptr += 1); *sptr && *sptr != ':'; sptr++);
}
if (NoneFound != -1) return (NoneFound);
return (LogicalName+1);
}
/*****************************************************************************/
/*
Translate a logical name using LNM$FILE_DEV. Returns a pointer to the value
string as a pointer to a static buffer which must be copied if to be retained,
or NULL if the name does not exist.
*/
char* SysTrnLnm (char *LogName)
{
static ushort ValueLength;
static ulong LnmAttributes;
static char LogValue [256];
static $DESCRIPTOR (LogNameDsc, "");
static $DESCRIPTOR (LnmTableDsc, "LNM$FILE_DEV");
static struct {
short int buf_len;
short int item;
void *buf_addr;
ushort *ret_len;
} LnmItems [] =
{
{ sizeof(LnmAttributes), LNM$_ATTRIBUTES, &LnmAttributes, 0 },
{ sizeof(LogValue)-1, LNM$_STRING, LogValue, &ValueLength },
{ 0,0,0,0 }
};
int status;
char *cptr;
/*********/
/* begin */
/*********/
for (cptr = LogName; *cptr; cptr++);
LogNameDsc.dsc$w_length = cptr - LogName;
LogNameDsc.dsc$a_pointer = LogName;
memset (LogValue, 0, sizeof(LogValue));
status = sys$trnlnm (0, &LnmTableDsc, &LogNameDsc, 0, &LnmItems);
if (!(status & 1) || !(LnmAttributes & LNM$M_EXISTS)) return (NULL);
return (LogValue);
}
/****************************************************************************/
/*
Assign a global symbol.
*/
int SetGlobalSymbol
(
char *Name,
char *String
)
{
static int GlobalSymbol = LIB$K_CLI_GLOBAL_SYM;
static $DESCRIPTOR (NameDsc, "");
static $DESCRIPTOR (ValueDsc, "");
int status;
/*********/
/* begin */
/*********/
NameDsc.dsc$a_pointer = Name;
NameDsc.dsc$w_length = strlen(Name);
ValueDsc.dsc$a_pointer = String;
ValueDsc.dsc$w_length = strlen(String);
status = lib$set_symbol (&NameDsc, &ValueDsc, &GlobalSymbol);
return (status);
}
/*****************************************************************************/
/*
Return a float number of seconds.
*/
float FloatDeltaTime (ulong *DeltaTimePtr)
{
static int LibDeltaSecs = LIB$K_DELTA_SECONDS_F;
int status;
float fsecs;
/*********/
/* begin */
/*********/
if (*DeltaTimePtr == DELTA64_ZERO) return (0.0);
#ifdef __ia64
status = lib$cvts_from_internal_time (&LibDeltaSecs, &fsecs, DeltaTimePtr);
#else
status = lib$cvtf_from_internal_time (&LibDeltaSecs, &fsecs, DeltaTimePtr);
# ifdef __ALPHA
if (VMSok(status))
status = cvt$convert_float (&fsecs, CVT$K_VAX_F,
&fsecs, CVT$K_IEEE_S, 0);
# endif
#endif
if (VMSnok(status))
{
ErrorNoticed (NULL, status, "lib$cvts_from_internal_time()", FI_LI);
return (0.0);
}
return (fsecs);
}
/*****************************************************************************/
/*
Return a longword bytes/second.
*/
int BytesPerSecond
(
ulong *BytesRxPtr,
ulong *BytesTxPtr,
ulong *DeltaTimePtr
)
{
static int LibDeltaSecs = LIB$K_DELTA_SECONDS_F;
int status;
uint bps;
float fbytes = 0.0,
fsecs;
/*********/
/* begin */
/*********/
/* if zero time then only an artifact will be generated! */
if (!*DeltaTimePtr || *DeltaTimePtr == DELTA64_ZERO) return (0);
if (BytesRxPtr)
fbytes += (float)BytesRxPtr[0] +
((float)BytesRxPtr[1] * 4294967296.0);
if (BytesTxPtr)
fbytes += (float)BytesTxPtr[0] +
((float)BytesTxPtr[1] * 4294967296.0);
#ifdef __ALPHA
status = lib$cvtf_from_internal_time (&LibDeltaSecs, &fsecs, DeltaTimePtr);
if (VMSok(status))
status = cvt$convert_float (&fsecs, CVT$K_VAX_F,
&fsecs, CVT$K_IEEE_S, 0);
#else
status = lib$cvts_from_internal_time (&LibDeltaSecs, &fsecs, DeltaTimePtr);
#endif
if (VMSnok(status))
{
if (status != LIB$_DELTIMREQ)
ErrorNoticed (NULL, status, "lib$cvts_from_internal_time()", FI_LI);
return (0);
}
/* if greater than 2000000000 (2GB/S) it's likely to be an artifact! */
if ((bps = (uint)(fbytes / fsecs)) < 2000000000)
return ((int)bps);
else
return (0);
}
/*****************************************************************************/
/*
Calculate percentages of unsigned longs using floats to avoid integer overflow
and allowing more accurate rounding.
*/
int PercentOf32
(
ulong arg1,
ulong arg2
)
{
int iperc;
float farg1, farg2, fperc;
/*********/
/* begin */
/*********/
if (arg2)
{
farg1 = (float)arg1;
farg2 = (float)arg2;
fperc = farg1 * 100.0 / farg2;
iperc = (int)fperc;
if (fperc - (float)iperc >= 0.5) iperc++;
return (iperc);
}
return (0);
}
/*****************************************************************************/
/*
Calculate percentages of unsigned 64bits using floats to avoid overflow and
allowing more accurate rounding.
*/
int PercentOf64
(
uint64 *arg1,
uint64 *arg2
)
{
int iqperc;
float farg1, farg2, fqperc;
/*********/
/* begin */
/*********/
if (arg2)
{
farg1 = (float)(*(UINT64PTR)arg1);
farg2 = (float)(*(UINT64PTR)arg2);
if (farg2 == 0.0) return (0);
fqperc = farg1 * 100.0 / farg2;
iqperc = (int)fqperc;
if (fqperc - (float)iqperc >= 0.5) iqperc++;
return (iqperc);
}
return (0);
}
/*****************************************************************************/
/*
Integer power-of.
*/
int ipow
(
int base,
int exp
)
{
int result = 1;
/*********/
/* begin */
/*********/
while (exp)
{
if (exp & 1) result *= base;
exp >>= 1;
base *= base;
}
return (result);
}
/*****************************************************************************/
/*
132 columns!
*/
#if WATCH_MOD
void TimeTest ()
{
int status;
int64 time64;
uint64 time128 [2];
ushort time7 [7],
time13 [13];
char *cptr;
char buf [256];
/*********/
/* begin */
/*********/
sys$getutc (&time128);
status = sys$numutc (&time13, &time128);
printf ("sys$numutc() %%X%08.08X %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
status,
time13[0], time13[1], time13[2], time13[3], time13[4],
time13[5], time13[6], time13[7], time13[8], time13[9],
time13[10], time13[11], time13[12]);
TimeSetHttpdUTC ();
printf ("TimeUtcDelta64: %lld sec:%lld min:%lld |%s|\n",
TimeUtcDelta64, TimeUtcDelta64 / TIME64_ONE_SEC,
TimeUtcDelta64 / TIME64_ONE_MIN, TimeGmtString);
HttpGetTime (cptr = "Fri, 25 Aug 1995 17:32:40 GMT", &time64);
sys$numtim (&time7, &time64);
HttpGmTimeString (buf, &time64);
printf ("%32.32s %d %d %d %d %d %d %d |%s|\n",
cptr, time7[0], time7[1], time7[2], time7[3], time7[4],
time7[5], time7[6], buf);
HttpGetTime (cptr = "Friday, 25-Aug-1995 17:32:40 GMT", &time64);
sys$numtim (&time7, &time64);
HttpGmTimeString (buf, &time64);
printf ("%32.32s %d %d %d %d %d %d %d |%s|\n",
cptr, time7[0], time7[1], time7[2], time7[3], time7[4],
time7[5], time7[6], buf);
HttpGetTime (cptr = "1995-08-25T17:32:40.34Z+0000", &time64);
sys$numtim (&time7, &time64);
HttpGmTimeString (buf, &time64);
printf ("%32.32s %d %d %d %d %d %d %d |%s|\n",
cptr, time7[0], time7[1], time7[2], time7[3], time7[4],
time7[5], time7[6], buf);
}
#endif /* WATCH_MOD */
/*****************************************************************************/
/*
Set any 64 bit time using the value of the specificed logical name.
Only for development purposes.
*/
#if WATCH_MOD
void SetAnyTime (char *lname, ulong *tbptr)
{
int status;
char *cptr;
if (cptr = SysTrnLnm (lname))
{
$DESCRIPTOR (dsc, "");
dsc.dsc$a_pointer = cptr;
dsc.dsc$w_length = strlen(cptr);
status = sys$bintim (&dsc, tbptr);
if (VMSnok (status)) ErrorExitVmsStatus (status, "SetAnyTime()", FI_LI);
}
}
#endif /* WATCH_MOD */
/*****************************************************************************/
/*
$ HTTPD /TEST MATCH for the relative performance of MATCH1..8() and memcmp().
*/
#if WATCH_MOD
MatchPerf ()
#pragma optimize save
#pragma optimize level=0
{
int cnt, result;
char MatchTest1 [] = "01234567890",
MatchTest2 [] = "012345678_0";
int MatchTest3 = '0123';
/*********/
/* begin */
/*********/
lib$init_timer (0);
for (cnt = 100000000; cnt; cnt--)
{
result = MATCH2 (MatchTest1, MatchTest2);
result = MATCH3 (MatchTest1, MatchTest2);
result = MATCH4 (MatchTest1, MatchTest2);
result = MATCH5 (MatchTest1, MatchTest2);
result = MATCH6 (MatchTest1, MatchTest2);
result = MATCH7 (MatchTest1, MatchTest2);
result = MATCH8 (MatchTest1, MatchTest2);
}
lib$show_timer (0,0,0,0);
lib$init_timer (0);
for (cnt = 100000000; cnt; cnt--)
{
result = !memcmp (MatchTest1, MatchTest2, 2);
result = !memcmp (MatchTest1, MatchTest2, 3);
result = !memcmp (MatchTest1, MatchTest2, 4);
result = !memcmp (MatchTest1, MatchTest2, 5);
result = !memcmp (MatchTest1, MatchTest2, 6);
result = !memcmp (MatchTest1, MatchTest2, 7);
result = !memcmp (MatchTest1, MatchTest2, 8);
}
lib$show_timer (0,0,0,0);
lib$init_timer (0);
for (cnt = 100000000; cnt; cnt--)
{
result = MATCH2 (MatchTest1, MatchTest2);
result = MATCH3 (MatchTest1, MatchTest2);
result = MATCH4 (MatchTest1, MatchTest2);
result = MATCH2 (MatchTest1, MatchTest2);
result = MATCH3 (MatchTest1, MatchTest2);
result = MATCH4 (MatchTest1, MatchTest2);
}
lib$show_timer (0,0,0,0);
lib$init_timer (0);
for (cnt = 100000000; cnt; cnt--)
{
result = SAME2 (MatchTest1, MatchTest3);
result = SAME3 (MatchTest1, MatchTest3);
result = SAME4 (MatchTest1, MatchTest3);
result = SAME2 (MatchTest1, MatchTest3);
result = SAME3 (MatchTest1, MatchTest3);
result = SAME4 (MatchTest1, MatchTest3);
}
lib$show_timer (0,0,0,0);
}
#pragma optimize restore
#endif /* WATCH_MOD */
/****************************************************************************/