'---------------------------------------------------------------------- ' ************* ' * CGI32.BAS * ' ************* ' ' VERSION: 1.9 (July 17, 1997) ' ' AUTHORS: Robert B. Denny ' Christopher J. Duke ' ' Common routines needed to establish a VB environment for ' Windows CGI programs that run behind the WebSite Server. ' ' INTRODUCTION ' ' The Common Gateway Interface (CGI) version 1.1 specifies a minimal ' set of data that is made available to the back-end application by ' an HTTP (Web) server. It also specifies the details for passing this ' information to the back-end. The latter part of the CGI spec is ' specific to Unix-like environments. The NCSA httpd for Windows does ' supply the data items (and more) specified by CGI/1.1, however it ' uses a different method for passing the data to the back-end. ' ' DEVELOPMENT ' ' WebSite requires any Windows back-end program to be an ' executable image. This means that you must convert your VB ' application into an executable (.EXE) before it can be tested ' with the server. ' ' ENVIRONMENT ' ' The WebSite server executes script requests by doing a ' CreateProcess with a command line in the following form: ' ' prog-name cgi-profile ' ' THE CGI PROFILE FILE ' ' The Unix CGI passes data to the back end by defining environment ' variables which can be used by shell scripts. The WebSite ' server passes data to its back end via the profile file. The ' format of the profile is that of a Windows ".INI" file. The keyword ' names have been changed cosmetically. ' ' There are 7 sections in a CGI profile file, [CGI], [Accept], ' [System], [Extra Headers], and [Form Literal], [Form External], ' and [Form huge]. They are described below: ' ' [CGI] <== The standard CGI variables ' CGI Version= The version of CGI spoken by the server ' Request Protocol= The server's info protocol (e.g. HTTP/1.0) ' Request Method= The method specified in the request (e.g., "GET") ' Request Keep-Alive= If the client requested connection re-use (Yes/No) ' Executable Path= Physical pathname of the back-end (this program) ' Logical Path= Extra path info in logical space ' Physical Path= Extra path info in local physical space ' Query String= String following the "?" in the request URL ' Content Type= MIME content type of info supplied with request ' Content Length= Length, bytes, of info supplied with request ' Request Range= Byte-range specfication received with request ' Server Software= Version/revision of the info (HTTP) server ' Server Name= Server's network hostname (or alias from config) ' Server Port= Server's network port number ' Server Admin= E-Mail address of server's admin. (config) ' Referer= URL of referring document ' From= E-Mail of client user (rarely seen) ' User Agent= String describing client/browser software/version ' Remote Host= Remote client's network hostname ' Remote Address= Remote client's network address ' Authenticated Username=Username if present in request ' Authenticated Password=Password if present in request ' Authentication Method=Method used for authentication (e.g., "Basic") ' Authentication Realm=Name of realm for users/groups ' ' [Accept] <== What the client says it can take ' The MIME types found in the request header as ' Accept: xxx/yyy; zzzz... ' are entered in this section as ' xxx/yyy=zzzz... ' If only the MIME type appears, the form is ' xxx/yyy=Yes ' ' [System] <== Windows interface specifics ' GMT Offset= Offset of local timezone from GMT, seconds (LONG!) ' Output File= Pathname of file to receive results ' Content File= Pathname of file containing raw request content ' Debug Mode= If server's CGI debug flag is set (Yes/No) ' ' [Extra Headers] ' Any "extra" headers found in the request that activated this ' program. They are listed in "key=value" form. Usually, you'll see ' at least the name of the browser here as "User-agent". ' ' [Form Literal] ' If the request was a POST from a Mosaic form (with content type of ' "application/x-www-form-urlencoded"), the server will decode the ' form data. Raw form input is of the form "key=value&key=value&...", ' with the value parts "URL-encoded". The server splits the key=value ' pairs at the '&', then spilts the key and value at the '=', ' URL-decodes the value string and puts the result into key=value ' (decoded) form in the [Form Literal] section of the INI. ' ' [Form External] ' If the decoded value string is more than 254 characters long, ' or if the decoded value string contains any control characters ' or quote marks the server puts the decoded value into an external ' tempfile and lists the field in this section as: ' key= ' where is the path and name of the tempfile containing ' the decoded value string, and is the length in bytes ' of the decoded value string. ' ' NOTE: BE SURE TO OPEN THIS FILE IN BINARY MODE UNLESS YOU ARE ' CERTAIN THAT THE FORM DATA IS TEXT! ' ' [Form File] ' If the form data contained any uploaded files, they are described in ' this section as: ' key=[] [] ' where is the path and name of the tempfile contining the ' uploaded file, is the length in bytes of the uploaded file, ' is the content type of the uploaded file as sent by the browser, ' is the content-transfer encoding of the uploaded file, and ' is the original file name of the uploaded file. ' ' [Form Huge] ' If the raw value string is more than 65,536 bytes long, the server ' does no decoding. In this case, the server lists the field in this ' section as: ' key= ' where is the offset from the beginning of the Content File ' at which the raw value string for this key is located, and ' is the length in bytes of the raw value string. You can use the ' to perform a "Seek" to the start of the raw value string, ' and use the length to know when you have read the entire raw string ' into your decoder. Note that VB has a limit of 64K for strings, so ' ' Examples: ' ' [Form Literal] ' smallfield=123 Main St. #122 ' ' [Form External] ' field300chars=c:\website\cgi-tmp\1a7fws.000 300 ' fieldwithlinebreaks=c:\website\cgi-tmp\1a7fws.001 43 ' ' [Form Huge] ' field230K=c:\website\cgi-tmp\1a7fws.002 276920 ' ' ===== ' USAGE ' ===== ' Include CGI32.BAS in your VB4 or VB5 project. Set the project options for ' "Sub Main" startup. The Main() procedure is in this module, and it ' handles all of the setup of the VB CGI environment, as described ' above. Once all of this is done, the Main() calls YOUR main procedure ' which must be called CGI_Main(). The output file is open, use Send() ' to write to it. The input file is NOT open, and "huge" form fields ' have not been decoded. ' ' NOTE: If your program is started without command-line args, ' the code assumes you want to run it interactively. This is useful ' for providing a setup screen, etc. Instead of calling CGI_Main(), ' it calls Inter_Main(). Your module must also implement this ' function. If you don't need an interactive mode, just create ' Inter_Main() and put a 1-line call to MsgBox alerting the ' user that the program is not meant to be run interactively. ' The samples furnished with the server do this. ' ' If a Visual Basic runtime error occurs, it will be trapped and result ' in an HTTP error response being sent to the client. Check out the ' Error Handler() sub. When your program finishes, be sure to RETURN ' TO MAIN(). Don't just do an "End". ' ' Have a look at the stuff below to see what's what. ' '---------------------------------------------------------------------- ' Author: Robert B. Denny ' April 15, 1995 ' ' Revision History: ' 15-Apr-95 rbd Initial release (ref VB3 CGI.BAS 1.7) ' 02-Aug-95 rbd Changed to take input and output files from profile ' Server no longer produces long command line. ' 24-Aug-95 rbd Make call to GetPrivateProfileString conditional ' so 16-bit and 32-bit versions supported. Fix ' computation of CGI_GMTOffset for offset=0 (GMT) ' case. Add FieldPresent() routine for checkbox ' handling. Clean up comments. ' 29-Oct-95 rbd Added PlusToSpace() and Unescape() functions for ' decoding query strings, etc. ' 16-Nov-95 rbd Add keep-alive variable, file uploading description ' in comments, and upload display. ' 20-Nov-95 rbd Fencepost error in ParseFileValue() ' 23-Nov-95 rbd Remove On Error Resume Next from error handler ' 03-Dec-95 rbd User-Agent is now a variable, real HTTP header ' Add Request-Range as http header as well. ' 30-Dec-96 rbd Fix "endless" loop if call FieldPresent() with ' zero form fields. ' 23-Feb-97 rbd Per MS Tech Support, do not do Exit Sub in sub Main ' as this can corrupt VB40032.DLL. Use End instead. ' 17-Jul-97 cjd 1.9: Removed MAX_FORM_TUPLES (was previously a ' constant set to 100) and replaced it with a ' varying-sized array in GetFormTuples(). '---------------------------------------------------------------------- Option Explicit ' ' ================== ' Manifest Constants ' ================== ' Const MAX_CMDARGS = 8 ' Max # of command line args Const ENUM_BUF_SIZE = 4096 ' Key enumeration buffer, see GetProfile() ' These are the limits in the server Const MAX_XHDR = 100 ' Max # of "extra" request headers Const MAX_ACCTYPE = 100 ' Max # of Accept: types in request Const MAX_HUGE_TUPLES = 16 ' Max # "huge" form fields Const MAX_FILE_TUPLES = 16 ' Max # of uploaded file tuples ' ' ' ===== ' Types ' ===== ' Type Tuple ' Used for Accept: and "extra" headers key As String ' and for holding POST form key=value pairs value As String End Type Type FileTuple ' Used for form-based file uploads key As String ' Form field name file As String ' Local tempfile containing uploaded file length As Long ' Length in bytes of uploaded file type As String ' Content type of uploaded file encoding As String ' Content-transfer encoding of uploaded file name As String ' Original name of uploaded file End Type Type HugeTuple ' Used for "huge" form fields key As String ' Keyword (decoded) offset As Long ' Byte offset into Content File of value length As Long ' Length of value, bytes End Type ' ' ' ================ ' Global Constants ' ================ ' ' ----------- ' Error Codes ' ----------- ' Global Const ERR_ARGCOUNT = 32767 Global Const ERR_BAD_REQUEST = 32766 ' HTTP 400 Global Const ERR_UNAUTHORIZED = 32765 ' HTTP 401 Global Const ERR_PAYMENT_REQUIRED = 32764 ' HTTP 402 Global Const ERR_FORBIDDEN = 32763 ' HTTP 403 Global Const ERR_NOT_FOUND = 32762 ' HTTP 404 Global Const ERR_INTERNAL_ERROR = 32761 ' HTTP 500 Global Const ERR_NOT_IMPLEMENTED = 32760 ' HTTP 501 Global Const ERR_TOO_BUSY = 32758 ' HTTP 503 (experimental) Global Const ERR_NO_FIELD = 32757 ' GetxxxField "no field" Global Const CGI_ERR_START = 32757 ' Start of our errors ' ==================== ' CGI Global Variables ' ==================== ' ' ---------------------- ' Standard CGI variables ' ---------------------- ' Global CGI_ServerSoftware As String Global CGI_ServerName As String Global CGI_ServerPort As Integer Global CGI_RequestProtocol As String Global CGI_ServerAdmin As String Global CGI_Version As String Global CGI_RequestMethod As String Global CGI_RequestKeepAlive As Integer Global CGI_LogicalPath As String Global CGI_PhysicalPath As String Global CGI_ExecutablePath As String Global CGI_QueryString As String Global CGI_RequestRange As String Global CGI_Referer As String Global CGI_From As String Global CGI_UserAgent As String Global CGI_RemoteHost As String Global CGI_RemoteAddr As String Global CGI_AuthUser As String Global CGI_AuthPass As String Global CGI_AuthType As String Global CGI_AuthRealm As String Global CGI_ContentType As String Global CGI_ContentLength As Long ' ' ------------------ ' HTTP Header Arrays ' ------------------ ' Global CGI_AcceptTypes(MAX_ACCTYPE) As Tuple ' Accept: types Global CGI_NumAcceptTypes As Integer ' # of live entries in array Global CGI_ExtraHeaders(MAX_XHDR) As Tuple ' "Extra" headers Global CGI_NumExtraHeaders As Integer ' # of live entries in array ' ' -------------- ' POST Form Data ' -------------- ' Global CGI_FormTuples() As Tuple ' Declare dynamic array for POST form key=value pairs Global CGI_NumFormTuples As Integer ' # of live entries in array Global CGI_HugeTuples(MAX_HUGE_TUPLES) As HugeTuple ' Form "huge tuples Global CGI_NumHugeTuples As Integer ' # of live entries in array Global CGI_FileTuples(MAX_FILE_TUPLES) As FileTuple ' File upload tuples Global CGI_NumFileTuples As Integer ' # of live entries in array ' ' ---------------- ' System Variables ' ---------------- ' Global CGI_GMTOffset As Variant ' GMT offset (time serial) Global CGI_ContentFile As String ' Content/Input file pathname Global CGI_OutputFile As String ' Output file pathname Global CGI_DebugMode As Integer ' Script Tracing flag from server ' ' ' ======================== ' Windows API Declarations ' ======================== ' ' NOTE: Declaration of GetPrivateProfileString is specially done to ' permit enumeration of keys by passing NULL key value. See GetProfile(). ' Both the 16-bit and 32-bit flavors are given below. We DO NOT ' recommend using 16-bit VB4 with WebSite! ' #If Win32 Then Declare Function GetPrivateProfileString Lib "kernel32" _ Alias "GetPrivateProfileStringA" _ (ByVal lpApplicationName As String, _ ByVal lpKeyName As Any, _ ByVal lpDefault As String, _ ByVal lpReturnedString As String, _ ByVal nSize As Long, _ ByVal lpFileName As String) As Long #Else Declare Function GetPrivateProfileString Lib "Kernel" _ (ByVal lpSection As String, _ ByVal lpKeyName As Any, _ ByVal lpDefault As String, _ ByVal lpReturnedString As String, _ ByVal nSize As Integer, _ ByVal lpFileName As String) As Integer #End If ' ' ' =============== ' Local Variables ' =============== ' Dim CGI_ProfileFile As String ' Profile file pathname Dim CGI_OutputFN As Integer ' Output file number Dim ErrorString As String