The Tutorial:
 
 


    Lesson 7: The CGI module

    The most common form of CGI programming handles interaction between user input in an HTML form and a database.  HTML forms consist of several input fields, each given a key identifier, and a submit button that sends the data in a query string.  Fortunately, the built-in Python CGI module parses this information in a class called cgi. The most useful member of this cgi class is the FieldStorage attribute. It returns the values of all the form fields in a object similar to the dictionary type.[4, 2: 276].

    The Official Python Website has a complete description of the CGI module.

    Code Example 7.1 demonstrates a simple use of the cgi class.

    Code Example 7.1: 
    #!/usr/bin/python

    # Import the CGI module
    import cgi

    # Required header that tells the browser how to render the HTML.
    print "Content-Type: text/html\n\n"

    # Define function to generate HTML form.
    def generate_form():
    print "<HTML>\n"
    print "<HEAD>\n"
    print "\t<TITLE>Info Form</TITLE>\n"
    print "</HEAD>\n"
    print "<BODY BGCOLOR = white>\n"
    print "\t<H3>Please, enter your name and age.</H3>\n"
    print "\t<TABLE BORDER = 0>\n"
    print "\t\t<FORM METHOD = post ACTION = \
    \"example_7.1.cgi\">\n"
    print "\t\t<TR><TH>Name:</TH><TD><INPUT type = text \
    name = \"name\"></TD><TR>\n"
    print "\t\t<TR><TH>Age:</TH><TD><INPUT type = text name = \
    \"age\"></TD></TR>\n"
    print "\t</TABLE>\n"
    print "\t<INPUT TYPE = hidden NAME = \"action\" VALUE = \
    \"display\">\n"
    print "\t<INPUT TYPE = submit VALUE = \"Enter\">\n"
    print "\t</FORM>\n"
    print "</BODY>\n"
    print "</HTML>\n"

    # Define function display data.
    def display_data(name, age):
    print "<HTML>\n"
    print "<HEAD>\n"
    print "\t<TITLE>Info Form</TITLE>\n"
    print "</HEAD>\n"
    print "<BODY BGCOLOR = white>\n"
    print name, ", you are", age, "years old."
    print "</BODY>\n"
    print "</HTML>\n"

    # Define main function.
    def main():
    form = cgi.FieldStorage()
    if (form.has_key("action") and form.has_key("name") \
    and form.has_key("age")):
             if (form["action"].value == "display"):
                display_data(form["name"].value, form["age"].value)
    else:
             generate_form()

    # Call main function.
    main()

    Notice that several key features have been introduced in this example. First, in pink, the Content-Type has been changed from text to html. Also, in pink, the ends of five lines each have a '\'. This '\' allows statements to wrap to a second line without affecting the indentation scheme of Python.

    In the example above, we use attributes (FieldStorage, has_key, and value) of the cgi class and the '.' qualifier to access the data sent by the HTML form.  Since the program was relatively long, we divided the code into separate functions, including a main() function. The program works in a recursive fashion by calling itself in the HTML POST form command.  Until the form is completed, the program calls generate_form.  Once all the if statement conditions are met, the data is displayed.

    Login Management and Security

    Retrieving data from HTML fields is useful, but the combination of logging-in users and providing a small level of security can really show the benefits of CGI.  In the example below, we demonstrate a basic method of password protection.  The program uses the unix_md5_crypt() function that takes a password and encryption salt as parameters.  It is located in the module md5crypt.py available from sabren.com.  In Code Example 7.2, the usernames and encrypted passwords are stored in the file passwords.txt.  An entered password is encrypted and tested against the first 20 letters of the encrypted password from the file (Evidently, there is a length limit on string comparisons).  Note that the sys and string modules are imported to manipulate the passwords.txt file and the strings read from the file.  For our example, ab is used as the encryption salt. The correct username is Python, and the correct password is Tutorial.

    Code Example 7.2: 
    #!/usr/bin/python

    # Import the CGI, string, sys, and md5crypt modules
    import cgi, string, sys, md5crypt

    # Required header that tells the browser how to render the HTML.
    print "Content-Type: text/html\n\n"

    # Define function to generate HTML form.
    def generate_form():
    print "<HTML>\n"
    print "<HEAD>\n"
    print "\t<TITLE>Info Form</TITLE>\n"
    print "</HEAD>\n"
    print "<BODY BGCOLOR = white>\n"
    print "\t<H3>Please, enter your username and password.</H3>\n"
    print "\t<TABLE BORDER = 0>\n"
    print "\t\t<FORM METHOD = post ACTION = \
    \"example_7.2.cgi\">\n"
    print "\t\t<TR><TH>Username:</TH><TD><INPUT TYPE = text \
    NAME = \"username\"></TD><TR>\n"
    print "\t\t<TR><TH>Password:</TH><TD><INPUT \
    TYPE = password NAME = \"password\"></TD></TR>\n"
    print "\t</TABLE>\n"
    print "\t<INPUT TYPE = hidden NAME = \"action\" VALUE = \
    \"display\">\n"
    print "\t<INPUT TYPE = submit VALUE = \"Enter\">\n"
    print "\t</FORM>\n"
    print "</BODY>\n"
    print "</HTML>\n"

    # Define function to test the password.
    def test(id, passwd):
    passwd_file = open('passwords.txt', 'r')
    line = passwd_file.readline()
    passwd_file.close()
    combo = string.split(line, ":")
    encrypted_pw = md5crypt.unix_md5_crypt(passwd, 'ab')
    if ((id == combo[0]) and (encrypted_pw[0:20] == combo[1][0:20])):
             return "passed"
    else:
             return "failed"

    # Define function display_page.
    def display_page(result):
    print "<HTML>\n"
    print "<HEAD>\n"
    print "\t<TITLE>Info Form</TITLE>\n"
    print "</HEAD>\n"
    print "<BODY BGCOLOR = white>\n"
    if (result == "passed"):
             print "You entered the correct combo.\n"
    else:
             print "You entered the incorrect combo.\n"
    print "</BODY>\n"
    print "</HTML>\n"

    # Define main function.
    def main():
    form = cgi.FieldStorage()
    if (form.has_key("action") and form.has_key("username") \
    and form.has_key("password")):
             if (form["action"].value == "display"):
                result = test(form["username"].value, form["password"].value)
                display_page(result)
    else:
             generate_form()

    # Call main function.
    main()

    Maintaining Sessions

    Once users have logged into your site, keeping track of their requests and associating the correct request with the correct user poses a challenging problem.  HTML does not provide a method of retaining state.  The status of a user, whether logged in or not, must be transferred from program to program (or recursively back to the same program).

    Assigning each user with a random session key can fix this problem.  The session key, a random string or number, can be used to correlate users and their session data.  After the key is established at login, this user data/key pairing can be stored in a file and accessed by subsequent CGI program requests.  After the user logs out of the site, the pairing should be erased so that future logins are not confused with those of the past.  Additionally, for security reasons, the session key file should be kept safely away from public view.

    Code Example 7.3 adds to the previous examples by allowing a user to remain logged in.  Each time the program executes it tests for the session key and finds its owner.

    Code Example 7.3: 
    #!/usr/bin/python

    # Import the CGI, string, sys, and md5crypt modules
    import cgi, string, sys, md5crypt

    # Required header that tells the browser how to render the HTML.
    print "Content-Type: text/html\n\n"

    # Define function to generate HTML form.
    def generate_form():
    print "<HTML>\n"
    print "<HEAD>\n"
    print "\t<TITLE>Info Form</TITLE>\n"
    print "</HEAD>\n"
    print "<BODY BGCOLOR = white>\n"
    print "\t<H3>Please, enter your username and password.</H3>\n"
    print "\t<TABLE BORDER = 0>\n"
    print "\t\t<FORM METHOD = post ACTION = \
    \"example_7.2.cgi\">\n"
    print "\t\t<TR><TH>Username:</TH><TD><INPUT TYPE = text \
    NAME = \"username\"></TD><TR>\n"
    print "\t\t<TR><TH>Password:</TH><TD><INPUT \
    TYPE = password NAME = \"password\"></TD></TR>\n"
    print "\t</TABLE>\n"
    print "\t<INPUT TYPE = hidden NAME = \"action\" VALUE = \
    \"display\">\n"
    print "\t<INPUT TYPE = submit VALUE = \"Enter\">\n"
    print "\t</FORM>\n"
    print "</BODY>\n"
    print "</HTML>\n"

    # Define function to test the password.
    def test(id, passwd):
    passwd_file = open('passwords.txt', 'r')
    line = passwd_file.readline()
    passwd_file.close()
    combo = string.split(line, ":")
    encrypted_pw = md5crypt.unix_md5_crypt(passwd, 'ab')
    if ((id == combo[0]) and (encrypted_pw[0:20] == combo[1][0:20])):
             return "passed"
    else:
             return "failed"

    # Define function to create a session.
    def create_session(id):
    session_file = open('sessions.txt', 'w')
    # In practice, use the random module for key value.
    session_key = "a12bc78z"
    session_file.write(session_key+":"+id)
    session_file.close()
    return session_key

    # Define a function to return username.
    def fetch_username(key):
    session_file = open('sessions.txt', 'r')
    # In practice, search file for correct key.
    line = session_file.readline()
    session_file.close()
    pair = string.split(line, ":")
    return pair[1]

    # Define function to delete a session.
    def delete_session(id):
    session_file = open('sessions.txt', 'w')
    # In practice, search the file for the correct key.
    # In our example, we just erase the only line in the file.
    session_file.write(" ")
    session_file.close()

    # Define function display_page.
    def display_page(result, id, session_key = 0):
    print "<HTML>\n"
    print "<HEAD>\n"
    print "\t<TITLE>Info Form</TITLE>\n"
    print "</HEAD>\n"
    print "<BODY BGCOLOR = white>\n"
    if (result == "passed"):
             if (session_key == 0):
                session_key = create_session(id)
             print id, , you are logged in with key:", session_key, "\n"
             print "\t\t<FORM METHOD = post ACTION = \
             \"example_7.3.cgi\">\n"
             print "\t\t<INPUT TYPE = \"hidden\" NAME = \
             \"session_key\" VALUE =\", session_key, ">\n"
             print "\t\t<INPUT TYPE = \"submit\" \
             VALUE = \"Stay Logged In.\"></FORM>\n"
             print "\t\t<FORM METHOD = post ACTION = \
             \"example_7.3.cgi\">\n"
             print "\t\t<INPUT TYPE = \"hidden\" NAME = \
             \"session_key\" VALUE =", session_key, ">\n"
             print "\t\t<INPUT TYPE = \"hidden\" NAME =\
             \"logout\" VALUE = \"logout\" >\n"
             print "\t\t<INPUT TYPE = \"submit\" \
             VALUE = \"Logout.\"></FORM>\n"
    else:
             print "You entered the incorrect combo.\n"
    print "</BODY>\n"
    print "</HTML>\n"

    # Define main function.
    def main():
    form = cgi.FieldStorage()
    if (form.has_key("session_key")):
             if (form.has_key("logout")):
                delete_session(form["session_key"].value)
                generate_form()
             else:
                u_id = fetch_username(form["session_key"].value)
                display_page("passed", u_id, form["session_key"].value)
    elif (form.has_key("action") and form.has_key("username") \
    and form.has_key("password")):
             if (form["action"].value == "display"):
                result = test(form["username"].value, form["password"].value)
                display_page(result, form["username"].value)
    else:
             generate_form()

    # Call main function.
    main()

    Correlating Multiple Sessions

    While managing a single session adds to the functionality of a site, correlating these sessions for repeat visitors presents another problem of user account management.  The module Cookie.py offers the ability for Python CGI programs to store HTTP cookies on the clients' local machines. Cookies are persistent data files stored on the client machine through the Web browser.  The Cookie.py module writes "Set-Cookie" headers and parses the HTTP-COOKIE environment variable.  These options create a more standardized method of dealing with client tracking and data retrieval.  As a warning, most Web browsers allow users to reject the use of cookies, and this can break code written with dependencies on cookies.

    Code Example 7.4 shows the syntax for setting and reading a simple cookie.

    Code Example 7.4: 
    #!/usr/bin/python

    # Import the cgi, os, and Cookie modules.
    import cgi, os, Cookie

    # Define function to set a cookie.
    def set_client_Cookie():
    # Create a Cookie object.
    a_cookie = Cookie.SmartCookie()

    # Assign the cookie a value.
    a_cookie["user"] = "Python"

    # Required header that tells the browser how to render the HTML.
    print "Content-Type: text/html"

    # Send the cookie back to the client.
    print a_cookie, "\n\n"

    # Print the cookie value.
    print "<HTML><BODY>"
    print a_cookie["user"].value, "user cookie set.\n"
    print "<FORM METHOD = post ACTION = \"example_7.4.cgi\">\n"
    print "<INPUT TYPE = \"hidden\" NAME = \
    \"set\" VALUE =\"yes\">\n"
    print "<INPUT TYPE = \"submit\" VALUE = \"Go\"></FORM>\n"
    print "</BODY></HTML>\n"

    # Define function to read a cookie.
    def read_client_Cookie():
    # Create a Cookie object.
    a_cookie = Cookie.SmartCookie( os.environ.get("HTTP_COOKIE", "") )

    # Assign the variable a cookie value.
    cookie_val = a_cookie["user"].value

    # Required header that tells the browser how to render the HTML.
    print "Content-Type: text/html\n\n"

    # Print the cookie value.
    print "<HTML><BODY>"
    print cookie_val, "user cookie read from client.\n"
    print "</BODY></HTML>\n"

    # Define main function.
    def main():
    form = cgi.FieldStorage()
    if (form.has_key("set")):
             if (form["set"].value == "yes"):
                      read_client_Cookie()
    else:
             set_client_Cookie()

    # Call main function.
    main()

    While the HTML generation may become clearer with HTML modules and the data storage better with databases, the above examples exhibit the core of CGI programming.

    To Lesson 8 ...

 © 2000 Department of Computer Science, University of Virginia