ifetch-tools-0.18.5/ 0000755 0001750 0001750 00000000000 14115452242 012723 5 ustar user user ifetch-tools-0.18.5/examples/ 0000755 0001750 0001750 00000000000 14115200051 014526 5 ustar user user ifetch-tools-0.18.5/examples/ifetch-tools.conf 0000644 0001750 0001750 00000040467 14115200051 020010 0 ustar user user #####################################################################################
# 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/ 0000755 0001750 0001750 00000000000 14113266077 016163 5 ustar user user ifetch-tools-0.18.5/examples/cameras/camera_number.conf 0000644 0001750 0001750 00000025465 14113266077 021646 0 ustar user user #####################################################################################
# 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.txt 0000644 0001750 0001750 00000003402 14113266077 014427 0 ustar user user ####################################################################################
# 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/wwwifetch 0000755 0001750 0001750 00000123636 14115452242 014673 0 ustar user user #!/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 = %{
")
# 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+''
else
myTempRespond = myTempRespond+'
'
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+''
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 = '
![]() (Archive) Live Monitor |
#{theDate}
#{pageToBuild}ifetch-tools version: #{VER}
![]() Cam #{cameraName} #{image_alt} Archive Live Monitor |
ifetch-tools
ifetch-tools version: #{VER}
![]() Monitor View |
|
#{_DATE} #{_UPTIME} |
Camera | PID - (Memory) VIRT RES SHR | Actions | Status | Logs | Information |
ifetch-tools version: #{VER}
} ifetch-tools-0.18.5/templates/Live 0000644 0001750 0001750 00000002540 13656401354 015553 0 ustar user user # 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. %{
![]() Archive (Live) Monitor |
![]() |
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: ![]() ![]() ![]() |
Cameras donated/tested for the project will be graded in to three categories as follows: Excellent, Average, Other.
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. |
|
Excellent | APM-J011-WS | Works out of the box. Camera web interface can now be utilized with Linux+Firefox so upgrading rating to Excellent. | |
Excellent | 2100, 210, 206, 207, 221 | Easy to work with out of the box, excellent frame rates, and tech support is very good. | |
Excellent | D-Link DCS-930L | Easy to work with out of the box and inexpensive. | |
Average | HNC320W | Works out of the box, inexpensive and sturdy. I only have a HNC320W wireless unit to test. | |
Other | IP-250E | Low fps on jpeg pull. About 1 frame every 2 or 3 seconds. | |
Average | 510 | Works out of the box, inexpensive and sturdy. | |
Average | IPC-E3600 | Works out of the box, inexpensive and sturdy. | |
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. | |
Excellent | BL-C210A | Works out of the box, inexpensive and sturdy. | |
Average | RLC-410 | Works out of the box. However sometimes has issue, inexpensive and sturdy. | |
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. |
|
Average | IK-WR01A Orite IC301 | Works out of the box, tested for a user. | |
Other | NC220 | Works, but, depends on M$ for some feature and password is bas64 encoded for snapshot pull. | |
Excellent | IP Camera | Works out of the box, inexpensive and sturdy. |
Current release - 0.18.2
ifetch-tools information being tracked in Debian:Also see FYEOX.COM for other ifetch-tools related information.
Last updated on 20200216
ifetch-tools-0.18.5/ifetch-tools-html-page/red.jpg 0000644 0001750 0001750 00000000677 13656401354 020474 0 ustar user user JFIF L H Exif MM * C !"$"$ C " " !"a1Q ! ? 2"*kK)>ǟHۜS]2KJ ' S7#Lm8\lw -/pXBM *R>>;'G^W ifetch-tools-0.18.5/ifetch-tools-html-page/green.jpg 0000644 0001750 0001750 00000000704 13656401354 021011 0 ustar user user JFIF L H Exif MM * C !"$"$ C " $ !1a !A ? [ynFrpd1-$s;VnUSUZ:l |dcUy?n[ehZ̅*ǹ&Z5ĈC$c'ZfnDu ifetch-tools-0.18.5/ifetch-tools-html-page/grey.jpg 0000644 0001750 0001750 00000000636 13656401354 020663 0 ustar user user JFIF L H Exif MM * 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.png 0000644 0001750 0001750 00000050515 13656401354 021164 0 ustar user user PNG IHDR Z= sRGB bKGD pHYs tIME2DJ tEXtComment Created with GIMPW IDATxipy( A . Hv`#۲eI=4vcvݝqرG^ynuubI A}_uW~@d "qT7+