#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# shellcheck disable=SC2006,SC2181

# remove all existing aliases
unalias -a

# use a safe umask for created files
umask 027

# set locale
LANG=C
export LANG
LC_ALL=C
export LC_ALL

# standards conformance for GNU utilities
_POSIX2_VERSION=199209
export _POSIX2_VERSION

# get current working dir
# $PWD is not set in solaris 10
UAC_DIR=`pwd`

# check if UAC is being executed from untarred directory
if [ ! -d "${UAC_DIR}/lib" ] || [ ! -d "${UAC_DIR}/artifacts" ]; then
  printf %b "uac: required files not found. Make sure you are executing uac \
from untarred directory.\n" >&2
  exit 1
fi

# set path
PATH="/usr/xpg4/bin:/usr/xpg6/bin:/bin:/sbin:/usr/bin:/usr/sbin"
PATH="${PATH}:/usr/local/bin:/usr/local/sbin:/usr/ucb:/usr/ccs/bin:/opt/bin"
PATH="${PATH}:/opt/sbin:/opt/local/bin:/snap/bin:/netscaler"
export PATH

# load lib files
# shellcheck disable=SC1091
. "${UAC_DIR}/lib/load_lib_files.sh"

# global vars
UAC_VERSION="2.7.0"
MOUNT_POINT="/"
OPERATING_SYSTEM=""
SYSTEM_ARCH=""
START_DATE=""
START_DATE_DAYS=""
START_DATE_EPOCH=""
END_DATE=""
END_DATE_DAYS=""
END_DATE_EPOCH=""

# the following variables are not always set on some systems, so they are set
# here to avoid the script exiting with errors after set -u is used below
# shellcheck disable=SC2269
HOME="${HOME}"
# shellcheck disable=SC2269
HOSTNAME="${HOSTNAME}"

# local vars
ua_artifacts=""
ua_destination_dir=""
ua_run_as_non_root=false
ua_temp_dir=""
ua_case_number=""
ua_evidence_number=""
ua_evidence_description=""
ua_examiner=""
ua_notes=""
ua_hostname=""
ua_sftp_destination=""
ua_sftp_port=""
ua_sftp_identity_file=""
ua_s3_presigned_url=""
ua_s3_presigned_url_log_file=""
ua_azure_storage_sas_url=""
ua_azure_storage_sas_url_log_file=""
ua_ibm_cos_url=""
ua_ibm_cos_url_log_file=""
ua_ibm_cloud_api_key=""
ua_delete_local_on_successful_transfer=false
ua_debug_mode=false
ua_temp_data_dir_symlink_support=false

# load config file
load_config_file "${UAC_DIR}/config/uac.conf" || exit 1

# get current command
# shellcheck disable=SC2124
ua_command_line="$0 $@"

