#!/bin/bash
#===============================================================================
#
# DIRECTORY:
#   /home/*/.local/share/nautilus/scripts/
# OR
#   /home/*/.gnome2/nautilus-sctipts/ (deprecated)
#
# FILE:
#   .casualscripter_nautilus-scripts_functions.sh
#
# USAGE:
#   This is a library for some other nautilus sripts.
#
# OPTIONS:
#   none
#
# DESCRIPTION:
#   Global configuration and functions for the other nautilus scripts.
#
# REQUIREMENTS:
#   to much! ;-)
#
# BUGS:
#   ---
#
# NOTES:
#   Tested on
#   - Debian 8+
#   - Arch Linux (work in progress)
#
# AUTHOR:
#   Patrick Neumann, patrick@neumannsland.de
#
# COMPANY:
#   (privately)
#
# VERSION:
#   0.9 (beta)
#
# LINK TO THE MOST CURRENT VERSION:
#   https://...
#
# CREATED:
#   21.03.2016
#
# COPYRIGHT (C):
#   2015-2020 - Patrick Neumann
#
# LICENSE:
#   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 3 of the License, or
#   (at your option) any later version.
#
# WARRANTY:
#   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, see <http://www.gnu.org/licenses/>.
#
# HISTORY:
#   0.9 - Patrick Neumann - Initial (public) release
#
#===============================================================================

