#!/usr/bin/ruby

#####################################################################################
# ifetch-tools is a set of tools that can collect images from ip based cameras,
# monitor collection process, and provide an interface to view collected history.
# Copyright (C) 2005-2015 Richard Nelson
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

##############################################################################################
# Set some variables.
##############################################################################################
# Version number
VER = "0.15.24d3"

##############################################################################################
# Set some options to ensure memory management.
##############################################################################################
GC.enable

##############################################################################################
# Do the require and include stuff we need for our operations.
##############################################################################################
require 'webrick'
require 'drb'
require 'RMagick'
#require 'logger'
# Unremark the below two lines if you want to try with high-performance.
#require 'rubygems'
#require 'webrick/highperformanceserver'
include WEBrick

##############################################################################################
# Suck in the config settings from the ifetch-tools.conf file.
##############################################################################################
begin
	eval(File.open("/etc/ifetch-tools/ifetch-tools.conf") {|fh| fh.read})
rescue
	puts "Error encountered reading the conf file of: "+$!.to_s
	# Stop after the error feedback.
	exit
end


##############################################################################################
# Class definitions below.
##############################################################################################

##############################################################################################
# Camera generates two frames and calls CameraHistory on the left frame of the system.
##############################################################################################
class Camera < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera History Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == HISTORYUSER && pass == HISTORYPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		raise HTTPServerError, 'Error: cameraName parameter not passed correctly.' if req.query['cameraName'] == nil || /[0-9]+/.match(req.query['cameraName']) == nil
		cameraName = /[0-9]+/.match(req.query['cameraName']).to_s
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/Camera") {|fh| fh.read})
	end
end