# parse command line arguments
while [ "${1:-}" != "" ]; do
  case "${1}" in
    # optional arguments
    "-h"|"--help")
      usage
      exit 1
      ;;
    "-V"|"--version")
      printf %b "UAC (Unix-like Artifacts Collector) ${UAC_VERSION}\n"
      exit 0
      ;;
    "--debug")
      ua_debug_mode=true
      ;;
    # profiling arguments
    "-p"|"--profile")
      if [ -n "${2}" ]; then
        # print available profiles
        if [ "${2}" = "list" ]; then
          list_profiles
          exit 1
        fi
        # get profile file based on the profile name
        ua_profile_file=`get_profile_file "${2}"`
        # exit if profile not found
        if [ -z "${ua_profile_file}" ]; then
          printf %b "uac: profile not found '${2}'\n"
          exit 1
        fi
        ua_profile_file="${UAC_DIR}/profiles/${ua_profile_file}"
        # check if profile file is valid
        validate_profile_file "${ua_profile_file}" || exit 1

        # convert profile file into a comma separated list of artifacts
        ua_artifacts_from_profile=`profile_file_to_artifact_list "${ua_profile_file}"`
        ua_artifacts="${ua_artifacts},${ua_artifacts_from_profile}"

        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "-a"|"--artifacts")
      if [ -n "${2}" ]; then
        # print available artifacts
        if [ "${2}" = "list" ]; then
          list_artifacts
          exit 1
        fi
        ua_artifacts="${ua_artifacts},${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    # collection arguments
    "-m"|"--mount-point")
      if [ -n "${2}" ]; then
        MOUNT_POINT="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "-s"|"--operating-system")
      if [ -n "${2}" ]; then
        OPERATING_SYSTEM="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "-u"|"--run-as-non-root")
      ua_run_as_non_root=true
      ;;
    "--hostname")
      if [ -n "${2}" ]; then
        ua_hostname="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--temp-dir")
      if [ -n "${2}" ]; then
        ua_temp_dir="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    # filter arguments
    "--date-range-start")
      if [ -n "${2}" ]; then
        START_DATE="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--date-range-end")
      if [ -n "${2}" ]; then
        END_DATE="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    # informational arguments
    "--case-number")
      if [ -n "${2}" ]; then
        ua_case_number="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--description")
      if [ -n "${2}" ]; then
        ua_evidence_description="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--evidence-number")
      if [ -n "${2}" ]; then
        ua_evidence_number="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--examiner")
      if [ -n "${2}" ]; then
        ua_examiner="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--notes")
      if [ -n "${2}" ]; then
        ua_notes="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    # remote transfer arguments
    "--sftp")
      if [ -n "${2}" ]; then
        ua_sftp_destination="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--sftp-port")
      if [ -n "${2}" ]; then
        ua_sftp_port="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--sftp-identity-file")
      if [ -n "${2}" ]; then
        ua_sftp_identity_file="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--s3-presigned-url")
      if [ -n "${2}" ]; then
        ua_s3_presigned_url="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--s3-presigned-url-log-file")
      if [ -n "${2}" ]; then
        ua_s3_presigned_url_log_file="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
      "--azure-storage-sas-url")
      if [ -n "${2}" ]; then
        ua_azure_storage_sas_url="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--azure-storage-sas-url-log-file")
      if [ -n "${2}" ]; then
        ua_azure_storage_sas_url_log_file="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
      "--ibm-cos-url")
      if [ -n "${2}" ]; then
        ua_ibm_cos_url="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--ibm-cos-url-log-file")
      if [ -n "${2}" ]; then
        ua_ibm_cos_url_log_file="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--ibm-cloud-api-key")
      if [ -n "${2}" ]; then
        ua_ibm_cloud_api_key="${2}"
        shift
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    "--delete-local-on-successful-transfer")
      ua_delete_local_on_successful_transfer=true
      ;;
    # validation arguments
    "--validate-artifacts-file")
      if [ -n "${2}" ]; then
        validate_artifacts_file "${2}" || exit 1
        printf %b "uac: artifacts file '${2}' successfully validated.\n"
        exit 0
      else
        printf %b "uac: option '${1}' requires an argument.\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
    # invalid arguments
    -*)
      printf %b "uac: invalid option '${1}'\n\
Try 'uac --help' for more information.\n" >&2
      exit 1
      ;;
    # positional arguments
    *)
      if [ -z "${ua_destination_dir}" ]; then
        ua_destination_dir="${1}"
      else
        printf %b "uac: invalid option '${1}'\n\
Try 'uac --help' for more information.\n" >&2
        exit 1
      fi
      ;;
  esac
  shift
done

# do not allow using undefined variables
# set -u cannot be set at the beginning of the file since it will fail on $@
set -u

# exit if list of artifacts or destination dir is empty
if [ -z "${ua_artifacts}" ] || [ -z "${ua_destination_dir}" ] ; then
  usage
  exit 1
fi

# sanitize artifact list
ua_artifacts=`sanitize_artifact_list "${ua_artifacts}"`

OIFS="${IFS}"
IFS=","
# check if artifacts exist
for ua_artifact_file in ${ua_artifacts}; do
  ua_artifact_file=`echo "${ua_artifact_file}" | sed -e 's:^!::'`
  if artifact_file_exist "${ua_artifact_file}"; then
    true
  else
    printf %b "uac: artifact file not found '${UAC_DIR}/artifacts/${ua_artifact_file}'\n" >&2
    exit 1
  fi
done
IFS="${OIFS}"

# check if destination directory exists
if [ ! -d "${ua_destination_dir}" ]; then
  printf %b "uac: no such file or directory '${ua_destination_dir}'\n" >&2
  exit 1
fi

# check if temp-dir exists
if [ -n "${ua_temp_dir}" ] && [ ! -d "${ua_temp_dir}" ]; then
  printf %b "uac: no such file or directory '${ua_temp_dir}'\n" >&2
  exit 1
fi

# get absolute destination directory path
ua_destination_dir=`get_absolute_directory_path "${ua_destination_dir}" 2>/dev/null`

# get operating system if not set by --operating-system
if [ -z "${OPERATING_SYSTEM}" ]; then
  OPERATING_SYSTEM=`get_operating_system`
