/*****************************************************************************/
/*
                               ISAPIexample.c

This is an example ISAPI script for the WASD server.  This program is built
into a shareable image.  It is loaded by the ISAPI interface (CGISAPI) and the
GetExtensionVersion() callback invoked to return version information.  The
HttpExtensionproc() callback is the used by CGISAPI to activate this script.

Supplying a single integer as the query string generates that number of lines
of 80 characters for performance comparison purposes.

Supplying a '!' followed by an integer outputs a 1 to 16384 size block of
characters for testing the CGIsapi explicit buffering.

Supplying "exit" as the query string causes the script to exit.

Supplying "debug=on" or "debug=off" toggles the CGIsapi DLL debug mode for
demonstration purposes.


BUILD DETAILS
-------------
See BUILD_ISAPIEXAMPLE.COM procedure.


COPYRIGHT
---------
Copyright (C) 1999-2020 Mark G.Daniel
This program, comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under the conditions of the GNU GENERAL PUBLIC LICENSE, version 2.


VERSION HISTORY (update SOFTWAREVN as well!)
---------------
23-DEC-2003  MGD  v1.1.1, minor conditional mods to support IA64
21-NOV-1999  MGD  v1.1.0, add data block (to check CGIsapi data buffer)
14-MAR-1999  MGD  v1.0.0, initial
*/
/*****************************************************************************/

#define SOFTWAREVN "1.1.1"
#define SOFTWARENM "ISAPIEXAMPLE"
#ifdef __ALPHA
#  define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN
#endif
#ifdef __ia64
#  define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN
#endif
#ifdef __VAX
#  define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN
#endif
#ifdef __x86_64
#  define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN
#endif

#ifndef __VAX
#   pragma nomember_alignment
#endif

#define boolean int
#define true 1
#define false 0

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>

#include "cgisapi.h"

char  Utility [] = "ISAPIEXAMPLE";

static boolean  Debug;

static int  UsageCount;

/* required prototypes */
DWORD WINAPI DisplayEcb (LPEXTENSION_CONTROL_BLOCK);
ExtensionExit (LPEXTENSION_CONTROL_BLOCK);
DWORD WINAPI GenerateLines (LPEXTENSION_CONTROL_BLOCK);
DWORD WINAPI GenerateBlock (LPEXTENSION_CONTROL_BLOCK);

/*****************************************************************************/
/*
ISAPI version check entry-point called by CGISAPI.
*/

BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *lpHvi)

{
   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "GetExtensionVersion()\n");

   lpHvi->dwExtensionVersion = (HSE_VERSION_MAJOR << 16) |
                                HSE_VERSION_MINOR;
   strcpy (lpHvi->lpszExtensionDesc, "WASD example ISAPI DLL");

   return (TRUE);
}

/*****************************************************************************/
/*
Main entry-point called by CGISAPI to perform the DLL functionality.
*/

DWORD WINAPI HttpExtensionProc (LPEXTENSION_CONTROL_BLOCK lpEcb)

{
   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "HttpExtensionProc()\n");

   UsageCount++;

   if (lpEcb->lpszQueryString != NULL &&
       !strcmp(lpEcb->lpszQueryString, "exit") ||
       !strncmp(lpEcb->lpszQueryString, "exit=", 5))
      ExtensionExit (lpEcb);

   if (lpEcb->lpszQueryString != NULL)
   {
      if (isdigit(*lpEcb->lpszQueryString))
      {
         GenerateLines (lpEcb);
         return (HSE_STATUS_SUCCESS);
      }
      if (*lpEcb->lpszQueryString == '!')
      {
         GenerateBlock (lpEcb);
         return (HSE_STATUS_SUCCESS);
      }
   }

   if (lpEcb->lpszQueryString != NULL &&
       !strcmp(lpEcb->lpszQueryString, "debug=on") ||
       !strcmp(lpEcb->lpszQueryString, "debug=ON"))
      lpEcb->ServerSupportFunction (lpEcb->ConnID, HSE_REQ_DLL_DEBUG_ON,
                                    NULL, NULL, NULL);
   else
   if (lpEcb->lpszQueryString != NULL &&
       !strcmp(lpEcb->lpszQueryString, "debug=off") ||
       !strcmp(lpEcb->lpszQueryString, "debug=OFF"))
      lpEcb->ServerSupportFunction (lpEcb->ConnID, HSE_REQ_DLL_DEBUG_OFF,
                                    NULL, NULL, NULL);

   DisplayEcb (lpEcb);

   return (HSE_STATUS_SUCCESS);
}

/*****************************************************************************/
/*
Where the ISAPI extension is loaded into the server's process space you
wouldn't do this ;^) but with WASD ISAPI in private process space it's an
effective method for loading a new version of the extension during development.
*/

ExtensionExit (LPEXTENSION_CONTROL_BLOCK lpEcb)

