Rubycam.rb v 1.7 CGI webcam script for use with cqcam, etc


(Phil) #1

I don鈥檛 want to mess with Sourceforge, so consider this my form of
dissemination in case the server disappears some time. Please forward
mods to the email address listed under AUTHOR below. PS: New to ruby
when I wrote it - now have RSI - probably not going to make it very
slick. :wink:

路路路

#!/usr/bin/env ruby

########################################################################

VERSION: $Id: rubycam.rb,v 1.7 2002/03/22 20:08:52 rubycam Exp $

AUTHOR: Phil Voris

LICENSE: Ruby License - must include this header comment block

http://www.ruby-lang.org/en/LICENSE.txt

DESCRIPTION:

This CGI webcam script attempts to conserve resources by not

creating images more often than the number of seconds indicated by

the refresh setting. Maximum flexibility is given by allowing the

administrator to set the image-producing command line. The

defaults are arranged to use cqcam

(http://www.cs.duke.edu/~reynolds/cqcam/).

REQUIRES: ruby

ruby modules: net/smtp (for error reporting), ftools

rb2html.rb (for viewing source)

some image-grabber, such as cqcam

optionally, some image manipulation software

USAGE:

* Install under cgi-bin or wherever cgi executables are allowed.

* Configure the global config section below.

* Configure the default config section below.

* Optionally create one or more config files to call in the PATH_INFO.

* Ensure that img_file_web_directory exists and is writable by the

web server

* [See end of script for recommendations for config and calling from

html.]

TO DO:

It would be nice to add caching. That is, deliver a header

indicating that the page (the image) hasn鈥檛 changed until it

actually does. I attempted this, but found that

HTTP_IF_MODIFIED_SINCE didn鈥檛 appear in the environment. I then

attempted to use a session to track if the use had seen the page

with the current image - however sessions weren鈥檛 quite working for

me - perhaps someone else can resolve this issue.

########################################################################

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

GLOBAL CONFIGURATION - EDIT HERE

These options must be set

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

allow options from URL (true / false)

use_config_files = true

img file directory [relative to DOC_ROOT]

img_file_web_directory = 鈥/img/cam鈥

configuration files directory

config_dir = 鈥./config鈥

directory where rb2html and associated files may be found

(used for /view)

rb2html_directory = 鈥./rb2html-1.0鈥

file base-name

lock_filename = 鈥榬ubycam.lock鈥

maximum age to keep a lock file (seconds, must exceed refresh time)

max_lockfile_age = 500

shows a little 庐 which links to the source. Specify the target

frame (鈥檁new鈥, 鈥榑self鈥, etc) or nil to not display it.

source_link_target = 鈥榑new鈥

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

END GLOBAL CONFIGURATION

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

PER-CAM CONFIGURATION - SET DEFAULTS HERE

- MAY MODIFY IN CONFIG FILES

Anything listed in this section may be set - as below - in a

separate file.

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

default_cqcam_opts

cmd_line = 鈥榗qcam -r -q 25 -s 1 -j鈥

filename for the images

img_filename = 鈥榩lain.jpg鈥

admin email address

admin_email_address = 鈥榳ebmaster@localhost鈥

page title

title = 鈥楻ubycam鈥

background (color)

bgcolor = 鈥榖lack鈥

text (color)

text_color = 鈥榶ellow鈥

browser refresh length (in seconds)

refresh = 60

img_background_color (color | nil to disable globally)

img_background_color = nil

randomize transparency_background_color (true / false)

use_randomize_img_background_color = false

randomize transparency_background_color (true / false)

use_randomize_text_color = false

match text and img backgrounds - useful for randomization

(true / false)

text_color_matches_img_background_color = false

color for the source link, if it appears (color)

source_link_color = 鈥榙arkred鈥

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

END PER-CAM CONFIGURATION

#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

##########################################

PROCESS URL OPTIONS FROM PATH_INFO

##########################################

If the script is called from a URL with path info equal to 鈥榲iew鈥 or

鈥榞et鈥 the contents of the script will be displayed. 鈥榞et鈥 results

in text/plain output while view - relying on rb2html - output color-

coded html output with line numbers.

path_info = ENV[鈥楶ATH_INFO鈥橾.untaint

if path_info == 鈥/get鈥

Print source

puts( 鈥淐ontent-type: text/plain\n\n鈥 )
IO::foreach( ENV[鈥楽CRIPT_FILENAME鈥橾.untaint ) { |l|
puts l
}
exit
elsif path_info == 鈥/view鈥

Print formatted source

puts( 鈥淐ontent-type: text/html\n\n鈥 )
rb2html_executable = rb2html_directory + 鈥/rb2html.rb鈥
if File::file?( rb2html_executable )
begin
htmlpipe = IO::popen( 鈥榬uby 鈥 + rb2html_executable + 鈥 鈥 +
ENV[鈥楽CRIPT_FILENAME鈥橾.untaint )
puts( htmlpipe.readlines() )
rescue SystemCallError
## Mail admin if any errors - if you don鈥檛 want mail, change
## the admin address.
require 鈥榥et/smtp鈥
smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
smtp.start
body = "Error:\nruby " + rb2html_executable + 鈥 鈥 +
ENV[鈥楽CRIPT_FILENAME鈥橾.untaint
smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥, admin_email_address )
smtp.finish
end
exit
else
puts( 鈥淐ontent-type: text/plain\n\n鈥 )
puts( 鈥淓rror: cannot execute " + rb2html_executable )
exit
end
elsif path_info != nil
if not File::file?( config_dir + path_info )
## Print error
puts( 鈥淐ontent-type: text/plain\n\n鈥 )
puts( 鈥淓rror: invalid config file: '鈥 + config_dir + path_info +
鈥濃橽n" +
鈥淪et the PATH_INFO (in the url) to a valid config file name.鈥 )
exit
elsif path_info.index( 鈥樷/鈥 ) != nil
## Print error
puts( 鈥淐ontent-type: text/plain\n\n鈥 )
puts( 鈥淓rror: invalid config file: '鈥 + config_dir + path_info +
"鈥橽n" +
"鈥欌/鈥 not permitted in config file pathname." )
exit
elsif not File::readable?( config_dir + path_info )
## Print error
puts( 鈥淐ontent-type: text/plain\n\n鈥 )
puts( 鈥淓rror: unreadable config file: '鈥 + config_dir + path_info +
"鈥" )
exit
else
## Process config file
config_file_string = ''
File::open( config_dir + path_info ).readlines.each do |line|
config_file_string.concat( line )
end
eval config_file_string
end
end

###########################

DETERMINE FILE INFO

###########################

First, we determine the colors to be used for text (the image time)

and for the background of the image (not the page bgcolor).

color_hash = {
0 => 鈥榳hite鈥,
1 => 鈥榬ed鈥,
2 => 鈥榦range鈥,
3 => 鈥榶ellow鈥,
4 => 鈥榞reen鈥,
5 => 鈥榖lue鈥,
6 => 鈥榩urple鈥,
7 => 鈥榣ightgreen鈥,
8 => 鈥榩ink鈥,
9 => 鈥榣ightyellow鈥,
10 => 鈥榣ightblue鈥,
11 => 鈥榣ightgray鈥,
12 => 鈥榞ray鈥
}

If img_background_color is set to nil, then no image background

color options will apply.

if img_background_color != nil and
use_randomize_img_background_color
img_background_color = color_hash[rand(13)]
end

If randomization is used for text and image, then the image

background color is used for both.

if use_randomize_text_color and
not text_color_matches_img_background_color
text_color = color_hash[rand(13)]
elsif ( img_background_color != nil ) and
text_color_matches_img_background_color
text_color = img_background_color
end

img_file_webpath = img_file_web_directory + 鈥/鈥 + img_filename
img_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_webpath
img_file_pathname.untaint
img_file_tmp_pathname = img_file_pathname + 鈥.tmp鈥

Time

time_now = Time::now

Mtime

if File::exist?( img_file_pathname )
img_file_mtime = File::mtime( img_file_pathname )
img_file_mtime_int = img_file_mtime.to_i
img_file_mtime_string = img_file_mtime.to_s
img_file_age = time_now.to_i - img_file_mtime_int
end

###########################

LOCKFILE CODE

###########################

Lock file name is global - there is only one camera and it can take

only one picture at a time. The lockfile is set during this process.

lock_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_web_directory + 鈥/鈥 +
lock_filename
lock_file_pathname.untaint

If the lockfile somehow gets stale, it will be removed.

locked = false
if File::exist?( lock_file_pathname )
if ( File::mtime( lock_file_pathname ) < ( time_now - (
max_lockfile_age ) ) )
File::unlink( lock_file_pathname )
else
locked = true
end
end

###########################

EXECUTE

###########################

If there鈥檚 no image, or it鈥檚 an old one, and the camera is free,

then we take a new picture.

if ( ( not File::exist?( img_file_pathname ) ) or
( img_file_age >= refresh ) ) and
( not locked )

lockfile = File::new( lock_file_pathname, mode=鈥榳鈥 )

begin
## Execute
img_file = File::open( img_file_tmp_pathname, mode=鈥榳鈥).write(
IO::popen( cmd_line ).read() )
require 'ftools鈥
File::mv( img_file_tmp_pathname, img_file_pathname )
File::unlink( lock_file_pathname )
img_file_mtime = Time::now
img_file_mtime_string = img_file_mtime.to_s
rescue SystemCallError
## Mail admin if any errors - if you don鈥檛 want mail, change
## the admin address.
require 'net/smtp鈥
smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
smtp.start
body = 鈥淪ubject: Error with rubycam\n鈥 +
鈥淔ailed to execute:\n鈥 + cmd_line + "\n\nSee logs for details."
smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥, admin_email_address )
smtp.finish
File::unlink( lock_file_pathname )
end

end

###########################

HTML

###########################

puts( 鈥淐ontent-type: text/html\n鈥 +
'Last-Modified: 鈥 + img_file_mtime_string + 鈥淺n鈥 +
'Expires: 鈥 + ( img_file_mtime + refresh ).to_s +
"\n\n" )

puts( 鈥樷 + title + 鈥樷 +
鈥欌 )

puts( 鈥樷 )

puts( 鈥

<td鈥 )

if img_background_color != nil
puts( 鈥 bgcolor="鈥 + img_background_color + 鈥"鈥 )
end

puts( 鈥>

鈥 )

puts( 鈥

鈥 + img_file_mtime_string + 鈥鈥 )

if source_link_target != nil
puts( 鈥 鈥 )
end

puts( 鈥樷 )

###########################

CALLING FROM HTML

###########################

Include the following in your page head:

Call within body of page as:

  • <a

    href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb鈥);鈥>Webcam

  • <a

    href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb/transgif.conf鈥);鈥>Psychedelicam

  • <a

    href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb/greyjpeg.conf鈥);鈥>Greycam

  • Formatted

    Source

  • Source

    ###########################

    CONFIG FILE # 1

    ###########################

    greyjpeg.conf - produces a grey jpeg

    cmd_line = 'cqcam -s 1 | ppmtojpeg --progressive --greyscale

    鈥搊ptimize --quality=33 --comment=鈥淢ade with Rubycam鈥濃

    img_filename = 鈥榞rey.jpg鈥

    title = 鈥楪reycam鈥

    refresh = 60

    ###########################

    CONFIG FILE # 2

    ###########################

    transgif.conf - produces a transparent gif with weird effects

    cmd_line = 'cqcam -s 1 | ppmquant 2 | ppmtogif -transparent grey

    -interlace -sort -comment=鈥淢ade with Rubycam鈥濃

    img_filename = 鈥榯ransparent.gif鈥

    title = 鈥楶sychedelicam鈥

    refresh = 60

    img_background_color = 鈥榬ed鈥

    use_randomize_img_background_color = true

    use_randomize_text_color = true

    text_color_matches_img_background_color = true

    EOF

    #!/usr/bin/env ruby

    ########################################################################

    VERSION: $Id: rubycam.rb,v 1.7 2002/03/22 20:08:52 pvoris Exp $

    AUTHOR: Phil Voris rubycam@nekophile.com

    LICENSE: Ruby License - must include this header comment block

    http://www.ruby-lang.org/en/LICENSE.txt

    DESCRIPTION:

    This CGI webcam script attempts to conserve resources by not

    creating images more often than the number of seconds indicated by

    the refresh setting. Maximum flexibility is given by allowing the

    administrator to set the image-producing command line. The

    defaults are arranged to use cqcam

    (http://www.cs.duke.edu/~reynolds/cqcam/).

    REQUIRES: ruby

    ruby modules: net/smtp (for error reporting), ftools

    rb2html.rb (for viewing source)

    some image-grabber, such as cqcam

    optionally, some image manipulation software

    USAGE:

    * Install under cgi-bin or wherever cgi executables are allowed.

    * Configure the global config section below.

    * Configure the default config section below.

    * Optionally create one or more config files to call in the PATH_INFO.

    * Ensure that img_file_web_directory exists and is writable by the

    web server

    * [See end of script for recommendations for config and calling from

    html.]

    TO DO:

    It would be nice to add caching. That is, deliver a header

    indicating that the page (the image) hasn鈥檛 changed until it

    actually does. I attempted this, but found that

    HTTP_IF_MODIFIED_SINCE didn鈥檛 appear in the environment. I then

    attempted to use a session to track if the use had seen the page

    with the current image - however sessions weren鈥檛 quite working for

    me - perhaps someone else can resolve this issue.

    ########################################################################

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    GLOBAL CONFIGURATION - EDIT HERE

    These options must be set

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    allow options from URL (true / false)

    use_config_files = true

    img file directory [relative to DOC_ROOT]

    img_file_web_directory = 鈥/img/cam鈥

    configuration files directory

    config_dir = 鈥./config鈥

    directory where rb2html and associated files may be found

    (used for /view)

    rb2html_directory = 鈥./rb2html-1.0鈥

    file base-name

    lock_filename = 鈥榬ubycam.lock鈥

    maximum age to keep a lock file (seconds, must exceed refresh time)

    max_lockfile_age = 500

    shows a little 庐 which links to the source. Specify the target

    frame (鈥檁new鈥, 鈥榑self鈥, etc) or nil to not display it.

    source_link_target = 鈥榑new鈥

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    END GLOBAL CONFIGURATION

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    PER-CAM CONFIGURATION - SET DEFAULTS HERE

    - MAY MODIFY IN CONFIG FILES

    Anything listed in this section may be set - as below - in a

    separate file.

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    default_cqcam_opts

    cmd_line = 鈥榗qcam -r -q 25 -s 1 -j鈥

    filename for the images

    img_filename = 鈥榩lain.jpg鈥

    admin email address

    admin_email_address = 鈥榳ebmaster@localhost鈥

    page title

    title = 鈥楻ubycam鈥

    background (color)

    bgcolor = 鈥榖lack鈥

    text (color)

    text_color = 鈥榶ellow鈥

    browser refresh length (in seconds)

    refresh = 60

    img_background_color (color | nil to disable globally)

    img_background_color = nil

    randomize transparency_background_color (true / false)

    use_randomize_img_background_color = false

    randomize transparency_background_color (true / false)

    use_randomize_text_color = false

    match text and img backgrounds - useful for randomization

    (true / false)

    text_color_matches_img_background_color = false

    color for the source link, if it appears (color)

    source_link_color = 鈥榙arkred鈥

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    END PER-CAM CONFIGURATION

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    ##########################################

    PROCESS URL OPTIONS FROM PATH_INFO

    ##########################################

    If the script is called from a URL with path info equal to 鈥榲iew鈥 or

    鈥榞et鈥 the contents of the script will be displayed. 鈥榞et鈥 results

    in text/plain output while view - relying on rb2html - output color-

    coded html output with line numbers.

    path_info = ENV[鈥楶ATH_INFO鈥橾.untaint

    if path_info == 鈥/get鈥

    Print source

    puts( 鈥淐ontent-type: text/plain\n\n鈥 )
    IO::foreach( ENV[鈥楽CRIPT_FILENAME鈥橾.untaint ) { |l|
    puts l
    }
    exit
    elsif path_info == 鈥/view鈥

    Print formatted source

    puts( 鈥淐ontent-type: text/html\n\n鈥 )
    rb2html_executable = rb2html_directory + 鈥/rb2html.rb鈥
    if File::file?( rb2html_executable )
    begin
    htmlpipe = IO::popen( 鈥榬uby 鈥 + rb2html_executable + 鈥 鈥 +
    ENV[鈥楽CRIPT_FILENAME鈥橾.untaint )
    puts( htmlpipe.readlines() )
    rescue SystemCallError
    ## Mail admin if any errors - if you don鈥檛 want mail, change
    ## the admin address.
    require 鈥榥et/smtp鈥
    smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
    smtp.start
    body = "Error:\nruby " + rb2html_executable + 鈥 鈥 +
    ENV[鈥楽CRIPT_FILENAME鈥橾.untaint
    smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥, admin_email_address )
    smtp.finish
    end
    exit
    else
    puts( 鈥淐ontent-type: text/plain\n\n鈥 )
    puts( 鈥淓rror: cannot execute " + rb2html_executable )
    exit
    end
    elsif path_info != nil
    if not File::file?( config_dir + path_info )
    ## Print error
    puts( 鈥淐ontent-type: text/plain\n\n鈥 )
    puts( 鈥淓rror: invalid config file: '鈥 + config_dir + path_info +
    鈥濃橽n" +
    鈥淪et the PATH_INFO (in the url) to a valid config file name.鈥 )
    exit
    elsif path_info.index( 鈥樷/鈥 ) != nil
    ## Print error
    puts( 鈥淐ontent-type: text/plain\n\n鈥 )
    puts( 鈥淓rror: invalid config file: '鈥 + config_dir + path_info +
    "鈥橽n" +
    "鈥欌/鈥 not permitted in config file pathname." )
    exit
    elsif not File::readable?( config_dir + path_info )
    ## Print error
    puts( 鈥淐ontent-type: text/plain\n\n鈥 )
    puts( 鈥淓rror: unreadable config file: '鈥 + config_dir + path_info +
    "鈥" )
    exit
    else
    ## Process config file
    config_file_string = ''
    File::open( config_dir + path_info ).readlines.each do |line|
    config_file_string.concat( line )
    end
    eval config_file_string
    end
    end

    ###########################

    DETERMINE FILE INFO

    ###########################

    First, we determine the colors to be used for text (the image time)

    and for the background of the image (not the page bgcolor).

    color_hash = {
    0 => 鈥榳hite鈥,
    1 => 鈥榬ed鈥,
    2 => 鈥榦range鈥,
    3 => 鈥榶ellow鈥,
    4 => 鈥榞reen鈥,
    5 => 鈥榖lue鈥,
    6 => 鈥榩urple鈥,
    7 => 鈥榣ightgreen鈥,
    8 => 鈥榩ink鈥,
    9 => 鈥榣ightyellow鈥,
    10 => 鈥榣ightblue鈥,
    11 => 鈥榣ightgray鈥,
    12 => 鈥榞ray鈥
    }

    If img_background_color is set to nil, then no image background

    color options will apply.

    if img_background_color != nil and
    use_randomize_img_background_color
    img_background_color = color_hash[rand(13)]
    end

    If randomization is used for text and image, then the image

    background color is used for both.

    if use_randomize_text_color and
    not text_color_matches_img_background_color
    text_color = color_hash[rand(13)]
    elsif ( img_background_color != nil ) and
    text_color_matches_img_background_color
    text_color = img_background_color
    end

    img_file_webpath = img_file_web_directory + 鈥/鈥 + img_filename
    img_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_webpath
    img_file_pathname.untaint
    img_file_tmp_pathname = img_file_pathname + 鈥.tmp鈥

    Time

    time_now = Time::now

    Mtime

    if File::exist?( img_file_pathname )
    img_file_mtime = File::mtime( img_file_pathname )
    img_file_mtime_int = img_file_mtime.to_i
    img_file_mtime_string = img_file_mtime.to_s
    img_file_age = time_now.to_i - img_file_mtime_int
    end

    ###########################

    LOCKFILE CODE

    ###########################

    Lock file name is global - there is only one camera and it can take

    only one picture at a time. The lockfile is set during this process.

    lock_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_web_directory + 鈥/鈥 +
    lock_filename
    lock_file_pathname.untaint

    If the lockfile somehow gets stale, it will be removed.

    locked = false
    if File::exist?( lock_file_pathname )
    if ( File::mtime( lock_file_pathname ) < ( time_now - (
    max_lockfile_age ) ) )
    File::unlink( lock_file_pathname )
    else
    locked = true
    end
    end

    ###########################

    EXECUTE

    ###########################

    If there鈥檚 no image, or it鈥檚 an old one, and the camera is free,

    then we take a new picture.

    if ( ( not File::exist?( img_file_pathname ) ) or
    ( img_file_age >= refresh ) ) and
    ( not locked )

    lockfile = File::new( lock_file_pathname, mode=鈥榳鈥 )

    begin
    ## Execute
    img_file = File::open( img_file_tmp_pathname, mode=鈥榳鈥).write(
    IO::popen( cmd_line ).read() )
    require 'ftools鈥
    File::mv( img_file_tmp_pathname, img_file_pathname )
    File::unlink( lock_file_pathname )
    img_file_mtime = Time::now
    img_file_mtime_string = img_file_mtime.to_s
    rescue SystemCallError
    ## Mail admin if any errors - if you don鈥檛 want mail, change
    ## the admin address.
    require 'net/smtp鈥
    smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
    smtp.start
    body = 鈥淪ubject: Error with rubycam\n鈥 +
    鈥淔ailed to execute:\n鈥 + cmd_line + "\n\nSee logs for details."
    smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥, admin_email_address )
    smtp.finish
    File::unlink( lock_file_pathname )
    end

    end

    ###########################

    HTML

    ###########################

    puts( 鈥淐ontent-type: text/html\n鈥 +
    'Last-Modified: 鈥 + img_file_mtime_string + 鈥淺n鈥 +
    'Expires: 鈥 + ( img_file_mtime + refresh ).to_s +
    "\n\n" )

    puts( 鈥樷 + title + 鈥樷 +
    鈥欌 )

    puts( 鈥樷 )

    puts( 鈥

    <td鈥 )

    if img_background_color != nil
    puts( 鈥 bgcolor="鈥 + img_background_color + 鈥"鈥 )
    end

    puts( 鈥>

    鈥 )

    puts( 鈥

    鈥 + img_file_mtime_string + 鈥鈥 )

    if source_link_target != nil
    puts( 鈥 鈥 )
    end

    puts( 鈥樷 )

    ###########################

    CALLING FROM HTML

    ###########################

    Include the following in your page head:

    Call within body of page as:

    • <a

      href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb鈥);鈥>Webcam

    • <a

      href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb/transgif.conf鈥);鈥>Psychedelicam

    • <a

      href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb/greyjpeg.conf鈥);鈥>Greycam

    • Formatted

      Source

    • Source

      ###########################

      CONFIG FILE # 1

      ###########################

      greyjpeg.conf - produces a grey jpeg

      cmd_line = 'cqcam -s 1 | ppmtojpeg --progressive --greyscale

      鈥搊ptimize --quality=33 --comment=鈥淢ade with Rubycam鈥濃

      img_filename = 鈥榞rey.jpg鈥

      title = 鈥楪reycam鈥

      refresh = 60

      ###########################

      CONFIG FILE # 2

      ###########################

      transgif.conf - produces a transparent gif with weird effects

      cmd_line = 'cqcam -s 1 | ppmquant 2 | ppmtogif -transparent grey

      -interlace -sort -comment=鈥淢ade with Rubycam鈥濃

      img_filename = 鈥榯ransparent.gif鈥

      title = 鈥楶sychedelicam鈥

      refresh = 60

      img_background_color = 鈥榬ed鈥

      use_randomize_img_background_color = true

      use_randomize_text_color = true

      text_color_matches_img_background_color = true

      EOF

      #!/usr/bin/env ruby

      ########################################################################

      VERSION: $Id: rubycam.rb,v 1.7 2002/03/22 20:08:52 pvoris Exp $

      AUTHOR: Phil Voris rubycam@nekophile.com

      LICENSE: Ruby License - must include this header comment block

      http://www.ruby-lang.org/en/LICENSE.txt

      DESCRIPTION:

      This CGI webcam script attempts to conserve resources by not

      creating images more often than the number of seconds indicated by

      the refresh setting. Maximum flexibility is given by allowing the

      administrator to set the image-producing command line. The

      defaults are arranged to use cqcam

      (http://www.cs.duke.edu/~reynolds/cqcam/).

      REQUIRES: ruby

      ruby modules: net/smtp (for error reporting), ftools

      rb2html.rb (for viewing source)

      some image-grabber, such as cqcam

      optionally, some image manipulation software

      USAGE:

      * Install under cgi-bin or wherever cgi executables are allowed.

      * Configure the global config section below.

      * Configure the default config section below.

      * Optionally create one or more config files to call in the PATH_INFO.

      * Ensure that img_file_web_directory exists and is writable by the

      web server

      * [See end of script for recommendations for config and calling from

      html.]

      TO DO:

      It would be nice to add caching. That is, deliver a header

      indicating that the page (the image) hasn鈥檛 changed until it

      actually does. I attempted this, but found that

      HTTP_IF_MODIFIED_SINCE didn鈥檛 appear in the environment. I then

      attempted to use a session to track if the use had seen the page

      with the current image - however sessions weren鈥檛 quite working for

      me - perhaps someone else can resolve this issue.

      ########################################################################

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      GLOBAL CONFIGURATION - EDIT HERE

      These options must be set

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      allow options from URL (true / false)

      use_config_files = true

      img file directory [relative to DOC_ROOT]

      img_file_web_directory = 鈥/img/cam鈥

      configuration files directory

      config_dir = 鈥./config鈥

      directory where rb2html and associated files may be found

      (used for /view)

      rb2html_directory = 鈥./rb2html-1.0鈥

      file base-name

      lock_filename = 鈥榬ubycam.lock鈥

      maximum age to keep a lock file (seconds, must exceed refresh time)

      max_lockfile_age = 500

      shows a little 庐 which links to the source. Specify the target

      frame (鈥檁new鈥, 鈥榑self鈥, etc) or nil to not display it.

      source_link_target = 鈥榑new鈥

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      END GLOBAL CONFIGURATION

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      PER-CAM CONFIGURATION - SET DEFAULTS HERE

      - MAY MODIFY IN CONFIG FILES

      Anything listed in this section may be set - as below - in a

      separate file.

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      default_cqcam_opts

      cmd_line = 鈥榗qcam -r -q 25 -s 1 -j鈥

      filename for the images

      img_filename = 鈥榩lain.jpg鈥

      admin email address

      admin_email_address = 鈥榳ebmaster@localhost鈥

      page title

      title = 鈥楻ubycam鈥

      background (color)

      bgcolor = 鈥榖lack鈥

      text (color)

      text_color = 鈥榶ellow鈥

      browser refresh length (in seconds)

      refresh = 60

      img_background_color (color | nil to disable globally)

      img_background_color = nil

      randomize transparency_background_color (true / false)

      use_randomize_img_background_color = false

      randomize transparency_background_color (true / false)

      use_randomize_text_color = false

      match text and img backgrounds - useful for randomization

      (true / false)

      text_color_matches_img_background_color = false

      color for the source link, if it appears (color)

      source_link_color = 鈥榙arkred鈥

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      END PER-CAM CONFIGURATION

      #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      ##########################################

      PROCESS URL OPTIONS FROM PATH_INFO

      ##########################################

      If the script is called from a URL with path info equal to 鈥榲iew鈥 or

      鈥榞et鈥 the contents of the script will be displayed. 鈥榞et鈥 results

      in text/plain output while view - relying on rb2html - output color-

      coded html output with line numbers.

      path_info = ENV[鈥楶ATH_INFO鈥橾.untaint

      if path_info == 鈥/get鈥

      Print source

      puts( 鈥淐ontent-type: text/plain\n\n鈥 )
      IO::foreach( ENV[鈥楽CRIPT_FILENAME鈥橾.untaint ) { |l|
      puts l
      }
      exit
      elsif path_info == 鈥/view鈥

      Print formatted source

      puts( 鈥淐ontent-type: text/html\n\n鈥 )
      rb2html_executable = rb2html_directory + 鈥/rb2html.rb鈥
      if File::file?( rb2html_executable )
      begin
      htmlpipe = IO::popen( 鈥榬uby 鈥 + rb2html_executable + 鈥 鈥 +
      ENV[鈥楽CRIPT_FILENAME鈥橾.untaint )
      puts( htmlpipe.readlines() )
      rescue SystemCallError
      ## Mail admin if any errors - if you don鈥檛 want mail, change
      ## the admin address.
      require 鈥榥et/smtp鈥
      smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
      smtp.start
      body = "Error:\nruby " + rb2html_executable + 鈥 鈥 +
      ENV[鈥楽CRIPT_FILENAME鈥橾.untaint
      smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥, admin_email_address )
      smtp.finish
      end
      exit
      else
      puts( 鈥淐ontent-type: text/plain\n\n鈥 )
      puts( 鈥淓rror: cannot execute " + rb2html_executable )
      exit
      end
      elsif path_info != nil
      if not File::file?( config_dir + path_info )
      ## Print error
      puts( 鈥淐ontent-type: text/plain\n\n鈥 )
      puts( 鈥淓rror: invalid config file: '鈥 + config_dir + path_info +
      鈥濃橽n" +
      鈥淪et the PATH_INFO (in the url) to a valid config file name.鈥 )
      exit
      elsif path_info.index( 鈥樷/鈥 ) != nil
      ## Print error
      puts( 鈥淐ontent-type: text/plain\n\n鈥 )
      puts( 鈥淓rror: invalid config file: '鈥 + config_dir + path_info +
      "鈥橽n" +
      "鈥欌/鈥 not permitted in config file pathname." )
      exit
      elsif not File::readable?( config_dir + path_info )
      ## Print error
      puts( 鈥淐ontent-type: text/plain\n\n鈥 )
      puts( 鈥淓rror: unreadable config file: '鈥 + config_dir + path_info +
      "鈥" )
      exit
      else
      ## Process config file
      config_file_string = ''
      File::open( config_dir + path_info ).readlines.each do |line|
      config_file_string.concat( line )
      end
      eval config_file_string
      end
      end

      ###########################

      DETERMINE FILE INFO

      ###########################

      First, we determine the colors to be used for text (the image time)

      and for the background of the image (not the page bgcolor).

      color_hash = {
      0 => 鈥榳hite鈥,
      1 => 鈥榬ed鈥,
      2 => 鈥榦range鈥,
      3 => 鈥榶ellow鈥,
      4 => 鈥榞reen鈥,
      5 => 鈥榖lue鈥,
      6 => 鈥榩urple鈥,
      7 => 鈥榣ightgreen鈥,
      8 => 鈥榩ink鈥,
      9 => 鈥榣ightyellow鈥,
      10 => 鈥榣ightblue鈥,
      11 => 鈥榣ightgray鈥,
      12 => 鈥榞ray鈥
      }

      If img_background_color is set to nil, then no image background

      color options will apply.

      if img_background_color != nil and
      use_randomize_img_background_color
      img_background_color = color_hash[rand(13)]
      end

      If randomization is used for text and image, then the image

      background color is used for both.

      if use_randomize_text_color and
      not text_color_matches_img_background_color
      text_color = color_hash[rand(13)]
      elsif ( img_background_color != nil ) and
      text_color_matches_img_background_color
      text_color = img_background_color
      end

      img_file_webpath = img_file_web_directory + 鈥/鈥 + img_filename
      img_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_webpath
      img_file_pathname.untaint
      img_file_tmp_pathname = img_file_pathname + 鈥.tmp鈥

      Time

      time_now = Time::now

      Mtime

      if File::exist?( img_file_pathname )
      img_file_mtime = File::mtime( img_file_pathname )
      img_file_mtime_int = img_file_mtime.to_i
      img_file_mtime_string = img_file_mtime.to_s
      img_file_age = time_now.to_i - img_file_mtime_int
      end

      ###########################

      LOCKFILE CODE

      ###########################

      Lock file name is global - there is only one camera and it can take

      only one picture at a time. The lockfile is set during this process.

      lock_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_web_directory + 鈥/鈥 +
      lock_filename
      lock_file_pathname.untaint

      If the lockfile somehow gets stale, it will be removed.

      locked = false
      if File::exist?( lock_file_pathname )
      if ( File::mtime( lock_file_pathname ) < ( time_now - (
      max_lockfile_age ) ) )
      File::unlink( lock_file_pathname )
      else
      locked = true
      end
      end

      ###########################

      EXECUTE

      ###########################

      If there鈥檚 no image, or it鈥檚 an old one, and the camera is free,

      then we take a new picture.

      if ( ( not File::exist?( img_file_pathname ) ) or
      ( img_file_age >= refresh ) ) and
      ( not locked )

      lockfile = File::new( lock_file_pathname, mode=鈥榳鈥 )

      begin
      ## Execute
      img_file = File::open( img_file_tmp_pathname, mode=鈥榳鈥).write(
      IO::popen( cmd_line ).read() )
      require 'ftools鈥
      File::mv( img_file_tmp_pathname, img_file_pathname )
      File::unlink( lock_file_pathname )
      img_file_mtime = Time::now
      img_file_mtime_string = img_file_mtime.to_s
      rescue SystemCallError
      ## Mail admin if any errors - if you don鈥檛 want mail, change
      ## the admin address.
      require 'net/smtp鈥
      smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
      smtp.start
      body = 鈥淪ubject: Error with rubycam\n鈥 +
      鈥淔ailed to execute:\n鈥 + cmd_line + "\n\nSee logs for details."
      smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥, admin_email_address )
      smtp.finish
      File::unlink( lock_file_pathname )
      end

      end

      ###########################

      HTML

      ###########################

      puts( 鈥淐ontent-type: text/html\n鈥 +
      'Last-Modified: 鈥 + img_file_mtime_string + 鈥淺n鈥 +
      'Expires: 鈥 + ( img_file_mtime + refresh ).to_s +
      "\n\n" )

      puts( 鈥樷 + title + 鈥樷 +
      鈥欌 )

      puts( 鈥樷 )

      puts( 鈥

      <td鈥 )

      if img_background_color != nil
      puts( 鈥 bgcolor="鈥 + img_background_color + 鈥"鈥 )
      end

      puts( 鈥>

      鈥 )

      puts( 鈥

      鈥 + img_file_mtime_string + 鈥鈥 )

      if source_link_target != nil
      puts( 鈥 鈥 )
      end

      puts( 鈥樷 )

      ###########################

      CALLING FROM HTML

      ###########################

      Include the following in your page head:

      Call within body of page as:

      • <a

        href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb鈥);鈥>Webcam

      • <a

        href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb/transgif.conf鈥);鈥>Psychedelicam

      • <a

        href=鈥淛avascript:openwin(鈥/cgi-bin/webcam/rubycam.rb/greyjpeg.conf鈥);鈥>Greycam

      • Formatted

        Source

      • Source

        ###########################

        CONFIG FILE # 1

        ###########################

        greyjpeg.conf - produces a grey jpeg

        cmd_line = 'cqcam -s 1 | ppmtojpeg --progressive --greyscale

        鈥搊ptimize --quality=33 --comment=鈥淢ade with Rubycam鈥濃

        img_filename = 鈥榞rey.jpg鈥

        title = 鈥楪reycam鈥

        refresh = 60

        ###########################

        CONFIG FILE # 2

        ###########################

        transgif.conf - produces a transparent gif with weird effects

        cmd_line = 'cqcam -s 1 | ppmquant 2 | ppmtogif -transparent grey

        -interlace -sort -comment=鈥淢ade with Rubycam鈥濃

        img_filename = 鈥榯ransparent.gif鈥

        title = 鈥楶sychedelicam鈥

        refresh = 60

        img_background_color = 鈥榬ed鈥

        use_randomize_img_background_color = true

        use_randomize_text_color = true

        text_color_matches_img_background_color = true

        EOF


      • (Phil) #2

        Want to clarify that some redundancy existed in that post - the relevant
        portion follows.

        路路路

        On Wed, 19 Jun 2002 16:54:21 -0700, Phil wrote:

        I don鈥檛 want to mess with Sourceforge, so consider this my form of
        dissemination in case the server disappears some time. Please forward
        mods to the email address listed under AUTHOR below. PS: New to ruby
        when I wrote it - now have RSI - probably not going to make it very
        slick. :wink:


        #!/usr/bin/env ruby

        ########################################################################

        VERSION: $Id: rubycam.rb,v 1.7 2002/03/22 20:08:52 rubycam Exp $

        AUTHOR: Phil Voris ## ## LICENSE: Ruby

        License - must include this header comment block ##
        http://www.ruby-lang.org/en/LICENSE.txt ##

        DESCRIPTION:

        This CGI webcam script attempts to conserve resources by not

        creating images more often than the number of seconds indicated by ##
        the refresh setting. Maximum flexibility is given by allowing the ##
        administrator to set the image-producing command line. The ## defaults
        are arranged to use cqcam ## (http://www.cs.duke.edu/~reynolds/cqcam/).

        REQUIRES: ruby

        ruby modules: net/smtp (for error reporting), ftools

          rb2html.rb (for viewing source) ##           some image-grabber,
        

        such as cqcam ## optionally, some image manipulation software

        USAGE:

        * Install under cgi-bin or wherever cgi executables are allowed. ## *

        Configure the global config section below. ## * Configure the default
        config section below. ## * Optionally create one or more config files to
        call in the PATH_INFO. ## * Ensure that img_file_web_directory exists
        and is writable by the ## web server ## * [See end of script for
        recommendations for config and calling from ## html.]

        TO DO:

        It would be nice to add caching. That is, deliver a header

        indicating that the page (the image) hasn鈥檛 changed until it ## actually
        does. I attempted this, but found that ## HTTP_IF_MODIFIED_SINCE didn鈥檛
        appear in the environment. I then ## attempted to use a session to
        track if the use had seen the page ## with the current image - however
        sessions weren鈥檛 quite working for ## me - perhaps someone else can
        resolve this issue.
        ########################################################################

        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # GLOBAL
        CONFIGURATION - EDIT HERE

        These options must be set

        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

        allow options from URL (true / false) use_config_files = true

        img file directory [relative to DOC_ROOT] img_file_web_directory =

        鈥/img/cam鈥

        configuration files directory

        config_dir = 鈥./config鈥

        directory where rb2html and associated files may be found ## (used

        for /view)
        rb2html_directory = 鈥./rb2html-1.0鈥

        file base-name

        lock_filename = 鈥榬ubycam.lock鈥

        maximum age to keep a lock file (seconds, must exceed refresh time)

        max_lockfile_age = 500

        shows a little 庐 which links to the source. Specify the target

        frame (鈥檁new鈥, 鈥榑self鈥, etc) or nil to not display it.
        source_link_target = 鈥榑new鈥

        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # END
        GLOBAL CONFIGURATION
        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> #
        PER-CAM CONFIGURATION - SET DEFAULTS HERE # - MAY
        MODIFY IN CONFIG FILES #

        Anything listed in this section may be set - as below - in a

        separate file.
        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

        default_cqcam_opts

        cmd_line = 鈥榗qcam -r -q 25 -s 1 -j鈥

        filename for the images

        img_filename = 鈥榩lain.jpg鈥

        admin email address

        admin_email_address = 鈥榳ebmaster@localhost鈥

        page title

        title = 鈥楻ubycam鈥

        background (color)

        bgcolor = 鈥榖lack鈥

        text (color)

        text_color = 鈥榶ellow鈥

        browser refresh length (in seconds) refresh = 60

        img_background_color (color | nil to disable globally)

        img_background_color = nil

        randomize transparency_background_color (true / false)

        use_randomize_img_background_color = false

        randomize transparency_background_color (true / false)

        use_randomize_text_color = false

        match text and img backgrounds - useful for randomization ## (true /

        false)
        text_color_matches_img_background_color = false

        color for the source link, if it appears (color) source_link_color =

        鈥榙arkred鈥

        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # END
        PER-CAM CONFIGURATION
        #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

        ########################################## ### PROCESS URL OPTIONS FROM
        PATH_INFO ### ########################################## ## If the
        script is called from a URL with path info equal to 鈥榲iew鈥 or ## 'get鈥
        the contents of the script will be displayed. 鈥榞et鈥 results ## in
        text/plain output while view - relying on rb2html - output color- ##
        coded html output with line numbers. path_info =
        ENV[鈥楶ATH_INFO鈥橾.untaint

        if path_info == 鈥/get鈥

        Print source

        puts( 鈥淐ontent-type: text/plain\n\n鈥 ) IO::foreach(
        ENV[鈥楽CRIPT_FILENAME鈥橾.untaint ) { |l|
        puts l
        }
        exit
        elsif path_info == 鈥/view鈥

        Print formatted source

        puts( 鈥淐ontent-type: text/html\n\n鈥 ) rb2html_executable =
        rb2html_directory + 鈥/rb2html.rb鈥 if File::file?( rb2html_executable
        )
        begin
        htmlpipe = IO::popen( 鈥榬uby 鈥 + rb2html_executable + 鈥 鈥 +
        ENV[鈥楽CRIPT_FILENAME鈥橾.untaint )
        puts( htmlpipe.readlines() )
        rescue SystemCallError
        ## Mail admin if any errors - if you don鈥檛 want mail, change ##
        the admin address.
        require 鈥榥et/smtp鈥
        smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
        smtp.start
        body = 鈥淓rror:\nruby " + rb2html_executable + 鈥 鈥 +
        ENV[鈥楽CRIPT_FILENAME鈥橾.untaint
        smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥,
        admin_email_address ) smtp.finish
        end
        exit
        else
        puts( 鈥淐ontent-type: text/plain\n\n鈥 ) puts( 鈥淓rror: cannot execute
        鈥 + rb2html_executable ) exit
        end
        elsif path_info != nil
        if not File::file?( config_dir + path_info )
        ## Print error
        puts( 鈥淐ontent-type: text/plain\n\n鈥 ) puts( 鈥淓rror: invalid config
        file: '鈥 + config_dir + path_info +
        鈥濃橽n" +
        鈥淪et the PATH_INFO (in the url) to a valid config file name.鈥 )
        exit
        elsif path_info.index( 鈥樷/鈥 ) != nil
        ## Print error
        puts( 鈥淐ontent-type: text/plain\n\n鈥 ) puts( 鈥淓rror: invalid config
        file: '鈥 + config_dir + path_info +
        "鈥橽n" +
        "鈥欌/鈥 not permitted in config file pathname." )
        exit
        elsif not File::readable?( config_dir + path_info )
        ## Print error
        puts( 鈥淐ontent-type: text/plain\n\n鈥 ) puts( 鈥淓rror: unreadable
        config file: '鈥 + config_dir + path_info +
        "鈥" )
        exit
        else
        ## Process config file
        config_file_string = ''
        File::open( config_dir + path_info ).readlines.each do |line|
        config_file_string.concat( line )
        end
        eval config_file_string
        end
        end

        ###########################

        DETERMINE FILE INFO

        ###########################

        First, we determine the colors to be used for text (the image time)

        and for the background of the image (not the page bgcolor).

        color_hash = {
        0 => 鈥榳hite鈥,
        1 => 鈥榬ed鈥,
        2 => 鈥榦range鈥,
        3 => 鈥榶ellow鈥,
        4 => 鈥榞reen鈥,
        5 => 鈥榖lue鈥,
        6 => 鈥榩urple鈥,
        7 => 鈥榣ightgreen鈥,
        8 => 鈥榩ink鈥,
        9 => 鈥榣ightyellow鈥,
        10 => 鈥榣ightblue鈥,
        11 => 鈥榣ightgray鈥,
        12 => 鈥榞ray鈥
        }
        }

        If img_background_color is set to nil, then no image background

        color options will apply.
        if img_background_color != nil and
        use_randomize_img_background_color
        img_background_color = color_hash[rand(13)]
        end

        If randomization is used for text and image, then the image

        background color is used for both. if use_randomize_text_color and
        not text_color_matches_img_background_color
        text_color = color_hash[rand(13)]
        elsif ( img_background_color != nil ) and
        text_color_matches_img_background_color
        text_color = img_background_color
        end

        img_file_webpath = img_file_web_directory + 鈥/鈥 + img_filename
        img_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_webpath
        img_file_pathname.untaint
        img_file_tmp_pathname = img_file_pathname + 鈥.tmp鈥

        Time

        time_now = Time::now

        Mtime

        if File::exist?( img_file_pathname )
        img_file_mtime = File::mtime( img_file_pathname ) img_file_mtime_int
        = img_file_mtime.to_i img_file_mtime_string = img_file_mtime.to_s
        img_file_age = time_now.to_i - img_file_mtime_int
        end

        ###########################

        LOCKFILE CODE

        ###########################

        Lock file name is global - there is only one camera and it can take

        only one picture at a time. The lockfile is set during this process.

        lock_file_pathname = ENV[鈥楧OCUMENT_ROOT鈥橾 + img_file_web_directory + 鈥/鈥
        +
        lock_filename
        lock_file_pathname.untaint

        If the lockfile somehow gets stale, it will be removed. locked =

        false
        if File::exist?( lock_file_pathname )
        if ( File::mtime( lock_file_pathname ) < ( time_now - (
        max_lockfile_age ) ) )
        File::unlink( lock_file_pathname )
        else
        locked = true
        end
        end

        ###########################

        EXECUTE

        ###########################

        If there鈥檚 no image, or it鈥檚 an old one, and the camera is free,

        then we take a new picture.
        if ( ( not File::exist?( img_file_pathname ) ) or
        ( img_file_age >= refresh ) ) and
        ( not locked )

        lockfile = File::new( lock_file_pathname, mode=鈥榳鈥 )

        begin
        ## Execute
        img_file = File::open( img_file_tmp_pathname, mode=鈥榳鈥).write(
        IO::popen( cmd_line ).read() )
        require 'ftools鈥
        File::mv( img_file_tmp_pathname, img_file_pathname ) File::unlink(
        lock_file_pathname )
        img_file_mtime = Time::now
        img_file_mtime_string = img_file_mtime.to_s
        rescue SystemCallError
        ## Mail admin if any errors - if you don鈥檛 want mail, change ## the
        admin address.
        require 'net/smtp鈥
        smtp = Net::SMTP::new( 鈥渓ocalhost鈥 )
        smtp.start
        body = 鈥淪ubject: Error with rubycam\n鈥 +
        鈥淔ailed to execute:\n鈥 + cmd_line + "\n\nSee logs for details."
        smtp.sendmail( body, 鈥榬ubycam_user@localhost鈥, admin_email_address
        ) smtp.finish
        File::unlink( lock_file_pathname )
        end

        end

        ###########################

        HTML

        ###########################

        puts( 鈥淐ontent-type: text/html\n鈥 +
        'Last-Modified: 鈥 + img_file_mtime_string + 鈥淺n鈥 + 'Expires: 鈥 + (
        img_file_mtime + refresh ).to_s + 鈥淺n\n鈥 )

        puts( 鈥樷 + title + 鈥樷 +
        鈥欌 )

        puts( 鈥樷 )

        puts( 鈥

        <td鈥 )

        if img_background_color != nil
        puts( 鈥 bgcolor="鈥 + img_background_color + 鈥"鈥 )
        end

        puts( 鈥>

        鈥 )

        puts( 鈥

        鈥 + img_file_mtime_string + 鈥鈥 )

        if source_link_target != nil
        puts( 鈥 鈥 )
        end

        puts( 鈥樷 )

        ###########################

        CALLING FROM HTML

        ###########################

        Include the following in your page head: ## ## <script

        language=javascript>

        function openwin(url){

        var hWnd =

        window.open(url,"",鈥渨idth=350,height=300,resizable=yes,scrollbars=yes鈥);

        if (!hWnd.opener) hWnd.opener = self; ## }

        function openminiwin(url){

        var hWnd =

        window.open(url,"",鈥渨idth=180,height=140,resizable=yes,scrollbars=yes鈥);

        if (!hWnd.opener) hWnd.opener = self; ## }

        ## ## Call within body of page as: ## ## ##

        ###########################

        CONFIG FILE # 1

        ###########################

        greyjpeg.conf - produces a grey jpeg # cmd_line = 'cqcam -s 1 |

        ppmtojpeg --progressive --greyscale --optimize --quality=33
        鈥揷omment=鈥淢ade with Rubycam鈥濃 # img_filename = 鈥榞rey.jpg鈥 # title =
        鈥楪reycam鈥

        refresh = 60

        ###########################

        CONFIG FILE # 2

        ###########################

        transgif.conf - produces a transparent gif with weird effects

        cmd_line = 鈥榗qcam -s 1 | ppmquant 2 | ppmtogif -transparent grey
        -interlace -sort -comment=鈥淢ade with Rubycam鈥濃 # img_filename =
        鈥榯ransparent.gif鈥

        title = 鈥楶sychedelicam鈥

        refresh = 60

        img_background_color = 鈥榬ed鈥

        use_randomize_img_background_color = true # use_randomize_text_color

        = true

        text_color_matches_img_background_color = true

        EOF