CGI: VBScript CGI Property Class 2

Revision as of 23:23, 7 November 2011 by Ric (talk | contribs) (Created page with "{{Nav CGI}} '''VBScript CGI Property Class 2''' The previous page concluded with a class capable obtaining any CGI variable in particular variables sent to a page from a form us...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

VBScript CGI Property Class 2

The previous page concluded with a class capable obtaining any CGI variable in particular variables sent to a page from a form using either the Get or Post method and an anchor (link) which uses Get. This page expands the class to include variables that have been previously set using cookies.

HTTP Header

The following line is part of the HTTP header, which is sent to a browser allowing it to understand the content of a page.

"Content-type: text/html"&vbLF&vbLF

A header is mandatory otherwise a browser will not understand what to do with the page received. The above is a minimum header indicating a page is plain text containing html mark-up and to process it accordingly.

The following table lists most commonly used custom headers in CGI scripts.

Header Description
Content-type This is a MIME string defining the file format being returned. For example Content-type:text/html covered above
Expires A date when the content becomes invalid. A browser uses this to refresh a cashed page. Date format 14 Feb 2011 12:00:00 GMT.
Location Returns a different URL instead of the URL requested. This can redirect a request to any page for example "Location:http://www.example.com"
Last-modified Date a page resource was last modified.
Content-length Length in bytes of the data being returned. Allows a browser to report estimated download time for a file.
Set-Cookie Sends a cookie to the browser for storing

For this tutorial we are interested in the Set-Cookie header.

Top

Cookies and CGI

The HTTP protocol is a stateless protocol meaning nothing is saved between serving one page to another or even the same page. A server because of the stateless protocol has no way of knowing if a page has been requested before. To overcome this cookies are used. A page containing set cookies header asks a browser to store its cookies for this server. Next time this page or another page is requested from the same server the stored cookies are sent along with the page request. These cookies are passed to a page as environment variables these can be extracted and processed.

A browser stores cookies in a list consisting of name-value pairs for a specific named server. It sends this list back to only that specific named server with every request.

Each cookie consists of a Name=Value pair followed by these optional attributes

  • Expires The date the cookie will expire. If this is blank, the cookie will expire when the visitor quits the browser.
  • Domain The domain name of your site.
  • Path The path to the directory or web page that set the cookie. This may be blank if you want to retrieve the cookie from any directory or page.
  • Secure If this field contains the word "secure" then the cookie may only be retrieved with a secure server. If this field is blank, no such restriction exists.

The format for setting a cookie is shown below. Each cookie attribute is separated by a semi-colon

Set-Cookie: Name=Value;  Domain=localhost; Path=/vbs_test; expires=Fri, 22 Sep 2011 21:00:00 GMT

Note: The first attribute Name=Value is required all others are optional.

Top

Header format

The header sent to a browser is the first string written by your CGI script. A blank new-line signifying end of header follows this. The header string must contain a Content-type as the last entry. This header string can become rather long hence split it into smaller and more manageable strings as shown in this example

Dim header
header =          "Set-Cookie: Name1=Value1;  Domain=localhost; expires=Fri, 22 Sep 2011 21:00:00 GMT" &vbLF
header = header & "Set-Cookie: Name2=Value2;  Path=/;           expires=Fri, 22 Sep 2011 21:00:00 GMT" &vbLF
header = header & "Set-Cookie: Name3=Value3;  Path=/vbs_test;   expires=Sat, 23 Sep 2011 21:00:00 GMT" &vbLF
header = header & "Set-Cookie: Name4=Value4" &vbLF
header = header & "Content-type: text/html"&vbLF&vbLF
Wscript.Echo header

The above code snippet sets four cookies Name1 to Name4. A browser will send only the name-value pair of a set cookie, it does not send any other attributes for example the following is sent after setting the above cookies:

  • HTTP_COOKIE = Name1=Value1; Name4=Value4; Name3=Value3; Name2=Value2

Top

Extract cookie name value pairs

The HTTP_COOKIE CGI variable is passed to your script with the following format. Each name-value pair is semi-colon delimited this is used for spliting the cookie string. Each name-value pair is separated by an equal sign, again easily split to give component parts.

The following code extracts cookie variables into key (name) and item (value) pairs and adds them to the dictionary.

'--Save any cookie variables
If my_hash.Exists("HTTP_COOKIE") Then                    'Does cgi_variable HTTP_COOKIE exist 
  cookie_array = split(my_hash.Item("HTTP_COOKIE"), ";") 'Yes: Split variable-value pairs
  For each str in cookie_array                           'Scan cookie pairs
    pairs_split_array = split(str, "=")                  'Split these pairs
    pairs_split_array(0) = Trim(pairs_split_array(0))    'Clean
    pairs_split_array(1) = Trim(pairs_split_array(1))    'Clean
    my_hash.add pairs_split_array(0),pairs_split_array(1)'Add to dictionary 
  Next                                                   'Get next pair 
End If

Top

Expand class to include cookies

Adding the above snippet of code expands our CGI Property Class to include extraction of any cookie variables sent with the page. The full code for our complete class is shown below:

Note: This is the final CGI property class edit file common_function.vbs and replace the old class with this new one.

Class cgi_get_var_class

 Private my_hash                                     'Local storage                     

 '--Constructor
 Private Sub Class_Initialize()                      'Set inital
   Set my_hash = CreateObject("Scripting.Dictionary")'Create associative array
   save_cgi_variables
 End Sub

 '-- Load cgi variables into local storage
 Sub save_cgi_variables
   Dim wshShell,wshUserEnv,strItem,objRegEx,colMatches,pair1,pair2
   Dim method,data_string,variable_array,cgi_variable,decoded_value,i
   Dim key_array,strKey,cookie_array,pairs_split_array,str

   '---Save process cgi variables
   Set wshShell   = CreateObject( "WScript.Shell" )   'Create shell object
   Set wshUserEnv = wshShell.Environment( "Process" ) 'Read process collection

   Set objRegEx = New RegExp                          'Create new reg obj
   objRegEx.Pattern = "^(.+?)=(.+)"                   'Pattern to search for
                                                      '(.+?) non-greedy (.+) greedy
   For Each strItem In wshUserEnv                     'Scan returned collection
    '--Perform regex 
    Set colMatches = objRegEx.Execute(strItem)        'Return collection of Matched objects 
    If colMatches.Count > 0 Then                      'Match found
       pair1 = colMatches(0).SubMatches(0)            'Extract first capturing group
       pair2 = colMatches(0).SubMatches(1)            'Extract second capturing group
       my_hash.Add pair1,pair2                        'Save locally
       'WScript.Echo pair1 & "====" & pair2 & "<br />"'Test code 
    End If
   Next                                               'get next item

   '---Save Post or Get variables
   method         = wshUserEnv("REQUEST_METHOD")      'Get method
   data_string    = wshUserEnv("QUERY_STRING")        'Get query string
   If method="POST" Then                              'If post data read stdin
     data_string  = WScript.Stdin.ReadAll             'Get post data
   End IF
   Set wshShell   = Nothing                           'clean-up
   Set wshUserEnv = Nothing

   variable_array = split (data_string, "&")          'split data save to array

   '--Save internally to my_hash - scripting dictionary
   for i = 0 to ubound(variable_array)                 'Scan array
    cgi_variable = split (variable_array(i), "=")      'Split variable-value pairs
    decoded_value1 = replace(cgi_variable(0), "+", " ")'Replace special character
    decoded_value1 = unescape(decoded_value1)          'Decode encoded value
    decoded_value2 = replace(cgi_variable(1), "+", " ")'Replace special character
    decoded_value2 = unescape(decoded_value2)          'Decode encoded value
    my_hash.add decoded_value1, decoded_value2         'Add to dictionary 
   next                                                'Get next pair

   '--Save any cookie variables
   If my_hash.Exists("HTTP_COOKIE") Then                    'Does cgi_variable HTTP_COOKIE exist 
     cookie_array = split(my_hash.Item("HTTP_COOKIE"), ";") 'Yes: Split variable-value pairs
     For each str in cookie_array                           'Scan cookie pairs
       pairs_split_array = split(str, "=")                  'Split these pairs
       pairs_split_array(0) = Trim(pairs_split_array(0))    'Clean
       pairs_split_array(1) = Trim(pairs_split_array(1))    'Clean
       my_hash.add pairs_split_array(0),pairs_split_array(1)'Add to dictionary 
     Next                                                   'Get next pair 
   End If
 End Sub

 '--Get named variable from my_hash
 Public Function getvar(key)
   getvar = my_hash.Item(key)     'return item
 End Function

 '--List all data in my_hash used for testing
 Public Sub list()
   Dim key_array, strKey
   key_array = my_hash.Keys                           'Get all Keys
   For Each strKey in key_array                       'Scan array
     Wscript.Echo strKey & " = " & my_hash.Item(strKey) & "<br />"
   Next
 End Sub

 '--Clean-up this class
 Private Sub Class_Terminate()
   'Cleaning up code.
   Set my_hash   = Nothing        'clean-up
 End Sub

End Class

Top

Test script 16 - CGI get property class cookie example

This script demonstrates use of cookies and sending data to the server. It requests a user to login with a name allowing the page content to be viewable. Once logged in the user name is stored as a cookie. Every time this page is revisited the cookie is checked, if set there is no need to re-enter a name. This is demonstrated by clicking the Reload Page link several times. Clicking the Reset Cookie link resets the cookie and returns to the login section.

The first thing to note only a single page is used. This is split into three sections:

  • First section:
    This section is displayed if a user is logged in. It is contained in an "if" statement which tests the variable login for true. Displays logged in page content.
  • Second section:
    Displayed if a user is not logged in. It is contained in an "if" statement which tests the variable login for false. Displays the login form.
  • Thirst section:
    This section is always displayed it contains two control links to either reload the page or reset the cookie.

Common to the above is the header section. This always sets a cookie before sending the Content-type. Cookie value is either blank or set to a name provided by the user. Note the cookie value is the result of either a user setting a new name or that of an existing value previously set.


CGI variable processing
The real work is performed by nested "if" statements, these are executed before sending the header. Top-level "if" checks the cookie setting, this can be either blank or set to a user name. Within the nested "ifs" a flag login is set according to the current login status along with user name and cookie value.

  • If cgi.getvar("name") = "" Then
  • Cookie has no value hence not been set.
  • Check if user has used the form
    • cgi.getvar("form_user") = "" Then
    • Variable form_user has no value hence user did not used the form
    • Set login flag to false
    • Else
    • Variable form_user has a value hence user set a name
    • Set login flag to true
    • Set my_cookie variable to CGI variable from_user
    • Also set variable name to match cookie
  • Else
  • Cookie has a value already been set.
  • Check if user wants to delete the cookie
    • If cgi.getvar("reset") = "reset"
    • Variable reset has a value of reset
    • Set login flag to false (will display login page)
    • Set my_cookie variable to blank (returned with page)
    • Else
    • Variable reset has no value
    • Still logged in
    • Set login flag to true
    • Set name to current cookie value
    • Set my_cookie variable to name variable. Refreshes the cookie value.


'!c:/windows/system32/cscript //nologo

<job>
'-- Include any external files
<script language="VBScript" src="common_functions.vbs"/>

'-- Main script ---
<script language="VBScript">
Option Explicit 
Dim cgi,header,name,login,my_cookie

set cgi  = New cgi_get_var_class        'Create new cgi object

If cgi.getvar("name")   = "" Then       'Cookie not set. Not logged in
  If cgi.getvar("form_user") = "" Then  'User has not set name in form
   login = False                        'Reset logged in flag
  Else                                  'User has set new name logged in
    login = True                        'Set logged in flag
    my_cookie = cgi.getvar("form_user") 'Ensure returned page sets cookie 
    name      = my_cookie               'logged in hence use cookie name
  End If
Else                                    'Cookie is set. Logged in
  If cgi.getvar("reset") = "reset" Then 'User wants to reset cookie
   login = False                        'Reset login flag 
   my_cookie = cgi.getvar("")           'Reset cookie
  Else                                  'Reset link not clicked
   login = True                         'Remain logged in
   name      = cgi.getvar("name")       'Get logged in name from cookie 
   my_cookie = name                     'Set cookie (keeps same name) 
  End if
End If

'-- Header common to both sections
  header=""
  header =          "Set-Cookie: name=" & my_cookie &vbLF
  header = header & "Content-type: text/html"&vbLF&vbLF
  Wscript.Echo header
  WScript.Echo "<title>Test 16</title>"


'== Not logged in section ===
If not login then   'Not logged in display login form
  '--Login page uses form method post
  WScript.Echo  "<b>Use name required</b><br />"
  WScript.Echo  "<form method=""POST"" action=""cooki_1.wsf"" >"
  WScript.Echo  "  <input type=""text"" name=""form_user"" value="""">"
  WScript.Echo  "  <input type=""submit"" value=""Post"">"
  WScript.Echo  "</form>"
End If

'=== Logged in section ===
If login Then    'Logged in display this section
  '--Main page when logged in
  Wscript.Echo  "Hello <b>" & name & "</b> Welcome<br /><br />"
  Wscript.Echo  "Click the reload link. No login required<br />"
  Wscript.Echo  "Click the reset link. Cookie reset and<br />"
  Wscript.Echo  "returns to login page (section)<br />"
End If

'--Control for demo always displyed
Wscript.Echo  "<br />======================<br />"
WScript.Echo  "<a href=""cooki_1.wsf"">Reload Page</a>"
Wscript.Echo  "       "
WScript.Echo  "<a href=""cooki_1.wsf?reset=reset"">Reset Cookie</a>"

</script>
</job>

Run script

  • Create a new file cooki_1.wsf with content as shown on right.
  • Save to test folder \www\vbs_test
  • Start Apache if not already running
  • Enter: http://localhost:8081/vbs_test/cooki_1.wsf into browser.

Top

Test script 17 - CGI get property class cookie password example

This script is based on the above and demonstrates use of cookies to remember a password. Only minor modifications are required, it uses a password hard coded into the script however it could equally have been obtained from a database of file.


The password is stored in a cookie unencrypted hence is insecure however it does demonstrate various techniques that may be of use.


Apart from variable name changes the following section hs been added to check the entered password against the hard coded value fred123

 If cgi.getvar("form_password") = pass Then
   login = True                        
   my_cookie = cgi.getvar("form_password")
   my_pass   = my_cookie         
 Else                         
   login = False             
 End if

Note:
The form and control links now point to the new script cooki_2.wsf

  • <form method=""POST"" action=""cooki_2.wsf"" >
  • <a href=""cooki_2.wsf"">Reload Page</a>
  • <a href=""cooki_2.wsf?reset=reset"">Reset Cookie</a>"


Run script

  • Create a new file cooki_2.wsf with content as shown on right.
  • Save to test folder \www\vbs_test
  • Start Apache if not already running
  • Enter: http://localhost:8081/vbs_test/cooki_2.wsf into browser.
  • Note: Password is fred123 try with different passwords
'!c:/windows/system32/cscript //nologo

<job>
'-- Include any external files
<script language="VBScript" src="common_functions.vbs"/>

'-- Main script ---
<script language="VBScript">
Option Explicit 
Dim cgi,header,my_pass,login,my_cookie,pass

pass = "fred123" 'The password is hard coded

set cgi  = New cgi_get_var_class               'Create new cgi object

If cgi.getvar("my_pass")   = "" Then           'Cookie not set. Not logged in
  If cgi.getvar("form_password") = "" Then     'User has not set password in form
   login = False                               'Reset logged in flag
  Else                                         'User has set new password logged in
    If cgi.getvar("form_password") = pass Then 'Check against known password/s
      login = True                             'Set logged in flag
      my_cookie = cgi.getvar("form_password")  'Ensure returned page sets cookie 
      my_pass   = my_cookie                    'logged in hence use cookie password
    Else                                       'Incorrect password
      login = False                            'Reset logged in flag
    End if

  End If
Else                                           'Cookie is set. Logged in
  If cgi.getvar("reset") = "reset" Then        'User wants to reset cookie
   login = False                               'Reset login flag 
   my_cookie = cgi.getvar("")                  'Reset cookie
  Else                                         'Reset link not clicked
   login = True                                'Remain logged in
   my_pass      = cgi.getvar("my_pass")        'Get logged in password from cookie 
   my_cookie = my_pass                         'Set cookie (keeps same password) 
  End if
End If

'-- Header common to both sections
  header=""
  header =          "Set-Cookie: my_pass=" & my_cookie &vbLF
  header = header & "Content-type: text/html"&vbLF&vbLF
  Wscript.Echo header
  WScript.Echo "<title>Test 17</title>"


'== Not logged in section ===
If not login then   'Not logged in display login form
  '--Login page uses form method post
  WScript.Echo  "<b>Password required</b><br />"
  WScript.Echo  "<form method=""POST"" action=""cooki_2.wsf"" >"
  WScript.Echo  "  <input type=""text"" name=""form_password"" value="""">"
  WScript.Echo  "  <input type=""submit"" value=""Post"">"
  WScript.Echo  "</form>"
End If

'=== Logged in section ===
If login Then    'Logged in display this section
  '--Main page when logged in
  Wscript.Echo  "Your password is <b>" & my_pass & "</b> Welcome<br /><br />"
  Wscript.Echo  "Click the reload link. No login required<br />"
  Wscript.Echo  "Click the reset link. Cookie reset and<br />"
  Wscript.Echo  "returns to login page (section)<br />"
End If

'--Control for demo always displyed
Wscript.Echo  "<br />======================<br />"
WScript.Echo  "<a href=""cooki_2.wsf"">Reload Page</a>"
Wscript.Echo  "       "
WScript.Echo  "<a href=""cooki_2.wsf?reset=reset"">Reset Cookie</a>"

</script>
</job>


Top

Summary

The CGI get property class allows you to easily obtain any CGI variable in particular variables sent to a page from a form, an anchor (link) and previously set cookies.

The above two scripts have shown how to use this class for setting and reading cookies and to read data sent from a form and links. The technique above avoids using a separate page for logging in this may be desirable for some applications.

Where to next

Next page covers a File DB class this class allows you to store a database (Scripting Dictionary) to a file.

Top