home *** CD-ROM | disk | FTP | other *** search
/ CD Shareware Magazine 1999 April / CD_Shareware_Magazine_31.iso / Free / Prg / cgi.exe / cgi.bas < prev   
Encoding:
BASIC Source File  |  1997-04-10  |  30.0 KB  |  757 lines

  1. Attribute VB_Name = "modCGI"
  2. '----------------------------------------------------------------------
  3. '       ***********
  4. '       * CGI.BAS *
  5. '       ***********
  6. '
  7. ' VERSION: 1.7  (March 18, 1995)
  8. '
  9. ' AUTHOR:  Robert B. Denny <rdenny@netcom.com>
  10. '
  11. ' Common routines needed to establish a VB environment for
  12. ' CGI "scripts" that run behind the Windows Web Server.
  13. '
  14. ' INTRODUCTION
  15. '
  16. ' The Common Gateway Interface (CGI) version 1.1 specifies a minimal
  17. ' set of data that is made available to the back-end application by
  18. ' an HTTP (Web) server. It also specifies the details for passing this
  19. ' information to the back-end. The latter part of the CGI spec is
  20. ' specific to Unix-like environments. The NCSA httpd for Windows does
  21. ' supply the data items (and more) specified by CGI/1.1, however it
  22. ' uses a different method for passing the data to the back-end.
  23. '
  24. ' DEVELOPMENT
  25. '
  26. ' Windows httpd requires any Windows back-end program to be an
  27. ' executable image. This means that you must convert your VB
  28. ' application into an executable (.EXE) before it can be tested
  29. ' with the server.
  30. '
  31. ' ENVIRONMENT
  32. '
  33. ' The Windows httpd server executes script requests by doing a
  34. ' WinExec with a command line in the following form:
  35. '
  36. '   prog-name cgi-profile input-file output-file url-args
  37. '
  38. ' Assuming you are familiar with the CGI specification, the above
  39. ' should be "intuitively obvious" except for the cgi-profile, which
  40. ' is described in the next section.
  41. '
  42. ' THE CGI PROFILE FILE
  43. '
  44. ' The Unix CGI passes data to the back end by defining environment
  45. ' variables which can be used by shell scripts. The Windows httpd
  46. ' server passes data to its back end via the profile file. The
  47. ' format of the profile is that of a Windows ".INI" file. The keyword
  48. ' names have been changed cosmetically.
  49. '
  50. ' There are 7 sections in a CGI profile file, [CGI], [Accept],
  51. ' [System], [Extra Headers], and [Form Literal], [Form External],
  52. ' and [Form huge]. They are described below:
  53. '
  54. ' [CGI]                <== The standard CGI variables
  55. ' CGI Version=         The version of CGI spoken by the server
  56. ' Request Protocol=    The server's info protocol (e.g. HTTP/1.0)
  57. ' Request Method=      The method specified in the request (e.g., "GET")
  58. ' Executable Path=     Physical pathname of the back-end (this program)
  59. ' Logical Path=        Extra path info in logical space
  60. ' Physical Path=       Extra path info in local physical space
  61. ' Query String=        String following the "?" in the request URL
  62. ' Content Type=        MIME content type of info supplied with request
  63. ' Content Length=      Length, bytes, of info supplied with request
  64. ' Server Software=     Version/revision of the info (HTTP) server
  65. ' Server Name=         Server's network hostname (or alias from config)
  66. ' Server Port=         Server's network port number
  67. ' Server Admin=        E-Mail address of server's admin. (config)
  68. ' Referer=             URL of referring document (HTTP/1.0 draft 12/94)
  69. ' From=                E-Mail of client user  (HTTP/1.0 draft 12/94)
  70. ' Remote Host=         Remote client's network hostname
  71. ' Remote Address=      Remote client's network address
  72. ' Authenticated Username=Username if present in request
  73. ' Authenticated Password=Password if present in request
  74. ' Authentication Method=Method used for authentication (e.g., "Basic")
  75. ' Authentication Realm=Name of realm for users/groups
  76. ' RFC-931 Identity=    (deprecated, removed from code)
  77. '
  78. ' [Accept]             <== What the client says it can take
  79. ' The MIME types found in the request header as
  80. '    Accept: xxx/yyy; zzzz...
  81. ' are entered in this section as
  82. '    xxx/yyy=zzzz...
  83. ' If only the MIME type appears, the form is
  84. '    xxx/yyy=Yes
  85. '
  86. ' [System]             <== Windows interface specifics
  87. ' GMT Offset=          Offset of local timezone from GMT, seconds (LONG!)
  88. ' Output File=         Pathname of file to receive results
  89. ' Content File=        Pathname of file containing request content (raw)
  90. ' Debug Mode=          If server's back-end debug flag is set (Yes/No)
  91. '
  92. ' [Extra Headers]
  93. ' Any "extra" headers found in the request that activated this
  94. ' program. They are listed in "key=value" form. Usually, you'll see
  95. ' at least the name of the browser here.
  96. '
  97. ' [Form Literal]
  98. ' If the request was a POST from a Mosaic form (with content type of
  99. ' "application/x-www-form-urlencoded"), the server will decode the
  100. ' form data. Raw form input is of the form "key=value&key=value&...",
  101. ' with the value parts "URL-encoded". The server splits the key=value
  102. ' pairs at the '&', then spilts the key and value at the '=',
  103. ' URL-decodes the value string and puts the result into key=value
  104. ' (decoded) form in the [Form Literal] section of the INI.
  105. '
  106. ' [Form External]
  107. ' If the decoded value string is more than 254 characters long,
  108. ' or if the decoded value string contains any control characters,
  109. ' the server puts the decoded value into an external tempfile and
  110. ' lists the field in this section as:
  111. '    key=<pathname> <length>
  112. ' where <pathname> is the path and name of the tempfile containing
  113. ' the decoded value string, and <length> is the length in bytes
  114. ' of the decoded value string.
  115. '
  116. ' NOTE: BE SURE TO OPEN THIS FILE IN BINARY MODE UNLESS YOU ARE
  117. '       CERTAIN THAT THE FORM DATA IS TEXT!
  118. '
  119. ' [Form Huge]
  120. ' If the raw value string is more than 65,536 bytes long, the server
  121. ' does no decoding. In this case, the server lists the field in this
  122. ' section as:
  123. '    key=<offset> <length>
  124. ' where <offset> is the offset from the beginning of the Content File
  125. ' at which the raw value string for this key is located, and <length>
  126. ' is the length in bytes of the raw value string. You can use the
  127. ' <offset> to perform a "Seek" to the start of the raw value string,
  128. ' and use the length to know when you have read the entire raw string
  129. ' into your decoder. Note that VB has a limit of 64K for strings, so
  130. '
  131. ' Examples:
  132. '
  133. '    [Form Literal]
  134. '    smallfield=123 Main St. #122
  135. '
  136. '    [Form External]
  137. '    field300chars=C:\TEMP\HS19AF6C.000 300
  138. '    fieldwithlinebreaks=C:\TEMP\HS19AF6C.001 43
  139. '
  140. '    [Form Huge]
  141. '    field230K=C:\TEMP\HS19AF6C.002 276920
  142. '
  143. ' =====
  144. ' USAGE
  145. ' =====
  146. ' Include CGI.BAS in your VB project. Set the project options for
  147. ' "Sub Main" startup. The Main() procedure is in this module, and it
  148. ' handles all of the setup of the VB CGI environment, as described
  149. ' above. Once all of this is done, the Main() calls YOUR main procedure
  150. ' which must be called CGI_Main(). The output file is open, use Send()
  151. ' to write to it. The input file is NOT open, and "huge" form fields
  152. ' have not been decoded.
  153. '
  154. ' (New in V1.3) If your program is started without command-line args,
  155. ' the code assumes you want to run it interactively. This is useful
  156. ' for providing a setup screen, etc. Instead of calling CGI_Main(),
  157. ' it calls Inter_Main(). Your module must also implement this
  158. ' function. If you don't need an interactive mode, just create
  159. ' Inter_Main() and put a 1-line call to MsgBox alerting the
  160. ' user that the program is not meant to be run interactively.
  161. ' The samples furnished with the server do this.
  162. '
  163. ' If a Visual Basic runtime error occurs, it will be trapped and result
  164. ' in an HTTP error response being sent to the client. Check out the
  165. ' Error Handler() sub. When your program finishes, be sure to RETURN
  166. ' TO MAIN(). Don't just do an "End".
  167. '
  168. ' Have a look at the stuff below to see what's what.
  169. '
  170. '----------------------------------------------------------------------
  171. ' Author:   Robert B. Denny <rdenny@netcom.com>
  172. '           June 7, 1994
  173. '
  174. ' Revision History:
  175. '   26-May-94 rbd   Initial experimental release
  176. '   07-Jun-94 rbd   Revised keyword names and form decoding per
  177. '                   httpd 1.2b8, fixed section name of Output File.
  178. '   13-Dec-94 rbd   Move FreeFile calls to just before opening files.
  179. '   04-Feb-94 rbd   Fix Authenticated User -> Username, added new
  180. '                   variables Referer, From and Authentication Realm.
  181. '                   Removed RFC931 stuff (deprecated)
  182. '   11-Feb-95 rbd   Added Inter_Main() support and stub.
  183. '   01-Mar-95 rbd   Add support for password pass-through, clean
  184. '                   up HTML in error handler. Add SendNoOp().
  185. '                   Add Server: header to error handler msg.
  186. '   17-Mar-95 rbd   Fix error handler to remove deprecated
  187. '                   MIME-Version header. Add GMT offset, new CGI var.
  188. '                   Add WebDate() function for producing HTTP/1.0
  189. '                   compliant date/time. Add Date: header to error
  190. '                   messages.
  191. '   18-Mar-95 rbd   Add CGI_ERR_START for catching CGI.BAS defined
  192. '                   errors in user code. Decode our "user defined:
  193. '                   errors in handler instead of saying "User
  194. '                   defined error".
  195. '----------------------------------------------------------------------
  196. Option Explicit
  197. '
  198. ' ==================
  199. ' Manifest Constants
  200. ' ==================
  201. '
  202. Const MAX_CMDARGS = 8       ' Max # of command line args
  203. Const ENUM_BUF_SIZE = 4096  ' Key enumeration buffer, see GetProfile()
  204. ' These are the limits in the server
  205. Const MAX_XHDR = 100        ' Max # of "extra" request headers
  206. Const MAX_ACCTYPE = 100     ' Max # of Accept: types in request
  207. Const MAX_FORM_TUPLES = 100 ' Max # form key=value pairs
  208. Const MAX_HUGE_TUPLES = 16  ' Max # "huge" form fields
  209. '
  210. '
  211. ' =====
  212. ' Types
  213. ' =====
  214. '
  215. Type Tuple                  ' Used for Accept: and "extra" headers
  216.     key As String           ' and for holding POST form key=value pairs
  217.     value As String
  218. End Type
  219.  
  220. Type HugeTuple              ' Used for "huge" form fields
  221.     key As String           ' Keyword (decoded)
  222.     offset As Long          ' Byte offset into Content File of value
  223.     length As Long          ' Length of value, bytes
  224. End Type
  225. '
  226. '
  227. ' ================
  228. ' Global Constants
  229. ' ================
  230. '
  231. ' -----------
  232. ' Error Codes
  233. ' -----------
  234. '
  235. Global Const ERR_ARGCOUNT = 32767
  236. Global Const ERR_BAD_REQUEST = 32766        ' HTTP 400
  237. Global Const ERR_UNAUTHORIZED = 32765       ' HTTP 401
  238. Global Const ERR_PAYMENT_REQUIRED = 32764   ' HTTP 402
  239. Global Const ERR_FORBIDDEN = 32763          ' HTTP 403
  240. Global Const ERR_NOT_FOUND = 32762          ' HTTP 404
  241. Global Const ERR_INTERNAL_ERROR = 32761     ' HTTP 500
  242. Global Const ERR_NOT_IMPLEMENTED = 32760    ' HTTP 501
  243. Global Const ERR_TOO_BUSY = 32758           ' HTTP 503 (experimental)
  244. Global Const ERR_NO_FIELD = 32757           ' GetxxxField "no field"
  245. Global Const CGI_ERR_START = 32757          ' Start of our errors
  246.  
  247. ' ====================
  248. ' CGI Global Variables
  249. ' ====================
  250. '
  251. ' ----------------------
  252. ' Standard CGI variables
  253. ' ----------------------
  254. '
  255. Global CGI_ServerSoftware As String
  256. Global CGI_ServerName As String
  257. Global CGI_ServerPort As Integer
  258. Global CGI_RequestProtocol As String
  259. Global CGI_ServerAdmin As String
  260. Global CGI_Version As String
  261. Global CGI_RequestMethod As String
  262. Global CGI_LogicalPath As String
  263. Global CGI_PhysicalPath As String
  264. Global CGI_ExecutablePath As String
  265. Global CGI_QueryString As String
  266. Global CGI_Referer As String
  267. Global CGI_From As String
  268. Global CGI_RemoteHost As String
  269. Global CGI_RemoteAddr As String
  270. Global CGI_AuthUser As String
  271. Global CGI_AuthPass As String
  272. Global CGI_AuthType As String
  273. Global CGI_AuthRealm As String
  274. Global CGI_ContentType As String
  275. Global CGI_ContentLength As Long
  276. '
  277. ' ------------------
  278. ' HTTP Header Arrays
  279. ' ------------------
  280. '
  281. Global CGI_AcceptTypes(MAX_ACCTYPE) As Tuple    ' Accept: types
  282. Global CGI_NumAcceptTypes As Integer            ' # of live entries in array
  283. Global CGI_ExtraHeaders(MAX_XHDR) As Tuple      ' "Extra" headers
  284. Global CGI_NumExtraHeaders As Integer           ' # of live entries in array
  285. '
  286. ' --------------
  287. ' POST Form Data
  288. ' --------------
  289. '
  290. Global CGI_FormTuples(MAX_FORM_TUPLES) As Tuple ' POST form key=value pairs
  291. Global CGI_NumFormTuples As Integer             ' # of live entries in array
  292. Global CGI_HugeTuples(MAX_HUGE_TUPLES) As HugeTuple ' Form "huge tuples
  293. Global CGI_NumHugeTuples As Integer             ' # of live entries in array
  294.  
  295. '
  296. ' ----------------
  297. ' System Variables
  298. ' ----------------
  299. '
  300. Global CGI_GMTOffset As Variant         ' GMT offset (time serial)
  301. Global CGI_ContentFile As String        ' Content/Input file pathname
  302. Global CGI_OutputFile As String         ' Output file pathname
  303. Global CGI_DebugMode As Integer         ' Script Tracing flag from server
  304. '
  305. '
  306. ' ========================
  307. ' Windows API Declarations
  308. ' ========================
  309. '
  310. ' NOTE: Declaration of GetPrivateProfileString is specially done to
  311. ' permit enumeration of keys by passing NULL key value. See GetProfile().
  312. '
  313. 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
  314. '
  315. '
  316. ' ===============
  317. ' Local Variables
  318. ' ===============
  319. '
  320. Dim CGI_ProfileFile As String           ' Profile file pathname
  321. Dim CGI_OutputFN As Integer             ' Output file number
  322. Dim ErrorString As String
  323.  
  324.  
  325.  
  326.  
  327. '---------------------------------------------------------------------------
  328. '
  329. '   ErrorHandler() - Global error handler
  330. '
  331. ' If a VB runtime error occurs dusing execution of the program, this
  332. ' procedure generates an HTTP/1.0 HTML-formatted error message into
  333. ' the output file, then exits the program.
  334. '
  335. ' This should be armed immediately on entry to the program's main()
  336. ' procedure. Any errors that occur in the program are caught, and
  337. ' an HTTP/1.0 error messsage is generated into the output file. The
  338. ' presence of the HTTP/1.0 on the first line of the output file causes
  339. ' NCSA httpd for WIndows to send the output file to the client with no
  340. ' interpretation or other header parsing.
  341. '---------------------------------------------------------------------------
  342. Sub ErrorHandler(code As Integer)
  343.  
  344.     On Error Resume Next     ' Give it a good try!
  345.  
  346.     Seek #CGI_OutputFN, 1    ' Rewind output file just in case
  347.     Send ("HTTP/1.0 500 Internal Error")
  348.     Send ("Server: " + CGI_ServerSoftware)
  349.     Send ("Date: " + WebDate(Now))
  350.     Send ("Content-type: text/html")
  351.     Send ("")
  352.     Send ("<HTML><HEAD>")
  353.     Send ("<TITLE>Error in " + CGI_ExecutablePath + "</TITLE>")
  354.     Send ("</HEAD><BODY>")
  355.     Send ("<H1>Error in " + CGI_ExecutablePath + "</H1>")
  356.     Send ("An internal Visual Basic error has occurred in " + CGI_ExecutablePath + ".")
  357.     Send ("<PRE>" + ErrorString + "</PRE>")
  358.     Send ("<I>Please</I> note what you were doing when this problem occurred,")
  359.     Send ("so we can identify and correct it. Write down the Web page you were using,")
  360.     Send ("any data you may have entered into a form or search box, and")
  361.     Send ("anything else that may help us duplicate the problem. Then contact the")
  362.     Send ("administrator of this service: ")
  363.     Send ("<A HREF=""mailto:" & CGI_ServerAdmin & """>")
  364.     Send ("<ADDRESS><" + CGI_ServerAdmin + "></ADDRESS>")
  365.     Send ("</A></BODY></HTML>")
  366.  
  367.     Close #CGI_OutputFN
  368.  
  369.     '======
  370.      End            ' Terminate the program
  371.     '======
  372. End Sub
  373.  
  374. '---------------------------------------------------------------------------
  375. '
  376. '   GetAcceptTypes() - Create the array of accept type structs
  377. '
  378. ' Enumerate the keys in the [Accept] section of the profile file,
  379. ' then get the value for each of the keys.
  380. '---------------------------------------------------------------------------
  381. Private Sub GetAcceptTypes()
  382.     Dim sList As String
  383.     Dim i As Integer, j As Integer, l As Integer, n As Integer
  384.  
  385.     sList = GetProfile("Accept", "") ' Get key list
  386.     l = Len(sList)                          ' Length incl. trailing null
  387.     i = 1                                   ' Start at 1st character
  388.     n = 0                                   ' Index in array
  389.     Do While ((i < l) And (n < MAX_ACCTYPE)) ' Safety stop here
  390.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  391.         CGI_AcceptTypes(n).key = Mid$(sList, i, j - i) ' Get Key, then value
  392.         CGI_AcceptTypes(n).value = GetProfile("Accept", CGI_AcceptTypes(n).key)
  393.         i = j + 1                           ' Bump pointer
  394.         n = n + 1                           ' Bump array index
  395.     Loop
  396.     CGI_NumAcceptTypes = n                  ' Fill in global count
  397.  
  398. End Sub
  399.  
  400. '---------------------------------------------------------------------------
  401. '
  402. '   GetArgs() - Parse the command line
  403. '
  404. ' Chop up the command line, fill in the argument vector, return the
  405. ' argument count (similar to the Unix/C argc/argv handling)
  406. '---------------------------------------------------------------------------
  407. Private Function GetArgs(argv() As String) As Integer
  408.     Dim buf As String
  409.     Dim i As Integer, j As Integer, l As Integer, n As Integer
  410.  
  411.     buf = Trim$(Command$)                   ' Get command line
  412.  
  413.     l = Len(buf)                            ' Length of command line
  414.     If l = 0 Then                           ' If empty
  415.         GetArgs = 0                         ' Return argc = 0
  416.         Exit Function
  417.     End If
  418.  
  419.     i = 1                                   ' Start at 1st character
  420.     n = 0                                   ' Index in argvec
  421.     Do While ((i < l) And (n < MAX_CMDARGS)) ' Safety stop here
  422.         j = InStr(i, buf, " ")              ' J -> next space
  423.         If j = 0 Then Exit Do               ' Exit loop on last arg
  424.         argv(n) = Trim$(Mid$(buf, i, j - i)) ' Get this token, trim it
  425.         i = j + 1                           ' Skip that blank
  426.         Do While Mid$(buf, i, 1) = " "      ' Skip any additional whitespace
  427.             i = i + 1
  428.         Loop
  429.         n = n + 1                           ' Bump array index
  430.     Loop
  431.  
  432.     argv(n) = Trim$(Mid$(buf, i, (l - i + 1))) ' Get last arg
  433.     GetArgs = n + 1                         ' Return arg count
  434.  
  435. End Function
  436.  
  437. '---------------------------------------------------------------------------
  438. '
  439. '   GetExtraHeaders() - Create the array of extra header structs
  440. '
  441. ' Enumerate the keys in the [Extra Headers] section of the profile file,
  442. ' then get the value for each of the keys.
  443. '---------------------------------------------------------------------------
  444. Private Sub GetExtraHeaders()
  445.     Dim sList As String
  446.     Dim i As Integer, j As Integer, l As Integer, n As Integer
  447.  
  448.     sList = GetProfile("Extra Headers", "") ' Get key list
  449.     l = Len(sList)                          ' Length incl. trailing null
  450.     i = 1                                   ' Start at 1st character
  451.     n = 0                                   ' Index in array
  452.     Do While ((i < l) And (n < MAX_XHDR))   ' Safety stop here
  453.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  454.         CGI_ExtraHeaders(n).key = Mid$(sList, i, j - i) ' Get Key, then value
  455.         CGI_ExtraHeaders(n).value = GetProfile("Extra Headers", CGI_ExtraHeaders(n).key)
  456.         i = j + 1                           ' Bump pointer
  457.         n = n + 1                           ' Bump array index
  458.     Loop
  459.     CGI_NumExtraHeaders = n                 ' Fill in global count
  460.  
  461. End Sub
  462.  
  463. '---------------------------------------------------------------------------
  464. '
  465. '   GetFormTuples() - Create the array of POST form input key=value pairs
  466. '
  467. '---------------------------------------------------------------------------
  468. Private Sub GetFormTuples()
  469.     Dim sList As String
  470.     Dim i As Integer, j As Integer, k As Integer
  471.     Dim l As Integer, n As Integer
  472.     Dim s As Long
  473.     Dim buf As String
  474.     Dim extName As String
  475.     Dim extFile As Integer
  476.     Dim extlen As Long
  477.  
  478.     n = 0                                   ' Index in array
  479.  
  480.     '
  481.     ' Do the easy one first: [Form Literal]
  482.     '
  483.     sList = GetProfile("Form Literal", "")  ' Get key list
  484.     l = Len(sList)                          ' Length incl. trailing null
  485.     i = 1                                   ' Start at 1st character
  486.     Do While ((i < l) And (n < MAX_FORM_TUPLES)) ' Safety stop here
  487.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  488.         CGI_FormTuples(n).key = Mid$(sList, i, j - i) ' Get Key, then value
  489.         CGI_FormTuples(n).value = GetProfile("Form Literal", CGI_FormTuples(n).key)
  490.         i = j + 1                           ' Bump pointer
  491.         n = n + 1                           ' Bump array index
  492.     Loop
  493.     '
  494.     ' Now do the external ones: [Form External]
  495.     '
  496.     sList = GetProfile("Form External", "") ' Get key list
  497.     l = Len(sList)                          ' Length incl. trailing null
  498.     i = 1                                   ' Start at 1st character
  499.     extFile = FreeFile
  500.     Do While ((i < l) And (n < MAX_FORM_TUPLES)) ' Safety stop here
  501.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  502.         CGI_FormTuples(n).key = Mid$(sList, i, j - i) ' Get Key, then pathname
  503.         buf = GetProfile("Form External", CGI_FormTuples(n).key)
  504.         k = InStr(buf, " ")                 ' Split file & length
  505.         extName = Mid$(buf, 1, k - 1)           ' Pathname
  506.         k = k + 1
  507.         extlen = CLng(Mid$(buf, k, Len(buf) - k + 1)) ' Length
  508.         '
  509.         ' Use feature of GET to read content in one call
  510.         '
  511.         Open extName For Binary Access Read As #extFile
  512.         CGI_FormTuples(n).value = String$(extlen, " ") ' Breathe in...
  513.         Get #extFile, , CGI_FormTuples(n).value 'GULP!
  514.         Close #extFile
  515.         i = j + 1                           ' Bump pointer
  516.         n = n + 1                           ' Bump array index
  517.     Loop
  518.  
  519.     CGI_NumFormTuples = n                   ' Number of fields decoded
  520.     n = 0                                   ' Reset counter
  521.     '
  522.     ' Finally, the [Form Huge] section. Will this ever get executed?
  523.     '
  524.     sList = GetProfile("Form Huge", "")     ' Get key list
  525.     l = Len(sList)                          ' Length incl. trailing null
  526.     i = 1                                   ' Start at 1st character
  527.     Do While ((i < l) And (n < MAX_FORM_TUPLES)) ' Safety stop here
  528.         j = InStr(i, sList, Chr$(0))        ' J -> next null
  529.         CGI_HugeTuples(n).key = Mid$(sList, i, j - i) ' Get Key
  530.         buf = GetProfile("Form Huge", CGI_HugeTuples(n).key) ' "offset length"
  531.         k = InStr(buf, " ")                 ' Delimiter
  532.         CGI_HugeTuples(n).offset = CLng(Mid$(buf, 1, (k - 1)))
  533.         CGI_HugeTuples(n).length = CLng(Mid$(buf, k, (Len(buf) - k + 1)))
  534.         i = j + 1                           ' Bump pointer
  535.         n = n + 1                           ' Bump array index
  536.     Loop
  537.     
  538.     CGI_NumHugeTuples = n                   ' Fill in global count
  539.  
  540. End Sub
  541.  
  542. '---------------------------------------------------------------------------
  543. '
  544. '   GetProfile() - Get a value or enumerate keys in CGI_Profile file
  545. '
  546. ' Get a value given the section and key, or enumerate keys given the
  547. ' section name and "" for the key. If enumerating, the list of keys for
  548. ' the given section is returned as a null-separated string, with a
  549. ' double null at the end.
  550. '
  551. ' VB handles this with flair! I couldn't believe my eyes when I tried this.
  552. '---------------------------------------------------------------------------
  553. Private Function GetProfile(sSection As String, sKey As String) As String
  554.     Dim retLen As Integer
  555.     Dim buf As String * ENUM_BUF_SIZE
  556.  
  557.     If sKey <> "" Then
  558.         retLen = GetPrivateProfileString(sSection, sKey, "", buf, ENUM_BUF_SIZE, CGI_ProfileFile)
  559.     Else
  560.         retLen = GetPrivateProfileString(sSection, 0&, "", buf, ENUM_BUF_SIZE, CGI_ProfileFile)
  561.     End If
  562.     If retLen = 0 Then
  563.         GetProfile = ""
  564.     Else
  565.         GetProfile = Left$(buf, retLen)
  566.     End If
  567.  
  568. End Function
  569.  
  570. '----------------------------------------------------------------------
  571. '
  572. ' Get the value of a "small" form field given the key
  573. '
  574. ' Signals an error if field does not exist
  575. '
  576. '----------------------------------------------------------------------
  577. Function GetSmallField(key As String) As String
  578.     Dim i As Integer
  579.  
  580.     For i = 0 To (CGI_NumFormTuples - 1)
  581.         If CGI_FormTuples(i).key = key Then
  582.             GetSmallField = Trim$(CGI_FormTuples(i).value)
  583.             Exit Function           ' ** DONE **
  584.         End If
  585.     Next i
  586.     '
  587.     ' Field does not exist
  588.     '
  589.     Error ERR_NO_FIELD
  590. End Function
  591.  
  592. '---------------------------------------------------------------------------
  593. '
  594. '   InitializeCGI() - Fill in all of the CGI variables, etc.
  595. '
  596. ' Read the profile file name from the command line, then fill in
  597. ' the CGI globals, the Accept type list and the Extra headers list.
  598. ' Then open the input and output files.
  599. '
  600. ' Returns True if OK, False if some sort of error. See ReturnError()
  601. ' for info on how errors are handled.
  602. '
  603. ' NOTE: Assumes that the CGI error handler has been armed with On Error
  604. '---------------------------------------------------------------------------
  605. Sub InitializeCGI()
  606.     Dim sect As String
  607.     Dim argc As Integer
  608.     Static argv(MAX_CMDARGS) As String
  609.     Dim buf As String
  610.  
  611.     CGI_DebugMode = True    ' Initialization errors are very bad
  612.  
  613.     '
  614.     ' Parse the command line. We need the profile file name (duh!)
  615.     ' and the output file name NOW, so we can return any errors we
  616.     ' trap. The error handler writes to the output file.
  617.     '
  618.     argc = GetArgs(argv())
  619.     CGI_ProfileFile = argv(0)
  620.  
  621.     sect = "CGI"
  622.     CGI_ServerSoftware = GetProfile(sect, "Server Software")
  623.     CGI_ServerName = GetProfile(sect, "Server Name")
  624.     CGI_RequestProtocol = GetProfile(sect, "Request Protocol")
  625.     CGI_ServerAdmin = GetProfile(sect, "Server Admin")
  626.     CGI_Version = GetProfile(sect, "CGI Version")
  627.     CGI_RequestMethod = GetProfile(sect, "Request Method")
  628.     CGI_LogicalPath = GetProfile(sect, "Logical Path")
  629.     CGI_PhysicalPath = GetProfile(sect, "Physical Path")
  630.     CGI_ExecutablePath = GetProfile(sect, "Executable Path")
  631.     CGI_QueryString = GetProfile(sect, "Query String")
  632.     CGI_RemoteHost = GetProfile(sect, "Remote Host")
  633.     CGI_RemoteAddr = GetProfile(sect, "Remote Address")
  634.     CGI_Referer = GetProfile(sect, "Referer")
  635.     CGI_From = GetProfile(sect, "From")
  636.     CGI_AuthUser = GetProfile(sect, "Authenticated Username")
  637.     CGI_AuthPass = GetProfile(sect, "Authenticated Password")
  638.     CGI_AuthRealm = GetProfile(sect, "Authentication Realm")
  639.     CGI_AuthType = GetProfile(sect, "Authentication Method")
  640.     CGI_ContentType = GetProfile(sect, "Content Type")
  641.     buf = GetProfile(sect, "Content Length")
  642.     If buf = "" Then
  643.         CGI_ContentLength = 0
  644.     Else
  645.         CGI_ContentLength = CLng(buf)
  646.     End If
  647.     buf = GetProfile(sect, "Server Port")
  648.     If buf = "" Then
  649.         CGI_ServerPort = -1
  650.     Else
  651.         CGI_ServerPort = CInt(buf)
  652.     End If
  653.  
  654.     sect = "System"
  655.     CGI_ContentFile = GetProfile(sect, "Content File")
  656.     CGI_OutputFile = GetProfile(sect, "Output File")     'argv(2)
  657.     CGI_OutputFN = FreeFile
  658.     Open CGI_OutputFile For Output Access Write As #CGI_OutputFN
  659.     buf = GetProfile(sect, "GMT Offset")
  660.     CGI_GMTOffset = CVDate(CDbl(buf) / 86400#) ' Timeserial offset
  661.     buf = GetProfile(sect, "Debug Mode")    ' Y or N
  662.     If (Left$(buf, 1) = "Y") Then           ' Must start with Y
  663.         CGI_DebugMode = True
  664.     Else
  665.         CGI_DebugMode = False
  666.     End If
  667.  
  668.     GetAcceptTypes          ' Enumerate Accept: types into tuples
  669.     GetExtraHeaders         ' Enumerate extra headers into tuples
  670.     GetFormTuples           ' Decode any POST form input into tuples
  671.  
  672. End Sub
  673.  
  674. '----------------------------------------------------------------------
  675. '
  676. '   main() - CGI script back-end main procedure
  677. '
  678. ' This is the main() for the VB back end. Note carefully how the error
  679. ' handling is set up, and how program cleanup is done. If no command
  680. ' line args are present, call Inter_Main() and exit.
  681. '----------------------------------------------------------------------
  682. Sub Main()
  683.     On Error GoTo ErrorHandler
  684.  
  685.     If Trim$(Command$) = "" Then    ' Interactive start
  686.         Inter_Main                  ' Call interactive main
  687.         Exit Sub                    ' Exit the program
  688.     End If
  689.  
  690.     InitializeCGI       ' Create the CGI environment
  691.  
  692.     '===========
  693.     CGI_Main            ' Execute the actual "script"
  694.     '===========
  695.  
  696. Cleanup:
  697.     Close #CGI_OutputFN
  698.     Exit Sub                        ' End the program
  699. '------------
  700. ErrorHandler:
  701.     Select Case Err                 ' Decode our "user defined" errors
  702.         Case ERR_NO_FIELD:
  703.             ErrorString = "Unknown form field"
  704.         Case Else:
  705.             ErrorString = Error$    ' Must be VB error
  706.     End Select
  707.  
  708.     ErrorString = ErrorString & " (error #" & Err & ")"
  709.     On Error GoTo 0                 ' Prevent recursion
  710.     ErrorHandler (Err)              ' Generate HTTP error result
  711.     Resume Cleanup
  712. '------------
  713. End Sub
  714.  
  715. '----------------------------------------------------------------------
  716. '
  717. '  Send() - Shortcut for writing to output file
  718. '
  719. '----------------------------------------------------------------------
  720. Sub Send(s As String)
  721.     Print #CGI_OutputFN, s
  722. End Sub
  723.  
  724. '---------------------------------------------------------------------------
  725. '
  726. '   SendNoOp() - Tell browser to do nothing.
  727. '
  728. ' Most browsers will do nothing. Netscape 1.0N leaves hourglass
  729. ' cursor until the mouse is waved around. Enhanced Mosaic 2.0
  730. ' oputs up an alert saying "URL leads nowhere". Your results may
  731. ' vary...
  732. '
  733. '---------------------------------------------------------------------------
  734. Sub SendNoOp()
  735.  
  736.     Send ("HTTP/1.0 204 No Response")
  737.     Send ("Server: " + CGI_ServerSoftware)
  738.     Send ("")
  739.  
  740. End Sub
  741.  
  742. '---------------------------------------------------------------------------
  743. '
  744. '   WebDate - Return an HTTP/1.0 compliant date/time string
  745. '
  746. ' Inputs:   t = Local time as VB Variant (e.g., returned by Now())
  747. ' Returns:  Properly formatted HTTP/1.0 date/time in GMT
  748. '---------------------------------------------------------------------------
  749. Function WebDate(dt As Variant) As String
  750.     Dim t As Variant
  751.     
  752.     t = CVDate(dt - CGI_GMTOffset)      ' Convert time to GMT
  753.     WebDate = Format$(t, "ddd dd mmm yyyy hh:mm:ss") & " GMT"
  754.  
  755. End Function
  756.  
  757.