File: wwwifetch

package info (click to toggle)
ifetch-tools 0.18.2-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 524 kB
  • sloc: ruby: 852; sh: 272; makefile: 4
file content (859 lines) | stat: -rwxr-xr-x 41,403 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
#!/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-2020 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.18.2"

##############################################################################################
# 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 'net/http'
require 'net/http/digest_auth'
#require 'logger'
# Unremark the below two lines if you want to try with high-performance.
#require 'rubygems'
#require 'webrick/highperformanceserver'
include WEBrick

##############################################################################################
# Define the way we pull in an image from a camera.
#
# This def will pull the image from the cam now and return the response.
##############################################################################################
def pullimage(address,port,url,uid,pwd,auth_type)
	Timeout.timeout(10) do
		# Here we determine if we need to handle as basic auth or digest
		if auth_type == 'digest' then
			uri = URI.parse('http://'+address.to_s+':'+port.to_s+''+url.to_s)
			uri.user = uid
			uri.password = pwd
			Net::HTTP.start(address, port) do |http|
				req_img = Net::HTTP::Get.new uri.request_uri
				digest_auth = Net::HTTP::DigestAuth.new
				response = http.request(req_img)
				# response is a 401 response with a WWW-Authenticate header
				auth = digest_auth.auth_header uri, response['www-authenticate'], 'GET'
				# create a new request with the Authorization header
				req_img = Net::HTTP::Get.new uri.request_uri
				req_img.add_field 'Authorization', auth
				# re-issue request with Authorization
				response = http.request req_img
				# Now send response back
				return response.body
			end
		else
			Net::HTTP.start(address, port) do |http|
				req_img = Net::HTTP::Get.new(url)
				#req_img.basic_auth 'username', 'password'
				req_img.basic_auth uid, pwd
				response = http.request(req_img)
				#puts "Code = #{response.code}"
				#puts "Message = #{response.message}"
				#response.each {|key, val| printf "%14s = %40.40s\n", key, val }
				## Nelson working		f.write(response.body)
				#Magick::Image.from_blob(response.body)[0];
				return response.body
			end
		end
	end
end

##############################################################################################
# This def will return an array of every camera status which has a .conf file.
##############################################################################################
def camerastatus(_camera_num)
	# Now test for the status of the cameras lock file.
	my_return= Array. new
	lock_file = File::open("/var/lock/ifetch-tools/"+_camera_num.to_s+".lock", 'w')
	if lock_file.flock(File::LOCK_EX|File::LOCK_NB) == 0 then
		my_return[0] = 0
		my_return[1] = "I, Collection offline!"
		# Really important here to close the file or you will get confused!
		lock_file.close
	else
		log_file_name = "/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.
		# last_line = `/usr/bin/tail -n 1 #{log_file_name}`.split(/,/)
		last_line= Array.new
		File.open(log_file_name, "r") { |f|
			while f.gets
				last_line.push $_
				last_line.shift if $. > 1
			end
		}
		my_return[0] = 1
		my_return[1] = last_line[0].split(/,/)
	end
	return my_return
end

##############################################################################################
# Suck in the config settings from the ifetch-tools.conf file.
##############################################################################################
def getlocalvars(_location, _sub_string)
	begin
		if _sub_string == nil then
			eval(File.open("/etc/ifetch-tools/ifetch-tools.conf") {|fh| fh.read})
		else
			File.readlines('/etc/ifetch-tools/ifetch-tools.conf').each do |line|
				if line.include?('_sub_string') then
					puts line
					eval(line)
				end
			end
		end
	rescue
		puts _location+": Error encountered reading the conf file of: "+$!.to_s
		# Stop after the error feedback.
		exit
	end
end

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

