#!/bin/bash set -e # # Checksum for Linux v0.7.5b License: GPLv2+ # # Welcome to Checksum for Linux - drop-dead simple hashing from your desktop! # # This is the BASH script part of Checksum for Linux, a simple checksum-like hash creation # & verification package, aiming to recreate the basic point-and-[right-]click hashing # functionality of the original checksum, except in the KDE Desktop environment*. # # Essentially, it's a handy right-click+GUI front-end for the ever-robust on-board *nix # hashing tools, with a good few shiny knobs on; Features.. # # Hash or verify files and folders, even entire volumes, with a click.. # # Select "Checksum" from any folder's services menu, and Checksum springs into action, # creating MD5 or SHA-* hashes for all files in the folder, and all the folders inside, # and all the folders inside that, and so on, aka. "recursively", through the entire # directory tree. # # Select "Verfiy this folder", from your service menu, and Checksum searches the # *entire* directory tree for .md5, .sha*, and .hash files, and verifies them *all*. # # Full Desktop GUI integration. # # The terminal is not required. Create and verify hashes directly from your desktop, # using Dolphin/Konqueror/etc. service menus. For output, Checksum supports both # kdialog (KDE) and zenity (Gnome) dialogs and notifications. Or you can use the shell. # # Configurable File Masks.. # # Checksum only music files, or whatever; with both single and multiple file masks. # # MD5/SHA1/SHA2 hashing algorithms supported. # # Okay, and SHA-384 and SHA-512, because it was so trivial to tag on. # # Intellignet append. # # Added a few files to a folder? No problem. With intelligent append, you can simply # checksum again, and *only* the new hashes get added. It's all automatic! # # Multihashing_(TM), with generic .hash file handling.. # # As well as full .md5 and .sha1 support, of course! # # So if you upgrade algorithms later, or wish to include multiple hashing algorithms # within a single hash file; no problem; checksum can verify them all. As well as # inferring security benefits, this enables the use of a cute, generic ".hash" # extension for *all* your checksums! (the real reason) One file fits all, baby! # # Checksum also makes it trivial to create such a file, with intelligent hash append; # auto-matically skipping any files already hashed with your current algorithm. Neat. # # Read-only Fall-back # # Checksum can automatically switch to a designated fall-back folder in the event # of encountering read-only conditions in the checksumming location. # # Shell use (mostly) supported. # # You want these groovy features in your shell, go for it! # # # Usage: # # In the shell.. # # To create hashes: # # checksum # # To verify hashes: # # verify # # Of course, the whole point of this, is to have the power of these commands on your # desktop, in the form of some handy services. The accompanying .desktop files enable # you to do exactly this. These simply launch checksum with different switches, instructing # Chec ksum to post GUI dialogs, notifications and such in your desktop environment. # # There is a choice of kdialog (KDE) and zenity (Gnome) GUI reporting, and checksum will # use whatever you specify on the command-line, or with symlinks. You can also force zenity # dialogson KDE, if you prefer. zenity's dialogs and notifications may be superior, but you # lose the ability to "Do not show this message again". FYI; The preferences for those # "kdialogs" are in ~/.kde/share/config/checksum. # # Because both systems have advantages (kdialog also has a handy find command, for searching # the text dialogs. Pretty neat. But zenity text boxes scroll the output automatically, for # example), there is also a "hybrid" setting, where you can use the best bits from each program. # # # Command-Line Switches.. # # You can override Checksum's behaviour with the use of command line arguments # aka. "switches". These override *everything*, so, for example, if you run Checksum # via the "kchecksum-sha1" symlink, and add the "--md5" switch, you get MD5 hashes. # These also enable you to use checksum without *any* symlinks, if you prefer that. # # Available switches.. # # --verify force verification mode, regardless of how checksum was called. # --kde use kdialogs (handy, when not using a k* symlink) # --zenity | --zen force zenity dialogs # --hybrid use hybrid kdialog + zenity GUI system (best of both) # --xl extensionless hashing (when not default - why oh why!) # --append set append to true when it is not (again, why?) # --noappend set append to false # --algo set hashext="algo", override generic .hash file creation. # --md5 default algorithm = MD5 # --sha | --sha1 default algorithm = SHA-1 # --sha2 default algorithm = SHA-256 # --sha3 default algorithm = SHA-384 # --sha5 default algorithm = SHA-512 # --mask=file.mask use regular file globbing patters. # e.g.. kchecksum --mask=*.avi /Archive/Movies # Multiple masks are okay, in the form.. --mask=*.mp3,*.ogg,*.flac # # NOTE: Switches are applied in the above order, later switches overriding earlier switches. # # You can also run kchecksum, kverify, etc. (the GUI versions) from the terminal; # handy if you need to debug/troubleshoot some [k|g]checksum/[k|g]verify operation; # the normal output will appear in your terminal. Also see Logging, below. # # # Checksum for Linux also comes with a handy Windows Hash File Converter script you can # use to convert any hash files made with windows tools. It does nothing more clever # than switch the linebreaks from DOS to UNIX, but hey, you *can* convert an entire # volume of hash files with one click! See: convert-winhashes for more details. # # If you have any questions, mail me, or better yet, post them on the Checksum page.. # # http://corz.org/linux/software/checksum/ # # Have fun! # # ;o) # Cor < linux at corz d.t org > # # # NOTE: Other than my own intense desktop usage, this has had *very* little testing; # so all comments, bug reports, etc., will be gratefully received. # # ALSO: Apologies for all the tabs and spaces in the kdialog titles, but I cannot # live with " - kDialog" slapped on there. If this interferes with any desktop # automations or somesuch you have running; edit them out. And let me know! # # * # It should work fine with other Linux desktops, too, but the GUI & activation methods # would need to be tweaked some. Gnome (zenity) dialogs are already coded in, though I # currently have no Gnome desktop to figure out Nautilus' service menus. Real soon, now! # # ** # File systems have "directories", but on a desktop they are called "folders". ;o) ## NOTE{S}: # ## KDE & Kdialog # # The funky dbus stuff, this requires KDE 4 to function. # ## Logging.. # # When you use checksum from inside your desktop, a logfile is created in # /tmp/checksum.log, which contains all the script output (stuff that you might see # if you used checksum from the shell). This file is (usually) deleted at boot time, # and can be handy meantime. For example, if you added debug echo statements into this # script, they could be viewed there. Or if nothing happened, that file may tell you why. # # However, if you prefer, you can disable this logging altogether with a single # alteration to the checksum .desktop files - full details inside "checksum.desktop". # ## ## ## SHA-* support.. # # Checksum's default operation is to create and verify MD5 hashes. Unless you have # specific security needs, MD5 is perfect for file verification purposes, and is # also faster, in use, than any of the SHA-* family of algorithms. # # Having said that, Checksum fully supports both SHA-1 and SHA-256 hashing algorithms.*** # There is a preference (below) where you can set your preferred default. # # Also, if you use a symlink with a name ending 'sha1', checksum will magically switch # to use sha1sum for all its operations. Use "sha2" to get SHA-256; 'kchecksum-sha1', # 'kchecksum-sha2', etc. Same story in a shell; 'checksum-sha1', etc. You can also use # the command-line switches described above. Or a combination of both. Your call. # # Checksum comes with a pre-made selection of .desktop files facilitating easy hashing # with the SHA-* hashing algorithms, which most people probably won't need. # # NOTE: If you set your default algorithm to SHA-something, you can override the other # way, too, by using a symlink ending in "md5", or by use of the '--md5' switch, # # During *verification*, checksum automatically switches algorithms, depending on # the current hash file extension, md5 => MD5; sha1 => SHA-1; sha2 => SHA-256; etc., # so you don't have to set anything special to verify them. One command verifies ALL! # # If you are using a generic extension, e.g. ".hash", checksum will interrogate the # file on a line-by-line basis, interrogating the individual hashes, and using # whatever tool is most approprite for each individual hash. # # FYI; if you ask checksum to verify a file of a *completely* unknown extension, it # will be checked with md5sum, just in case it really is an old checksum file. It # will *not* be treated as a generic .hash file. This is by design. # # *** Checksum also supports SHA-384 ('sha3') and SHA-512 ('sha5') hashing. # But let's be serious - this is file verification! # ## ## ## :BEGIN PREFS # # Default Hashing Algorithm.. [string] [default: algorithm='md5'] # # Choose from 'md5' (for MD5), 'sha1' (SHA-1), or 'sha2' (SHA-256). # algorithm='md5' # # NOTE: you can override this default algorithm at any time by accessing checksum # via a symlink of the appropriate name; e.g. 'kchecksum-sha1' for SHA-1 hashing. # See the notes above, for more details. There's also 'sha3' and 'sha5', for nutters! # Hashfile extension. [string {file extension}] [default: hashext='hash'] # # Your preferred hash extension (for creating hashes).. # files of this extension will also be treated as checksum files during verification. # hashext='hash' # # Checksum fully supports MultiHashing, and will happily verify a mix of MD5 and SHA # hashes, even mixed up in the same file, hence the ability to use a single, generic # .hash extension for *all* your hashes, as well as some other goodies. # # If you use your /own/ generic extension, you will probably want to also add it to your # system filetype associations, so it automatically runs with kverify %f, or whatever. # # (at the time of writing, checksum doesn't setup .hash files, either; this is planned) # # There is also the special value, 'algo', which will use the currrent algorithm as the # hash extension; e.g. an MD5 checksum file might be called, 'foobar.md5'. While # clutterish and less flexible, if you tend to hash very large numbers of small files; # note; this setting has performance benefits during verification, because there is no # need to interrogate the hash file line-by-line, or switch hashing tools: the # extension sets the algorithm. You can also set this at runtime, with switches, so it # may still be a good idea to leave the generic extension, and specify --algo as needed. # # ** IMPORTANT ** # # DO NOT set this to 'md5', or some other algorithm - use 'algo', and set your default # algorithm, above, instead. # # Append hashes to existing hash files? [bool {0|1}] [default: append=1][true] # # Checksum can automatically append any hashes you create, onto existing hash files. # As well as making it trivial to add new files to an existing folder hash, this enables # you to easily make MultiHashes, adding SHA-1 hashes into an existing MD5 hash file, or # some other combination of actions. The possibilities are vast. You could easily create # a hash file that was *ordered*; movies, music, archives, everything else, for example. # # Otherwise, set this to 0, and Checksum will ask you if you wish to overwrite existing # hash files as and when it discovers them. Boring! # append=1 # Extensionless Hashes? [bool {0|1}] [default: extensionless=1][true] # # When hashing individual files, should checksum create 'extensionless' hash files? # e.g. MyMovie.avi >> 'MyMovie.md5', NOT 'MyMovie.avi.md5' . # extensionless=1 # # As well as being rather elegant, it's rather efficient; a hash for 'foo.avi' can # be stored alongside hashes for 'foo.mpg', 'foo.html', & 'foo.nfo', etc., in one # file. During verification, it looks just like a regular folder checksum. wekl!! # Force Zenity? [bool {0|1}] [default: force_zenity=0][false] # # If you prefer to use zenity's notifications and dialogs, even when kdialog is available # on KDE, set this to 1.. NOTE: You can also force zenity by using any of the g* symlinks. # force_zenity=0 # # Don't forget to install zenity! (sudo aptitude install zenity) # Hybrid GUI System # # This basically uses kdialog where it might be useful to have the "do not show this again" # option, and zenity for everything else. The advantage of this is three-fold; firstly, you # get the "Do not show this again" option for dialogs where such things might matter. Secondly, # you get superior notifications which stay put throughout the length of the entire operation, # and thirdly, you get a neat icon in your system tray, as opposed to the rather distressing # kNotifications! Zenity also scrolls its text output to the end (where the errors are), which # is nice. This was trivial to implement, and trivial for you to customize, if required. See below. # hybrid=1 # # Obviously, for this to work as expected, you need both kdialog and zenity installed. # Ignore certain file types [array] # [default: ignore_extensions=('desktop' 'ini' 'lnk' 'directory')] # # When creating hashes, checksum will skip files with the following extensions.. # DO Leave a space between each quoted entry. No dots required (unless the extension has one) # ignore_extensions=('desktop' 'ini' 'lnk' 'directory' 'nfo' 'm3u' 'pls' 'url' 'sfv') # # NOTE: The 'directory' entry is for '.directory' files, NOT directories! # NOTE: Checksum will also automatically skip any hash files. # Fall-Back location [folder path] [default: fallback_dir="~/Hashes"] # # If you ask checksum to create hashes of some read-only file/directory/volume, it # obviously cannot store the hashes in the usual location, and so will use a fall-back. # fallback_dir="$HOME/Hashes" # # You can use "$HOME" variable to refer to your actual home folder. # NOTE: You must use double-quotes to expand $VARIABLES. ("$var" not '$var') # Temp file [file path] [default: tmpfile='/tmp/checksum.tmp'] # # Temporary file for folder verification (usually inside /tmp/). Output from multiple # checksum files is collected here and posted at the finish. This gets wiped on every run. # tmpfile='/tmp/checksum.tmp' # # NOTE: The command-line inside any .desktop files used to launch Checksum may also log. # By default, a semi-permanent (deleted@boot) log will be created in /tmp/checksum.log # Error Log [file path] [default: errlog='/tmp/checksum-errors.log'] # # Another temporary file, wiped at run-time. Any verification errors are collected # here and its output is posted at the foot of your verification results. # errlog='/tmp/checksum-errors.log' # Keep the error log? [bool {0|1}] [default: append=0][false] # # Set this to 1, and the error log (if there is one) will be time-stamped and copied over # to the working directory at the end of the verification procedure. In actual fact, you # get a log of the entire operation + the error log joined together. # keep_errlog=0 # Dialog Maximum Height # # If you have an especially large monitor, you may wish to hack a higher value here.. # dh_max=600 # Show Times. # # If you are testing or something, you might want to enable this. Total time is reported. # NOTE: this time is sent to stdout, so you will need to either examine checksum.log # (or whatever it's called in your .desktop files), or else run things from the shell. show_times=1 # ## :END PREFS # Note to experienced BASH coders.. # # I'm an amateur bash coder, but I'd like to be better. # All suggestions for improvements to Checksum will be warmly received. # init.. mode= mask= folder=0 filebad=0 notifypid=0 existing=() # simple flag set if we use fall-back fb=0 # this changes, depending somewhat on the dialog's content.. diag_height=600 # this may change, depending on the symlink used.. me="${0##*/}" # use last parameter (we may add --switches, later).. path="${!#}" # that changes, so we'll make a copy, now.. opath="$path" # I do hope we won't need this.. here="$(pwd)" # setup.. rm -rf "$errlog" rm -rf "$tmpfile" # for testing.. start_time=$(date +%s) # A few functions.. # # SwitchAlgo {string} # Switch hashing algorithm by file extension.. # # This sets the file extension and actual binary used to compute the hash(es).. # # If you wanted to hack the default algorithm used for *completely* unknown files # (aka. fallback algorithm), this is where you could do it. Currently it's md5.. # Of course, it's better if you have the correct extensions on your hash files. # And best of all to use a generic extension, e.g. "hash". ;o) # # Fall-back only comes into effect if you specifically ask checksum to verify a # single, non-hash-extensioned file, e.g. "kverify foo.bar", where 'bar' is unknown. # # This function gets used a *lot*. # SwitchAlgo() { case "$1" in sha1) algo='sha1' tool='sha1sum' hashlen=40 ;; sha2) algo='sha2' tool='sha256sum' hashlen=64 ;; sha3) algo='sha3' tool='sha384sum' hashlen=96 ;; sha5) algo='sha5' tool='sha512sum' hashlen=128 ;; *) algo='md5' tool='md5sum' hashlen=32 ;; esac } # CheckMultiHash {file path} # # This function accepts the local path to a checksum file, # interrogates it on a line-by-line basis, and checks each # recorded hash against a freshly computed hash, reporting # success or failure, depending. # CheckMultiHash() { cat "$1" | while read line; do xhash=${line%% *} file=${line#*\*} # if it's not hash-looking, don't bother.. if [[ $xhash =~ ^[[:xdigit:]]+$ ]]; then if [[ ! -f "$file" ]]; then echo "$file: $algo: MISSING" echo "$1: $algo: MISSING: $file" >> "$errlog" continue fi # calculate a nice new hash,, case ${#xhash} in 32) SwitchAlgo 'md5' nnh=$($tool "$file") nnh=${nnh:0:32} ;; 40) SwitchAlgo 'sha1' nnh=$($tool "$file") nnh=${nnh:0:40} ;; 64) SwitchAlgo 'sha2' nnh=$($tool "$file") nnh=${nnh:0:64} ;; 96) SwitchAlgo 'sha3' nnh=$($tool "$file") nnh=${nnh:0:96} ;; 128) SwitchAlgo 'sha5' nnh=$($tool "$file") nnh=${nnh:0:128} ;; *) # some other hex digit! continue ;; esac if [[ "$nnh" = "$xhash" ]]; then echo "$file: $algo: OK" else echo "$file: $algo: FAILED" echo "$1: $algo: FAILED: $file" >> "$errlog" fi fi done | tee -a "$tmpfile" } # read the number of lines in an output file, and *roughly* # set the dialog height to match. ish. Did I mention this isn't precise? SetDialogHeight() { fl=$(wc -l < "$1" ) diag_height=$((100+(fl*=30))) # magic! if [[ $diag_height -gt $dh_max ]] || [[ $diag_height -lt 50 ]]; then diag_height=$dh_max; fi } # add a checksum file's hashes into the existing() array grabhashes() { while read; do hash=${REPLY%% *} [[ ${#hash} -eq $hashlen ]] && existing+=("${REPLY#*\*}") done < "$1" } # pop-up notification message, usually.. notify_user() { case $dlg_mode in 0) echo echo "$2 hashes in \"$1\" .." echo "** To abort, press Ctrl-C **" echo ;; 1 | 3) zenity --notification --text="checksum: $3 in progress (click to hide)" \ --window-icon="$HOME/.local/share/icons/checksum.png" & notifypid="$!" ;; 2) kdialog --title "Checksum: $3 in progress.." --passivepopup \ "(click here to hide this notification)" 5 ;; esac } log_out() { if [[ -s "$errlog" ]]; then echo "" >> "$tmpfile" echo "THERE WERE ERRORS!" >> "$tmpfile" echo "" >> "$tmpfile" cat "$errlog" >> "$tmpfile" else echo "" >> "$tmpfile" echo "All checksums 100% AOK!" >> "$tmpfile" fi } TestDirWrite() { tname="$1.$(date +%s%N).test" echo "write***test" > "$tname" wtest=$(cat "$tname") rm -rf "$tname" if [[ $wtest = "write***test" ]]; then rodir=0 else rodir=1 fi return 0 } > /dev/null 2>&1 || true # Setup user preferences.. # # Dialog mode.. case "$me" in k*) # kchecksum, kverify, etc. # we will use kdialog.. dlg_mode=2 ;; g*) # gchecksum, gverify, etc. # we will use zenity.. dlg_mode=1 ;; *) # checksum, verify, etc. # no dialogs. output to stdout.. dlg_mode=0 ;; esac ## hybrid mode => dlg_mode=3 ## We simply add an " | 3" wherever we want to apply SuperFandangoHybridGUIElements. ## If you'd prefer *your* hybrid system to be different, simply switch those around. # runtime [symlink]overrides.. # case "$me" in # the first condition is for users who have sha* hashing set as the default, but wish # to override by the use of an MD5-specific symlink, e.g. "kchecksum-md5 movie.avi" *md5) algorithm="md5" ;; *sha1) algorithm="sha1" ;; *sha2) algorithm="sha2" ;; *sha3) algorithm="sha3" ;; *sha5) algorithm="sha5" ;; esac # grab any command-line switches.. # while [[ $# -gt 0 ]]; do case "$1" in --kde) dlg_mode=2 ;; --zenity | --zen) force_zenity=1 ;; --hybrid) hybrid=1 ;; --xl) extensionless=1 ;; --append) append=1 ;; --noappend) append=0 ;; --algo) hashext="algo" ;; --md5) algorithm="md5" ;; --sha | --sha1) algorithm="sha1" ;; --sha2) algorithm="sha2" ;; --sha3) algorithm="sha3" ;; --sha5) algorithm="sha5" ;; --mask*) mask="$1" ;; esac shift done ## NOW we can set the user's default Hashing Algorithm.... SwitchAlgo "$algorithm" # During folder/tree verification, we shall consider these types to be hash files.. # it's down here so that "average" users don't mess with it. # hash_types=([0]="md5" [1]="sha1" [2]="sha2" [3]="sha3" [4]="sha5") # # NOTE: if the number of entries in this list gets alterered, you must also alter the # find command which uses it, below - simply search this script for "{hash_types[0]}". # just in case they can't read.. for i in "${hash_types[@]}"; do if [[ $i = "$hashext" ]]; then # No! I will not set it to $algo! Yet.. hashext="algo" break fi done # special extension setting: # match checksum file extension to algorithm used (alternative to generic extension).. [[ "$hashext" = "algo" ]] && hashext="$algo" # Ignore types.. # An array of file types (by extension) to skip when creating hash files. # we simply concatenate the hash types array with the user's ignore types array.. ignore_extensions=("$hashext" "${hash_types[@]}" "${ignore_extensions[@]}") ## GUI setup. # # basic checks for hybrid gui mode # (but not if called from the shell with no k|g* symlink).. if [[ $hybrid -eq 1 ]] && [[ $dlg_mode -ne 0 ]]; then dlg_mode=3; [[ ! -x /usr/bin/zenity ]] && dlg_mode=2; [[ ! -x /usr/bin/kdialog ]] && dlg_mode=1; fi # force zenity dialogs.. [[ $force_zenity -eq 1 ]] && [[ $dlg_mode -eq 2 ]] && dlg_mode=1 # kdialog is missing, fall-back to zenity.. [[ ! -x /usr/bin/kdialog ]] && [[ $dlg_mode -eq 2 ]] && dlg_mode=1 # zenity is missing, fall-back to kdialog. If kdialog is missing, fall-back to stdout.. if [[ ! -x /usr/bin/zenity ]] && [[ $dlg_mode -eq 1 ]]; then if [[ -x /usr/bin/kdialog ]]; then dlg_mode=2 else dlg_mode=0 echo "*** NOTE ***" echo "Checksum can use zentiy to display dialogs in your desktop. " echo "To install zenity (on a debian-based system, like Ubuntu) do:" echo echo " sudo apt-get install zenity" echo echo "For other systems, check your package manager's man page, or" echo "compile from source. See: http://freshmeat.net/projects/zenity" echo #exit 7 fi fi # are we creating or verifying? case "$me" in checksum* | kchecksum* | gchecksum*) mode="create" usg_str="" ;; verify* | kverify* | gverify*) mode="verify" usg_str="Hash " ;; esac [[ "$mode" = '' ]] && exit 2 # more user error!.. if [[ "$path" = "$0" ]] || [[ ! -a "$path" ]]; then msg='*** NO PATH WAS SPECIFIED! ***' gmsg='Something bad happened. Please reinstall checksum!' if [[ "$path" = "$0" ]]; then msg='*** NO SUCH PATH! ***' gmsg='Checksum got sent a non-existant path!!!' fi case $dlg_mode in 0) echo echo "$msg" echo echo "Usage: To $mode hashes.." echo echo $me' <'$usg_str'File|Directory>' echo ;; 1 | 3) zenity --error --title 'Error!!!' --text "$gmsg" \ --width=280 --timeout=20 --window-icon="$HOME/.local/share/icons/checksum.png" ;; 2) kdialog --error "$gmsg" -title 'Checksum Error! ' ;; esac exit 1 fi # still here? Okay, let's look at that path.. filename=${path##*/} fileext=${filename##*.} # was it a folder? [[ -d "$path" ]] && folder=1 # just in case.. [[ "$fileext" = "$filename" ]]&& fileext="" # create a hash of a hash file!?!.. if [[ -f "$path" ]] && [[ "$mode" = "create" ]]; then # if it looks like a duck.. #2do.. use the types array.. if (inarray(foo,bar)).. I wish! if [[ "$fileext" = "$algo" ]] || [[ "$fileext" = "$hashext" ]]; then hasherr='This is a checksum file. You can verify it.' case $dlg_mode in 0) echo "$hasherr" ;; 1 | 3) zenity --info --title "Hashes Of Hashes!" --text="$hasherr" --timeout=10 \ --window-icon="$HOME/.local/share/icons/checksum.png" ;; 2) # kDialog likes to post " - kDialog" on every window title *grrr* kdialog --msgbox "$hasherr" --title "Checksum WHAT!?! " ;; esac exit 11 fi fi ## Let's CREATE! # if [[ "$mode" = "create" ]]; then if [[ $folder -eq 1 ]]; then if [[ "${path%/*}" == "$path" ]]; then # "local" shell usage.. cd foo && checksum bar hashfile="$filename.$hashext" # switch the "original" path, for any fall-back hashing.. opath="$here/$path" else hashfile="$path/$filename.$hashext" fi cd "$path" else if [[ "${path%/*}" == "$path" ]]; then #2do.. read-only single hash file.. path="./" #opath="$here/$path" fi if [[ $extensionless -eq 1 ]]; then hashfile="${path%/*}/${filename%\.*}.$hashext" else hashfile="${path%/*}/$filename.$hashext" fi cd "${path%/*}" fi # no hash file, cool. # if [[ ! -f "$hashfile" ]]; then # but is the dir writeable?.. TestDirWrite $hashfile [[ $rodir -eq 1 ]] && fb="$hashfile" # Hash File EXISTS! .. # else # The hash file is not writeable.. if [[ ! -w "$hashfile" ]]; then # they may be hashing IN the fallback location! So we use a flagoid. fb="$hashfile" else # the hash file is writeable,. if [[ $append -eq 1 ]]; then # build an array ofexisting files + hashes.. grabhashes "$hashfile" else # Do they wish to overwrite it? case $dlg_mode in 0) until [[ "$answer" != "" ]]; do echo echo 'HASH FILE EXISTS!' echo 'Do you wish to overwrite it?' echo select answer in yes no; do [[ "$answer" != 'yes' ]] && exit 9 break done done echo ;; 1) zanswer=$(zenity --question --title='Checksum File Exists!!!' --width=280 \ --text="A checksum file already exists.\nShall I overwrite it?" \ --timeout=10 --window-icon="$HOME/.local/share/icons/checksum.png") [[ "$zanswer" ]] && exit 9 ;; 2 | 3) kdialog \ --warningyesno " A checksum file already exists. \n Shall I overwrite it? " \ --dontagain 'checksum:do_overwrite' \ --title 'Checksum File Exists!!! ' ;; esac fi fi fi # using the fall-back location.. if [[ "$fb" != 0 ]]; then [[ ! -d "$fallback_dir" ]] && mkdir "$fallback_dir" hashfile="$fallback_dir/${hashfile##*/}" # add more hashes to our existing() array.. [[ -f "$hashfile" ]] && grabhashes "$hashfile" fi if [[ ! -f "$hashfile" ]] || [[ $append -eq 0 ]]; then # okay, let's start a new hash file.. echo '# Checksum for Linux..' > "$hashfile" echo '# http://corz.org/linux/software/checksum/' >> "$hashfile" [[ "$fb" != 0 ]] && echo "# hashes continued from read-only checksum file @ $fb" >> "$hashfile" fi # One last check.. if [[ ! -w "$hashfile" ]]; then echo 'I am having trouble creating the checksum file.' echo "Check the permission on your fallback location: $fallback_dir" exit 9 fi # okay, we're good to go.. notify_user "$hashfile" 'Creating' 'Hashing' if [[ $folder -eq 1 ]]; then # Kinda hackish, but fun.. if [[ "$mask" != '' ]]; then fmask= mask="${mask##*=}" # remove "--mask=" part until [[ "$mask" = ',' ]]; do tmask=$(echo "$mask" | cut -d\, -f1) fmask=" -iname '$tmask'$fmask" mask="${mask#*,}" # on the final pass we add a comma so the previous line removes the final mask next time around. ;o) [[ "$(echo "$mask" | cut -s -d\, -f1)" = "" ]] && mask="$mask," [[ "$mask" != ',' ]] && fmask=" -o $fmask" done fi [[ "$fmask" != '' ]] && fmask=" \( $fmask \) " #2do.. investigate: add name exclusions to find?? eval "find . -type f ${fmask} -print" | while read i; do # check for ignored file types.. thisname="${i##*/}" thisext="${thisname##*.}" for j in "${ignore_extensions[@]}"; do if [[ $j = "$thisext" ]]; then [[ $dlg_mode -eq 0 ]] && echo "*** ** skipping IGNORED file ** *** ${i##*/}" continue 2 fi done # if appending - check against algo array, and skip.. if [[ $append -eq 1 ]]; then for xfile in "${existing[@]}"; do if [[ "$xfile" = "$i" ]]; then [[ $dlg_mode -eq 0 ]] && echo "*** ** skipping checked file ** *** ${i##*/}" continue 2 fi done fi ## NOTE: if using full paths in fall-back checksum file, ($i = relative path) #2do # fall-back has been set, we are in a new location - rewrite the hash paths? if [[ "$fb" != 0 ]]; then # this method will write FULL absolute paths into the fallback hash file.. $tool -b "${opath}/${i#*/}" 2>>"$errlog" | tee -a "$hashfile" # #and/or we could employ some kind of user policy.. # case $fb_method in ### ? # 0) # ;; # 1) # ;; # *) # ;; # esac else # regular write-access.. $tool -b "${i}" 2>>"$errlog" | tee -a "$hashfile" fi done else # hash a single file.. if [[ "$fb" != 0 ]]; then echo "fallback hash foo" $tool -b "${i}" 2>>"$errlog" | tee -a "$hashfile" else $tool -b "$filename" 2>>"$errlog" | tee -a "$hashfile" fi SetDialogHeight "$hashfile" #diag_height=150 fi # this may be zenity, or may be hybrid, so put it first.. [[ "$notifypid" -ne 0 ]] && kill "$notifypid" case $dlg_mode in 0) echo echo 'All done with hashing.' echo ;; 1) zenity --info --title 'All Done!' --text='Checksum operation complete. ' --timeout=10 \ --window-icon="$HOME/.local/share/icons/checksum.png" ;; 2 | 3) kdialog --msgbox ' Checksum operation complete! ' \ --title 'Checksum Complete! ' \ --dontagain 'checksum:notify_when_complete' ;; esac ## VERIFY hashes.. # else # Verify a folder.. # if [[ $folder -eq 1 ]]; then notify_user "${path%/*}" 'Verifying' 'verification' cd "$path" find . -type f \( -name "*.$hashext" -o -name "*.${hash_types[0]}" \ -o -name "*.${hash_types[1]}" -o -name "*.${hash_types[2]}" \ -o -name "*.${hash_types[3]}" -o -name "*.${hash_types[4]}" \) -print | while read i; do chkerror=0 hashfname="${i##*/}" thishext="${hashfname##*.}" cd "$path/${i%/*}" echo "$hashfname: " >> "$tmpfile" if [[ "$thishext" = "$hashext" ]]; then CheckMultiHash "$hashfname" else SwitchAlgo "$thishext" cout=$($tool --check -- "$hashfname" 2>>"$errlog") || chkerror=1 echo "$cout" | tee -a "$tmpfile" fi if [[ "$chkerror" == 1 ]]; then echo "IN: $i" >> "$errlog" echo "" >> "$errlog" fi echo "" >> "$tmpfile" done if [[ -s "$tmpfile" ]]; then echo "" >> "$tmpfile" log_out SetDialogHeight "$tmpfile" fi [[ "$notifypid" -ne 0 ]] && kill "$notifypid" case $dlg_mode in 1 | 3) if [[ -s "$tmpfile" ]]; then zenity --text-info --editable --filename="$tmpfile" --title='Checksum Results..' \ --width=600 --height=$diag_height --window-icon="$HOME/.local/share/icons/checksum.png" else zenity --info --title 'Nothing done!' --text='No checksum files were found! ' --timeout=10 \ --window-icon="$HOME/.local/share/icons/checksum.png" fi ;; 2) if [[ -s "$tmpfile" ]]; then kdialog --textbox "$tmpfile" 600 $diag_height \ --title 'Checksum Results.. ' else kdialog --sorry 'No checksum files were found!' \ --title 'Nothing done! ' fi ;; esac # Verify a hash file.. # # While technically feasible to have some reusable function, I like to keep this # separate and deal with single files (and their notifications) slightly differently.. # else cd "${path%/*}" hashfile="$path" notify_user "$filename" 'Verifying' 'verification' # a .hash file.. if [[ "${hashfile##*.}" = "$hashext" ]]; then CheckMultiHash "$hashfile" else # oldschool .md5/.sha1 file... SwitchAlgo "${hashfile##*.}" if [[ "$dlg_mode" -eq 0 ]]; then $tool --check -- "$hashfile" 2>>"$errlog" else $tool --check -- "$hashfile" >> $tmpfile 2>>"$errlog" || echo "IN: $hashfile" >> "$errlog" fi fi log_out SetDialogHeight "$tmpfile" [[ "$notifypid" -ne 0 ]] && kill "$notifypid" case $dlg_mode in 0) echo echo 'All done with verification!' echo ;; 1 | 3) zenity --text-info --title='Checksum Results' --filename="$tmpfile" --width=600 \ --height=$diag_height --window-icon="$HOME/.local/share/icons/checksum.png" \ --text="Verifying hashes in \"$filename\". This may take a moment.." ;; 2) kdialog --textbox $tmpfile 600 $diag_height --title 'Checksum Results.. ' ;; esac fi if [[ "$keep_errlog" -eq 1 ]] && [[ -s "$errlog" ]]; then cp "$tmpfile" "${path%/*}/checksum_errors [$(date)].log" cat "$errlog" >> "${path%/*}/checksum_errors [$(date)].log" fi fi if [[ "$show_times" = 1 ]]; then finish_time=$(date +%s) echo "All done in $((finish_time - start_time)) seconds." fi exit 0 ## # Context/Service/Action/Right-Click Menus.. # # To get the right-click action, simply create some.desktop files inside.. # # /home//.kde/share/kde4/services/ServiceMenus # ## Example.. # # You could roll them into one single "checksum" action, though I prefer # to keep more than one, with an eye on future improvements. # ## This one is for regular files.. # ##!/home/cor/.kde/share/kde4/services/ServiceMenus/checksum.desktop # # # Simple checksum-like right-click "action" for files # # (c) cor 2009 # # License: GPL.v3 # # [Desktop Entry] # Actions=MD5Checksum # Encoding=UTF-8 # # assuming the icon is located at /home//.local/share/icons/checksum. # Icon=checksum # MimeType=application/octet-stream;*/*; # ServiceTypes=KonqPopupMenu/Plugin,*/* # Type=Service # # checksum does its own startup notifications, no need for bouncing hashes.. # X-KDE-StartupNotify=false # # comment out the next two lines to put this service into the "Actions" submenu. # X-KDE-Priority=TopLevel # # comment out just this next line to have the checksum command in the *main* menu. # X-KDE-Submenu=Checksum # # [Desktop Action MD5Checksum] # Name=Checksum (MD5) # Icon=checksum # #Exec=/bin/sh -c "kchecksum '%f'" # # remove the logging if you like. It's deleted at every bootup, and is handy. # Exec=/bin/sh -c "kchecksum '%f' >> /tmp/checksum.log" ## 2do.. # # switch to $algodeep tools where applicable (*should* be faster) # folder hashing with *individual* hashes (e.g. all files get inividual .hash) # Silent operation (dlg_mode = -1) # Notify when failures *may* have been caused by encountering a Windows checksum file # (is there a CRLF in the file?) & perhaps an option to automatically convert those files on-the-fly. # gui notification on errors? Beep, maybe? # beep when done - handy when completion dialog has been disabled. ## Gnome desktop activation. I must install Gnome and see how it works there! # multihash-in-one-go - user could have an array of chosen algos, e.g. multi=( "md5" "sha1" ) # when not multihashing, simply set it to $algo # when user attempts to hash a single hash file - rather than redundant message (literally!) # post some random easter egg, like "The tops of old socks make excellent hair-bands." # maybe put the info too, in brackets. # per-folder checksum files? Hmmm. Maybe. # switch to bring up mini-options - basically file masks - maybe a presets list, though-[ # --choose flag, to bring up a dialog to choose file masks. # [default to "*.*"s perhaps, for clarity] # hmmm.. would it be possible to pause and continue an operation? # or even continue a crashed (volume) verify and pick up from where we left off # (if the log wasn't in /tmp maybe. hmm) ## BUGS & Foibles.. # # "&" in the file name will mess up verify results dialogs (needs to be converted to &) # I assume other entities will need to be treated the same way. When this happens, a generic # message is posted instead, and checksum otherwise acts as normal, so it's no biggie.