ifetch-tools-0.18.5/0000755000175000017500000000000014115452242012723 5ustar useruserifetch-tools-0.18.5/examples/0000755000175000017500000000000014115200051014526 5ustar useruserifetch-tools-0.18.5/examples/ifetch-tools.conf0000644000175000017500000004046714115200051020010 0ustar useruser##################################################################################### # 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-2021 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. ################################################################################## ################# DO NOT RUN ifetch-tools BY DEFAULT ############################# ################################################################################## ## For security the below exit line prevents the start of ifetch and wwwifetch. ## Please set the HISTORYUSER, HISTORYPASS, MONITORUSER, and MONITORPASS to what ## you want,then remark out the lines below when you are ready to start using the ## program. ## ## ################################################################################## ################# DO NOT RUN ifetch-tools BY DEFAULT ############################# ################################################################################## ################################################################################## ###################### BEGIN CONSTANT VARIABLE CONFIG ############################ ################################################################################## # Variables in the CONSTANT VARIABLE section are constants used throughout. ####################################################### ## security settings ####################################################### # user name and password for the camera history operations. HISTORYUSER="history" HISTORYPASS="history" # user name and password for the camera monitor operations. MONITORUSER="monitor" MONITORPASS="monitor" ####################################################### ## Web Page Settings ####################################################### ######################### # This sets the home page to navigate to if you do not want to drop one in # in the ifetch-tools/htdocs/index.html . You should put in a fully qualified # link, something like the below # HOMELINK="http://somepage.somewhere/page.html" HOMELINK="/" ####################################################### ## camera history navigation ####################################################### # set the size of the image on the navigation pane. HISTORYIMAGEWIDTH="240" HISTORYIMAGEHEIGHT="180" ####################################################### ## system settings ####################################################### ######################### # Set the archive and live image update time. ALIMGUPDATETIME=1000 ######################### # Set the base port to add to camera names. BASEPORT=60000 ######################### # Set the sleep when a collection process hits an error. ERRORSLEEP=5 ######################### # Set some variables for the wwwifetch.rb file. # Images to show per page. IMAGESPERPAGE=150 ######################### # This is the max exchange load limit between ifetch.rb and wwwifetch.rb. 50mb = 52428800 LOADLIMIT = 52428800 ###### CAUTION - CAUTION - CAUTION - CAUTION - CAUTION - CAUTION - CAUTION - ###### # Set this variable for history tune. This number has to be prime and I would advise you read # the code in ifetch.rb for how this works. For my development area I will be setting MODPRIME # to 101. This setting is dangerous! This number must be prime and you need to read the code to # understand that it is for directory storage structure. If you change this number after launching # once, you will need to smoke your history. I hope that you understand what I am trying to tell # you in this section. Please, please do not tamper with unless you understand! # # Here are some other prime numbers to adjust to your storage needs. # # 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 # 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 # 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 # 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 # 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 # 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 # 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 # MODPRIME = 101 # ###### END CAUTION - END CAUTION - END CAUTION - END CAUTION - END CAUTION - ###### ################################################################################## ######################## END CONSTANT VARIABLE CONFIG ############################ ################################################################################## ################################################################################## ################### BEGIN NON-CONSTANT VARIABLE CONFIG ########################### ################################################################################## ## Variables in the NON-CONSTANT VARIABLE section are dynamic and can be overriden ## in the various conf files. They are placed here to be used as system defaults ## that again can be overriden in various conf files. So if you are familar with ## these variables, please tune them as you wish. If you are not familar with these ## variables, PLEASE READ THE CODE AND UNDERSTAND WHAT THEY DO before you change ## any of them. ##################################################################################### ## Set the location for data_location ##################################################################################### ## The data_location variable is if your storage is somewhere else other than ## ifetch-tools/htdocs . Do not include a trailing / and do not include the camera ## number. Example line is below: ## ## data_location="/path/to/camera/storage" ##################################################################################### ## Set the options for day_image_sleep ##################################################################################### ## The below is for a feature add allowing for a subset of the entire day to have a ## a different sleep padding on a given time range. So on each day of the week you ## can have a time range where image collection padding is different than what is ## specified with image_sleep. ## ## O is Sun, 1 is Mon, 2 is Tue, 3 is Wed, 4 is Thu, 5 Fri, 6 is Sat ## On a given day "00:00,00:00,1" implies "startTime,stopTime,sleepDuration" ## See the example line is below: ## day_image_sleep={ 0 => "00:00,00:00,1", 1 => "00:00,00:00,1", 2 => "00:00,00:00,1", 3 => "00:00,00:00,1", 4 => "00:00,00:00,1", 5 => "00:00,00:00,1", 6 => "00:00,00:00,1"} #################################################################################### ## Set the options for image_uid, image_pwd, and image_auth_type ##################################################################################### ## The image_uid is the user name and the image_pwd is the password to use if ## using security on the remote camera. The image_auth_type is for basic (the default) ## or digest. image_alt is for dynamic page creation with img src alt tag. image_uid='' image_pwd='' image_auth_type='basic' image_alt='no_image_alt' #################################################################################### ## Set the option for image_addr and image_addr_port ##################################################################################### ## The image_addr is the IP address of the remote camera and the image_addr_port is ## the port the camera is listening on. ## Example line is below: ## image_addr='127.0.0.1' image_addr_port=80 ##################################################################################### ## Set the option for image_count ##################################################################################### ## The image_count is the number of images to store history. image_count=200 ##################################################################################### ## Set the option for image_count_on_motion ##################################################################################### ## The image_count_on_motion is the number of images to store once motion is detected ## without another motion test on images. image_count_on_motion=20 ##################################################################################### ## Set the option for log_files ##################################################################################### ## The log_files is the number of log files to keep in rotation. log_files=10 ##################################################################################### ## Set the option for image_sleep ##################################################################################### ## The image sleep is the padding between image downloads. Each download is a complete ## http transaction with an open, download, and close operation. Tuning will depend on ## each individual setup. For partial second sleep use something like 0.5 image_sleep=1 ##################################################################################### ## Set the option for log_size ##################################################################################### ## The log_size is the size in kb for a rotation. log_size=1000 ##################################################################################### ## Set the option for image_url ##################################################################################### ## This is the url to get the current image of the camera. ############################## ## Amcrest MODEL: IP3M-954E ## http://user:password@xxx.xxx.xxx.xxx/cgi-bin/snapshot.cgi ## ## image_url='/cgi-bin/snapshot.cgi' ## ############################## ## Apexis MODEL: APM-J011-WS ## http://user:password@xxx.xxx.xxx.xxx/snapshot.cgi ## ## image_url='/snapshot.cgi' ## ############################## ## Axis cameras are the default setting. ## image_url='/jpg/image.jpg' ## ############################## ## D-Link DCS-930L ## image_url='/image.jpg' ## ############################## ## Hawking HNC320W wireless ## image_url='/IMAGE.JPG' ## ############################## ## IGuard IP-250E ## According to firmware upgrade 2.55.cv7 ## ## http://xxx.xxx.xxx.xxx/pda.cgi?user=admin&password=1234&page=image&cam=1 ## ## And the following is configured as below ## xxx.xxx.xxx.xxx - the IP address of the camera ## admin - the login account name ## 1234 - the login password ## 1 - the camera number ## ## So the image_url would look something like: ## image_url='/pda.cgi?user=admin&password=1234&page=image&cam=1' ## ############################## ## IPC-E36000 ## image_url='/jpgimage/1/image.jpg' ## ############################## ## IPCamera 510 ## ## image_url='/image.jpg' ## ############################## ## IPcam P1000 from Plustek ## ## http://xxx.xxx.xxx.xxx/snapshot.jpg?account=admin&password=1234` ## ## And the following is configured as below ## xxx.xxx.xxx.xxx - the IP address of the camera ## admin - the login account name for the admin username ## 1234 - the login password for the admin username ## ## So the image_url would look something like: ## image_url='/snapshot.jpg?account=admin&password=1234' ## ############################## ## Panasonic BL-C210A ## ## http://xxx.xxx.xxx.xxx/SnapshotJPEG?Resolution=640x480&Quality=Clarity ## ## And the following is configured as below ## xxx.xxx.xxx.xxx - the IP address of the camera ## Resolution - 320x240 640x480 ## Quality - Motion Standard Clarity ## ### So the image_url would look something like: ## image_url='/SnapshotJPEG?Resolution=640x480&Quality=Clarity' ## ############################## ## Reolink RLC-410 ## ## http://xxx.xxx.xxx.xxx/cgi-bin/api.cgi?cmd=Snap&channel=0&user=admin&password=1234 ## ## And the following is configured as below ## xxx.xxx.xxx.xxx - the IP address of the camera ## admin - the login account name for the admin username ## 1234 - the login password for the admin username ## ## So the image_url would look something like: ## image_url='/cgi-bin/api.cgi?cmd=Snap&channel=0&user=admin&password=1234' ## ############################## ## SV3C ProHD 1080p ## image_url='/snapshot.cgi/stream=1' ## ############################# ## TPLink NC220 ## image_url='/stream/snapshot.jpg' ## ## Snapshot pulls appear to have port hard coded at 8080 so include the below ## in your config: ## ## image_addr_port=8080 ## ## And security for the pull at time of writing requires that you pass the ## base64 encoded password you configured in the web interface. ## ############################## ## Trendnet TV-IP201 use the below: ## image_url='/goform/capture' ## ############################## ## Trendnet TV-IP301 use the below: ## image_url='/goform/video' ## ############################## ## Trendnet TV-IP314PI ## image_url='/ISAPI/streaming/channels/1/picture' ## ############################## ## TV-IP320PI ## image_url='/streaming/channels/1/picture' ## image_auth_type='digest' ## ############################## ## Trendnet TV-IP512P use the below: ## image_url='/image/jpeg.cgi' ## ############################## ## Toshiba IK-WR01A Orite IC301 use the below: ## image_url='/Jpeg/CamImg.jpg' ## ############################## ## Wanscam IP Camera: ## image_url='/snapshot.cgi' ##################################################################################### ## Set the option for image_watermark ##################################################################################### ## The image_watermark when true writes the collection time on the image. ## The value is either 0 for True or 1 for False. ## The default is 1. image_watermark=1 ##################################################################################### ## Set the option for marshal_dump_enable ##################################################################################### ## The marshal dump option is an attempt to help the indexing of time stamps after a ## shutdown,reboot, or a service restart. ## The marshal_dump_enable is either 0 for True or 1 for False. ## The default is 1. marshal_dump_enable=1 ##################################################################################### ## Set the option for motion_enable ##################################################################################### ## The motion_enable is either 0 for True or 1 for False. ## The default is 1. motion_enable=1 ##################################################################################### ## Set the option for motion_mdpp ##################################################################################### ## The motion_mdpp is used to adjust the threshold of motion detection. motion_mdpp=0.002 ##################################################################################### ## Set the option for motion_sleep ##################################################################################### ## The motion_sleep is if you want to shorten the sleep when motion is detected. motion_sleep=0.5 ##################################################################################### ## Set the option for $video_export_type ##################################################################################### ## The $video_export_type sets the video export type. It is set only from ## this file for wwwifetch. The default is mp4. $video_export_type='mp4' ##################################################################################### ## Set the option for $video_export_delay ##################################################################################### ## The $video_export_delay sets the video delay between frames. It is set only from ## this file for wwwifetch. The default is 50. $video_export_delay=50 ################################################################################## ##################### END NON-CONSTANT VARIABLE CONFIG ########################### ################################################################################## ifetch-tools-0.18.5/examples/cameras/0000755000175000017500000000000014113266077016163 5ustar useruserifetch-tools-0.18.5/examples/cameras/camera_number.conf0000644000175000017500000002546514113266077021646 0ustar useruser##################################################################################### # 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-2021 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. #################################################################################### #################################################################################### # General notes on the config file are as follows. # - Each camera needs a config file for operations. # - Default settings are stored in the /etc/ifetch-tools/ifetch-tools.conf file now. # - This file is used to override the default options in ifetch-tools.conf. #################################################################################### # Set the location for data_location #################################################################################### # The data_location variable is if your storage is somewhere else other than # ifetch-tools/htdocs . Do not include a trailing / and do not include the camera # number. Example line is below: # # data_location="/path/to/camera/storage" #################################################################################### # Set the options for day_image_sleep #################################################################################### # The below is for a feature add allowing for a subset of the entire day to have a # a different sleep padding on a given time range. So on each day of the week you # can have a time range where image collection padding is different than what is # specified with image_sleep. # # O is Sun, 1 is Mon, 2 is Tue, 3 is Wed, 4 is Thu, 5 Fri, 6 is Sat # On a given day "00:00,00:00,1" implies "startTime,stopTime,sleepDuration" # See the example line is below: # # day_image_sleep={ 0 => "00:00,00:00,1", 1 => "00:00,00:00,1", 2 => "00:00,00:00,1", 3 => "00:00,00:00,1", 4 => "00:00,00:00,1", 5 => "00:00,00:00,1", 6 => "00:00,00:00,1"} #################################################################################### ## Set the options for image_uid, image_pwd, image_auth_type, and image_alt ##################################################################################### ## The image_uid is the user name and the image_pwd is the password to use if ## using security on the remote camera. The image_auth_type is for basic (the default) ## or digest. image_alt is for dynamic page creation with img src alt tag. # Example lines are below: # # image_uid='' # image_pwd='' # image_auth_type='basic # image_alt='no_image_alt' #################################################################################### # Set the option for image_addr #################################################################################### # The image_addr is the IP address of the remote camera and the image_addr_port is # the port the camera is listening on. # Example line is below: # # image_addr='127.0.0.1' # image_addr_port=80 #################################################################################### # Set the option for image_count #################################################################################### # The image_count is the number of images to store history. # Example line is below: # # image_count=1 #################################################################################### # Set the option for image_count_on_motion #################################################################################### # The image_count_on_motion is the number of images to store once motion is detected # without another motion test on images. # image_count_on_motion=20 #################################################################################### # Set the option for image_sleep #################################################################################### # The image sleep is the padding between image downloads. Each download is a complete # http transaction with an open, download, and close operation. Tuning will depend on # each individual setup. # If you use a float use the example of 0.5 and the example # line is below: # # image_sleep=0 #################################################################################### # Set the option for image_url #################################################################################### # This is the url to get the current image of the camera. # ############################# # Amcrest MODEL: IP3M-954E # http://user:password@xxx.xxx.xxx.xxx/cgi-bin/snapshot.cgi # # image_url='/cgi-bin/snapshot.cgi' # ############################# # Apexis MODEL: APM-J011-WS # http://user:password@xxx.xxx.xxx.xxx/snapshot.cgi # # image_url='/snapshot.cgi' # ############################# # Axis cameras use the below: # image_url='/jpg/image.jpg' # ############################# # D-Link DCS-930L # image_url='/image.jpg' # ############################# # Hawking HNC320W wireless # image_url='/IMAGE.JPG' # ############################# # IGuard IP-250E # According to firmware upgrade 2.55.cv7 # # http://xxx.xxx.xxx.xxx/pda.cgi?user=admin&password=1234&page=image&cam=1 # # And the following is configured as below # xxx.xxx.xxx.xxx - the IP address of the camera # admin - the login account name # 1234 - the login password # 1 - the camera number # # So the image_url would look something like: # image_url='/pda.cgi?user=admin&password=1234&page=image&cam=1' # ############################# # IPC-E36000 # image_url='/jpgimage/1/image.jpg' # ############################# # IPCamera 510 # # image_url='/image.jpg' # ############################# # IPcam P1000 from Plustek # # http://xxx.xxx.xxx.xxx/snapshot.jpg?account=admin&password=1234` # # And the following is configured as below # xxx.xxx.xxx.xxx - the IP address of the camera # admin - the login account name for the admin username # 1234 - the login password for the admin username # # So the image_url would look something like: # image_url='/snapshot.jpg?account=admin&password=1234' # ############################# # Panasonic BL-C210A # # http://xxx.xxx.xxx.xxx/SnapshotJPEG?Resolution=640x480&Quality=Clarity # # And the following is configured as below # xxx.xxx.xxx.xxx - the IP address of the camera # Resolution - 320x240 640x480 # Quality - Motion Standard Clarity # ## So the image_url would look something like: # image_url='/SnapshotJPEG?Resolution=640x480&Quality=Clarity' # ############################# # Reolink RLC-410 # # http://xxx.xxx.xxx.xxx/cgi-bin/api.cgi?cmd=Snap&channel=0&user=admin&password=1234 # # And the following is configured as below # xxx.xxx.xxx.xxx - the IP address of the camera # admin - the login account name for the admin username # 1234 - the login password for the admin username # # So the image_url would look something like: # image_url='/cgi-bin/api.cgi?cmd=Snap&channel=0&user=admin&password=1234' # ############################## # SV3C ProHD 1080p # image_url='/snapshot.cgi/stream=1' # ############################## # TPLink NC220 # image_url='/stream/snapshot.jpg' # # Snapshot pulls appear to have port hard coded at 8080 so include the below # in your config: # # image_addr_port=8080 # # And security for the pull at time of writing requires that you pass the # base64 encoded password you configured in the web interface. # ############################# # Trendnet TV-IP201 use the below: # image_url='/goform/capture' # ############################# # Trendnet TV-IP301 use the below: # image_url='/goform/video' # ############################# # Trendnet TV-IP314PI # image_url='/ISAPI/streaming/channels/1/picture' # ############################# # TV-IP320PI # image_url='/streaming/channels/1/picture' # image_auth_type='digest' # ############################# # Trendnet TV-IP512P use the below: # image_url='/image/jpeg.cgi' # ############################# # Toshiba IK-WR01A Orite IC301 use the below: # image_url='/Jpeg/CamImg.jpg' # ############################# # Wanscam IP Camera: # image_url='/snapshot.cgi' #################################################################################### # Set the option for image_watermark #################################################################################### # The image_watermark when true writes the collection time on the image. # The value is either 0 for True or 1 for False. # The default is 1. # image_watermark=1 #################################################################################### # Set the option for log_files #################################################################################### # The log_files is the number of log files to keep in rotation. # Example line is below: # # log_files=5 #################################################################################### # Set the option for log_size #################################################################################### # The log_size is the size in kb for a rotation. # Example line is below: # # log_size=100 ##################################################################################### # Set the option for marshal_dump_enable ##################################################################################### # The marshal dump option is an attempt to help the indexing of time stamps after a # shutdown,reboot, or a service restart. # The marshal_dump_enable is either 0 for True or 1 for False. # The default is 1. # marshal_dump_enable=1 #################################################################################### # Set the option for motion_enable #################################################################################### # The motion_enable is either 0 for True or 1 for False. # The default is 1. # motion_enable=1 ##################################################################################### ## Set the option for motion_mdpp ##################################################################################### ## The motion_mdpp is used to adjust the threshold of motion detection. # motion_mdpp=0.002 #################################################################################### # Set the option for motion_sleep #################################################################################### # The motion_sleep is if you want to shorten the sleep when motion is detected. # motion_sleep=0.5 ifetch-tools-0.18.5/README.txt0000644000175000017500000000340214113266077014427 0ustar useruser#################################################################################### # 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-2021 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. #################################################################################### # Notes for README.txt #################################################################################### # - As of ifetch-tools-0.15.0 you need to install via the .deb and start and stop # from /etc/init.d/ifetch-tools # - ifetch-tools will not start until you edit the /etc/ifetch-tools/ifetch-tools.conf # - Please read the available options in the /etc/ifetch-tools/ifetch-tools.conf file. To access a camera history once configured try something like the following URL: http://localhost:2000/camera?cameraName=camera# where exampleCamera would be the ending dir name in the htdocs directory. To check the status of your cameras try something like the following URL: http://localhost:2000/monitor ifetch-tools-0.18.5/wwwifetch0000755000175000017500000012363614115452242014673 0ustar useruser#!/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-2021 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.5" ############################################################################################## # 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) # 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 response = Net::HTTP.start(address, port, open_timeout: 5, read_timeout: 5) do |http| req_img = Net::HTTP::Get.new uri.request_uri digest_auth = Net::HTTP::DigestAuth.new temp_response = http.request(req_img) # response is a 401 response with a WWW-Authenticate header auth = digest_auth.auth_header uri, temp_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 http.request(req_img) end else response = Net::HTTP.start(address, port, open_timeout: 5, read_timeout: 5) do |http| req_img = Net::HTTP::Get.new(url) req_img.basic_auth uid, pwd http.request(req_img) end end # Now send response back return response.body 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" last_line = `/usr/bin/tail -n 1 #{log_file_name}`.split(/,/) my_return[0] = 1 my_return[1] = last_line 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+'
'+imgTime+'

' # 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+'
'+imgTime+'

' # 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+'\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] = '\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 = %{
Please select a time.
} 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 = %{
< Previous } else myNavigation = %{
< Previous } end # Here is the move forward between page code if index.to_i+IMAGESPERPAGE <= imgSortedLength-1 myNavigation = %{#{myNavigation} | Next >
} else myNavigation = %{#{myNavigation} | Next >
} end end if basicView.to_i == 1 # If we are here we are asking to build a page with images only. myTempRespond.gsub!(/(]*)width=\d+ height=\d+/i, '\\1') myTempRespond.gsub!(/(
/i,"A>