##############################################################################################
# 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-3]/.match(req.query['basicView']) == nil
		basicView = /[0-3]/.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
			# Keep track of image file names for javascript playback
			imgPlaybackList = 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(/,/)
				myTempRespond = myTempRespond+'<BR>'+imgTime+'<BR><A HREF="/camerahistory?cameraName='+cameraName+'&index='+imgIndex.to_s+'&navigation=1&basicView=3"><IMG SRC="/data/'+imgFile+'?timehash='+imgTime+'" width='+HISTORYIMAGEWIDTH+' height='+HISTORYIMAGEHEIGHT+'></A><BR>'
				# Push the image on the array javascript playback
				imgPlaybackList.push("/data/#{imgFile}?timehash=#{imgTime}")
				# 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="/camerahistory?cameraName='+cameraName+'&index='+imgIndex.to_s+'&navigation=1&basicView=3"><IMG SRC="/data/'+imgFile+'?timehash='+imgTime+'" width='+HISTORYIMAGEWIDTH+' height='+HISTORYIMAGEHEIGHT+'></A><BR>'
					# Push the image on the array javascript playback
					imgPlaybackList.push("/data/#{imgFile}?timehash=#{imgTime}")
					# 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["999999999999"] = '<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=3">'+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.reverse.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>
					<div id="jump">< <A HREF="/camerahistory?cameraName=#{cameraName}&index=#{index.to_i-IMAGESPERPAGE}&navigation=1&basicView=3">Previous</A>
				}
			else
				myNavigation = %{<FORM NAME="myform">
					<SELECT name="mylist" onChange="disp_text();">
					#{myJumpNavigation}
					</SELECT></FORM>
					<div id="jump">< <A HREF="/camerahistory?cameraName=#{cameraName}&index=#{index.to_i-IMAGESPERPAGE+imgSortedLength-1}&navigation=1&basicView=3">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=3">Next</A> ></div>
				}
			else
				myNavigation = %{#{myNavigation} | <A HREF="/camerahistory?cameraName=#{cameraName}&index=#{indexSentry}&navigation=1&basicView=3">Next</A> ></div>
				}
			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."+$video_export_type)
				mtype = WEBrick::HTTPUtils::mime_type("#{dir}/tmpVideo."+$video_export_type, WEBrick::HTTPUtils::DefaultMimeTypes)
				res['Content-Type'] = mtype
				res['Content-Disposition'] = "inline; filename=\"video."+$video_export_type
				puts mtype
				res.body = File.open("#{dir}/tmpVideo."+$video_export_type,"rb") {|io| io.read}
			}
		# elsif basicView.to_i == 4
		#	# If we are here then we need to setup for javascript playback
		#	# Now generate the page with the correct substitution.
		#	# Unify history playback here while dropping ShowImage method
		#	# tmpIndex will hold the index position of the page we are on.
		#	res.body = eval(File.open("/usr/share/ifetch-tools/templates/CameraHistoryPlayback") {|fh| fh.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 = ""
		###############################################################
		# Setup the image_ vars we are going to need
		image_alt = "no_image_alt"
		Dir["/etc/ifetch-tools/cameras/*.conf"].sort.each do |cam_tmp|
			###############################################################
			# First source in the locals from ifetch-tools.conf file
			getlocalvars('CArchive Class', 'image_')
			# Now let the camera.conf file overload the settings they want.
			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
			# Add the inline image and link to the page.
			# If cam offline drop archive link
			cam_status = camerastatus(File.basename(cam_tmp, ".conf"))
			if cam_status[0] == 0 then
				myTempRespond = myTempRespond+'<img src="/snapshot?cameraName='+File.basename(cam_tmp, ".*")+'" class="camImage" id="'+File.basename(cam_tmp, ".conf")+'" alt="'+image_alt+'" width="'+HISTORYIMAGEWIDTH+'" height="'+HISTORYIMAGEHEIGHT+'" >'
			else
				myTempRespond = myTempRespond+'<a href="/camerahistory?cameraName='+File.basename(cam_tmp, ".conf").to_s+'&index=0&navigation=0&basicView=0" title="'+image_alt+'"><img src="/snapshot?cameraName='+File.basename(cam_tmp, ".*")+'" class="camImage" id="'+File.basename(cam_tmp, ".conf")+'" alt="'+image_alt+'" width="'+HISTORYIMAGEWIDTH+'" height="'+HISTORYIMAGEHEIGHT+'" ></a>'
			end
		end
		GC.start
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/Archive") {|fh| fh.read})
	end
end