fi
# check if operating system is supported
if is_valid_operating_system "${OPERATING_SYSTEM}"; then
  true
else
  printf %b "uac: invalid operating system '${OPERATING_SYSTEM}'. \
Use '-s' option to set one.\n\
Try 'uac --help' for more information.\n" >&2
  exit 1
fi

# check if start and end dates are valid
if [ -n "${START_DATE}" ]; then
  START_DATE_EPOCH=`get_epoch_date "${START_DATE}"` || exit 1
  # shellcheck disable=SC2034
  START_DATE_DAYS=`get_days_since_date_until_now "${START_DATE}"`
fi
if [ -n "${END_DATE}" ]; then
  END_DATE_EPOCH=`get_epoch_date "${END_DATE}"` || exit 1
  # shellcheck disable=SC2034
  END_DATE_DAYS=`get_days_since_date_until_now "${END_DATE}"`
  if [ "${START_DATE_EPOCH}" -gt "${END_DATE_EPOCH}" ]; then
    printf %b "uac: start date cannot be greater than end date.\n" >&2
    exit 1
  fi
fi

# check if mount point exists
MOUNT_POINT=`sanitize_path "${MOUNT_POINT}"`
if [ ! -d "${MOUNT_POINT}" ]; then
  printf %b "uac: invalid mount point. \
No such file or directory '${MOUNT_POINT}'\n" >&2
  exit 1
fi

# cannot use not (!) as Solaris 10 does not support it
if is_running_with_root_privileges || "${ua_run_as_non_root}"; then
  true
else
  printf %b "uac: this script requires root privileges to run properly. \
Use '-u' option to disable root user check.\n\
Try 'uac --help' for more information.\n" >&2
  exit 1
fi

# get hostname if not set by --hostname
# useful when running UAC against a mounted image file/disk
if [ -z "${ua_hostname}" ]; then
  ua_hostname=`get_hostname 2>/dev/null`
fi

# check if destination directory's file system supports symlink creation
if [ -n "${ua_temp_dir}" ]; then
  file_system_symlink_support "${ua_temp_dir}" >/dev/null 2>/dev/null \
    && ua_temp_data_dir_symlink_support=true
  TEMP_DATA_DIR="${ua_temp_dir}/uac-data.tmp"
else
  file_system_symlink_support "${ua_destination_dir}" >/dev/null 2>/dev/null \
    && ua_temp_data_dir_symlink_support=true
  TEMP_DATA_DIR="${ua_destination_dir}/uac-data.tmp"
fi

# test the connectivity to remote sftp server
if [ -n "${ua_sftp_destination}" ]; then
  if sftp_transfer_test "${ua_sftp_destination}" "${ua_sftp_port}" \
    "${ua_sftp_identity_file}" >/dev/null; then
    true
  else
    exit 1
  fi
fi

# test the connectivity to S3 presigned url
if [ -n "${ua_s3_presigned_url}" ]; then
  if eval "curl --version" >/dev/null 2>/dev/null; then
    if s3_presigned_url_transfer_test "${ua_s3_presigned_url}"; then
      true
    else
      exit 1
    fi
  else
    printf %b "uac: cannot transfer to S3 presigned URL because 'curl' \
tool was not found.\n"
    exit 1
  fi
fi

# test the connectivity to Azure Blob Storage SAS url
if [ -n "${ua_azure_storage_sas_url}" ]; then
  if eval "curl --version" >/dev/null 2>/dev/null; then
    if azure_storage_sas_url_transfer_test "${ua_azure_storage_sas_url}"; then
      true
    else
      exit 1
    fi
  else
    printf %b "uac: cannot transfer to Azure Blob Storage SAS URL because 'curl' \
tool was not found.\n"
    exit 1
  fi
fi

# test the connectivity to IBM Cloud Object Storage url
if [ -n "${ua_ibm_cos_url}" ]; then
  if [ -n "${ua_ibm_cloud_api_key}" ]; then
    if eval "curl --version" >/dev/null 2>/dev/null; then
      if ibm_cos_transfer_test "${ua_ibm_cos_url}" "${ua_ibm_cloud_api_key}"; then
        true
      else
        exit 1
      fi
    else
      printf %b "uac: cannot transfer to IBM Cloud Object Storage because 'curl' \
tool was not found.\n"
      exit 1
    fi
  else
    printf %b "uac: cannot transfer to IBM Cloud Object Storage. No API \
key / Bearer token provided. Please use --ibm-cloud-api-key option to \
provide one.\n"
      exit 1
  fi
fi