") # 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 = "" if req.query['cameraList'] == nil cameraList = Dir["/etc/ifetch-tools/cameras/*.conf"] else raise HTTPServerError, 'Error: cameraList parameter not passed correctly.' if /[0-9_]+/.match(req.query['cameraList']) == nil cameraList = /[0-9_]+/.match(req.query['cameraList']).to_s.split("_") #cameraList.reject { |item| item.nil? || item == '' } cameraList.map! do |cam_tmp| cam_tmp = '/etc/ifetch-tools/cameras/'+cam_tmp+'.conf' #myTempRespond = myTempRespond+"
"+cam_tmp+" -- "+cam_tmp.class.to_s end end ############################################################### # Setup the image_ vars we are going to need image_alt = "no_image_alt" cameraList.each do |cam_tmp| #cam_tmp = '/etc/ifetch-tools/cameras/'+cam_tmp+'.conf' if File.exist?(cam_tmp) then # Camera collection being attempted ############################################################### # 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+''+image_alt+'' else myTempRespond = myTempRespond+''+image_alt+'' end else myTempRespond = myTempRespond+'*** Unable to locate '+cam_tmp+' file.! ***' 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/rtsp/'+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 = "" if req.query['cameraList'] == nil cameraList = Dir["/etc/ifetch-tools/cameras/*.conf"] else raise HTTPServerError, 'Error: cameraList parameter not passed correctly.' if /[0-9_]+/.match(req.query['cameraList']) == nil cameraList = /[0-9_]+/.match(req.query['cameraList']).to_s.split("_") #cameraList.reject { |item| item.nil? || item == '' } cameraList.map! do |cam_tmp| cam_tmp = '/etc/ifetch-tools/cameras/'+cam_tmp+'.conf' #myTempRespond = myTempRespond+"
"+cam_tmp+" -- "+cam_tmp.class.to_s end #myTempRespond = myTempRespond+"
"+cameraList.length.to_s #tmpCameraList.split("_").sort.each do |camera_num| # camera_num = '/etc/ifetch-tools/cameras/'+camera_num+'.conf' # #myTempRespond = myTempRespond+"
"+camera_num #end end #cameraList = Dir["/etc/ifetch-tools/cameras/*.conf"] #cameraList.sort.each do |cam_tmp| # cam_tmp = '/etc/ifetch-tools/cameras/'+cam_tmp+'.conf' # myTempRespond = myTempRespond+"
"+cam_tmp+" -- "+cam_tmp.class.to_s # #myTempRespond = myTempRespond+"
* "+cam_tmp #end ############################################################### # Setup the image_ vars we are going to need image_alt = "no_image_alt" #myTempRespond = myTempRespond+"
Getting ready to spin the cameras!" cameraList.each do |cam_tmp| #cam_tmp = '/etc/ifetch-tools/cameras/'+cam_tmp+'.conf' if File.exist?(cam_tmp) then # 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+''+image_alt+'' else myTempRespond = myTempRespond+'*** Unable to locate '+cam_tmp+' file.! ***' end 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 = ''+image_alt+'' 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+'

'+camera_num.to_s+'
- / -
Camera ifetch is not running. ' # 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+'
'+camera_num.to_s+'
'+camera_pid+' - '+%x[pmap -x #{camera_pid} | tail -1][10,40].strip+'
' if cam_status[1][0] == "I" then myTempRespond = myTempRespond+'
'+cam_status[1][1]+" " else myTempRespond = myTempRespond+'
'+cam_status[1][1]+" " 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/loading.gif') 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 = %{} 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 = %{} 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 ifetch-tools-0.18.5/ifetch0000755000175000017500000007125414115452226014126 0ustar useruser#!/usr/bin/ruby #################################################################################### # ifetch-tools is a set of tools in ruby that can collect images from ip based cameras, # monitor collection process, and provide an interface to view collected history. # Copyright (C) 2005-2021 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 the current version information. ############################################################################################## VER = "0.18.5" ############################################################################################## # The below should daemonize this code. ############################################################################################## #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. #File.umask 0177 # 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. ############################################################################################## # Set some options to ensure memory management. ############################################################################################## RMAGICK_ENABLE_MANAGED_MEMORY = true GC.enable ############################################################################################## # Do the require and include stuff we need for our operations. ############################################################################################## require 'English' require 'drb' require 'fileutils' require 'logger' require 'uri' require 'net/http' require 'net/http/digest_auth' require 'time' #require "rubygems" #gem "rmagick", "= 2.15.3" require 'rmagick' include Magick ############################################################################################## # Initialize the global array for the image access info with all nil values. ############################################################################################## $image_array = Array.new ############################################################################################## # Initialize the global variables to assist RMagick in memory help. ############################################################################################## $image_temp = nil $mdpp = nil ############################################################################################## # Initialize various variables that we intend to pull in with nil values. ############################################################################################## data_location=nil day_image_sleep=nil image_addr=nil image_addr_port=nil image_auth_type=nil image_count=nil image_count_on_motion=nil image_pwd=nil image_sleep=nil image_uid=nil image_url=nil image_watermark=nil log_files=nil log_size=nil marshal_dump_enable=nil marshal_dump_sentry=1 motion_enable=nil motion_mdpp=nil motion_sleep=nil sentry_gc_count=0 ############################################################################################## # Define a class for our DRB communications. ############################################################################################## class DataExchangeServer def xchange_array $image_array end end ############################################################################################## # 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) # 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 response = Net::HTTP.start(address, port, open_timeout: 5, read_timeout: 5) do |http| req_img = Net::HTTP::Get.new uri.request_uri digest_auth = Net::HTTP::DigestAuth.new temp_response = http.request(req_img) # response is a 401 response with a WWW-Authenticate header auth = digest_auth.auth_header uri, temp_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 http.request(req_img) end else response = Net::HTTP.start(address, port, open_timeout: 5, read_timeout: 5) do |http| req_img = Net::HTTP::Get.new(url) req_img.basic_auth uid, pwd http.request(req_img) end end # Now send response back return response.body end ############################################################################################## # The two def below are modified from gpl code at: # http://spodzone.org.uk/packages/hawking-HNC230G-motion-detect.txt # Copyright Tim Haynes 2006- # Distributable under the terms of the Gnu Public Licence (GPL) - see # . ############################################################################################## ############################################################################################## # Define process(image) # massage an image to be suitable for comparison # here we compute the luminosity channel since rmagick's builtin doesn't work # and scale it down to half/quarter-size to remove tiny transient errors. ############################################################################################## #def process(image) # #image.colorize(0.25, 0.6, 0.15, 1, Magick::Pixel::from_color("grey")).modulate(1.25,0.01,1).scale(0.5) # #image_temp=Magick::Image.from_blob(image)[0] # #image_temp.colorize(0.25, 0.6, 0.15, 1, Magick::Pixel::from_color("grey")).modulate(1.25,0.01,1).scale(0.5) # #image_temp.modulate(1.25,0.01,1).scale(0.5) # #image_temp.modulate.scale(0.25) # Magick::Image.from_blob(image)[0] #end def difference(a,b) #a.write("a.jpeg") #b.write("b.jpeg") # see "mean difference per pixel" in the RMagick docs a=Magick::Image.from_blob(a)[0] b=Magick::Image.from_blob(b)[0] $mdpp = a.difference(b)[1] a.destroy! b.destroy! return $mdpp end ############################################################################################## # timeStamp images with RMagick. ############################################################################################## def timeStamp(image,textInfo) mark = Magick::Image.new(300, 30) do self.background_color = 'black' end gc = Magick::Draw.new gc.annotate(mark, 0, 0, 0, 0, textInfo) do self.gravity = Magick::CenterGravity self.pointsize = 32 self.font_family = "Times" self.fill = "white" self.stroke = "none" end #image_temp.write("/tmp/annotatewatermark.jpg") # Now return the image #image.to_blob # Convert to from_blob image = Magick::Image.from_blob(image)[0] # Watermark and to_blob $image_temp = image.watermark(mark, 0.15, 0, Magick::NorthWestGravity).to_blob # Try to cleanup things mark.destroy! image.destroy! gc = nil GC.start(full_mark: true, immediate_sweep: true) return $image_temp end ############################################################### # Eval in the ifetch-tools.conf file with error trap on this operation. begin eval(File.open("/etc/ifetch-tools/ifetch-tools.conf") {|fh| fh.read}) rescue puts "Error encountered reading the ifetch-tools.conf file of: "+$!.to_s # Stop after the error feedback. exit end ############################################################### # Eval in the conf file for the camera with error trap on this operation. begin eval(File.open(ARGV[0]) {|fh| fh.read}) rescue puts "Encountered reading the camera.conf file of: "+$!.to_s # Stop after the error feedback. exit end ############################################################### # Set the prefix to be the name of the conf file. Also the prefix is now expected to be a numeric value for port operations on drb exchange. prefix=File.basename(ARGV[0], ".conf") ############################################################### # Set on varialbe names for file operations. log_name = "/var/log/ifetch-tools/#{prefix}.txt" pid_name = "/var/run/ifetch-tools/#{prefix}.pid" ################################################################ # Set the singleton so we can be the only one with this config. lock_file = File::open("/var/lock/ifetch-tools/#{prefix}.lock", 'w') if lock_file.flock(File::LOCK_EX|File::LOCK_NB) == 0 then # Do stuff on singleton if clean singleton established. ############################################################### # Just drop our PID out for multi camera monitoring and operations. File.open(pid_name,'w') do |f| f.write(Process.pid) end ############################################################### # Setup for logging general output. log = Logger.new(log_name, log_files, log_size*1024) log.info("RMagick memory management status: "+Magick::MANAGED_MEMORY.to_s) ############################################################### # Lets trap here to ensure we can close clean with the signal of INT # Funny on the rescue where this shows up. Please see the rescue where we # are watching the HTTP:: stuff that I seem to have to make a clause for # exit. trap("INT"){ puts "Caught INT so attempting to exit!" log.warn("Caught INT so attempting to exit!") exit } ############################################################### # First thing and let the log file know we are alive. log.info("ifetch version - #{VER} starting.") ############################################################### ## 20070203 Nelson - After a bit of consideration and talking with a user on history ## operations I need a way to offer greater history than what I had designed for so ## the trick we shall try is to use the mod of the image count and splice out the images ## to sub folders. This may take some time and to do so we will glob in still. ############################################################### # 20090122 Nelson - tesing for /var/lib/ifetch-tools which is the default location that # data is to be stored in. Just create it if it does not exists. if File.exist?("/var/lib/ifetch-tools") then log.info("/var/lib/ifetch-tools directory exists.") else log.info("No /var/lib/ifetch-tools data directory found so creating one.") Dir.mkdir("/var/lib/ifetch-tools") # This is a simple step on directory structures to override File.umaks setting. File.chmod(0700, "/var/lib/ifetch-tools") end ############################################################### # 20080224 Nelson - modified for dynamic symlink via variable data_location that can now # be included in the camera conf file. # # Below is the code to generate the folder structure for history work. # If the MODPRIME is changed in the ifetch.conf file then the history will need to be smoked! log.info("Testing for directory structure for camera #{prefix}!") # Fist check for the camera directory. if File.exist?("/var/lib/ifetch-tools/#{prefix}") then log.info("Camera #{prefix} parent directory exists.") # This is a simple step on directory structures to override File.umaks setting. File.chmod(0700, "/var/lib/ifetch-tools/#{prefix}") else log.info("Camera #{prefix} NO htdocs folder starting data directory generation.") if data_location!=nil then log.info("Camera #{prefix} data directory is being generated for symlink operations at #{data_location}.") if File.exist?(data_location+"/#{prefix}") then log.info("Camera #{prefix} symlink parent directory exists.") log.info("Camera #{prefix} only creating symlink.") File.symlink(data_location+"/#{prefix}", "/var/lib/ifetch-tools/#{prefix}") else log.info("Camera #{prefix} NO symlink parent directory exists.") log.info("Camera #{prefix} creating parent and symlink.") Dir.mkdir(data_location+"/#{prefix}") log.info("Camera #{prefix} data directory is being symlinked from #{data_location}/#{prefix} to /var/lib/ifetch-tools/#{prefix}.") File.symlink(data_location+"/#{prefix}", "/var/lib/ifetch-tools/#{prefix}") end # This is a simple step on directory structures to override File.umaks setting. File.chmod(0700, data_location+"/#{prefix}") else log.info("Camera #{prefix} data directory is being generated and NOT symlinked.") Dir.mkdir("/var/lib/ifetch-tools/#{prefix}") # This is a simple step on directory structures to override File.umaks setting. File.chmod(0700, "/var/lib/ifetch-tools/#{prefix}") end end # Now handle the mod directory. 0.upto(MODPRIME-1) do |count| if File.exist?("/var/lib/ifetch-tools/#{prefix}/#{count}") then log.info("Camera #{prefix} mod #{count} directory exists.") else log.info("Camera #{prefix} mod #{count} directory is being generated.") Dir.mkdir("/var/lib/ifetch-tools/#{prefix}/#{count}") end # This is a simple step on directory structures to override File.umaks setting. File.chmod(0700, "/var/lib/ifetch-tools/#{prefix}/#{count}") end ############################################################### # We will use info here because we will sniff for all ok later with entry on the log # and a simple sentry on success after trouble. log.info("Please wait for history to initialize before attempting to access the history!") ############################################################### # This is the area where we collect the timestamps are on any history. # 20170205 Feature add to attempt to improve indexing on large storages utilizing Marshaling. # Set a temp file name for the tstamp path. temp_mdump_path = ["/var/lib/ifetch-tools",prefix,prefix+".marshal"].join('/') # Load the contents of last Marshal dump first in to the global array. if File.exist?(temp_mdump_path) then log.info("Camera .marshal file found. Attempting to load...") # If we are here then we have found a .marshal file for the camera. # First let us load the image_array dump. $image_array = Marshal.load File.read(temp_mdump_path) log.info("Camera .marshal file loaded!") # Second let us pull in all the .tstamp files for the images that we need to pull in # to make our image_array up to day. log.info("Attempting to load camera .tstamp files...") temp_tstamp_files = Dir["/var/lib/ifetch-tools/#{prefix}/*.tstamp"] 0.upto(temp_tstamp_files.length - 1) do |count| temp_fn = File.basename(temp_tstamp_files[count], ".tstamp").split(/_/)[1].to_i begin $image_array[temp_fn] = Marshal.load File.read(temp_tstamp_files[count]) rescue Exception log.warn("Encountered an error during .tstamp import of: "+$!.to_s) end end log.info("Loading of camera .tstamp files complete!") else # If we are here then we must brute force time stamps for all files. log.info("No .marshal file found to switching to brute force dating of history!") # Get a listing of all the images in the history history_files = Dir["/var/lib/ifetch-tools/#{prefix}/*/*.jpg"] # Unremark the below lines for diagnostic #puts history_files #puts history_files.length 0.upto(history_files.length - 1) do |count| # Here we step through the files and pop off the file name/number and put it in the image_array in # the correct position. temp_fn = File.basename(history_files[count], ".jpg").split(/_/)[1].to_i $image_array[temp_fn] = Time.parse(File.mtime(history_files[count]).to_s).strftime("%Y-%m-%d %H:%M:%S")+","+[prefix,temp_fn.divmod(MODPRIME)[1].to_s,File.basename(history_files[count])].join('/') #Unremark for below for diag on the actual number being split out. #puts File.basename(history_files[count], ".jpg").split(/_/)[1] end end ############################################################### # Here we adjust the array if need be to be the size requested by the configs. if image_count >= $image_array.length then log.info("Starting history generation with history count normal!") # Make sure no entries are nil 0.upto($image_array.length - 1) do |count| if $image_array[count] == nil then $image_array[count] = "19000101 00:00:01,images/missed.jpg" end end else #puts "History is more than desired count." log.info("Starting history generation with history count over set collection!") 0.upto(image_count - 1) do |count| # This is the same as the above logic for images missing from the array. if $image_array[count] == nil then $image_array[count] = "19000101 00:00:01,images/missed.jpg" end end log.info("Dumping history count over set collection from disk!") image_count.upto($image_array.length - 1) do |count| #puts "Deleting "+$image_array[count].split(/,/)[1]+"!" if File.exist?("/var/lib/ifetch-tools/"+$image_array[count].split(/,/)[1]) then File.delete("/var/lib/ifetch-tools/"+$image_array[count].split(/,/)[1]) else # FIXME test the below logging. log.warn("File /var/lib/ifetch-tools/"+$image_array[count].split(/,/)[1]+" was not found to delete.") end end # Drop the elements out of the array now. $image_array = $image_array.take image_count end # 20070108 Nelson: We need to bump the image count to the next correct number. # Set the counter up for starting at correct location. count = $image_array.index($image_array.max) if count == nil count = -1 end history_count = $image_array.length log.info("History generation complete with #{history_count} images in the history and start image of #{count+1}. Collection count set at #{image_count}. History access is now online.") # Here we clear out things we want to like unused array space and others. #history_files = nil temp_mdump_path = nil temp_tstamp_files = nil ############################################################### # Put in service 20061011 test code. # Setup the communication model with DRB for the servlet to talk to us. #DRb::DRbServer.default_load_limit LOADLIMIT #DRb.start_service("druby://:7777", SentenceWrapper.new, {:load_limit => 90214400}) #DRb.start_service('druby://localhost:9000', aServerObject) drb_port = "druby://localhost:"+(BASEPORT+prefix.to_i).to_s aServerObject = DataExchangeServer.new DRb.start_service(drb_port, aServerObject, {:load_limit => LOADLIMIT}) #puts "Hello!" #DRb.thread.join # Don't exit just yet! # Set a sentry to a value of 0 to skip clean operation logging. This is the # default behavior. sentry_check = 0 ############################################################### # Here is where we add a couple of things for the motion detection operations. # Set a sentry for skip checking that will count down until images without test are done. motion_sentry = 0 # We need a reference image to test with and we will not write it so we do not need to time stamp it. if motion_enable == 0 then begin log.info("Motion detection enabled. Pulling first image!") image1=pullimage(image_addr,image_addr_port,image_url,image_uid,image_pwd,image_auth_type) rescue Exception log.warn("Encountered an error during image1 initial pull of: "+$!.to_s) end end ############################################################### # Now start the loop that monitors via image_count. until count>image_count # Test here to ensure once we get to image_count just start over. if count < image_count-1 # puts "I am adding 1 to count check!" count=count+1 else #puts "I am here at count check!" #puts count count=0 end begin # Keep the GC in order sentry_gc_count=sentry_gc_count+1 if sentry_gc_count%100 == 0 # For sanity run the GC in an effort to keep RMagick memory in check. memory_usage_before = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes GC.start(full_mark: true, immediate_sweep: true) sentry_gc_count = 0 memory_usage_after = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes log.info("After garbage collection: before=#{memory_usage_before} after=#{memory_usage_after} !") end # Get the current image image2=pullimage(image_addr,image_addr_port,image_url,image_uid,image_pwd,image_auth_type) # Adding pullTime to timeStamp images. pullTime = Time.now.strftime("%Y-%m-%d %H:%M:%S") image_name_DRB_exchange = [prefix,count.divmod(MODPRIME)[1].to_s,prefix+"_"+count.to_s+".jpg"].join('/') image_name = ["/var/lib/ifetch-tools",image_name_DRB_exchange].join('/') ################################################################################ # Now we test to see if we are doing motion detection and if so then we need two # images and we will now compare for difference unless asked to skip # Just to be safe run the Garbage Collection if motion_enable == 0 then #puts "Starting sentry_motion test.\n" if motion_sentry < 1 then # Here we are asked to listen for testing again. #puts "Before" mdpp = difference(image2,image1) #puts "Images tested resulted with mdpp of #{mdpp}" # Log out all mdpp tests to log file. if mdpp.is_a?(Numeric) then if mdpp == 0 then #puts "Zero mdpp detected!" log.warn("Zero mdpp image difference detected: mdpp=#{mdpp} and motion_mdpp=#{motion_mdpp}!") elsif mdpp > motion_mdpp then #puts "Motion detected!" log.info("Motion detected: mdpp=#{mdpp} and motion_mdpp=#{motion_mdpp}!") motion_sentry = image_count_on_motion else log.info("Insufficient motion: mdpp=#{mdpp} and motion_mdpp=#{motion_mdpp}.") # Update image1 since we compare latest of image2 image1 = image2 end else log.info("The mdpp call did not return a number: mdpp=#{mdpp}!") end else # Here we are taking the number of images without testing. # Drop one of the count of untested images. motion_sentry = motion_sentry-1 end #puts "Ending sentry_motion test.\n" end ############################################################### # Here we test to determine if motion is detected or if motion # is off. If either case write file to disk. if motion_sentry > 0 || motion_enable == 1 then # Update image1 since we compare latest of image2 image1 = image2 # Add to the image_array for DRB exchange $image_array[count] = pullTime+","+image_name_DRB_exchange #puts $image_array.max #puts $image_array.index($image_array.max) #log.info("The $image_array.max="+$image_array.max.to_s+" and the $image_array.index($image_array.max)="+$image_array.index($image_array.max).to_s+"!") ############################################################### # Now put image in a file. Also let us do a quick error trap here. #puts count #puts "#{image_name} - Image Name" #puts $image_array[count] begin File.open(image_name,'w') do |f| # Here is where we watermark the image we are going to write if enabled. if image_watermark == 0 then f.write(timeStamp(image2,pullTime)) else # No watermark f.write(image2) end end rescue Exception log.warn("Encountered an error during image file output of: "+$!.to_s) end ############################################################### # To improve large data stores we want to marshal dump every 1000 images. # Do marshal_dump operations if true and it is the last image recorded on montion detection. if count.divmod(1000)[1] == 0 then marshal_dump_sentry = 0 end if marshal_dump_enable == 0 then if marshal_dump_sentry == 0 then ############################################################### # To improve large data stores we want to marshal dump image_array every 1000 images. begin # First marshal dump the image array. image_array_name_mdump = ["/var/lib/ifetch-tools",prefix,prefix+".marshal"].join('/') image_array_dump = Marshal.dump($image_array) File.open(image_array_name_mdump, 'w') {|f| f.write(image_array_dump) } # Second clean up the tstamp dumps. Dir.glob("/var/lib/ifetch-tools/#{prefix}/*.tstamp").each { |f| File.delete(f) } rescue Exception log.warn("Encountered an error during marshal dump of: "+$!.to_s) end # Reset the sentry for the next time. marshal_dump_sentry = 1 else ############################################################### # Now only dump single images one per file for 1000 begin image_name_dump = ["/var/lib/ifetch-tools",prefix,prefix+"_"+count.to_s+".tstamp"].join('/') image_dump = Marshal.dump($image_array[count]) File.open(image_name_dump, 'w') {|f| f.write(image_dump) } rescue Exception log.warn("Encountered an error during marshal dump of: "+$!.to_s) end end end else # Here drop the count back and just keep writing out the same image number # until we detect sufficient mdpp. # I suppose count could equal 0. if count >= 0 then count=count-1 end end ############################################################### # Now throttle the program back if need be. # First test to see if which sleep we need to use. # Use the motion burst as first option if triggered. if motion_sentry > 0 then #puts "Performing motion_sleep of #{motion_sleep}." sleep motion_sleep else # Set the daily sleep modification specified in each camera conf. sleep_test_data = day_image_sleep[Time.now.strftime("%w").to_i].split(",") if Time.now.strftime("%H:%M") >= sleep_test_data[0] && Time.now.strftime("%H:%M") <= sleep_test_data[1] # If we are here we are in a modified time in the day for sleep change #puts true sleep sleep_test_data[2].to_f else #puts false sleep image_sleep end end ############################################################### # If we encounter an issue that raised the rescue Exception and set the sentry_check = 1 # then we are just listenting here if we get to this point to let the log file know that # we are ok again. This is used to determine the health of a running script to a camera for # for problems. I am sure there are better ways but this seems KISS. if sentry_check != 0 log.info("Sentry clear hit and appears to be operating again ok! ifetch version - #{VER}!") sentry_check = 0 end ############################################################### # Rescue out for all errors. rescue Exception ############################################################### # First things is notify there is an error. #puts "Encountered an error: "+$!.to_s #log.warn("Encountered an error from the Net::HTTP work of: "+$!.to_s) log.warn("Encountered an error in collection operations of: "+$!.to_s) # Sleep a bit when error is raised for this process. sleep ERRORSLEEP ############################################################### # Try to setup catching a clean interrupt. This seems to be odd that I have to # do this. It is for the trap("INT") form comman line testing. if ""+$!.to_s == 'exit' log.warn('Exiting now!') # Try to be nice and close the lock file. lock_file.close exit end ############################################################### # If we encounter an issue lets set a sentry to let the program know that # we are still alive and well. Setting the sentry to 1 will force an information # output to logging that we are back to ok status. sentry_check = 1 # Write image2 to image1, since image1 is what we write and image2 is latest. # We swap here in case we get a bad image as image1 and this should flush out. image1 = image2 # If we encounter a bad image we need to dump the bad pull and pull again. log.warn("Backing image count sequence down by 1 in an attempt to pull image again after rescue!") if count < image_count-1 && count > 0 count=count-1 else count=0 end end end ############################################################### # Try to close the lock file and the Logger. lock_file.close log.close DRb.stop_service() # Stop the DRb on the ifetch session else ############################################################### # If we get here we already have a running ifetch.rb puts "According to lock file the program for the conf file is already running!" end ifetch-tools-0.18.5/helpers/0000755000175000017500000000000014115200075014360 5ustar useruserifetch-tools-0.18.5/helpers/rtsp-to-jpegs.sh0000755000175000017500000000452414115200075017442 0ustar useruser#!/bin/bash #set -x # Test and assign some required variables for rtsp-to-jpegs _imagePath=0 _rtspPath=0 while [ -n "$1" ]; do # while loop starts case "$1" in --imagepath) shift echo "--imagepath of $1 was passed..." _imagePath=$1 ;; --rtsppath) shift echo "--rtsppath of $1 was passed..." _rtspPath=$1 ;; --usage) echo echo "######################################### Required parameters #########################################" echo " --imagepath - path to store images (note: if using ifetch-tools imagepath should be /var/lib/ifetch-tools/rtsp/)" echo " --rtsppath - path of the rtsp stream in the form rtsp://username:password@ipaddress/blah/blah/" echo exit 0 ;; *) echo "Option $1 not recognized" ;; # In case you typed a different option other than a,b,c esac shift done if [ ${_imagePath} != "0" ] && [ ${_rtspPath} != "0" ]; then echo "I: All parameters were passed!" # Assume the camera number the last octect of the ipv4 address _cameraNum=$(echo ${_rtspPath} |sed -n 's/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/\nip&\n/gp' | grep ip | sed 's/ip//' | awk -F. '{print $4}') if [ ! -d ${_imagePath}/${_cameraNum} ] then mkdir -p ${_imagePath}/${_cameraNum} fi echo $$ > ${_imagePath}/${_cameraNum}/pid if [ -f ${_imagePath}/${_cameraNum}/log ] then mv ${_imagePath}/${_cameraNum}/log ${_imagePath}/${_cameraNum}/$(date +%Y%m%d%H%M%S).log fi echo "I: Attempting to spawn ffmpeg rtsp collection and images housekeeper." > ${_imagePath}/${_cameraNum}/log _spawnCount=0 while true do _spawnCount=$((${_spawnCount} +1)) ffmpeg -y -rtsp_transport tcp -stimeout 2000000 -i ${_rtspPath} -vf fps=fps=5 ${_imagePath}/${_cameraNum}/%1d.jpg >/dev/null 2>&1 < /dev/null & _PID=$! echo "$(date +%Y%m%d%H%M%S) I: Spawning ffmpeg - spawn count of ${_spawnCount} and pid of ${_PID} ." >> ${_imagePath}/${_cameraNum}/log sleep 45 while true do if ! find ${_imagePath}/${_cameraNum}/ -name "*.jpg" -type f | grep -qs jpg then echo "$(date +%Y%m%d%H%M%S) E: Appears no jpg files found! Killing ffmpeg and sending break to respawn." >> ${_imagePath}/${_cameraNum}/log kill -9 ${_PID} sleep 5 break else find ${_imagePath}/${_cameraNum}/ -name "*.jpg" -type f -mmin +1 -delete sleep 30 fi done done else echo "E: Not enough parameters passed! Please see --usage." exit 0 fi ifetch-tools-0.18.5/templates/0000755000175000017500000000000014115415503014720 5ustar useruserifetch-tools-0.18.5/templates/Archive0000644000175000017500000000254113656401354016236 0ustar useruser# Call to get uptime info for template. _UPTIME = %x[uptime] # Call to get current date for template. _DATE =%x[date +%Y-%m-%d] # Main page. %{ Camera Archive View Interface
#{_DATE} #{_UPTIME}
(Archive) Live Monitor
#{myTempRespond}
version: #{VER} } ifetch-tools-0.18.5/templates/ShowImage0000644000175000017500000000140713656401354016540 0ustar useruser%{ Camera #{cameraName} Interface

