#define BOOL int
#define true 1
#define false 0
#define VMSok(x) ((x) & STS$M_SUCCESS)
#define VMSnok(x) !(((x) & STS$M_SUCCESS))
#define FI_LI __FILE__, __LINE__
#define DEFAULT_BUFFER_CHUNK 32
#define DEFAULT_FTP_CONTENT_TYPE "application/octet-stream"
#define DEFAULT_ICON_PATH "/httpd/-/"
#define DEFAULT_PARAM_SUBS_CHAR '*'
#define DEFAULT_PROXY_SCRIPT_NAME "/fetch"
/******************/
/* global storage */
/******************/
char CopyrightInfo [] =
"Copyright (C) 1998-2005 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.";
char Utility [] = "FETCH";
BOOL CgiLibDebug,
Debug,
DoCheckOnly,
DoEscapeHtml,
DoFtp,
DoMassageLinks,
DoReport,
DoResponseBody,
DoResponseHeader,
FetchDebugFtp,
IsCgiPlus,
IsFetchScript,
WatchEnabled;
int BufferChunk,
BufferCount,
BufferRemaining,
BufferSize,
CgiScriptNameLength,
FetchPrefixLength,
RemoteHostPortLength,
RemotePort,
RequestHttpVersion,
ResponseHttpVersion,
ReverseProxyLength;
char *BufferPtr,
*BufferCurrentPtr,
*CgiContentTypePtr,
*CgiLibEnvironmentPtr,
*CgiQueryStringPtr,
*CgiRequestSchemePtr,
*CgiRequestMethodPtr,
*CgiScriptNamePtr,
*CgiServerNamePtr,
*CgiServerPortPtr,
*CgiServerSoftwarePtr,
*CliHttpMethodPtr,
*FetchScriptNamePtr,
*FetchUrlPtr,
*FtpDefaultContentTypePtr = DEFAULT_FTP_CONTENT_TYPE,
*HttpMethodPtr,
*IconPathPtr = DEFAULT_ICON_PATH,
*OutputPtr,
*ReversePathPtr,
*ReverseProxyPtr,
*UserAgentPtr;
struct HttpEnvironmentStruct
{
char *field,
*cgi;
}
/* those commented out should be commented-out for a reason :-) */
HttpEnvironment [] =
{
{ "Accept: ", "HTTP_ACCEPT" },
{ "Accept-Charset: ", "HTTP_ACCEPT_CHARSET" },
/** { "Accept-Encoding: ", "HTTP_ACCEPT_ENCODING" }, **/
{ "Accept-Language: ", "HTTP_ACCEPT_LANGUAGE" },
{ "Authorization: ", "HTTP_AUTHORIZATION" },
/** { "Cache-Control: ", "HTTP_CACHE_CONTROL" }, **/
/** { "Connection: ", "HTTP_CONNECTION" }, **/
/** { "Content-Length: ", "HTTP_CONTENT_LENGTH" }, **/
/* not 'HTTP_' to be backward-compatible with pre-9.0 WASD */
{ "Content-Type: ", "CONTENT_TYPE" },
{ "Cookie: ", "HTTP_COOKIE" },
/** { "Forwarded: ", "HTTP_FORWARDED" }, **/
/** { "Host: ", "HTTP_HOST" }, **/
{ "If-Match: ", "HTTP_IF_MATCH" },
{ "If-None-Match: ", "HTTP_IF_NONE_MATCH" },
{ "If-Modified-Since: ", "HTTP_IF_MODIFIED_SINCE" },
{ "If-Unmodified-Since: ", "HTTP_IF_UNMODIFIED_SINCE" },
{ "If-Range: ", "HTTP_IF_RANGE" },
/** { "Pragma: ", "HTTP_PRAGMA" }, **/
{ "Range: ", "HTTP_RANGE" },
/** { "Referer: ", "HTTP_REFERER" }, **/
{ "Trailer: ", "HTTP_TRAILER" },
/** { "Transfer-Encoding: ", "HTTP_TRANSFER_ENCODING" }, **/
/** { "User-Agent: ", "HTTP_USER_AGENT" }, **/
/** { "X-Forwarded-For: ", "HTTP_X_FORWARDED_FOR" }, **/
{ NULL, NULL }
};
char ParamSubsChar = DEFAULT_PARAM_SUBS_CHAR;
char FetchPrefix [256],
FetchUrlBuffer [1024],
FtpIconDirImg [256],
FtpIconFileImg [256],
LocalHostPort [256],
MassageLinksPath [256],
RemoteHost [256],
RemoteHostPort [256],
RequestHeader [4096],
RequestPath [1024],
RequestScheme [64],
ResponseContentType [256],
ReverseProxyPrefix [256],
SoftwareId [96];
struct AnIOsb {
unsigned short Status;
unsigned short Count;
unsigned long Unused;
} IOsb;
#define CGIVAR(p,n) \
{ p = CgiLibVar(n); \
if (WatchEnabled) WatchThis ("CGIVAR !AZ=!AZ",n,p); }
#define CGIVARNULL(p,n) \
{ p = CgiLibVarNull(n); \
if (WatchEnabled) WatchThis ("CGIVAR !AZ=!AZ",n,p?p:"(null)"); }
$DESCRIPTOR (InetDeviceDsc, "UCX$DEVICE");
unsigned short RemoteChannel;
int OptionEnabled = 1;
struct {
unsigned short Length;
unsigned short Parameter;
void *Address;
} ReuseAddress =
{ sizeof(OptionEnabled), UCX$C_REUSEADDR, &OptionEnabled },
ReuseAddressSocketOption =
{ sizeof(ReuseAddress), UCX$C_SOCKOPT, &ReuseAddress };
unsigned short TcpSocket [3] =
{ UCX$C_TCP, INET_PROTYP$C_STREAM, UCX$C_AF_INET };
/* these 3 strings may be changed to a desired language */
char FtpCurrentDirectory [] = "Current directory is";
char FtpFilesDirectories [] =
"\n\
Files: %d\n\
Directories: %d\n\
%s\
\n";
char FtpGetAs [] =
"
a ... get as text\n\
i ... as binary\n\
d ... as directory\n";
/* required prototypes */
char* FetchFtpResponse ();
char* FetchHttpResponse ();
char* FtpCommand (BOOL, unsigned short, char*, ...);
char* FtpReply (unsigned short);
char* GetParameterString (char*);
char* TrnLnm (char*, char*, int);
int ReadConnection (unsigned short, char*, int, int*, BOOL);
char* ReplyVmsError (int);
int WriteConnection (unsigned short, char*, int);
int WatchDump (char*, int);
int WatchThis (char*, ...);
/*****************************************************************************/
/*
*/
main
(
int argc,
char *argv[]
)
{
int status;
char *cptr;
/*********/
/* begin */
/*********/
sprintf (SoftwareId, "%s, %s", SOFTWAREID, CgiLibEnvironmentVersion());
FetchScriptNamePtr = DEFAULT_PROXY_SCRIPT_NAME;
HttpMethodPtr = "GET";
RequestHttpVersion = 10;
OutputPtr = "";
DoResponseBody = DoResponseHeader = true;
DoEscapeHtml = DoFtp = DoReport = false;
if (TrnLnm ("FETCH$DBUG", NULL, 0)) Debug = true;
if (TrnLnm ("FETCH$DBUG_CGILIB", NULL, 0)) CgiLibDebug = true;
GetParameters ();
if (CliHttpMethodPtr) HttpMethodPtr = CliHttpMethodPtr;
if (!HttpMethodPtr[0]) HttpMethodPtr = "GET";
if (RequestHttpVersion != 9 && RequestHttpVersion != 10)
{
fprintf (stdout, "%%%s-E-VERSION, must be 0.9 or 1.0\n", Utility);
exit (STS$K_ERROR | STS$M_INHIB_MSG);
}
/***********/
/* process */
/***********/
CgiLibEnvironmentInit (argc, argv, false);
CgiLibEnvironmentSetDebug (Debug);
CgiLibResponseSetSoftwareID (SoftwareId);
CgiLibResponseSetErrorStatus (502);
CgiLibResponseSetErrorMessage ("Reported by Proxy Agent");
IsCgiPlus = CgiLibEnvironmentIsCgiPlus ();
if (IsCgiPlus)
{
for (;;)
{
if (Debug) fprintf (stdout,
"Content-Type: text/plain\nScript-Control: X-content-encoding-gzip=0\n\n");
/* block waiting for the next request */
CgiLibVar ("");
ProcessRequest ();
CgiLibCgiPlusEOF ();
CgiQueryStringPtr = FetchUrlPtr = NULL;
}
}
else
ProcessRequest ();
exit (SS$_NORMAL);
}
/*****************************************************************************/
/*
*/
ProcessRequest ()
{
int StatusValue;
char *cptr, *sptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessRequest()\n");
if (cptr = TrnLnm ("FETCH$WATCH", NULL, 0))
{
if (Debug) fprintf (stdout, "FETCH$WATCH |%s|\n", cptr);
WatchEnabled = 1;
if (isdigit(*cptr))
{
/* if it doesn't match the remote address then disable again */
if (!(sptr = CgiVar ("REMOTE_ADDR"))) sptr = "";
if (strcmp (cptr, sptr)) WatchEnabled = 0;
}
}
if (Debug) WatchEnabled = 1;
if (WatchEnabled)
{
/* initialize watch mode */
WatchThis (NULL);
if (CgiLibVarNull ("SCRIPT_RTE"))
cptr = "RTE";
else
if (IsCgiPlus)
cptr = "CGIplus";
else
cptr = "CGI";
WatchThis ("BEGIN !AZ; !AZ", cptr, SoftwareId);
}
CgiLibEnvironmentPtr = CgiLibEnvironmentName();
CGIVAR (CgiScriptNamePtr, "WWW_SCRIPT_NAME");
CgiScriptNameLength = strlen(CgiScriptNamePtr);
if (CgiScriptNamePtr[0] && !FetchUrlPtr)
{
if (!strsame (CgiScriptNamePtr, FetchScriptNamePtr, -1))
{
fprintf (stdout, "%%%s-E-SCRIPT, must be mapped to \"%s\"\n",
Utility, FetchScriptNamePtr);
exit (STS$K_ERROR | STS$M_INHIB_MSG);
}
IsFetchScript = true;
}
else
IsFetchScript = false;
CGIVAR (CgiRequestSchemePtr, "WWW_REQUEST_SCHEME");
CGIVAR (CgiRequestMethodPtr, "WWW_REQUEST_METHOD");
CGIVAR (CgiServerNamePtr, "WWW_SERVER_NAME");
CGIVAR (CgiServerPortPtr, "WWW_SERVER_PORT");
CGIVAR (CgiServerSoftwarePtr, "WWW_SERVER_SOFTWARE");
CGIVAR (CgiQueryStringPtr, "WWW_QUERY_STRING");
CGIVAR (UserAgentPtr, "WWW_HTTP_USER_AGENT");
if (!UserAgentPtr[0]) UserAgentPtr = SoftwareId;
/* e.g. created with a 'set * script=param=FETCH_REVERSE_PROXY=*' */
CGIVARNULL (ReverseProxyPtr, "FETCH_REVERSE_PROXY");
if (ReverseProxyPtr) ReverseProxyLength = strlen(ReverseProxyPtr);
/* e.g. created with a 'set * script=param=FETCH_REVERSE_PATH=/whatever' */
CGIVAR (ReversePathPtr, "FETCH_REVERSE_PATH");
/* URL not supplied on command line, get it from the script path */
if (!FetchUrlPtr)
{
CGIVAR (FetchUrlPtr, "WWW_PATH_INFO");
FetchUrlPtr++;
}
if (!FetchUrlPtr)
{
fprintf (stdout, "%%%s-E-URL, no URL supplied\n", Utility);
exit (STS$K_ERROR | STS$M_INHIB_MSG);
}
/* buffer in case the following code distorts the original URL */
strcpy (FetchUrlBuffer, FetchUrlPtr);
/* pages with massaged links will have the circumflex before the protocol */
if (!memcmp (FetchUrlPtr, "~http://", 8) ||
!memcmp (FetchUrlPtr, "~ftp://", 7))
{
/* step over the circumflex */
FetchUrlPtr++;
DoMassageLinks = true;
}
else
if (ReverseProxyPtr)
DoMassageLinks = true;
else
DoMassageLinks = false;
/* the leading '_' is just a contrivance to allow easier HTTPd mapping */
if (!memcmp (FetchUrlPtr, "_http://", 8) ||
!memcmp (FetchUrlPtr, "_ftp://", 7))
{
/* step over the underscore */
FetchUrlPtr++;
}
if (DoFtp || !memcmp (FetchUrlPtr, "ftp://", 6))
{
/***************/
/* FTP request */
/***************/
cptr = FetchFtpResponse ();
SetLocalSymbol ("FETCH_RESPONSE", cptr);
if (*cptr != '2')
{
if (IsFetchScript || DoReport)
{
if (!memcmp (cptr, "599", 3))
CgiLibResponseError (FI_LI, 0, " %s",
cptr+4, cptr+14);
else
CgiLibResponseError (FI_LI, 0, "%s", cptr);
}
else
{
if (!memcmp (cptr, "599", 3))
sscanf (cptr+6, "%x", &StatusValue);
else
StatusValue = STS$K_ERROR;
exit (StatusValue | (WatchEnabled ? 0 : STS$M_INHIB_MSG));
}
}
}
else
{
/****************/
/* HTTP request */
/****************/
if (DoCheckOnly)
HttpMethodPtr = "HEAD";
else
if (CliHttpMethodPtr)
HttpMethodPtr = CliHttpMethodPtr;
else
if (CgiRequestMethodPtr[0])
{
HttpMethodPtr = CgiRequestMethodPtr;
if (strcmp (HttpMethodPtr, "GET") &&
strcmp (HttpMethodPtr, "HEAD") &&
strcmp (HttpMethodPtr, "POST"))
{
CgiLibResponseError (FI_LI, 0, "HTTP method %s not supported",
HttpMethodPtr);
return;
}
}
else
HttpMethodPtr = "GET";
cptr = FetchHttpResponse ();
SetLocalSymbol ("FETCH_RESPONSE", cptr);
if (*cptr == '2')
{
if (DoCheckOnly)
{
if (!(cptr = BufferPtr))
exit (STS$K_ERROR | STS$M_INHIB_MSG);
if (Debug) fprintf (stdout, "|%s|\n", BufferPtr);
/* this just creates the informational symbols */
ProcessHttpResponse ();
/* check the response status code for success */
while (*cptr && !isspace(*cptr)) cptr++;
while (*cptr && isspace(*cptr)) cptr++;
if (*cptr == '2') exit (SS$_NORMAL);
if (!memcmp (cptr, "599", 3))
sscanf (cptr+6, "%x", &StatusValue);
else
StatusValue = 0;
exit (StatusValue | (WatchEnabled ? 0 : STS$M_INHIB_MSG));
}
/* HTTP response is being written to a file */
if (*OutputPtr)
if (!(stdout = freopen (OutputPtr, "w", stdout)))
exit (vaxc$errno);
if (DoMassageLinks)
MassageLinks ();
else
ProcessHttpResponse ();
}
else
if (!memcmp (cptr, "302", 3))
exit (SS$_NORMAL);
else
{
if (IsFetchScript || DoReport)
{
if (!memcmp (cptr, "599", 3))
CgiLibResponseError (FI_LI, 0, " %s",
cptr+4, cptr+14);
else
CgiLibResponseError (FI_LI, 0, "%s", cptr);
}
else
{
if (!memcmp (cptr, "599", 3))
sscanf (cptr+6, "%x", &StatusValue);
else
StatusValue = STS$K_ERROR;
exit (StatusValue | (WatchEnabled ? 0 : STS$M_INHIB_MSG));
}
}
}
/* reset watch mode as necessary */
if (WatchEnabled) WatchThis (NULL);
}
/****************************************************************************/
/*
Determine the host name and port and then create a request to send to it.
Connect and send that request, reading the complete response into a buffer of
dynamically allocated memory.
*/
char* FetchHttpResponse ()
{
static char HexDigits [] = "0123456789ABCDEF";
int idx, status,
BodyLength,
LocalPort,
ReadCount;
unsigned short RemoteChannel;
unsigned char ch;
char *cptr, *hptr, *sptr,
*BodyPtr;
char LocalHost [128];
/*********/
/* begin */
/*********/
if (!FetchUrlPtr[0]) return ("599 %x00038268");
RequestHeader[0] = RemoteHost[0] = '\0';
hptr = RequestHeader;
for (cptr = HttpMethodPtr; *cptr; *hptr++ = *cptr++);
*hptr++ = ' ';
cptr = FetchUrlPtr;
if (strsame (cptr, "http://", 7)) cptr += 7;
if (*CgiServerNamePtr)
strcpy (LocalHost, CgiServerNamePtr);
else
gethostname (LocalHost, sizeof(LocalHost));
if (*CgiServerPortPtr)
LocalPort = atoi(CgiServerPortPtr);
else
LocalPort = 80;
if (LocalPort == 80 || LocalPort == 443)
strcpy (LocalHostPort, LocalHost);
else
sprintf (LocalHostPort, "%s:%d", LocalHost, LocalPort);
if (Debug) fprintf (stdout, "LocalHostPort |%s|\n", LocalHostPort);
if (*cptr == '/')
{
strcpy (RemoteHost, LocalHost);
RemotePort = LocalPort;
strcpy (RemoteHostPort, LocalHostPort);
}
else
{
/* get host and optional port */
sptr = RemoteHost;
while (*cptr && *cptr != '/' && *cptr != ':')
*sptr++ = *cptr++;
*sptr = '\0';
if (*cptr == ':')
{
cptr++;
RemotePort = atoi(cptr);
if (!RemotePort) RemotePort = 80;
while (*cptr && *cptr != '/') cptr++;
}
else
RemotePort = 80;
if (RemotePort == 80)
strcpy (RemoteHostPort, RemoteHost);
else
sprintf (RemoteHostPort, "%s:%d", RemoteHost, RemotePort);
}
RemoteHostPortLength = strlen(RemoteHostPort);
if (Debug) fprintf (stdout, "|%s|%s|\n", RemoteHostPort, cptr);
if (DoMassageLinks)
{
sprintf (FetchPrefix, "%s//%s%s/~",
CgiRequestSchemePtr, LocalHostPort, CgiScriptNamePtr);
sprintf (MassageLinksPath, "%s/~", CgiScriptNamePtr);
sprintf (ReverseProxyPrefix, "%s//%s%s",
CgiRequestSchemePtr, LocalHostPort, ReversePathPtr);
}
else
FetchPrefix[0] = MassageLinksPath[0] = ReverseProxyPrefix[0] = '\0';
if (IsFetchScript && *cptr != '/')
{
/* no path (i.e. "http://remote.host.name") which can break links */
fprintf (stdout,
"HTTP/1.0 302 Moved temporarily\r\n\
Location: %s%s/\r\n\
\r\n",
FetchPrefix, FetchUrlPtr);
return ("302 Redirection!");
}
if (!strcmp (CgiRequestMethodPtr, "POST"))
{
if (!CgiLibReadRequestBody (&BodyPtr, &BodyLength))
{
CgiLibResponseError (FI_LI, 0, "Could not read request body.");
return ("502 Could not read request body.");
}
if (WatchEnabled)
{
WatchThis ("POST !UL", BodyLength);
WatchDump (BodyPtr, BodyLength);
}
}
/* rest of the path, which is actually the desired request path */
sptr = RequestPath;
if (!*cptr) *hptr++ = *sptr++ = '/';
/* escape any URL forbidden characters in the path */
while (ch = *cptr)
{
if (iscntrl(ch) || isspace(ch))
{
*hptr++ = *sptr++ = '%';
*hptr++ = *sptr++ = HexDigits[ch >> 4];
*hptr++ = *sptr++ = HexDigits[ch & 0x0f];
cptr++;
}
else
*hptr++ = *sptr++ = *cptr++;
}
/* any non-command-line query string */
if (IsFetchScript && (cptr = CgiQueryStringPtr))
{
if (*cptr)
{
if (*cptr) *hptr++ = *sptr++ = '?';
while (*cptr) *hptr++ = *sptr++ = *cptr++;
}
}
/* terminate the request path */
*sptr = '\0';
if (RequestHttpVersion == 9)
{
/* end of request line */
*hptr++ = '\r';
*hptr++ = '\n';
}
else
{
/* add the HTTP protocol to the request */
for (cptr = " HTTP/1.0\r\n"; *cptr; *hptr++ = *cptr++);
/* fudge the "Host:" request field */
for (sptr = "Host: "; *sptr; *hptr++ = *sptr++);
cptr = RemoteHostPort;
while (*cptr) *hptr++ = *cptr++;
*hptr++ = '\r';
*hptr++ = '\n';
cptr = CgiLibVarNull("WWW_HTTP_REFERER");
if (cptr && ReverseProxyPtr)
{
/* if it referes to the proxy server then replace with proxied-to */
if (strstr (cptr, CgiLibVar("WWW_HTTP_HOST")))
{
for (sptr = "Referer: http://"; *sptr; *hptr++ = *sptr++);
for (sptr = RemoteHostPort; *sptr; *hptr++ = *sptr++);
/* skip over the scheme and host components */
while (*cptr && *cptr != '/') cptr++;
while (*cptr && *cptr == '/') cptr++;
while (*cptr && *cptr != '/') cptr++;
}
else
for (sptr = "Referer: "; *sptr; *hptr++ = *sptr++);
while (*cptr) *hptr++ = *cptr++;
*hptr++ = '\r';
*hptr++ = '\n';
}
else
if (cptr)
{
sptr = cptr;
if (!memcmp (sptr, "http://", 7))
{
sptr += 7;
while (*sptr && *sptr != '/') sptr++;
if (!memcmp (sptr, CgiScriptNamePtr, CgiScriptNameLength))
{
sptr += CgiScriptNameLength;
if (*sptr == '/') cptr = sptr + 1;
if (Debug) fprintf (stdout, "Referer |%s|\n", cptr);
}
}
for (sptr = "Referer: "; *sptr; *hptr++ = *sptr++);
while (*cptr) *hptr++ = *cptr++;
*hptr++ = '\r';
*hptr++ = '\n';
}
if (!UserAgentPtr) UserAgentPtr = CgiLibVarNull ("HTTP_USER_AGENT");
if ((cptr = UserAgentPtr))
{
for (sptr = "User-Agent: "; *sptr; *hptr++ = *sptr++);
while (*cptr) *hptr++ = *cptr++;
*hptr++ = '\r';
*hptr++ = '\n';
}
if (!strcmp (CgiRequestMethodPtr, "POST"))
hptr += sprintf (hptr, "Content-Length: %d\r\n", BodyLength);
for (idx = 0; HttpEnvironment[idx].cgi; idx++)
{
if (!(cptr = CgiLibVarNull(HttpEnvironment[idx].cgi))) continue;
for (sptr = HttpEnvironment[idx].field; *sptr; *hptr++ = *sptr++);
while (*cptr) *hptr++ = *cptr++;
*hptr++ = '\r';
*hptr++ = '\n';
}
/* end of request header blank line */
*hptr++ = '\r';
*hptr++ = '\n';
}
/* terminate the request header */
*hptr = '\0';
if (hptr > RequestHeader + sizeof(RequestHeader)) exit (SS$_BUGCHECK);
if (WatchEnabled) WatchThis ("HTTP\n!AZ", RequestHeader);
/*******************/
/* open connection */
/*******************/
status = OpenConnection (&RemoteChannel, RemoteHost, RemotePort, NULL);
if (VMSnok (status)) return (ReplyVmsError (status));
/****************/
/* send request */
/****************/
if (Debug) fprintf (stdout, "request |%s|\n", RequestHeader);
status = WriteConnection (RemoteChannel, RequestHeader, -1);
if (VMSnok (status)) return (ReplyVmsError (status));
if (!strcmp (CgiRequestMethodPtr, "POST"))
{
status = WriteConnection (RemoteChannel, BodyPtr, BodyLength);
if (VMSnok (status)) return (ReplyVmsError (status));
}
/*********************/
/* read the response */
/*********************/
if (BufferChunk <= 0) BufferChunk = DEFAULT_BUFFER_CHUNK;
/* with CGIplus 'BufferPtr'/'BufferSize' may still have memory allocated */
BufferCount = 0;
BufferRemaining = BufferSize;
BufferCurrentPtr = BufferPtr;
for (;;)
{
if (BufferCount == BufferSize)
{
BufferSize += (BufferRemaining = (BufferChunk << 10));
if (!(BufferPtr = realloc (BufferPtr, BufferSize+1)))
exit (vaxc$errno);
BufferCurrentPtr = BufferPtr + BufferSize - BufferRemaining;
}
ReadCount = BufferRemaining <= 32767 ? BufferRemaining : 32767,
status = ReadConnection (RemoteChannel, BufferCurrentPtr, ReadCount,
&ReadCount, false);
if (VMSnok (status)) break;
BufferCount += ReadCount;
BufferCurrentPtr += ReadCount;
BufferRemaining -= ReadCount;
if (Debug)
fprintf (stdout, "Remaining: %d Count: %d\n",
BufferRemaining, BufferCount);
}
/****************/
/* close socket */
/****************/
if (status == SS$_LINKDISCON) status = SS$_NORMAL;
sys$dassgn (RemoteChannel);
return ("200 OK!");
}
/****************************************************************************/
/*
Output the response. Can output only the header, only the body, both, and
also escape the HTML-forbidden characters "<", ">", and "&" so an HTML file
can be presented as plain text inside another HTML document.
*/
ProcessHttpResponse ()
{
int cnt;
char *bptr, *cptr, *sptr;
char SymbolName [256],
SymbolValue [1024];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessHttpResponse()\n");
ResponseContentType[0] = '\0';
if (!(cptr = BufferPtr)) return;
if (!(cnt = BufferCount)) return;
if (memcmp (cptr, "HTTP/", 5) ||
!isdigit(cptr[5]) ||
cptr[6] != '.' ||
!isdigit(cptr[7]))
ResponseHttpVersion = 9;
else
ResponseHttpVersion = 10;
if (ResponseHttpVersion == 9)
{
/* HTTP/0.9 only ever returned HTML! */
strcpy (ResponseContentType, "text/html");
}
else
{
/* process the response header */
while (cnt)
{
/* create a symbol representing the response header line */
sptr = SymbolName;
for (bptr = "FETCH_"; *bptr; *sptr++ = *bptr++);
if (!memcmp (cptr, "HTTP/", 5))
{
for (bptr = "STATUS"; *bptr; *sptr++ = *bptr++);
*sptr = '\0';
bptr = cptr;
}
else
{
for (bptr = cptr;
*bptr && *bptr != ':' &&
*bptr != ' ' && *bptr != '\t' &&
*bptr != '\r' && *bptr != '\n';
bptr++)
{
if (!isalnum(*bptr))
*sptr++ = '_';
else
*sptr++ = *bptr;
}
*sptr = '\0';
}
if (*bptr == ':') bptr++;
while (*bptr && (*bptr == ' ' || *bptr == '\t')) bptr++;
sptr = SymbolValue;
while (*bptr && *bptr != '\r' && *bptr != '\n')
*sptr++ = *bptr++;
*sptr = '\0';
SetLocalSymbol (SymbolName, SymbolValue);
if (toupper (cptr[0]) == 'C' &&
strsame (cptr, "Content-Type:", 13))
{
cptr += 13;
cnt -= 13;
while (cnt && isspace(*cptr))
{
cptr++;
cnt--;
}
sptr = ResponseContentType;
while (cnt && *cptr != '\r' && *cptr != '\n')
{
*sptr++ = *cptr++;
cnt--;
}
*sptr = '\0';
}
/* skip to end-of-line */
while (cnt && *cptr != '\r' && *cptr != '\n')
{
cptr++;
cnt--;
}
if (cnt && *cptr == '\r')
{
cptr++;
cnt--;
}
if (cnt && *cptr == '\n')
{
cptr++;
cnt--;
}
/* break if end of request header */
if (cnt && *cptr == '\n')
{
cptr++;
cnt--;
break;
}
if (cnt >= 2 && cptr[0] == '\r' && cptr[1] == '\n')
{
cptr += 2;
cnt -= 2;
break;
}
}
}
if (Debug)
fprintf (stdout, "ResponseContentType |%s|\n", ResponseContentType);
if (DoCheckOnly) return;
if (DoResponseHeader)
{
if (Debug) fprintf (stdout, "DoResponseHeader\n");
if (cptr > BufferPtr)
fwrite (BufferPtr, cptr-BufferPtr, 1, stdout);
}
if (DoResponseBody)
{
if (Debug) fprintf (stdout, "DoResponseBody\n");
if (DoEscapeHtml)
{
cptr = (char*)CgiLibHtmlEscape (cptr, cnt, NULL, 0);
cnt = strlen (cptr);
fwrite (cptr, cnt, 1, stdout);
}
else
if (cnt)
{
/* write response as-is */
fwrite (cptr, cnt, 1, stdout);
}
}
}
/****************************************************************************/
/*
Fetch a response (directory listing or file) using the FTP protocol.
*/
char* FetchFtpResponse ()
{
BOOL IsDirList,
IsDosFileSystem,
IsTypeAscii,
IsUnixFileSystem,
IsVmsFileSystem,
IsUnknownFileSystem;
int status,
ListenPort,
LocalPort,
IpAddress,
ReadCount,
ReturnLength;
unsigned short DataChannel,
FtpChannel,
ListenChannel;
char ch;
char *cptr, *sptr,
*FileSystemPtr,
*RemotePassPtr;
char Buffer [4096],
CwdFtpDirectory [256],
CwdFtpDirEncoded [256],
FtpDirectory [256],
FtpFile [256],
FtpGreetingEscaped [256],
FtpPwdEscaped [256],
FtpStatEscaped [256],
FtpSystemEscaped [256],
LocalHost [128],
RemotePass [256],
RemoteUser [256],
RemoteUserPass [256],
RemoteUserPassEncoded [256],
RequestPathEncoded [256],
RequestPathEscaped [256],
Scratch [1024];
struct AnIOsb IOsb;
struct sockaddr_in SocketName;
struct {
unsigned long Length;
void *Address;
int *LengthPtr;
} SocketNameItem =
{ sizeof(SocketName), &SocketName, &ReturnLength };
/*********/
/* begin */
/*********/
if (WatchEnabled) WatchThis ("FTP !AZ", FetchUrlPtr);
if (WatchEnabled)
FetchDebugFtp = true;
else
if (!TrnLnm ("FETCH$DBUG_FTP", NULL, 0))
FetchDebugFtp = false;
else
{
FetchDebugFtp = true;
fprintf (stdout,
"HTTP/1.0 200 Success\n\
Content-Type: text/plain\n\
Script-Control: X-content-encoding-gzip=0\n\
\n");
}
CgiContentTypePtr = CgiLibVar ("WWW_CONTENT_TYPE");
if (!CgiContentTypePtr[0])
CgiContentTypePtr = FtpDefaultContentTypePtr;
else
if (!strcmp (CgiContentTypePtr, "x-internal/unknown"))
CgiContentTypePtr = FtpDefaultContentTypePtr;
RemotePassPtr = NULL;
RemoteHost[0] = RemoteUser[0] =
RemoteUserPass[0] = RemoteUserPassEncoded[0] = '\0';
if (strsame (FetchUrlPtr, "/ftp://", 7)) FetchUrlPtr++;
cptr = FetchUrlPtr;
if (strsame (cptr, "ftp://", 6)) cptr += 6;
if (*CgiServerNamePtr)
strcpy (LocalHost, CgiServerNamePtr);
else
strcpy (LocalHost, "localhost");
if (*CgiServerPortPtr)
LocalPort = atoi(CgiServerPortPtr);
else
LocalPort = 21;
if (LocalPort == 21)
strcpy (LocalHostPort, LocalHost);
else
sprintf (LocalHostPort, "%s:%d", LocalHost, LocalPort);
if (Debug) fprintf (stdout, "LocalHostPort |%s|\n", LocalHostPort);
if (*cptr == '/')
{
strcpy (RemoteHost, LocalHost);
RemotePort = LocalPort;
strcpy (RemoteHostPort, LocalHostPort);
}
else
{
/* get optional username[:password], host and optional port */
for (sptr = cptr; *sptr && *sptr != '/' && *sptr != '@'; sptr++);
if (*sptr == '@')
{
sptr = RemoteUser;
while (*cptr && *cptr != '@') *sptr++ = *cptr++;
*sptr = '\0';
if (*cptr) cptr++;
for (sptr = RemoteUser; *sptr && *sptr != ':'; sptr++);
if (*sptr)
{
*sptr++ = '\0';
RemotePassPtr = sptr;
}
sprintf (RemoteUserPass, "%s:%s", RemoteUser, RemotePassPtr);
}
sptr = RemoteHost;
while (*cptr && *cptr != '/' && *cptr != ':')
*sptr++ = *cptr++;
*sptr = '\0';
if (*cptr == ':')
{
cptr++;
RemotePort = atoi(cptr);
if (!RemotePort) RemotePort = 80;
while (*cptr && *cptr != '/') cptr++;
}
else
RemotePort = 21;
if (RemotePort == 21)
strcpy (RemoteHostPort, RemoteHost);
else
sprintf (RemoteHostPort, "%s:%d", RemoteHost, RemotePort);
}
RemoteHostPortLength = strlen(RemoteHostPort);
if (Debug) fprintf (stdout, "|%s|%s|\n", RemoteHostPort, cptr);
if (IsFetchScript && *cptr != '/')
{
/* no path (i.e. "http://remote.host.name") which can break links */
fprintf (stdout,
"HTTP/1.0 302 Moved temporarily\r\n\
Location: %s%s/\r\n\
\r\n",
FetchPrefix, FetchUrlPtr);
return ("302 Redirection!");
}
if (DoMassageLinks)
{
sprintf (FetchPrefix, "%s//%s%s/~",
CgiRequestSchemePtr, LocalHostPort, CgiScriptNamePtr);
sprintf (MassageLinksPath, "%s/~", CgiScriptNamePtr);
}
else
FetchPrefix[0] = MassageLinksPath[0] = '\0';
sptr = RequestPath;
while (*cptr) *sptr++ = *cptr++;
*sptr = '\0';
if (Debug) fprintf (stdout, "|%s|\n", RequestPath);
if (RemoteUserPass[0])
{
sptr = RemoteUserPassEncoded;
cptr = RemoteUserPass;
while (*cptr)
{
switch (*cptr)
{
case ' ' :
*sptr++ = '%'; *sptr++ = '2'; *sptr++ = '0'; cptr++; break;
case '?' :
*sptr++ = '%'; *sptr++ = '3'; *sptr++ = 'f'; cptr++; break;
case '@' :
*sptr++ = '%'; *sptr++ = '4'; *sptr++ = '0'; cptr++; break;
default : *sptr++ = *cptr++;
}
}
*sptr++ = '@';
*sptr = '\0';
}
if (Debug)
fprintf (stdout, "RemoteUserPassEncoded |%s|\n", RemoteUserPassEncoded);
if (DoMassageLinks)
{
sprintf (FetchPrefix, "%s//%s%s/",
CgiRequestSchemePtr, LocalHostPort, CgiScriptNamePtr);
if (Debug) fprintf (stdout, "FetchPrefix |%s|\n", FetchPrefix);
}
else
FetchPrefix[0] = '\0';
IsDirList = IsTypeAscii = false;
CwdFtpDirectory[0] = CwdFtpDirEncoded[0] = FtpDirectory[0] = '\0';
for (cptr = RequestPath; *cptr && *cptr != ';'; cptr++);
if (cptr[-1] == '/') IsDirList = true;
if (*cptr)
*cptr++ = '\0';
else
{
/* no request modifies on path, have a look on the query string */
strcpy (cptr = Scratch, CgiQueryStringPtr);
CgiLibUrlDecode (cptr);
}
while (*cptr)
{
/**************************/
/* URL file type modifier */
/**************************/
if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
if (*cptr == ';') cptr++;
if (!memcmp (cptr, "type=", 5))
{
if (!memcmp (cptr, "type=debug", 10))
{
cptr += 10;
FetchDebugFtp = true;
}
else
if (!memcmp (cptr, "type=dos", 8))
{
cptr += 8;
IsUnixFileSystem = IsUnknownFileSystem = IsVmsFileSystem = false;
IsDosFileSystem = true;
}
else
if (!memcmp (cptr, "type=unix", 9))
{
cptr += 9;
IsDosFileSystem = IsUnknownFileSystem = IsVmsFileSystem = false;
IsUnixFileSystem = true;
}
else
if (!memcmp (cptr, "type=unknown", 12))
{
cptr += 12;
IsDosFileSystem = IsUnixFileSystem = IsVmsFileSystem = false;
IsUnknownFileSystem = true;
}
else
if (!memcmp (cptr, "type=vms", 8))
{
cptr += 8;
IsDosFileSystem = IsUnknownFileSystem = IsUnixFileSystem = false;
IsVmsFileSystem = true;
}
else
if (!memcmp (cptr, "type=a", 6))
{
cptr += 6;
IsTypeAscii = true;
CgiContentTypePtr = "text/plain";
}
else
if (!memcmp (cptr, "type=d", 6))
{
cptr += 6;
IsDirList = true;
/* if not already that way terminate the path with a '/' */
strcat (RequestPath, "/");
}
else
if (!memcmp (cptr, "type=i", 6))
{
cptr += 6;
IsTypeAscii = false;
CgiContentTypePtr = "application/octet-stream";
}
}
else
if (!memcmp (cptr, "cwd=", 4))
{
cptr += 4;
sptr = CwdFtpDirectory;
while (*cptr && *cptr != ';') *sptr++ = *cptr++;
*sptr = '\0';
if (Debug) fprintf (stdout, "CwdFtpDirectory |%s|\n", CwdFtpDirectory);
strcpy (CwdFtpDirEncoded, "cwd=");
CgiLibUrlEncode (CwdFtpDirectory, -1,
CwdFtpDirEncoded+4, sizeof(CwdFtpDirEncoded)-4);
}
else
return ("500 Unknown modifier.");
}
if (IsDirList || !memcmp(CgiContentTypePtr, "text/", 5))
IsTypeAscii = true;
/*********/
/* login */
/*********/
status = OpenConnection (&FtpChannel, RemoteHost, RemotePort, NULL);
if (VMSnok (status)) return (ReplyVmsError (status));
cptr = FtpReply (FtpChannel);
for (sptr = cptr; *sptr && *sptr != '\r' && *sptr != '\n'; sptr++);
*sptr = '\0';
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
CgiLibHtmlEscape (cptr+4, -1, FtpGreetingEscaped,
sizeof(FtpGreetingEscaped));
/* get the IP address of the connected command socket */
if (Debug) fprintf (stdout, "sys$qiow() IO$_SENSEMODE\n");
status = sys$qiow (0, FtpChannel, IO$_SENSEMODE, &IOsb, 0, 0,
0, 0, &SocketNameItem, 0, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSnok (status)) return (ReplyVmsError (status));
memcpy (&IpAddress, &SocketName.sin_addr.s_addr, 4);
/* now we've got the IP address we can build an anonymous password */
if (!RemoteUser[0]) strcpy (RemoteUser, "anonymous");
if (!RemotePassPtr)
{
sprintf (RemotePassPtr = RemotePass, "proxy@%d.%d.%d.%d",
IpAddress & 0x000000ff,
(IpAddress & 0x0000ff00) >> 8,
(IpAddress & 0x00ff0000) >> 16,
/* signed ints, mask off the shifted sign bit */
((IpAddress & 0xff000000) >> 24) & 0xff);
}
cptr = FtpCommand (true, FtpChannel, "USER %s\r\n", RemoteUser);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
if (!memcmp (cptr, "331", 3))
{
cptr = FtpCommand (true, FtpChannel, "PASS %s\r\n", RemotePassPtr);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
}
if (IsTypeAscii)
cptr = FtpCommand (true, FtpChannel, "TYPE A\r\n");
else
cptr = FtpCommand (true, FtpChannel, "TYPE I\r\n");
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
if (RequestPath[0] == '/' && RequestPath[1] == '|')
{
/********************/
/* ad hoc commands */
/*******************/
fprintf (stdout,
"HTTP/1.0 200 Success\n\
Content-Type: text/plain\n\
\n");
strcpy (sptr = Scratch, RequestPath+2);
CgiLibUrlDecode (sptr);
while (*sptr)
{
cptr = sptr;
while (*sptr && *sptr != '|') sptr++;
if (*sptr) *sptr++ = '\0';
cptr = FtpCommand (false, FtpChannel, "%s\r\n", cptr);
if (cptr[0] == '4' || cptr[0] == '5')
{
fprintf (stdout, "%s\n", ReplyVmsError (status));
break;
}
for (;;)
{
status = ReadConnection (FtpChannel, Buffer, sizeof(Buffer),
&ReadCount, true);
if (VMSnok (status))
{
fprintf (stdout, "%s\n", ReplyVmsError (status));
break;
}
fprintf (stdout, "%s", Buffer);
cptr = Buffer;
while (*cptr)
{
if (isdigit(cptr[0]) && isdigit(cptr[1]) &&
isdigit(cptr[2]) && cptr[3] == ' ') break;
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
while (*cptr == '\r' || *cptr == '\n') cptr++;
}
if (isdigit(cptr[0]) && isdigit(cptr[1]) &&
isdigit(cptr[2]) && cptr[3] == ' ') break;
}
}
sys$dassgn (FtpChannel);
return ("200 OK!");
}
/************************************/
/* determine (probable) file system */
/************************************/
cptr = FtpCommand (true, FtpChannel, "SYST\r\n");
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
for (sptr = cptr; *sptr && *sptr != '\r' && *sptr != '\n'; sptr++);
*sptr = '\0';
CgiLibHtmlEscape (cptr+4, -1, FtpSystemEscaped, sizeof(FtpSystemEscaped));
cptr = FtpCommand (true, FtpChannel, "PWD\r\n");
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
IsDosFileSystem = IsUnixFileSystem =
IsUnknownFileSystem = IsVmsFileSystem = false;
if (strchr (cptr, '/'))
{
IsUnixFileSystem = true;
FileSystemPtr = "Unix";
}
else
if (strstr (cptr, ":["))
{
IsVmsFileSystem = true;
FileSystemPtr = "VMS";
}
else
if (strchr (cptr, '\\'))
{
IsDosFileSystem = true;
FileSystemPtr = "DOS";
}
else
{
IsUnknownFileSystem = true;
FileSystemPtr = "unknown";
}
if (WatchEnabled) WatchThis ("FTP FILE-SYSTEM !AZ", FileSystemPtr);
if (RequestPath[1] && IsVmsFileSystem)
{
sptr = FtpDirectory;
if (strchr (RequestPath+1, '/'))
{
*sptr++ = '[';
*sptr++ = '.';
}
for (cptr = RequestPath+1; *cptr; *sptr++ = *cptr++);
*sptr = '\0';
/* turn any final '/' into a directory delimiting ']' */
while (sptr > FtpDirectory && *sptr != '/')
{
sptr--;
cptr--;
}
if (*sptr == '/')
{
*sptr++ = ']';
cptr++;
}
*sptr = '\0';
/* turn any intermediate '/' into directory separating '.' */
for (sptr = FtpDirectory; *sptr; sptr++)
if (*sptr == '/') *sptr = '.';
/* get any file component */
sptr = FtpFile;
while (*cptr) *sptr++ = *cptr++;
*sptr = '\0';
}
else
if (RequestPath[1])
{
sptr = FtpDirectory;
*sptr++ = '.';
for (cptr = RequestPath; *cptr; *sptr++ = *cptr++);
*sptr = '\0';
while (sptr > FtpDirectory && *sptr != '/')
{
sptr--;
cptr--;
}
if (*sptr == '/')
{
sptr++;
cptr++;
}
*sptr = '\0';
/* get any file component */
sptr = FtpFile;
while (*cptr) *sptr++ = *cptr++;
*sptr = '\0';
if (IsDosFileSystem)
{
/* for DOS, turn each forward slash into a back-slash */
for (sptr = FtpDirectory; *sptr; sptr++)
if (*sptr == '/') *sptr = '\\';
}
}
else
strcpy (FtpFile, RequestPath+1);
if (Debug) fprintf (stdout, "|%s|%s|\n", FtpDirectory, FtpFile);
if (CwdFtpDirectory[0])
{
/*******************/
/* ;cwd= directory */
/*******************/
cptr = FtpCommand (true, FtpChannel, "CWD %s\r\n", CwdFtpDirectory);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (ListenChannel);
sys$dassgn (FtpChannel);
return (cptr);
}
}
if (FtpDirectory[0])
{
/*********************/
/* request directory */
/*********************/
cptr = FtpCommand (true, FtpChannel, "CWD %s\r\n", FtpDirectory);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (ListenChannel);
sys$dassgn (FtpChannel);
return (cptr);
}
}
if (IsDirList)
{
/* get the directory location for inclusion in the directory listing */
cptr = FtpCommand (true, FtpChannel, "PWD\r\n");
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
for (sptr = cptr; *sptr && *sptr != '\r' && *sptr != '\n'; sptr++)
if (*sptr == '\"') *sptr = '\'';
*sptr = '\0';
CgiLibHtmlEscape (cptr+4, -1, FtpPwdEscaped, sizeof(FtpPwdEscaped));
}
/**********************/
/* open listen socket */
/**********************/
/* assign a channel to the internet template device */
if (VMSnok (status = sys$assign (&InetDeviceDsc, &ListenChannel, 0, 0)))
return (ReplyVmsError (status));
SocketNameItem.Length = sizeof(SocketName);
SocketNameItem.Address = &SocketName;
status = sys$qiow (0, ListenChannel, IO$_SETMODE, &IOsb, 0, 0,
&TcpSocket, 0, 0, 1, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSnok (status))
{
sys$dassgn (FtpChannel);
return (ReplyVmsError (status));
}
/* get the port number listening */
if (Debug) fprintf (stdout, "sys$qiow() IO$_SENSEMODE\n");
status = sys$qiow (0, ListenChannel, IO$_SENSEMODE, &IOsb, 0, 0,
0, 0, &SocketNameItem, 0, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSnok (status))
{
sys$dassgn (FtpChannel);
sys$dassgn (ListenChannel);
return (ReplyVmsError (status));
}
ListenPort = (int)SocketName.sin_port;
/*****************************/
/* tell FTP server data port */
/*****************************/
cptr = FtpCommand (true, FtpChannel, "PORT %d,%d,%d,%d,%d,%d\r\n",
IpAddress & 0x000000ff,
(IpAddress & 0x0000ff00) >> 8,
(IpAddress & 0x00ff0000) >> 16,
/* signed ints, mask off the shifted sign bit */
((IpAddress & 0xff000000) >> 24) & 0xff,
(ListenPort & 0x000000ff),
(ListenPort & 0x0000ff00) >> 8);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
if (IsDirList)
{
/*********************/
/* directory listing */
/*********************/
if (!FtpIconDirImg[0] && IconPathPtr[0])
{
sprintf (FtpIconDirImg,
" ",
CgiRequestSchemePtr, CgiServerNamePtr,
CgiServerPortPtr, IconPathPtr);
sprintf (FtpIconFileImg,
" ",
CgiRequestSchemePtr, CgiServerNamePtr,
CgiServerPortPtr, IconPathPtr);
}
if (IsVmsFileSystem)
cptr = FtpCommand (true, FtpChannel, "LIST *.*;0\r\n");
else
if (IsUnixFileSystem || IsDosFileSystem)
cptr = FtpCommand (true, FtpChannel, "LIST\r\n");
else
cptr = FtpCommand (true, FtpChannel, "NLST\r\n");
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (ListenChannel);
sys$dassgn (FtpChannel);
return (cptr);
}
/* it has been observed that responses can span commands with LIST */
while (*cptr && *cptr != '\n') cptr++;
if (*cptr) cptr++;
/*************************/
/* accept on data socket */
/*************************/
/* assign a channel to the internet template device */
if (VMSnok (status = sys$assign (&InetDeviceDsc, &DataChannel, 0, 0)))
return (ReplyVmsError (status));
status = sys$qiow (0, ListenChannel, IO$_ACCESS|IO$M_ACCEPT, &IOsb, 0, 0,
0, 0, &SocketNameItem, &DataChannel, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSnok (status))
{
sys$dassgn (ListenChannel);
sys$dassgn (FtpChannel);
return (ReplyVmsError (status));
}
/********************/
/* read data stream */
/********************/
if (BufferChunk <= 0) BufferChunk = DEFAULT_BUFFER_CHUNK;
/* with CGIplus 'BufferPtr'/'BufferSize' may still have memory */
BufferCount = 0;
BufferRemaining = BufferSize;
BufferCurrentPtr = BufferPtr;
for (;;)
{
if (BufferCount == BufferSize)
{
BufferSize += (BufferRemaining = (BufferChunk << 10));
if (!(BufferPtr = realloc (BufferPtr, BufferSize+1)))
exit (vaxc$errno);
BufferCurrentPtr = BufferPtr + BufferSize - BufferRemaining;
}
ReadCount = BufferRemaining <= 32767 ? BufferRemaining : 32767,
status = ReadConnection (DataChannel, BufferCurrentPtr, ReadCount,
&ReadCount, false);
if (VMSnok (status)) break;
BufferCount += ReadCount;
BufferCurrentPtr += ReadCount;
BufferRemaining -= ReadCount;
if (Debug)
fprintf (stdout, "Remaining: %d Count: %d\n",
BufferRemaining, BufferCount);
}
BufferPtr[BufferCount] = '\0';
/** if (Debug) fprintf (stdout, "|%s|\n", BufferPtr); **/
if (FetchDebugFtp) fprintf (stdout, "->|%s|\n", BufferPtr);
sys$dassgn (DataChannel);
sys$dassgn (ListenChannel);
/*******************/
/* final FTP reply */
/*******************/
/* it has been observed that responses can span commands with LIST */
if (!*cptr) cptr = FtpReply (FtpChannel);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
if (DoCheckOnly)
{
FtpCommand (true, FtpChannel, "QUIT\r\n");
sys$dassgn (FtpChannel);
return ("200 OK!");
}
/*****************/
/* generate page */
/*****************/
CgiLibUrlEncode (RequestPath, -1,
RequestPathEncoded, sizeof(RequestPathEncoded));
CgiLibHtmlEscape (RequestPath, -1,
RequestPathEscaped, sizeof(RequestPathEscaped));
fprintf (stdout,
"HTTP/1.0 200 Success\n\
Server: %s\n\
Content-Type: text/html\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
Directory of %s\n\
\n\
\n\
%s %s
\n\
%s\
",
CgiServerSoftwarePtr, SoftwareId,
FtpGreetingEscaped, FtpSystemEscaped, FtpPwdEscaped, FileSystemPtr,
RequestPathEscaped, FtpCurrentDirectory, RequestPathEscaped,
IsUnknownFileSystem ?
"Note: FTP server type not recognized!\n\n" : "");
if (IsVmsFileSystem)
ProcessFtpVmsList (RequestPathEncoded,
RemoteUserPassEncoded,
CwdFtpDirEncoded);
else
if (IsUnixFileSystem)
ProcessFtpUnixList (RequestPathEncoded,
RemoteUserPassEncoded,
CwdFtpDirEncoded);
else
if (IsDosFileSystem)
ProcessFtpDosList (RequestPathEncoded,
RemoteUserPassEncoded,
CwdFtpDirEncoded);
else
ProcessFtpUnknownList (RequestPathEncoded,
RemoteUserPassEncoded,
CwdFtpDirEncoded);
fprintf (stdout,
"
\n\
\n\
\n");
}
else
{
/*****************/
/* file transfer */
/*****************/
cptr = FtpCommand (true, FtpChannel, "RETR %s\r\n", FtpFile);
if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (ListenChannel);
sys$dassgn (FtpChannel);
return (cptr);
}
/* it has been observed that responses can span commands with RETR */
while (*cptr && *cptr != '\n') cptr++;
if (*cptr) cptr++;
/*************************/
/* accept on data socket */
/*************************/
/* assign a channel to the internet template device */
if (VMSnok (status = sys$assign (&InetDeviceDsc, &DataChannel, 0, 0)))
return (ReplyVmsError (status));
status = sys$qiow (0, ListenChannel, IO$_ACCESS|IO$M_ACCEPT, &IOsb, 0, 0,
0, 0, &SocketNameItem, &DataChannel, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSnok (status))
{
sys$dassgn (ListenChannel);
sys$dassgn (FtpChannel);
return (ReplyVmsError (status));
}
/********************/
/* read data stream */
/********************/
if (*OutputPtr)
{
/* FTP response is being output to a file */
if (!(stdout = freopen (OutputPtr, "w", stdout)))
exit (vaxc$errno);
}
else
{
fprintf (stdout,
"HTTP/1.0 200 Success\n\
Server: %s\n\
Content-Type: %s\n\
\n",
CgiServerSoftwarePtr, CgiContentTypePtr);
}
for (;;)
{
status = ReadConnection (DataChannel, Buffer, sizeof(Buffer),
&ReadCount, false);
if (VMSnok (status)) break;
if (!DoCheckOnly) fwrite (Buffer, ReadCount, 1, stdout);
}
sys$dassgn (DataChannel);
sys$dassgn (ListenChannel);
/* was being written to a file, reopen to */
if (*OutputPtr)
{
if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout)))
exit (vaxc$errno);
}
/* it has been observed that responses can span commands with RETR */
if (!*cptr) cptr = FtpReply (FtpChannel);
if (Debug) fprintf (stdout, "cptr |%s|\n", cptr);
if (cptr[0] == '4' || cptr[0] == '5')
{
sys$dassgn (FtpChannel);
return (cptr);
}
}
/********************/
/* close connection */
/********************/
if (status == SS$_LINKDISCON) status = SS$_NORMAL;
FtpCommand (true, FtpChannel, "QUIT\r\n");
sys$dassgn (FtpChannel);
if (VMSnok (status)) return (ReplyVmsError (status));
return ("200 OK!");
}
/****************************************************************************/
/*
Variable length argument list. Send the printf()ed command to the FTP server,
optionally reading the reply.
*/
char* FtpCommand
(
BOOL GetReply,
unsigned short Channel,
char *FormatString,
...
)
{
int status,
argcnt,
Length;
char *cptr;
char Buffer [1024];
va_list argptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "FtpCommand() |%s|\n", FormatString);
va_count (argcnt);
va_start (argptr, FormatString);
Length = vsprintf (Buffer, FormatString, argptr);
if (Debug) fprintf (stdout, "%d |%s|\n", Length, Buffer);
if (FetchDebugFtp)
{
for (cptr = Buffer + Length;
cptr > Buffer && (!*cptr || *cptr == '\r' || *cptr == '\n');
cptr--);
if (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
fprintf (stdout, "->|%*.*s|\n", cptr-Buffer, cptr-Buffer, Buffer);
}
status = WriteConnection (Channel, Buffer, Length);
if (VMSnok (status)) return (ReplyVmsError (status));
if (GetReply) return (FtpReply (Channel));
return ("200 OK!");
}
/****************************************************************************/
/*
Read and return a single line response from the FTP server (With multi-line
responses it returns only the last line).
*/
char* FtpReply (unsigned short Channel)
{
static char Message [256] = "599 ",
Buffer [1024];
static $DESCRIPTOR (MessageDsc, Message);
int status,
BufferRemaining,
ReadCount;
short int Length;
char *cptr, *sptr,
*BufferPtr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "FtpReply()\n");
BufferPtr = Buffer;
BufferRemaining = sizeof(Buffer)-1;
for (;;)
{
status = ReadConnection (Channel, BufferPtr, BufferRemaining,
&ReadCount, true);
if (VMSnok (status)) return (ReplyVmsError (status));
for (cptr = BufferPtr; *cptr && *cptr != '\r' && *cptr != '\n'; cptr++);
if (*cptr != '\r' && *cptr != '\n')
{
/* all lines must be terminated with carriage control, get rest */
BufferPtr += ReadCount;
BufferRemaining -= ReadCount;
continue;
}
if (Debug) fprintf (stdout, "|%s|\n", Buffer);
if (FetchDebugFtp) fprintf (stdout, "<-|%s|\n", Buffer);
cptr = sptr = Buffer;
while (*cptr)
{
sptr = cptr;
if (isdigit(cptr[0]) && isdigit(cptr[1]) &&
isdigit(cptr[2]) && cptr[3] == ' ') break;
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
while (*cptr == '\r' || *cptr == '\n') cptr++;
}
if (isdigit(cptr[0]) && isdigit(cptr[1]) &&
isdigit(cptr[2]) && cptr[3] == ' ') return (sptr);
}
}
/****************************************************************************/
/*
Convert a VMS status value into a pseudo-reply-message containing the
corresponding VMS error message. For example,
"599 %X0000028C Remote node is unknown". The hexadecimal status value can be
converted back and used as an exit status.
*/
char* ReplyVmsError (int StatusValue)
{
static $DESCRIPTOR (StringFaoDsc, "599 %X!XL !AZ.");
static char Message [256],
String [256];
static $DESCRIPTOR (MessageDsc, Message);
static $DESCRIPTOR (StringDsc, String);
int status;
short int Length;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ReplyVmsError() %%X%08.08X\n", StatusValue);
status = sys$getmsg (StatusValue, &Length, &MessageDsc, 1, 0);
if (VMSok (status))
{
Message[Length] = '\0';
Message[0] = toupper(Message[0]);
}
else
strcpy (Message, ""sys$getmsg() failed"");
status = sys$fao (&StringFaoDsc, &Length, &StringDsc, StatusValue, Message);
if (VMSok (status))
String[Length] = '\0';
else
strcpy (String, "599 %X00000002 "sys$fao() failed"");
if (Debug) fprintf (stdout, "|%s|\n", String);
return (String);
}
/****************************************************************************/
/*
HTML format a VMS FTP server directory listing.
Expected format: "file-name size-blocks date_time any-thing-else..."
*/
ProcessFtpVmsList
(
char *RequestPathEncoded,
char *RemoteUserPassEncoded,
char *CwdFtpDirEncoded
)
{
BOOL IsDirectory;
int DirectoryCount,
FileCount;
char *cptr, *sptr;
char FileDate [256],
FileNameEncoded [256],
FileNameEscaped [256],
FileName [256],
FileSize [256];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessFtpVmsList()\n");
DirectoryCount = FileCount = 0;
cptr = BufferPtr;
while (*cptr)
{
/* skip leading white-space, blank lines, etc. */
while (*cptr && *cptr == ' ') cptr++;
if (!cptr) break;
if (*cptr == '\r' || *cptr == '\n')
{
while (*cptr == '\r' || *cptr == '\n') cptr++;
continue;
}
sptr = FileName;
while (*cptr && *cptr != ' ' && *cptr != ';' &&
*cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
if (*cptr == ';')
{
/* skip version number in file specification */
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
cptr++;
}
/* absorb white-space between name and size */
while (*cptr == ' ') cptr++;
sptr = FileSize;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
/* absorb white-space between size and date */
while (*cptr == ' ') cptr++;
sptr = FileDate;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
while (*cptr == ' ') *sptr++ = *cptr++;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
/* skip to start of next line */
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
while (*cptr == '\r' || *cptr == '\n') cptr++;
if (FileName[0] && !FileSize[0] && !FileDate[0] &&
strlen(FileName) > 16)
{
/***************************************************/
/* file name too long, distibuted across two lines */
/***************************************************/
/* skip leading white-space */
while (*cptr && *cptr == ' ') cptr++;
if (!cptr) break;
sptr = FileSize;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
/* absorb white-space between size and date */
while (*cptr == ' ') cptr++;
sptr = FileDate;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
while (*cptr == ' ') *sptr++ = *cptr++;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
}
/* if all components not present then just ignore */
if (!FileName[0] || !FileSize[0] || !FileDate[0]) continue;
/* try to eliminate other lines ... file size really look like one? */
for (sptr = FileSize; isdigit(*sptr) || *sptr == '/'; sptr++);
if (*sptr) continue;
/* try to eliminate other lines ... file date really look like one? */
sptr = FileDate;
if (!((isdigit(sptr[0]) && isdigit(sptr[1]) && sptr[2] == '-') ||
(isdigit(sptr[0]) && sptr[1] == '-')))
continue;
for (sptr = FileName; *sptr; sptr++);
while (sptr > FileName && *sptr != '.') sptr--;
if (*(unsigned long*)sptr == '.DIR')
{
/* terminate to eliminate the ".DIR" */
*sptr++ = '/';
*sptr = '\0';
IsDirectory = true;
sptr = FtpIconDirImg;
DirectoryCount++;
}
else
{
IsDirectory = false;
sptr = FtpIconFileImg;
FileCount++;
}
CgiLibUrlEncode (FileName, -1, FileNameEncoded, sizeof(FileNameEncoded));
CgiLibHtmlEscape (FileName, -1, FileNameEscaped, sizeof(FileNameEscaped));
fprintf (stdout,
" %s%s%*s ",
sptr, MassageLinksPath,
RemoteUserPassEncoded, RemoteHostPort, RequestPathEncoded,
FileNameEncoded, CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
FileNameEscaped, 30-strlen(FileName), "");
if (IsDirectory)
{
for (sptr = FileNameEncoded; *sptr; sptr++);
*--sptr = '\0';
}
fprintf (stdout,
"\
a\
i\
d\
",
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded);
if (IsDirectory)
fprintf (stdout, "\n");
else
fprintf (stdout, " %14s %20s\n", FileSize, FileDate);
}
fprintf (stdout, FtpFilesDirectories,
FileCount, DirectoryCount,
FileCount || DirectoryCount ? FtpGetAs : "");
}
/****************************************************************************/
/*
HTML format a "Unix" FTP server directory listing.
Expected format: "mode links owner group size time_time_time file-name"
*/
ProcessFtpUnixList
(
char *RequestPathEncoded,
char *RemoteUserPassEncoded,
char *CwdFtpDirEncoded
)
{
#define MAX_FIELDS 16
static char *MonthName [] = { "Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec" };
BOOL IsDirectory;
int idx,
DirectoryCount,
FieldCount,
FileCount,
MonthField;
char *cptr, *sptr;
char *FieldPtr [MAX_FIELDS];
char FileName [256],
FileNameEncoded [256],
FileNameEscaped [256],
LinkTarget [256];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessFtpUnixList()\n");
DirectoryCount = FileCount = 0;
cptr = BufferPtr;
while (*cptr)
{
/* skip leading white-space, blank lines, etc. */
while (*cptr == ' ') cptr++;
if (!cptr) break;
if (*cptr == '\r' || *cptr == '\n')
{
while (*cptr == '\r' || *cptr == '\n') cptr++;
continue;
}
FieldCount = 0;
while (*cptr && *cptr != '\r' && *cptr != '\n')
{
if (FieldCount >= MAX_FIELDS) continue;
FieldPtr[FieldCount++] = cptr;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n') cptr++;
/* terminate that field */
if (*cptr) *cptr++ = '\0';
while (*cptr == ' ') cptr++;
}
if (Debug)
for (idx = 1; idx < FieldCount; idx++)
fprintf (stdout, "%d |%s|\n", idx, FieldPtr[idx]);
/* skip to start of next line */
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
while (*cptr == '\r' || *cptr == '\n') cptr++;
/* won't make any sense at all unless there are at least six fields */
if (FieldCount < 6) continue;
/* find field containing month name */
for (MonthField = 1; MonthField < FieldCount; MonthField++)
{
for (idx = 0; idx < 12; idx++)
if (strsame (FieldPtr[MonthField], MonthName[idx], 3))
break;
if (idx < 12) break;
}
/* doesn't make sense if it doesn't contain a month name somewhere */
if (MonthField >= FieldCount) continue;
strcpy (FileName, FieldPtr[MonthField+3]);
/* dot and double-dot directories are ignored */
if (FileName[0] == '.' && !FileName[1]) continue;
if (FileName[0] == '.' && FileName[1] == '.' && !FileName[2]) continue;
if (FieldPtr[0][0] == 'd')
{
IsDirectory = true;
strcat (FileName, "/");
sptr = FtpIconDirImg;
DirectoryCount++;
}
else
if (FieldPtr[0][0] == 'l')
{
for (sptr = FieldPtr[MonthField+5]; *sptr; sptr++);
if (sptr > FieldPtr[MonthField+5] && sptr[-1] == '/')
{
IsDirectory = true;
strcat (FileName, "/");
sptr = FtpIconDirImg;
DirectoryCount++;
}
else
{
IsDirectory = false;
sptr = FtpIconFileImg;
FileCount++;
}
}
else
{
IsDirectory = false;
sptr = FtpIconFileImg;
FileCount++;
}
CgiLibUrlEncode (FileName, -1, FileNameEncoded, sizeof(FileNameEncoded));
CgiLibHtmlEscape (FileName, -1, FileNameEscaped, sizeof(FileNameEscaped));
fprintf (stdout,
" %s%s%*s ",
sptr, MassageLinksPath,
RemoteUserPassEncoded, RemoteHostPort, RequestPathEncoded,
FileNameEncoded, CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
FileNameEscaped, 30-strlen(FileName), "");
if (IsDirectory)
{
for (sptr = FileNameEncoded; *sptr; sptr++);
*--sptr = '\0';
}
fprintf (stdout,
"\
a\
i\
d\
",
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded);
if (IsDirectory)
fprintf (stdout, "\n");
else
fprintf (stdout, " %10s %3.3s %2.2s %4.4s\n",
FieldPtr[MonthField-1], FieldPtr[MonthField],
FieldPtr[MonthField+1], FieldPtr[MonthField+2]);
}
fprintf (stdout, FtpFilesDirectories,
FileCount, DirectoryCount,
FileCount || DirectoryCount ? FtpGetAs : "");
}
/****************************************************************************/
/*
HTML format a "DOS" FTP server directory listing.
Expected format: "mm-dd-yy_hh:mm |size file-name"
*/
ProcessFtpDosList
(
char *RequestPathEncoded,
char *RemoteUserPassEncoded,
char *CwdFtpDirEncoded
)
{
BOOL IsDirectory;
int DirectoryCount,
FileCount;
char *cptr, *sptr;
char FileDate [256],
FileName [256],
FileNameEncoded [256],
FileNameEscaped [256],
FileSize [256];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessFtpDosList()\n");
DirectoryCount = FileCount = 0;
cptr = BufferPtr;
while (*cptr)
{
/* skip leading white-space, blank lines, etc. */
while (*cptr && *cptr == ' ') cptr++;
if (!cptr) break;
if (*cptr == '\r' || *cptr == '\n')
{
while (*cptr == '\r' || *cptr == '\n') cptr++;
continue;
}
IsDirectory = false;
/* get the two fields comprising the date */
sptr = FileDate;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
while (*cptr == ' ') *sptr++ = *cptr++;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
/* absorb white-space */
while (*cptr == ' ') cptr++;
if (*cptr == '<')
{
/* it's a directory */
if (!memcmp (cptr, "", 5)) IsDirectory = true;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n') cptr++;
FileSize[0] = '\0';
}
else
{
/* file size */
sptr = FileSize;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
}
/* absorb white space between or file size and file name */
while (*cptr == ' ') cptr++;
sptr = FileName;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
/* skip to start of next line */
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
while (*cptr == '\r' || *cptr == '\n') cptr++;
/* if all components not present then just ignore */
if (!FileName[0]) continue;
if (!IsDirectory && !FileSize[0]) continue;
/* dot and double-dot directories are ignored */
if (FileName[0] == '.' && !FileName[1]) continue;
if (FileName[0] == '.' && FileName[1] == '.' && !FileName[2]) continue;
CgiLibUrlEncode (FileName, -1, FileNameEncoded, sizeof(FileNameEncoded));
CgiLibHtmlEscape (FileName, -1, FileNameEscaped, sizeof(FileNameEscaped));
if (IsDirectory)
{
IsDirectory = true;
strcat (FileName, "/");
sptr = FtpIconDirImg;
DirectoryCount++;
}
else
{
IsDirectory = false;
sptr = FtpIconFileImg;
FileCount++;
}
CgiLibUrlEncode (FileName, -1, FileNameEncoded, sizeof(FileNameEncoded));
CgiLibHtmlEscape (FileName, -1, FileNameEscaped, sizeof(FileNameEscaped));
fprintf (stdout,
" %s%s%*s ",
sptr, MassageLinksPath,
RemoteUserPassEncoded, RemoteHostPort, RequestPathEncoded,
FileNameEncoded, CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
FileNameEscaped, 30-strlen(FileName), "");
if (IsDirectory)
{
for (sptr = FileNameEncoded; *sptr; sptr++);
*--sptr = '\0';
}
fprintf (stdout,
"\
a\
i\
d\
",
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded,
CwdFtpDirEncoded[0] ? ";" : "", CwdFtpDirEncoded);
if (IsDirectory)
fprintf (stdout, "\n");
else
fprintf (stdout, " %10s %s\n", FileSize, FileDate);
}
fprintf (stdout, FtpFilesDirectories,
FileCount, DirectoryCount,
FileCount || DirectoryCount ? FtpGetAs : "");
}
/****************************************************************************/
/*
HTML format an unknown format FTP server directory listing. Now this was done
as a "NLST" and so should contain one name per lines, that's all!
*/
ProcessFtpUnknownList
(
char *RequestPathEncoded,
char *RemoteUserPassEncoded,
char *CwdFtpDirEncoded
)
{
int FileCount;
char *cptr, *sptr;
char FileName [256],
FileNameEncoded [256],
FileNameEscaped [256];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "ProcessFtpUnknownList()\n");
FileCount = 0;
cptr = BufferPtr;
while (*cptr)
{
/* skip leading white-space, blank lines, etc. */
while (*cptr && *cptr == ' ') cptr++;
if (!cptr) break;
if (*cptr == '\r' || *cptr == '\n')
{
while (*cptr == '\r' || *cptr == '\n') cptr++;
continue;
}
sptr = FileName;
while (*cptr && *cptr != ' ' && *cptr != '\r' && *cptr != '\n')
*sptr++ = *cptr++;
*sptr = '\0';
/* skip to start of next line */
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
while (*cptr == '\r' || *cptr == '\n') cptr++;
if (!FileName[0]) continue;
FileCount++;
CgiLibUrlEncode (FileName, -1, FileNameEncoded, sizeof(FileNameEncoded));
CgiLibHtmlEscape (FileName, -1, FileNameEscaped, sizeof(FileNameEscaped));
fprintf (stdout,
" %s%s%*s \
\
a\
i\
d\
\n",
FtpIconFileImg, MassageLinksPath,
RemoteUserPassEncoded, RemoteHostPort, RequestPathEncoded,
FileNameEncoded, FileNameEscaped, 30-strlen(FileName), "",
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded, CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded, CwdFtpDirEncoded,
MassageLinksPath, RemoteUserPassEncoded, RemoteHostPort,
RequestPathEncoded, FileNameEncoded, CwdFtpDirEncoded);
}
fprintf (stdout, FtpFilesDirectories,
FileCount, 0,
FileCount ? FtpGetAs : "");
}
/****************************************************************************/
/*
Open a "socket" connect to the specific host name and port. If 'IpAddressPtr'
is not NULL then set where it points to the 32 IP address of the remote system.
*/
OpenConnection
(
unsigned short *ChannelPtr,
char *RemoteName,
int RemotePort,
int *IpAddressPtr
)
{
int status,
IpAddress;
unsigned short Channel;
char *cptr;
struct AnIOsb IOsb;
struct sockaddr_in SocketName;
struct hostent *HostEntryPtr;
struct {
unsigned long Length;
void *Address;
int *LengthPtr;
} SocketNameItem =
{ sizeof(SocketName), &SocketName, 0 };
/*********/
/* begin */
/*********/
if (WatchEnabled)
WatchThis ("TCPIP CONNECT !AZ:!UL", RemoteName, RemotePort);
if (isdigit((cptr = RemoteName)[0]))
while (*cptr && (isdigit(*cptr) || *cptr == '.')) cptr++;
if (!*cptr)
{
/*************************/
/* address "131.185.2.4" */
/*************************/
if ((IpAddress = inet_addr (RemoteName)) == -1)
return (vaxc$errno);
}
else
{
/***************************/
/* address "the.host.name" */
/***************************/
if (!(HostEntryPtr = gethostbyname (RemoteName)))
{
if (vaxc$errno == RMS$_RNF) return (SS$_NOSUCHNODE);
if (vaxc$errno == SS$_ENDOFFILE) return (SS$_NOSUCHNODE);
return (vaxc$errno);
}
memcpy (&IpAddress, HostEntryPtr->h_addr, 4);
}
/* assign a channel to the internet template device */
if (VMSnok (status = sys$assign (&InetDeviceDsc, &Channel, 0, 0)))
return (status);
/* make the channel a TCP, connection-oriented socket */
status = sys$qiow (0, Channel, IO$_SETMODE, &IOsb, 0, 0,
&TcpSocket, 0, 0, 0, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb.Status %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSnok (status)) return (status);
SocketName.sin_family = AF_INET;
SocketName.sin_port = htons (RemotePort);
memcpy (&SocketName.sin_addr.s_addr, &IpAddress, 4);
if (IpAddressPtr) memcpy (IpAddressPtr, &IpAddress, 4);
status = sys$qiow (0, Channel, IO$_ACCESS, &IOsb, 0, 0,
0, 0, &SocketNameItem, 0, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb.Status %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSok (status))
*ChannelPtr = Channel;
else
*ChannelPtr = 0;
return (status);
}
/****************************************************************************/
/*
Read from the specific "socket" into the the supplied buffer. If 'AsText' is
true the allow for and supply a terminating null character. If 'ReadCountPtr'
is not NULL then set it's location to the number of bytes read.
*/
int ReadConnection
(
unsigned short Channel,
char *DataBuffer,
int SizeOfDataBuffer,
int *ReadCountPtr,
BOOL AsText
)
{
int status;
struct AnIOsb IOsb;
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "ReadConnection() %d %d\n", SizeOfDataBuffer, AsText);
if (AsText) SizeOfDataBuffer--;
status = sys$qiow (0, Channel, IO$_READVBLK, &IOsb, 0, 0,
DataBuffer, SizeOfDataBuffer, 0, 0, 0, 0);
if (Debug)
fprintf (stdout,
"sys$qiow() status %%X%08.08X IOsb.Status %%X%08.08X %d bytes\n",
status, IOsb.Status, IOsb.Count);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (VMSok (status))
{
if (AsText)
{
DataBuffer[IOsb.Count] = '\0';
if (Debug) fprintf (stdout, "|%s|\n", DataBuffer);
}
if (WatchEnabled)
{
WatchThis ("TCPIP READ !UL", IOsb.Count);
WatchDump (DataBuffer, IOsb.Count);
}
if (ReadCountPtr) *ReadCountPtr = IOsb.Count;
}
else
{
if (WatchEnabled) WatchThis ("TCPIP READ %X!8XL", status);
if (ReadCountPtr) *ReadCountPtr = 0;
}
return (status);
}
/****************************************************************************/
/*
Write the supplied data to the specific "socket". If 'DataLength' is -1 then
consider the data to be a null-terminated string and get it's length using
strlen().
*/
int WriteConnection
(
unsigned short Channel,
char *DataPtr,
int DataLength
)
{
int status;
struct AnIOsb IOsb;
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "WriteConnection() %d |%s|\n", DataLength, DataPtr);
if (DataLength == -1) DataLength = strlen(DataPtr);
if (WatchEnabled)
{
WatchThis ("TCPIP WRITE !UL", DataLength);
WatchDump (DataPtr, DataLength);
}
status = sys$qiow (0, Channel, IO$_WRITEVBLK, &IOsb, 0, 0,
DataPtr, DataLength, 0, 0, 0, 0);
if (Debug)
fprintf (stdout, "sys$qiow() %%X%08.08X IOsb.Status %%X%08.08X\n",
status, IOsb.Status);
if (VMSok (status) && VMSnok (IOsb.Status)) status = IOsb.Status;
if (WatchEnabled && VMSnok(status))
WatchThis ("TCPIP WRITE %X!8XL", status);
return (status);
}
/****************************************************************************/
/*
Script is being used as a quasi-proxy. Examine the response header. If not
"text/html" content type just return it directly to the client. If HTML then
parse the body looking for things that look like HTTP URL links. When found
either 1) prefix the link with the quasi-proxy server host name (and port if
not 80) and the quasi-proxy script name, or 2) convert it from an absolute to
relative link for reverse proxy purposes.
*/
MassageLinks ()
{
#define FLUSH_TO_CPTR { \
\
if (cptr > BufferCurrentPtr) \
{ \
ch = *cptr; \
*cptr = '\0'; \
fputs (BufferCurrentPtr, stdout); \
*cptr = ch; \
} \
}
BOOL FullUrl,
NextPlease;
char ch;
char *cptr, *hptr, *sptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "MassageLinks()\n");
cptr = BufferCurrentPtr = BufferPtr;
cptr[BufferCount] = '\0';
/* when massaging let's limit ourselves to HTTP/1.0 responses */
if (!memcmp (cptr, "HTTP/1.1", 8)) memcpy (cptr, "HTTP/1.0", 8);
while (*cptr)
{
if (toupper (cptr[0]) == 'C' && tolower(cptr[1]) == 'o' &&
strsame (cptr, "Content-Type:", 13))
{
sptr = cptr + 13;
if (Debug) fprintf (stdout, "|%13.13s|\n", cptr);
while (*sptr && isspace(*sptr)) *sptr++;
if (Debug) fprintf (stdout, "|%20.20s|\n", sptr);
/* if not HTML, just output original data */
if (!strsame (sptr, "text/html", 9))
{
fwrite (BufferPtr, BufferCount, 1, stdout);
return;
}
}
if (toupper(cptr[0]) == 'C' && strsame (cptr, "Content-Length:", 15))
{
/* as we're intending to alter the content disable the length field */
memcpy (cptr, "C_ntent-L_ngth:", 15);
}
else
if (toupper(cptr[0]) == 'S' && strsame (cptr, "Set-Cookie:", 11))
{
/* certain components will need massaging to reflect the proxy */
cptr += 11;
while (*cptr && *cptr != '\r' && *cptr != '\n')
{
if (tolower(*cptr) == 'd' && strsame (cptr, "domain=", 7))
{
/* make any cookie domain reflect the reverse-proxy server */
cptr += 7;
FLUSH_TO_CPTR
while (*cptr && *cptr != ';' && *cptr != ',' && !isspace(*cptr))
cptr++;
BufferCurrentPtr = cptr;
fputs (CgiServerNamePtr, stdout);
}
else
if (tolower(*cptr) == 'p' && strsame (cptr, "path=", 5) &&
ReversePathPtr[0])
{
/* prepend the path discriminator */
cptr += 5;
FLUSH_TO_CPTR
BufferCurrentPtr = cptr;
fputs (ReversePathPtr, stdout);
}
else
cptr++;
}
}
else
if ((toupper (cptr[0]) == 'L' &&
strsame (cptr, "Location:", 9)) ||
(toupper (cptr[0]) == 'C' &&
strsame (cptr, "Content-Location:", 17)))
{
while (*cptr && *cptr != ' ') cptr++;
if (*cptr == ' ') cptr++;
FLUSH_TO_CPTR
if (ReverseProxyPtr)
{
hptr = cptr + 7;
if (!strsame (cptr, "http://", 7))
fprintf (stdout, "%s", ReversePathPtr);
else
if (ReverseProxyPtr[0] == '*')
{
/* modify all full URLs */
cptr = hptr;
while (*cptr && *cptr != '/' &&
(isalnum(*cptr) || *cptr == '.' ||
*cptr == '-' || *cptr == '_')) cptr++;
fprintf (stdout, "%s%s",
ReverseProxyPrefix, *cptr == '/' ? "" : "/");
}
else
if (ReverseProxyPtr[0] == '#')
{
/* modify URLs only belonging to the proxied-to host */
if (strsame (hptr, RemoteHostPort, RemoteHostPortLength) &&
!isalnum(hptr[RemoteHostPortLength]) &&
hptr[RemoteHostPortLength] != '.')
{
/* matches the reverse-proxied-to host so modify */
cptr = hptr + RemoteHostPortLength;
fprintf (stdout, "%s%s",
ReverseProxyPrefix, *cptr == '/' ? "" : "/");
}
}
else
if (strsame (hptr, ReverseProxyPtr, ReverseProxyLength) &&
!isalnum(hptr[ReverseProxyLength]) &&
hptr[ReverseProxyLength] != '.')
{
/* matches the supplied string (host name) so modify */
cptr = hptr + ReverseProxyLength;
fprintf (stdout, "%s%s",
ReverseProxyPrefix, *cptr == '/' ? "" : "/");
}
}
else
fprintf (stdout, "%s%s", FetchPrefix, cptr);
BufferCurrentPtr = cptr;
}
/* skip to end-of-line */
while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
/* break if end of request header */
if (*(unsigned long*)cptr == '\r\n\r\n')
{
cptr += 4;
break;
}
if (*(unsigned short*)cptr == '\n\n')
{
cptr += 2;
break;
}
/* skip to start-of-line */
while (*cptr && (*cptr == '\r' || *cptr == '\n')) cptr++;
}
NextPlease = true;
while (*cptr)
{
if (*cptr != '=')
{
cptr++;
continue;
}
FullUrl = false;
hptr = "";
if (!memcmp (cptr, "=\"http://", 9))
{
if (Debug) fprintf (stdout, "|%9.9s|\n", cptr);
cptr += 2;
hptr = cptr + 7;
FullUrl = true;
NextPlease = false;
}
else
if (!memcmp (cptr, "=\"ftp://", 8))
{
if (Debug) fprintf (stdout, "|%8.8s|\n", cptr);
cptr += 2;
hptr = cptr + 6;
FullUrl = true;
NextPlease = false;
}
else
if (!memcmp (cptr, "=\'http://", 9))
{
if (Debug) fprintf (stdout, "|%9.9s|\n", cptr);
cptr += 2;
hptr = cptr + 7;
FullUrl = true;
NextPlease = false;
}
else
if (!memcmp (cptr, "=\'ftp://", 8))
{
if (Debug) fprintf (stdout, "|%8.8s|\n", cptr);
cptr += 2;
hptr = cptr + 6;
FullUrl = true;
NextPlease = false;
}
else
if (!memcmp (cptr, "=http://", 8))
{
if (Debug) fprintf (stdout, "|%8.8s|\n", cptr);
cptr++;
hptr = cptr + 7;
FullUrl = true;
NextPlease = false;
}
else
if (!memcmp (cptr, "=ftp://", 7))
{
if (Debug) fprintf (stdout, "|%7.7s|\n", cptr);
cptr++;
hptr = cptr + 6;
FullUrl = true;
NextPlease = false;
}
else
if (*(unsigned short*)(cptr+1) == '\"/')
{
if (Debug) fprintf (stdout, "|%2.2s|\n", cptr);
sptr = cptr+2;
while (*sptr && *sptr != '\"') sptr++;
if (*sptr == '\"')
{
/* starts with a '="/' and ends with a '"', looks likely */
if (Debug) fprintf (stdout, "|%c|\n", *sptr);
cptr += 2;
NextPlease = false;
}
}
else
if (*(unsigned short*)(cptr+1) == '\'/')
{
if (Debug) fprintf (stdout, "|%2.2s|\n", cptr);
sptr = cptr+2;
while (*sptr && *sptr != '\'') sptr++;
if (*sptr == '\'')
{
/* starts with a '='/' and ends with a ''', looks likely */
if (Debug) fprintf (stdout, "|%c|\n", *sptr);
cptr += 2;
NextPlease = false;
}
}
else
if (*(cptr+1) == '/')
{
if (Debug) fprintf (stdout, "|%2.2s|\n", cptr);
sptr = cptr+1;
while (*sptr &&
*sptr != ' ' &&
*sptr != '\t' &&
*sptr != '>' &&
*sptr != '\r' &&
*sptr != '\n') sptr++;
if (*sptr == ' ' ||
*sptr == '\t' ||
*sptr == '>' ||
*sptr == '\r' ||
*sptr == '\n')
{
/* starts with a '=/' and ends plausably, looks possible */
if (Debug) fprintf (stdout, "|%c|\n", *sptr);
cptr++;
NextPlease = false;
}
}
if (NextPlease)
{
cptr++;
continue;
}
FLUSH_TO_CPTR
if (FullUrl)
{
if (ReverseProxyPtr)
{
if (ReverseProxyPtr[0] == '*')
{
/* modify all full URLs */
cptr = hptr;
while (*cptr && *cptr != '/' &&
(isalnum(*cptr) || *cptr == '.' ||
*cptr == '-' || *cptr == '_')) cptr++;
fprintf (stdout, "%s%s",
ReverseProxyPrefix, *cptr == '/' ? "" : "/");
}
else
if (ReverseProxyPtr[0] == '#')
{
/* modify URLs only belonging to the proxied-to host */
if (strsame (hptr, RemoteHostPort, RemoteHostPortLength) &&
!isalnum(hptr[RemoteHostPortLength]) &&
hptr[RemoteHostPortLength] != '.')
{
/* matches the reverse-proxied-to host so modify */
cptr = hptr + RemoteHostPortLength;
fprintf (stdout, "%s%s",
ReverseProxyPrefix, *cptr == '/' ? "" : "/");
}
}
else
if (strsame (hptr, ReverseProxyPtr, ReverseProxyLength) &&
!isalnum(hptr[ReverseProxyLength]) &&
hptr[ReverseProxyLength] != '.')
{
/* matches the supplied string (host name) so modify */
cptr = hptr + ReverseProxyLength;
fprintf (stdout, "%s%s",
ReverseProxyPrefix, *cptr == '/' ? "" : "/");
}
}
else
fputs (MassageLinksPath, stdout);
}
else
if (ReverseProxyPtr)
fprintf (stdout, "%s%s", ReversePathPtr, *cptr == '/' ? "" : "/");
else
fprintf (stdout, "%shttp://%s%s",
MassageLinksPath, RemoteHostPort, *cptr == '/' ? "" : "/");
BufferCurrentPtr = cptr++;
NextPlease = true;
}
FLUSH_TO_CPTR
}
/*****************************************************************************/
/*
Hmmm, idea look familiar? :-)
*/
int WatchThis
(
char *FaoString,
...
)
{
static unsigned long LibStatTimerReal = 1,
LibStatTimerCpu = 2;
static int WatchCount;
int argcnt, status;
char *cptr, *sptr, *zptr;
char Buffer [1024],
FaoBuffer [256];
unsigned long CpuBinTime;
unsigned long RealBinTime [2];
unsigned long *vecptr;
$DESCRIPTOR (BufferDsc, Buffer);
$DESCRIPTOR (FaoBufferDsc, FaoBuffer);
unsigned long FaoVector [32];
va_list argptr;
/*********/
/* begin */
/*********/
va_count (argcnt);
if (Debug) fprintf (stdout, "WatchThis() %d |%s|\n", argcnt, FaoString);
if (!FaoString)
{
if (WatchCount)
{
/* post-processing reset */
lib$stat_timer (&LibStatTimerReal, &RealBinTime, 0);
lib$stat_timer (&LibStatTimerCpu, &CpuBinTime, 0);
WatchThis ("TIME REAL=!%T CPU=!UL.!2ZL",
&RealBinTime, CpuBinTime/100, CpuBinTime%100);
WatchThis ("WATCH end");
WatchEnabled = WatchCount = 0;
/* back to binary mode (remove if necessary) */
if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin")))
exit (vaxc$errno);
if (!(stderr = freopen ("SYS$OUTPUT:", "w", stderr, "ctx=bin")))
exit (vaxc$errno);
}
else
{
/* pre-processing initialize */
WatchCount = 1;
/* back to record mode as WATCH is line-oriented output */
if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=rec")))
exit (vaxc$errno);
if (!(stderr = freopen ("SYS$OUTPUT:", "w", stderr, "ctx=rec")))
exit (vaxc$errno);
lib$init_timer (0);
fprintf (stdout,
"Content-Type: text/plain\nScript-Control: X-content-encoding-gzip=0\n\n");
WatchThis ("WATCH begin");
}
return (SS$_NORMAL);
}
vecptr = FaoVector;
*vecptr++ = WatchCount++;
*vecptr++ = 0;
va_start (argptr, FaoString);
for (argcnt -= 1; argcnt; argcnt--)
*vecptr++ = (unsigned long)va_arg (argptr, unsigned long);
va_end (argptr);
zptr = (sptr = FaoBuffer) + sizeof(FaoBuffer)-3;
for (cptr = "|!4ZL|!%T|"; *cptr; *sptr++ = *cptr++);
for (cptr = FaoString; *cptr && sptr < zptr; *sptr++ = *cptr++);
*sptr++ = '|';
*sptr++ = '\n';
*sptr++ = '\0';
FaoBufferDsc.dsc$a_pointer = FaoBuffer;
FaoBufferDsc.dsc$w_length = sptr - FaoBuffer;
status = sys$faol (&FaoBufferDsc, 0, &BufferDsc,
(unsigned long*)&FaoVector);
if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status);
if (!(status & 1)) exit (status);
fputs (Buffer, stdout);
return (status);
}
/*****************************************************************************/
/*
Ditto? :-)
*/
int WatchDump
(
char *DataPtr,
int DataLength
)
{
int cnt, len;
char *cptr, *lptr, *sptr;
char WatchBuffer [256];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "WatchDump() %d\n", DataLength);
cptr = DataPtr;
len = DataLength;
while (len)
{
sptr = WatchBuffer;
lptr = cptr;
cnt = 0;
while (cnt < 32)
{
if (cnt < len)
sptr += sprintf (sptr, "%02.02X", (unsigned char)*cptr++);
else
{
*sptr++ = ' ';
*sptr++ = ' ';
}
if (!(++cnt % 4)) *sptr++ = ' ';
}
*sptr++ = ' ';
cptr = lptr;
cnt = 0;
while (cnt < 32 && cnt < len)
{
if (isprint(*cptr))
*sptr++ = *cptr;
else
*sptr++ = '.';
cptr++;
cnt++;
}
if (len > 32) len -= 32; else len = 0;
*sptr++ = '\n';
*sptr = '\0';
fputs (WatchBuffer, stdout);
}
return (1);
}
/*****************************************************************************/
/*
Translate a logical name using LNM$FILE_DEV. Returns a pointer to the value
string, or NULL if the name does not exist. If 'LogValue' is supplied the
logical name is translated into that (assumed to be large enough), otherwise
it's translated into an internal static buffer. 'IndexValue' should be zero
for a 'flat' logical name, or 0..127 for interative translations.
*/
char* TrnLnm
(
char *LogName,
char *LogValue,
int IndexValue
)
{
static unsigned short ValueLength;
static unsigned long LnmAttributes,
LnmIndex;
static char StaticLogValue [256];
static $DESCRIPTOR (LogNameDsc, "");
static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV");
static struct {
short int buf_len;
short int item;
void *buf_addr;
unsigned short *ret_len;
} LnmItems [] =
{
{ sizeof(LnmIndex), LNM$_INDEX, &LnmIndex, 0 },
{ sizeof(LnmAttributes), LNM$_ATTRIBUTES, &LnmAttributes, 0 },
{ 255, LNM$_STRING, 0, &ValueLength },
{ 0,0,0,0 }
};
int status;
char *cptr;
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "TrnLnm() |%s| %d\n", LogName, IndexValue);
LnmIndex = IndexValue;
LogNameDsc.dsc$a_pointer = LogName;
LogNameDsc.dsc$w_length = strlen(LogName);
if (LogValue)
cptr = LnmItems[2].buf_addr = LogValue;
else
cptr = LnmItems[2].buf_addr = StaticLogValue;
status = sys$trnlnm (0, &LnmFileDevDsc, &LogNameDsc, 0, &LnmItems);
if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status);
if (!(status & 1) || !(LnmAttributes & LNM$M_EXISTS))
{
if (Debug) fprintf (stdout, "|(null)|\n");
return (NULL);
}
cptr[ValueLength] = '\0';
if (Debug) fprintf (stdout, "|%s|\n", cptr);
return (cptr);
}
/*****************************************************************************/
/*
Get "command-line" parameters, whether from the command-line or from a
configuration symbol or logical containing the equivalent.
*/
GetParameters ()
{
static char CommandLine [256];
static unsigned long Flags = 0;
int status;
unsigned short Length;
char ch;
char *aptr, *cptr, *clptr, *sptr;
$DESCRIPTOR (CommandLineDsc, CommandLine);
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "GetParameters()\n");
if (!(clptr = getenv ("FETCH$PARAM")))
{
/* get the entire command line following the verb */
if (VMSnok (status =
lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags)))
exit (status);
(clptr = CommandLine)[Length] = '\0';
}
aptr = NULL;
ch = *clptr;
for (;;)
{
if (aptr && *aptr == '/') *aptr = '\0';
if (!ch) break;
*clptr = ch;
if (Debug) fprintf (stdout, "clptr |%s|\n", clptr);
while (*clptr && isspace(*clptr)) *clptr++ = '\0';
aptr = clptr;
if (*clptr == '/') clptr++;
while (*clptr && !isspace (*clptr) && *clptr != '/')
{
if (*clptr != '\"')
{
clptr++;
continue;
}
cptr = clptr;
clptr++;
while (*clptr)
{
if (*clptr == '\"')
if (*(clptr+1) == '\"')
clptr++;
else
break;
*cptr++ = *clptr++;
}
*cptr = '\0';
if (*clptr) clptr++;
}
ch = *clptr;
if (*clptr) *clptr = '\0';
if (Debug) fprintf (stdout, "aptr |%s|\n", aptr);
if (!*aptr) continue;
if (strsame (aptr, "/BODY", 4))
{
DoResponseBody = true;
continue;
}
if (strsame (aptr, "/NOBODY", 6))
{
DoResponseBody = false;
continue;
}
if (strsame (aptr, "/CHECK", 4))
{
DoCheckOnly = true;
continue;
}
if (strsame (aptr, "/CHUNK=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
BufferChunk = atoi(cptr);
continue;
}
if (strsame (aptr, "/DBUG", -1))
{
Debug = true;
continue;
}
if (strsame (aptr, "/ESCAPE_HTML", 4))
{
DoEscapeHtml = true;
continue;
}
if (strsame (aptr, "/NOESCAPE_HTML", 6))
{
DoEscapeHtml = false;
continue;
}
if (strsame (aptr, "/FETCH=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
FetchScriptNamePtr = cptr;
continue;
}
if (strsame (aptr, "/FTP", 4))
{
DoFtp = true;
continue;
}
if (strsame (aptr, "/HEADER", 4))
{
DoResponseHeader = true;
continue;
}
if (strsame (aptr, "/NOHEADER", 6))
{
DoResponseHeader = false;
continue;
}
if (strsame (aptr, "/HTTP", 4))
{
DoFtp = false;
continue;
}
if (strsame (aptr, "/ICONS=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
IconPathPtr = cptr;
continue;
}
if (strsame (aptr, "/METHOD=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
CliHttpMethodPtr = cptr;
continue;
}
if (strsame (aptr, "/OUTPUT=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
OutputPtr = cptr;
if (strsame (OutputPtr, "TT:", -1) ||
strsame (OutputPtr, "TERMINAL", -1) ||
strsame (OutputPtr, "SYS$OUTPUT", 10))
OutputPtr = "";
continue;
}
if (strsame (aptr, "/REPORT", 4))
{
DoReport = true;
continue;
}
if (strsame (aptr, "/REVERSE=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
ReverseProxyPtr = cptr;
ReverseProxyLength = strlen(cptr);
continue;
}
if (strsame (aptr, "/SUBSTITUTE=", 4))
{
/* get this one by hand :^) */
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
if (*cptr) ParamSubsChar = *cptr;
continue;
}
if (strsame (aptr, "/URL=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
FetchUrlPtr = cptr;
continue;
}
if (strsame (aptr, "/UNKNOWN=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
FtpDefaultContentTypePtr = cptr;
continue;
}
if (strsame (aptr, "/USER_AGENT=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
UserAgentPtr = cptr;
continue;
}
if (strsame (aptr, "/VERSION=", 4))
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr)
{
fprintf (stdout, "%%%s-I-SOFTWAREID, %s\n%s\n",
Utility, SoftwareId, CopyrightInfo);
exit (SS$_NORMAL);
}
if (strsame (cptr, "1.0", -1))
RequestHttpVersion = 10;
else
if (strsame (cptr, "0.9", -1))
RequestHttpVersion = 9;
else
RequestHttpVersion = -1;
continue;
}
if (strsame (aptr, "/WATCH", 4))
{
WatchEnabled = true;
continue;
}
if (*aptr == '/')
{
fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n",
Utility, aptr+1);
exit (STS$K_ERROR | STS$M_INHIB_MSG);
}
if (!FetchUrlPtr)
{
cptr = GetParameterString (aptr);
if (!cptr || !*cptr) continue;
FetchUrlPtr = cptr;
continue;
}
fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n",
Utility, aptr);
exit (STS$K_ERROR | STS$M_INHIB_MSG);
}
}
/*****************************************************************************/
/*
Get a string from the command-line. It can be a qualifier specified string
(e.g. /QUALIFIER=) or just a string supplied as a parameter. If a
qualifier then it must have a string following the '=' otherwise a NULL is
returned. If the string begins with the character specified by global variable
'ParamSubsChar' (which in turn can be specified by the /SUBSTITUTE= qualifier)
and the parameter string begins with this as the first character the remainder
of the string is used as a C-RTL getenv() function argument and it's value is
attempted to be resolved. If it does not exist the function returns a NULL.
If it does exist the a pointer to it's value is returned. If the string does
not begin with the substitution character a pointer to it is returned. The
substitution character may be escaped using a leading backslash.
*/
char* GetParameterString (char *aptr)
{
char *cptr;
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "GetParameterString() %c |%s|\n", ParamSubsChar, aptr);
if (!aptr) return (NULL);
if (*aptr == '/')
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (!*cptr) return (NULL);
cptr++;
}
else
cptr = aptr;
if (*cptr == ParamSubsChar)
cptr = getenv(cptr+1);
else
if (*cptr == '\\' && *(cptr+1) == ParamSubsChar)
cptr++;
if (Debug) fprintf (stdout, "|%s|\n", cptr ? cptr : "(null)");
return (cptr);
}
/*****************************************************************************/
/*
*/
int SetLocalSymbol
(
char *SymbolName,
char *SymbolValue
)
{
static int LocalSymbol = LIB$K_CLI_LOCAL_SYM;
static $DESCRIPTOR (SymbolNameDsc, "");
static $DESCRIPTOR (SymbolValueDsc, "");
int status;
/*********/
/* begin */
/*********/
if (WatchEnabled) WatchThis ("SYMBOL !AZ \"!AZ\"", SymbolName, SymbolValue);
SymbolNameDsc.dsc$w_length = strlen(SymbolName);
SymbolNameDsc.dsc$a_pointer = SymbolName;
SymbolValueDsc.dsc$w_length = strlen(SymbolValue);
SymbolValueDsc.dsc$a_pointer = SymbolValue;
status = lib$set_symbol (&SymbolNameDsc, &SymbolValueDsc, &LocalSymbol);
if (Debug) fprintf (stdout, "lib$set_symbol() %%X%08.08X\n", status);
return (status);
}
/****************************************************************************/
/*
Does a case-insensitive, character-by-character string compare and returns
true if two strings are the same, or false if not. If a maximum number of
characters are specified only those will be compared, if the entire strings
should be compared then specify the number of characters as 0.
*/
BOOL strsame
(
char *sptr1,
char *sptr2,
int count
)
{
/*********/
/* begin */
/*********/
/** if (Debug) fprintf (stdout, "strsame() |%s|%s|\n", sptr1, sptr2); **/
while (*sptr1 && *sptr2)
{
if (toupper (*sptr1++) != toupper (*sptr2++)) return (false);
if (count)
if (!--count) return (true);
}
if (*sptr1 || *sptr2)
return (false);
else
return (true);
}
/****************************************************************************/