##############################################################################################
# CameraHistory generates the left frame of the system and setup some stuff to do the magic
# on the camera history.
##############################################################################################
class CameraHistory < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera History Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == HISTORYUSER && pass == HISTORYPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		# cameraName we are to work with.
		raise HTTPServerError, 'Error: cameraName parameter not passed correctly.' if req.query['cameraName'] == nil || /[0-9]+/.match(req.query['cameraName']) == nil
		cameraName = /[0-9]+/.match(req.query['cameraName']).to_s

		# index is a page reference to where we are in the view.
		raise HTTPServerError, 'Error: index parameter not passed correctly.' if req.query['index'] == nil || /[0-9]+/.match(req.query['index']) == nil
		index = /[0-9]+/.match(req.query['index']).to_s # Crop out all the bad stuff.

		# navigation is to determine whether we populate the viewing pane of first view.
		raise HTTPServerError, 'Error: navigation parameter not passed correctly.' if req.query['navigation'] == nil || /[0-9]+/.match(req.query['navigation']) == nil
		navigation = /[0-1]/.match(req.query['navigation']).to_s # Crop out all the bad stuff.

		# basicView is for the Basic Archive View.
		raise HTTPServerError, 'Error: basicView parameter not passed correctly.' if req.query['basicView'] == nil || /[0-2]/.match(req.query['basicView']) == nil
		basicView = /[0-2]/.match(req.query['basicView']).to_s

		# Initialize some vars.
		myJumpNavigation = "" # Jump navigation string.
		myNavigation = "" # General navigation string.
		myTempRespond = "" # Set a local var to null.

		# 20090201 Nelson - I am now starting this on startup and only do the new object here.
		# Here is the performance boost for exchange of data between the collection daemon and the web interface
		#DRb.start_service(nil,nil,{:load_limit => LOADLIMIT})
		drb_port = "druby://localhost:"+(BASEPORT+cameraName.to_i).to_s
		#obj = DRbObject.new(nil, 'druby://localhost:9000')
		obj = DRbObject.new(nil, drb_port)

		# Define our array to handle the DRb exchange.
		imgSorted = Array.new

		# Now use obj, imgSorted is not as descriptive but remember that we expect the sequence to be pre sorted so in fact name is ok.
		imgSorted = obj.xchange_array
		# 20090201 Nelson - I am now stoping this on trap of close here.
		#DRb.stop_service() # Stop the DRb on the webrick session

		imgSortedLength = imgSorted.length
		totalPages = imgSortedLength / IMAGESPERPAGE # Get the total number of pages.
		#puts imgSortedLength

		# This is just a logic flow here for the first page while the system is populating.
		# Note, that the way we display images will cause drift in respect to amount of images and the time at the interface.
		#if totalPages != 0
		#totalPagesCounter = totalPages - 1
		#end

		# Ok here we are getting a number to reference so we do not have time drift.
		tmpIndex = index.to_i

		# Set a sentry for start over on count back to 0
		# We do not have to worry about backward count since that is addressed in the "< Previous | Next >" navigation on down.
		indexSentry = -1

		# Generate the response of images per page and guard against no images yet.
		if imgSortedLength > 0
			if tmpIndex+IMAGESPERPAGE >= imgSortedLength
				lastImgOnPage = imgSortedLength - 1
				indexSentry = tmpIndex+IMAGESPERPAGE - imgSortedLength - 1
			else
				lastImgOnPage = tmpIndex+IMAGESPERPAGE - 1
			end
		else
			lastImgOnPage = 0
			totalPages = -1
		end

		# This just keeps us from populating the navigation frame on the initial login to history and if no history collected yet.
		if navigation.to_i == 1 && imgSortedLength > 0
		# Ok now actually populate the page with the lastImgOnPage
			# Keep track of image file names when building frame for video export
			imgFileList = Array.new
			index.to_i.upto(lastImgOnPage) do |imgIndex|
				#imgTime, imgFile = imgSorted[5].to_s.split(/,/)
				imgTime, imgFile = imgSorted[imgIndex].split(/,/)
				#puts imgFile
				# Ok here we are getting a number to reference so we do not have time drift based on our new navigation.
				#imgTime, imgFile = imgSorted[imgIndex].to_s.split(/,/)
				# 20090122 Nelson - Added small fix for .deb and user data expected in different location.
				# myTempRespond = myTempRespond+"<BR>"+imgTime+'<BR><A HREF="/showimage?cameraName='+cameraName+'&imageName='+imgFile+'" target="showframe"><IMG SRC="'+imgFile+'" width='+HISTORYIMAGEWIDTH+' height='+HISTORYIMAGEHEIGHT+'></A><BR>'+"\n"
				myTempRespond = myTempRespond+"<BR>"+imgTime+'<BR><A HREF="/showimage?cameraName='+cameraName+'&imageName='+imgFile+'?timehash='+imgTime+'" target="showframe"><IMG SRC="/data/'+imgFile+'?timehash='+imgTime+'" width='+HISTORYIMAGEWIDTH+' height='+HISTORYIMAGEHEIGHT+'></A><BR>'+"\n"
				# Push the image on the array where we are keeping image file names
				if File.exist?("/var/lib/ifetch-tools/#{imgFile}") && File.size("/var/lib/ifetch-tools/#{imgFile}")>0 then
					imgFileList.push("/var/lib/ifetch-tools/#{imgFile}")
				end
			end
			# 20081007 Nelson - removing this block to leave last page possibly short.
			### Now finish starting back at 0 on the page that ran over the end of the array
			if imgSortedLength >= IMAGESPERPAGE
				0.to_i.upto(indexSentry) do |imgIndex|
					#imgTime, imgFile = imgSorted[5].to_s.split(/,/)
					imgTime, imgFile = imgSorted[imgIndex].split(/,/)
					#puts imgFile
					# Ok here we are getting a number to reference so we do not have time drift based on our new navigation.
					#imgTime, imgFile = imgSorted[imgIndex].to_s.split(/,/)
					myTempRespond = myTempRespond+"<BR>"+imgTime+'<BR><A HREF="/showimage?cameraName='+cameraName+'&imageName='+imgFile+'" target="showframe"><IMG SRC="/data/'+imgFile+'?timehash='+imgTime+'" width='+HISTORYIMAGEWIDTH+' height='+HISTORYIMAGEHEIGHT+'></A><BR>'+"\n"
					# Push the image on the array where we are keeping image file names
					if File.exist?("/var/lib/ifetch-tools/#{imgFile}") && File.size("/var/lib/ifetch-tools/#{imgFile}")>0 then
						imgFileList.push("/var/lib/ifetch-tools/#{imgFile}")
					end
				end
			end
		end

		# Ok here we do the Jump List as an array for just a bit.
		#myJumpNavigation = myJumpNavigation+'<OPTION STYLE="color : #ff6666" selected > *-- Navigation Jump --* '+"\n"
		myJumpHash = Hash.new
		myJumpHash["0"] = '<OPTION STYLE="color : #ff6666" selected > *-- Navigation Jump --*'+"</OPTION>\n"

		# Build the navi
		0.upto(totalPages) do |count|
			pageIndex = count*IMAGESPERPAGE
			# Bug 11618 squash - The below is since we index with 0 and not 1 we have to watch for even page count division in to our total images and adjust if need be.
			if pageIndex >= lastImgOnPage
				pageIndex = pageIndex-1
			end

			# Split out our info from the array element.
			imgTime, imgFile = imgSorted[pageIndex].split(/,/)
			#puts imgFile

			# tmpIndex will hold the index position of the page we are on.
			tmpIndex = File.basename(imgFile, ".jpg").split(/_/)[1].to_i

			# This is the elements in the jumping list. This could be sorted but for now I am leaving since I sort of like how if shows the scroll of image count in an abstract way.
			myJumpHash[imgTime] = '<OPTION value="/camerahistory?cameraName='+cameraName+'&index='+tmpIndex.to_s+'&navigation=1&basicView=0">'+imgTime+"</OPTION>\n"
		end

		# Now that we have built it lets prep it to display it like we want (sorted in this case).
		#myJumpNavigation = myJumpHash.sort.each {|key, value| puts "The key value is #{key} and the hash is #{value}" }
		myJumpHash.sort.each {|key, value| myJumpNavigation = "#{myJumpNavigation}#{value}" }
		#puts myJumpNavigation

		# Ok here is where we handle the < Previous | Next >" navigation.
		# Here is the code for a sinle page of images only or if this is the first time the user hits the hitsory.
		if index.to_i-IMAGESPERPAGE < 0 && index.to_i+IMAGESPERPAGE > imgSortedLength-1 || navigation.to_i == 0
			myNavigation = %{<FORM NAME="myform">
				<SELECT name="mylist" onChange="disp_text();">
				#{myJumpNavigation}
				</SELECT></FORM>Please select a time.<BR>
			}
		else
			# If we are here then there is more than one page of images.
			# Here is the move back page code
			if index.to_i-IMAGESPERPAGE >= 0
				myNavigation = %{<FORM NAME="myform">
					<SELECT name="mylist" onChange="disp_text();">
					#{myJumpNavigation}
					</SELECT></FORM>
					<A HREF="/camerahistory?cameraName=#{cameraName}&index=#{index.to_i-IMAGESPERPAGE}&navigation=1&basicView=0"> < Previous </A>
				}
			else
				myNavigation = %{<FORM NAME="myform">
					<SELECT name="mylist" onChange="disp_text();">
					#{myJumpNavigation}
					</SELECT></FORM>
					<A HREF="/camerahistory?cameraName=#{cameraName}&index=#{index.to_i-IMAGESPERPAGE+imgSortedLength-1}&navigation=1&basicView=0"> < Previous </A>
				}
			end
			# Here is the move forward between page code
			if index.to_i+IMAGESPERPAGE <= imgSortedLength-1
				myNavigation = %{#{myNavigation}| <A HREF="/camerahistory?cameraName=#{cameraName}&index=#{index.to_i+IMAGESPERPAGE}&navigation=1&basicView=0"> Next > </A><BR>
				}
			else
				myNavigation = %{#{myNavigation}| <A HREF="/camerahistory?cameraName=#{cameraName}&index=#{indexSentry}&navigation=1&basicView=0"> Next > </A><BR>
				}
			end
		end

		if basicView.to_i == 1
			# If we are here we are asking to build a page with images only.
			myTempRespond.gsub!(/(<img\b[^>]*)width=\d+ height=\d+/i, '\\1')
			myTempRespond.gsub!(/(<a) href="[^"]+"/i, '\\1')
			# FIXME - This one is ugly and depends on the output from the above. But it does appear to work.
			myTempRespond.gsub!(/a><br>/i,"A><p style=\"page-break-before: always\">")
			# Now generate the page with the correct substitution.
			res.body = eval(File.open("/usr/share/ifetch-tools/templates/CameraHistoryBasic") {|fh| fh.read})
		elsif basicView.to_i == 2
			# If we are here we need to call rmagick with to make a vidoe of the frame of images starting with the selected image.
			# Setup to make video from images
			tempResponse = Magick::ImageList.new(*imgFileList) do
				self.delay = $video_export_delay
			end
			# Write out temp video to a random directory in /tmp, set mime_type for default launch,
			# then destroy said temp area upon return for security.
			Dir.mktmpdir(nil, "/tmp") {|dir|
				# use the directory...
				tempResponse.write("#{dir}/tmpVideo.mp4")
				mtype = WEBrick::HTTPUtils::mime_type("#{dir}/tmpVideo.mp4", WEBrick::HTTPUtils::DefaultMimeTypes)
				res['Content-Type'] = mtype
				res['Content-Disposition'] = 'inline; filename="video.mp4"'
				puts mtype
				res.body = File.open("#{dir}/tmpVideo.mp4","rb") {|io| io.read}
			}
		else
			GC.start
			# Now generate the page with the correct substitution.
			res.body = eval(File.open("/usr/share/ifetch-tools/templates/CameraHistory") {|fh| fh.read})
		end
	end