# remove any existing (old) collected data
if [ -d "${TEMP_DATA_DIR}" ]; then
  rm -rf "${TEMP_DATA_DIR}" >/dev/null
  if [ "$?" -gt 0 ]; then
    printf %b "uac: cannot remove old temporary data directory from previous \
collection '${TEMP_DATA_DIR}'.\n"
    exit 1
  fi
fi

# create temporary directory
mkdir "${TEMP_DATA_DIR}" >/dev/null
if [ "$?" -gt 0 ]; then
  printf %b "uac: cannot create temporary data directory '${TEMP_DATA_DIR}'.\n"
  exit 1
fi

# clean up and exit if SIGINT (ctrl-c) is sent
trap terminate INT

# set log files
# shellcheck disable=SC2034
UAC_LOG_FILE="${TEMP_DATA_DIR}/uac.log"
UAC_STDERR_LOG_FILE="${TEMP_DATA_DIR}/uac.log.stderr"

# get current user
ua_current_user=`get_current_user 2>>"${UAC_STDERR_LOG_FILE}"`
# get system arch
SYSTEM_ARCH=`get_system_arch 2>>"${UAC_STDERR_LOG_FILE}"`

# add local 'bin' directory to path
PATH="${UAC_DIR}/bin/${OPERATING_SYSTEM}/${SYSTEM_ARCH}:${PATH}"
# add 'avml' tool directory to path
PATH="${UAC_DIR}/tools/avml/bin/${OPERATING_SYSTEM}/${SYSTEM_ARCH}:${PATH}"
# add 'linux_procmemdump.sh' tool directory to path
PATH="${UAC_DIR}/tools/linux_procmemdump.sh:${PATH}"
export PATH

printf %b "--------------------------------------------------------------------------------\n"
printf %b "  __   __ _______ _______ \n"
printf %b " |: | |  |:  _   |:  ____|\n"
printf %b " |  |_|  |  | |  |  |____ \n"
printf %b " |_______|__| |__|_______|\n"
printf %b "\n"
printf %b " Unix-like Artifacts Collector ${UAC_VERSION}\n"
printf %b "--------------------------------------------------------------------------------\n"
printf %b "Operating System    : ${OPERATING_SYSTEM}\n"
printf %b "System Architecture : ${SYSTEM_ARCH}\n"
printf %b "Hostname            : ${ua_hostname}\n"
printf %b "Mount Point         : ${MOUNT_POINT}\n"
printf %b "Running as          : ${ua_current_user}\n"
printf %b "Temp Directory      : ${TEMP_DATA_DIR}\n"
printf %b "--------------------------------------------------------------------------------\n"

# start uac.log file
log_message INFO "UAC (Unix-like Artifacts Collector) ${UAC_VERSION}"
log_message INFO "Command line: ${ua_command_line}"
log_message INFO "Operating system: ${OPERATING_SYSTEM}"
log_message INFO "System architecture: ${SYSTEM_ARCH}"
log_message INFO "Hostname: ${ua_hostname}"
log_message INFO "Mount point: ${MOUNT_POINT}"
log_message INFO "Running as: ${ua_current_user}"
log_message INFO "Date range start: ${START_DATE}"
log_message INFO "Date range end: ${END_DATE}"
log_message INFO "Case number: ${ua_case_number}"
log_message INFO "Evidence number: ${ua_evidence_number}"
log_message INFO "Description: ${ua_evidence_description}"
log_message INFO "Examiner: ${ua_examiner}"
log_message INFO "Notes: ${ua_notes}"
log_message INFO "Temp directory: ${TEMP_DATA_DIR}"
log_message INFO "Current PID: ${$}"

# global exclusions from uac.conf
log_message INFO "Loading uac.conf settings"
log_message INFO "Global exclude path pattern: ${GLOBAL_EXCLUDE_PATH_PATTERN}"
log_message INFO "Global exclude name pattern: ${GLOBAL_EXCLUDE_NAME_PATTERN}"
log_message INFO "Global exclude file system: ${GLOBAL_EXCLUDE_FILE_SYSTEM}"
# get mount points to globally exclude from collection
GLOBAL_EXCLUDE_MOUNT_POINT=""
if [ -n "${GLOBAL_EXCLUDE_FILE_SYSTEM}" ]; then
  GLOBAL_EXCLUDE_MOUNT_POINT=`get_mount_point_by_file_system \
    "${GLOBAL_EXCLUDE_FILE_SYSTEM}" 2>>"${UAC_STDERR_LOG_FILE}"`
