#!/bin/bash # maravento.com # ################################################################################ # # checkmd5 - Verify MD5 checksum of selected file # # Nautilus Path: ~/.local/share/nautilus/scripts or ~/.config/nautilus/scripts # Caja Path: ~/.config/caja/scripts # Thunar Path: ~/.local/share/Thunar/scripts or ~/.config/Thunar/scripts # Nemo Path: ~/.local/share/nemo/scripts # ################################################################################ set -uo pipefail # check no-root if [ "$(id -u)" == "0" ]; then echo "[ERROR] This script should not be run as root." exit 1 fi echo "Start CheckMD5. Wait..." # Function to show error and exit show_error() { zenity --error --title="MD5 Verification" --text="$1" exit 1 } # Function to show info show_info() { zenity --info --title="MD5 Verification" --text="$1" } # Check if zenity is available if ! command -v zenity &> /dev/null; then echo "Error: zenity is required but not installed." exit 1 fi # Check if md5sum is available if ! command -v md5sum &> /dev/null; then show_error "Error: md5sum is required but not installed." fi # Get the selected file (universal file manager detection) if [ -n "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS:-}" ]; then # Nautilus or Caja - get first line only original_file=$(echo "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | head -n1) elif [ -n "${THUNAR_SELECTION:-}" ]; then # Thunar - get first line only original_file=$(echo "$THUNAR_SELECTION" | head -n1) elif [ -n "${NEMO_SCRIPT_SELECTED_FILE_PATHS:-}" ]; then # Nemo (Linux Mint) - get first line only original_file=$(echo "$NEMO_SCRIPT_SELECTED_FILE_PATHS" | head -n1) elif [ $# -eq 1 ]; then # Called from command line with argument original_file="$1" else # No file provided, ask user to select original_file=$(zenity --file-selection \ --title="Select the original file to verify") [ $? -ne 0 ] && exit 0 fi # Check if original file exists and is readable if [ ! -f "$original_file" ]; then show_error "File does not exist: $original_file" fi if [ ! -r "$original_file" ]; then show_error "Cannot read file: $original_file" fi # Get file info for display original_filename=$(basename "$original_file") # Look for checksum file with same name + .md5 extension checksum_file="${original_file}.md5" # If not found, ask user to locate it if [ ! -f "$checksum_file" ]; then zenity --info \ --title="MD5 Verification" \ --text="Checksum file not found:\n$(basename "$checksum_file")\n\nPlease select the .md5 checksum file manually." checksum_file=$(zenity --file-selection \ --title="Select the .md5 checksum file for: $original_filename" \ --file-filter="MD5 files (*.md5) | *.md5" \ --file-filter="All files | *") # If user cancelled the file selection if [ $? -ne 0 ] || [ -z "$checksum_file" ]; then show_error "No checksum file selected. Verification cancelled." fi fi # Validate checksum file if [ ! -f "$checksum_file" ]; then show_error "Checksum file does not exist: $(basename "$checksum_file")" fi if [ ! -r "$checksum_file" ]; then show_error "Cannot read checksum file: $(basename "$checksum_file")" fi if [ ! -s "$checksum_file" ]; then show_error "Checksum file is empty: $(basename "$checksum_file")" fi # Extract hash from checksum file line_count=$(grep -c '' "$checksum_file") if [ "$line_count" -le 1 ]; then # Single-entry file: no ambiguity, use it as-is (covers plain "hash" # or "hash filename" sidecar files) hash=$(head -n1 "$checksum_file" | awk '{print $1}') else # Multi-file manifest (e.g. generated by makemd5): match the entry # whose filename corresponds to the file being verified target_basename=$(basename "$original_file") hash="" while IFS= read -r line; do [ -z "$line" ] && continue line_file="${line#* }" if [ "$(basename "$line_file")" = "$target_basename" ]; then hash="${line%% *}" break fi done < "$checksum_file" if [ -z "$hash" ]; then show_error "No checksum entry found for '$target_basename' in $(basename "$checksum_file")." fi fi # Clean any whitespace hash=$(echo "$hash" | tr -d '[:space:]') # Validate hash format (MD5 should be 32 hex characters) if [[ ! "$hash" =~ ^[a-fA-F0-9]{32}$ ]]; then show_error "Invalid MD5 hash format in checksum file.\n\nExpected: 32 hexadecimal characters\nFound: $hash\n\nChecksum file: $(basename "$checksum_file")" fi # Show progress dialog while calculating ( echo "10" ; echo "# Reading checksum file..." ; sleep 0.3 echo "40" ; echo "# Calculating MD5 hash..." ; sleep 0.5 echo "90" ; echo "# Comparing checksums..." ; sleep 0.3 echo "100" ; echo "# Complete!" ) | zenity --progress \ --title="MD5 Verification" \ --text="Verifying: $original_filename" \ --percentage=0 \ --auto-close \ --no-cancel & progress_pid=$! # Calculate actual hash of the file actual_hash=$(md5sum "$original_file" | awk '{print $1}') # Kill progress dialog kill $progress_pid 2>/dev/null wait $progress_pid 2>/dev/null # Compare hashes directly (case insensitive) if [ "$(echo "$hash" | tr '[:upper:]' '[:lower:]')" = "$(echo "$actual_hash" | tr '[:upper:]' '[:lower:]')" ]; then show_info "VERIFICATION SUCCESSFUL\n\nFile: $original_filename\nHash: $hash\nStatus: INTEGRITY VERIFIED\n\nThe file has not been corrupted or modified." else show_error "VERIFICATION FAILED\n\nFile: $original_filename\nExpected: $hash\nActual: $actual_hash\nStatus: CHECKSUMS DO NOT MATCH\n\nThe file may be corrupted, incomplete, or modified." fi