#{theDate}

#{pageToBuild}

Home | Archive | Live | Monitor | HTML Archive | Video Export

ifetch-tools version: #{VER}

} ifetch-tools-0.18.5/templates/LiveSingle0000644000175000017500000000220013656401354016706 0ustar useruser# Call to get uptime info for template. _UPTIME = %x[uptime] # Call to get current date for template. _DATE =%x[date +%Y-%m-%d] # Main page. %{ Camera Single Live View
#{_DATE} #{_UPTIME}
Cam #{cameraName} #{image_alt}
Archive Live Monitor
#{myTempRespond}
version: #{VER} } ifetch-tools-0.18.5/templates/CameraHistory0000644000175000017500000001530713656401354017433 0ustar useruser%{ Camera #{cameraName} History Interface

ifetch-tools




Home | Archive | Live | Monitor | HTML Export | Video Export

ifetch-tools version: #{VER}

} ifetch-tools-0.18.5/templates/Monitor0000644000175000017500000000320013656401354016275 0ustar useruser# Call to get uptime info for template. _UPTIME = %x[uptime] # Call to get current date for template. _DATE =%x[date +%Y-%m-%d] # Main page. %{ Camera Monitor Interface

Monitor View
Operation & Action Legend
- Start collection
- Stop collection
- View log
- Start all cameras
- Collection offline
- Collection working
- Collection in alarm mode
#{_DATE} #{_UPTIME}
#{myTempRespond}
Camera PID - (Memory) VIRT RES SHR Actions Status Logs Information

ifetch-tools version: #{VER}

} ifetch-tools-0.18.5/templates/Live0000644000175000017500000000254013656401354015553 0ustar useruser# Call to get uptime info for template. _UPTIME = %x[uptime] # Call to get current date for template. _DATE =%x[date +%Y-%m-%d] # Main page. %{ Camera Live View Interface
#{_DATE} #{_UPTIME}
Archive (Live) Monitor
#{myTempRespond}
version: #{VER} } ifetch-tools-0.18.5/templates/CameraStartStop0000644000175000017500000000077313656401354017736 0ustar useruser%{ Camera #{cameraName} Action Info

#{myTempRespond}

You will be redirected in just a moment or you may click this link.

} ifetch-tools-0.18.5/templates/CameraHistoryBasic0000644000175000017500000000064713656401354020376 0ustar useruser%{ Camera #{cameraName} History Interface




#{myTempRespond}
} ifetch-tools-0.18.5/templates/StartAllCameras0000644000175000017500000000100213656401354017666 0ustar useruser%{ Start All Cameras

The servlet is attempting to start all cameras. You will be redirected in just a moment or you may click this link.

} ifetch-tools-0.18.5/ifetch-tools-html-page/0000755000175000017500000000000014113265413017177 5ustar useruserifetch-tools-0.18.5/ifetch-tools-html-page/index.html0000644000175000017500000001343113656401354021205 0ustar useruser ifetch tools
ifetch-tools - A set of tools that can collect images from ip based cameras, monitor collection process, and provide an interface to view collected history. The current release has two components:

1.) ifetch.rb - A console tool written in ruby to collect images from ip based cameras with a conf file per camera.

2.) wwwifetch.rb - A ruby servlet to view collected history from a camera and monitor the collection status of each camera. The following feedback is provided on each cameras collection status:

- all well
- collection in alarm mode
- collection is offline

Cameras donated/tested for the project will be graded in to three categories as follows: Excellent, Average, Other.
Manufacturer
Rating
Model
Comments
Amcrest
Average IP3M-954E Works out of the box for configuration. Camera exterior is well constructed and should last a long time.
* Had trouble getting good frame rate on image pull which is what ifetch-tools utilizes.
Apexis
Excellent APM-J011-WS Works out of the box. Camera web interface can now be utilized with Linux+Firefox so upgrading rating to Excellent.
Axis
Excellent 2100, 210, 206, 207, 221 Easy to work with out of the box, excellent frame rates, and tech support is very good.
D-Link
Excellent D-Link DCS-930L Easy to work with out of the box and inexpensive.
Hawking
Average HNC320W Works out of the box, inexpensive and sturdy. I only have a HNC320W wireless unit to test.
IGuard
Other IP-250E Low fps on jpeg pull. About 1 frame every 2 or 3 seconds.
IP Camera
Average 510 Works out of the box, inexpensive and sturdy.
JideTech
Average IPC-E3600 Works out of the box, inexpensive and sturdy.
Plustek
Average IPcam P1000 Works out of the box, web interface, dome model, and tech support is very good. Camera expects to have IE for web browser and general access.
Panasonic
Excellent BL-C210A Works out of the box, inexpensive and sturdy.
Reolink
Average RLC-410 Works out of the box. However sometimes has issue, inexpensive and sturdy.
Trendnet
Average
x-Other-x
*TV-IP201(P), TV-IP301, **TV-IP320PI, TV-IP512(P), ***x-(TV-IP314PI)-x Works out of the box, inexpensive and sturdy. Takes cctv lenses.
* On the TV-IP201(P) can get duplicate frames on short padded jpeg pulls.
** On the TV-IP320PI it requires addon software that appears to be IE only for username and passwor configuration. All other settings I cared about I could set from a generic browser.
*** On the TV-IP314PI support was no help to find url for snapshot. Camera performs poorly on heavy url pulls.
Toshiba
Average IK-WR01A Orite IC301 Works out of the box, tested for a user.
TP-Link
Other NC220 Works, but, depends on M$ for some feature and password is bas64 encoded for snapshot pull.
Wanscam
Excellent IP Camera Works out of the box, inexpensive and sturdy.

If you do not like the above opinions please email me for information to send a unit you want me to test.

Current release - 0.18.2

ifetch-tools information being tracked in Debian:

BTS (Bug Tracking System)
PDO (packages.debian.org)
PTS (Package Tracking System)
VCS (Version Control System)

Also see FYEOX.COM for other ifetch-tools related information.

Last updated on 20200216