end

##############################################################################################
# Start and Stop the collection process for a given camera.
##############################################################################################
class CameraStartStop < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera Monitor Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == MONITORUSER && pass == MONITORPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		raise HTTPServerError, 'Error: cameraName parameter not passed correctly.' if req.query['cameraName'] == nil || /[0-9]+/.match(req.query['cameraName']) == nil
		cameraName = /[0-9]+/.match(req.query['cameraName']).to_s
		# Now test for the status of the cameras lock file. If running we stop and if not we start.
		myTempRespond = ""
		lock_file = File::open("/var/lock/ifetch-tools/"+cameraName.to_s+".lock", 'w')
		if lock_file.flock(File::LOCK_EX|File::LOCK_NB) == 0 then
			# Camera not running action here
			# Really important here to close the file or you will get confused!
			lock_file.close
			system_call = "/usr/bin/ifetch /etc/ifetch-tools/cameras/"+cameraName.to_s+".conf&"
			pid = fork {
				Process.setsid
				(0...2).each do |i|
					begin
						#closing stdin, stdout and stderr 0,1,2
						IO.for_fd(i).close
					rescue Errno::EBADF
					end
				end
				pid2 = fork { exec(system_call) }
				Process.detach(pid2)
				exit!
			}
			Process.detach(pid)
			myTempRespond = "The servlet is attempting to start camera #{cameraName.to_s}"
		else
			# Camera running action here
			# Set the camera_pid to hold the pid of the running camera process.
			camera_pid = File.read("/var/run/ifetch-tools/"+cameraName.to_s+".pid").chomp
			system_call = "kill -s 9 #{camera_pid} &"
			Process.detach( fork { system(system_call) } )
			myTempRespond = "The servlet is attempting to stop camera #{cameraName.to_s} "
		end
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/CameraStartStop") {|fh| fh.read})
	end