{
   static char  ContentTextHtml [] = "Content-Type: text/html\r\n\r\n";

   int  BufferCount;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ExtensionExit()\n");

   BufferCount = sizeof(ContentTextHtml)-1;
   lpEcb->ServerSupportFunction (lpEcb->ConnID,
                                 HSE_REQ_SEND_RESPONSE_HEADER,
	                         NULL, &BufferCount, ContentTextHtml);

   BufferCount = 5;
   lpEcb->WriteClient (lpEcb->ConnID, "Bye!\n", &BufferCount, 0);

   /* ensure the write buffer is flushed */
   lpEcb->WriteClient (lpEcb->ConnID, NULL, 0, 0);

   exit (1);
}

/*****************************************************************************/
/*
Return an HTML page showing the contents of all relevant fields in the
extension control block.
*/

DWORD WINAPI DisplayEcb (LPEXTENSION_CONTROL_BLOCK lpEcb)

{
   int  BufferCount,
        ContentLength,
        DataLength,
        DataRemaining,
        VariableCount;
   char  Buffer [4096],
         FarTooSmall [8],
         HttpUserAgent [256],
         RemoteHost [256],
         ServerSoftware [256];
   char  *DataPtr;
   time_t  UnixSeconds;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "DisplayEcb()\n");

   time (&UnixSeconds);

   /* just for DLL debug demonstration! */
   VariableCount = sizeof(Buffer);
   lpEcb->GetServerVariable (lpEcb, "all_http",
                             Buffer, &VariableCount);

   /* CGI variable does not exist, just for DLL debug demonstration! */
   VariableCount = sizeof(Buffer);
   lpEcb->GetServerVariable (lpEcb, "does_not_exist",
                             Buffer, &VariableCount);

   /* will not fit in storage provided, just for DLL debug demonstration! */
   VariableCount = sizeof(FarTooSmall);
   lpEcb->GetServerVariable (lpEcb, "http_user_agent", 
                             FarTooSmall, &VariableCount);

   /* special case (returns binary value), just for DLL debug demonstration! */
   VariableCount = sizeof(ContentLength);
   lpEcb->GetServerVariable (lpEcb, "content_length", 
                             &ContentLength, &VariableCount);

   VariableCount = sizeof(HttpUserAgent);
   lpEcb->GetServerVariable (lpEcb, "http_user_agent",
                             HttpUserAgent, &VariableCount);

   VariableCount = sizeof(RemoteHost);
   lpEcb->GetServerVariable (lpEcb, "remote_host",
                             RemoteHost, &VariableCount);

   VariableCount = sizeof(ServerSoftware);
   lpEcb->GetServerVariable (lpEcb, "server_software",
                             ServerSoftware, &VariableCount);

   BufferCount = sprintf (Buffer,
"Expires: Fri, 13 Jan 1978 14:00:00 GMT\r\n\
Content-type: text/html\r\n\
\r\n");

   lpEcb->ServerSupportFunction (lpEcb->ConnID,
                                 HSE_REQ_SEND_RESPONSE_HEADER,
	                         NULL, &BufferCount, Buffer);

   BufferCount = sprintf (Buffer,
"<HTML>\n\
<HEAD>\n\
<META NAME=\"generator\" CONTENT=\"%s\">\n\
<TITLE>Example ISAPI DLL</TITLE>\n\
</HEAD>\n\
<BODY>\n\
<FONT SIZE=+1><B>This is the WASD example ISAPI DLL!</B></FONT>\n\
<P>The time is currently %s\
<P>This script has been called <FONT SIZE=+1>%d</FONT> time%s.\n\
<P><TABLE CELLPADDING=5 CELLSPACING=0 BORDER=0>\n\
<TR><TH COLSPAN=2 ALIGN=left><U>EXTENSION_CONTROL_BLOCK</U></TH></TR>\n\
<TR><TH ALIGN=right>lpszMethod:</TH><TD>%s</TD></TR>\n\
<TR><TH ALIGN=right>lpszQueryString:</TH><TD>%s</TD></TR>\n\
<TR><TH ALIGN=right>lpszPathInfo:</TH><TD>%s</TD></TR>\n\
<TR><TH ALIGN=right>lpszPathTranslated:</TH><TD>%s</TD></TR>\n\
<TR><TH ALIGN=right>lpszContentType:</TH><TD>%s</TD></TR>\n\
<TR><TH ALIGN=right>cbTotalBytes:</TH><TD>%d</TD></TR>\n\
<TR><TH ALIGN=right>cbAvailable:</TH><TD>%d</TD></TR>\n\
<TR><TH ALIGN=right VALIGN=top>lpbData:</TH><TD><TT>\n",
      SOFTWAREID,
      ctime (&UnixSeconds),
      UsageCount,
      UsageCount == 1 ? "" : "s",
      lpEcb->lpszMethod == NULL ? "(null)" : lpEcb->lpszMethod,
      lpEcb->lpszQueryString == NULL ? "(null)" : lpEcb->lpszQueryString,
      lpEcb->lpszPathInfo == NULL ? "(null)" : lpEcb->lpszPathInfo,
      lpEcb->lpszPathTranslated == NULL ? "(null)" : lpEcb->lpszPathTranslated,
      lpEcb->lpszContentType == NULL ? "(null)" : lpEcb->lpszContentType,
      lpEcb->cbTotalBytes,
      lpEcb->cbAvailable);

   lpEcb->WriteClient (lpEcb->ConnID, Buffer, &BufferCount, 0);

   if (lpEcb->lpbData == NULL)
   {
      BufferCount = 6;
      lpEcb->WriteClient (lpEcb->ConnID, "(null)", &BufferCount, 0);
   }
   else
   {
      if (lpEcb->lpszMethod != NULL &&
          !strcmp (lpEcb->lpszMethod, "POST"))
      {
         /* for better layout break the body into 80 character chunks */
         DataPtr = lpEcb->lpbData;
         DataRemaining = lpEcb->cbAvailable;
         while (DataRemaining > 0)
         {
            if (DataRemaining > 80)
               DataLength = 80;
            else
               DataLength = DataRemaining;
            lpEcb->WriteClient (lpEcb->ConnID, DataPtr, &DataLength, 0);
            DataPtr += DataLength;
            DataRemaining -= DataLength;
            DataLength = 5;
            lpEcb->WriteClient (lpEcb->ConnID, "<BR>\n", &DataLength, 0);
         }
      }
   }

   BufferCount = sprintf (Buffer,
"</TT></TD></TR>\n\
<TR><TH></TH></TR>\n\
<TR><TH COLSPAN=2 ALIGN=left><U>GetServerVariable()</U></TH></TR>\n\
<TR><TH ALIGN=right>remote_host:</TH><TD>%s</TD></TR>\n\
<TR><TH ALIGN=right>http_user_agent:</TH><TD>%s</TD></TR>\n\
<TR><TH ALIGN=right>server_software:</TH><TD>%s</TD></TR>\n\
</TABLE>\n\
</BODY>\n\
</HTML>\n",
      RemoteHost,
      HttpUserAgent,
      ServerSoftware);

   lpEcb->WriteClient (lpEcb->ConnID, Buffer, &BufferCount, 0);

   return (HSE_STATUS_SUCCESS);
}