ifetch-tools-0.18.5/ifetch-tools-html-page/red.jpg0000644000175000017500000000067713656401354020474 0ustar useruserJFIFLHExifMM*C  !"$"$C""!"a1Q ! ?2 "*kK)>ǟHۜS]2KJ 'S7#Lm8\lw -/pXBM *R>>;'G^Wifetch-tools-0.18.5/ifetch-tools-html-page/green.jpg0000644000175000017500000000070413656401354021011 0ustar useruserJFIFLHExifMM*C  !"$"$C"$!1a!A ?[ynFrpd1-$s;VnUSUZ :l |dcUy?n[ehZ̅ *ǹ&Z5ĈC$c'ZfnDuifetch-tools-0.18.5/ifetch-tools-html-page/grey.jpg0000644000175000017500000000063613656401354020663 0ustar useruserJFIFLHExifMM*C  !"$"$C"$A!1aq!A ?9-)IoVX4|ZUQks>UZ$@pCQc{+x\~^TUZjtӀW}xV|JpQN`Kkph?ifetch-tools-0.18.5/ifetch-tools-html-page/ifetch.png0000644000175000017500000005051513656401354021164 0ustar useruserPNG  IHDRZ=sRGBbKGD pHYs  tIME 2DJtEXtCommentCreated with GIMPW IDATxipy( A .Hv`#۲eI=4vcvݝqرG^ynuubI A}_uW~@d "qT7+)v)*c~u?Օߴ1cBι1֬)M^Tnܸ_m{EKQL?;deYW&BP.+~: fyC`, W\o'fff&''0 B;,;!B4Bgsss+*Eф:@c4 v0;˗իWp8l_5 d2w粮cXQWUU)Fi.77ISS|>߶ {GG:::ٹ~GuB7F !wђ?Xw+++4 tx9PVa 郼!BhBR?n;B;McdEX~1ƈFCCfJiMӠ=zرcF-33\2kch===tww~Ѳ,eXRmۦB=dggӧO={Vv{[!6הRUH)mJ^ً:!DWUd]]݁ZU4BIw4M!&ضd2y_>|g>3g"*(RJ5߽QJQPP@h~~>[YY)X[[ (to0N !~#33svlXw 9De޶޲,L&1==wKaJ|BUVV6UVV_`K*8m[XB<ٍ{.ѣ78v9|3 n1kѺRJկ7 >66fі{-m=|8q~>|t{fDRcǎ#G= ٧?_u\<eW^aB>ON>TWWׯömX3.+J$X/{F5Eqy<ӡP?y׏2܊C ιvrD"˻<44!:uhjjB~9dz}s dz6(,B"=ʚ{BضN|_Eoo/f>??O ccc6%0KL%riPJ_R唪B</FvvJ~~QRR8v횸~:,+~)y_֭[uyDs%E+++YYYeRr++..VgE(w%ηV6ǁi0MX ϟ'(++SO8A7sN ygg'YYYʖ߭1ڲ 7o"R~Z\\Lsssz`ll `?0JKKIII <Zzjp!o֭[WߩT /vfܶs7oBگBDBB QU4x]ѣr̒-VWQm=4ea``Ͳ2"۸uI}?J]~_֭[8w\[YY&J^VVFEI'kB :ׯ_wfgg!`,]@vc~:ZZZ  ܏>>>1LLL H 77X]]r C~ ׯ_OÇ VZZ>h 4166fMOOz^X97Ӣ2<n]P8{,$Ec xߏL`uu<p]j;طv[n}_~WՊ CQQ3664Bԝp\x;o=BPPP1# n II|˲:8ccc|ԝ23??˲>UYYv7A++hkkǎ5#9t!={nj(jxC//  ͅ Z΍7 2226L&/ ! ېYo=VqSι;8mەڪ>.Ӏwvvvӛom"V{d(ʷށZ wx/5W^IEϟ?_w }z%%%X__SSS$ȑ#paqdjqM%XsǶm0 H'8[4rTU},[p\zV, ݘa~0uЏAus~"UD.^Eo7Zkmt!Ǫ*?~7o$dX]]ɧ%˶mg||ܲmVUUBطoN8oiiÇ555.%^_җ z tbo k2k*=vMpV uo"D.#{N$K"7NC++;Oi ~w}wW9rwx0>>.8LN¼xN$fѪszLĄ=>>X]]d-b1WCCCi}}=MIߩ:h<(> Ԃl}TU`.!yc2PBP~Dr/_F(BQQCgz~D<' BuBx^^33381KI8狎=c˕_SSӧOk%%%TQ=9iQ!sb^dǐx|MUXJVN$ct2XP2Yn@W\aBu244D"\Zs Dqqqw4f0RXX|ȑ7kjj_yRkt^aOϜ@H(xrd"P2ctjJvǏ@x{*//1@7o$|rrr%;;{ezzom9?KПD`0zˬUnJ=2p,߂0aN"^ԭ@X6e$Ɛ \4P TS`NÜ^߉P.@NNdrx<> !߹6]jjj̈l=C,X˰oz\uTW7{e1$oLnXfzyDWA}nPMM,Hֆ7|nouuuu8v9qDԔX^^\J?MיYݨ!5^niZ9 jx]]8@c C}i3;Ff}JП^mPSÇ>|XZ{{;:;;qGw\!-ǝqÄ5 @-ٰ^7׵j  3 ~jkkÙ3gVr|?*}U4(v[kkk}555l2ikSK|:H p²8M* dw]zzkeYpVff&4Ms&A*!D^w~j;:@)m̻!. Lv,46~&# 8QۆÚ]deʮL[Uzb6d=H v7PY}}=}g- q`,Z9Fr cbzE܇@=OޘZ!{ppJ$\.-S^mdi%Yq@(0-y8eØE>7&`E pp#>8h σ3>Z.… |۶1<Z9Th\0ZI7n^ʲ2A @ n,m9wAmmLh8ܺu+_Qi%OtEQNVVVձ(S؉p}HL¿7%*d7(q;ϟǛoD"/| b1 ɨ%cɓY ei_oQ @y1/A+Fr6#Gք6m|'XX,Hos}x\曲Nt0<qiCNPj_j!zQXH$+SrxԩS M8mc{)**Baa!}\xot;JA:gaҚ׽1 :<կk_b/ r۶kuIwߕOџyIсGxw7SJ.HP r; nXxzyípŚ/\X,=:_M }(eYbp.^1BPQQ ,fa\0*،vo y-O} ׮]CFFrssO~ԧ>܃x<ћ7o (E1pEQl0qbxΝ;w}gΜA󯽌, Eâ<+իWV Ol6ʿK]NU-OGv헗l5 IDATv9s#vvvnO͑tds !4BBH%OvC)eǎcGogZwOi8z$i6MV ',.p O{?Â{JF$Se] ;Ң?qEQ1$Ot۶? \B8;&u-[AT얳!.q_X%GQ 폶mG{{{!{:/Boo/9wu.QT#!vV{l%˲> GQ JNNs.;^͋SJۅW$MӴ~}ι{(B(@,AMӴoܸQ'gXn$O}ОS%LӔn`$+++U$O􌌌K__+@qO"`r~.Ahyy9H$}ڻSK/뵵5kMotB"b)@ wZJ.^vP*+i"c}%Ke80zaYL&H6O*!$sjaa!YYU޲m?񏝟gGGGWssnnN}ȝZZZZܲ,z>@,͇iRjnXEe_}_i.LN\bQ~~>-(( ~rMӄimmH$dvB,AWZ D]XX(/c1Gi,BII ___ׯ]av")[I߬]XX !yyyJAAJd2qķX,vݲ+Ң\21H$;scMQ>abX]]Eoo(JвBKo;<Ҽ:"]{I&Bb||QU{iF\ܱiymb13++bcc㡮.[Feu6@#x|-YGZnIⅅ₂ZXXH Qp8<\QQ7;;wm'@!LӼ{H%φltwwZ>`paaaRBGҫi؏?!^y[gg:6$u6xclnuu5{qqPQevv_ ޟ{)DUUҜunnxvvq /JП J-B,,,,(7n r3g 0%EB|KǑ/JП!7-bavv6{aa!_x<466]Vk"J)rrrz```=uªܾ*.q4ͯ8_CP2R]]tENBQi'N"Ң?[RUP(.G)y+^{5x^8C όښ<#Av BNO744?{VRRBSj{{eYcA9 3󗋊m3gθ꘮wL]8bvv`zzz$B%ҀYRR~ mu]Glp V}vv,//7,,,;3 ~oC"`_;!1ܜw1r|[FSJOBdPN Rruu+ nOE![֑e+}vR__O],O >)rJU՗*Oty8ڋSNl 挱N5662UU!F>Zr `xHХ5FԔqEEEl{t4O՚UUMMM\/))!~T:yfff&_UU, --QUUr^4iwhC}}=gǑ=$SR ksZT@1&!"uti̗zi`eKH;(TA^滛SeYٳg)]}l=8NX":d1 iK,))OEQd2)[d:v=ehTZt PkktsޚJR;c ~JEaXL6tVVVɓ'0Lwg)4hTU%DB6O_eѮ+2'tB)1sRI6.4-9w֐s&9,m'&&&se }߹ ??8HQ-%]w s$۶a!ò,iѥEtefz pq~7lz( ҂,;8SN5v]zU&HNn \VA #no\0J&8DU5XOqp9x<6P ࡣ $7o\[J`BpNLsddĒ?gxiGE4W?##'TtTU݄Ll(edy aa]f"rPN>,=mnIC.DZ?J Ѩ?2{5rM/(*QJ6݂;p|/c7vrrx?E"oG&'~?c 0\?6ȷZ?/ !JEQضϚǢpYEir3AU?mYf ~1:51ꚚXm'4U4m8gTYi~˲`òۉ0RT\1t} Eq B4S=`m uk.(;'O?c}_szqI&w?Zǭ+ AaQ93X2* UU=X䛮^сW^SMӶ 544h>߯\gLy(.nn+p3 3$JӢ F\b( C/,vbE[~@@u\9Խ !6I/yyyv΢r-T\}PPTR!ijWL4UU m?|57 ¢ϗA< nq\ -趝LDkk-vϱj)n>W!(( Y~C 3"UUS5wNF*4T\F Ce^Ƕ3eY`LQb1ղtv?X^^/km`Ybiy}S4**GALx4M dgggǢLLB|mgUECaQ/9Sײ≄?]FaYד?q\| 84eIJ)EcbX/յ}A7Y͂a۶#m y*e˿ }!F C9z-!kGkk -7ۓH$0<<躾gp>e:::O~:\.Dz5Dd"4]Iv9B0H~a/"˦}kzj͏LM-B (EQW86(IV]5R\rfg&Jpn۶f&TӴ,7LD$傢(i"Lb}}sss)+*}vcnSSsU鱘++Ƀ;n1EAaep{Y ! x=W^yExVVq#՘PH"^א,.*8eB)%ı93 +N8J˫K)4]G]PX6-10(y&n޼@ Yy7i{9PpΑL&ݍ~ ayyXYYA,m PTT7x8z(<|>ߞVI/Fiyn<5Us9D]Ḵm(EѶaiJm tݟ3322m92r8_/G#՘qW] *Sp81QN6 T\]wm ΩPBF K4<FGGQ[[oсo~F,{4ѱѹ^g>gb޾9UTjWoǶ1911pe %6Q9.]GOOWD"s!L~ na2TCdSwtGm/SJ2ĵy$ޟ@oJMomcJB=ٶHП!M,$Ͱp.|>x~Psr˂X8Et"ɝm) r S X> ]qjmm sa}}_/b||x,A+@<L٩0[\\__;|y{K[s GQI5o6SSc0 L < H0,?2\d{@bE1;7 ?\' i,HT+gHo>%, nWfx3qupykTy-r#Ƶkdƅ%I\v ?v~D"DʚHEԲLxE h Z ZmH021O(*)GUXC%ϐ&1- k q vF|^8 n8tڿ1MՑL`ffp[з>.in,'vx!`'V `ˏ¢rl.&rY6~e"E^0P**v2c J*,R͕JП$-{r!2MM.]]38Ξ=Z,V 1?7 .tuuKSo]u`0|:=6++ (**B xh{:ilp4 C[Ҍw> (#9_fFi,#f lj ik!(% *yyyԟ1[1||[tZp1ckID ^U?.a Di PTR9owDDװd"۷o#77YYY[RjDڒ:tpa4=9?MӐBTUUm&x;Χw!i()Ѻ&'{m s30wI!HXY[úebL"`$e JKn;NM#Y8666+AߟR(mu ee_.[! lL%-2eXV Z9zE5}3cθ]@@@`q~i"H`jj `nv]i4MC]]aYlۆNO p!|sٳgQXX̀ܽ[{{;z-öm5Ǒc`Ap!KwEEʖS 1uZ BJʡGv}!8)NCae+=X\\Cض&|>\K'|+X[[VWWFa6ǁmucCaa!~w%%%D جgn{Mm҉1CֶrWϧA\J4xgc@ nC30@ƒ(.ocvzQθ\.۷;ejJQqrmĶ;j:|c9iw±mpȪݨ*2elˈp<8 Dž۷oܹsp\رcGu[ZaOOD099 `oc<}{؎qc21$"F*+jXݭ}ZlU#J&3{<=_s?<:&Zb|(R9w9{Rf( VRIDATS?EQ( 4Mxڥ###xgQV,˕]N]9i$QjԢ4MfTuR)teTIdW*Udj Ʀ&ژeȲ lzxn}ܒ2ɒw~?pEa&ָ[GS SG2+\3J5n(eYZ7CԦnUUIZmfK/,"xȲI]seqI !"r) T%ʊSTjNvfUEY(:wI]|҇҇(2 5iq};OݛEYVnK4x,jlgjRjBr /^Q|>7 eYx`Ae}(ou$O&\.˗/КzEQw4v7!麞P@nBw\ut:yPNc':.NS[ɚbin.^msu=L&nw??*9|)zaf5'!8t:K|0׬T Xn;B+N$ pF˰,Kjll6(7}󡡡rѣGxnK3>~hffɃַUU,?Tntuuaqqz[ QHaLMM:t'Nnݵ>t(5 X:36r^!&ci{"].Y,Pm-F߱:;;y&,G2٬Xȁs8]pp1M4WaQ ˢ0<ąwWJfsScP,ncjjSyd2luuUX0t4JxZc6U?GсW^y=_?8??gX7U;|>J慠^^N=^OLG٩ȤSSiUu~m`R f/K{} >Zּ +QRWm6[X,*ʿU*])*Iiy0Jү⋙C`}wjWo5iwuzr h;w`غmttccc&9s'u;WUr}MQ*B#mxDܹt{cEETRl6cnǾ-} (5[3אL&]޽}hni!KKr2whcnGLp9˲|0i&c!]/q.\0w* >.ov:J!0  tJ)fxl6q5O#v,bct^4;9џvoZ#KfH-3gx~n"$ R&k!N#vI$RzMyRkn-0y(eUUeZ[[<,]]]r4ݸi{@ ikk#ߩSމ$p8r%#IᡡiB!4͍_74 ^=}"# ŋR #k_S/>@eBoo/ B!:u _S\bt^}F$8΍}>i4 ^?x(T*_s=r_N | ڹݞ188xwޙ{ %B499ӧOo';|>!p8vg?L#<}a``. eJ}㗿\4}-X4t`P BwTjw0逸'm.FeDh#GڿRT*LLL'N^T*n %DՂagMӌR E!@JZ9UUy54 s^KAsrP:w;w\)_({snV#pޯ(OWե#G׷mnzzzz577g1 B|j&2fff.NMMP(M)-7ArSS=MӴ+1^Be={{zzynnNTU5PUաi3zގ;u}maa!S;"*@nwwwwkR b*~ao8փM(cqw\XX8uע(J}vvڻwo4sR)\|"Ѕm'TBf}}}iJ&|>oz<+Lʵ֫~ /.|NtX\\r+#~aZT*W_                                    Gd?SIENDB`ifetch-tools-0.18.5/htdocs/0000755000175000017500000000000014115450625014212 5ustar useruserifetch-tools-0.18.5/htdocs/ccerror.jpg0000644000175000017500000011614013656401354016362 0ustar useruserJFIF1ExifII*bt|(1 2iCreated with GIMP77GIMP 2.10.82019:10:09 21:48:46Created with GIMP6<0JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(z+$NȿyZV)I{<(Eco\{U# C_ڧ7Eʢ!e:ӏΙWsŭQEYP0P0R1)IE]JB V"߃?G"߃?Xf#?1(d{yJLgi㑟Z_~\B)69تҔ)cZG*w"kӓum9Ԥ]lPYKp r i-?ZN-aF1(E²粗˞2z!Zݢ=jn+(m溘EsV^[G]߮E*n/wB _{種&~}iQʲ *xu$=zm.?ɲwOԌyxG\{UEœӋR_S j cVD<RWSjp .x' EV QOXexE$ƴLCV;6{U#40u q}]u22^o_~tʻ-n(GCcQjm$l2 ["hj#l\psָ?]Oh%sUz׶CI-kw*M %m~Xaj-꒲_e> ~H-^,a(\l+=wJƮ-9S3c)b )+lչ|B/!fGŭ Yb)z0$tܖZlŷz#'=!?]AknD1w=?^fuBNki޻ԗ/q.73`p1+ѻWO Χ4166c+Zcio,_ysjוּsc4!e8V3&U,[F AcqJֳE-zsTym+RyZJ/3eko?S?_}ZK-2k6pkƮ!h؀Ct'<~*齴<0vmϳgh$Q '?Jn{?ʷ8,H`w¬ium/o$!,OTA/Vve,&z|wu_=0Vb9=89;5ڗo*&nҠ~V߫8 :%:k-_#qMק6nnQ՚%klJkg~~n"3!gWm"$0:Pk𷃵/!Rts^ۤ|+%֡sp*M+ԣD<jRխݭ5l>[Z-\ ?/O{> inuI\ԐxGA,cc Ixiάj7l F/fOOdYZM64VZ|HĻ:LxĺM(,>J*IXύn专;6G:cdޡI29BJqjt?O>FK'h\()r"ޣ|ڍמ }*V񊊲fQj΀ɥ^62?F޼vΪMy۠4:|vsΗQ 0G^q ޣt6Kt+f&Vh8#8$h?ERQ|s4xFgeTGۗ' ϱKƺ=41Lonob~s_T֣9XoVTcR+8#jy/3\JQ0)|tZ:j tS\4r rs|EIp2j:((((((TF"bpZgPt DHOr?!ZO8̒ZiWo+ ּΒ>aaa&Wdmei/:|gt.ota $EʏPA$Mv5a7h|+RS˥M "Z(], s<5bQE]ۉf`gy{manJFYsFѣjʜ~N!;'*0rGQoekhm"OYNkHZp>~n :K+ c,@'*2zV=Իu,Wii'HQUFy Ft+כ|Pm᧳W댁{jzkвcR~ZzEoxbKhJD 23R%'רx)A*Q_sUCLӤ 0[jm)++QFnEf0*((((AcLc0Mr> Alj!1j]_E]Kl1>ּzST1iSVvM5q5%\b]F:ޏsea; ]߆kXm>]t>ZC.D< h}!uegˎOx+lh~yt﷔+3|bO%wC IwϥCX}+W%e%x$?SyRYpCGk|].?4D?F#gX[H٣SN{nfNX3}tI$ ӢY%h* +ش[U:ԟ5^"WI>2E{&זO|5x^[[Xu',?Cg0BXXJX5{޿-\rѫRAZU[R5)n5ϰ"u/bHksH|!iPڙ՘ rX/“ +kH__i4vMy4HnO5yy3*7߭w9[x]2Fʜd`ץFksna kkp!%$'8 +Q#(8B8Lq+TWWi4ythNH h;8s4|U]E;yfeL>>fX-Wn;4Lm/ОU=3Zq*ͨ+_MV3m!XDPHa:tx#Wc ۄmeIrZb ϯS\.<^~gS-ZiΤuD~$EmF O8&oB ),|¿6QEw.QEQEQEQE:9)Db շo0NëF*¢(Tμ.?⻢B5.u5ݴ\aU(S13QkwXQ LQ0=XTT}Vn?Ky^dh^q<S{DHceLs*n* hs3ƻFo7#o-o\GyL{S<1gk?'޳,H A6׃it2mo UVw'T+]lgK+sowcp$RI{T Yxm%nXcV:qrkQG^4]zzyAsKuWh2Iԗ5Jt?\x|v' 3OvJfU<[gԴM>k)9Fj8%ʖTml۽M^yZ[27>ݭ׈,YY\{W]D(H80VЊ]$k"X4Ϝ)Uʈ(+Ͻo|Oh^$8p >P; Y}Zw0QQU]} U<EV+EXbb+˙QEYQEQEQEU u XُE$ 8+ te./:JGEuXFsx /N}JDQqs[!blV0I[XEv8 N6_ ,<M3oZ"|O'URJ/fWQ\߆]mFLd?C[ZmIwu Hr}}iy寂Bۿr/.&dt܎5^jVs$  OCc)rzxJ8ںRԭt6rRztXki#,g J/['نWǷ`xgōflǚ۲dۏS(znl$]Y?=~Sok4dƅ\W :aZΛJÅөS:[Z9F/ݮbd py?OcA>^pRp7·cQa%~n͂EjbFsD8V%Qǧ\vd}?xwgxe7"Aa]쥿Pi2Ny߅V|6zsH?(-teZhk>mzQå]Q/ v,{#\_z-5 Mz!1B۱Ǯ+ξrEvZmlNUNtTf7VBxLj[xh(oI>U崺H4qrW$k.[RYS^5x*jUqi`ݦWWp]6)'`r7ֳlʀvǟ#癩xDqF@z%Do~\Xz\ُ6ĺl6KžNיj7?d矜?SXP9gD16ylhdѮ@vϱ VO}Zrg=%++m "FY\m>]? '뉮gũ,zͅQ]Q@ZS0Ȫ;Uh⥺4V'zri;fo.d:5X (E- jT|ӓoܸ E(4wFRKObj>by]I[՚#<"HUһ*R#9*vsZHUe2A+Ӽ5┰P8`Êx.g+'dGݝOC/l`tk 耖:q~Uj2x [w(yX`+(JQ樵#0!7N EQ" _̒&kUm6bз;\4m /pj}N8TuGGS=) xžmՄSFV }+uLZ&mgt_4FI4,cMdFDȍPQ?IҕcIJ s&f,4-'@5|D3\J]0 =(OEy뾻VjS,,ȫxz)Ӝ3W-;tZ[8R~Mz;u Al"UD+j:.ijOkH5g4\oBkKZ4ԵPk %rhy8ezuW.9~=T\(q^]1}2ŬΧh-20qȘVCއ5& CW(FUܝ[o.b1pZ(QQ!ԫ:~nES3 ( ( (.6p0&7' v&0u\ށ!o&^xcQ޼eIƪJVGpWRJJrMe^EY,;Hxc G -y,Z &=O\8jF0,.0ÎӅj3'7fSM]RY^I 1BqӞZڥ˹; C+rf@I=zW;IgzC)che⸕j$}5L FnVN}3U<tK|9 {)o#;P$? NܝyY”l_5gG_GZ ]^ p{DyD1VFxrk-a"IZdzRrf[ nO}I5Y8D̉{nށ.symU$7l9/hV0 c w>Z>.=>t4!!Wۑ]qIa>vOJYV8]qVk!w1]~'Ū`[/c]Z[W̹W@21ڧվ%k^ I Ҵ|I$eӦk:TjTswgf?0`O <\#d:o,nV<{޼Nf-w!<+=jl`񞍎z+w$ *xʔ5dxzNoT@\~&/;)Xzm1`p-嵫r\0+U|^Dz8'W-m:u]mo nxU,tڼ[uor+ӵoh3IY; F{8jҒ?>0ڔ|)\g~3jy;IY+:8oiNO;3,౴usЮ_G(‹\${SH<0CrV&"frץgL<q7 4)lֶ)V3JxBwO_&ww7Q=A U_mX[6K/7….MVd`QcRGB1rOT^+ }j5$_P 0r?.PBȮ, Y5`૸裡ZzTy'qRFQ9KxƍlBo_ަmȬ4p lCY#'$zFr2x +(a9_9t3H׳_&3Z{Zy^ĚȎGFY-tc#grawkGUΝ"C r__噋3NI4Q]GQEQEQE̕8f@$A6Iz?oX_l_75r,Y M뜅+>$JAGr:bi%Iݞ6&tUʌyc^QT`QEQEQEQEQEQEQE>$fH>&~͂AЊ_75Xԕ%cRR'>ڵoI߿eja3(z\b~fY% # 6ji²+Cl^'.IA]QEyEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPC     C   Created with GIMP |֚}#ZP+/v4:ֱ$i}YȾbkZkL)^~WE޾_g{]hw^c{+0m ;g1NXִ՟'Ge*d@Ha$j7|䪟qsk}uǨ,4`c:omoP0Ko"w;jhvnTwXzv긻/NQYo,^[=P=ZCCAumxRRqLsjhCva@)~\G\["NGAu[v_ձwS]g}91 w;'QYo-{X){_ۿ<}}01D,j;?k;.޿߬X=5ɩǹ[2;\L2\2X660ǵ԰ j8 <Z}FG|4|fDt 0G Ob}xe},o ӗ5[DK3Y:WH K X-p]G][>elh,Px~ '&bxǷx8Ա=mY5F-̼t{ܓ) |b"Ic?&0}Vt#k`-]y&tj>H`۵du7&~HW;4ROdZ犜nf!bWGA m NNkh>cs_NגwT ^eh;JEV]iYo0;jHqNSw~xjMYRR/) )jɎ۫de??9+_G5gvӒMl-sjְo&R闼+k>}JMUSUM[Pp٠u)ϙ.<!1:ن--'rVb3zmbzgڱ|o_x]\)~`I`s/M#J;4eegM_^'IW{$[@c/;)D"{O-LϠWq}Fظ?KH6"/YY'REt=+o:w='̰JױM9F`qwp!M'EdiܫVaԌ/N7?/Q]YL:~ܯk-w)`[G |oXZ\2c8޴șnW#ZR+J˼}YvE՞fiepiXm<6+˸*l}<{^cm>Ð/056 4@!&12P`vxzr-Sxrp ^JY+i7? {8~{.|n#m5~k.3k^ w.~L5y{c&{7lZ8]3LT*#͝v紾h.cFƠfYoFO8Xg ؋ F%:H Y!{}0chX UcpdOV2̠[N&y {jw㠟7N]C 3\%Lc)-j+Kɟ <Ȯ OZA]sF'I۷$h]k.o^u>Lb&\ %gwI8CXE..(kqT93J 798Tl|H¼ @uYO9Ie|3^=Z 64nY KzSBDnt? $փm˸TP=K%ӶEX|m{[Hүd Ш_^=Y&$2ȉ8fn]YKF?DpXBՍx#&5%̰=&C Ff.KI̘SG>I5[o:eM$5f;"T3gc~? lN[2 r }4s]_r V"*Qfv&:FeM*؞V5ԜHkSI4z,BbHx[,q{r~-JyD(4 5f|l?Shv LZMԱյ4ލ(hv\k@Ց}Jer.nZz 7OQ6 ՎM6FIaX%{8Y _L֨Js~]4ھG֍:C]bu^%IEf5t2_!皿U(˶]3z&o_/SED>7c<ɪ"x3q CHm|͞oJ 8Xp bqarKjIM,DՔ{tI 4Z\՚zwjVUV wLW`6pE1y lDrG5~:5f58 6NE&XB%$f H=eN%:&8tS߽6ܶe&rN`Br Kg}yGJv OKŽN_)-*ʔɌ|߽N ^ZFVڟSPws~zWL7^]sŖ%Zib-ʒ"_8J"[D.u!wRH6j(Ta9h_h|V1OrETfM&q[e#[c's; o\V8')%\V#A]);& z-*BFĎ \tٚ,ÿtzcO%ԩ]ݷ_vZeGGLR+‚rV93V.璫~cX˩wuѰg(c}Ya^3 >B=F1;%|_TZ?U:# T{O|6vsF#x:}A>j<(U*l i[?D=[M=_q-{Tb~qu ̹P=Sؓ\&֓7aI):'e^x0+'r4jnK}$>-/oA ~õ&i\_{;n)X( _":Da*E7(n 71z+Ta?ZF9e/BbUXT5S.V=797(3M٧J:h>OJw=1jlI:/i>/$ysA{12fvϗ\L}rԻR pXt] BчhcFgqF p)gE"di8yMJPsOo[+ FSqK$Efa^d$R$^h]Kt趛qt\9׺WWVmr2NL3NPh}}1gERR2M@Keգ!m -c`PsSswZ=]~_zDc-.=ÞcĽ* R^1 ٕm;9>8Db:;t67~cĪG9+6X^1?QtT+#ߒ[|{ccXmM{=q;sF/UN)s>]k-˦Uξx-E/q5! q#1PU' 98n떛ݛ6)W\-,ijpsZS.)ѡ qA&<P{#5zn17 xWž߅=:}g&1*Sm)O{;ĕT4WȒӖ\V<'j3U+H:"RqwiuY_;;X74 8PJM7_6,9jaDM:*f,JH&߃oGcNTe*Ro9 1!3Q"02ABRq #@4Pa`bC?G~wSjUx8D顎,}Ux8&+%1Wڎ2{JVm*-ivkW)׮Yk6Z7VxL3 Ç}zӧ2Q7]>]UF?uWfh\KYFg[8}3ijsݱOY N7FaLV/^$zK?76eU X 5ݾջkO.˱o-PHN~Ө UhuUӣ|#Z?5F#"5Z'45 rk][J4v˱o">l]y?C‣hcna<Atr֟s :8рY4 =(3FȬn/#.J(oMdYMY܈mc7g>Ҭrm%lCmZW{O/6Yc`n~ nc8|>I C:>Ʀ[vMɱptf4(J ~B㮦 ߻Is3YƆ/!2:Iyķ#˥E&5lfo6gVSHiĻZۉf=&+\gW KX1lz̝i'?eyn]y<_pӆm8XN]5ʺ샔DzB*'Np:7#H^ ^iOGbNL 0nɟk(ky:Ml÷I }KYq ݡ3'RbSjnbf3y5՛I^; Q2NP- Q83u/jSx: fdwi櫂o8}k:N$ 0=#OtpvKA\=?C[8JԶ&QPg‡:6˼00nS7h'T7bh<&k3- 1 ZO@iD_nbh4 1z]JܻZ7 o YYr*dArD| YZ·]6:O#=ϼ6ǣM̬>]/Vk!ްNȼ&4]2-ȴؤET ޳QX[8qcFlqX.<oj*qA1 VYrR5s<ȩֱڼLm^&UX/KsO6eXUԟ7N !1"AQa#2Bqr03s 4@CR$5Sb%tP`?j=Nk5Hl5I,t5Ms %"]D/_qN?ieHۃ-BPH~57Cb}2qk0ōwW6Re-o1rHZѱS^ޭqk ZL%A$MwV89KְVtviwyOumxM3qtwm~jK6`l:; +NGrصl.ų'NHT!MuݵvăO¬=Tsm%,V>z'g ;٨s?}.c_ X/H6A0ԫ$q<RF(Dw40$2F<<悯ѶO.0Rru(5C 캹ԏʜ#̲L70 daupw=YzSOmxwm~jImI>kp[Us8\-Z94~Mz9_I.wv  =P;~S'nc7^F3=bXj#smjf#sNqp"6o_0|R[ݒa)e N@Ϭջ۩-$0㻣~jXӾ6;M83!mn_Mt\o)m0ϼgZeYvaմmtY1Omw ~x9>tvkZYYG$ӟV+m3$yDs$*xH=M';:h2{œ[ U?ʭѱZ 'ܯC ugk Tu>ږ;h)t)ucvj :XP k 18.1Ly<Š1ț mTsKoco?9@DCX^sR7նn 7)9ڥԱH̍-.M9g<@ ]۸ FKuXM/s__~+-z**d{)(N&;e7Hc9\~^%LE:O5a>uY-I4[#BgLwN\Z&~-a_~+iou?KORƃ ~rr =u96Ғe9h2]ݏ7w2 :U YlZI>x{m ІK,V)r1ݠ k&4\4rL7TVVw on2Na,i^[?в?O,yV G`:C=Tc%^ѼT[cg&%jM+(E7mW)Vl,9lj\QT^C Q+F'G-Ź v<SAω4dۘyVhX>݊2KN`}K$!K?+jo:>5؏yU;B6 w0kJrcTߦV6kSz \ӾU:2*h-4xP)12gu\SptsFQ \B夀LÈW1d~YI:QMm +;rt%Dz<3HaT`}:e^rP\cxh ֧=Kqg ԌuWgWd=ggPcVݤ^2keaǶ#ltm:{ #\`hS;)Oe@m8 U"+(x ֘"EzF"娀sO쎓Rq828nm"GAr5;!h^IuBtM|;ɑjdޒBz쇾\D0VEnd)bH3+Kr"s>yCj;BӉ^o1 *>*GЅNgJkGM$v,_i$K+OkP^^laѐuC b#dN]R}xWy ptƟ.K)w襂XvHnwMl,\,^j6lA#*'^/`VF>TQ9ќiTw*M_ULuO'RA6D9}U X~Hp[X[ZJW=ݼeeN:$ä8F"U8wSO$jI۳KmbeUor%TsHj]E̘.;[Y7W_Gd-*=µqpATVѶOQNCy/KI=r +k.Xw**x`nyf__G}9uxx1~k;Q5t)VxYb]7hG'uzB WTrDUoRKԎ5)6w`%m?_h:n_qο2W[]sIqSzяǸe6\oEwQTŲF9#Q>6Qb;ZAvOciz e"7G]Ǹ\6чƂumӾ!UX~=fm?_ίCPIܐBW5Z_娭!,ǜ%tuMc'j+ei#+!1AQaq0 @P`?!M JDևA8L.@t+@4Pm<"yA"3#zg_%5lH: 0l v jD] XDm~Ta ,FA 1ڲN F2I0r(p<*MHʰqS.$`ƀN bMx Tt! `-B!`_cg#L]>G ;m])OLMP WmW.G*.Yކ+#o0E|YSN 5GpMI 3!p9PL z!-/j&慞VT>9B۹Juvk6?-( ǔה/NrBpGXdE@IDѨmP1C,5GJORӃJua^&ĩO޶x7"LTJ1P;fjTNHQmk\lm\!C}/ bhpl?֔u+ͿVėoY 萶yci ")|O2>*]잼E= B Kͪ>%nPޛ9&xDBS`T:k d K8]1Hfd#I~uQ`D7gZ@rù }ِR Y0jlKBuY<% +c4&uJO$"JW9KɞliehhQrXfZD+XmKAean@ Rj3Sk.S)Zpw8R>=60 E"GW9i",ETdɔjx M2ڣc:wV[`4BGcs\PUYޠBY։%M"&kW/d8f.V_ݸ`(9 2ҿ*S\S.|y&Ė q`3#[R_0 pWf]!Lt(؝l':7SzAQ[g>96R?uchChgl6PH#Om!!Pes+jG"%ͩ[,[OJ/ϰY2RCLO y9x *tc[SltJl:/aM2XYW@ ThⰛcrMikuZyJX:~>m6"l~jAr\ JR؝tUqjY@syx›TPL"[Xj9hi= 7Q6ƴ˚*:9;SBԜ zM$*.do IpQ}aO l4:, ϕj^um]G7ϕbWO.ͪJKb݇>PiogGNЌ*qd~3=.߆0H *Q=pp"եOׄLy{4?@?`W܁(r g=uuʛJ=}L]wJ2EZ+ܛ9 9%Ld7\gǼ׾[ re4r;j_bnqF mqOM%@rދE0ũ^5M ?L[ 2AlOI MO|#/: ;Ɲ8z%} vA<)Ey#D|z #=3=Fܬ= 8ܸ,ýs":f *N߽tZ-r$A%ܚ8v5~#ga|D9pg[!Lg M*dYc`*n6wgAaC>v='Θ>Äm3IΧ6egEi. *bXInJ?+@СF05p rvٛ[qofu=dž/^O|+Aӣ f_`uj\$45Q`mKZ٠cЧΎB'@3K:X{:|M/SH#A, ׾6ƨ 9l@=D+/Xt(HD&!hRX*FEdLhaLeS,,۬|][Ek0I%bm=6!9зnBcPNE9VlO^DLUaK, J(_N& gt Vƒ1t G~>\b]=)!_h8yOa':z27!G}$gخWy_1JjGJ?nُGu:2V0hHGd;"jnf kBut.ϐ?)enMCDNFH{ b`yUp'1B 7YNhˎGqޯ $ڮ8?= Bl=H馐a}긔 I@W][q:?: neg a^}X#{9YW`na}RzQy4g:-SGq~ߓOU:Ev7HN=\TE2vo d`_T P{i3g&9;ƺ0.EL}C"ij#or9D H8rqkS9=;i^؏KxӀKwn:i2S.̚Dc$0`ŸzC(ÓY>Ĥ ~Mrx j}@}5ZzA B:ǣMdk(c|bPh-}d2rq8{`tCCAp]vf۶ef*x4(#$HyNp pE7:QXr/om}ᡪPɀ l]};2;P< i/l1^ѻkCD֓p?uDvNtC|B|R kbS/ΤUDj: whVT1P穢@cqLy҉wX飼5űM.5 mc~v Wec`ӈV2^ͺL ^\]UC)';%to֞@BQ$f)WS0 8蝐_"I04i*Lݾfb.TuQ%^qN M }1\=DP6=ꗮ?- ;xkT7tέH:Szv`.$v>ց̇tIH5J揯ſxڛv}X\F箆?zO qѓ԰,t SpG0 gG8 '.|'ޛGje+I5!jKq+΀ <,%;O$J C'ަ>&,ڎð=}楢+,0^қh.?[ץK,q__q394@(rfjPQBAL $=$@JԼt8r< _f No0$= f>d u\øD*ɀC6wII;z2`m` 0k? ѳw>?ڭCٜ ՁjDqlɶ s9\Vc1gWu[[~o7&q@l_@ Do\<1$NZ9>=iRNozZ c}Tp7G4k6CV1 o|iC <$|Į>ǴaWBy\nbV0koRvaff<:nf]HӦw%a^Y^]|g`~"px}e;nS#3eO |Agt?OA1,PX{>9f_D]-J0ٌ8IKx?l(\r<˚Ϙ6mFc1{p(0[YOc 6tcCu YTxV/VŚO0B\K/yScQ&f6V O31ʝڿ8$!#|*ÙRaGuY?5>J_|"L &d9Bl>u1#lm8/J*M68l?%C/̅SwܹBz[pu7XF2*P'&G_Vs:Mg$ `dS2xg9BrHz+8!'g'v]p0r r39N-Q]]FeauHcZOS|2ݛٸn%M*zb4kV8Za}<\q;SpՇ%].PG6Ve_r[n*lsx윷y/&瑠} 6J:aXVq9u "Ϛ1&7"L`S.C3dyByZ3m%Us`^r; D N[]y]f`o Ҿ3a NF'u42 Gnopdi ^2dnbw 0~?P.+,8hٕ Lj_|G (oMU,i`^$op!F,۫9j#,Ǝ8f'xE9>5E]֙w:3\)Hw/Iz oDE߹>Ex> 1DWObJYWq,{@FϢ<0)vG{݈%1O&NDVQ19xFvaAB ?xC>ոuf]}{ &d&xz n{`!tD$n{o+EnrJ"4ja dL E "ި̶'+seE`Q@Q`jt9 SI;t oq4 yUVǞh쟤Rŧ\ e|,9K~jU(GU H• ٟ6w.]B)օҺ*&=-o|ԳjƔ`*(&(@}/>p#I9Ji6k$#MB/'G"="ܦh9ES)޼]toz 4 ۜ_xޗhsLҽ@9j[C{x87jsMo;ي鑺xm7̏tkje3iZޖC:rTO*:$պa*4B"CϏXav\ %'&?*.6R;f Uܽ{~)ALlu78/3 ag3:z'l`_ƤL@s:Nn}pu xEvCZHW SXʼR *jPPRzCr#wƟDV(^ ںOZGfqŢGp'HQ|?@@Q%"iˮ0ij6v.ba46\aa*Uڕ֎i[sZy6ӦjS O!ŀ#Ю="S;) $s((Xpb=_D@) \5,2ĖPQb9F4'LG3\ĺwKz!$4c'i!4h _.N*y1sd+#i+*#M|14^IF_ *\}' (#H͏a:VƹP6CwL4\0ݺH̜^ga37²ܺV'y`k: SI:G JB(lUZ YUhӓ; C5W>4HRe-q4h)ځp n_8K o O~pC KA9L1#ox n7\49*M(  0@8|HeyX`x&MN)YK؇l?hUKu7mP H]̘zP: 7]TxiD9(۲زo w.V"""=M*'!`J) XybT2(SClRTn>We>h#xEBw/Xcpph)HP8tWo,RUj\u4284pʟK&6?@ ?1|rxkSb& m _@,9BYUkР ݚ(_ʝ b~p|9}ycI5n{R_^4G-4zMpSO.^k) 4D7&hΝ*KnWN&xhSMJ8 )r(ܩܔ5p$3[S3/,G`gó4ZviGSFPJlG_~KJIZFa@NLTBAH~Q@u+ )ېgKt@0-<s۠K% 4>wYOSIq8@7k?Ḱ" tYS/bˆ}}XH_"' ZʆrCKs ΪGnrJV l1O"\"1#A@RqAф@b7;bm3Vh6@#o[m"eBV7ܞ-ǏFqN;ujQ%"QđC.̶;D/fծW-]!B;5Xh wl$$p%TTE"\r@lxq bi!7\@@ jD0\\O!GKw^}u|>ދ1@HTn\P5zRaIƃ;ůB >@p (-GA*i2^|៷(*HBx!.7Nܜ2z.Ì[cadA 7Ea̻8Ds\.( _ԔQt\9]\b+#;TjA͜dA`vcN.n .U (A)l%@*iǺݜ48!"n9|um2sp 5 n~q`sB 1w!cq_eNplyL\TA5.Ev {p%aem&r.p|,-I\Jj1n2\{Agy Ƭx%+S|^^AȂP!tD)3TސG?M<(k=$Vċpa*uXHW%՝+PQ ALmi ..SصW $(PT_#l(PH f)gM)}*b%{  f\x i%<)x`$%{R7:=U]cO/~i?Ӏhr܍p nS{ox9i\|fB<Z㬰,]]t@ M-zFzA:Q]Gf/!LaES@ !9R }a<؛ (v4%iyFZlu1Z(a:^W]UƃC1(& !A!"#ɀR?:>_Hc7FXnvBczYUND"OA0QVa^e7$@ Q/O=S%~tC= sfifetch-tools-0.18.5/htdocs/loading.gif0000644000175000017500000016637514115450625016340 0ustar useruserGIF89a1$Hl$$$H$l$$$$$H$HHHlHHHHHl$lHlllllll$Hlؐ$Hlش$Hl$HlU$UHUlUUUUU$U$$UH$Ul$U$U$U$U$UHU$HUHHUlHUHUHUHUHUlU$lUHlUllUlUlUlUlUU$UHUlUUUؐUUU$UHUlUUUشUUU$UHUlUUUUUU$UHUlUUUUU$Hl$$$H$l$$$$$H$HHHlHHHHHl$lHlllllll$Hlؐ$Hlشت$تHتlتتتتت$Hl$Hl$$$H$l$$$$$H$HHHlHHHHHl$lHlllllll$Hlؐ$Hlش$Hl$Hl! NETSCAPE2.0!,H"TX!A FѠD/VܨcƏ =PdI 9|h˗0cʜI͛8sɳϟ@ JѣH*]ʴ̔,'b UjUQZz+ׯ[jUlY`њM{ӷpʝKݻx˗*G >98+ÆKEXTƏyuՋkν7}<^o^}{˟Ob/LuQ& x]]~ 6F8_|UHa_nȞ ($T ,b.ɘ8<顐DhdgL6do(H6R9eVJe\bٓ`)dughe)t%[޹bz']Z砄jhi&ꦢm6*餔V]|镚v'i騤j*hBh.ꪎ~i=rꈶHA=Fh&JRlg+IF!& n[eHK/-Km[(nq .lq,k諱 /P˫ 8=+d3oxBQ]+,yxzHb ~.}Jc<-ݻwh!q2bEI/<>w箻s@to޷ӿe}ڊߓ\8Vr^BX"kWI%bƻV,A گɩ<)ρջС3,[ٶA!@. ?$R}H!~ax>$htAXs*X."X5hKłF edVH=[ٸ6q}+cy!m<W7?{bE1ِw,mD ثZČycJ@iNK?BV:gRB@:r=^n0I(!h6G~B0®X aWXAMf^lNҎ)D 3:^y)NDNWd$/!k %FlȒWihCN+^‘Ɩ=k sz eXˠ %JU J΄]Gqi!],M̢fe +V$3t#AsY6- ځ<0xY_HvUGePҢ +ZWBm_O7ȨtLT뵾:d/V5kf1YN m_KZV5jW;r}-fa-ֆ(v>uHp?ɣ/4C!,X7AYH!oÇ#.l(ń]ȱǏhQ!Hڑٴev )$H{dHI"!Rf jSPHPgPBJs(;hMBգj5IdҜ `[6 R$WbfPRx ,Z8*U0-L)ȁHmѴe-*]hKʖ$RH 'E{ZjQ˘CJ:ѨVd|&ѲũV?J ΄FmvRZ:Uh]m俰@&BʫVA%e Ԭޔ {J W[&O%oӜUXY̖-vr d]Ez~ `bR-&%81Тk2cKhںm4.$݆$z"=G!ПnrMGJHz-neXIMig2E\P[44goQH] T;)qV1\`[uuQmBUy]#AebFaUZkIU٤U  E_`b5UIB؝R`9֋R''NEfb ufoVF!crWpzt_>ag SʉfW*ZѨ 9lT-)+DZa:A?U.]w IAf6dilmqm!2kXoPQ\N W՛N■pߒvT+E:x-afNQ&q,xu;0X7!זٟۭZdƛ&o~y [RךO./E] L.D{RX32*bwz|@Rz*M gͶUf3Rs)З:yk8ʩEaX 2e6+ԑl%Si]钪ŚsB'MxrI6X ޡBKU 6j~W-HAfw Fd}:'qAk\ k|Pw Ƥ!,V8MXH:/Ç#JHŋ3jЪVƓ *)0RX/(s"$%>ISgF )S)5QjC0}fjQTRMh͒H9mvKDU{)ec 6^+Kcϟ0ɴ[kv?2תRk)g-B TPǭyՍ<َyk,MKzvIJy{PlEl!,u`ǏZH&yQ̖L6y55fj 30LعpPm|@p7Mf)EkcB](؜EWyٳB}QIQ6f" -+ՊxAub&dŹ}UŬቯT^S|\D#Š@.9@f9m=]HF$U_eb |L ؞!,T7QYHÇ;+ŋ3jȱǏ 9p@@L8Q䯑/lEfRVdC#k)RHْSgVKJ) RJ"aSEBTBm)4V%Muh֬\i] Is.‰WNZP*Jdg,7|@Pc t=uKv`h \MVZ{פH%wAVlۑrԶg+[>zLA'ٳr~M ]R2u\q-$|Uyx^DT݃Q ylmFa)Ys EJF4) \ZRYlmFF#9F8fBԊY!EUT[hUBrT;_9jqE%I[Mԗ["D#VKTSdlfKⱹIrfIe袑Vv#5]H"bR"z?T)xY{$)Aj*ޡq)KߙvajRnfB5jJҡ-yF+ ʒzM7 lAVEm <䬤gYp5A8&R T_IĹnAV9mIߺJ&WDWY1Ձ R8h'aq9j.phqyXlU$DZٝYWR¬Ĝ&䏣Jn}^D'hٜ;аJ r$I3 _ ]j* (RX&`KSXNU&vLRM#ഊ 3v`LٌRҎdž!`ZN{8d .un76κQM vjw cpu+d ,^\QDHr`vKuͨWT7(9)b-K g>zk^3۠8K`C?ɮjt\TZ'z!,T7QYH ;/Ç#Jċ3jȱǏ "ŊTV6 aE6I"Aʖv/eˏ*yXik$G%$Ҋӧ?>Tͫ4:5FN#4ѳRlUʡNwSBInU@{ڝlpSRyˢ VlKI؊ )2nVcBV[2.d!ǴMrmo Hjd1^h)dhOŪ4m\p_r7L Y6|v 6P{ŷ|z]9[AmfG\M( G\n+UZyKS+%-|qTuTeW|U&I6@|AS~ْ_?2'NEڀ?ESRqcz]9Dx]V/ee? SLFmTx. ie]Pej"bm3mb|#U-8>m iPUi3S{Rr=VS&JūqoKkBa5S~cVImCeҡ[ ךc Ч{TZc% Τ'kmH":o6N{oYJ8ԑq0Z)?!`,PE3[oIx,P-2ѴEr6kEkT@/=e2s\!W8%1m3]=/ין$}QPVXK!B&af 9(tVZ=e}YPU(AqWwqʢKptzcSq\d+r!^<Y`FK Mx~3IP6;<*¢T7T~k!~|+ٔ!kd_֑2A+mfb;韔zĀ''-A̠ 3!,T7QIH KȰÇ.Hŋ3j8ǏI(!ڳΖHL,U˗)?"'{&HLO)dZO I$ */RRTc-I +ժ]+%]zUT)Z%H.޻Z&mmIRmFvXhh{̠rոhu۷J+<ԍmmxpۤ-)'ȴؔLQI~83~ca$fA)gO+E%=!n(֊QbI \2omgTe-AbfewpvIbRX'-ixY'ӌF]oU[%I@ كZIE?]ToȐH(+-/Ep(~&&BbNrYX[CU5%R-PUfPP8ܝۨ U"TؑתzIlS %xXQ[mAM Yol\-G@E!yX5X_yÝ'EjEZV݇@ b#C39\p͘E4gJ!2)$02IX"Ҏ^-$ڍFfDW"AaٽLٵUk[șwZ_I&E;@jA(%IlzAԁDjB֨թK1j]xjB=wAiR%vjmv F_P'&lD+FFVz\%wGY&}[P `F6YޫQ[oNguUxfE 7 mW^ɹgۺ.%^ /J-h )!xٱxy~;ۇ@=o{̵e,[ sJ5[(Me㞊(3+٫4uׁ @hs]Ё^M3"rՁ*jۯ PZ:Qzgg?!qbz}@cK+W_E6SbD{AM}1א!'\cio7ԀJ^ nuí\Gc9]&O,*T)e9-i(:%WA8P+p)ֆfmCؑBeQyeB%P5T>q5&AYVN+ъo"Ƃm!ByԧC7ކhߡ\N[ UUB] iFI J ރ mw+1G({:ZzP2Zt&T4`J+FjJdlQ)bBdX QPԎS:eZ>+_PTR/+P)Źi{{E! Ycf݈.o/)DNb ӚZQ=qdw,j-h$A+2A-*~5ٳpW$uU`<+N+E(}WoQ>Vl^Ňa JlXS[-b|TeiSպíV -d^'Gt@OEg@!,`7EUHP<ÇA|(qŋ3jGZŐcF{ Q #AR[lJɍŒK[$$ h+9oj5Re˧9III2UJQ)rTՖM3WJ s+ׄ^&I Rm]*=Kf]LWmlڢCdJ0NV:RʝZB9}LTK+9k"T薿y*QleU4Mje&Ł<NvKH"C ʝ=L;IhR+Y6t?D ^&Ek2l5^+GPI6_VM_z/HRI5djme=h!I`衘bDXm}Xc}7dShIEgIaXxA6aCmp٤BB&!)_BP]IrZbhc7]KlifI'NezW3RJb]azI(eBގUhrVaũElBU8DjV]gZxQkCqv X -ꄠ> mv6؛;5 sMlHJ)i'pJWKХԭ嫶0&KЀ;E #MDƈ%/$"h.KvU+4"  n1p6,Jߙ\/팷(Y)nVpd!kxgۼk#nqW6y|GI+\s+ -TUUc܈r1mq,/wWK!Y}~D\mIDɵwd+t^US'.T!ڎX^e^dfmcK?yt@Z$+xhQUKA=Sͦc@S+TƉR+ZWY8Q[~Y3Ib*5dzm4kq(W9Pz[6!,d7AU(0Ç 2l8ŋЈLJڑ!E-R"R/U<S G $HJH[AZՖ$5`M2ƞί>7)ו]TJTmDԲܤo[z۪-bkmF1MmkJ^jp[M˩WN"! i)>eh[92Y}S' LcK}jeemx1c/>hkV7_[գlQeeVNe-&}^ "J5ؒqIXDziD];6ؘS ')vNBV$f*YQ )~%GY6~Z|H%=e5!#EvDmڱ$K&MR;4!@ej Q+5B-'&{ݵ}]t\=gIR)㔠(L"z/tY&5:PyYѶvqw4ڎya*A[ڒVJ:aEd؈YZ!h@=zx"|b8Zz\vkIwXj\Uc|:VکAh|dЪVκE:BJɋp¥fnٞQa5J]O;dVxE~bR1T"DBҙ(yd|YUP\FBі[ayܖo=U@CPje&&˵XmrwM4śuAOc fG }H;IŗaR $W@Հdcԧ}KSTZ܁jY NabE&}JPz r;(Sy_i&=jKsr^x9eٲM)%.!q6KqB+9sί+xoQ@!,h7=UH*\Ȱ_#Jh3j$GmjΖ(ZqV[Hʁ S/?"h Z=)SDHdfЖL:eٮר[mͪp?쑩**ۋOVJ_H8iPͦea)нSʸ^d[rϋӞH2lW;2ȰR}sqC64[m\T )߿ ۩U&޲MԼ^8"-n!=-xn/?Q[ud[ZL`;R1If)iU u-e9XY zdyqX?5hZE%!mEx3!-%E;0T"5qH(cFy.TdK;$%NكV|=Bj:Ј9QƓµ2YWf/utk.h˟-&w0:TE?&ޤ:x睋`vDn$-JilVhHcwgiO)aB#m&&X Ht]rSAՙmG x& @@䕃$mdeJ}J^QBJleo2p4zBE@2罐p,ڢqKRzIc)dtE ;Z+m#KBqhuLizQ|,նk(a&IʟG );IJ^v4Jz"ɭC/igk3%FnP9+?qHGZUF'FrG5w@35V!xGZVtf,X8e5C{).]+C4`ڂ/Sbךq}q!,d7AYH!\ȰÇ AHŋi܈#ō 57ОǓA(K!C K1eki˦V7+*)H`3(Cݖ$fiCw"M"^!iTַX6M5'2cbk(ݺREr{$[VIb%e1D3o[rA%[m"e e!jgfΩlڵRN ogқ_RaYm $3Mo 'ΐU)Q" )gn\y⥐oOӟNsWfHm~2Ֆ{u [)Ӄ)i4p ?2ux[QUj?/GF ]xX% )D`vl&|a|q@n_b$ڲIΕE^YQ[>Ťka8h!QezH)dʅDYwу!yHB&wh^vRB"d)DVQ;{!Iiy%BM]#vqmeIbyZ((-j$5VP$k kZF]G&"j+"J0 (䩻RQ ܦ-{/؝%DX|{I,Ў!"IL\q kr~ty˗ YUAFfC> @[DTf%$Yf6hҿ%'y1\O`IT@>ϑSFGF TZ؎5?eL({JVxqwg"mu[Q U*XpTB[<NJUsۜ@U?bǬ=2nHLJ"yF{amO-&1pT*Jh=*jf{$KȘ=O!,`8EXHKȰÇ#JHŋ  Ԉc_C,ȓ K䈲ʕ[|YI2/|SN=;\i@Ѡ ATi'M[HBJqh-RD(U;& ,ׯik5Y&Ɩ-unD{d܎L֖@~#»v'-^qF& XJ n*TS4d{Cg?jRwB52(7⾉N=[N3W0Md OގLY[n?ؙq-} U[k%-Rt@ly )Z hiID ؑ]ʕKt*rGp%fx/*xbn.c[+VlRfbymk[zZzOV[[d%XcXeoItO=Jyz+]Vɜslʛ!N&!@ \;CA'R ֢Ax"$+$'Y $Aw DMu`U)YQ&j4YYՊ'-O;XV~ AdgZ3id\ )ke I+[II{ƤD)e+*AJZT|"$sv*Zx}KnIF[q dBdx@4rImD٘@՚pWx^MQJP..DJ}K DM:+t%^L{du [ %LfU3ݎj Q6g^ǖDwlbI dW-*5^&ȎĖ}A>ߗeKquMSIUok$Q+dM}^Qڊ>qAuX!\_|)mx4j^!wO/彉1:觟H!,`CEIH‡#JK_2VȱcČ CzIbȓ%SNb-EFgԊ^r[tURvkTA^/EQ5tS(Q֣i@u&)dw+Mh/b TX+yEHKFx_IU+Kp6Eܓ7 DKӮXl_ qI%h4q!@4byU,NiU_QE;!L{(g~'[ȄWᅗc)VA[Q TsFUU%3WBE!'٨E& g7PeuEUd\؊Ko.Y_F7@Kkpd\F@IP;{<K3 mʴeQMU-@ T@-A(J1K ["$V^e 5G/R!,_HFDH#JHŋ>ȱ Cz9߯'A\0K,c|ISM/md2I:ctK{Hae(ːH eVNWGJ$Ji1+G[R$QV{f= Kvq;oV^.ݻ1Joئ/zkZYze jժ-{C8T꒭˾n y)7ؽ}iRE oik\//I8p۵YyNb%lL~-_DL]߾`W^EaY eb1A/qFx[;F`F%ԊTd`5^Q%eUX[ هIr^[ّy@ @QPddQEI@+4dtŅcIrTN])@sz@_N@\R-b}g=CQ.w]>dY5ҊRXH4'(5%U1q Xٵ#NiԜ o\zdk5ѕ mqvǶR/JmAƄe!Or'_E/&P@_٥R@V!\/'mA֡fΩ=`-(^!IQ~ 'E"O]V(/Y#&O˨͕RxlE:fghפ5E*UdG퐡k3iJ ai%fKU>ES+:yUf~ETrWZR;Z9%jqP<Б !,ߵ5Ɩ{ +CIym$1VYo#fJJؐUNǛlI=Kg>)n԰^Y K!,\PM<H*`Å#JHŋi܈G5{ōPjˤK!c|IS̘5s9Μ)eύ~j=Fsjd -@)IU/R]+Lٜ Vʖ5[v Y.[ɭVMi-̶U MKc6޲7UgDfH]]翧qϤ=_l"nom%)(r6 e`^DKXI I2|&⸼`%%0-˶q³[Hf~]sAeZ$McRH_'PpTnx z[lAJ{WUAbY7bW-T\M(vZgK+6#BT0*=La=Lhu=`YzF-8EV0a=el &EaQfUTIo66$uQG+G6ԞI.tz~ Tdv`H#Ba[s[GIf; U)dajEA!YiM[AߨNEbGUeUXa*!~k$#pG)yh"@DJvBF*i@ `GM֊9b#t\gMEtm%haX)qM1ַx;+MŤVzؐ-K$q@&]tFXp%ED\\ [`$ \HBtVa|fi'1O)٨}D )bi4\VaixYF؃܏fAg5hz\neiIIQ]d0@6Og c>`⥹V+'Pg9i\dA'ʉ` @b0f cְP!,]`L0H*DoÇ#JHŋ3*lǏ CIdBQ4%_+MkISeL5s 93翙 mkΖ=1a T =2ȴIPi-R[p_I٢yЖ z))I nlPoK\`%LE/$RH(ɸTi+nAmWeE8%MUW~t b)pnimp&ٶr-jH1Ej i:ڊ[Jo58W~V+[:p wQi1r$-4= duSi_D5@FR@fMw*5$R#/-w7Z_@۽us=ualFhQ8P\sUIV7$P+h#@QdAhmcYi[m2/w!v\o!&&"e;0JxFn}֎/xZ"EW}&]'mq8y5YQkQĉ@uf(t&^8n4JaqTdEIAiA_|@Q)hĩfadxs>'Zɶ+Hٟ[ g`EP+Xmsr :h 4su5G@Š҆6^"h]PTA|F.æU@!,\iI$H*mW ݶ*Jnw2nj!,ZhG%H*\oÇ#Jў-(j() ɴؐɄ%E$\hMZ%d`-#oL֦ӄx&@L):- u?[.*0cƁ"Z6M-lV` nI.C6믫2- ;@R vdHD0ƚY )$S L|c'O2o~,wSa%dns[[ەmI;\,kז fRx\χ:#+p p7͆)PeRDREtc9)`OܵB Hc[YoTYEX/Tp@=.?d^ iAJ ]AdLH0X;-m@ 87RbH=APKeQK$JK\Im4BYF&XbhKYWMDO9YfLډ唐d!;Ef!]HRiV`Rmr򑒙pTBJCoM\hyfAJe[Ad @ajE x@= ^JDFBA0Dtd {)RDA+/t;olF!,XgI&hϗ=\ȰÇ#JՎ R(ȱǎHDB2~\ɒ/{[J^x_˛8*l$I2m GR%bMR%贪VnϪ׆6(0 Rn&K7Jۖeڻi 2ljjݼw;d{%[Ha/[ /b{<1؊eBBR3[} W _EZ;ΰ @@Ti+C;$C7_ lꬻh#͚-yd+pKV[6i3lL%I $WGhYEc¥JRD!I)f{Rc (IR` SmDs5Vn T FݘĈvfTSQE]TGeVmf^@iqAze 5Й1eR͙B|\F#$C~MvIY~$0P~y(W4iiߘVY~4`d'Dk_FU][/Hh ڐ\&Cx&\aJ06D`qnkCأJ]4SH"ShVJb(0N%G,1fM!,h!,X`F0k*\ȰÇ#JtXpŋ jeClGFl\)ݖ$R`&I%˛ <%Οmm 3fL2@g%Q iR צD n*ΝRnVHJE,ziUZm"Պ.ٶvEkU;#)/L)rbHd-ֽ,Bb6&M8ٙk. S ΤۭncGB,Y{M%-,mÚaoh.𛷐UcvYA|{RRy@[/hmFq [I{crEbc0!dCS c Z Fwrv`B] 6L"sJvn])L<6QA;4 gW=dMwSi)w1w CE^aE|i-Y[יS]Z9$Ih-A'd[x!f +dlt꫒&4?\g=2VIwhz )iXtesp yXA6uuY%ETzC*)u̻P]]=&mZD'ѹ G,Wl^5R@!,V_F-hV_*\ȰÇ0EʖMdAȱFR[HH"EJF(Snl%)/_MH$E I)n E)fQԡ?dHPҦXp ͬ`$E`I ?[UZ Hj.ݺY/wX=UXȭ.n!Uk`b5 ,Ir*&MAS eIgٲ~- $w̵nk yG{ , @)sԩGD}4VO.«oHUVR>kܪ-iL\Y֘=QWv*!pL[6Cq$QQRqhfhv.K QJd)Rl u) a+pU4}8҄B H/8aJg6$M&iqc;&il4efIm](afq%&Ц& Uz)}K+ϔ1Nh'wەd9q7ubEikݦFv*EQB|bDS-$ kBv*VT)i UiBؑ}%iQCi$h]ZQJqI`nIaŒPfd=h1ո%C$7\A,T@!,VWG6o*\Ȱa_#JHŋ3jqV$$ eK+0$%I͚.c4HJ%˟-Eiͣ7sϕRTnitHFUJuiU$ LWlm"p+&nShlW^tJlEƌ5v8=!B\˻[6y|hԕQ$&MQh+[uC@}J#tmjj%$@,jMloA)n !GV,z&n`UPW@ѵ//RH & &BQ$Պ;m-y?Ua_juwuyхdTsb Tz#C TMR4'~|Ʊe'B^Pp, D)_"oM"FX[b%ձib&KwmT[h`y_gDPEJ-0rq$AV?E! fszϹv _e@^ey"$e M UXr[圭EW VlKŖHRw6IY!uҴs-qo%yA,S+";&4XAZU+9G?"DMpjfUm%߸ir%vgJ+2][ROS b bD k$X>{AA'DF4CR!,VTG<o*\ȰB{9HC[Ȑeŏ k%EJ Rȴ ɲ?{d$8)%8A*y2Ir JM(%tN&&!QĦMmI@@ Qb(YjmͳXgkRZɭ wqWǾ oqA~;Y\B=lǘ"jh$Ly-PD5MeoVڦ@[H^>k ]}>Lb`m)=xRG×Ziy-M>At!H魆\ &j(Vq%D Y!@EmX GC$E%P[TDYm]I1^&NIر))%%E;B zUMcA^%eB$4W1SK+ uP9@ &iћPP^$/B^wU}hj"WCXL[%t|A8WFЦ=_u%5bl`A W+z]Iogj(KU^I+v4&P+1$')[pҧv]xe I$?xꕨsBȵ|4RX\i&Uiώ+~deX[K;5"DLX M=EA%"׶p-ӿЈw(I!Dm p< D/vY^* Kgdϫ}e+[g.'&Zp{F\]714A.ԡm5u{GzW8)7w>T@!,PHMHk*\ȰÇ#JHŋ P#Ə )A&A\ɲ˗92f_ljʖ͟푑"%->R O&eRf+FצQ#$e)׏Bf`TԳb np @Q)Q$l?JiupDÇn8YYǏ!ծU_)1O=2ES thՙ|Eà[C=Z)RdHġt^686mF!ܹB[ukU2nN~`-knߞWWxT`~'aQh'dXN혶 {핦ۂ tQ{Y+$S̙֊Χ"X=W!vvE_QlI1-SwW=6`G嗒dM_2΁~ 䖇\R|yG2"glxM>U])Lv?%1&%&UF?=Vđa%AŶXFE#QQ${.$io]-98װÒ2c>IP!ʐQJoU zX\ :`F+I R֎Jnji)_Ȫ AKziALʥEbAd#=䱎3f 0)Z۟-$9kQ"A @}\f$JͥDq*0Be,xZ+eF-iÕE9-|Q]”hOACI2nm8"L"x7(uwjZ!F1 %54g^Q/þ!,PGKFH`A*\Ȱa#JH"{3jhRH"D'mmEB-~IP@p$!).ITb->HI#ƢP  'աQgLHjW/Ie ߿ Zm kNDͶW2O5]?KY~2-a~%-Ï!>p潛CoѰhϤ 'Zѩk%$(ckGOuWkx/ѷs)ҫ]J؍"~(IރK[=d&Ec%e@rh'Xa-~E+X@J7DHWpT`ZE&nߔ[.%}|uH&[lke]h08&s1&4RS5Ies2&$}!x#A$ؓ^6L%ܒ\MPX; ҂B\ƙB% I ق?ʥCD ɵީ[)AJױEghXEUMXiA ᦒa#h֥֗2 +߻!XKR؆IE)jE`bT_$RYeO*TIӺ&dFN]̈́\B;r#ɴ@j+K ŸNUQZk:6 D|IDV;mⱷ$m0GG*m!=LK ))5mA0 ҆5N vqcdK_)TUSȬ|+Pm1Zq Oisf}Ye+ |rirnɐ{+ `{gXgXdOeqW!Q]AxG@0EVI@mWup WI]$ r\m!n`Q:$HSAr&-z `qIf7I-]Be'i|%IRQFcP j[i 쥨:])$cze7׼uőW^ [ha>}Q !,P8BUHKȰÇ#JHň.G%-RdXG0c $#fM2,]JՎ`̟hJ"!P2:!#eR~dY3VJvNҎH#i!EQR\b(I.֫"+-k'-QÁj՚[XeYjѡ[%$Iƨͻ5m^qȂmz ךĢn-,vZ ٪]?t[ _9tH0{RRўܲL+%GT,2-/I0}H鷕[l8גAgMILI )=}FemaE UN4_mRDlkRveUӌěȣCSJ` Ib$)?I坓r4 ͗ >A S4YvҊjfyЄN9o][LɥVmsdzzoRImE]E Ȩ$-Z_IYCX]Ch}XHWBꓑdWHSe'Cff)%PyA%q>AJj)\8 \zbA`Xi/vf.Up%˥}ڦ5 ?zD%1ЅREّTKK;Gץ5[)ZTz],Ltff4mZɭD1UadlmO0D,"COd_nB$ M "DOLhA]o^/zt҄\M뷄X<@lZaG0|nUr wU3CuG|?b82\O ' 'h疴[R')?o:G='^IfG}ZD!,P;ERH lgK!‡#J6St3!m_R;M$F(M_?_\whYKGWdC7ꄀL`D!,P;MVH =[JHA bAR[6œ('*Ȓ-RĔ UĔ8Qkųǖ-"EB4I\:-IleK22ʔI$K9=(-8n iѷ[$h,VGeF)UnhI'}6_[%DAߺ/$-K3 TYmȌ֨V=2ZXu@wjܷWۭ6Be˙MJ ۞lTz0nɛc3Z (ZA>$զIto,)f]Q]xP+P'|81DV{Bʇb 4^1wN"5aKQIBpR҅QDADXSC@VԊYoJ7M nRDb|4a B4(gZXmV+q9'zF)GYўAY)h,=M.zs2ʐhUk1ikY餔J?gjf骈 UZU/NVTںԜF#`@eev)f=2LE @}|iEAbkJpӀ:z T[#F{]oE\ jo\ ʒxdn.Vb+2\e,**sA}ZW p,Mb%Fr[ӾmiqU YI-{?UJSakoKNH1D[Jang@ ʒi__D3Ksl"[F7h!Uym·d]`׷B5چ/UZoAV"ĺɥǞw,[nl;$% ߄(| cFU=LH@c%.3!,P8QYHJȰÇ#JHb3jتV C"%A-0$q)p$B ̣$yD)A[b̋J)Z Я.mm VFDZs/^ mV[n"cO Jk5˖b w2!#;)P^ rY-j1Vl2 CFK֤My[mK(۞%jQJ*e$S HcNu2,&OBF1k9Ս}idksk;\{F7x)bV@M$PυVGjmaD`}8VFGS^U4oxIEеb_ 98cRH/mRhQCHx )9PAF)Hf}VL]5RfGf`ybY&B] (&ifH rޝBW.)OItFh`\K:"PQג d(w&zzjBh1`[PYg9QDXKezp)Z~ݞ| 5,eRik$. mIt]-`f g+ǽ/ 9)d0Rl :ZI+kci}p"p`\eKCA*k"-k./lKJ ]mmMǛM +F2N p[XQMYTCXog|e.CLS=]\vV1c:rdbV5U.]˗ U.WRZZd0~Xt;-[Dhϖ=}鋧X!JӽExG&U~TFETIENFSW-f]\[rR;T?5T}EFnuxuIPjUXJ6Hael#y󽶡ud,(L)ӆWQEMƥ@R/ZyE#[IPT[HBs]POF׊z*wG '\G6Gmn1Riۤ EG&s%j_'':[SUiJ]BB-jA GZ{~ET&x =avԞ J@칉݄ۥmi)&Q%feWU)R&1Bf&\[=5&EKv~AU#\Qa1I&Al #X#䨖akM駥)x'g" 1Y5U-k 8kT|l<2)*w~n"jZKHx=-ME#nh1;|o V䱙lkqYZkU;T4zu-WQ4UiPQ=qPWp A> )A W,]!,P8UYHJȰÇ#JHŋ3 VqƓ T )R$H!cnDɓ͛mL"%$4u*ڳRH<:uSMO^'0ug֋JsikSթQ\/{yaבe1*-j s&[!E]QƔ@JڽjKjTX9`+4橮qq$I+*![)lBnehmWƧً.ejd~o!Vm#7ɻb}ȋ}A 3Xc`mV]\UXI+=KF %&dSSJqX4l!TS=Պf$E;gC&dm-R6xӏK7QbPބtAf)ZyDb&Ytr堙Ĥr&L/ C);ngB= D6$ /aͶ qz.UBFX.ꗪ $z*TU_zPJC$ j@GƚI—)nN{PЧSa-C$(GW~$~N< /vpxbkoS!kantr4c0qrp{"'C]yXJ)3{E-/m&CI[[΄jP/)40J| qZE ţM}J(_]8sk'Wši9~Rlޮ&+X難)W@sjX 205D,~v4[6e$J:B ©@|/Ķۧp&e̻@,<yG 5>-U#asoG[l uha=|ϑ'Ԛuk:>U ApL:/pyU(C5ic &A}~]- ' (L Wpl !,P8UIHKȰÇ#2'ŋ3jǍ /.Ϟvz/Z&BR[lʟ#)MH"EB RQN%DhR&2vT;R(=lմV Ҷ$c׮V.GeP&mtMpb[?P3׵O7wR!O[ċM&ٱe6%[6K~mv ?[Lے*\ŷj>[2cHDC_Ȉo׼iz% ͎WHq-{mdV[LF&aETRa?$^qLŕQnq_U_K;ɃZtNFG-d4@ATRNYٵd!P}!h h!_Dץ6V[ӝ}lFJd[5+^}A%FZTO>ԣEhvXWc}e5 ]>dZӟOI$>4xT(G&؜zgdb` ]W#mjkMѤGۯ)W@:5BZ$ mw^BJ;2K+@긩nSdxNS5]:!*/Y ԊYnE׊%eN!XߺIXy]/60AoYPh:m7 ZW*)W34U/^ $u+t~4%" ZӹF DmDOkݙt>uHʽTeMI+SE$df44NQHgIɷ0YG$-{"^TR9N,+rEܧ݌oSe64t|ɉz24`*S9&a%r%" Rp_Z" &ˆeF%9!,P7YHHKȰÇ#%ŋȱ_l٣$Dž mmڲ )0#L$IH"ARhhHk E $N&MjTv*eJ6i+hJU؋!Ch<./4|kP[Z}mg(=ٖTVU sY{d6ܲIqf]@N$Z㨉{[IE:,;fcɃ^4SW>,ޝ.)vneld,DEnUG[%GQc _ Ta`]wgE]L]0oPIX} )fq[T&%eT- HH3_F"`D0Cu))et5IXVJ~Bіe]>TCC|G7==5P8rT_Ʌ}uئO+U-6QD^imLw!G}E:@bC㑑%~IH@wᨭJdJaD-g TvW<9PqS:K(\%DS6ghR 2]#{P;'#]#yQ=lPJɜ #_56鰅]^e$cpWoADUQ&/xa 2LM5ٱ(t,fX<7ݼ'7gI [q-nJrzTnZLZ6r z˪ pnj>^m]ġz\-h̺̞[ ,a3ALqu`W['SvC)5sHA-4"PZ(pW䛃-cTǜ;:>V*Pf/ebyXWOf|ynXO=CFrXQmBeOօcT_)GJ%7M~ 3"tJT92oZ fh>j`!"P"!,P7YVHKȰÇ#\(ŋ aȱ# A&CٲR`Ȑ&c^$Ԗl+dOD TfI[[$Q*EJP"EfS+F )_%h@2`niGRI{[FiʔTِZ]4ɖVn/7׽dgkNcYqʕb?K?ziKJiKd)N=q=V5o9%К9J9n> e=rf^V\ir)dMUzqjү)VY;d$r|iĐz*҂t5xL6Ce-UfKuVIҊ-c~5܊1VS[XIYbTeN(a_Xo!gbR̵&YRcG^1IeP[tWc&S[&A3ŗR4CGFNIX`9De6Zd H)B[L)lrJ!*O9@RvWEnzk.B+P鈬Q}+eeڟM d@ne)P;O&EܮJ;nݟ|}mXU nWRS{a)aȕAR>P+ܹ;V{9ձ@c曢EW7TjM+ aLPRg_&"ťRA#+^ dHFG4zԤ)*ͽ2fU Պk6q-b" t=j٫ %z&`niy7o7+m 2~2G^eyE_ĭ=$+eE8U:敜m&Lu'׏wΰmRFXcQ40isB+E{'Ae%̭C_kvjMʙ!kgqgőT_E yaR%d+F<~lwK^RIOQVL`D>2"H}²0 gHJ@!,P9YTH!\ȰÇ#gޯ %jG"d+S i/"%A R@*:I4f+ I$]ڪhLP Z)QdnJɕ-, YrW.U6{` 8WC[" ̗.?fջWW  ޥjZxcp'e \oB\M ecŊZVmqfTkdi{a;pK DqݚD g?~Lzww ^svj/vZܼl+X| TL{Eq~iշII5QH@S5o;[l3[1SQIr85yB͸|vBSa%a]hsGm蕒A\VT/BQ\[b4JѴYY>XfKg ido_4J o'Chr$&8(D!n/5]y%e"DvRxUiC {jm:BIRBWAtV'9A)ylA~ZXy9-C"IBthMm]Zla,{4nBmZ0דy/*PgnïUzJVyiC0"97)#A0U!KGӜ5 s+a;lɝR<ңrV'ЪҶ\ 垯8vK4+dgkmo jwRK7ߥ{,"^j-m3K&`?1БpG>e\9Bb*ؒ19Axw&M{:A-*+E陇lg$nca_̀yZs;NQei?{֚ij8!nGUYB Yہ`d?Fa~$SS2D5axwz63 z $yt*"+Z& J΃ GHZ  J@!,X7QVH\ȰÇ ŋ3jȱ-{$ٱ$G{B0IL(Х@&Zdho Ini/ &=2 JdX fӋ$IVVX"eM $SV%6&xIpܣ#h㢉ŬeIqv[ߕ /2S]3C0mjl鴶6նd_ךax0X-iL -vː ;c2WeJje[Rhaz&}3Ƶyٖ^tUK4hS% vHği%V[^(&SG!ms[_NMUF=9f&wV.mSFnW$aCC.r mwE:dcRdFЎJdqXEcFmEU+ݑ`Dnd gNb=%j?ڲbg1yCvXQ 7C(^4&'ԟ$ZoW5]dQ(5 -iWtM*iꐇHq}NfL lDEQM[?%喗yR[ⵅfTQ&)̶KdcS= TRk2voD.Q%w╴veQXTkERQnřOf1slЎ7ʵ s2\d5Y";5<-&B 4~¼bJp50]&mPhF5.ai=IkV)jUWGdҖ1W#K[R g~a?K+)7h#"}K -r&dT؛WzM [ɸߪٽaɆvIpHÓ-t+u/5ajR$@VixE\Uwj;$Ot9[\,Ve,ҺG@{qtч̦a'8A' !,_7FVH 4/Ç#J%3jtpǍB&eV;[S0KIm2@bĨ\$G%H!p Şg ˫IIIU6M^z_J!%v,YvيK[[vԞ_w(B_&[l W\nRl%]*W-)(\'男V`pд{/u#/ $[ y`+Rd~9ٙdi,5M:a&di^\BwܖQʤ_d|Ecةr@B&dAϵD$s*&W] *ZD_ZLA O+4u*Dk%&\5RdF"4mBw4VcEn^Uh}Xޗ^ь5IyřnXZ5"=EY[މgO2}Ghe=| UY '[\ m!gӊ$IY:&[l TIJeZ`vKB[YӇvi[D f"6`mqYNbd˥!E ZDƢ5Z ǕMپ&nՁK1F^Jl~k #,ΙK m ߸W-t2e]j[VL&Y0\ݦRtVL!DobRtJUU+[U_y+z(PIʘ 4a]i^9B*.Sҕ)M(V-*֊4fS@v/;Τ6dqN DKG{pu?wUF~hŕɮnDJJ˜fUYu,I7~VjAc_IR=z6YI" g!,h7=VH*\ȰÇ#JTpEڵ"E A^X?Hd !C ,io_IIH"AJO @ 9L6aޖ$+l*A{j&Yb+`Jiu-ȴ򕴬HRRY%WRlYj6a%w馟yJI0_߈u3h TQXl(EI۩M"\؊j8PVն}lmp^Y6 o&[_d\I&W̉R6fud"N%&Dl’fm U'XX>-$D5ȣ[x$Dm1$#ғJZbwX.ܗPLdifA uߡƴqAͦ&"{.$XRXA1O/A:PT䣥i1R8YhMZHJ==!&Eg Tu`$*!YYYڦsoŚ,}UBJ+drXY-F w8#XXZS=%&=m팱\% QF.%Uߎ,[Gc 젆e*MfoZ3,[G=*GLpC-BytSDPczgې(]B]mJeUDdD@ -i"VevJRfIDZH95U1aA7$YaSsV\DԢY{RJI&PUv0%7b=@9;Yݏ2<Fg$W%~/eJr 2WOuJ@9jI6)(\YǕehS3e:i~0ڢ`k[h{jU䗏H!,h4AYH&<ȰÇ#JHŋ3j`Ǎ CtIڵjgkń&cj)Z6ٲeD.l#EB$F[ ԧ˜PnDժ[Z8iTD1Jj]=~_XR5-AKY(Vz$eٛ@UK0CW5~m}d,EPѤVF1sXPK +͑[l,@GҶXpeEۄIQYEaIﭭI:&rD:Ws"b@ X ,nZ.aImI@ښ[ E!ٮ+QaJ} L+PI v֑Hqǝ5 P!79 /=pn ʠC샤!,d4E\H\ȰÇ#JHŋ3j|`Ǎ Cdɑ( \yR!ir?01dIe΋;ykC )[?% ʲ՘$RHg҆70)H ìWnjXb M/RcF +ݺA+X[=[&WRSboǏڴ5ma g~HiжVY^-_-ojh ڲԱ\;8֭g7[*Y\c^}ZH%s-HIp즹 &KٻTWQV}V^ A gT-]p =xkJKmhZ*~L35o-TY9rś=[fWyEX"a}MM %^xWg`wg}R旐-0)HPG!YR+n"/Fbe@]MB苢ٸ YqXc %`z-_A%UyV^VW֙YWV. &m1!sd[mU@6lRȆYJE7WU f^L;LdK;C[UP+ѱ#KztX0@ޞuP+D&B)f[XƣC${%A=TM:@V۱biXғgO2kX]_IU JQۑd Nc.#@}xR[6kjR6p uEƀ%IڙԊpD:4թS,n ?eַK5mBMLJJ-ubZy.LRAa]q%CXFDE=kSne-.cI;om7֤tD &yqT|8뚹 Dhq/姏!,`;EUHKȰÇ  ("ċ3 \ǃBHȓ!K_)W@3gDN"ł"at%wk%$R" դKVH+UW.푑"[)82[aJ wŅvߒ6%{u [V ZIT2F{&Ҫ鎿):B@֦& w ܡTŃow+,jTŧ-6ɜF2o.t?l׊ԖMZ>^[Ɠ?WݿRHފ$bR=edE ROk fsVU[}d|-ewfrڹUn(Hs!T2:tMٲ%AanqXg&}AgO}w)ҡ_nfe2}WNjQ*]ꕔQ!]٠YIyl  M$X۞n6[VF(md)6YSLy Y\(\6o#Ke؊V'mժBrDFHhh9y_<&$Y wFExG*U UQ wҔI`W7tRYIŤ]xP {EAHf"w;vfTJۭi]Q)`ⵣPc%W+JƠRRyK W7˿Yz&`k X{nb*3x*)Fs#k1km[Hr$k;Wbv ņE]c&F@BFٮT)O-IjAiN]wH0 eOh7?S.ؐSİwyݼcZyEU%k$4o0^Q@!,!,\KJBH*\(Ç#.5C3fDžBHC(KĈʗ [I̙5sodΜCΝ>DQK9]JTTlj՚6I2vׯ$E6$i/RqJ2L]W&;K1XO˃ qE ۊbѢ5_X[6הCZEC!SO~N=;o+{B7֖Iޮר"N )n19d⏽A k[|EW&cg/YW/VF&]]ca[lR"@pI1_Ab@R?:uJw -Z;cKP7@wQcK+"fMP%1t=a}B}iI[pUK[ceRB^V@C:'LŨzJjm^I$Ѣw f֕sz`gj&đ-jdih$DMzY\! JrtJ  F$Xd'@v%kuH&JFP~S*ua"]ҧ.6K]\I+D)PXChKm]cQ!,\PM=H*\(Ç#JHŋ3ZǏ CIG&KLɒ࿗0[ISG4i_0tسcNbxTҥW ٪)[/A(K6$ $t+&()mm E,]iVd%+c%DKV}wr" %e+9kWd)?3tܺ[Z#THbU;ϣ/{3s?Cj &u2-RN/{d&Vh lwMEk agVV[&@y/ZFdzvQ @HXTյɀ] Ab E;"SzA@b rxn%*!CGta)kD&صȒtҎ-ܥE&ddBۃי@xmqbq|m!d+7@ lRoezEY i(AV|NdO_ΙdTMFARpwmׅ[PK56zBeَe!A{\?ن|z]Fug$)d&Hku(Z]-NVl}hZh#7`G畋Tdi{㝈ZrBR]lyXU z)Ue/-/&_~fb^K&c+5*I7N adDdުaK5\Oŧ`5!UwM@+YYjBf߈'}!,`pH@Pਁ&D12P!,\[M2H*\(Ç#JHŅ/jx0#ǏI#y%Ɣ0],̛jWN#W\ӥΔERM{ڳe3*vdlTm"AJ-%fJ!_SEʬ߲em)lu˖ ߾6RmBPJ{Ef%XJ+IFyAg[d'HmvmQ&ՔΦE0²GȉH[<^pZO*r+R#~9w۠dƴW]=Q6_Ol!ŁBpQuSmuZ,A?Y&Ly4D"b(D*I1{%bES[nY؂{ MՊ ǘd5GХ6VfzVa^m^NdZxfցʕ#L?ןc QZPV& )dZTS@SE;d&dQdn¤җbdan57eY#$H+Jbhgeڡ_ Y{q`H&xؘ@gzaJK> m96r$Zmr6AidϨV=2&lHư[S*,vW@idhqIsVi/8[vHU`8-J@^CI0?dEj"ƠԠp -^u&uv)-TWmVkR@!,XhM%H*\(CJHË+jܨG8d$["CUȑ0 bu?HId'v16Ζ=푑"Ad|)p )[ 'Q'#I nZWJiUN L4Vi֦$s!ЃR6 hAp-=S@M@JVhkʷg+xTpZPOpl Vʉ35EJկԯz_9䁣F| 0$[Zݨ`XK4Y%DF:%WOmqJ"[\RU#cM`gAXA!Wau֊ J5O^EC7qƄN mI~~ ERNTx5U9¤\=Tb_ a[E'K]TS X#5&oIPrƝuSe~_Uy_d')Әt䠠-nNG?DטLR@]mSyUP~z9s yW=H_mH++^ti'PQ=WtFyP;]Vjc=$mlxSt7Bd G]SX=@+FQH(v+RTKfA 7ä!,w 8_!,XgK)(*\ȰCzHڲe0Ǐ$ER@\9^I$$)%˛' |)AJϟt\)!ItS[|&GX*,)e`IzjbV"$SׁT}x/ۑu`+{r6gV+u @ v V苔-75wB3niaDwG &2mdJL[l.5Mf߮R+$,%H.է]n-ݒY>*WR?2չGժ ^8~FF%cW&Iծ~My_lڲI `=Ֆ$&` !&*IxF[uӹC~+e:AS"P5]Uh!=E[ix?a{Mg%e2[pdTGA@+mQφs!LD$NEZW%W.%]SVUJD{A%>TA M=U\XkL> )84)/b ٲ)I~bK;SqV JYiNf;U$&^%&h*פ&jRXL`A>BftNPE~dAZ$:Qٲ(@dn TtBDnrX`+]vڕ]Wl4YUh \U楗g%@SzJ]efJ X%{j;yz#ARɥu~ŽԣM isym+pWй+'.o[^&1ܧB&^PԿ5ۇmq\.mep b&VE&wFqМ 61lumF&{~וmb k4gN\ylw1s@!,!,PGMFHP*\ȰÇ#J A{3jh )V~mI_$D )0G"EB$RH3b-7U܂Qmj޴iѧ Imd*M-B=ڪf8(rQ{B-jvT6k ݝ@eK7_wٳnlߣ_ŁUl/d":eZ"U廟ADN5<{-$nwgƮLJq&,l2}o觗hK*)l_OP+B+fLa`Dy6 C%v`C鷙 2Y+yD^ e P} //Jㅨu4D%XtDFQeUv(@Eq?^ԊZQ@; =@8m^Lפ ؊-Ybm\W:D0'w fP[ydYi1"]xdVU][)&_D&۝ jg|4PU!-'C;@ijF;]G@6Ry@ K~"$_FSa-Z՜ Vc+NՑR \zi1EY?I&A=;Rh[lQi䵈N׎VZvdkε{FKJGTRn0LK)O @b|Uɖ&^&O~U8 JJEd,5ĉ$i>??_`66(a0ĒmV5Sgԫw!\eQYbQUX%[ /[)4tamT6l%6*-y{U]YJJVR%dte֮{P@!,PDMHH jת-#JH"D{H(= CJ$'%hi5K ژDJ͚R^I&QdɳŘKܸ?P+d?ʕ ©`+tӮ]K&gПm=?7_[,=֯?:]ÿ.zdz<|9M7`3SR靛Fykp$ȷy$;"o_ƾI[J;B$GFÃPԂKlYH`ǟ^?Dk"uXg/Aŧ`[q%`DdNlR*h8')Vs΍D\H]M8vX&:X7)1bnI@ Eq-M vbOj)9#b Yja9a6 hT`yXmjzU#P''Xl9wA/r!$NĤlOJ{i)XS]e$i*U]AJ t$^:F"OGzɩK]nBZlʤ#}jN$bieS@J55:P^UQq6 .EGF9KI&0>ɚGP,?(V[Pɒ_2e]>e˝ϽM۰E0PFO$) I=QHU\Lmq!3F% e[d)%-L%I*9%iIgkQw&ٛk=^!PwNȭQDiػC!,P;GRH pa‡#JD-Rd6i5ǏmIDB-[~\ɒHĔ@Je˛Y )@GtseOv&=*̧3p,ERltIIHҤ ҞS Qlj(ph6l\bw$I2 Jl%$MKDJjK[ Ӯ@)l`+([S; g-tT:P3OL }P-IFR3ż%5&[^+Rd[zĶ=Y?ПwL}GSq,uom+]Ef[|vdc%AV%(vx×_wa6JgaXPEP(!G{b+NTbTQc"Ef7*Hh֣GW"ڐe!C$:%#"V^$Os Z;-A[Omʔ1hi@q吭lX]ڝ\5*JP`$LP H' kF&Fj|\'Р"%_Rj婸eyj[ֵ@m"ɕ$(w (PIR9J?:sZYZ$}S2HiD8*]8dCd wHZYQdY~IbJmdkxU+lb8Uq< XMR$_\rPR v lߔ؛L'!DtV&ҳ;M5]&ӨZy?*+N"3z[J\y5ta}:RKu`%&-K;3\fK$TVv' ςWsT.f֮x{)dH)2P4>`ջ~;Pln%R}%etdvMǛ\xF.AS5.K+o~ywEkAq *H zҩoWA2X#nIZ ^ZcI YcɆhR"fXzd..5HJCX&<Lky[xgDr5h.uN?¹W=FuV}+A_1'eaYX &Es^Pf+dTE"/ebZ= _DQZRjr~VvKq˂խ1 y[8`=vAN:\Y*jx4"HZq洎vݔ3i*dPvg>6Z S+GFtu?dQ;Q,iq/ئI[d?< eF_)Oai YImXa{Ѿ@#6;ifetch-tools-0.18.5/htdocs/start.jpg0000644000175000017500000000116313656401354016056 0ustar useruserJFIFKHExifMM*CC"  !  !"1 ?ptzJ)LUkr/SْQkqfvm˺/=i͵IN.bR3jhHf#a Q$aY!߆Eh򼯛j`lf UHmXGf 0Z,ʹ"&<_;ys̻7}uOռv+"$ W+%~rk&lQxz>.qsA~q9lp͌+fPyHȠmO=MimhvI0.^zlZjh)YEeeLn୚+ #mieJ\f%;jD琰drQos!'t Y;GCN=@s_<2}TI\/j>}IB?BJH.Z?nJH`UNypu\-5:[h~^MzU!; Ԭ駏m+-k\EWifetch-tools-0.18.5/htdocs/ccoffline.jpg0000644000175000017500000005350213656401354016655 0ustar useruserJFIF4ExifII*bt|(1 2iCreated with GIMP77GIMP 2.10.82019:10:09 21:58:35Created with GIMP6<JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(((((((((((((((((((((((((((((((((((*ŕp$pdz+/ڢRisn* 7ooy`w3U;G 9,87Oy?rPPQ]/<Cn"8ײ{\\\ 8P?Jcw?*3}801?k+x{NpU\ir##i֥X`)z~T*[i:Sk@<-?zyk ijd{9I;Q@?]G< xfrUyR?sQH'e9? #y {jKxQKk*٣Am+:}O[۷ C,jyIE%w c>%ZiDghQTbOj|iLϒƟ('[Fڼ>n5$C,qO{w%Ę!jPEPEPEPEPEPEPEP+ɬ.㹷}!5/I#]כӣdF*r@y^xsŰgJu{dԞW?Www$,q}8S$!"c)3¨>Տ\\+šD" ?΀Kqp f }1Q7#f&j8t1Ʊ* |YvYA"~P&tУ:_2m|_%([8v85xkqc^%;W7:ԏsi4I^@e 0km恬;o.F<}zP~yq}qM%,^G9,{ ,I$zI@Q@Q@Q@Q@Q@Q@Q@Q@Q@+O6ZL={yAf|?"luZףyt+ԍp+Zɦu@Gk&d5C?VdfѨԗv:$<="x.8mdH2)9q3`7CUSVy4G׎ԙB҃;taO{Y!;n!nnl\*GpÚ|D;(WVj1\f/ެ,jBry<ѩxGf)8[ Oנp[^~\qg+MʙjZ#gr}U s^EkR87F/^^wMRAC"kAnTy.RU^\G@Q@Q@Q@Q@Q@Q@Q@Q@Q@> XV$ Egz(((((((((( V6[^B)SpzW:6kip^H $?J꺏 x]2. q;d2 qPՔWxGKR+w/[=+=$_ђ<68gh<3(G:\HdWit2mo^ki[_!jNBAP}΀2>/]ˬ!]l:'DX7 x~z]}"&04{c^3h@"j-*d!fʧC/־4o GZN9Rjđ?ؠn⼿N% 9Xv^:t&+d$-ĒiڦaYMF; 8>Ҁ>8o:m Gk) Jߓ\MQEQEQEQEQEQEQEQEQEQNE"qJ^mO u-:Z0+KC2*w$09,+iG] z};NuM7" =n8ځv3zu]Ek 2׵]/HW_7O!2nJ bYOҒ S@mOG!O9kS [})t@I;WwGuԇ`LCEPEPEPEPEPEPEPEPZvZ\ .y WxLCWUQWbnǕEԶѓQa;(!r @cj|n4!\)[KμK/kZBj[llcO½[X j.U] >-GRԀy^foG<&6 PIq@?iӮ-I_ᶨb9{t>Ҽ#}Z,LhJ+h<]v>$H$pLߥzt?UVqJ7N'PXzR* '-ƙ-Ůl<UN9Rpju e]Aڷ_wE92[+sր-?W{ o7I1p[ wۿ|'U*}:Sf?Qҳ.h:(PhY^y7ee8`A@hZu]> $g t>5~jt`H`(a Iq4vO35,ivbN| \O5ī.\)Ҁ9SRfGf>^GҬAM> ei!2#YtkyDe(@oJ6Z1T2L"Kj6,b M}"A0([Кֵ 5-qT'<­YFVGVQ(-?3W[DY@{>m8L+!oCmr̄oFzz4QEAAEPEPEPEPEP Ѻt.GSW4RK~me'yD `GBIʓƝ~ub3`zŇ,6,gf@RpJf#s|d Ó <\<:@b0 b++H5Nm#\H*FxUvS{jrXZB軋M& ;SZG}ku9Vd08ی$z̺^s_%մB~gWE}[X#$c;n3 >YOnO,XgM</ ɫCr=HBgޛIur託VohGĄ O|u gVݳ64<0(­ ژGr?Zif>_p s=ktǦx5#aNH<~x_<] ~k2=0T/ՁO@OzEm`!cTb27O¼O47ߓq1 OP3ӭrzf%"Ew`ިPIoyh.#$V>٩񶤸ܖ=^+X;/ؾjqI4Etj:]ZF> x]mhL`zWStip0,X0FAǥ\eܗ׼_X,1iMzV!#\mEcv5v9<7v4EET((((((((),i^F Ĝel[j`hC+G]tϤmM7Vf-slo,;}+H&c_8wm㴻#x4o>%jVѨ8\lc^OZw}s}&K>+EQESG#U8@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@C     C   Created with GIMP  "}qcr"}qx}pz=`^ds,!'E|COpϣPjOhÃSJҍ? R]Ez+#tuQ 6)StnĵY#Z9莖A&a9:h),]S貧)e?M9}P`4Dk9+| 7$x{[-R~I1Jۂ6fƟ1ї[#7̬ << &̔c $&2<֛Xb%Nh( Ў*|FА sd3Hɷ&fOA:'i^HjIɶ+pz7`<2=eVGJ `&2x9-&x/~sAf>t9 4>Iъ:6b+}jo|Ή6RR1Wi8S`RY<`JOް"ҰyO6#iWk6MnHPp٠u)ϚWkN'r߹r`9~ɷ-SIl*<y)r:4ƕ^w鿪ŧS\kYLmEKr;3 6 n"7JXi:9Աn6|vy*)Ҕ5fٓCTY܏&f t՟TN/ @05467!1$Pp_N+lTVŏ[ci<m2S(#.9=bL՛e|6 0f"!a9B@OMK6ͻ(_ s2 `Wd I2&#+1R?>ƫcpZԮ17ط׋2Fjg&VdiU/ ]¥-fmZDQۛ!ǚnۥuIJ=CJ4j0o PT^'3v5[Fwi:jd=MbG&К{J-ι_L/ ʚJFa2?eltmM5=[;eՃ,)5 &oV%xYAAmJ_]YNuf˟"9 g):wY)ŎQ⟬Qyت{4E,0{V_]-kx`#)b+Kτ =KdW R8dUɇ`c. ~==X} IJ+\48t#Hee0,JH$2㖡pY/cG3a{'[cA<# mP%=`"mH5T0ԾUgё6Śb";7";gUaUډlwv˕76+:=V#MNd%0g+ Ul% 7Q;~^!#Cf/%ߘ65 Faΰ|*pvLBB!c8ex{k ?z*0mI cs6 1;[ QX6\gPcDZ$ _C,,WLZz[Ab_sB;³(gZUfh$xռ|_]4׃^݄WC0{EvsL߁߹>u?6.qWl Etɋ7z$m SXU@4ĞI4l,bxS (VArG !)gܪHYkn@( kXljC9WLȊ>eCmIEs-OҌ8@P)~~i')-W~\Dk48}]ޕ|%ֆZ6pHBg3YDrG5&7oHҴՀL5^#]`-BVeY_Q؃{CUrc.g``Sl>j5'PO qB?ELxW[[MTKUE߶Qi_&cB\l$lH9]G1ŭ;t"TeEeT+1]'fCV0'mugxR_rq6s"QN8q(zNQS*!0@"1Aa#BQRp?; E2cT2B Jvʻ^_sLQ+aXPe9A6Cf֯2 ⤴2(GRaLuia6sO kvSZ ,[oT:] '5+q',[k'c:[Ks5 6*;xuMcx4GRT6 #d\l*?-U+S0؛PŊ6*lymp) I6Cz[jj7D(c6<~P0ldalMOPńOt[Q2ۅM>ZN[8vZ|{ÌgHsuQ`9!әmkRɌ A:/HgMOaNGkMb1F\tWW q?_8c%n>'RiJu";MIO%/cVZ7ҎH"Y 6W,̇{cf֨fUjg~[ެ%"i]P~ GTEZ4voάI%6ӟ}qBN :ۮ:ԯ:֊#FϾ:rSjƝoQK[wr 3a=:2 S,+ѱC^ltWQHܛq%Yԇ렎J]u!Ԏ'”qNV^7FS͇ f/ϳC|32Y>i,8O.K_"}Odyk}ڲ\GHM((+/WJS R$s%]˜!YHo`>EI6P1%9]7zh^+P^}LfZ\ 5纅Tfe-$[b02eB{(R-*+Ҙ%LHxeWr(r:E-Dž,ח;VjKIuT٦[MrURVwzBjb+G)l f4FO3%Bf79O[:x44Jb8ڣi=Aud<Ϭ}'H77&3Ɨ*C+ʣɈ`: Y=ꤡ<ssZQ\V#+Go)ESi)F[Y\v# G#[{6FYvkC^k7%BOB+Sy~'iMR`'v7ZNJ%Tj/"xj\eܪ:w&.?.Gh/`Zy%Ձ/WS@JHZMqy#)꺣RQQo`o᰽Eojf6L~㽵|g}ZtA7?dbB[e`lxH3g7W]K"B}fuPՙG*jǐxμW\[6 F+lE΢-ޗnmCY*T.TlS-Oaz SDw[攅'7L՝̎-9ف?3Vꨏä`G2?Fyj$ci:~Jȱop-ᱽEsj36IC,QX#a.FB]7y÷mVw}ueq.RKm+p&ٶJr:uG%yb?'(^mʭúžBei%H3;I9MF m Ϛ㟕JompuN}-)PSE2=RJ+QJڭ- 3s 9O\HiHiZwy :&5!%: uQ쓕3u4TSg@o#R$[6jruZSZO}tWCR XPL&So  fx-!BuI/"̞7*r^F\t۽+V'#&/M15^4u7utCJҕKmsR^F&L\J*ܻIs#GNk㗎Nunxڵ'NbPe}PW~m o*̜;Ni+SeǦ5N'9njWV@ ].ЇRwI5 * i-(}HnsQJrӨꏯ~evӷV-SvlݭR5|yfKoszs~Y*:W-Եuht[rJ a$)e3|T@ Yu!9xvmQ,6cqOQ AÖo*,RĤsZOx5o1W7u+4fv;Hm_gJ9.jnj%,B܎ϐ5ueFJTot#H2,悽QPgŦ-/!+^w)'2;e&F;8Dᢕ[J›=F{HP,=[Esߘu[DwKK>JKy{)8SxJʮAx{׸܂YK_ [KW5e8k6dҌ7X:Q`5x~7+gbHA ?ܚmPi%#(}]%djRni )17E2$ -$Er-ߪERcB疔Җ~6  ӎBwbn<-HFU+!D2ߎVdZUщ J3\!rs>PbK-`d2KS=bSzK2bV:U[qWo<+mS\-}7%Rc%-2 |k8!f4-Y4Ѱʿ i r)듬U-/1<0@2yVXz'nHdJ%~2( ٩ )"Ej1 !o S38say9)YO|*U&˔Js~\N+.BLG*+M1ϫI`G#L?@ WM^-#|\>3;/ 4 #7ɩiPG% B Srqiѱ~^p vyd}dqM /lO9:U]G)2'.;DYA 5Tnrv &.,fV !gf6g$wi?ϫI`G#L?0r.s Q;ɼl: , e#(a[JvJRԣd\ nFO|QW+huDU-F6B;v.MiJa-ܓIآȕAqNʅ!Jlފ U$"dנʍbª>gnjYPot~qlFm?a|@..&iO)D|G~Y,3)-leE>HDIL|Wދ$ gGhxENӂ EH$gl? a|7RiWq5؀,rp)`f`bI(ќ Ȑ[X,Zu2q v CJk%>7l: cx脑0N.pe$) u$g7g.7EU`t\0X;[@̈́RHUՆ?F\IٓHjv)^"Lo ]ͱrp QL&aash.UۖP[#6M#R9wZmEȱPοcSSDr# D^XFz~C؇)EwQvT"r1( "u2F!VȀ O3PKftB0Z(X&$yړǮzHDI"*UEɃ"ӈE&q6ˑ\ OF{b0 2FJ`[L$JݙBTBOI`Vtz]rj8?!aF=-J&|"1!&@qmʂCq>=k'2^)AxLELƐ8C.SyrlxMY'M>q8GhQ{笺9\y?ƒ)$ RaP&%BH1M;N"RgQciN*<{mO{g9 ϙ<8?9-/B{^2j LZj?z! b Ǎ2/I,9MI?(߄A>XBeT?5Cby 30iʍ,=sSbgg [yɴ'9C0]R;)`.Zρh> Q V@a숰ǸFt-9N`"g1ğptCFTw+rI%"`| F <( Hx vG!n*VAGY=!G˃$5F33M₨Ț9j]pq[tQ5c!!B_8g,_;_!f\WJs2b/CbqҾlq`?ŋc&)G= HY9F72ӦEydOj!Y>y X< zì9ydNMdl-jC%(swlMV$"lȻKᥟ_a(rJc 8EQ(l"$ pê(7sҸh PZ5T\1]''(W|f?NI?3ǬnH DzAOUI#).MKݾQ54{f((lW~kĸLBuS!0}d ۡy0IMƬ9yYB)ҺHs7qt"ad'ӹ?~oǯCpNda  yAb s5uYBGN8< Dx!H( I$I$I$I$I$I$ $H$IIHI$H$H$I$@@$I$ @@ $I AI$I I$I$I$& I$I$#4I$I$I$"$I$I$7"@$I$I$$I$I$ $I$I$I$I$AII$I$II$I$I$AI$I$H$I$I @$I$ H@"nA$A$41A $I$^j ,I$I$S$Hw $ II$I$I$ $ $I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$'1!0AQ@aqp? X13*M= )1WS{UJ-<Ɂxy^֡ 88^ǼJkʴV/}#VQu;)+~p!#Z`ٲ檾ټ-hZZ6Kl]%C~ 7oz_!!01@AQap?`z|( :yۤKRJ|i1CbP1n9{ʈG v3=gDw0&+./Qc #*!1AQaq0@ Pp?9dR@aXd/PtZgmy֍o0"~!b;}H;o9dR@aXd/PtZgmy֍o0"~!b;z%*3A 1]pjh*;d$X)EFq%MXIS}Xu,Z5|3NdJlқ!őM!N~e^l8 ztQd ĺfa"!`<w[pf\ 97%@Gx?F6>='frCqd3WU0U~㩲o`jt_mO8!^|zjypR꺛mCAa@q(KH/#%GAl=l Ma3~]C\ATʹ1šD)ѝ\`s|g#'b\ڤ+Z)NѼu2ЍOC> 7CifQB&zb;7$Wc-;<Wf2 P kB sK@2ĖPQb9F4'LS.^s (#srG4"2xq qN!ր|#g!*t:CtHid<'44~b'I]/?.35>W􈦃7ȾdLUǙw}LWSb% :hvtoo gr%#k引 )rַ0[/k'&Y?$^"``G 0M@AQr{J .ʴ#ص&E4HF#LB9dU&zS1ŀ[TZݼ s@F^c O*6AS{H-Bֆ=X]lź!x2 629[%S Ζ聴aבZx ϻnM,@,G}?g,I{9R&VDoX}3dM/^+(A@WjQ$'zH8_/xG< X}h 0=Bi W z0(@_*,\L鋪[QRʽ30hFf+d[V]=k0x y8H+ "KX e)@cuH`~Z-Rk_= ElTRV0-R"b\A\ u4V9fEjnt X؈˳rT(#PHc&$Ny  k@@:Nێ Bs*T^"!Qtx@W4XK "y(MER+ޓ2y Y^U%b *4Z,f;`{Q6-Ma9RGX#R!~W ,a ӎ}bP4ܸkQr:>S“w57^|'OAJQ&$[O2 TEeҽ)>T 4!,F]7B\WC\l+SX&#c{0ٸh鄈&J1]˹*p#7cGf%_kl7G1~@Mb( ;Z7qr-0 M2 ^҄ a!Z P&ZCY0_fϐ7~ '+Ci^,ʭ\EzHGdvJx rfpVzŽx(%ʻlIWV٥'PBBjܕ|hߧhq\4ؠYѶ6$i!bk$lQX"{`gSlg1v6k8LAOUj ?rۣXa$Ǒ&9Sv4z 5+<{ZA(C(;@,>1qZC&TlĂ|6付F;:::C12tNt@zEBr#{CBĠt8ÙچƊ:'~lCU]ٵ-c ٦']mz+CUr$j۽p $8DRtQRpThPFGJ-Pl@/ifetch-tools-0.18.5/htdocs/missed.jpg0000644000175000017500000003264013656401354016211 0ustar useruserJFIFHHCreated with GIMPC  !"$"$C" V !1"AQ#2aq BR$3%47CvUWbr58Ggs#!1"AQq ?t((((Yj7ln^Mylw%J؁k)JBAQ5VzZYtL}!6Ҕ 8ZRT HۂI^:W4!&c@XJ NRZ'^zolxy0-cNu} :VqNTLeA* :JZ#jxr2y sXj8ݫY+IΏ^Ӯ &KlpVJ+_4ďDFm5&{vLVTw:iBxP$ ޡjX+/F,!J H ’q|OU.T AYqK>˅4iP*AV4>i&QwuFR!I*^Ԕ@ܲ8 řO g nu&"(JB ImDD !)FJqAd_σt,ʉ%ۨPV 5RRRRRRR9\oԷxם4ĜOKHr;ʂTLoY3Jyp!Hʔ$S:񨶴D7ey:O4Dt/n|ĀBmMΩ%K*YjK(Huⵡ;=>> LTvZw8dzF7(Kaj$N!ZuԚTަ繑pbJ($^-Ys 7%c;+c*ǜܕR7͸OY-.6ȍA 8n<3R QiFv@lyUB\b%nmCRNBB gb~^ , A߭5\l !^%r@Jp # } /OxV Pux?VO]'" ]08vihg*<+8 stlOvݰ ܬgOΣaOg558o^mOۛq>JQh&sX狀tz{f=luͩ p_ԍ:\A(VczD筐~-ќe ?i<k3KZi4nUkWYS)I;|p01 u$eƔBb^(Sm(No'H»>#"UKtRcDnJ8- P>7Ztg48Fj1¶rSQi+%8*ܢ ZA+e~TGPiJ L,:Jp lG_^HjCE]tVi( b"M_I_Å$#҃Բo)ֽE;?n{b>rJJӵ{x w F_N<~[w 9;FӹiBK[t@ӢI.,r X^Y܀5{S kpmؒlMuAFI >I'ߩT7Ç뮘}3ԧf.^]yN˄gw*}E'v7 k@flt7ԈI*I =f3U_[N:ͺ6DOTH @uzu}M706d:AI$nYH:kzz suԇF%qhAvd(pF[[V5/ ^x]_%6=9neDa![ZJۀVHJ@4J:&m7Q] uƇg|sGwyخ֍u`m2KyeU$G^$j/r{\4.(.-xY *A' ̝A]/!^yZ>ŋLۭ1CmŎG{}^rq^|՞ 剷h^ !@|'cw_Ή%&@7 lh$Fv=]1Ѻr#"!@3gސ>ڊ~CBï;t8wo]vY֞!=Lt~k}t-5b6*I<1y ǰ5o/BMn~)z8{5>:kyOzo_Iz7qHP[I!]n[.ZN-8Ru+>Vo6axp]2%~ʳ޲M}xuɑ`t$)jwHO]51;p8M{q?\?*vDo+R56o}x8C#^p >.EWr\T7H~"JqA#k14{ˀ2Ҥ3 d~}XX-3i/r6*q VO8R~[3H}2sP9/o>dFUΕ(s@> ulֵtM]q/j j8ސq-m>ꂫ(EJ$O&/Vņ?>s R;J>I?uaϤkmH#ZS-|>& ..Ӓq2v}! IPCV1ք ^X];aqd$$G²>tc \#9S[ԟ1R$2?* :V<Pt3._{Ko:R ۜZ V4IRI>#:fJ:7L!ۖ%$=v:@PXP܏5dG.%A;RӻvG Pmʕұ] _,p/v"3rIq!HVIOzER <+ڧTrH~::,id8(+C J[Tܓ3YjV3ϹDv'J %$W>\N%R产} uj9RԣʔI$&Ьʺ_>mڒTiZPZHP)y\ mZ7HZ*2#)x+*QeJ'޽Rj0sNܮzZ§ JP%jNUEl#%AȎV\إvH H \s(: 䈒~&"{]< %n{T#]tJPHMHK7Ft\8WMcFTr*rV ?J@*W}4LN=Lvw_)m`gpey8''֑Z3^?*osg{vv{px KA'{M&kmN?! ʹkb910$cjAF Ϸ5$%)zLiXzxi߹P=G88o'<<3k{fqE+G#)K}N_ |,6 j" RnqfZtt7K{me{'= 7f˽%&4(+qv̓Q3RatQ¾;.bӑ\1|TkR^Z2wS:^u]Ԣs~}jPS10igX`J A8cL͡tH_>ڴ5MvmZ[$rm8I@H)Y08\Qhx*\IcPk}GpLmH);S{Z4\6h)s#YP{ׄxI=*u:r!_@[\)*I JxpU#3*Mտa/Ac7y4r- g48Qǀ|9ᾞk)m(._$G2֐Zچd=5mSUcZP>CVwkǗ?wYsJRJRJRPJE]EԺr 9w AqHi!%D 3*ogˍS lWT\Vq>9;!?H 7qg&jo$,  u@{ y27m5o߯?KKZ?\?^KC^JwYi +)$zq qEA\ʠ~\u^LG:pִH`!e=XՎa$ PMYuF_9ܕ'[Q =NfcYm^izW%єl'#* z֜n$g-q9I~Cޤ_nƠh[{V{XYݺI yyh~jqPq_9rRC,2Z$ OUoYAJ{e9J#&jxC?nyi b<mS\\ HnDY iչ+Ikq q mhJҡ8##SaԷM0\7XJ0Hc4+s\)JP)JPk7srBb?;?1 .T޺맦IzKYJpA !J86~\mK!uSirr9o8_*R ׎\+乌yLD [|ƃd}c.'ӞZߤLw%Yfj"3.mvy<kz{fvMQ':^) 6gnF+N+ϷnTWv%^[)  >uk \#7*+1a@p@?:)J)J >¢μ]-LʼɊA3 ֯qGR*)ss ۬B[P!jRJF=h=ډX@mBR`}kXT5@z贐v*CzP V:eVS:KtvS &&'r FߺAV-gOtjEs;7d~ 4jkA@P9RWJtcϽxJ+i-XH$ Oʻ ?u;&\o',%Q1 AIO"t/J%<8VQʖTI$I5tF{6=o{q%ɇw)uN'_hgKgLyHqɽ~rS6P04K;)JB~6qnqJAItwtV&lgtfeDҙ} m(aHRO I 6ļkOF(YɠٕpEs^6q.1X RR(J67]!iBL⛞Q'n2I Rʁ씐y'O":G&iV>znAg$- n Glڡ82q8+[9ZO%DyEA J†1቎埔ϥݭ)i|UwO#gLRiLh7_P(q)Qu*9 zO#rn:1G)Sj JApMs&%x;*p},DҝyBR2MkQԭRRQD c?(~H.];$*gHtvBH3UqIV/IZv.b# /r'st+Am.5jR8 !ޮp?[ڠX- Ii`>: )@H8N5V)v?NiFڟCD-̟a~uV`V;m$>i8^ƚR,GIq"x.%Hf,H͗yՄ!2TI5+ĆRa[>8Q ?{]H<)!N$;w%}9m͈Tʂ‚6C$ )H<^^Ե-v^%-] KnHRI>}KM,8)$jMdu)mI I )jMϧkRRNW𧍙=@vL^=}'Euw]ki^{ʱe\XmRJKi( $ aA`ԞVn.R’/7(% (z]e>%r”RFHBSpY,XsK[>%Zޔ'* @4.Ҷ9ǜj&!$V3}PoZQ@Q7!@IHVG2JRJR.%-L6n^RXc)RT?$Fkqw\ }@Fr,>cN$iH#)$d~U~4߉}ET%8*i V_/]W{}4T+_cn8-}LyױRR аJ) )#pR#חknTgR%m݉Y!CjRvRABǬ:s]Ẋ-r.B[N¤')'FA#u,6d6BH-N 6 m$8WVh{Z'VRp3Y[lwfѶ Bqr#%,e*QJBʁvw{cPl[ 8#$3^vzr4xz}Ge9jKLUL=㭅);ATkmQ?B_gFӒE.ŖUUā/O44v<ߗNqUpu?pꝐ֢}_{g툟oݦcӬTI$y$שT(vj? $ט҄$j8J@'WIYZm{tQ\ xA6 ?>=Oے-#|p0D5 qo{Q[Vġ>ZHn{x醡N,l9=-6~3i*qvwTaݐN=6I9w\68ݵӫtwSY[n|)@A'q٨U֑J_ݝ;j`AC Fsڼ{a^Ͳ&- 2@W$uRFsyӵ@}'ˍ36T%m)I@JTQKg5 Ef&g:X~[μY(rBTܥ8PۅML^Pz⍖=1%LSIBӁjq\i)!dӟǤ]fu՜Jp'r8ZJPRv݌ԙJP)JP)JP*ghSNδ[:6ˡ()$nJ%*^fPi|IP'H:3euL͔8p)'AEJ~zv]RJ\+h 6焺28 %Da*E:߯SkKSRd9Fh-[W"0%nSؗ OI 0=[ۭ͑p専VR=9zɤ(LԺj`O֡' ~Y PZrBCvΛ-lQR(\)qTH3LFGQKrJNCh^%*oѧ>>Ӧ-.%sWG*agkd|;_uUfqP/CJ.g}Սr|ҒZ7EFT $h0ه am!)B@ ͇duƗ ahu!IPj{Lyl%ϴj*B1޾ #Ty53:AB4xc6V\JՔ$c t;t;ͥ Mi ؒ ²SH 4NPqZIJ^OPW>ڠioč:EjdVB;^TU_[jN=+raZ$3TO:tjǑ.lsǷz !,oҖm<Qj哧V8܌bPo!c;pj꾠5Sk.Qm.<}cʾfO F-< DGa Cm)ijRH`cA8#+7:\یՠ|T?\GpIYOw+⭫y}9RRjw"'sK a+IP?U]NS4.T+1Ԕ\ur`(j3[%`-9ڣ(?=2ѷM-NYLN%)JAR!G+i,6h{~ DG yd*$[nC)ZT8RH 8 ~iPA*b}u[<.nD <S"%ㅳS~U-Ќ@sW~>%KG1;`+i>gF[O@}C0yi! 8e Ƃ&Z6Ʀ2 ҨVL̅哑RF+^f6 5f㡔?>Z#$$)dAҫnK#+oKEe6щ%xRGP9H}t?Sƿ~Ҭzma'%N bD(-/nka霟/"CIZU2SZ;·(H5UuCn/\ndHt{) WRDz:#c9[*vnҚFj,D|-HRTkoҽmW[ֻݲ|سK9ACr8Pdwiҽ15V՝Kz|N߈:sݹۻbnV3'WS6xt<WJs !*솋cNKRx#֌NB4Vވ^NR qI$07ۭ6ˑ:l4y֐NA n#331{UGmenGJvvvq"z h7 )եW!#$%tjㄤOXO?g]zo~6Gi=3 10?%zFw8 ʲF0& Ib:;Չgڵ.ZB`-i ByJ  PdګĿZu 3c;޷DͶ;Q߸!P9)G ''1eu]^snsʘuͩ I@'k'.×!Zsv.l)F־Ha'3$<֘`m'9bϨF0% *g]).lΔ ifetch-tools HOME