fi
log_message INFO "Global exclude mount point: ${GLOBAL_EXCLUDE_MOUNT_POINT}"
log_message INFO "Hash algorithm: ${HASH_ALGORITHM}"
log_message INFO "Enable find mtime: ${ENABLE_FIND_MTIME}"
log_message INFO "Enable find atime: ${ENABLE_FIND_ATIME}"
log_message INFO "Enable find ctime: ${ENABLE_FIND_CTIME}"

# check available system tools
log_message INFO "Checking available system tools"
check_available_system_tools >/dev/null 2>>"${UAC_STDERR_LOG_FILE}"

log_message INFO "'find' opperators support: ${FIND_OPERATORS_SUPPORT}"
log_message INFO "'find -path' support: ${FIND_PATH_SUPPORT}"
log_message INFO "'find -type' support: ${FIND_TYPE_SUPPORT}"
log_message INFO "'find -maxdepth' support: ${FIND_MAXDEPTH_SUPPORT}"
log_message INFO "'find -size' support: ${FIND_SIZE_SUPPORT}"
log_message INFO "'find -perm' support: ${FIND_PERM_SUPPORT}"
log_message INFO "'find -atime' support: ${FIND_ATIME_SUPPORT}"
log_message INFO "'find -mtime' support: ${FIND_MTIME_SUPPORT}"
log_message INFO "'find -ctime' support: ${FIND_CTIME_SUPPORT}"
log_message INFO "MD5 hashing tool: ${MD5_HASHING_TOOL}"
log_message INFO "SHA1 hashing tool: ${SHA1_HASHING_TOOL}"
log_message INFO "SHA256 hashing tool: ${SHA256_HASHING_TOOL}"
log_message INFO "'gzip' tool available: ${GZIP_TOOL_AVAILABLE}"
log_message INFO "'perl' tool available: ${PERL_TOOL_AVAILABLE}"
log_message INFO "'tar' tool available: ${TAR_TOOL_AVAILABLE}"
log_message INFO "'stat' tool available: ${STAT_TOOL_AVAILABLE}"
log_message INFO "'stat' btime support: ${STAT_BTIME_SUPPORT}"
log_message INFO "'statx' tool available: ${STATX_TOOL_AVAILABLE}"

log_message INFO "PATH: ${PATH}"

# add UAC_DIR abd TEMP_DATA_DIR to GLOBAL_EXCLUDE_PATH_PATTERN
if [ -n "${GLOBAL_EXCLUDE_PATH_PATTERN}" ]; then
  GLOBAL_EXCLUDE_PATH_PATTERN="${GLOBAL_EXCLUDE_PATH_PATTERN},${UAC_DIR},${TEMP_DATA_DIR}"
else
  GLOBAL_EXCLUDE_PATH_PATTERN="${UAC_DIR},${TEMP_DATA_DIR}"
fi

# get all user/home list
# shellcheck disable=SC2034
USER_HOME_LIST=`get_user_home_list 2>>"${UAC_STDERR_LOG_FILE}"`
# get user/home list skipping users with non-interactive shells
# shellcheck disable=SC2034
VALID_SHELL_ONLY_USER_HOME_LIST=`get_user_home_list true 2>>"${UAC_STDERR_LOG_FILE}"`

# acquisition start date
ua_acq_start_date=`date "+%a %b %d %H:%M:%S %Y %z" 2>>"${UAC_STDERR_LOG_FILE}"`
# acquisition start epoch date
ua_acq_start_date_epoch=`get_epoch_date 2>>"${UAC_STDERR_LOG_FILE}"`

log_message INFO "Artifacts collection started"
printf %b "Artifacts collection started...\n"

# create artifact list
create_artifact_list "${ua_artifacts}" \
  >"${TEMP_DATA_DIR}/.artifacts.tmp" \
  2>>"${UAC_STDERR_LOG_FILE}"

ua_progress_current=0
ua_progress_total=`wc -l "${TEMP_DATA_DIR}/.artifacts.tmp" | awk '{print $1}'`

# enable debug mode if it is set to true
${ua_debug_mode} && set -x

