From: Phil Date: 2002-06-20T09:07:22+09:00 Subject: rubycam.rb v 1.7 CGI webcam script for use with cqcam, etc... I don't 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. ;-) ------------- #!/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't changed until it ## actually does. I attempted this, but found that ## HTTP_IF_MODIFIED_SINCE didn't 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't 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 = 'rubycam.lock' ## maximum age to keep a lock file (seconds, must exceed refresh time) max_lockfile_age = 500 ## shows a little (R) 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 = 'cqcam -r -q 25 -s 1 -j' ## filename for the images img_filename = 'plain.jpg' ## admin email address admin_email_address = 'webmaster@localhost' ## page title title = 'Rubycam' ## background (color) bgcolor = 'black' ## text (color) text_color = 'yellow' ## 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 = 'darkred' #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # END PER-CAM CONFIGURATION #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ########################################## ### PROCESS URL OPTIONS FROM PATH_INFO ### ########################################## ## If the script is called from a URL with path info equal to 'view' or ## 'get' the contents of the script will be displayed. 'get' results ## in text/plain output while view - relying on rb2html - output color- ## coded html output with line numbers. path_info = ENV['PATH_INFO'].untaint if path_info == '/get' ## Print source puts( "Content-type: text/plain\n\n" ) IO::foreach( ENV['SCRIPT_FILENAME'].untaint ) { |l| puts l } exit elsif path_info == '/view' ## Print formatted source puts( "Content-type: text/html\n\n" ) rb2html_executable = rb2html_directory + '/rb2html.rb' if File::file?( rb2html_executable ) begin htmlpipe = IO::popen( 'ruby ' + rb2html_executable + ' ' + ENV['SCRIPT_FILENAME'].untaint ) puts( htmlpipe.readlines() ) rescue SystemCallError ## Mail admin if any errors - if you don't want mail, change ## the admin address. require 'net/smtp' smtp = Net::SMTP::new( "localhost" ) smtp.start body = "Error:\nruby " + rb2html_executable + ' ' + ENV['SCRIPT_FILENAME'].untaint smtp.sendmail( body, 'rubycam_user@localhost', admin_email_address ) smtp.finish end exit else puts( "Content-type: text/plain\n\n" ) puts( "Error: cannot execute " + rb2html_executable ) exit end elsif path_info != nil if not File::file?( config_dir + path_info ) ## Print error puts( "Content-type: text/plain\n\n" ) puts( "Error: invalid config file: '" + config_dir + path_info + "'\n" + "Set the PATH_INFO (in the url) to a valid config file name." ) exit elsif path_info.index( '../' ) != nil ## Print error puts( "Content-type: text/plain\n\n" ) puts( "Error: 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( "Content-type: text/plain\n\n" ) puts( "Error: 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 => 'white', 1 => 'red', 2 => 'orange', 3 => 'yellow', 4 => 'green', 5 => 'blue', 6 => 'purple', 7 => 'lightgreen', 8 => 'pink', 9 => 'lightyellow', 10 => 'lightblue', 11 => 'lightgray', 12 => 'gray' } ## 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['DOCUMENT_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['DOCUMENT_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's no image, or it's 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='w' ) begin ## Execute img_file = File::open( img_file_tmp_pathname, mode='w').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't want mail, change ## the admin address. require 'net/smtp' smtp = Net::SMTP::new( "localhost" ) smtp.start body = "Subject: Error with rubycam\n" + "Failed to execute:\n" + cmd_line + "\n\nSee logs for details." smtp.sendmail( body, 'rubycam_user@localhost', admin_email_address ) smtp.finish File::unlink( lock_file_pathname ) end end ########################### ### HTML ### ########################### puts( "Content-type: text/html\n" + 'Last-Modified: ' + img_file_mtime_string + "\n" + 'Expires: ' + ( img_file_mtime + refresh ).to_s + "\n\n" ) puts( '' + title + '' + '' ) puts( '' ) 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: ## ## ## ########################### ### CONFIG FILE # 1 ### ########################### ## greyjpeg.conf - produces a grey jpeg # cmd_line = 'cqcam -s 1 | ppmtojpeg --progressive --greyscale --optimize --quality=33 --comment="Made with Rubycam"' # img_filename = 'grey.jpg' # title = 'Greycam' # 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="Made with Rubycam"' # img_filename = 'transparent.gif' # title = 'Psychedelicam' # refresh = 60 # img_background_color = 'red' # 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 ## ## 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't changed until it ## actually does. I attempted this, but found that ## HTTP_IF_MODIFIED_SINCE didn't 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't 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 = 'rubycam.lock' ## maximum age to keep a lock file (seconds, must exceed refresh time) max_lockfile_age = 500 ## shows a little (R) 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 = 'cqcam -r -q 25 -s 1 -j' ## filename for the images img_filename = 'plain.jpg' ## admin email address admin_email_address = 'webmaster@localhost' ## page title title = 'Rubycam' ## background (color) bgcolor = 'black' ## text (color) text_color = 'yellow' ## 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 = 'darkred' #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # END PER-CAM CONFIGURATION #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ########################################## ### PROCESS URL OPTIONS FROM PATH_INFO ### ########################################## ## If the script is called from a URL with path info equal to 'view' or ## 'get' the contents of the script will be displayed. 'get' results ## in text/plain output while view - relying on rb2html - output color- ## coded html output with line numbers. path_info = ENV['PATH_INFO'].untaint if path_info == '/get' ## Print source puts( "Content-type: text/plain\n\n" ) IO::foreach( ENV['SCRIPT_FILENAME'].untaint ) { |l| puts l } exit elsif path_info == '/view' ## Print formatted source puts( "Content-type: text/html\n\n" ) rb2html_executable = rb2html_directory + '/rb2html.rb' if File::file?( rb2html_executable ) begin htmlpipe = IO::popen( 'ruby ' + rb2html_executable + ' ' + ENV['SCRIPT_FILENAME'].untaint ) puts( htmlpipe.readlines() ) rescue SystemCallError ## Mail admin if any errors - if you don't want mail, change ## the admin address. require 'net/smtp' smtp = Net::SMTP::new( "localhost" ) smtp.start body = "Error:\nruby " + rb2html_executable + ' ' + ENV['SCRIPT_FILENAME'].untaint smtp.sendmail( body, 'rubycam_user@localhost', admin_email_address ) smtp.finish end exit else puts( "Content-type: text/plain\n\n" ) puts( "Error: cannot execute " + rb2html_executable ) exit end elsif path_info != nil if not File::file?( config_dir + path_info ) ## Print error puts( "Content-type: text/plain\n\n" ) puts( "Error: invalid config file: '" + config_dir + path_info + "'\n" + "Set the PATH_INFO (in the url) to a valid config file name." ) exit elsif path_info.index( '../' ) != nil ## Print error puts( "Content-type: text/plain\n\n" ) puts( "Error: 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( "Content-type: text/plain\n\n" ) puts( "Error: 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 => 'white', 1 => 'red', 2 => 'orange', 3 => 'yellow', 4 => 'green', 5 => 'blue', 6 => 'purple', 7 => 'lightgreen', 8 => 'pink', 9 => 'lightyellow', 10 => 'lightblue', 11 => 'lightgray', 12 => 'gray' } ## 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['DOCUMENT_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['DOCUMENT_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's no image, or it's 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='w' ) begin ## Execute img_file = File::open( img_file_tmp_pathname, mode='w').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't want mail, change ## the admin address. require 'net/smtp' smtp = Net::SMTP::new( "localhost" ) smtp.start body = "Subject: Error with rubycam\n" + "Failed to execute:\n" + cmd_line + "\n\nSee logs for details." smtp.sendmail( body, 'rubycam_user@localhost', admin_email_address ) smtp.finish File::unlink( lock_file_pathname ) end end ########################### ### HTML ### ########################### puts( "Content-type: text/html\n" + 'Last-Modified: ' + img_file_mtime_string + "\n" + 'Expires: ' + ( img_file_mtime + refresh ).to_s + "\n\n" ) puts( '' + title + '' + '' ) puts( '' ) 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: ## ## ## ########################### ### CONFIG FILE # 1 ### ########################### ## greyjpeg.conf - produces a grey jpeg # cmd_line = 'cqcam -s 1 | ppmtojpeg --progressive --greyscale --optimize --quality=33 --comment="Made with Rubycam"' # img_filename = 'grey.jpg' # title = 'Greycam' # 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="Made with Rubycam"' # img_filename = 'transparent.gif' # title = 'Psychedelicam' # refresh = 60 # img_background_color = 'red' # 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 ## ## 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't changed until it ## actually does. I attempted this, but found that ## HTTP_IF_MODIFIED_SINCE didn't 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't 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 = 'rubycam.lock' ## maximum age to keep a lock file (seconds, must exceed refresh time) max_lockfile_age = 500 ## shows a little (R) 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 = 'cqcam -r -q 25 -s 1 -j' ## filename for the images img_filename = 'plain.jpg' ## admin email address admin_email_address = 'webmaster@localhost' ## page title title = 'Rubycam' ## background (color) bgcolor = 'black' ## text (color) text_color = 'yellow' ## 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 = 'darkred' #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # END PER-CAM CONFIGURATION #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ########################################## ### PROCESS URL OPTIONS FROM PATH_INFO ### ########################################## ## If the script is called from a URL with path info equal to 'view' or ## 'get' the contents of the script will be displayed. 'get' results ## in text/plain output while view - relying on rb2html - output color- ## coded html output with line numbers. path_info = ENV['PATH_INFO'].untaint if path_info == '/get' ## Print source puts( "Content-type: text/plain\n\n" ) IO::foreach( ENV['SCRIPT_FILENAME'].untaint ) { |l| puts l } exit elsif path_info == '/view' ## Print formatted source puts( "Content-type: text/html\n\n" ) rb2html_executable = rb2html_directory + '/rb2html.rb' if File::file?( rb2html_executable ) begin htmlpipe = IO::popen( 'ruby ' + rb2html_executable + ' ' + ENV['SCRIPT_FILENAME'].untaint ) puts( htmlpipe.readlines() ) rescue SystemCallError ## Mail admin if any errors - if you don't want mail, change ## the admin address. require 'net/smtp' smtp = Net::SMTP::new( "localhost" ) smtp.start body = "Error:\nruby " + rb2html_executable + ' ' + ENV['SCRIPT_FILENAME'].untaint smtp.sendmail( body, 'rubycam_user@localhost', admin_email_address ) smtp.finish end exit else puts( "Content-type: text/plain\n\n" ) puts( "Error: cannot execute " + rb2html_executable ) exit end elsif path_info != nil if not File::file?( config_dir + path_info ) ## Print error puts( "Content-type: text/plain\n\n" ) puts( "Error: invalid config file: '" + config_dir + path_info + "'\n" + "Set the PATH_INFO (in the url) to a valid config file name." ) exit elsif path_info.index( '../' ) != nil ## Print error puts( "Content-type: text/plain\n\n" ) puts( "Error: 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( "Content-type: text/plain\n\n" ) puts( "Error: 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 => 'white', 1 => 'red', 2 => 'orange', 3 => 'yellow', 4 => 'green', 5 => 'blue', 6 => 'purple', 7 => 'lightgreen', 8 => 'pink', 9 => 'lightyellow', 10 => 'lightblue', 11 => 'lightgray', 12 => 'gray' } ## 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['DOCUMENT_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['DOCUMENT_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's no image, or it's 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='w' ) begin ## Execute img_file = File::open( img_file_tmp_pathname, mode='w').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't want mail, change ## the admin address. require 'net/smtp' smtp = Net::SMTP::new( "localhost" ) smtp.start body = "Subject: Error with rubycam\n" + "Failed to execute:\n" + cmd_line + "\n\nSee logs for details." smtp.sendmail( body, 'rubycam_user@localhost', admin_email_address ) smtp.finish File::unlink( lock_file_pathname ) end end ########################### ### HTML ### ########################### puts( "Content-type: text/html\n" + 'Last-Modified: ' + img_file_mtime_string + "\n" + 'Expires: ' + ( img_file_mtime + refresh ).to_s + "\n\n" ) puts( '' + title + '' + '' ) puts( '' ) 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: ## ## ## ########################### ### CONFIG FILE # 1 ### ########################### ## greyjpeg.conf - produces a grey jpeg # cmd_line = 'cqcam -s 1 | ppmtojpeg --progressive --greyscale --optimize --quality=33 --comment="Made with Rubycam"' # img_filename = 'grey.jpg' # title = 'Greycam' # 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="Made with Rubycam"' # img_filename = 'transparent.gif' # title = 'Psychedelicam' # refresh = 60 # img_background_color = 'red' # use_randomize_img_background_color = true # use_randomize_text_color = true # text_color_matches_img_background_color = true ## EOF