#! /bin/bash

config_file=""
default_config_file="$HOME/.config/ext_luks_bck/config.sh"

re="\/$" # Matches strings that end with a forward slash.

# Rsync command. Options:
# -a : archive mode. Among other things, ensure that timestamps are preserved.
# -v : verbose mode. Because a bit more info doesn't hurt...
# --relative : recreate full paths of files in backup device. This allows
# restoring files to their previous location automatically.
rsync_cmd_base="rsync -av --relative "

# Exit on Ctrl-C. This is required because otherwise, if the user pressed C-c
# say, at the sudo prompt, the sudo command would halt, but the script would
# proceed to open the LUKS device, etc.
trap ctrl_c INT
function ctrl_c() {
  echo "Interrupted."
  exit 0
}

function backup() {
  mount_luks

  for i in "${excludes[@]}" ; do
    rsync_excludes+=( --exclude "$i" )
  done

  # The $rsync_cmd_base command is explained above. Here I only explain the
  # --delete option: it is here to ensure that when the user deletes a file or
  # folder that had been previously backed up, then it is also deleted on the
  # backup copy.
  $rsync_cmd_base --delete "${locations[@]}" "${rsync_excludes[@]}" "$ext_mnt_point/$ext_root_backup_folder" | grep -E -v '/$'

  umount_luks
}

function display_usage() {
  echo -e "\nOptions for $0:\n- Run without arguments to make backup, run with -r option to restore a backup.\n- Use the -y option, either when doing a backup or when restoring one, to run rsync with the --dry-run option (simulation).\n- Use -m to mount LUKS volume only; -u to dismount LUKS volume only.\n"
}

function mount_luks {
  # Even "inside" sudo, $USER is the user *running* the sudo command.
  sudo -- sh -c "cryptsetup --key-file=$ext_luks_key open UUID=$ext_luks_dev extbck ; mount /dev/mapper/extbck $ext_mnt_point ; chown -R $USER $ext_mnt_point"

  if [[ $? != 0 ]]; then
    echo "$0: Mounting encrypted volume failed. Exiting..."
    exit 1
  else
    echo "$0: Mounted encrypted volume."
  fi
}

function restore() {
  mount_luks

  # The $rsync_cmd_base command is explained above. Here I only explain the
  # --update option: it is here to ensure that when restoring a backup, files
  # that have been modified AFTER the backup was made, are NOT overwritten.
  # Hence, the newer version of the files is preserved.
  $rsync_cmd_base --update "$ext_mnt_point/$ext_root_backup_folder/./" / | grep -E -v '/$'

  umount_luks
}

function umount_luks {
  sudo -- sh -c "umount $ext_mnt_point ; cryptsetup close extbck"

  if [[ $? != 0 ]]; then
    echo "$0: Unmounting encrypted volume failed. Exiting..."
    exit 1
  else
    echo "$0: Unmounted encrypted volume."
  fi
}

# main() is the last function because it calls all the other functions -- and
# with shell script, this means all those other functions must be defined
# before. Or equivalently, main() must be the last function.
function main {
  config_file=""

  # First, find if user provided a custom config file.
  got_dash_f_in_prev_iter=0
  for i in $@ ; do
    if [[ $got_dash_f_in_prev_iter -eq 1 ]]; then
      config_file="$i"
      break
    fi

    if [[ "$i" == "-f" ]] ; then
      got_dash_f_in_prev_iter=1
      continue
    fi
  done

  if [[ $got_dash_f_in_prev_iter -eq 0 ]]; then
    # If no -f handle provided, then use default config file.
    config_file="$default_config_file"
  elif [[ $got_dash_f_in_prev_iter -eq 1 ]]; then
    if [[ -z "$config_file" ]]; then
      # If -f handle was provided, but without a config a file, then throw
      # error.
      echo "ERROR: -f provied without a config file!"
      exit 1
    fi
  else
    echo "ERROR: \$got_dash_f_in_prev_iter has value $got_dash_f_in_prev_iter"
    exit 1
  fi

  # If control reaches here, there is a config provided, so let us check if it
  # is proper.
  if ! [[ -f  "$config_file" ]]; then
    echo "Could not read file $config_file!"
    exit 1
  fi

  # Config file is proper, so source it and move on...
  source "$config_file"

  # Now let us deal with other command line arguments, if any.
  local op="backup"
  for i in $@ ; do
    if [[ "$i" == "-h" ]] ; then
      display_usage
      exit 0
    elif [[ "$i" == "-m" ]] ; then
      mount_luks
      exit $?
    elif [[ "$i" == "-u" ]] ; then
      umount_luks
      exit $?
    elif [[ "$i" == "-r" ]] ; then
      op="restore"
    elif [[ "$i" == "-y" ]] ; then
      rsync_cmd_base="$rsync_cmd_base --dry-run"
    fi
  done

  if ! [[ -f "$ext_luks_key" ]]; then
    echo "LUKS key file $ext_luks_key could not be read. Exiting..."
    exit 1
  fi

  if ! [[ -d "$ext_mnt_point" ]]; then
    echo "Mount point $ext_mnt_point does not exist (or is not directory). Exiting..."
    exit 1
  fi

  # Iterate over the (indices of) the locations array, and in any location that
  # ends with a forward slash, remove said forward slash. This is done because
  # while rsync'ing folder foobar transfers the folder foobar itself onto the
  # backup location, rsync'ing foobar/ transfers only the CONTENTS of the
  # folder foobar. Among other things, this makes it impossible to restore the
  # backup automatically.
  for i in "${!locations[@]}" ; do
    if [[ "${locations[$i]}" =~ $re ]]; then
      locations[$i]=${locations[$i]%?}
    fi
  done

  if [[ "$op" == "backup" ]]; then
    backup
  elif [[ "$op" == "restore" ]]; then
    restore
  else
    echo "Unknown operation $op. Exiting..."
    exit 1
  fi
}

main "$@"