# shellcheck disable=SC2162
while read ua_artifact_file || [ -n "${ua_artifact_file}" ]; do
  log_message INFO "Parsing artifacts file '${ua_artifact_file}'"
  # shellcheck disable=SC2003,SC2086
  ua_progress_current=`expr ${ua_progress_current} + 1`
  ua_progress_timestamp=`date "+%Y-%m-%d %H:%M:%S %z"`
  printf "[%03d/%03d] %b %b\n" "${ua_progress_current}" \
    "${ua_progress_total}" "${ua_progress_timestamp}" "${ua_artifact_file}"
  ua_artifacts_root_output_directory=`dirname "${ua_artifact_file}"`
  parse_artifacts_file "${UAC_DIR}/artifacts/${ua_artifact_file}" \
    "${ua_artifacts_root_output_directory}"
  echo "${ua_artifacts_root_output_directory}" >>"${TEMP_DATA_DIR}/.output_file.tmp"
done <"${TEMP_DATA_DIR}/.artifacts.tmp" 2>>"${UAC_STDERR_LOG_FILE}"

# disable debug mode
${ua_debug_mode} && set +x

# acquisition end date
ua_acq_end_date=`date "+%a %b %d %H:%M:%S %Y %z" 2>>"${UAC_STDERR_LOG_FILE}"`
# acquisition end epoch date
ua_acq_end_date_epoch=`get_epoch_date 2>>"${UAC_STDERR_LOG_FILE}"`
# get current date and time string (it will be part of the output file name)
ua_current_date_time=`date "+%Y%m%d%H%M%S"`

# calculate running time
# shellcheck disable=SC2003
ua_total_running_time=`expr "${ua_acq_end_date_epoch}" - "${ua_acq_start_date_epoch}"`

printf %b "--------------------------------------------------------------------------------\n"
log_message INFO "Artifacts collection complete. \
Total execution time: ${ua_total_running_time} seconds"

printf %b "Artifacts collection complete. \
Total execution time: ${ua_total_running_time} seconds\n"

# output file/directory name
ua_output_base_name="uac-${ua_hostname}-${OPERATING_SYSTEM}-${ua_current_date_time}"
# output file/directory name
ua_output_name="${ua_output_base_name}"
# acquisition log file name
ua_acquisition_log="${ua_output_base_name}.log"
# output file hash
ua_output_file_hash="-"

# sort and uniq
sort_uniq_file "${TEMP_DATA_DIR}/.output_file.tmp" 2>>"${UAC_STDERR_LOG_FILE}"
# add uac.log to the list of files to be archived/copied within the output file
echo "uac.log" >>"${TEMP_DATA_DIR}/.output_file.tmp"
# add uac.log.stderr to the list of files to be archived/copied within the output file
echo "uac.log.stderr" >>"${TEMP_DATA_DIR}/.output_file.tmp"

if ${TAR_TOOL_AVAILABLE}; then

  if [ -f "${TEMP_DATA_DIR}/.files.tmp" ]; then
    # sort and uniq
    sort_uniq_file "${TEMP_DATA_DIR}/.files.tmp" 2>>"${UAC_STDERR_LOG_FILE}"
    if ${ua_temp_data_dir_symlink_support}; then
      # create symbolic link to /
      ln -s "/" "${TEMP_DATA_DIR}/[root]" 2>>"${UAC_STDERR_LOG_FILE}"
    else
      # copy files to uac-data.tmp/[root]
      printf %b "Copying files to ${TEMP_DATA_DIR}/[root]. Please wait...\n"
      copy_data "${TEMP_DATA_DIR}/.files.tmp" "${TEMP_DATA_DIR}/[root]" \
        2>>"${UAC_STDERR_LOG_FILE}"
    fi
    # add [root] string to the beginning of each entry in .files.tmp
    # and add them to the list of files to be archived within the output file
    sed -e 's:^/:\[root\]/:' "${TEMP_DATA_DIR}/.files.tmp" \
      >>"${TEMP_DATA_DIR}/.output_file.tmp"
  fi

  # archive (and compress) collected artifacts to output file
  printf %b "Creating output file. Please wait...\n"
  cd "${TEMP_DATA_DIR}" || exit 1

  if ${GZIP_TOOL_AVAILABLE}; then
    ua_output_name="${ua_output_base_name}.tar.gz"
    archive_compress_data ".output_file.tmp" \
      "${ua_destination_dir}/${ua_output_name}" 2>/dev/null
  else
    ua_output_name="${ua_output_base_name}.tar"
    archive_data ".output_file.tmp" \
      "${ua_destination_dir}/${ua_output_name}" 2>/dev/null
  fi

  if [ -f "${ua_destination_dir}/${ua_output_name}" ]; then
    printf %b "Output file created '${ua_destination_dir}/${ua_output_name}'\n"
    cd "${UAC_DIR}" || exit 1
    if ${ua_debug_mode}; then
      printf %b "Temporary directory not removed '${TEMP_DATA_DIR}'\n"
    else
      rm -rf "${TEMP_DATA_DIR}" >/dev/null 2>/dev/null
      if [ -d "${TEMP_DATA_DIR}" ]; then
        printf %b "Cannot remove temporary directory '${TEMP_DATA_DIR}'\n"
      fi
    fi
    # hash output file
    printf %b "Hashing output file. Please wait...\n"
    cd "${ua_destination_dir}" || exit 1
    ua_output_file_hash=`${MD5_HASHING_TOOL} "${ua_output_name}"`
    cd "${UAC_DIR}" || exit 1
  else
    printf %b "Cannot create output file\n"
    printf %b "Please check collected artifacts in '${TEMP_DATA_DIR}'\n"
    cd "${UAC_DIR}" && exit 1
  fi
