#define boolean int
#define BOOL int
#define true 1
#define false 0
/* application related header file */
#include "cgilib.h"
char CopyrightInfo [] =
"Copyright (C) 2004-2006 Mark G.Daniel.\n\
This software comes with ABSOLUTELY NO WARRANTY.\n\
This is free software, and you are welcome to redistribute it\n\
under the conditions of the GNU GENERAL PUBLIC LICENSE, version 2.";
#ifndef __VAX
# pragma nomember_alignment
#endif
#define VMSok(x) ((x) & STS$M_SUCCESS)
#define VMSnok(x) (!((x) & STS$M_SUCCESS))
#define FI_LI "FORMWORK", __LINE__
#define QUIET_ERROR_EXIT STS$M_INHIB_MSG | STS$K_ERROR
#define FORM_URLENCODED "application/x-www-form-urlencoded"
#define WATCH_DIVIDER "+++++++++++++++"
char *MonthName [] = { "", "Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec" };
char Utility [] = "FORMWORK";
#define MAX_FIELDS 255
typedef struct FieldStruct FIELD_STRUCT;
struct FieldStruct {
BOOL AsIsCarCon,
EmailField,
IgnoreField,
MandatoryField,
NumericField;
int MaxSize;
char *DescPtr,
*FormWorkPtr,
*NamePtr,
*ValuePtr;
};
FIELD_STRUCT FieldData [MAX_FIELDS];
BOOL Debug,
CliDoubleSpace,
CliNoResponse,
FormWorkWatch;
int BodyBufferCount,
FieldCount,
FieldValueCapacity,
FormLength,
PrintfReportLength,
PrintfStringLength,
ScriptPid;
unsigned long BinTime [2];
unsigned short NumTime [7];
char *BodyBufferPtr,
*CharsetPtr,
*CgiContentTypePtr,
*CgiEnvironmentPtr,
*CgiRequestMethodPtr,
*CliCharsetPtr,
*CliCsvFileNamePtr,
*CliEmailAddressPtr,
*CliHtmlFileNamePtr,
*CliLocationPtr,
*CliPersonalNamePtr,
*CliProcessedFileNamePtr,
*CliRawFileNamePtr,
*CliSeparatorPtr = "\t",
*CliSubjectPtr,
*CliSymbolPtr = "FORMWORK_",
*FormPtr,
*PrintfReportPtr,
*PrintfStringPtr;
char CharsetString [64],
ContentTypeCharset [64],
SoftwareID [48],
SourceHost [256];
char ErrorBufferOverflow [] = "Buffer overflow.",
ErrorConfig [] =
"FormWork configuration error. Contact the site administrator.",
ErrorHttpMethod [] = "HTTP method must be \'GET\' or \'POST\'!",
ErrorReadingFile [] = "reading HTML file.",
ErrorMemory [] = "allocating memory.",
ErrorNotUrlEncoded [] = "Must be URL-encoded form data!",
ErrorWritingFile [] = "writing data file.",
SuccessMessage [] = "Successfully submitted. Thankyou.";
char ReportMandatory [] = "\'%s\' must be supplied.",
ReportNotEmail [] = "\'%s\' must be in the format \'user@host\'.",
ReportNotNumeric [] = "\'%s\' must contain only a number.",
ReportTooLarge [] = "\'%s\' must contain a maximum of %d characters.";
char ReportToClient [] =
"The following information must be corrected before proceding.\n\
\n%s
\n\
Go back to the form, make the changes, then submit again. Thankyou.\n";
/* required function prototypes */
void PrintfReport (char*, ...);
void PrintfString (char*, ...);
/*****************************************************************************/
/*
'argc/argv' are only required to support OSU, in particular CgiLibOsuInit().
*/
main
(
int argc,
char *argv[]
)
{
/*********/
/* begin */
/*********/
sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion());
if (getenv ("FORMWORK$DBUG"))
{
Debug = true;
CgiLibResponseHeader (200, "text/plain");
CgiLibEnvironmentSetDebug (Debug);
}
GetParameters ();
/* starts off as integer kBytes and ends up as integer bytes */
if (FieldValueCapacity < 1 || FieldValueCapacity > 1024)
FieldValueCapacity = 4;
FieldValueCapacity = 1024 * FieldValueCapacity;
CgiLibEnvironmentInit (argc, argv, false);
CgiLibResponseSetCharset (CliCharsetPtr);
CgiLibResponseSetSoftwareID (SoftwareID);
CgiLibResponseSetErrorMessage ("Reported by FormWork");
CgiEnvironmentPtr = CgiLibEnvironmentName ();
ProcessRequest ();
exit (SS$_NORMAL);
}
/*****************************************************************************/
/*
Script workhorse.
*/
ProcessRequest ()
{
static struct
{
unsigned short buf_len;
unsigned short item;
void *buf_addr;
void *ret_len;
} JpiItems [] =
{
{ sizeof(ScriptPid), JPI$_PID, &ScriptPid, 0 },
{ 0,0,0,0 }
};
int cnt, status;
char *cptr, *sptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessRequest()\n");
/* for time-stamping file names, etc. */
sys$gettim (&BinTime);
sys$numtim (&NumTime, &BinTime);
/* get the script PID for unique-ifying any output file names */
status = sys$getjpiw (0, 0, 0, &JpiItems, 0, 0, 0);
if (VMSnok (status)) exit (status);
if (cptr = getenv ("FORMWORK$WATCH"))
if (strsame (cptr, CliHtmlFileNamePtr, -1))
FormWorkWatch = true;
/* put together a string trying to identify the source host */
cptr = CgiLibVar ("REMOTE_ADDR");
sptr = CgiLibVar ("REMOTE_HOST");
if (strsame (cptr, sptr, -1))
cnt = sprintf (SourceHost, "%s", cptr);
else
cnt = sprintf (SourceHost, "%s (%s)", sptr, cptr);
cptr = CgiLibVarNull ("HTTP_X_FORWARDED_FOR");
if (cptr) cnt += sprintf (SourceHost+cnt, " (%s)", cptr);
cptr = CgiLibVarNull ("HTTP_FORWARDED");
if (cptr) cnt += sprintf (SourceHost+cnt, " (%s)", cptr);
if (cnt > sizeof(SourceHost)) exit (SS$_BUGCHECK);
status = ReadFileIntoMemory (CliHtmlFileNamePtr, &FormPtr, &FormLength);
if (VMSnok (status))
{
CgiLibResponseError (FI_LI, status, ErrorReadingFile);
exit (QUIET_ERROR_EXIT);
}
if (FormWorkWatch)
{
CgiLibResponseHeader (200, "text/plain");
fprintf (stdout, "%sHTML\n%s%sINPUTS\n",
WATCH_DIVIDER, FormPtr, WATCH_DIVIDER);
}
ProcessHtmlFile ();
CgiLibReadRequestBody (&BodyBufferPtr, &BodyBufferCount);
if (!BodyBufferPtr) BodyBufferPtr = "";
if (Debug) fprintf (stdout, "%d |%s|\n", BodyBufferCount, BodyBufferPtr);
if (FormWorkWatch)
fprintf (stdout, "%sBODY\n%s\n%sFIELDS\n",
WATCH_DIVIDER, BodyBufferPtr, WATCH_DIVIDER);
ProcessRequestBody ();
if (PrintfReportPtr)
{
if (FormWorkWatch) fprintf (stdout, "%sRESPONSE\n", WATCH_DIVIDER);
CgiLibResponseHeader (200, "text/html");
fprintf (stdout, "\n
\n\n");
fprintf (stdout, ReportToClient, PrintfReportPtr);
fprintf (stdout, "\n\n");
exit (QUIET_ERROR_EXIT);
}
else
{
if (CliCsvFileNamePtr ||
CliEmailAddressPtr ||
CliProcessedFileNamePtr ||
CliRawFileNamePtr)
{
if (FormWorkWatch) fprintf (stdout, "%sACTION\n", WATCH_DIVIDER);
FormatResponse ();
if (CliRawFileNamePtr)
{
status = WriteRawFile ();
if (VMSnok (status))
{
CgiLibResponseError (FI_LI, status, ErrorWritingFile);
exit (QUIET_ERROR_EXIT);
}
}
if (CliProcessedFileNamePtr)
{
status = WriteProcessedFile ();
if (VMSnok (status))
{
CgiLibResponseError (FI_LI, status, ErrorWritingFile);
exit (QUIET_ERROR_EXIT);
}
}
if (CliEmailAddressPtr)
{
char MailSubject [256];
if (!CliSubjectPtr)
{
sprintf (MailSubject, "FormWork: %s", CliHtmlFileNamePtr);
CliSubjectPtr = MailSubject;
}
MailMessage (CliPersonalNamePtr, CliEmailAddressPtr,
CliSubjectPtr, PrintfStringPtr);
}
/* must be done last because it reuses the FprintfString() storage */
if (CliCsvFileNamePtr)
{
status = WriteCsvFile ();
if (VMSnok (status))
{
CgiLibResponseError (FI_LI, status, ErrorWritingFile);
exit (QUIET_ERROR_EXIT);
}
}
}
if (FormWorkWatch) fprintf (stdout, "%sRESPONSE\n", WATCH_DIVIDER);
if (CliLocationPtr)
CgiLibResponseRedirect (CliLocationPtr);
else
if (!CliNoResponse)
{
fprintf (stdout, "Status: 200\r\nContent-Type: text/html\r\n\r\n");
fprintf (stdout, "%s\n", SuccessMessage);
}
if (CliSymbolPtr) SetCliSymbols ();
}
if (FormWorkWatch) fprintf (stdout, "%sEND\n", WATCH_DIVIDER);
}
/*****************************************************************************/
/*
Generate a human-readable version of the submitted form. This in-memory
version is used to generate the processed file and for the email body.
*/
FormatResponse ()
{
int idx;
char *cptr, *sptr;
FIELD_STRUCT *fptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "FormatResponse()\n");
/* reset the string function */
PrintfString (NULL);
PrintfString ("\
Date: %02.02d %s %04.04d %02.02d:%02.02d:%02.02d\n\
Server: %s:%s\n\
Host: %s\n\
Agent: %s\n\n",
NumTime[2], MonthName[NumTime[1]], NumTime[0],
NumTime[3], NumTime[4], NumTime[5],
CgiLibVar("SERVER_NAME"), CgiLibVar("SERVER_PORT"),
SourceHost, CgiLibVar("HTTP_USER_AGENT"));
for (idx = 0; idx < FieldCount; idx++)
{
fptr = &FieldData[idx];
if (fptr->IgnoreField) continue;
for (cptr = fptr->ValuePtr; *cptr && *cptr != '\n'; cptr++);
if (*cptr)
{
PrintfString ("%s:\n\n", fptr->DescPtr, fptr->ValuePtr);
cptr = fptr->ValuePtr;
while (*cptr)
{
sptr = cptr;
while (*cptr && *cptr != '\n') cptr++;
PrintfString (" %*.*s\n", cptr-sptr, cptr-sptr, sptr);
if (*cptr) cptr++;
}
PrintfString ("\n");
}
else
PrintfString ("%s: %s%s", fptr->DescPtr, fptr->ValuePtr,
CliDoubleSpace ? "\n\n" : "\n");
}
PrintfString ("%s: %s\n", SOFTWAREID, CliHtmlFileNamePtr);
if (Debug) fprintf (stdout, "PrintfStringPtr |%s|\n", PrintfStringPtr);
}
/*****************************************************************************/
/*
Process the in-memory request body, parsing out URL-form-encoded filed names
and the corresponding values checking each against the field directives
previously read from the file of the generating form. If there are any
discrepancies between the submitted and required daya then note each for later
reporting to the client.
*/
ProcessRequestBody ()
{
int idx, len,
FieldNameLength,
FieldValueLength,
FormWorkCount;
char *cptr, *sptr, *zptr,
*FieldValue;
char FieldName [256];
FIELD_STRUCT *fptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessRequestBody()\n");
FormWorkCount = 0;
FieldValue = calloc (1, FieldValueCapacity);
if (!FieldValue) exit (vaxc$errno);
cptr = BodyBufferPtr;
while (*cptr)
{
/**************/
/* field name */
/**************/
FieldName[0] = '\0';
FieldNameLength = 0;
while (*cptr && *cptr != '=')
{
zptr = (sptr = FieldName) + sizeof(FieldName)-1;
while (*cptr && *cptr != '=' && sptr < zptr)
{
if (isprint(*cptr))
*sptr++ = *cptr++;
else
cptr++;
}
if (sptr >= zptr)
{
CgiLibResponseError (FI_LI, 0, ErrorBufferOverflow);
exit (QUIET_ERROR_EXIT);
}
*sptr = '\0';
if (Debug) fprintf (stdout, "FieldName: |%s|\n", FieldName);
FieldNameLength += sptr - FieldName;
}
if (*cptr == '=') cptr++;
/***************/
/* field value */
/***************/
FieldValue[0] = '\0';
FieldValueLength = 0;
while (*cptr && *cptr != '&')
{
zptr = (sptr = FieldValue) + FieldValueCapacity - 1;
/* absorb leading white-space */
while (*cptr && *cptr != '&' && isspace(*cptr)) cptr++;
/* copy value while stripping carriage-control and non-printables */
while (*cptr && *cptr != '&' && sptr < zptr)
{
if (isprint(*cptr))
*sptr++ = *cptr++;
else
cptr++;
}
if (sptr >= zptr)
{
CgiLibResponseError (FI_LI, 0, ErrorBufferOverflow);
exit (QUIET_ERROR_EXIT);
}
/* absorb trailing white-space */
if (sptr > FieldValue && isspace(*(sptr-1)))
{
sptr--;
while (sptr > FieldValue && isspace(*sptr)) sptr++;
if (!isspace(*sptr)) sptr++;
}
*sptr = '\0';
if (Debug) fprintf (stdout, "FieldValue: |%s|\n", FieldValue);
CgiLibUrlDecode (FieldValue);
FieldValueLength = strlen(FieldValue);
}
if (*cptr == '&') cptr++;
/***********************/
/* find the field data */
/***********************/
if (FormWorkWatch)
fprintf (stdout, "%03.03d %3.d|%s|\n %3.d|%s|\n",
++FormWorkCount,
strlen(FieldName), FieldName,
strlen(FieldValue), FieldValue);
for (idx = 0; idx < FieldCount; idx++)
if (strsame (FieldData[idx].NamePtr, FieldName, -1)) break;
if (idx >= FieldCount)
{
if (FormWorkWatch)
fprintf (stdout, "FIELD NAME NOT FOUND %d|%s|\n",
strlen(FieldName), FieldName);
CgiLibResponseError (FI_LI, 0, ErrorConfig);
exit (QUIET_ERROR_EXIT);
}
/*******************/
/* store the value */
/*******************/
if (FieldData[idx].ValuePtr &&
FieldData[idx].ValuePtr[0])
{
if (FieldValue[0])
{
len = strlen(FieldData[idx].ValuePtr);
FieldData[idx].ValuePtr = realloc (FieldData[idx].ValuePtr,
len+1+FieldValueLength+1);
FieldData[idx].ValuePtr[len] = '+';
strcpy (FieldData[idx].ValuePtr+len+1, FieldValue);
}
}
else
{
FieldData[idx].ValuePtr = calloc (1, FieldValueLength+1);
strcpy (FieldData[idx].ValuePtr, FieldValue);
}
if (FormWorkWatch)
if (FieldData[idx].ValuePtr)
fprintf (stdout, " %3.d|%s|\n",
strlen(FieldData[idx].ValuePtr), FieldData[idx].ValuePtr);
else
fprintf (stdout, " %3.d|{null}|\n", 0);
}
/****************************/
/* check/process the fields */
/****************************/
for (idx = 0; idx < FieldCount; idx++)
{
fptr = &FieldData[idx];
if (Debug)
fprintf (stdout, "%03d %d %d %d %d %d |%s|%s|%s|%s|\n",
idx+1,
fptr->EmailField, fptr->IgnoreField,
fptr->MandatoryField, fptr->NumericField,
fptr->MaxSize,
fptr->NamePtr, fptr->FormWorkPtr,
fptr->DescPtr, fptr->ValuePtr);
if (fptr->MandatoryField && (!fptr->ValuePtr || !fptr->ValuePtr[0]))
{
PrintfReport (ReportMandatory, fptr->DescPtr);
continue;
}
if (fptr->EmailField)
{
for (cptr = fptr->ValuePtr; *cptr && *cptr != '@'; cptr++);
if (!*cptr || cptr == fptr->ValuePtr)
PrintfReport (ReportNotEmail, fptr->DescPtr);
else
{
cptr++;
sptr = cptr;
while (*cptr) cptr++;
if (cptr == sptr) PrintfReport (ReportNotEmail, fptr->DescPtr);
}
}
if (fptr->MaxSize)
{
if (fptr->ValuePtr && strlen(fptr->ValuePtr) > fptr->MaxSize)
PrintfReport (ReportTooLarge, fptr->DescPtr, fptr->MaxSize);
}
if (fptr->NumericField)
{
for (cptr = fptr->ValuePtr; *cptr && isdigit(*cptr); cptr++);
if (*cptr) PrintfReport (ReportNotNumeric, fptr->DescPtr);
}
if (!fptr->AsIsCarCon && fptr->ValuePtr)
{
/* by default carriage-control is replaced by spaces */
cptr = fptr->ValuePtr;
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
if (*cptr)
{
sptr = cptr;
while (*cptr)
{
if (*(unsigned short*)cptr == '\r\n')
{
*sptr++ = ' ';
cptr += 2;
}
else
if (*cptr == '\r' || *cptr == '\n')
{
*sptr++ = ' ';
cptr++;
}
else
*sptr++ = *cptr++;
}
*sptr = '\0';
}
}
}
free (FieldValue);
}
/*****************************************************************************/
/*
Some small functions used by ProcessHtmlFile().
*/
char* FindStartOfTag (char *cptr)
{
while (*cptr && *cptr != '<') cptr++;
return (cptr);
}
char* FindEndOfTag (char *cptr)
{
while (*cptr) {
while (*cptr && *cptr != '>' && *cptr != '\"') cptr++;
if (!*cptr || *cptr == '>') break;
cptr++;
while (*cptr && *cptr != '\"') cptr++;
if (*cptr) cptr++;
}
return (cptr);
}
/* find something like 'name="content"' */
char* FindElement (char *cptr, char *eptr, int elen)
{
while (*cptr) {
while (*cptr && *cptr != '\"' && *cptr != '>' &&
!strsame(cptr, eptr, elen)) cptr++;
if (*cptr != '\"') break;
cptr++;
while (*cptr && *cptr != '\"') cptr++;
if (*cptr) cptr++;
}
return (cptr);
}
/* get the content from something like 'name="content"' */
char* GetElement (char *cptr, char *bptr, int blen)
{
char *sptr, *zptr;
zptr = (sptr = bptr) + blen-1;
if (*cptr == '\"')
{
cptr++;
while (*cptr && *cptr != '\"' && sptr < zptr) *sptr++ = *cptr++;
if (*cptr) cptr++;
}
else
while (*cptr && *cptr != '>' && !isspace(*cptr) && sptr < zptr)
*sptr++ = *cptr++;
*sptr = '\0';
return (cptr);
}
/*****************************************************************************/
/*
Read the contents of the /HTML= specified HTML file containing the form and
the formwork="" directives into memory. Parse that file populating the
'FormData' array with the form data. While processing check the directives for
anomalies and report as necessary.
*/
ProcessHtmlFile ()
{
int idx, status,
FormWorkCount,
FormWorkDataLength,
InputDescLength,
InputLiteralLength,
InputNameLength;
char *cptr, *sptr, *zptr;
char FormWorkData [256],
FormName [256],
InputDesc [256],
InputLiteral [256],
InputName [256],
InputTag [1024],
InputType [256],
Scratch [256];
FIELD_STRUCT *fptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessHtmlFile()\n");
FormWorkCount = 0;
cptr = FormPtr;
while (*cptr)
{
/********************************/
/* find the (next) tag */
/********************************/
cptr = FindStartOfTag (cptr);
if (!*cptr) break;
if (!strsame (cptr, "