end

##############################################################################################
# CArchive is dynamic to create an archive view of each camera that is listed from the
# conf dir at the time of the servlet creation. The tool will use the listing of files with
# the .conf extension.
##############################################################################################
class CArchive < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera History Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == HISTORYUSER && pass == HISTORYPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		myTempRespond = ""

		# 20061207 not as clean as I would like it but this seems to do the trick
		camera_array = Array.new
		cam_count = 0
		image_alt='no_image_alt'
		image_uid='no_image_uid'
		image_pwd='no_image_pwd'
		image_addr='no_image_ip'
		image_url='/no_image_url_defined'
		Dir["/etc/ifetch-tools/cameras/*.conf"].sort.each do |cam_tmp|
			###############################################################
			# Eval in the conf file for the camera with error trap on this operation.
			begin
				eval(File.open(cam_tmp) {|fh| fh.read})
			rescue
				res.body = eval(puts "Encountered reading the camera.conf file of: "+$!.to_s)
				# Stop after the error feedback.
				break
			end
			cam_count = cam_count+1
			# Put the camera pid and camera number in the table
			myTempRespond = myTempRespond+'<a href="/camera?cameraName='+image_addr.split('.').last.to_s+'" title="'+image_alt+'"><img src="http://'+image_uid.to_s+':'+image_pwd.to_s+'@'+image_addr.to_s+''+image_url.to_s+'" alt="'+image_alt+'" width="'+HISTORYIMAGEWIDTH+'" heigth="'+HISTORYIMAGEHEIGHT+'" ></a>
			'
		end
		GC.start
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/Archive") {|fh| fh.read})
	end