/*****************************************************************************/
/*
Simply generates the query-string specified number of of 80 character lines. 
Used for performance comparisons with other forms of scripting.
*/

DWORD WINAPI GenerateLines (LPEXTENSION_CONTROL_BLOCK lpEcb)

{
   static char  ContentTextPlain [] = "Content-Type: text/plain\r\n\r\n";

   static char  Line80Chars [] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789\
!@#$%^&*()_+-=<>,\n";

   int  BufferCount,
        RequestedCount;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "GenerateLines()\n");

   BufferCount = sizeof(ContentTextPlain)-1;
   lpEcb->ServerSupportFunction (lpEcb->ConnID,
                                 HSE_REQ_SEND_RESPONSE_HEADER,
	                         NULL, &BufferCount, ContentTextPlain);

   RequestedCount = atoi (lpEcb->lpszQueryString);
   while (RequestedCount--)
   {
      BufferCount = sizeof(Line80Chars)-1;
      lpEcb->WriteClient (lpEcb->ConnID, Line80Chars, &BufferCount, 0);
   }

   return (HSE_STATUS_SUCCESS);
}

/*****************************************************************************/
/*
Generate a block of 16384 characters, basically divided into lines of 63
characters, plus the newline, with a vertical bar marked at each 4096 character
boundary.  Then allow any number of these characters to be requested to be
output.  Just to test that the explicit buffer of CGISAPI.C is working
correctly.
*/

DWORD WINAPI GenerateBlock (LPEXTENSION_CONTROL_BLOCK lpEcb)

{
   static char  ContentTextPlain [] = "Content-Type: text/plain\r\n\r\n";

   int  BufferCount;
   char  *cptr;
   char  DataBlock [16384];

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "GenerateBlock()\n");

   BufferCount = sizeof(ContentTextPlain)-1;
   lpEcb->ServerSupportFunction (lpEcb->ConnID,
                                 HSE_REQ_SEND_RESPONSE_HEADER,
	                         NULL, &BufferCount, ContentTextPlain);

   memset (DataBlock, '1', 4096);
   memset (DataBlock+4096, '2', 4096);
   memset (DataBlock+4096+4096, '3', 4096);
   memset (DataBlock+4096+4096+4096, '4', 4096);
   for (cptr = DataBlock+63;
        cptr < DataBlock+sizeof(DataBlock);
        cptr += 64)
      *cptr = '\n';
   for (cptr = DataBlock+4095;
        cptr < DataBlock+sizeof(DataBlock);
        cptr += 4096)
      *cptr = '|';

   BufferCount = atoi(lpEcb->lpszQueryString+1);
   if (!BufferCount || BufferCount > sizeof(DataBlock))
      BufferCount = sizeof(DataBlock);
   lpEcb->WriteClient (lpEcb->ConnID, DataBlock, &BufferCount, 0);

   return (HSE_STATUS_SUCCESS);
}

/*****************************************************************************/