else
  printf %b "'tar' not found. Copying collected artifacts to '${ua_destination_dir}/${ua_output_name}'. Please wait...\n"
  if [ -f "${TEMP_DATA_DIR}/.files.tmp" ]; then
    # sort and uniq
    sort_uniq_file "${TEMP_DATA_DIR}/.files.tmp" 2>>"${UAC_STDERR_LOG_FILE}"
    copy_data "${TEMP_DATA_DIR}/.files.tmp" "${ua_destination_dir}/${ua_output_name}/[root]" \
      2>>"${UAC_STDERR_LOG_FILE}"
  fi

  cd "${TEMP_DATA_DIR}" || exit 1
  copy_data "${TEMP_DATA_DIR}/.output_file.tmp" "${ua_destination_dir}/${ua_output_name}" \
    2>>"${UAC_STDERR_LOG_FILE}"
  ua_file_count=`find "${ua_destination_dir}/${ua_output_name}" -print | wc -l`
  if [ "${ua_file_count}" -gt 2 ]; then
    printf %b "Please check collected artifacts in '${ua_destination_dir}/${ua_output_name}'\n"
    cd "${UAC_DIR}" || exit 1
    if ${ua_debug_mode}; then
      printf %b "Temporary directory not removed '${TEMP_DATA_DIR}'\n"
    else
      rm -rf "${TEMP_DATA_DIR}" >/dev/null 2>/dev/null
      if [ -d "${TEMP_DATA_DIR}" ]; then
          printf %b "Cannot remove temporary directory '${TEMP_DATA_DIR}'\n"
      fi
    fi
  else
    printf %b "Cannot copy collected artifacts\n"
    printf %b "Please check collected artifacts in '${TEMP_DATA_DIR}'\n"
    exit 1
  fi
fi

# create the acquisition log
if create_acquisition_log \
  "${ua_case_number}" \
  "${ua_evidence_number}" \
  "${ua_evidence_description}" \
  "${ua_examiner}" \
  "${ua_notes}" \
  "${ua_hostname}" \
  "${ua_acq_start_date}" \
  "${ua_acq_end_date}" \
  "${ua_output_file_hash}" \
  "${ua_destination_dir}" \
  "${ua_acquisition_log}" 2>/dev/null; then
  printf %b "Acquisition log created '${ua_destination_dir}/${ua_acquisition_log}'\n"
else
  printf %b "Cannot create acquisition log\n"
fi

# transfer output and log file to remote sftp server
if [ -n "${ua_sftp_destination}" ]; then
  if [ -f "${ua_destination_dir}/${ua_output_name}" ] \
    || [ -d "${ua_destination_dir}/${ua_output_name}" ]; then
    printf %b "Transferring output file to remote SFTP server. Please wait...\n"
    if sftp_transfer "${ua_destination_dir}/${ua_output_name}" \
      "${ua_sftp_destination}" "${ua_sftp_port}" "${ua_sftp_identity_file}"; then
      printf %b "File transferred successfully\n"
      # delete output file on success transfer
      ${ua_delete_local_on_successful_transfer} \
        && rm -f "${ua_destination_dir}/${ua_output_name}" 2>/dev/null
      printf %b "Transferring log file to remote SFTP server. Please wait...\n"
      if sftp_transfer "${ua_destination_dir}/${ua_acquisition_log}" \
        "${ua_sftp_destination}" "${ua_sftp_port}" "${ua_sftp_identity_file}"; then
        printf %b "File transferred successfully\n"
        # delete log file on success transfer
        ${ua_delete_local_on_successful_transfer} \
          && rm -f "${ua_destination_dir}/${ua_acquisition_log}" 2>/dev/null
      else
        printf %b "Could not transfer log file to remote SFTP server\n"
        exit 1
      fi
    else
      printf %b "Could not transfer output file to remote SFTP server\n"
      exit 1
    fi
  fi