end

##############################################################################################
# CLive is dynamic to create a live view of each camera that is listed from the
# conf dir at the time of the servlet creation. The tool will use the listing of files with
# the .conf extension.
##############################################################################################
class CLive < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera History Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == HISTORYUSER && pass == HISTORYPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		myTempRespond = ""

		# 20061207 not as clean as I would like it but this seems to do the trick
		camera_array = Array.new
		cam_count = 0
		image_alt='no_image_alt'
		image_uid='no_image_uid'
		image_pwd='no_image_pwd'
		image_addr='no_image_ip'
		image_url='/no_image_url_defined'
		Dir["/etc/ifetch-tools/cameras/*.conf"].sort.each do |cam_tmp|
			###############################################################
			# Eval in the conf file for the camera with error trap on this operation.
			begin
				eval(File.open(cam_tmp) {|fh| fh.read})
			rescue
				res.body = eval(puts "Encountered reading the camera.conf file of: "+$!.to_s)
				# Stop after the error feedback.
				break
			end
			cam_count = cam_count+1
			# Put the camera pid and camera number in the table
			myTempRespond = myTempRespond+'<a href="/livesingle?cameraName='+File.basename(cam_tmp, ".*")+'" title="'+image_alt+'"><img src="http://'+image_uid.to_s+':'+image_pwd.to_s+'@'+image_addr.to_s+''+image_url.to_s+'" alt="'+image_alt+'" width="'+HISTORYIMAGEWIDTH+'" heigth="'+HISTORYIMAGEHEIGHT+'" ></a>
			'
		end
		GC.start
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/Live") {|fh| fh.read})
	end
end

##############################################################################################
# CLiveSingle creates a live view of a single camera.
##############################################################################################
class CLiveSingle < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera History Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == HISTORYUSER && pass == HISTORYPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		raise HTTPServerError, 'Error: cameraName parameter not passed correctly.' if req.query['cameraName'] == nil || /[0-9]+/.match(req.query['cameraName']) == nil
		cameraName = /[0-9]+/.match(req.query['cameraName']).to_s
		myTempRespond = ""

		# 20061207 not as clean as I would like it but this seems to do the trick
		camera_array = Array.new
		cam_count = 0
		image_alt='no_image_alt'
		image_uid='no_image_uid'
		image_pwd='no_image_pwd'
		image_addr='no_image_ip'
		image_url='/no_image_url_defined'
		###############################################################
		# Eval in the conf file for the camera with error trap on this operation.
		begin
			eval(File.open("/etc/ifetch-tools/cameras/"+cameraName+".conf") {|fh| fh.read})
		rescue
			res.body = eval(puts "Encountered reading the camera.conf file of: "+$!.to_s)
			# Stop after the error feedback.
			#break
		end
		# Put the camera image in the template return
		myTempRespond = '<img src="http://'+image_uid.to_s+':'+image_pwd.to_s+'@'+image_addr.to_s+''+image_url.to_s+'" alt="'+image_alt+'"></a>'
		GC.start
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/LiveSingle") {|fh| fh.read})
	end
end