##############################################################################################
# CFile returns newest image for a given camera uploading images to folder.
##############################################################################################
class CFile < 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'] = "image/jpeg" # 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 = IO.read(Dir['/var/lib/ifetch-tools/htdocs/'+cameraName+'/*.jpg'].sort_by(&File.method(:ctime)).last.to_s)
		imgArray = Dir['/var/lib/ifetch-tools/htdocs/'+cameraName+'/*.jpg'].sort_by(&File.method(:ctime))
		res.body = IO.read(imgArray[imgArray.length - 1].to_s)
	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 = ""
		###############################################################
		# Setup the image_ vars we are going to need
		image_alt = "no_image_alt"
		Dir["/etc/ifetch-tools/cameras/*.conf"].sort.each do |cam_tmp|
			# Camera collection being attempted
			###############################################################
			# First source in the locals from ifetch-tools.conf file
			getlocalvars('CLive Class', 'image_')
			# Now let the camera.conf file overload the settings they want.
			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
			# Add the inline image and link to the page.
			myTempRespond = myTempRespond+'<a href="/livesingle?cameraName='+File.basename(cam_tmp, ".conf")+'" title="'+image_alt+'"><img src="/snapshot?cameraName='+File.basename(cam_tmp, ".*")+'" class="camImage" id="'+File.basename(cam_tmp, ".conf")+'" alt="'+image_alt+'" width="'+HISTORYIMAGEWIDTH+'" height="'+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 = ""
		###############################################################
		# Setup the image_ vars we are going to need
		image_alt = "no_image_alt"
		image_sleep = "1"
		# First check camera status
		###############################################################
		# First source in the locals from ifetch-tools.conf file
		getlocalvars('CLiveSingle Class', 'image_')
		# Now let the camera.conf file overload the settings they want.
		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
		# Add the inline image and link to the page.
		# Put the camera image in the template return
		myTempRespond = '<img height="80%" src="/snapshot?cameraName='+cameraName+'" id="myImage" 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.
			# Table col 1 camera number, 2 - pid / mem, 3 - actions, 4 - status, 5 - log file, 6 - information
			cam_status = camerastatus(camera_num)
			if cam_status[0] == 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>Camera ifetch is not running. </TD></TR>'
				# Really important here to close the file or you will get confused!
			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="/camerahistory?cameraName='+camera_num.to_s+'&index=0&navigation=0&basicView=0"> '+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>'

				if cam_status[1][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> '+cam_status[1][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> '+cam_status[1][1]+" </TD></TR>"
				end
			end
		end
		GC.start
		res.body = eval(File.open("/usr/share/ifetch-tools/templates/Monitor") {|fh| fh.read})
	end
end

##############################################################################################
# CSnapshot pulls a live snapshot of a given camera.
##############################################################################################
class CSnapshot < 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'] = "image/jpeg" # 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
		cam_status = camerastatus(cameraName)
		if cam_status[0] == 0 then
			res.body = IO.read('/usr/share/ifetch-tools/htdocs/ccoffline.jpg')
		else
			###############################################################
			# Setup the image_ vars we are going to need
			image_addr = nil
			image_addr_port = nil
			image_auth_type='basic'
			image_uid = "no_image_uid"
			image_url = "no_image_url"
			image_pwd = "no_image_pwd"
			###############################################################
			# First source in the locals from ifetch-tools.conf file
			getlocalvars('CSnapshot Class', 'image_')
			# Now let the camera.conf file overload the settings they want.
			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
			# Add the inline image and link to the page.
			# Put the camera image in the template return
			if cam_status[1][0] == "I" then
				# return the current camera snapshot image.
				res.body = pullimage(image_addr,image_addr_port,image_url,image_uid,image_pwd,image_auth_type)
			else
				res.body = IO.read('/usr/share/ifetch-tools/htdocs/ccerror.jpg')
			end
		end
	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.

##############################################################################################
# Setup local vars
##############################################################################################
getlocalvars("Main Block", nil)

##############################################################################################
# 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("/snapshot", CSnapshot)
s.mount("/cfile", CFile)
s.mount("/live", CLive)
s.mount("/livesingle", CLiveSingle)
s.mount("/monitor", CMonitor)
s.mount("/camerastartstop", CameraStartStop)
s.mount("/startallcameras", StartAllCameras)

# History Operations
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