19. CGI Programming

Introduction

#-----------------------------
# http://www.perl.com/CPAN/
# http://www.perl.com:8001/bad/mojo.html
# ftp://gatekeeper.dec.com/pub/misc/netlib.tar.Z
# ftp://anonymous@myplace:gatekeeper.dec.com/pub/misc/netlib.tar.Z
# file:///etc/motd
#-----------------------------
# http://mox.perl.com/cgi-bin/program?name=Johann&born=1685
#-----------------------------
# http://mox.perl.com/cgi-bin/program
#-----------------------------

Writing a CGI Script

#!/usr/local/bin/ruby -w
# hiweb - load CGI class to decode information given by web server

require 'cgi'

cgi = CGI.new('html3')

# get a parameter from a form
value = cgi.params['PARAM_NAME'][0]

# output a document
cgi.out {
    cgi.html {
        cgi.head { cgi.title { "Howdy there!" } } +
            cgi.body { cgi.p { "You typed: " + cgi.tt {
                    CGI.escapeHTML(value) } } }
    }
}

require 'cgi'
cgi = CGI.new
who   = cgi.param["Name"][0]     # first param in list
phone = cgi.param["Number"][0]
picks = cgi.param["Choices"]     # complete list

print cgi.header( 'type' => 'text/plain',
                  'expires' => Time.now + (3 * 24 * 60 * 60) )

Redirecting Error Messages

Fixing a 500 Server Error

#!/usr/local/bin/ruby -w
# webwhoami - show web user's id
require 'etc'
print "Content-Type: text/plain\n\n"
print "Running as " + Etc.getpwuid.name + "\n"

# % ruby -wc cgi-script     # just check syntax

# % ruby -w  cgi-script     # params from stdin
# (offline mode: enter name=value pairs on standard input)
# name=joe
# number=10
# ^D

# % ruby -w  cgi-script name=joe number=10     # run with mock form input
# % ruby -d  cgi-script name=joe number=10     # ditto, under the debugger

# POST method script in csh
# % (setenv HTTP_METHOD POST; ruby -w cgi-script name=joe number=10)
# POST method script in sh
# % HTTP_METHOD=POST perl -w cgi-script name=joe number=10

Writing a Safe CGI Program

# ruby has several security levels, the level "1" is similar to perls taint mode.
# It can be switched on by providing the -T command line parameter
# or by setting $SAFE to 1. Setting $SAFE to 2,3 or 4 restricts possible
# harmful operations further.

#!/usr/bin/ruby -T
$SAFE = 1
File.open(ARGV[0], "w")
# ruby warns with:
# taint1.rb:2:in `initialize': Insecure operation - initialize (SecurityError)

$SAFE = 1
file = ARGV[0]
unless /^([\w.-]+)$/.match(file)
    raise "filename #{file} has invalid characters"
end
file = $1
# In ruby, even the back reference from a regular expression stays tainted.
# you need to explicitly untaint the variable:
file.untaint
File.open(file, "w")

# Race condition exists like in perl:
unless File.exists(filename)        # Wrong because of race condition
    File.open(filename, "w")
end

Making CGI Scripts Efficient

Executing Commands Without Shell Escapes

Formatting Lists and Tables with HTML Shortcuts

Redirecting to a Different Location

url = "http://pleac.sourceforge.net/pleac_ruby/"
print "Location: #{url}\r\n\r\n"
exit

#!/usr/bin/ruby
require 'cgi'

cgi = CGI.new
oreo = CGI::Cookie.new('name' => 'filling',
                       'value' => 'vanilla creme',
                       'expires' => Time.now + (3 * 30 * 24 * 60 * 60),
                       'domain' => '.pleac.sourceforge.net')

whither = 'http://pleac.sourceforge.net/pleac_ruby/cgiprogramming.html'

cgi.out('cookie' => oreo,
        'Location' => whither){""}

#!/usr/bin/ruby
# os_snipe - redirect to a Jargon File entry about current OS
dir = 'http://www.elsewhere.org/jargon/html/entry'

agent = ENV['HTTP_USER_AGENT']

page = case
    when agent =~ /Mac/: 'Macintrash.html'
    when agent =~ /Win(dows )?NT/: 'evil_and_rude.html'
    when agent =~ /Win|MSIE|WebTV/: 'Microsloth_Windows.html'
    when agent =~ /Linux/: 'Linux.html'
    when agent =~ /HP-UX/: 'HP-SUX.html'
    when agent =~ /SunOS/: 'ScumOS.html'
    else 'Appendix_B.html'
end

print "Location: #{dir}/#{page}\n\n"

require 'cgi'
cgi = CGI.new
cgi.out('status' => '204 No response'){""}
# this produces:
# Status: 204 No response
# Content-Type: text/html
# Content-Length: 0
# <blank line here>

Debugging the Raw HTTP Exchange

Managing Cookies

preference_value = cgi.cookies["preference name"][0]

packed_cookie = CGI::Cookie.new("name" => "preference name",
                                "value" => "whatever you'd like",
                                "expires" => Time.local(Time.now.year + 2,
    Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )

cgi.header("cookie" => [packed_cookie])

#!/usr/local/bin/ruby -w
# ic_cookies - sample CGI script that uses a cookie
require 'cgi'

cgi = CGI.new('html3')

cookname = "favorite ice cream"
favorite = cgi.params["flavor"][0]
tasty    = cgi.cookies[cookname][0] || 'mint'

unless favorite
    cgi.out {
        cgi.html {
            cgi.head { cgi.title { "Ice Cookies" } } +
            cgi.body {
                cgi.h1 { "Hello Ice Cream" } +
                cgi.hr +
                cgi.form {
                    cgi.p { "Please select a flavor: " +
                            cgi.text_field("flavor", tasty ) }
                } +
                cgi.hr
            }
        }
    }
else
    cookie = CGI::Cookie.new( "name"    => cookname,
                              "value"   => favorite,
                              "expires" => Time.local(Time.now.year + 2,
Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )
    cgi.out("cookie" => [cookie]) {
        cgi.html {
            cgi.head { cgi.title { "Ice Cookies" } } +
            cgi.body {
                cgi.h1 { "Hello Ice Cream" } +
                cgi.p { "You chose as your favorite flavor `#{favorite}'." }
            }
        }
    }
end

Creating Sticky Widgets

Writing a Multiscreen CGI Script

Saving a Form to a File or Mail Pipe

Program: chemiserie