##############################################################################################
# Monitor will be used as the basis to check the status of each camera that is listed from the
# conf dir at the time of the servlet creation. The tool will use the listing of files with
# the .conf extension.
##############################################################################################
class CMonitor < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera Monitor Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == MONITORUSER && pass == MONITORPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		myTempRespond = ""

		# 20061207 not as clean as I would like it but this seems to do the trick
		camera_array = Array.new
		cam_count = 0
		Dir["/etc/ifetch-tools/cameras/*.conf"].each do |cam_tmp|
			camera_array[cam_count] = File.basename(cam_tmp, ".conf").to_i
			cam_count = cam_count+1
		end
		camera_array.sort.each do |camera_num|
			# Now test for the status of the cameras lock file.
			lock_file = File::open("/var/lock/ifetch-tools/"+camera_num.to_s+".lock", 'w')
			# Table col 1 camera number, 2 - pid / mem, 3 - actions, 4 - status, 5 - log file, 6 - mdpp, 7 - information
			if lock_file.flock(File::LOCK_EX|File::LOCK_NB) == 0 then
				myTempRespond =	 myTempRespond+'<TR><TD><CENTER>'+camera_num.to_s+'</CENTER></TD>
				<TD><CENTER> - / - </CENTER></TD>
				<TD><A HREF="/camerastartstop?cameraName='+camera_num.to_s+'"><CENTER><IMG SRC="/start.jpg"></CENTER></A></TD>
				<TD><CENTER><IMG SRC="/grey.jpg"></CENTER></TD>
				<TD><A HREF="/log/'+camera_num.to_s+'.txt" target="_blank"><CENTER><IMG SRC="/log.jpg"></CENTER></A></TD>
				<TD><A HREF="/log/'+camera_num.to_s+'.mdpp.txt" target="_blank"><CENTER><IMG SRC="/mdpplog.jpg"></A></CENTER></TD>
				<TD>Camera ifetch is not running. </TD></TR>'
				# Really important here to close the file or you will get confused!
				lock_file.close
			else
				# Set the camera_pid to hold the pid of the running camera process.
				camera_pid = File.read("/var/run/ifetch-tools/"+camera_num.to_s+".pid").chomp
				# Put the camera pid and camera number in the table
				myTempRespond = myTempRespond+'<TD><CENTER><A HREF="/camera?cameraName='+camera_num.to_s+'"> '+camera_num.to_s+'</A></CENTER></TD>
				<TD><CENTER>'+camera_pid+' - '+%x[pmap -x #{camera_pid} | tail -1][10,40].strip+'</CENTER></TD>
				<TD><CENTER><A HREF="/camerastartstop?cameraName='+camera_num.to_s+'"><IMG SRC="/stop.jpg"></A></CENTER></TD>'
			# Set the logFileName
			logFileName = "/var/log/ifetch-tools/"+camera_num.to_s+".txt"
			# Remarked out the backtick with tail for a pure ruby try. Not for sure about performance but want to try.
			# logFileLastLine = `/usr/bin/tail -n 1 #{logFileName}`.split(/,/)
				# Put the notes of what the log file says in monitor.
				tempForLastLine=[]
				File.open(logFileName, "r") { |f|
					while f.gets
						tempForLastLine.push $_
						tempForLastLine.shift if $. > 1
					end
				}

				#tempForLastLine.each{|e| print e}
				logFileLastLine = tempForLastLine[0].split(/,/)

				# Below is for diagnostics
				#myTempRespond = myTempRespond+logFileLastLine[0]+logFileName
				if logFileLastLine[0] == "I" then
					myTempRespond = myTempRespond+'<TD><CENTER><IMG SRC="/green.jpg"></CENTER></TD><TD><A HREF="/log/'+camera_num.to_s+'.txt" target="_blank"><CENTER><IMG SRC="/log.jpg"></A></CENTER></TD><TD><A HREF="/log/'+camera_num.to_s+'.mdpp.txt" target="_blank"><CENTER><IMG SRC="/mdpplog.jpg"></A></CENTER></TD><TD> '+logFileLastLine[1]+" </TD></TR>"
				else
					myTempRespond = myTempRespond+'<TD><CENTER><IMG SRC="/red.jpg"></CENTER></TD><TD><A HREF="/log/'+camera_num.to_s+'.txt" target="_blank"><CENTER><IMG SRC="/log.jpg"></A><CENTER></TD><TD><A HREF="/log/'+camera_num.to_s+'.mdpp.txt" target="_blank"><CENTER><IMG SRC="/mdpplog.jpg"></A></CENTER></TD><TD> '+logFileLastLine[1]+" </TD></TR>"
				end
			end
		end
		GC.start
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/Monitor") {|fh| fh.read})
	end
end