#=== FUNCTION ==================================================================
# NAME:        trim
# DESCRIPTION: Remove leading and trailing whitespace characters from a string.
# PARAMETER 1: string
#-------------------------------------------------------------------------------
# IMPORTANT:   NEEDED FOR CONFIGURATION!
#===============================================================================
trim() {
  local var="${1}"
  var="${var#"${var%%[![:space:]]*}"}"
  var="${var%"${var##*[![:space:]]}"}"
  echo -n "${var}"
}

#=== CONFIGURATION =============================================================
readonly WHICH_BIN="/usr/bin/which"
# which has to be configured before (!):

readonly AWK_BIN="$( ${WHICH_BIN} awk )"
readonly BASE64_BIN="$( ${WHICH_BIN} base64 )"
readonly BASENAME_BIN="$( ${WHICH_BIN} basename )"
readonly BASH_BIN="$( ${WHICH_BIN} bash )"
readonly BC_BIN="$( ${WHICH_BIN} bc )"
readonly BINWALK_BIN="$( ${WHICH_BIN} binwalk )"
readonly CAT_BIN="$( ${WHICH_BIN} cat )"
readonly CURL_BIN="$( ${WHICH_BIN} curl )"
readonly CUT_BIN="$( ${WHICH_BIN} cut )"
readonly DATE_BIN="$( ${WHICH_BIN} date )"
readonly DD_BIN="$( ${WHICH_BIN} dd )"
readonly DC3DD_BIN="$( ${WHICH_BIN} dc3dd )"
readonly DIRNAME_BIN="$( ${WHICH_BIN} dirname )"
readonly EWFINFO_BIN="$( ${WHICH_BIN} ewfinfo )"
readonly EWFVERIFY_BIN="$( ${WHICH_BIN} ewfverify )"
readonly FCAT_BIN="$( ${WHICH_BIN} fcat )"
readonly FILE_BIN="$( ${WHICH_BIN} file )"
readonly FIND_BIN="$( ${WHICH_BIN} find )"
readonly FLS_BIN="$( ${WHICH_BIN} fls )"
readonly FRED_BIN="$( ${WHICH_BIN} fred )"
readonly FSSTAT_BIN="$( ${WHICH_BIN} fsstat )"
#readonly GKSU_BIN="$( ${WHICH_BIN} gksu )" # switched to pkexec!
readonly GPG_BIN="$( ${WHICH_BIN} gpg )"
readonly GPGSM_BIN="$( ${WHICH_BIN} gpgsm )"
readonly GREP_BIN="$( ${WHICH_BIN} grep )"
readonly GSETTINGS_BIN="$( ${WHICH_BIN} gsettings )"
readonly GTERMINAL_BIN="$( ${WHICH_BIN} gnome-terminal )"
readonly HASHCAT_BIN="$( ${WHICH_BIN} hashcat )"
readonly HEAD_BIN="$( ${WHICH_BIN} head )"
readonly HIVEXGET_BIN="$( ${WHICH_BIN} hivexget )"
readonly HXREGEDIT_BIN="$( ${WHICH_BIN} hivexregedit )"
readonly ICAT_BIN="$( ${WHICH_BIN} icat )"
readonly IFIND_BIN="$( ${WHICH_BIN} ifind )"
readonly ISTAT_BIN="$( ${WHICH_BIN} istat )"
readonly KCPPY_BIN="$( ${WHICH_BIN} kcpass.py )"
readonly KPARTX_BIN="$( ${WHICH_BIN} kpartx )"
readonly LOSETUP_BIN="$( ${WHICH_BIN} losetup )"
readonly MINISIGN_BIN="$( ${WHICH_BIN} minisign )"
readonly MKDIR_BIN="$( ${WHICH_BIN} mkdir )"
readonly MMLS_BIN="$( ${WHICH_BIN} mmls )"
readonly MMSTAT_BIN="$( ${WHICH_BIN} mmstat )"
readonly MOUNT_BIN="$( ${WHICH_BIN} mount )"
readonly MSIGFIND_BIN="$( ${WHICH_BIN} multi_sigfind )"
readonly MV_BIN="$( ${WHICH_BIN} mv )"
readonly OPENSSL_BIN="$( ${WHICH_BIN} openssl )"
readonly OPHCRACK_BIN="$( ${WHICH_BIN} ophcrack )"
readonly PASSWD_BIN="$( ${WHICH_BIN} passwd )"
readonly PGREP_BIN="$( ${WHICH_BIN} pgrep )"
readonly PKEXEC_BIN="$( ${WHICH_BIN} pkexec )"
readonly PLUTIL_BIN="$( ${WHICH_BIN} plistutil )"
readonly PPEPY_BIN="$( ${WHICH_BIN} print_plist_entry.py )"
readonly PRINTF_BIN="$( ${WHICH_BIN} printf )"
readonly PSTAT_BIN="$( ${WHICH_BIN} pstat )"
readonly PWDUMP_BIN="$( ${WHICH_BIN} pwdump.py )"
readonly QEMUIMG_BIN="$( ${WHICH_BIN} qemu-img )"
readonly QEMUSYS64_BIN="$( ${WHICH_BIN} qemu-system-x86_64 )"
readonly RM_BIN="$( ${WHICH_BIN} rm )"
readonly RMDIR_BIN="$( ${WHICH_BIN} rmdir )"
readonly SED_BIN="$( ${WHICH_BIN} sed )"
readonly SIGFIND_BIN="/usr/local/bin/sigfind"
readonly SIGNIFY_BIN="$( ${WHICH_BIN} signify )"
readonly SLEEP_BIN="$( ${WHICH_BIN} sleep )"
readonly SORT_BIN="$( ${WHICH_BIN} sort )"
readonly STRINGS_BIN="$( ${WHICH_BIN} strings )"
readonly TAIL_BIN="$( ${WHICH_BIN} tail )"
readonly TEE_BIN="$( ${WHICH_BIN} tee )"
readonly TESTDISK_BIN="$( ${WHICH_BIN} testdisk )"
# TRACKER_BIN is defined after the call of check_osr!
readonly TR_BIN="$( ${WHICH_BIN} tr )"
readonly UNBUFFER_BIN="$( ${WHICH_BIN} unbuffer )"
readonly UMOUNT_BIN="$( ${WHICH_BIN} umount )"
readonly VINAGRE_BIN="$( ${WHICH_BIN} vinagre )"
readonly XMOUNT_BIN="$( ${WHICH_BIN} xmount )"
readonly XXD_BIN="$( ${WHICH_BIN} xxd )"
readonly ZCAT_BIN="$( ${WHICH_BIN} zcat )"
readonly ZENITY_BIN="$( ${WHICH_BIN} zenity )"
readonly ZGREP_BIN="$( ${WHICH_BIN} zgrep )"

# needs configured sudo (!):
readonly SUDO_BIN="$( ${WHICH_BIN} sudo )"
readonly CHNTPW_BIN="$( ${SUDO_BIN} ${WHICH_BIN} chntpw )"
readonly PARTED_BIN="$( ${SUDO_BIN} ${WHICH_BIN} parted )"

if [ -z "${SUPPORTED_OSR:+x}" ] ; then
  readonly SUPPORTED_OSR="debian8"
else
  readonly SUPPORTED_OSR="debian8 $( trim "${SUPPORTED_OSR}" )"
fi

if [ -n "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS}" ] ; then
  # Remove the final "newline"!
  readonly SOURCE="${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS%?}"
else
  readonly SOURCE="${1}"
fi

if ${GREP_BIN} --fixed-strings --line-regexp "${PWD// /\\040}" \
<( ${CUT_BIN} -d " " -f 2 /proc/mounts ) \
> /dev/null 2>&1 ; then
  DIRNAME="$( ${DIRNAME_BIN} "${SOURCE}" )"
  readonly DIRNAME="${DIRNAME%/*}"
else
  readonly DIRNAME="$( ${DIRNAME_BIN} "${SOURCE}" )"
fi

#=== FUNCTIONS =================================================================

#=== FUNCTION ==================================================================
# NAME:        error_exit
# DESCRIPTION: Display error message with "zenity" and exit.
# PARAMETER 1: message (string)
#===============================================================================
function error_exit () {
  ${ZENITY_BIN} --error \
                --width="240" \
                --height="120" \
                --text \
                "ERROR: ${1}... EXIT!!!"
  exit 1
}

#=== FUNCTION ==================================================================
# NAME:        hint
# DESCRIPTION: Display hint message with "zenity".
# PARAMETER 1: message (string)
#===============================================================================
function hint () {
  ${ZENITY_BIN} --info \
                --width="240" \
                --height="120" \
                --text \
                "HINT: ${1}."
}

#=== FUNCTION ==================================================================
# NAME:        success
# DESCRIPTION: Display succes message with "zenity".
# PARAMETER 1: message (string)
#===============================================================================
function success () {
  ${ZENITY_BIN} --info \
                --width="240" \
                --height="120" \
                --text \
                "SUCCESS: ${1}!"
}

#=== FUNCTION ==================================================================
# NAME:        check_osr
# DESCRIPTION: Check os release id and version.
# PARAMETER 1: space seperated "list" (string) of  all
#                supported os releases (id+version_id),
#                e. g.: "debian7 debian8 arch ubuntu14.04".
#===============================================================================
function check_osr () {
  if [ ! -f "/etc/os-release" ] ; then
    error_exit "sorry, your distribution is not supported"
  fi

  source <( ${GREP_BIN} --fixed-strings \
                        --regexp="ID=" \
                        --regexp="VERSION_ID=" \
                        --regexp="PRETTY_NAME=" \
                        "/etc/os-release" \
            | ${SED_BIN} "s/^/OR_/" )

  supported=0
  for os in ${1} ; do
    if echo "${os}" \
    | ${GREP_BIN} --quiet \
                  --fixed-strings \
                  "${OR_ID}${OR_VERSION_ID}" ; then
      supported=1
    fi
  done

  if [ ${supported} -eq 0 ] ; then
    error_exit "sorry, ${OR_PRETTY_NAME} is not supported"
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_dep
# DESCRIPTION: Check dependencies.
# PARAMETER 1: path+binary, e. g.: "/usr/bin/ewfinfo".
# PARAMETER 2: package (deb), e. g.: "ewf-tools".
#===============================================================================
function check_dep () {
  if [ ! -x "${1}" ] ; then
    error_exit "please install ${2}"
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_xmount_version
# DESCRIPTION: Check if xmount version is 0.7.0 or higher.
# PARAMETERS:  none
#===============================================================================
check_xmount_version () {
  readonly XMOUNT_VERSION="$( ${XMOUNT_BIN} --version \
                              | ${AWK_BIN} '/^xmount[[:space:]]v[0-9\.]*[[:space:]].*/ { print $2; }' \
                              | ${GREP_BIN} --extended-regexp \
                                            --only-matching "[[:digit:]\.]*" )"

  if (( "$( ${BC_BIN} <<< "${XMOUNT_VERSION%.*} < 0.7" )" == "1" )) ; then
    error_exit "please upgrade xmount to version 0.7.0 or newer"
  fi
}

check_user_allow_other () {
  if ! ${GREP_BIN} --extended-regexp "^user_allow_other.*" /etc/fuse.conf > /dev/null 2>&1 ; then
    error_exit "user_allow_other is not configured in /etc/fuse.conf"
	fi
}

#=== FUNCTION ==================================================================
# NAME:        check_ext
# DESCRIPTION: Check file extension.
# PARAMETER 1: path+file.ext (may be a variable!?).
# PARAMETER 2: file extension (without dot).
#===============================================================================
function check_ext () {
  if [ ! -f "${1}" ] ; then
    error_exit "please select a regular file"
  fi

  if ! [[ "${1##*.}" =~ ${2} ]] ; then
    error_exit "please select a file ending with .${2}"
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_file
# DESCRIPTION: Check if param is a regular file with given name.
# PARAMETER 1: path+filename (may be a variable!?).
# PARAMETER 2: expected name.
#===============================================================================
function check_file () {
  if [ ! -f "${1}" ] ; then
    error_exit "please select a regular file"
  fi

  if ! [[ "${1##*/}" =~ ${2} ]] ; then
    error_exit "please select a file named \"${2}\""
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_dir
# DESCRIPTION: Check if param is a directory with given name.
# PARAMETER 1: path+directory (may be a variable!?).
# PARAMETER 2: expected name.
#===============================================================================
function check_dir () {
  if [ ! -d "${1}" ] ; then
    error_exit "please select a directory"
  fi

  if ! [[ "${1##*/}" =~ ${2} ]] ; then
    error_exit "please select a directory named \"${2}\""
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_tmp
# DESCRIPTION: Check if tmp directory is present and provide it as variable.
#===============================================================================
function check_tmp () {
  if [ ! -d "${DIRNAME}/tmp" ] ; then
    if ! ${MKDIR_BIN} "${DIRNAME}/tmp" ; then
      error_exit "was not able to create tmp directory"
    fi
  fi

  TMP="${DIRNAME}/tmp"
}

#=== FUNCTION ==================================================================
# NAME:        source_is_mounted
# DESCRIPTION: Probe if a "device" (or image) is mounted.
# PARAMETERS:  none
#===============================================================================
function source_is_mounted () {
  if ${GREP_BIN} --fixed-strings --line-regexp "${SOURCE// /\\040}" \
  <( ${CUT_BIN} -d " " -f 1 /proc/mounts ) \
  > /dev/null 2>&1 ; then
    return 0
  else
    return 1
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_if_source_is_not_mounted
# DESCRIPTION: Check if a "device" (or image) is NOT mounted.
# PARAMETERS:  none
#===============================================================================
function check_if_source_is_not_mounted () {
  if source_is_mounted ; then
    error_exit "partition of the image is not mounted - mount it first"
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_if_source_is_mounted
# DESCRIPTION: Check if a "device" (or image) IS mounted.
# PARAMETERS:  none
#===============================================================================
function check_if_source_is_mounted () {
  if ! source_is_mounted ; then
    error_exit "partition of the image is still mounted - u(n)mount it first"
  fi
}

#=== FUNCTION ==================================================================
# NAME:        pwd_used_as_mountpoint
# DESCRIPTION: Probe if the current working directory is a used mountpoint, eg.
#                an image is mounted through xmount.
# PARAMETERS:  none
#===============================================================================
function pwd_used_as_mountpoint () {
  if ${GREP_BIN} --fixed-strings --line-regexp "${PWD// /\\040}" \
  <( ${CUT_BIN} -d " " -f 2 /proc/mounts ) \
  > /dev/null 2>&1 ; then
    return 0
	else
		return 1
  fi
}

#=== FUNCTION ==================================================================
# NAME:        check_if_pwd_is_not_used_as_mountpoint
# DESCRIPTION: Check if the current working directory is NOT a used mountpoint.
#                (To avoid unintended changes!)
# PARAMETERS:  none
#===============================================================================
function check_if_pwd_is_not_used_as_mountpoint () {
  if pwd_used_as_mountpoint ; then
		error_exit "trough xmounted RAW images are not supported"
	fi
}

#=== FUNCTION ==================================================================
# NAME:        check_if_pwd_is_used_as_mountpoint
# DESCRIPTION: Check if the current working directory IS a used mountpoint.
#                (To avoid unintended changes!)
# PARAMETERS:  none
#===============================================================================
function check_if_pwd_is_used_as_mountpoint () {
  if ! pwd_used_as_mountpoint ; then
		error_exit "only xmounted RAW images are supported"
	fi
}

#=== FUNCTION ==================================================================
# NAME:        check_if_is_looped
# DESCRIPTION: Check if directory is a part of a xmounted image with losetup.
# PARAMETERS:  none
#===============================================================================
function check_if_is_looped () {
  if ! ${GREP_BIN} --extended-regexp "^/dev/loop[[:digit:]]{1,2}[[:space:]]${SOURCE}" /proc/mounts > /dev/null 2>&1 ; then
    error_exit "only with losetup mounted parts of with xmount mounted images are supported"
  fi
}

#=== FUNCTION ==================================================================
# NAME:        disable_gnome_automount
# DESCRIPTION: Disable automounting in gnome.
# PARAMETERS:  none
#===============================================================================
function disable_gnome_automount () {
  readonly MEDIA_HANDLING="org.gnome.desktop.media-handling"
  if [ "$( ${GSETTINGS_BIN} get ${MEDIA_HANDLING} automount-open )" != "false" ] ; then
    ${GSETTINGS_BIN} set "${MEDIA_HANDLING}" automount-open false
  fi
  if [ "$( ${GSETTINGS_BIN} get ${MEDIA_HANDLING} automount )" != "false" ] ; then
    ${GSETTINGS_BIN} set "${MEDIA_HANDLING}" automount false
  fi
  if [ "$( ${GSETTINGS_BIN} get ${MEDIA_HANDLING} autorun-never )" != "true" ] ; then
    ${GSETTINGS_BIN} set "${MEDIA_HANDLING}" autorun-never true
  fi
}

#=== FUNCTION ==================================================================
# NAME:        disable_tracker_miner
# DESCRIPTION: Disable Tracker Miner (may interfere with later umount).
# PARAMETERS:  none
#===============================================================================
function kill_all_tracker_processes () {
  if ${PGREP_BIN} "tracker-" > /dev/null 2>&1 ; then
    ${TRACKER} --kill=all
	fi
}

#=== FUNCTION ==================================================================
# NAME:        xmount_out_format
# DESCRIPTION: Ask the user for the output format for xmount.
# PARAMETERS:  none
#===============================================================================
xmount_out_format() {
  ${ZENITY_BIN} --list \
                --text "Please select the output image format!" \
                --radiolist \
                --column "" \
                --column "Output image formats (--out):" TRUE raw FALSE vdi FALSE vmdk \
                --width="320" \
                --height="320"
}

#=== FUNCTION ==================================================================
# NAME:        choose_partiton
# DESCRIPTION: Ask the user for a partition of an image.
# PARAMETER 1: image file (absolute path)
# PARAMETER 2: "mmls" (default) or "parted"
#===============================================================================
choose_partition() {
  if [ "${2}" = "parted" ] ; then
    PARTITIONS="$( ${PARTED_BIN} -s -m "${1}" unit b print )"
  else
		PARTITIONS="$( ${MMLS_BIN} -aM "${1}" )"
  fi

  readonly PARTITIONS="$( echo "${PARTITIONS}" \
                          | ${GREP_BIN} --extended-regexp "^[[:digit:]]" \
                          | ${TR_BIN} -s " " "_" \
                          | ${AWK_BIN} 'NR == 1 { print "TRUE", $0; }; NR > 1 { print "FALSE", $0; }' \
                          | ${TR_BIN} "\n" " " )"

  if [ -z "${PARTITIONS}" ] ; then
    error_exit "no valid partition table found"
  fi

  readonly CHOICE="$( ${ZENITY_BIN} --list \
                                    --text "Please select a partition!" \
                                    --radiolist \
                                    --column "Pick" \
                                    --column "Partitions" \
                                    ${PARTITIONS} \
                                    --width="800" \
                                    --height="400" )"

  if [ -z "${CHOICE}" ] ; then
    error_exit "no partition selected"
  fi

  echo -n "${CHOICE}"
}

#=== FUNCTION ==================================================================
# NAME:        display_resultfile
# DESCRIPTION: Display the resultfile if present or after generating it.
# PARAMETERS:  Path and filename of the resultfile
#===============================================================================
display_resultfile() {
  if [ -f "${1}" ] ; then
    ${ZENITY_BIN} --text-info \
                  --title "$( ${BASENAME_BIN} "${1}" )" \
                  --width="800" \
                  --height="400" \
                  --filename="${1}"
  else
    error_exit "$( ${BASENAME_BIN} "${1}" ) is missing"
  fi
}

#=== GLOBAL CHECKS =============================================================

#-------------------------------------------------------------------------------
# Check supported operating system release.
#-------------------------------------------------------------------------------
check_osr "${SUPPORTED_OSR}"

case "${OR_ID}${OR_VERSION_ID}" in
	debian8) TRACKER_BIN="$( ${WHICH_BIN} tracker-control )"
           ;;
  arch) TRACKER_BIN="$( ${WHICH_BIN} tracker ) daemon"
        ;;
  *) error_exit "sorry, ${OR_PRETTY_NAME} is not supported"
esac

#-------------------------------------------------------------------------------
# Check, if only one file is selectet (only from nautilus!).
#-------------------------------------------------------------------------------
if [ -n "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS}" ] ; then
  if [[ "${#}" -gt "1" ]] ; then
    error_exit "please select only one file"
  fi
fi

#-------------------------------------------------------------------------------
# Check for absolute path.
#-------------------------------------------------------------------------------
if [[ "${SOURCE}" != /* ]] ; then
  error_exit "please use an absolute path"
fi

#-------------------------------------------------------------------------------
# Check, if image exist.
#-------------------------------------------------------------------------------
if [ ! -e "${SOURCE}" ] ; then
  error_exit "image/mountpoint does not exist"
fi

# Do not use "exit" at the end of an sources shell script/library!
# exit 0