Archive | Live | Monitor
####################################################################################
# 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-2021 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.
####################################################################################

This page is custom coded at each location that ifetch-tools is installed.

If you need $$ support or consulting on ifetch-tools you are welcome to email the author at unixabg@gmail.com , which may elect to provide support and consulting for a fee. On a paid solicitation please include a subject line of **pay-support-ifetch-tools** to ensure that your email will not be filtered.

Updated on 20170911 by RJ Ent of FYEOX

ifetch-tools-0.18.5/htdocs/red.jpg0000644000175000017500000000067713656401354015504 0ustar useruserJFIFLHExifMM*C  !"$"$C""!"a1Q ! ?2 "*kK)>ǟHۜS]2KJ 'S7#Lm8\lw -/pXBM *R>>;'G^Wifetch-tools-0.18.5/htdocs/log.jpg0000644000175000017500000000077013656401354015505 0ustar useruserJFIFLHExifMM*C  !"$"$C"#!"1Br1Qa! ?`U&!GɢXa:cA#ҧfljxXu1GVHƒkJ1(^[⡓'Z% ߭~4|=>Mk؀m%!SĽnň!R6v]@-cYB®~戱6q\ꏟ)Ң(QW{v* H<|~>j!E=ˢ.{++y3.[lyx[l cXv7B/=u]OXy8 Vّ\M5)m`V,jF|EL/M@\/=鵹̵kuBԲ4qIlak5_ P+th\ͧ !bEt+O}H$kb'ifetch-tools-0.18.5/htdocs/green.jpg0000644000175000017500000000070413656401354016021 0ustar useruserJFIFLHExifMM*C  !"$"$C"$!1a!A ?[ynFrpd1-$s;VnUSUZ :l |dcUy?n[ehZ̅ *ǹ&Z5ĈC$c'ZfnDuifetch-tools-0.18.5/htdocs/grey.jpg0000644000175000017500000000063613656401354015673 0ustar useruserJFIFLHExifMM*C  !"$"$C"$A!1aq!A ?9-)IoVX4|ZUQks>UZ$@pCQc{+x\~^TUZjtӀW}xV|JpQN`Kkph?ifetch-tools-0.18.5/htdocs/ifetch.png0000644000175000017500000005051513656401354016174 0ustar useruserPNG  IHDRZ=sRGBbKGD pHYs  tIME 2DJtEXtCommentCreated with GIMPW IDATxipy( A .Hv`#۲eI=4vcvݝqرG^ynuubI A}_uW~@d "qT7+)v)*c~u?Օߴ1cBι1֬)M^Tnܸ_m{EKQL?;deYW&BP.+~: fyC`, W\o'fff&''0 B;,;!B4Bgsss+*Eф:@c4 v0;˗իWp8l_5 d2w粮cXQWUU)Fi.77ISS|>߶ {GG:::ٹ~GuB7F !wђ?Xw+++4 tx9PVa 郼!BhBR?n;B;McdEX~1ƈFCCfJiMӠ=zرcF-33\2kch===tww~Ѳ,eXRmۦB=dggӧO={Vv{[!6הRUH)mJ^ً:!DWUd]]݁ZU4BIw4M!&ضd2y_>|g>3g"*(RJ5߽QJQPP@h~~>[YY)X[[ (to0N !~#33svlXw 9De޶޲,L&1==wKaJ|BUVV6UVV_`K*8m[XB<ٍ{.ѣ78v9|3 n1kѺRJկ7 >66fі{-m=|8q~>|t{fDRcǎ#G= ٧?_u\<eW^aB>ON>TWWׯömX3.+J$X/{F5Eqy<ӡP?y׏2܊C ιvrD"˻<44!:uhjjB~9dz}s dz6(,B"=ʚ{BضN|_Eoo/f>??O ccc6%0KL%riPJ_R唪B</FvvJ~~QRR8v횸~:,+~)y_֭[uyDs%E+++YYYeRr++..VgE(w%ηV6ǁi0MX ϟ'(++SO8A7sN ygg'YYYʖ߭1ڲ 7o"R~Z\\Lsssz`ll `?0JKKIII <Zzjp!o֭[WߩT /vfܶs7oBگBDBB QU4x]ѣr̒-VWQm=4ea``Ͳ2"۸uI}?J]~_֭[8w\[YY&J^VVFEI'kB :ׯ_wfgg!`,]@vc~:ZZZ  ܏>>>1LLL H 77X]]r C~ ׯ_OÇ VZZ>h 4166fMOOz^X97Ӣ2<n]P8{,$Ec xߏL`uu<p]j;طv[n}_~WՊ CQQ3664Bԝp\x;o=BPPP1# n II|˲:8ccc|ԝ23??˲>UYYv7A++hkkǎ5#9t!={nj(jxC//  ͅ Z΍7 2226L&/ ! ېYo=VqSι;8mەڪ>.Ӏwvvvӛom"V{d(ʷށZ wx/5W^IEϟ?_w }z%%%X__SSS$ȑ#paqdjqM%XsǶm0 H'8[4rTU},[p\zV, ݘa~0uЏAus~"UD.^Eo7Zkmt!Ǫ*?~7o$dX]]ɧ%˶mg||ܲmVUUBطoN8oiiÇ555.%^_җ z tbo k2k*=vMpV uo"D.#{N$K"7NC++;Oi ~w}wW9rwx0>>.8LN¼xN$fѪszLĄ=>>X]]d-b1WCCCi}}=MIߩ:h<(> Ԃl}TU`.!yc2PBP~Dr/_F(BQQCgz~D<' BuBx^^33381KI8狎=c˕_SSӧOk%%%TQ=9iQ!sb^dǐx|MUXJVN$ct2XP2Yn@W\aBu244D"\Zs Dqqqw4f0RXX|ȑ7kjj_yRkt^aOϜ@H(xrd"P2ctjJvǏ@x{*//1@7o$|rrr%;;{ezzom9?KПD`0zˬUnJ=2p,߂0aN"^ԭ@X6e$Ɛ \4P TS`NÜ^߉P.@NNdrx<> !߹6]jjj̈l=C,X˰oz\uTW7{e1$oLnXfzyDWA}nPMM,Hֆ7|nouuuu8v9qDԔX^^\J?MיYݨ!5^niZ9 jx]]8@c C}i3;Ff}JП^mPSÇ>|XZ{{;:;;qGw\!-ǝqÄ5 @-ٰ^7׵j  3 ~jkkÙ3gVr|?*}U4(v[kkk}555l2ikSK|:H p²8M* dw]zzkeYpVff&4Ms&A*!D^w~j;:@)m̻!. Lv,46~&# 8QۆÚ]deʮL[Uzb6d=H v7PY}}=}g- q`,Z9Fr cbzE܇@=OޘZ!{ppJ$\.-S^mdi%Yq@(0-y8eØE>7&`E pp#>8h σ3>Z.… |۶1<Z9Th\0ZI7n^ʲ2A @ n,m9wAmmLh8ܺu+_Qi%OtEQNVVVձ(S؉p}HL¿7%*d7(q;ϟǛoD"/| b1 ɨ%cɓY ei_oQ @y1/A+Fr6#Gք6m|'XX,Hos}x\曲Nt0<qiCNPj_j!zQXH$+SrxԩS M8mc{)**Baa!}\xot;JA:gaҚ׽1 :<կk_b/ r۶kuIwߕOџyIсGxw7SJ.HP r; nXxzyípŚ/\X,=:_M }(eYbp.^1BPQQ ,fa\0*،vo y-O} ׮]CFFrssO~ԧ>܃x<ћ7o (E1pEQl0qbxΝ;w}gΜA󯽌, Eâ<+իWV Ol6ʿK]NU-OGv헗l5 IDATv9s#vvvnO͑tds !4BBH%OvC)eǎcGogZwOi8z$i6MV ',.p O{?Â{JF$Se] ;Ң?qEQ1$Ot۶? \B8;&u-[AT얳!.q_X%GQ 폶mG{{{!{:/Boo/9wu.QT#!vV{l%˲> GQ JNNs.;^͋SJۅW$MӴ~}ι{(B(@,AMӴoܸQ'gXn$O}ОS%LӔn`$+++U$O􌌌K__+@qO"`r~.Ahyy9H$}ڻSK/뵵5kMotB"b)@ wZJ.^vP*+i"c}%Ke80zaYL&H6O*!$sjaa!YYU޲m?񏝟gGGGWssnnN}ȝZZZZܲ,z>@,͇iRjnXEe_}_i.LN\bQ~~>-(( ~rMӄimmH$dvB,AWZ D]XX(/c1Gi,BII ___ׯ]av")[I߬]XX !yyyJAAJd2qķX,vݲ+Ң\21H$;scMQ>abX]]Eoo(JвBKo;<Ҽ:"]{I&Bb||QU{iF\ܱiymb13++bcc㡮.[Feu6@#x|-YGZnIⅅ₂ZXXH Qp8<\QQ7;;wm'@!LӼ{H%φltwwZ>`paaaRBGҫi؏?!^y[gg:6$u6xclnuu5{qqPQevv_ ޟ{)DUUҜunnxvvq /JП J-B,,,,(7n r3g 0%EB|KǑ/JП!7-bavv6{aa!_x<466]Vk"J)rrrz```=uªܾ*.q4ͯ8_CP2R]]tENBQi'N"Ң?[RUP(.G)y+^{5x^8C όښ<#Av BNO744?{VRRBSj{{eYcA9 3󗋊m3gθ꘮wL]8bvv`zzz$B%ҀYRR~ mu]Glp V}vv,//7,,,;3 ~oC"`_;!1ܜw1r|[FSJOBdPN Rruu+ nOE![֑e+}vR__O],O >)rJU՗*Oty8ڋSNl 挱N5662UU!F>Zr `xHХ5FԔqEEEl{t4O՚UUMMM\/))!~T:yfff&_UU, --QUUr^4iwhC}}=gǑ=$SR ksZT@1&!"uti̗zi`eKH;(TA^滛SeYٳg)]}l=8NX":d1 iK,))OEQd2)[d:v=ehTZt PkktsޚJR;c ~JEaXL6tVVVɓ'0Lwg)4hTU%DB6O_eѮ+2'tB)1sRI6.4-9w֐s&9,m'&&&se }߹ ??8HQ-%]w s$۶a!ò,iѥEtefz pq~7lz( ҂,;8SN5v]zU&HNn \VA #no\0J&8DU5XOqp9x<6P ࡣ $7o\[J`BpNLsddĒ?gxiGE4W?##'TtTU݄Ll(edy aa]f"rPN>,=mnIC.DZ?J Ѩ?2{5rM/(*QJ6݂;p|/c7vrrx?E"oG&'~?c 0\?6ȷZ?/ !JEQضϚǢpYEir3AU?mYf ~1:51ꚚXm'4U4m8gTYi~˲`òۉ0RT\1t} Eq B4S=`m uk.(;'O?c}_szqI&w?Zǭ+ AaQ93X2* UU=X䛮^сW^SMӶ 544h>߯\gLy(.nn+p3 3$JӢ F\b( C/,vbE[~@@u\9Խ !6I/yyyv΢r-T\}PPTR!ijWL4UU m?|57 ¢ϗA< nq\ -趝LDkk-vϱj)n>W!(( Y~C 3"UUS5wNF*4T\F Ce^Ƕ3eY`LQb1ղtv?X^^/km`Ybiy}S4**GALx4M dgggǢLLB|mgUECaQ/9Sײ≄?]FaYד?q\| 84eIJ)EcbX/յ}A7Y͂a۶#m y*e˿ }!F C9z-!kGkk -7ۓH$0<<躾gp>e:::O~:\.Dz5Dd"4]Iv9B0H~a/"˦}kzj͏LM-B (EQW86(IV]5R\rfg&Jpn۶f&TӴ,7LD$傢(i"Lb}}sss)+*}vcnSSsU鱘++Ƀ;n1EAaep{Y ! x=W^yExVVq#՘PH"^א,.*8eB)%ı93 +N8J˫K)4]G]PX6-10(y&n޼@ Yy7i{9PpΑL&ݍ~ ayyXYYA,m PTT7x8z(<|>ߞVI/Fiyn<5Us9D]Ḵm(EѶaiJm tݟ3322m92r8_/G#՘qW] *Sp81QN6 T\]wm ΩPBF K4<FGGQ[[oсo~F,{4ѱѹ^g>gb޾9UTjWoǶ1911pe %6Q9.]GOOWD"s!L~ na2TCdSwtGm/SJ2ĵy$ޟ@oJMomcJB=ٶHП!M,$Ͱp.|>x~Psr˂X8Et"ɝm) r S X> ]qjmm sa}}_/b||x,A+@<L٩0[\\__;|y{K[s GQI5o6SSc0 L < H0,?2\d{@bE1;7 ?\' i,HT+gHo>%, nWfx3qupykTy-r#Ƶkdƅ%I\v ?v~D"DʚHEԲLxE h Z ZmH021O(*)GUXC%ϐ&1- k q vF|^8 n8tڿ1MՑL`ffp[з>.in,'vx!`'V `ˏ¢rl.&rY6~e"E^0P**v2c J*,R͕JП$-{r!2MM.]]38Ξ=Z,V 1?7 .tuuKSo]u`0|:=6++ (**B xh{:ilp4 C[Ҍw> (#9_fFi,#f lj ik!(% *yyyԟ1[1||[tZp1ckID ^U?.a Di PTR9owDDװd"۷o#77YYY[RjDڒ:tpa4=9?MӐBTUUm&x;Χw!i()Ѻ&'{m s30wI!HXY[úebL"`$e JKn;NM#Y8666+AߟR(mu ee_.[! lL%-2eXV Z9zE5}3cθ]@@@`q~i"H`jj `nv]i4MC]]aYlۆNO p!|sٳgQXX̀ܽ[{{;z-öm5Ǒc`Ap!KwEEʖS 1uZ BJʡGv}!8)NCae+=X\\Cض&|>\K'|+X[[VWWFa6ǁmucCaa!~w%%%D جgn{Mm҉1CֶrWϧA\J4xgc@ nC30@ƒ(.ocvzQθ\.۷;ejJQqrmĶ;j:|c9iw±mpȪݨ*2elˈp<8 Dž۷oܹsp\رcGu[ZaOOD099 `oc<}{؎qc21$"F*+jXݭ}ZlU#J&3{<=_s?<:&Zb|(R9w9{Rf( VRIDATS?EQ( 4Mxڥ###xgQV,˕]N]9i$QjԢ4MfTuR)teTIdW*Udj Ʀ&ژeȲ lzxn}ܒ2ɒw~?pEa&ָ[GS SG2+\3J5n(eYZ7CԦnUUIZmfK/,"xȲI]seqI !"r) T%ʊSTjNvfUEY(:wI]|҇҇(2 5iq};OݛEYVnK4x,jlgjRjBr /^Q|>7 eYx`Ae}(ou$O&\.˗/КzEQw4v7!麞P@nBw\ut:yPNc':.NS[ɚbin.^msu=L&nw??*9|)zaf5'!8t:K|0׬T Xn;B+N$ pF˰,Kjll6(7}󡡡rѣGxnK3>~hffɃַUU,?Tntuuaqqz[ QHaLMM:t'Nnݵ>t(5 X:36r^!&ci{"].Y,Pm-F߱:;;y&,G2٬Xȁs8]pp1M4WaQ ˢ0<ąwWJfsScP,ncjjSyd2luuUX0t4JxZc6U?GсW^y=_?8??gX7U;|>J慠^^N=^OLG٩ȤSSiUu~m`R f/K{} >Zּ +QRWm6[X,*ʿU*])*Iiy0Jү⋙C`}wjWo5iwuzr h;w`غmttccc&9s'u;WUr}MQ*B#mxDܹt{cEETRl6cnǾ-} (5[3אL&]޽}hni!KKr2whcnGLp9˲|0i&c!]/q.\0w* >.ov:J!0  tJ)fxl6q5O#v,bct^4;9џvoZ#KfH-3gx~n"$ R&k!N#vI$RzMyRkn-0y(eUUeZ[[<,]]]r4ݸi{@ ikk#ߩSމ$p8r%#IᡡiB!4͍_74 ^=}"# ŋR #k_S/>@eBoo/ B!:u _S\bt^}F$8΍}>i4 ^?x(T*_s=r_N | ڹݞ188xwޙ{ %B499ӧOo';|>!p8vg?L#<}a``. eJ}㗿\4}-X4t`P BwTjw0逸'm.FeDh#GڿRT*LLL'N^T*n %DՂagMӌR E!@JZ9UUy54 s^KAsrP:w;w\)_({snV#pޯ(OWե#G׷mnzzzz577g1 B|j&2fff.NMMP(M)-7ArSS=MӴ+1^Be={{zzynnNTU5PUաi3zގ;u}maa!S;"*@nwwwwkR b*~ao8փM(cqw\XX8uע(J}vvڻwo4sR)\|"Ѕm'TBf}}}iJ&|>oz<+Lʵ֫~ /.|NtX\\r+#~aZT*W_                                    Gd?SIENDB`