##############################################################################################
# Display an image and a time stamp. of the system.
##############################################################################################
class ShowImage < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera History Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == HISTORYUSER && pass == HISTORYPASS
		}
		res['Content-Type'] = "text/html" # Tell the browser how we are talking
		raise HTTPServerError, 'Error: cameraName parameter not passed correctly.' if req.query['cameraName'] == nil || /[0-9]+/.match(req.query['cameraName']) == nil
		cameraName = /[0-9]+/.match(req.query['cameraName']).to_s
		imgFile = /([\/A-Za-z0-9_]+)+(.jpg|.png)/.match(req.query['imageName'])[0] # Crop out all the bad stuff.

		if imgFile == "images/ifetch.png" || imgFile == "images/missed.jpg"
			tmpIndex = 0
			theDate = "Welcome to camera number #{cameraName} history."
			pageToBuild = %{<IMG SRC=/data/#{imgFile}>}
			pageToBuildBasic = %{/data/#{imgFile}}
			pageToBuildVideo = %{/data/#{imgFile}}
		elsif imgFile != nil
			# tmpIndex will hold the index position of the page we are on.
			tmpIndex = File.basename(imgFile, ".jpg").split(/_/)[1].to_i
			theDate = File.mtime("/var/lib/ifetch-tools/#{imgFile}")
			pageToBuild = %{<A HREF="/camerahistory?cameraName=#{cameraName}&index=#{tmpIndex}&navigation=1&basicView=0" target="shownav"><IMG SRC="/data/#{imgFile}?timehash=#{theDate}"></A>}
			pageToBuildBasic = %{/camerahistory?cameraName=#{cameraName}&index=#{tmpIndex}&navigation=1&basicView=1}
			pageToBuildVideo = %{/camerahistory?cameraName=#{cameraName}&index=#{tmpIndex}&navigation=1&basicView=2}
		else
			pageToBuild = %{Error in image passing to ShowImage could be a security issue.}
		end
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/ShowImage") {|fh| fh.read})
	end
end

##############################################################################################
# Attempt to start all cameras.
##############################################################################################
class StartAllCameras < HTTPServlet::AbstractServlet
	def do_GET(req, res)
		HTTPAuth.basic_auth(req, res, 'Camera Monitor Realm') {|user, pass|
			# this block returns true if
			# authentication token is valid
			user == MONITORUSER && pass == MONITORPASS
		}
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/StartAllCameras") {|fh| fh.read})
		start_all_cameras
	end
end

##############################################################################################
# The below def should attempt to start every camera conf it can find.
##############################################################################################
def start_all_cameras
	Dir["/etc/ifetch-tools/cameras/*.conf"].each do |cam_tmp|
		cameraName = File.basename(cam_tmp, ".conf").to_i
		pid = fork {
			Process.setsid
			(0...2).each do |i|
				begin
					#closing stdin, stdout and stderr 0,1,2
					IO.for_fd(i).close
				rescue Errno::EBADF
				end
			end
			system_call = "/usr/bin/ifetch /etc/ifetch-tools/cameras/"+cameraName.to_s+".conf&"
			pid2 = fork { exec(system_call) }
			Process.detach(pid2)
			exit!
		}
		Process.detach(pid)
	end
end

##############################################################################################
# The below def should attempt to stop every camera pid it can find upon a shutdown request.
##############################################################################################
def stop_all_cameras
	Dir["/var/lock/ifetch-tools/*.pid"].each do |cam_tmp|
		cameraName = File.basename(cam_tmp, ".pid").to_i
		# Camera running action here
		# Set the camera_pid to hold the pid of the running camera process.
		camera_pid = File.read("/var/run/ifetch-tools/"+cameraName.to_s+".pid").chomp
		system_call = "kill -s 9 #{camera_pid} &"
		Process.detach( fork { system(system_call) } )
		puts "Shutdown signal received, attempting stop on"+cameraName.to_s+".pid"
	end
end

##############################################################################################
# The below should daemonize this code.
# 20090124 Nelson - Remarking out since I want init.d to catch the pid correct.
##############################################################################################
#exit if fork			# Parent exits, child continues.
#Process.setsid			# Become session leader.
#exit if fork			# Zap session leader. See [1].
#Dir.chdir "/"			# Release old working directory.
##File.umask 0000			# Ensure sensible umask. Adjust as needed.
#STDIN.reopen "/dev/null"	# Free file descriptors and
#STDOUT.reopen "/dev/null", "a"	# point them somewhere sensible.
#STDERR.reopen STDOUT		# STDOUT/ERR should better go to a logfile.

##############################################################################################
# Enable the logging operations we want below.
##############################################################################################
# Set up the log information
server_log = WEBrick::Log::new("/var/log/ifetch-tools/wwwifetch-server.txt", WEBrick::Log::DEBUG)
access_log = WEBrick::BasicLog::new("/var/log/ifetch-tools/wwwifetch-access.txt")
referer_log = WEBrick::BasicLog::new("/var/log/ifetch-tools/wwwifetch-referer.txt")
agent_log = WEBrick::BasicLog::new("/var/log/ifetch-tools/wwwifetch-agent.txt")
custom_log = WEBrick::BasicLog::new("/var/log/ifetch-tools/wwwifetch-custom.txt")

##############################################################################################
# Setup for layered approach for authentication.
##############################################################################################
authenticate_history = Proc.new do |req, res|
	HTTPAuth.basic_auth(req, res, 'History Level Authentication Required') do |user, pass|
		#user == 'foo' && password == 'bar'
		user == HISTORYUSER && pass == HISTORYPASS
	end
end

##############################################################################################
# Start the DRb service for use later.
##############################################################################################
DRb.start_service(nil,nil,{:load_limit => LOADLIMIT})


##############################################################################################
# Create the instance of the web server.
##############################################################################################
s = WEBrick::HTTPServer.new(
	:Port => 2000,
	#:DocumentRoot => "/usr/share/ifetch-tools/htdocs",
	:DocumentRoot => "/dev/null",
	:Logger         => server_log,
	:AccessLog      => [
	[ access_log, WEBrick::AccessLog::COMMON_LOG_FORMAT  ],
	[ referer_log,   WEBrick::AccessLog::REFERER_LOG_FORMAT ],
	[ agent_log,     WEBrick::AccessLog::AGENT_LOG_FORMAT   ],
	[ custom_log, "%a %U %T" ]  # peeraddr, Request-URI, process time
	]
	)

##############################################################################################
# Create some mount points for servlet navigation
##############################################################################################
# Monitor Operations
s.mount("/archive", CArchive)
s.mount("/live", CLive)
s.mount("/livesingle", CLiveSingle)
s.mount("/monitor", CMonitor)
s.mount("/camerastartstop", CameraStartStop)
s.mount("/startallcameras", StartAllCameras)

# History Operations
s.mount("/camera", Camera)
s.mount("/camerahistory", CameraHistory)
s.mount("/showimage", ShowImage)
# Below creates symlink to default area if no custom /var/lib/ifetch-tools/htdocs folder or symlink is found.
if File.exist?("/var/lib/ifetch-tools/htdocs") then
	puts "Symlink or folder exists for /var/lib/ifetch-tools/htdocs."
else
	puts "No symlink or folder exists for /var/lib/ifetch-tools/htdocs."
	puts "Attempting to create symlink to /usr/share/ifetch-tools/htdocs."
	File.symlink("/usr/share/ifetch-tools/htdocs", "/var/lib/ifetch-tools/htdocs")
end

s.mount('/', HTTPServlet::FileHandler, "/var/lib/ifetch-tools/htdocs/",
	:FancyIndexing => true,
	:HandlerCallback => authenticate_history # Hook up the authentication proc.
)

# The below is for .deb operations and good locations.
s.mount('/log/', WEBrick::HTTPServlet::FileHandler, '/var/log/ifetch-tools/')
s.mount('/data/', WEBrick::HTTPServlet::FileHandler, '/var/lib/ifetch-tools/')
# Added the below in an attempt to refer default images.
s.mount('/data/images/', WEBrick::HTTPServlet::FileHandler, '/usr/share/ifetch-tools/htdocs/')

# Catch the INT sig to shutdown
trap("INT"){
	puts "Shutdown signal received, stop all cameras."
	stop_all_cameras # Stop all running camera collection processes
	DRb.stop_service() # Stop the DRb on the webrick session
	s.shutdown
}

##############################################################################################
# Start the cameras
##############################################################################################
start_all_cameras

##############################################################################################
# Launch webrick
##############################################################################################
s.start