fi

# transfer output and log file to S3 presigned url
if [ -n "${ua_s3_presigned_url}" ]; then
  if [ -f "${ua_destination_dir}/${ua_output_name}" ]; then
    printf %b "Transferring output file to S3 presigned URL. Please wait...\n"
    if s3_presigned_url_transfer "${ua_destination_dir}/${ua_output_name}" \
      "${ua_s3_presigned_url}"; then
      printf %b "File transferred successfully\n"
      # delete output file on success transfer
      ${ua_delete_local_on_successful_transfer} \
        && rm -f "${ua_destination_dir}/${ua_output_name}" 2>/dev/null
    else
      printf %b "Could not transfer output file to S3 presigned URL\n"
      exit 1
    fi
    if [ -n "${ua_s3_presigned_url_log_file}" ]; then
      printf %b "Transferring log file to S3 presigned URL. Please wait...\n"
      if s3_presigned_url_transfer "${ua_destination_dir}/${ua_acquisition_log}" \
        "${ua_s3_presigned_url_log_file}"; then
        printf %b "File transferred successfully\n"
        # delete output file on success transfer
        ${ua_delete_local_on_successful_transfer} \
          && rm -f "${ua_destination_dir}/${ua_acquisition_log}" 2>/dev/null
      else
        printf %b "Could not transfer log file to S3 presigned URL\n"
        exit 1
      fi
    fi
  fi
fi

# transfer output and log file to Azure Storage SAS url
if [ -n "${ua_azure_storage_sas_url}" ]; then
  if [ -f "${ua_destination_dir}/${ua_output_name}" ]; then
    printf %b "Transferring output file to Azure Storage SAS URL. Please wait...\n"
    if azure_storage_sas_url_transfer "${ua_destination_dir}/${ua_output_name}" \
      "${ua_azure_storage_sas_url}"; then
      printf %b "File transferred successfully\n"
      # delete output file on success transfer
      ${ua_delete_local_on_successful_transfer} \
        && rm -f "${ua_destination_dir}/${ua_output_name}" 2>/dev/null
    else
      printf %b "Could not transfer output file to Azure Storage SAS URL\n"
      exit 1
    fi
    if [ -n "${ua_azure_storage_sas_url_log_file}" ]; then
      printf %b "Transferring log file to Azure Storage SAS URL. Please wait...\n"
      if azure_storage_sas_url_transfer "${ua_destination_dir}/${ua_acquisition_log}" \
        "${ua_azure_storage_sas_url_log_file}"; then
        printf %b "File transferred successfully\n"
        # delete output file on success transfer
        ${ua_delete_local_on_successful_transfer} \
          && rm -f "${ua_destination_dir}/${ua_acquisition_log}" 2>/dev/null
      else
        printf %b "Could not transfer log file to Azure Storage SAS URL\n"
        exit 1
      fi
    fi
  fi
fi

# transfer output and log file to IBM Cloud Object Storage
if [ -n "${ua_ibm_cos_url}" ]; then
  if [ -f "${ua_destination_dir}/${ua_output_name}" ]; then
    printf %b "Transferring output file to IBM Cloud Object Storage. Please wait...\n"
    if ibm_cos_transfer "${ua_destination_dir}/${ua_output_name}" \
      "${ua_ibm_cos_url}" "${ua_ibm_cloud_api_key}"; then
      printf %b "File transferred successfully\n"
      # delete output file on success transfer
      ${ua_delete_local_on_successful_transfer} \
        && rm -f "${ua_destination_dir}/${ua_output_name}" 2>/dev/null
    else
      printf %b "Could not transfer output file to IBM Cloud Object Storage\n"
      exit 1
    fi
    if [ -n "${ua_ibm_cos_url_log_file}" ]; then
      printf %b "Transferring log file to IBM Cloud Object Storage. Please wait...\n"
      if ibm_cos_transfer "${ua_destination_dir}/${ua_acquisition_log}" \
        "${ua_ibm_cos_url_log_file}" "${ua_ibm_cloud_api_key}"; then
        printf %b "File transferred successfully\n"
        # delete output file on success transfer
        ${ua_delete_local_on_successful_transfer} \
          && rm -f "${ua_destination_dir}/${ua_acquisition_log}" 2>/dev/null
      else
        printf %b "Could not transfer log file to IBM Cloud Object Storage\n"
        exit 1
      fi
    fi
  fi
fi

exit 0
