diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f570dd9..a99a7f0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: - repo: https://github.com/scop/pre-commit-shfmt rev: v3.8.0-1 hooks: - - id: shfmt + - id: shfmt - repo: https://github.com/shellcheck-py/shellcheck-py rev: v0.10.0.1 diff --git a/bin/winapps b/bin/winapps index 228f397..bc5518d 100755 --- a/bin/winapps +++ b/bin/winapps @@ -1,77 +1,74 @@ #!/usr/bin/env bash if [ ! -f "$HOME/.config/winapps/winapps.conf" ] && [ ! -f "$HOME/.winapps" ]; then - echo "You need to create a ~/.config/winapps/winapps.conf configuration. Exiting..." - exit + echo "You need to create a ~/.config/winapps/winapps.conf configuration. Exiting..." + exit fi DIR="$(dirname "$(readlink -f "$0")")" RUN="$(date)-$RANDOM" if [ ! -d "$HOME/.local/share/winapps" ]; then - mkdir -p "$HOME/.local/share/winapps" + mkdir -p "$HOME/.local/share/winapps" fi RDP_SCALE=100 if [ -f "$HOME/.config/winapps/winapps.conf" ]; then - # shellcheck source=/dev/null - . "$HOME/.config/winapps/winapps.conf" + # shellcheck source=/dev/null + . "$HOME/.config/winapps/winapps.conf" else - # shellcheck source=/dev/null - . "$HOME/.winapps" + # shellcheck source=/dev/null + . "$HOME/.winapps" fi function dprint() { - if [ "$DEBUG" = "true" ]; then - echo "[$RUN] $1" >>"$HOME/.local/share/winapps/winapps.log" - fi + if [ "$DEBUG" = "true" ]; then + echo "[$RUN] $1" >>"$HOME/.local/share/winapps/winapps.log" + fi } dprint "START" if [ -f "$HOME/.local/share/winapps/run" ]; then - LAST_RAN=$(stat -t -c %Y "$HOME/.local/share/winapps/run") - dprint "LAST_RAN:${LAST_RAN}" - touch "$HOME/.local/share/winapps/run" - THIS_RUN=$(stat -t -c %Y "$HOME/.local/share/winapps/run") - dprint "THIS_RUN:$THIS_RUN" - if ((THIS_RUN - LAST_RAN < 2)); then - exit - fi + LAST_RAN=$(stat -t -c %Y "$HOME/.local/share/winapps/run") + dprint "LAST_RAN:${LAST_RAN}" + touch "$HOME/.local/share/winapps/run" + THIS_RUN=$(stat -t -c %Y "$HOME/.local/share/winapps/run") + dprint "THIS_RUN:$THIS_RUN" + if ((THIS_RUN - LAST_RAN < 2)); then + exit + fi else - touch "$HOME/.local/share/winapps/run" + touch "$HOME/.local/share/winapps/run" fi if [ -z "${FREERDP_COMMAND}" ]; then - if command -v xfreerdp &> /dev/null - then - FREERDP_COMMAND="xfreerdp" - elif command -v xfreerdp3 &> /dev/null - then - FREERDP_COMMAND="xfreerdp3" - fi -elif command -v "$FREERDP_COMMAND" &> /dev/null -then - dprint "Using custom freerdp command $FREERDP_COMMAND" + if command -v xfreerdp &>/dev/null; then + FREERDP_COMMAND="xfreerdp" + elif command -v xfreerdp3 &>/dev/null; then + FREERDP_COMMAND="xfreerdp3" + fi +elif command -v "$FREERDP_COMMAND" &>/dev/null; then + dprint "Using custom freerdp command $FREERDP_COMMAND" else - echo "You have supplied a custom FreeRDP command, but the command is not available." - exit + echo "You have supplied a custom FreeRDP command, but the command is not available." + exit fi if [ -z "$RDP_IP" ]; then - if groups | grep -vq libvirt; then - echo "You are not a member of the libvirt group. Run the below then reboot." - echo " sudo usermod -a -G libvirt $(whoami)" - echo " sudo usermod -a -G kvm $(whoami)" - exit - fi - if ! virsh list --state-running --name | grep -q '^RDPWindows$'; then - echo "RDPWindows is not running. Please run:" - echo " virsh start RDPWindows" - exit - fi - RDP_IP=$(virsh net-dhcp-leases default | grep "RDPWindows" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}') + if groups | grep -vq libvirt; then + echo "You are not a member of the libvirt group. Run the below then reboot." + echo " sudo usermod -a -G libvirt $(whoami)" + echo " sudo usermod -a -G kvm $(whoami)" + exit + fi + if ! virsh list --state-running --name | grep -q '^RDPWindows$'; then + echo "RDPWindows is not running. Please run:" + echo " virsh start RDPWindows" + exit + fi + RDP_IP=$(virsh net-dhcp-leases default | grep "RDPWindows" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}') fi dprint "1:$1" @@ -82,128 +79,128 @@ dprint "@:${@}" MULTI_FLAG="+span" if [ "$MULTIMON" = "true" ]; then - MULTI_FLAG="/multimon" + MULTI_FLAG="/multimon" fi # Append additional flags or parameters to FreeRDP -if [[ -n "$RDP_FLAGS" ]]; then - FREERDP_COMMAND="$FREERDP_COMMAND $RDP_FLAGS" +if [[ -n $RDP_FLAGS ]]; then + FREERDP_COMMAND="$FREERDP_COMMAND $RDP_FLAGS" fi if [ "$1" = "check" ]; then - # Open File Explorer - dprint "CHECK" - COMMAND=( - "${FREERDP_COMMAND}" - "/d:${RDP_DOMAIN}" - "/u:${RDP_USER}" - "/p:${RDP_PASS}" - "/scale:${RDP_SCALE}" - "+auto-reconnect" - "+home-drive" - "-wallpaper" - "+dynamic-resolution" - "${MULTI_FLAG}" - "/app:program:explorer.exe" - "/v:${RDP_IP}" - ) - "${COMMAND[@]}" + # Open File Explorer + dprint "CHECK" + COMMAND=( + "${FREERDP_COMMAND}" + "/d:${RDP_DOMAIN}" + "/u:${RDP_USER}" + "/p:${RDP_PASS}" + "/scale:${RDP_SCALE}" + "+auto-reconnect" + "+home-drive" + "-wallpaper" + "+dynamic-resolution" + "${MULTI_FLAG}" + "/app:program:explorer.exe" + "/v:${RDP_IP}" + ) + "${COMMAND[@]}" elif [ "$1" = "windows" ]; then - # Open Virtual Machine - dprint "WINDOWS" - COMMAND=( - "${FREERDP_COMMAND}" - "/d:${RDP_DOMAIN}" - "/u:${RDP_USER}" - "/p:${RDP_PASS}" - "/scale:${RDP_SCALE}" - "+dynamic-resolution" - "+auto-reconnect" - "+home-drive" - "/wm-class:\"Microsoft Windows\"" - "/v:${RDP_IP}" - ) - # Run the command in the background, redirecting both stdout and stderr to /dev/null - "${COMMAND[@]}" 1>/dev/null 2>&1 & + # Open Virtual Machine + dprint "WINDOWS" + COMMAND=( + "${FREERDP_COMMAND}" + "/d:${RDP_DOMAIN}" + "/u:${RDP_USER}" + "/p:${RDP_PASS}" + "/scale:${RDP_SCALE}" + "+dynamic-resolution" + "+auto-reconnect" + "+home-drive" + '/wm-class:"Microsoft Windows"' + "/v:${RDP_IP}" + ) + # Run the command in the background, redirecting both stdout and stderr to /dev/null + "${COMMAND[@]}" 1>/dev/null 2>&1 & elif [ "$1" = "manual" ]; then - # Open Specified Application - dprint "MANUAL:${2}" - COMMAND=( - "${FREERDP_COMMAND}" - "/d:${RDP_DOMAIN}" - "/u:${RDP_USER}" - "/p:${RDP_PASS}" - "/scale:${RDP_SCALE}" - "+auto-reconnect" - "+home-drive" - "+dynamic-resolution" - "${MULTI_FLAG}" - "/app:program:${2}" - "/v:${RDP_IP}" - ) - # Run the command in the background, redirecting both stdout and stderr to /dev/null - "${COMMAND[@]}" 1>/dev/null 2>&1 & + # Open Specified Application + dprint "MANUAL:${2}" + COMMAND=( + "${FREERDP_COMMAND}" + "/d:${RDP_DOMAIN}" + "/u:${RDP_USER}" + "/p:${RDP_PASS}" + "/scale:${RDP_SCALE}" + "+auto-reconnect" + "+home-drive" + "+dynamic-resolution" + "${MULTI_FLAG}" + "/app:program:${2}" + "/v:${RDP_IP}" + ) + # Run the command in the background, redirecting both stdout and stderr to /dev/null + "${COMMAND[@]}" 1>/dev/null 2>&1 & elif [ "$1" != "install" ]; then - dprint "DIR:${DIR}" - if [ -e "${DIR}/../apps/$1/info" ]; then - # shellcheck disable=SC1090 - . "${DIR}/../apps/$1/info" - ICON="${DIR}/../apps/$1/icon.svg" - elif [ -e "$HOME/.local/share/winapps/apps/$1/info" ]; then - # shellcheck disable=SC1090 - . "$HOME/.local/share/winapps/apps/$1/info" - ICON="$HOME/.local/share/winapps/apps/$1/icon.svg" - elif [ -e "/usr/local/share/winapps/apps/$1/info" ]; then - # shellcheck disable=SC1090 - . "/usr/local/share/winapps/apps/$1/info" - ICON="/usr/local/share/winapps/apps/$1/icon.svg" - else - echo "You need to run 'installer.sh' first." - exit 1 - fi - if [ -n "$2" ]; then - dprint "HOME:$HOME" - FILE=$(echo "$2" | sed 's|'"$HOME"'|\\\\tsclient\\home|;s|/|\\|g;s|\\|\\\\|g') - dprint "FILE:${FILE}" - # shellcheck disable=SC2140 - COMMAND=( - "${FREERDP_COMMAND}" - "/d:${RDP_DOMAIN}" - "/u:${RDP_USER}" - "/p:${RDP_PASS}" - "/scale:${RDP_SCALE}" - "+auto-reconnect" - "+clipboard" - "+home-drive" - "-wallpaper" - "+dynamic-resolution" - "${MULTI_FLAG}" - "/wm-class:${FULL_NAME}" - "/app:program:${WIN_EXECUTABLE},icon:${ICON},name:${FULL_NAME},cmd:\"${FILE}\"" - "/v:${RDP_IP}" - ) - # Run the command in the background, redirecting both stdout and stderr to /dev/null - echo "${COMMAND[@]}" #1>/dev/null 2>&1 & - else - COMMAND=( - "${FREERDP_COMMAND}" - "/d:${RDP_DOMAIN}" - "/u:${RDP_USER}" - "/p:${RDP_PASS}" - "/scale:${RDP_SCALE}" - "+auto-reconnect" - "+clipboard" - "+home-drive" - "-wallpaper" - "+dynamic-resolution" - "${MULTI_FLAG}" - "/wm-class:${FULL_NAME}" - "/app:program:${WIN_EXECUTABLE},icon:${ICON},name:${FULL_NAME}" - "/v:${RDP_IP}" - ) - # Run the command in the background, redirecting both stdout and stderr to /dev/null - "${COMMAND[@]}" 1>/dev/null 2>&1 & - fi + dprint "DIR:${DIR}" + if [ -e "${DIR}/../apps/$1/info" ]; then + # shellcheck disable=SC1090 + . "${DIR}/../apps/$1/info" + ICON="${DIR}/../apps/$1/icon.svg" + elif [ -e "$HOME/.local/share/winapps/apps/$1/info" ]; then + # shellcheck disable=SC1090 + . "$HOME/.local/share/winapps/apps/$1/info" + ICON="$HOME/.local/share/winapps/apps/$1/icon.svg" + elif [ -e "/usr/local/share/winapps/apps/$1/info" ]; then + # shellcheck disable=SC1090 + . "/usr/local/share/winapps/apps/$1/info" + ICON="/usr/local/share/winapps/apps/$1/icon.svg" + else + echo "You need to run 'installer.sh' first." + exit 1 + fi + if [ -n "$2" ]; then + dprint "HOME:$HOME" + FILE=$(echo "$2" | sed 's|'"$HOME"'|\\\\tsclient\\home|;s|/|\\|g;s|\\|\\\\|g') + dprint "FILE:${FILE}" + # shellcheck disable=SC2140 + COMMAND=( + "${FREERDP_COMMAND}" + "/d:${RDP_DOMAIN}" + "/u:${RDP_USER}" + "/p:${RDP_PASS}" + "/scale:${RDP_SCALE}" + "+auto-reconnect" + "+clipboard" + "+home-drive" + "-wallpaper" + "+dynamic-resolution" + "${MULTI_FLAG}" + "/wm-class:${FULL_NAME}" + "/app:program:${WIN_EXECUTABLE},icon:${ICON},name:${FULL_NAME},cmd:\"${FILE}\"" + "/v:${RDP_IP}" + ) + # Run the command in the background, redirecting both stdout and stderr to /dev/null + echo "${COMMAND[@]}" #1>/dev/null 2>&1 & + else + COMMAND=( + "${FREERDP_COMMAND}" + "/d:${RDP_DOMAIN}" + "/u:${RDP_USER}" + "/p:${RDP_PASS}" + "/scale:${RDP_SCALE}" + "+auto-reconnect" + "+clipboard" + "+home-drive" + "-wallpaper" + "+dynamic-resolution" + "${MULTI_FLAG}" + "/wm-class:${FULL_NAME}" + "/app:program:${WIN_EXECUTABLE},icon:${ICON},name:${FULL_NAME}" + "/v:${RDP_IP}" + ) + # Run the command in the background, redirecting both stdout and stderr to /dev/null + "${COMMAND[@]}" 1>/dev/null 2>&1 & + fi fi dprint "END" diff --git a/install/inquirer.sh b/install/inquirer.sh index 180fa07..f985422 100755 --- a/install/inquirer.sh +++ b/install/inquirer.sh @@ -1,186 +1,186 @@ #!/usr/bin/env bash ### GLOBAL CONSTANTS ### -declare -r ANSI_LIGHT_BLUE="\033[1;94m" # Light blue text. -declare -r ANSI_LIGHT_GREEN="\033[92m" # Light green text. -declare -r ANSI_CLEAR_TEXT="\033[0m" # Default text. -declare -r DIALOG_HEIGHT=14 # Height of dialog window. -declare -r TEXT_WIDTH_OFFSET=4 # Offset for fitting title text. -declare -r CHK_OPTION_WIDTH_OFFSET=10 # Offset for fitting options. -declare -r MNU_OPTION_WIDTH_OFFSET=7 # Offset for fitting options. +declare -r ANSI_LIGHT_BLUE="\033[1;94m" # Light blue text. +declare -r ANSI_LIGHT_GREEN="\033[92m" # Light green text. +declare -r ANSI_CLEAR_TEXT="\033[0m" # Default text. +declare -r DIALOG_HEIGHT=14 # Height of dialog window. +declare -r TEXT_WIDTH_OFFSET=4 # Offset for fitting title text. +declare -r CHK_OPTION_WIDTH_OFFSET=10 # Offset for fitting options. +declare -r MNU_OPTION_WIDTH_OFFSET=7 # Offset for fitting options. ### FUNCTIONS ### function inqMenu() { - # DECLARE VARIABLES. - # Variables created from function arguments: - declare DIALOG_TEXT="$1" # Dialog heading. - declare INPUT_OPTIONS_VAR="$2" # Input variable name. - declare RETURN_STRING_VAR="$3" # Output variable name. - declare -n INPUT_OPTIONS="$INPUT_OPTIONS_VAR" # Input array nameref. - declare -n RETURN_STRING="$RETURN_STRING_VAR" # Output string nameref. - # Note: namerefs allow changes made through the nameref to affect the - # referenced variable, even across different scopes like function calls. + # DECLARE VARIABLES. + # Variables created from function arguments: + declare DIALOG_TEXT="$1" # Dialog heading. + declare INPUT_OPTIONS_VAR="$2" # Input variable name. + declare RETURN_STRING_VAR="$3" # Output variable name. + declare -n INPUT_OPTIONS="$INPUT_OPTIONS_VAR" # Input array nameref. + declare -n RETURN_STRING="$RETURN_STRING_VAR" # Output string nameref. + # Note: namerefs allow changes made through the nameref to affect the + # referenced variable, even across different scopes like function calls. - # Other variables: - declare TRIMMED_OPTIONS=() # Input array post-trimming. - declare PADDED_OPTIONS=() # Input array with extra white space. - declare DIALOG_OPTIONS=() # Input array for options dialog. - declare DIALOG_WIDTH=0 # Width of dialog window. - declare OPTION_NUMBER=0 # Number of options in dialog window. - declare SELECTED_OPTIONS_STRING="" # Output value from dialog window. + # Other variables: + declare TRIMMED_OPTIONS=() # Input array post-trimming. + declare PADDED_OPTIONS=() # Input array with extra white space. + declare DIALOG_OPTIONS=() # Input array for options dialog. + declare DIALOG_WIDTH=0 # Width of dialog window. + declare OPTION_NUMBER=0 # Number of options in dialog window. + declare SELECTED_OPTIONS_STRING="" # Output value from dialog window. - # MAIN LOGIC. - # Trim leading and trailing white space for each option. - for OPTION in "${INPUT_OPTIONS[@]}"; do - TRIMMED_OPTIONS+=("$(echo "$OPTION" | sed 's/^[ \t]*//;s/[ \t]*$//')") - done + # MAIN LOGIC. + # Trim leading and trailing white space for each option. + for OPTION in "${INPUT_OPTIONS[@]}"; do + TRIMMED_OPTIONS+=("$(echo "$OPTION" | sed 's/^[ \t]*//;s/[ \t]*$//')") + done - # Find the length of the longest option to set the dialog width. - for OPTION in "${TRIMMED_OPTIONS[@]}"; do - if [ "${#OPTION}" -gt "$DIALOG_WIDTH" ]; then - DIALOG_WIDTH=${#OPTION} - fi - done + # Find the length of the longest option to set the dialog width. + for OPTION in "${TRIMMED_OPTIONS[@]}"; do + if [ "${#OPTION}" -gt "$DIALOG_WIDTH" ]; then + DIALOG_WIDTH=${#OPTION} + fi + done - # Apply the offset value to the dialog width. - DIALOG_WIDTH=$((DIALOG_WIDTH + MNU_OPTION_WIDTH_OFFSET)) + # Apply the offset value to the dialog width. + DIALOG_WIDTH=$((DIALOG_WIDTH + MNU_OPTION_WIDTH_OFFSET)) - # Adjust the dialog width again if the dialog text is longer. - if [ "$DIALOG_WIDTH" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then - DIALOG_WIDTH="$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))" - fi + # Adjust the dialog width again if the dialog text is longer. + if [ "$DIALOG_WIDTH" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then + DIALOG_WIDTH="$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))" + fi - # Pad option text with trailing white space to left-align all options. - for OPTION in "${TRIMMED_OPTIONS[@]}"; do - local PAD_LENGTH=$((DIALOG_WIDTH - MNU_OPTION_WIDTH_OFFSET - ${#OPTION})) - # shellcheck disable=SC2155 - local PADDED_OPTION="${OPTION}$(printf '%*s' $PAD_LENGTH)" - PADDED_OPTIONS+=("$PADDED_OPTION") - done + # Pad option text with trailing white space to left-align all options. + for OPTION in "${TRIMMED_OPTIONS[@]}"; do + local PAD_LENGTH=$((DIALOG_WIDTH - MNU_OPTION_WIDTH_OFFSET - ${#OPTION})) + # shellcheck disable=SC2155 + local PADDED_OPTION="${OPTION}$(printf '%*s' $PAD_LENGTH)" + PADDED_OPTIONS+=("$PADDED_OPTION") + done - # Convert options into the appropriate format for a 'dialog' menu. - for PADDED_OPTION in "${PADDED_OPTIONS[@]}"; do - DIALOG_OPTIONS+=("$PADDED_OPTION" "") - done + # Convert options into the appropriate format for a 'dialog' menu. + for PADDED_OPTION in "${PADDED_OPTIONS[@]}"; do + DIALOG_OPTIONS+=("$PADDED_OPTION" "") + done - # Store the number of options. - OPTION_NUMBER="${#INPUT_OPTIONS[@]}" + # Store the number of options. + OPTION_NUMBER="${#INPUT_OPTIONS[@]}" - # Produce checkbox. - # The output string contains options delimited by spaces. - # Each option is enclosed in double quotes within the output string. - # For example: '"Option 1 " "The Second Option " " Option Number 3 "' - SELECTED_OPTIONS_STRING=$(dialog \ ---keep-tite \ ---clear \ ---no-shadow \ ---menu \ -"$DIALOG_TEXT" \ -"$DIALOG_HEIGHT" \ -"$DIALOG_WIDTH" \ -"$OPTION_NUMBER" \ -"${DIALOG_OPTIONS[@]}" \ -2>&1 >/dev/tty) || exit 0 + # Produce checkbox. + # The output string contains options delimited by spaces. + # Each option is enclosed in double quotes within the output string. + # For example: '"Option 1 " "The Second Option " " Option Number 3 "' + SELECTED_OPTIONS_STRING=$(dialog \ + --keep-tite \ + --clear \ + --no-shadow \ + --menu \ + "$DIALOG_TEXT" \ + "$DIALOG_HEIGHT" \ + "$DIALOG_WIDTH" \ + "$OPTION_NUMBER" \ + "${DIALOG_OPTIONS[@]}" \ + 2>&1 >/dev/tty) || exit 0 - # Remove white space added previously. - RETURN_STRING=$(echo "$SELECTED_OPTIONS_STRING" | \ - sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + # Remove white space added previously. + RETURN_STRING=$(echo "$SELECTED_OPTIONS_STRING" | + sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - # Remove escapes (introduced by 'dialog' if options have parentheses). - RETURN_STRING="${RETURN_STRING//\\/}" # ${variable//search/replace} + # Remove escapes (introduced by 'dialog' if options have parentheses). + RETURN_STRING="${RETURN_STRING//\\/}" # ${variable//search/replace} - # Display question and response. - echo -e "${ANSI_LIGHT_GREEN}Q) ${ANSI_CLEAR_TEXT}${ANSI_LIGHT_BLUE}${DIALOG_TEXT}${ANSI_CLEAR_TEXT} --> ${ANSI_LIGHT_GREEN}${RETURN_STRING}${ANSI_CLEAR_TEXT}" + # Display question and response. + echo -e "${ANSI_LIGHT_GREEN}Q) ${ANSI_CLEAR_TEXT}${ANSI_LIGHT_BLUE}${DIALOG_TEXT}${ANSI_CLEAR_TEXT} --> ${ANSI_LIGHT_GREEN}${RETURN_STRING}${ANSI_CLEAR_TEXT}" } function inqChkBx() { - # DECLARE VARIABLES. - # Variables created from function arguments: - declare DIALOG_TEXT="$1" # Dialog heading. - declare INPUT_OPTIONS_VAR="$2" # Input variable name. - declare RETURN_ARRAY_VAR="$3" # Output variable name. - declare -n INPUT_OPTIONS="$INPUT_OPTIONS_VAR" # Input array nameref. - declare -n RETURN_ARRAY="$RETURN_ARRAY_VAR" # Output array nameref. - # Note: namerefs allow changes made through the nameref to affect the - # referenced variable, even across different scopes like function calls. + # DECLARE VARIABLES. + # Variables created from function arguments: + declare DIALOG_TEXT="$1" # Dialog heading. + declare INPUT_OPTIONS_VAR="$2" # Input variable name. + declare RETURN_ARRAY_VAR="$3" # Output variable name. + declare -n INPUT_OPTIONS="$INPUT_OPTIONS_VAR" # Input array nameref. + declare -n RETURN_ARRAY="$RETURN_ARRAY_VAR" # Output array nameref. + # Note: namerefs allow changes made through the nameref to affect the + # referenced variable, even across different scopes like function calls. - # Other variables: - declare TRIMMED_OPTIONS=() # Input array post-trimming. - declare PADDED_OPTIONS=() # Input array with extra white space. - declare DIALOG_OPTIONS=() # Input array for options dialog. - declare DIALOG_WIDTH=0 # Width of dialog window. - declare OPTION_NUMBER=0 # Number of options in dialog window. - declare SELECTED_OPTIONS_STRING="" # Output value from dialog window. + # Other variables: + declare TRIMMED_OPTIONS=() # Input array post-trimming. + declare PADDED_OPTIONS=() # Input array with extra white space. + declare DIALOG_OPTIONS=() # Input array for options dialog. + declare DIALOG_WIDTH=0 # Width of dialog window. + declare OPTION_NUMBER=0 # Number of options in dialog window. + declare SELECTED_OPTIONS_STRING="" # Output value from dialog window. - # MAIN LOGIC. - # Trim leading and trailing white space for each option. - for OPTION in "${INPUT_OPTIONS[@]}"; do - TRIMMED_OPTIONS+=("$(echo "$OPTION" | sed 's/^[ \t]*//;s/[ \t]*$//')") - done + # MAIN LOGIC. + # Trim leading and trailing white space for each option. + for OPTION in "${INPUT_OPTIONS[@]}"; do + TRIMMED_OPTIONS+=("$(echo "$OPTION" | sed 's/^[ \t]*//;s/[ \t]*$//')") + done - # Find the length of the longest option to set the dialog width. - for OPTION in "${TRIMMED_OPTIONS[@]}"; do - if [ "${#OPTION}" -gt "$DIALOG_WIDTH" ]; then - DIALOG_WIDTH=${#OPTION} - fi - done + # Find the length of the longest option to set the dialog width. + for OPTION in "${TRIMMED_OPTIONS[@]}"; do + if [ "${#OPTION}" -gt "$DIALOG_WIDTH" ]; then + DIALOG_WIDTH=${#OPTION} + fi + done - # Apply the offset value to the dialog width. - DIALOG_WIDTH=$((DIALOG_WIDTH + CHK_OPTION_WIDTH_OFFSET)) + # Apply the offset value to the dialog width. + DIALOG_WIDTH=$((DIALOG_WIDTH + CHK_OPTION_WIDTH_OFFSET)) - # Adjust the dialog width again if the dialog text is longer. - if [ "$DIALOG_WIDTH" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then - DIALOG_WIDTH="$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))" - fi + # Adjust the dialog width again if the dialog text is longer. + if [ "$DIALOG_WIDTH" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then + DIALOG_WIDTH="$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))" + fi - # Pad option text with trailing white space to left-align all options. - for OPTION in "${TRIMMED_OPTIONS[@]}"; do - local PAD_LENGTH=$((DIALOG_WIDTH - CHK_OPTION_WIDTH_OFFSET - ${#OPTION})) - # shellcheck disable=SC2155 - local PADDED_OPTION="${OPTION}$(printf '%*s' $PAD_LENGTH)" - PADDED_OPTIONS+=("$PADDED_OPTION") - done + # Pad option text with trailing white space to left-align all options. + for OPTION in "${TRIMMED_OPTIONS[@]}"; do + local PAD_LENGTH=$((DIALOG_WIDTH - CHK_OPTION_WIDTH_OFFSET - ${#OPTION})) + # shellcheck disable=SC2155 + local PADDED_OPTION="${OPTION}$(printf '%*s' $PAD_LENGTH)" + PADDED_OPTIONS+=("$PADDED_OPTION") + done - # Convert options into the appropriate format for a 'dialog' checkbox. - for PADDED_OPTION in "${PADDED_OPTIONS[@]}"; do - DIALOG_OPTIONS+=("$PADDED_OPTION" "" off) - done + # Convert options into the appropriate format for a 'dialog' checkbox. + for PADDED_OPTION in "${PADDED_OPTIONS[@]}"; do + DIALOG_OPTIONS+=("$PADDED_OPTION" "" off) + done - # Store the number of options. - OPTION_NUMBER="${#INPUT_OPTIONS[@]}" + # Store the number of options. + OPTION_NUMBER="${#INPUT_OPTIONS[@]}" - # Produce checkbox. - # The output string contains options delimited by spaces. - # Each option is enclosed in double quotes within the output string. - # For example: '"Option 1 " "The Second Option " " Option Number 3 "' - SELECTED_OPTIONS_STRING=$(dialog \ ---keep-tite \ ---clear \ ---no-shadow \ ---checklist \ -"$DIALOG_TEXT" \ -"$DIALOG_HEIGHT" \ -"$DIALOG_WIDTH" \ -"$OPTION_NUMBER" \ -"${DIALOG_OPTIONS[@]}" \ -2>&1 >/dev/tty) || exit 0 + # Produce checkbox. + # The output string contains options delimited by spaces. + # Each option is enclosed in double quotes within the output string. + # For example: '"Option 1 " "The Second Option " " Option Number 3 "' + SELECTED_OPTIONS_STRING=$(dialog \ + --keep-tite \ + --clear \ + --no-shadow \ + --checklist \ + "$DIALOG_TEXT" \ + "$DIALOG_HEIGHT" \ + "$DIALOG_WIDTH" \ + "$OPTION_NUMBER" \ + "${DIALOG_OPTIONS[@]}" \ + 2>&1 >/dev/tty) || exit 0 - # Convert the output string into an array. - # shellcheck disable=SC2001 - while IFS= read -r LINE; do - LINE="${LINE/#\"}" # Remove leading double quote. - LINE="${LINE/%\"}" # Remove trailing double quote. - RETURN_ARRAY+=("$LINE") # Add to array. - done < <(echo "$SELECTED_OPTIONS_STRING" | sed 's/\" \"/\"\n\"/g') + # Convert the output string into an array. + # shellcheck disable=SC2001 + while IFS= read -r LINE; do + LINE="${LINE/#\"/}" # Remove leading double quote. + LINE="${LINE/%\"/}" # Remove trailing double quote. + RETURN_ARRAY+=("$LINE") # Add to array. + done < <(echo "$SELECTED_OPTIONS_STRING" | sed 's/\" \"/\"\n\"/g') - # Final modifications. - for (( i=0; i<${#RETURN_ARRAY[@]}; i++ )); do - # Remove white space added previously. - # shellcheck disable=SC2001 - RETURN_ARRAY[i]=$(echo "${RETURN_ARRAY[i]}" | \ - sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + # Final modifications. + for ((i = 0; i < ${#RETURN_ARRAY[@]}; i++)); do + # Remove white space added previously. + # shellcheck disable=SC2001 + RETURN_ARRAY[i]=$(echo "${RETURN_ARRAY[i]}" | + sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - # Remove escapes (introduced by 'dialog' if options have parentheses). - RETURN_ARRAY[i]=${RETURN_ARRAY[i]//\\/} # ${variable//search/replace} - done + # Remove escapes (introduced by 'dialog' if options have parentheses). + RETURN_ARRAY[i]=${RETURN_ARRAY[i]//\\/} # ${variable//search/replace} + done } diff --git a/installer.sh b/installer.sh index 1e882f9..755661a 100755 --- a/installer.sh +++ b/installer.sh @@ -3,133 +3,132 @@ ### GLOBAL CONSTANTS ### # ANSI ESCAPE SEQUENCES -readonly BOLD_TEXT="\033[1m" # Bold -readonly CLEAR_TEXT="\033[0m" # Clear -readonly COMMAND_TEXT="\033[0;37m" # Grey -readonly DONE_TEXT="\033[0;32m" # Green -readonly ERROR_TEXT="\033[1;31m" # Bold + Red -readonly EXIT_TEXT="\033[1;41;37m" # Bold + White + Red Background -readonly FAIL_TEXT="\033[0;91m" # Bright Red -readonly INFO_TEXT="\033[0;33m" # Orange/Yellow -readonly SUCCESS_TEXT="\033[1;42;37m" # Bold + White + Green Background +readonly BOLD_TEXT="\033[1m" # Bold +readonly CLEAR_TEXT="\033[0m" # Clear +readonly COMMAND_TEXT="\033[0;37m" # Grey +readonly DONE_TEXT="\033[0;32m" # Green +readonly ERROR_TEXT="\033[1;31m" # Bold + Red +readonly EXIT_TEXT="\033[1;41;37m" # Bold + White + Red Background +readonly FAIL_TEXT="\033[0;91m" # Bright Red +readonly INFO_TEXT="\033[0;33m" # Orange/Yellow +readonly SUCCESS_TEXT="\033[1;42;37m" # Bold + White + Green Background # ERROR CODES -readonly EC_FAILED_CD="1" # Failed to change directory to location of script. -readonly EC_BAD_ARGUMENT="2" # Unsupported argument passed to script. -readonly EC_EXISTING_INSTALL="3" # Existing conflicting WinApps installation. -readonly EC_NO_CONFIG="4" # Absence of a valid WinApps configuration file. -readonly EC_MISSING_DEPS="5" # Missing dependencies. -readonly EC_NO_SUDO="6" # Insufficient privilages to invoke superuser access. -readonly EC_NOT_IN_GROUP="7" # Current user not in group 'libvirt' and/or 'kvm'. -readonly EC_VM_OFF="8" # Windows VM powered off. -readonly EC_VM_PAUSED="9" # Windows VM paused. -readonly EC_VM_ABSENT="10" # Windows VM does not exist. -readonly EC_VM_NO_IP="11" # Windows VM does not have an IP address. -readonly EC_VM_BAD_PORT="12" # Windows VM is unreachable via RDP_PORT. -readonly EC_RDP_FAIL="13" # FreeRDP failed to establish a connection with the Windows VM. -readonly EC_APPQUERY_FAIL="14" # Failed to query the Windows VM for installed applications. +readonly EC_FAILED_CD="1" # Failed to change directory to location of script. +readonly EC_BAD_ARGUMENT="2" # Unsupported argument passed to script. +readonly EC_EXISTING_INSTALL="3" # Existing conflicting WinApps installation. +readonly EC_NO_CONFIG="4" # Absence of a valid WinApps configuration file. +readonly EC_MISSING_DEPS="5" # Missing dependencies. +readonly EC_NO_SUDO="6" # Insufficient privilages to invoke superuser access. +readonly EC_NOT_IN_GROUP="7" # Current user not in group 'libvirt' and/or 'kvm'. +readonly EC_VM_OFF="8" # Windows VM powered off. +readonly EC_VM_PAUSED="9" # Windows VM paused. +readonly EC_VM_ABSENT="10" # Windows VM does not exist. +readonly EC_VM_NO_IP="11" # Windows VM does not have an IP address. +readonly EC_VM_BAD_PORT="12" # Windows VM is unreachable via RDP_PORT. +readonly EC_RDP_FAIL="13" # FreeRDP failed to establish a connection with the Windows VM. +readonly EC_APPQUERY_FAIL="14" # Failed to query the Windows VM for installed applications. # PATHS # 'BIN' -readonly SYS_BIN_PATH="/usr/local/bin" # UNIX path to 'bin' directory for a '--system' WinApps installation. -readonly USER_BIN_PATH="${HOME}/.local/bin" # UNIX path to 'bin' directory for a '--user' WinApps installation. -readonly USER_BIN_PATH_WIN="\\\\tsclient\\home\\.local\\bin" # WINDOWS path to 'bin' directory for a '--user' WinApps installation. +readonly SYS_BIN_PATH="/usr/local/bin" # UNIX path to 'bin' directory for a '--system' WinApps installation. +readonly USER_BIN_PATH="${HOME}/.local/bin" # UNIX path to 'bin' directory for a '--user' WinApps installation. +readonly USER_BIN_PATH_WIN='\\tsclient\home\.local\bin' # WINDOWS path to 'bin' directory for a '--user' WinApps installation. # 'APP' -readonly SYS_APP_PATH="/usr/share/applications" # UNIX path to 'applications' directory for a '--system' WinApps installation. -readonly USER_APP_PATH="${HOME}/.local/share/applications" # UNIX path to 'applications' directory for a '--user' WinApps installation. -readonly USER_APP_PATH_WIN="\\\\tsclient\\home\\.local\\share\\applications" # WINDOWS oath to 'applications' directory for a '--user' WinApps installation. +readonly SYS_APP_PATH="/usr/share/applications" # UNIX path to 'applications' directory for a '--system' WinApps installation. +readonly USER_APP_PATH="${HOME}/.local/share/applications" # UNIX path to 'applications' directory for a '--user' WinApps installation. +readonly USER_APP_PATH_WIN='\\tsclient\home\.local\share\applications' # WINDOWS oath to 'applications' directory for a '--user' WinApps installation. # 'APPDATA' -readonly SYS_APPDATA_PATH="/usr/local/share/winapps" # UNIX path to 'application data' directory for a '--system' WinApps installation. -readonly USER_APPDATA_PATH="${HOME}/.local/share/winapps" # UNIX path to 'application data' directory for a '--user' WinApps installation. -readonly USER_APPDATA_PATH_WIN="\\\\tsclient\\home\\.local\\share\\winapps" # WINDOWS path to 'application data' directory for a '--user' WinApps installation. +readonly SYS_APPDATA_PATH="/usr/local/share/winapps" # UNIX path to 'application data' directory for a '--system' WinApps installation. +readonly USER_APPDATA_PATH="${HOME}/.local/share/winapps" # UNIX path to 'application data' directory for a '--user' WinApps installation. +readonly USER_APPDATA_PATH_WIN='\\tsclient\home\.local\share\winapps' # WINDOWS path to 'application data' directory for a '--user' WinApps installation. # 'Installed Batch Script' -readonly BATCH_SCRIPT_PATH="${USER_APPDATA_PATH}/installed.bat" # UNIX path to a batch script used to search the Windows VM for applications. -readonly BATCH_SCRIPT_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed.bat" # WINDOWS path to a batch script used to search the Windows VM for applications. +readonly BATCH_SCRIPT_PATH="${USER_APPDATA_PATH}/installed.bat" # UNIX path to a batch script used to search the Windows VM for applications. +readonly BATCH_SCRIPT_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed.bat" # WINDOWS path to a batch script used to search the Windows VM for applications. # 'Installed File' -readonly TMP_INST_FILE_PATH="${USER_APPDATA_PATH}/installed.tmp" # UNIX path to a temporary file containing the names of detected officially supported applications. -readonly TMP_INST_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed.tmp" # WINDOWS path to a temporary file containing the names of detected officially supported applications. -readonly INST_FILE_PATH="${USER_APPDATA_PATH}/installed" # UNIX path to a file containing the names of detected officially supported applications. -readonly INST_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed" # WINDOWS path to a file containing the names of detected officially supported applications. +readonly TMP_INST_FILE_PATH="${USER_APPDATA_PATH}/installed.tmp" # UNIX path to a temporary file containing the names of detected officially supported applications. +readonly TMP_INST_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed.tmp" # WINDOWS path to a temporary file containing the names of detected officially supported applications. +readonly INST_FILE_PATH="${USER_APPDATA_PATH}/installed" # UNIX path to a file containing the names of detected officially supported applications. +readonly INST_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed" # WINDOWS path to a file containing the names of detected officially supported applications. # 'PowerShell Script' -readonly PS_SCRIPT_PATH="./install/ExtractPrograms.ps1" # UNIX path to a PowerShell script used to store the names, executable paths and icons (base64) of detected applications. -readonly PS_SCRIPT_HOME_PATH="${USER_APPDATA_PATH}/ExtractPrograms.ps1" # UNIX path to a copy of the PowerShell script within the user's home directory to enable access by Windows VM. -readonly PS_SCRIPT_HOME_PATH_WIN="${USER_APPDATA_PATH_WIN}\\ExtractPrograms.ps1" # WINDOWS path to a copy of the PowerShell script within the user's home directory to enable access by Windows VM. +readonly PS_SCRIPT_PATH="./install/ExtractPrograms.ps1" # UNIX path to a PowerShell script used to store the names, executable paths and icons (base64) of detected applications. +readonly PS_SCRIPT_HOME_PATH="${USER_APPDATA_PATH}/ExtractPrograms.ps1" # UNIX path to a copy of the PowerShell script within the user's home directory to enable access by Windows VM. +readonly PS_SCRIPT_HOME_PATH_WIN="${USER_APPDATA_PATH_WIN}\\ExtractPrograms.ps1" # WINDOWS path to a copy of the PowerShell script within the user's home directory to enable access by Windows VM. # 'Detected File' -readonly DETECTED_FILE_PATH="${USER_APPDATA_PATH}/detected" # UNIX path to a file containing the output generated by the PowerShell script, formatted to define bash arrays. -readonly DETECTED_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\detected" # WINDOWS path to a file containing the output generated by the PowerShell script, formatted to define bash arrays. +readonly DETECTED_FILE_PATH="${USER_APPDATA_PATH}/detected" # UNIX path to a file containing the output generated by the PowerShell script, formatted to define bash arrays. +readonly DETECTED_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\detected" # WINDOWS path to a file containing the output generated by the PowerShell script, formatted to define bash arrays. # 'FreeRDP Connection Test File' -readonly TEST_PATH="${USER_APPDATA_PATH}/FreeRDP_Connection_Test" # UNIX path to temporary file whose existence is used to confirm a successful RDP connection was established. -readonly TEST_PATH_WIN="${USER_APPDATA_PATH_WIN}\\FreeRDP_Connection_Test" # WINDOWS path to temporary file whose existence is used to confirm a successful RDP connection was established. +readonly TEST_PATH="${USER_APPDATA_PATH}/FreeRDP_Connection_Test" # UNIX path to temporary file whose existence is used to confirm a successful RDP connection was established. +readonly TEST_PATH_WIN="${USER_APPDATA_PATH_WIN}\\FreeRDP_Connection_Test" # WINDOWS path to temporary file whose existence is used to confirm a successful RDP connection was established. # 'WinApps Configuration File' -readonly CONFIG_PATH="${HOME}/.config/winapps/winapps.conf" # UNIX path to the WinApps configuration file. +readonly CONFIG_PATH="${HOME}/.config/winapps/winapps.conf" # UNIX path to the WinApps configuration file. # 'Inquirer Bash Script' -readonly INQUIRER_PATH="./install/inquirer.sh" # UNIX path to the 'inquirer' script, which is used to produce selection menus. +readonly INQUIRER_PATH="./install/inquirer.sh" # UNIX path to the 'inquirer' script, which is used to produce selection menus. # REMOTE DESKTOP CONFIGURATION -readonly VM_NAME="RDPWindows" # Name of the Windows VM. -readonly RDP_PORT=3389 # Port used for RDP on the Windows VM. -readonly WINAPPS_CONFIG="\ -RDP_USER=\"MyWindowsUser\" -RDP_PASS=\"MyWindowsPassword\" -#RDP_DOMAIN=\"MYDOMAIN\" -#RDP_IP=\"192.168.123.111\" +readonly VM_NAME="RDPWindows" # Name of the Windows VM. +readonly RDP_PORT=3389 # Port used for RDP on the Windows VM. +readonly WINAPPS_CONFIG='RDP_USER="MyWindowsUser" +RDP_PASS="MyWindowsPassword" +#RDP_DOMAIN="MYDOMAIN" +#RDP_IP="192.168.123.111" #RDP_SCALE=100 -#RDP_FLAGS=\"\" -#MULTIMON=\"true\" -#DEBUG=\"true\" -#FREERDP_COMMAND=\"xfreerdp\"" # Default WinApps configuration file content. +#RDP_FLAGS="" +#MULTIMON="true" +#DEBUG="true" +#FREERDP_COMMAND="xfreerdp"' # Default WinApps configuration file content. ### GLOBAL VARIABLES ### # USER INPUT -OPT_SYSTEM=0 # Set to '1' if the user specifies '--system'. -OPT_USER=0 # Set to '1' if the user specifies '--user'. -OPT_UNINSTALL=0 # Set to '1' if the user specifies '--uninstall'. -OPT_AOSA=0 # Set to '1' if the user specifies '--setupAllOfficiallySupportedApps'. +OPT_SYSTEM=0 # Set to '1' if the user specifies '--system'. +OPT_USER=0 # Set to '1' if the user specifies '--user'. +OPT_UNINSTALL=0 # Set to '1' if the user specifies '--uninstall'. +OPT_AOSA=0 # Set to '1' if the user specifies '--setupAllOfficiallySupportedApps'. # WINAPPS CONFIGURATION FILE -RDP_USER="" # Imported variable. -RDP_PASS="" # Imported variable. -RDP_DOMAIN="" # Imported variable. -RDP_IP="" # Imported variable. -RDP_SCALE=100 # Imported variable. -RDP_FLAGS="" # Imported variable. -MULTIMON="false" # Imported variable. -DEBUG="false" # Imported variable. -FREERDP_COMMAND="" # Imported variable. -MULTI_FLAG="" # Set based on value of $MULTIMON. +RDP_USER="" # Imported variable. +RDP_PASS="" # Imported variable. +RDP_DOMAIN="" # Imported variable. +RDP_IP="" # Imported variable. +RDP_SCALE=100 # Imported variable. +RDP_FLAGS="" # Imported variable. +MULTIMON="false" # Imported variable. +DEBUG="false" # Imported variable. +FREERDP_COMMAND="" # Imported variable. +MULTI_FLAG="" # Set based on value of $MULTIMON. # PERMISSIONS AND DIRECTORIES -SUDO="" # Set to "sudo" if the user specifies '--system', or "" if the user specifies '--user'. -BIN_PATH="" # Set to $SYS_BIN_PATH if the user specifies '--system', or $USER_BIN_PATH if the user specifies '--user'. -APP_PATH="" # Set to $SYS_APP_PATH if the user specifies '--system', or $USER_APP_PATH if the user specifies '--user'. -APPDATA_PATH="" # Set to $SYS_APPDATA_PATH if the user specifies '--system', or $USER_APPDATA_PATH if the user specifies '--user'. +SUDO="" # Set to "sudo" if the user specifies '--system', or "" if the user specifies '--user'. +BIN_PATH="" # Set to $SYS_BIN_PATH if the user specifies '--system', or $USER_BIN_PATH if the user specifies '--user'. +APP_PATH="" # Set to $SYS_APP_PATH if the user specifies '--system', or $USER_APP_PATH if the user specifies '--user'. +APPDATA_PATH="" # Set to $SYS_APPDATA_PATH if the user specifies '--system', or $USER_APPDATA_PATH if the user specifies '--user'. # INSTALLATION PROCESS -INSTALLED_EXES=() # List of executable file names of officially supported applications that have already been configured during the current installation process. +INSTALLED_EXES=() # List of executable file names of officially supported applications that have already been configured during the current installation process. ### TRAPS ### -set -o errtrace # Ensure traps are inherited by all shell functions and subshells. -trap "waTerminateScript" ERR # Catch non-zero return values. +set -o errtrace # Ensure traps are inherited by all shell functions and subshells. +trap "waTerminateScript" ERR # Catch non-zero return values. ### FUNCTIONS ### # Name: 'waTerminateScript' # Role: Terminates the script when a non-zero return value is encountered. # shellcheck disable=SC2317 # Silence warning regarding this function being unreachable. function waTerminateScript() { - # Store the non-zero exit status received by the trap. - local EXIT_STATUS=$? + # Store the non-zero exit status received by the trap. + local EXIT_STATUS=$? - # Display the exit status. - echo -e "${EXIT_TEXT}Exiting with status '${EXIT_STATUS}'.${CLEAR_TEXT}" + # Display the exit status. + echo -e "${EXIT_TEXT}Exiting with status '${EXIT_STATUS}'.${CLEAR_TEXT}" - # Terminate the script. - exit "$EXIT_STATUS" + # Terminate the script. + exit "$EXIT_STATUS" } # Name: 'waUsage' # Role: Displays usage information for the script. function waUsage() { - echo -e "Usage: + echo -e "Usage: ${COMMAND_TEXT}./installer.sh --user${CLEAR_TEXT} # Install WinApps and selected applications in ${HOME} ${COMMAND_TEXT}./installer.sh --system${CLEAR_TEXT} # Install WinApps and selected applications in /usr ${COMMAND_TEXT}./installer.sh --user --setupAllOfficiallySupportedApps${CLEAR_TEXT} # Install WinApps and all officially supported applications in ${HOME} @@ -142,785 +141,781 @@ function waUsage() { # Name: 'waSetWorkingDirectory' # Role: Changes the working directory to the directory containing the script. function waSetWorkingDirectory() { - # Declare variables. - local SCRIPT_DIR_PATH="" # Stores the absolute path of the directory containing the script. + # Declare variables. + local SCRIPT_DIR_PATH="" # Stores the absolute path of the directory containing the script. - # Determine the absolute path to the directory containing the script. - SCRIPT_DIR_PATH=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")") + # Determine the absolute path to the directory containing the script. + SCRIPT_DIR_PATH=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")") - # Silently change the working directory. - if ! cd "$SCRIPT_DIR_PATH" &>/dev/null; then - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}DIRECTORY CHANGE FAILURE.${CLEAR_TEXT}" + # Silently change the working directory. + if ! cd "$SCRIPT_DIR_PATH" &>/dev/null; then + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}DIRECTORY CHANGE FAILURE.${CLEAR_TEXT}" - # Display error details. - echo -e "${INFO_TEXT}Failed to change the working directory to ${CLEAR_TEXT}${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT}${INFO_TEXT}.${CLEAR_TEXT}" + # Display error details. + echo -e "${INFO_TEXT}Failed to change the working directory to ${CLEAR_TEXT}${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT}${INFO_TEXT}.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Ensure:" - echo -e " - ${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT} exists." - echo -e " - ${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT} is valid and does not contain syntax errors." - echo -e " - The current user has sufficient permissions to access ${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT}." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Ensure:" + echo -e " - ${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT} exists." + echo -e " - ${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT} is valid and does not contain syntax errors." + echo -e " - The current user has sufficient permissions to access ${COMMAND_TEXT}${SCRIPT_DIR_PATH}${CLEAR_TEXT}." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_FAILED_CD" - fi + # Terminate the script. + return "$EC_FAILED_CD" + fi } # Name: 'waCheckInput' # Role: Sanitises input and guides users through selecting appropriate options if no arguments are provided. function waCheckInput() { - # Declare variables. - local OPTIONS=() # Stores the options. - local SELECTED_OPTION # Stores the option selected by the user. + # Declare variables. + local OPTIONS=() # Stores the options. + local SELECTED_OPTION # Stores the option selected by the user. - if [[ $# -gt 0 ]]; then - # Parse arguments. - for argument in "$@"; do - case "$argument" in - "--user") - OPT_USER=1 - ;; - "--system") - OPT_SYSTEM=1 - ;; - "--setupAllOfficiallySupportedApps") - OPT_AOSA=1 - ;; - "--uninstall") - OPT_UNINSTALL=1 - ;; - "--help") - waUsage - exit 0 - ;; - *) - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INVALID ARGUMENT.${CLEAR_TEXT}" + if [[ $# -gt 0 ]]; then + # Parse arguments. + for argument in "$@"; do + case "$argument" in + "--user") + OPT_USER=1 + ;; + "--system") + OPT_SYSTEM=1 + ;; + "--setupAllOfficiallySupportedApps") + OPT_AOSA=1 + ;; + "--uninstall") + OPT_UNINSTALL=1 + ;; + "--help") + waUsage + exit 0 + ;; + *) + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INVALID ARGUMENT.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}Unsupported argument${CLEAR_TEXT} ${COMMAND_TEXT}${argument}${CLEAR_TEXT}${INFO_TEXT}.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}Unsupported argument${CLEAR_TEXT} ${COMMAND_TEXT}${argument}${CLEAR_TEXT}${INFO_TEXT}.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - waUsage - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + waUsage + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_BAD_ARGUMENT" - ;; - esac - done - else - # Install vs. uninstall? - OPTIONS=("Install" "Uninstall") - inqMenu "Install or uninstall WinApps?" OPTIONS SELECTED_OPTION + # Terminate the script. + return "$EC_BAD_ARGUMENT" + ;; + esac + done + else + # Install vs. uninstall? + OPTIONS=("Install" "Uninstall") + inqMenu "Install or uninstall WinApps?" OPTIONS SELECTED_OPTION - # Set flags. - if [[ "$SELECTED_OPTION" == "Uninstall" ]]; then - OPT_UNINSTALL=1 - fi + # Set flags. + if [[ $SELECTED_OPTION == "Uninstall" ]]; then + OPT_UNINSTALL=1 + fi - # User vs. system? - OPTIONS=("Current User" "System") - inqMenu "Configure WinApps for the current user '$(whoami)' or the whole system?" OPTIONS SELECTED_OPTION + # User vs. system? + OPTIONS=("Current User" "System") + inqMenu "Configure WinApps for the current user '$(whoami)' or the whole system?" OPTIONS SELECTED_OPTION - # Set flags. - if [[ "$SELECTED_OPTION" == "Current User" ]]; then - OPT_USER=1 - elif [[ "$SELECTED_OPTION" == "System" ]]; then - OPT_SYSTEM=1 - fi + # Set flags. + if [[ $SELECTED_OPTION == "Current User" ]]; then + OPT_USER=1 + elif [[ $SELECTED_OPTION == "System" ]]; then + OPT_SYSTEM=1 + fi - # Automatic vs. manual? - if [ "$OPT_UNINSTALL" -eq 0 ]; then - OPTIONS=("Manual (Default)" "Automatic") - inqMenu "Automatically install supported applications or choose manually?" OPTIONS SELECTED_OPTION + # Automatic vs. manual? + if [ "$OPT_UNINSTALL" -eq 0 ]; then + OPTIONS=("Manual (Default)" "Automatic") + inqMenu "Automatically install supported applications or choose manually?" OPTIONS SELECTED_OPTION - # Set flags. - if [[ "$SELECTED_OPTION" == "Automatic" ]]; then - OPT_AOSA=1 - fi - fi + # Set flags. + if [[ $SELECTED_OPTION == "Automatic" ]]; then + OPT_AOSA=1 + fi + fi - # Newline. - echo "" - fi + # Newline. + echo "" + fi - # Simultaneous 'User' and 'System'. - if [ "$OPT_SYSTEM" -eq 1 ] && [ "$OPT_USER" -eq 1 ]; then - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}" + # Simultaneous 'User' and 'System'. + if [ "$OPT_SYSTEM" -eq 1 ] && [ "$OPT_USER" -eq 1 ]; then + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--user${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--system${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--user${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--system${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - waUsage - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + waUsage + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_BAD_ARGUMENT" - fi + # Terminate the script. + return "$EC_BAD_ARGUMENT" + fi - # Simultaneous 'Uninstall' and 'AOSA'. - if [ "$OPT_UNINSTALL" -eq 1 ] && [ "$OPT_AOSA" -eq 1 ]; then - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}" + # Simultaneous 'Uninstall' and 'AOSA'. + if [ "$OPT_UNINSTALL" -eq 1 ] && [ "$OPT_AOSA" -eq 1 ]; then + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--uninstall${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--aosa${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--uninstall${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--aosa${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - waUsage - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + waUsage + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_BAD_ARGUMENT" - fi + # Terminate the script. + return "$EC_BAD_ARGUMENT" + fi - # No 'User' or 'System'. - if [ "$OPT_SYSTEM" -eq 0 ] && [ "$OPT_USER" -eq 0 ]; then - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INSUFFICIENT ARGUMENTS.${CLEAR_TEXT}" + # No 'User' or 'System'. + if [ "$OPT_SYSTEM" -eq 0 ] && [ "$OPT_USER" -eq 0 ]; then + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INSUFFICIENT ARGUMENTS.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}You must specify either${CLEAR_TEXT} ${COMMAND_TEXT}--user${CLEAR_TEXT} ${INFO_TEXT}or${CLEAR_TEXT} ${COMMAND_TEXT}--system${CLEAR_TEXT} ${INFO_TEXT}to proceed.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}You must specify either${CLEAR_TEXT} ${COMMAND_TEXT}--user${CLEAR_TEXT} ${INFO_TEXT}or${CLEAR_TEXT} ${COMMAND_TEXT}--system${CLEAR_TEXT} ${INFO_TEXT}to proceed.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - waUsage - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + waUsage + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_BAD_ARGUMENT" - fi + # Terminate the script. + return "$EC_BAD_ARGUMENT" + fi } # Name: 'waConfigurePathsAndPermissions' # Role: Sets paths and adjusts permissions as specified. function waConfigurePathsAndPermissions() { - if [ "$OPT_USER" -eq 1 ]; then - SUDO="" - BIN_PATH="$USER_BIN_PATH" - APP_PATH="$USER_APP_PATH" - APPDATA_PATH="$USER_APPDATA_PATH" - elif [ "$OPT_SYSTEM" -eq 1 ]; then - SUDO="sudo" - BIN_PATH="$SYS_BIN_PATH" - APP_PATH="$SYS_APP_PATH" - APPDATA_PATH="$SYS_APPDATA_PATH" + if [ "$OPT_USER" -eq 1 ]; then + SUDO="" + BIN_PATH="$USER_BIN_PATH" + APP_PATH="$USER_APP_PATH" + APPDATA_PATH="$USER_APPDATA_PATH" + elif [ "$OPT_SYSTEM" -eq 1 ]; then + SUDO="sudo" + BIN_PATH="$SYS_BIN_PATH" + APP_PATH="$SYS_APP_PATH" + APPDATA_PATH="$SYS_APPDATA_PATH" - # Preemptively obtain superuser privileges. - sudo -v || { - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}AUTHENTICATION FAILURE.${CLEAR_TEXT}" + # Preemptively obtain superuser privileges. + sudo -v || { + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}AUTHENTICATION FAILURE.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}Failed to gain superuser privileges.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}Failed to gain superuser privileges.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Please check your password and try again." - echo "If you continue to experience issues, contact your system administrator." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Please check your password and try again." + echo "If you continue to experience issues, contact your system administrator." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_NO_SUDO" - } - fi + # Terminate the script. + return "$EC_NO_SUDO" + } + fi } # Name: 'waCheckExistingInstall' # Role: Identifies any existing WinApps installations that may conflict with the new installation. function waCheckExistingInstall() { - # Print feedback. - echo -n "Checking for existing conflicting WinApps installations... " + # Print feedback. + echo -n "Checking for existing conflicting WinApps installations... " - # Check for an existing 'user' installation. - if [ -f "${USER_BIN_PATH}/winapps" ]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # Check for an existing 'user' installation. + if [ -f "${USER_BIN_PATH}/winapps" ]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}EXISTING 'USER' WINAPPS INSTALLATION.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}EXISTING 'USER' WINAPPS INSTALLATION.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}A previous WinApps installation was detected for the current user.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}A previous WinApps installation was detected for the current user.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo -e "Please remove the existing WinApps installation using ${COMMAND_TEXT}./installer.sh --user --uninstall${CLEAR_TEXT}." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo -e "Please remove the existing WinApps installation using ${COMMAND_TEXT}./installer.sh --user --uninstall${CLEAR_TEXT}." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_EXISTING_INSTALL" - fi + # Terminate the script. + return "$EC_EXISTING_INSTALL" + fi - # Check for an existing 'system' installation. - if [ -f "${SYS_BIN_PATH}/winapps" ]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # Check for an existing 'system' installation. + if [ -f "${SYS_BIN_PATH}/winapps" ]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}EXISTING 'SYSTEM' WINAPPS INSTALLATION.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}EXISTING 'SYSTEM' WINAPPS INSTALLATION.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}A previous system-wide WinApps installation was detected.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}A previous system-wide WinApps installation was detected.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo -e "Please remove the existing WinApps installation using ${COMMAND_TEXT}./installer.sh --system --uninstall${CLEAR_TEXT}." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo -e "Please remove the existing WinApps installation using ${COMMAND_TEXT}./installer.sh --system --uninstall${CLEAR_TEXT}." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_EXISTING_INSTALL" - fi + # Terminate the script. + return "$EC_EXISTING_INSTALL" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waLoadConfig' # Role: Loads settings specified within the WinApps configuration file. function waLoadConfig() { - # Print feedback. - echo -n "Attempting to load WinApps configuration file... " + # Print feedback. + echo -n "Attempting to load WinApps configuration file... " - if [ ! -f "$CONFIG_PATH" ]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + if [ ! -f "$CONFIG_PATH" ]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING CONFIGURATION FILE.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING CONFIGURATION FILE.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}A valid WinApps configuration file was not found.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}A valid WinApps configuration file was not found.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo -e "Please create a configuration file at ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}." - echo -e "\nThe configuration file should contain the following:" - echo -e "\n${COMMAND_TEXT}${WINAPPS_CONFIG}${CLEAR_TEXT}" - echo -e "\nThe ${COMMAND_TEXT}RDP_USER${CLEAR_TEXT} and ${COMMAND_TEXT}RDP_PASS${CLEAR_TEXT} fields should contain the Windows user's account name and password." - echo -e "Note that the Windows user's PIN combination CANNOT be used to populate ${COMMAND_TEXT}RDP_PASS${CLEAR_TEXT}." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo -e "Please create a configuration file at ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}." + echo -e "\nThe configuration file should contain the following:" + echo -e "\n${COMMAND_TEXT}${WINAPPS_CONFIG}${CLEAR_TEXT}" + echo -e "\nThe ${COMMAND_TEXT}RDP_USER${CLEAR_TEXT} and ${COMMAND_TEXT}RDP_PASS${CLEAR_TEXT} fields should contain the Windows user's account name and password." + echo -e "Note that the Windows user's PIN combination CANNOT be used to populate ${COMMAND_TEXT}RDP_PASS${CLEAR_TEXT}." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_NO_CONFIG" - else - # Load the WinApps configuration file. - # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. - source "$CONFIG_PATH" - fi + # Terminate the script. + return "$EC_NO_CONFIG" + else + # Load the WinApps configuration file. + # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. + source "$CONFIG_PATH" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waCheckDependencies' # Role: Terminate script if dependencies are missing. function waCheckDependencies() { - # Print feedback. - echo -n "Checking whether all dependencies are installed... " + # Print feedback. + echo -n "Checking whether all dependencies are installed... " - # 'Dialog'. - if ! command -v dialog &>/dev/null; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # 'Dialog'. + if ! command -v dialog &>/dev/null; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}Please install 'dialog' to proceed.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}Please install 'dialog' to proceed.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Debian/Ubuntu-based systems:" - echo -e " ${COMMAND_TEXT}sudo apt install dialog${CLEAR_TEXT}" - echo "Red Hat/Fedora-based systems:" - echo -e " ${COMMAND_TEXT}sudo dnf install dialog${CLEAR_TEXT}" - echo "Arch Linux systems:" - echo -e " ${COMMAND_TEXT}sudo pacman -S dialog${CLEAR_TEXT}" - echo "Gentoo Linux systems:" - echo -e " ${COMMAND_TEXT}sudo emerge --ask dialog${CLEAR_TEXT}" - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Debian/Ubuntu-based systems:" + echo -e " ${COMMAND_TEXT}sudo apt install dialog${CLEAR_TEXT}" + echo "Red Hat/Fedora-based systems:" + echo -e " ${COMMAND_TEXT}sudo dnf install dialog${CLEAR_TEXT}" + echo "Arch Linux systems:" + echo -e " ${COMMAND_TEXT}sudo pacman -S dialog${CLEAR_TEXT}" + echo "Gentoo Linux systems:" + echo -e " ${COMMAND_TEXT}sudo emerge --ask dialog${CLEAR_TEXT}" + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_MISSING_DEPS" - fi + # Terminate the script. + return "$EC_MISSING_DEPS" + fi - # 'FreeRDP' (Version 3). - # Attempt to set a FreeRDP command if the command variable is empty. - if [ -z "$FREERDP_COMMAND" ]; then - if command -v xfreerdp &>/dev/null; then - FREERDP_COMMAND="xfreerdp" - elif command -v xfreerdp3 &>/dev/null; then - FREERDP_COMMAND="xfreerdp3" - fi - fi + # 'FreeRDP' (Version 3). + # Attempt to set a FreeRDP command if the command variable is empty. + if [ -z "$FREERDP_COMMAND" ]; then + if command -v xfreerdp &>/dev/null; then + FREERDP_COMMAND="xfreerdp" + elif command -v xfreerdp3 &>/dev/null; then + FREERDP_COMMAND="xfreerdp3" + fi + fi - if ! command -v "$FREERDP_COMMAND" &>/dev/null; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + if ! command -v "$FREERDP_COMMAND" &>/dev/null; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}Please install 'FreeRDP' to proceed.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}Please install 'FreeRDP' to proceed.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Debian/Ubuntu-based systems:" - echo -e " ${COMMAND_TEXT}sudo apt install freerdp3-x11${CLEAR_TEXT}" - echo "Red Hat/Fedora-based systems:" - echo -e " ${COMMAND_TEXT}sudo dnf install freerdp${CLEAR_TEXT}" - echo "Arch Linux systems:" - echo -e " ${COMMAND_TEXT}sudo pacman -S freerdp${CLEAR_TEXT}" - echo "Gentoo Linux systems:" - echo -e " ${COMMAND_TEXT}sudo emerge --ask net-misc/freerdp${CLEAR_TEXT}" - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Debian/Ubuntu-based systems:" + echo -e " ${COMMAND_TEXT}sudo apt install freerdp3-x11${CLEAR_TEXT}" + echo "Red Hat/Fedora-based systems:" + echo -e " ${COMMAND_TEXT}sudo dnf install freerdp${CLEAR_TEXT}" + echo "Arch Linux systems:" + echo -e " ${COMMAND_TEXT}sudo pacman -S freerdp${CLEAR_TEXT}" + echo "Gentoo Linux systems:" + echo -e " ${COMMAND_TEXT}sudo emerge --ask net-misc/freerdp${CLEAR_TEXT}" + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_MISSING_DEPS" - fi + # Terminate the script. + return "$EC_MISSING_DEPS" + fi - # 'libvirt' / 'virt-manager'. - if ! command -v virsh &>/dev/null; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # 'libvirt' / 'virt-manager'. + if ! command -v virsh &>/dev/null; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}Please install 'Virtual Machine Manager' to proceed.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}Please install 'Virtual Machine Manager' to proceed.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Debian/Ubuntu-based systems:" - echo -e " ${COMMAND_TEXT}sudo apt install virt-manager${CLEAR_TEXT}" - echo "Red Hat/Fedora-based systems:" - echo -e " ${COMMAND_TEXT}sudo dnf install virt-manager${CLEAR_TEXT}" - echo "Arch Linux systems:" - echo -e " ${COMMAND_TEXT}sudo pacman -S virt-manager${CLEAR_TEXT}" - echo "Gentoo Linux systems:" - echo -e " ${COMMAND_TEXT}sudo emerge --ask app-emulation/virt-manager${CLEAR_TEXT}" - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Debian/Ubuntu-based systems:" + echo -e " ${COMMAND_TEXT}sudo apt install virt-manager${CLEAR_TEXT}" + echo "Red Hat/Fedora-based systems:" + echo -e " ${COMMAND_TEXT}sudo dnf install virt-manager${CLEAR_TEXT}" + echo "Arch Linux systems:" + echo -e " ${COMMAND_TEXT}sudo pacman -S virt-manager${CLEAR_TEXT}" + echo "Gentoo Linux systems:" + echo -e " ${COMMAND_TEXT}sudo emerge --ask app-emulation/virt-manager${CLEAR_TEXT}" + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_MISSING_DEPS" - fi + # Terminate the script. + return "$EC_MISSING_DEPS" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waCheckGroupMembership' # Role: Ensures the current user is part of the required groups. function waCheckGroupMembership() { - # Print feedback. - echo -n "Checking whether the user '$(whoami)' is part of the required groups... " + # Print feedback. + echo -n "Checking whether the user '$(whoami)' is part of the required groups... " - # Declare variables. - local USER_GROUPS="" # Stores groups the current user belongs to. + # Declare variables. + local USER_GROUPS="" # Stores groups the current user belongs to. - # Identify groups the current user belongs to. - USER_GROUPS=$(groups "$(whoami)") + # Identify groups the current user belongs to. + USER_GROUPS=$(groups "$(whoami)") - if ! (echo "$USER_GROUPS" | grep -q -E "\blibvirt\b") || ! (echo "$USER_GROUPS" | grep -q -E "\bkvm\b"); then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + if ! (echo "$USER_GROUPS" | grep -q -E "\blibvirt\b") || ! (echo "$USER_GROUPS" | grep -q -E "\bkvm\b"); then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}GROUP MEMBERSHIP CHECK ERROR.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}GROUP MEMBERSHIP CHECK ERROR.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}The current user '$(whoami)' is not part of group 'libvirt' and/or group 'kvm'.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}The current user '$(whoami)' is not part of group 'libvirt' and/or group 'kvm'.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Please run the below commands, followed by a system reboot:" - echo -e "${COMMAND_TEXT}sudo usermod -a -G libvirt $(whoami)${CLEAR_TEXT}" - echo -e "${COMMAND_TEXT}sudo usermod -a -G kvm $(whoami)${CLEAR_TEXT}" - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Please run the below commands, followed by a system reboot:" + echo -e "${COMMAND_TEXT}sudo usermod -a -G libvirt $(whoami)${CLEAR_TEXT}" + echo -e "${COMMAND_TEXT}sudo usermod -a -G kvm $(whoami)${CLEAR_TEXT}" + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_NOT_IN_GROUP" - fi + # Terminate the script. + return "$EC_NOT_IN_GROUP" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waCheckVMRunning' # Role: Checks the state of the Windows VM to ensure it is running. function waCheckVMRunning() { - # Print feedback. - echo -n "Checking the status of the Windows VM... " + # Print feedback. + echo -n "Checking the status of the Windows VM... " - # Declare variables. - local VM_STATE="" # Stores the state of the Windows VM. + # Declare variables. + local VM_STATE="" # Stores the state of the Windows VM. - # Obtain VM Status - VM_STATE=$(virsh list --all | grep -w "$VM_NAME") + # Obtain VM Status + VM_STATE=$(virsh list --all | grep -w "$VM_NAME") - if [[ "$VM_STATE" == *"shut off"* ]]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + if [[ $VM_STATE == *"shut off"* ]]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM NOT RUNNING.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM NOT RUNNING.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}The Windows VM '${VM_NAME}' is powered off.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}The Windows VM '${VM_NAME}' is powered off.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Please run the below command to start the Windows VM:" - echo -e "${COMMAND_TEXT}virsh start ${VM_NAME}${CLEAR_TEXT}" - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Please run the below command to start the Windows VM:" + echo -e "${COMMAND_TEXT}virsh start ${VM_NAME}${CLEAR_TEXT}" + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_VM_OFF" - elif [[ "$VM_STATE" == *"paused"* ]]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # Terminate the script. + return "$EC_VM_OFF" + elif [[ $VM_STATE == *"paused"* ]]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM NOT RUNNING.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM NOT RUNNING.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}The Windows VM '${VM_NAME}' is paused.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}The Windows VM '${VM_NAME}' is paused.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Please run the below command to resume the Windows VM:" - echo -e "${COMMAND_TEXT}virsh resume ${VM_NAME}${CLEAR_TEXT}" - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Please run the below command to resume the Windows VM:" + echo -e "${COMMAND_TEXT}virsh resume ${VM_NAME}${CLEAR_TEXT}" + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_VM_PAUSED" - elif [[ "$VM_STATE" != *"running"* ]]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # Terminate the script. + return "$EC_VM_PAUSED" + elif [[ $VM_STATE != *"running"* ]]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM DOES NOT EXIST.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM DOES NOT EXIST.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}The Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}The Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Please ensure a Windows VM with the name '${VM_NAME}' exists." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Please ensure a Windows VM with the name '${VM_NAME}' exists." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_VM_ABSENT" - fi + # Terminate the script. + return "$EC_VM_ABSENT" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waCheckVMContactable' # Role: Assesses whether the Windows VM can be contacted (prior to attempting a remote desktop connection). function waCheckVMContactable() { - # Print feedback. - echo -n "Attempting to contact the Windows VM... " + # Print feedback. + echo -n "Attempting to contact the Windows VM... " - # Declare variables. - local VM_MAC="" # Stores the MAC address of the Windows VM. + # Declare variables. + local VM_MAC="" # Stores the MAC address of the Windows VM. - # Obtain Windows VM IP Address - if [ -z "$RDP_IP" ]; then - VM_MAC=$(virsh domiflist "$VM_NAME" | grep -oE "([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})") # VM MAC address. - RDP_IP=$(arp -n | grep "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") # VM IP address. + # Obtain Windows VM IP Address + if [ -z "$RDP_IP" ]; then + VM_MAC=$(virsh domiflist "$VM_NAME" | grep -oE "([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})") # VM MAC address. + RDP_IP=$(arp -n | grep "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") # VM IP address. - if [ -z "$RDP_IP" ]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + if [ -z "$RDP_IP" ]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}The IP address of the Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}The IP address of the Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Please ensure networking is properly configured for the Windows VM." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Please ensure networking is properly configured for the Windows VM." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_VM_NO_IP" - fi - fi + # Terminate the script. + return "$EC_VM_NO_IP" + fi + fi - # Check for an open RDP port. - if ! timeout 5 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # Check for an open RDP port. + if ! timeout 5 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}Failed to establish a connection with the Windows VM '${VM_NAME}' at '${RDP_IP}:${RDP_PORT}'.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}Failed to establish a connection with the Windows VM '${VM_NAME}' at '${RDP_IP}:${RDP_PORT}'.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo "Please ensure Remote Desktop is configured on the Windows VM as per the WinApps README." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo "Please ensure Remote Desktop is configured on the Windows VM as per the WinApps README." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_VM_BAD_PORT" - fi + # Terminate the script. + return "$EC_VM_BAD_PORT" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waCheckRDPAccess' # Role: Tests if the Windows VM is accessible via remote desktop. function waCheckRDPAccess() { - # Print feedback. - echo -n "Establishing a Remote Desktop connection with the Windows VM... " + # Print feedback. + echo -n "Establishing a Remote Desktop connection with the Windows VM... " - # Declare variables. - local FREERDP_LOG="" # Stores the path of the FreeRDP log file. - local FREERDP_PROC="" # Stores the FreeRDP process ID. - local ELAPSED_TIME="" # Stores the time counter. + # Declare variables. + local FREERDP_LOG="" # Stores the path of the FreeRDP log file. + local FREERDP_PROC="" # Stores the FreeRDP process ID. + local ELAPSED_TIME="" # Stores the time counter. - # Log file path. - FREERDP_LOG="${USER_APPDATA_PATH}/FreeRDP_Test_$(date +'%Y%m%d_%H%M_%N').log" + # Log file path. + FREERDP_LOG="${USER_APPDATA_PATH}/FreeRDP_Test_$(date +'%Y%m%d_%H%M_%N').log" - # Ensure the output directory exists. - mkdir -p "$USER_APPDATA_PATH" + # Ensure the output directory exists. + mkdir -p "$USER_APPDATA_PATH" - # Remove existing 'FreeRDP Connection Test' file. - rm -f "$TEST_PATH" + # Remove existing 'FreeRDP Connection Test' file. + rm -f "$TEST_PATH" - # This command should create a file on the host filesystem before terminating the RDP session. This command is silently executed as a background process. - # If the file is created, it means the Windows VM received the command via FreeRDP successfully and can read and write to the Linux home folder. - # Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session. - # [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server. - # shellcheck disable=SC2140,SC2027 # Disable warnings regarding unquoted strings. - "$FREERDP_COMMAND" \ -/cert:tofu \ -/d:"$RDP_DOMAIN" \ -/u:"$RDP_USER" \ -/p:"$RDP_PASS" \ -/scale:"$RDP_SCALE" \ -+auto-reconnect \ -+home-drive \ --wallpaper \ -+dynamic-resolution \ -/app:\ -program:"C:\Windows\System32\cmd.exe",\ -cmd:"/C type NUL > "$TEST_PATH_WIN" && tsdiscon" \ -/v:"$RDP_IP" &>"$FREERDP_LOG" & + # This command should create a file on the host filesystem before terminating the RDP session. This command is silently executed as a background process. + # If the file is created, it means the Windows VM received the command via FreeRDP successfully and can read and write to the Linux home folder. + # Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session. + # [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server. + # shellcheck disable=SC2140,SC2027 # Disable warnings regarding unquoted strings. + "$FREERDP_COMMAND" \ + /cert:tofu \ + /d:"$RDP_DOMAIN" \ + /u:"$RDP_USER" \ + /p:"$RDP_PASS" \ + /scale:"$RDP_SCALE" \ + +auto-reconnect \ + +home-drive \ + -wallpaper \ + +dynamic-resolution \ + /app:program:"C:\Windows\System32\cmd.exe",cmd:"/C type NUL > "$TEST_PATH_WIN" && tsdiscon" \ + /v:"$RDP_IP" &>"$FREERDP_LOG" & - # Store the FreeRDP process ID. - FREERDP_PROC=$! + # Store the FreeRDP process ID. + FREERDP_PROC=$! - # Initialise the time counter. - ELAPSED_TIME=0 + # Initialise the time counter. + ELAPSED_TIME=0 - # Wait a maximum of 30 seconds for the background process to complete. - while [ "$ELAPSED_TIME" -lt 30 ]; do - # Check if the FreeRDP process is complete or if the test file exists. - if ! ps -p "$FREERDP_PROC" &>/dev/null || [ -f "$TEST_PATH" ]; then - break - fi + # Wait a maximum of 30 seconds for the background process to complete. + while [ "$ELAPSED_TIME" -lt 30 ]; do + # Check if the FreeRDP process is complete or if the test file exists. + if ! ps -p "$FREERDP_PROC" &>/dev/null || [ -f "$TEST_PATH" ]; then + break + fi - # Wait for 5 seconds. - sleep 5 - ELAPSED_TIME=$((ELAPSED_TIME + 5)) - done + # Wait for 5 seconds. + sleep 5 + ELAPSED_TIME=$((ELAPSED_TIME + 5)) + done - # Check if FreeRDP process is not complete. - if ps -p "$FREERDP_PROC" &>/dev/null; then - # SIGKILL FreeRDP. - kill -9 "$FREERDP_PROC" - fi + # Check if FreeRDP process is not complete. + if ps -p "$FREERDP_PROC" &>/dev/null; then + # SIGKILL FreeRDP. + kill -9 "$FREERDP_PROC" + fi - # Check if test file does not exist. - if ! [ -f "$TEST_PATH" ]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # Check if test file does not exist. + if ! [ -f "$TEST_PATH" ]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}REMOTE DESKTOP PROTOCOL FAILURE.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}REMOTE DESKTOP PROTOCOL FAILURE.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}FreeRDP failed to establish a connection with the Windows VM '${VM_NAME}'.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}FreeRDP failed to establish a connection with the Windows VM '${VM_NAME}'.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo -e "Please view the log at ${COMMAND_TEXT}${FREERDP_LOG}${CLEAR_TEXT}." - echo "Troubleshooting Tips:" - echo " - Ensure the user is logged out of the Windows VM prior to initiating the WinApps installation." - echo " - Ensure the credentials within the WinApps configuration file are correct." - echo " - Ensure the Windows VM is correctly named as specified within the README." - echo " - Ensure 'Remote Desktop' is enabled within the Windows VM." - echo " - Ensure you have merged 'RDPApps.reg' into the Windows VM's registry." - echo -e " - Utilise a new certificate by removing relevant certificate(s) in ${COMMAND_TEXT}${HOME}/.config/freerdp/server${CLEAR_TEXT}." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo -e "Please view the log at ${COMMAND_TEXT}${FREERDP_LOG}${CLEAR_TEXT}." + echo "Troubleshooting Tips:" + echo " - Ensure the user is logged out of the Windows VM prior to initiating the WinApps installation." + echo " - Ensure the credentials within the WinApps configuration file are correct." + echo " - Ensure the Windows VM is correctly named as specified within the README." + echo " - Ensure 'Remote Desktop' is enabled within the Windows VM." + echo " - Ensure you have merged 'RDPApps.reg' into the Windows VM's registry." + echo -e " - Utilise a new certificate by removing relevant certificate(s) in ${COMMAND_TEXT}${HOME}/.config/freerdp/server${CLEAR_TEXT}." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_RDP_FAIL" - else - # Remove the temporary test file. - rm -f "$TEST_PATH" - fi + # Terminate the script. + return "$EC_RDP_FAIL" + else + # Remove the temporary test file. + rm -f "$TEST_PATH" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waFindInstalled' # Role: Identifies installed applications on the Windows VM. function waFindInstalled() { - # Print feedback. - echo -n "Checking for installed Windows applications... " + # Print feedback. + echo -n "Checking for installed Windows applications... " - # Declare variables. - local FREERDP_LOG="" # Stores the path of the FreeRDP log file. - local FREERDP_PROC="" # Stores the FreeRDP process ID. - local ELAPSED_TIME="" # Stores the time counter. + # Declare variables. + local FREERDP_LOG="" # Stores the path of the FreeRDP log file. + local FREERDP_PROC="" # Stores the FreeRDP process ID. + local ELAPSED_TIME="" # Stores the time counter. - # Log file path. - FREERDP_LOG="${USER_APPDATA_PATH}/FreeRDP_Scan_$(date +'%Y%m%d_%H%M_%N').log" + # Log file path. + FREERDP_LOG="${USER_APPDATA_PATH}/FreeRDP_Scan_$(date +'%Y%m%d_%H%M_%N').log" - # Make the output directory if required. - mkdir -p "$USER_APPDATA_PATH" + # Make the output directory if required. + mkdir -p "$USER_APPDATA_PATH" - # Remove temporary files from previous WinApps installations. - rm -f "$BATCH_SCRIPT_PATH" "$TMP_INST_FILE_PATH" "$INST_FILE_PATH" "$PS_SCRIPT_HOME_PATH" "$DETECTED_FILE_PATH" + # Remove temporary files from previous WinApps installations. + rm -f "$BATCH_SCRIPT_PATH" "$TMP_INST_FILE_PATH" "$INST_FILE_PATH" "$PS_SCRIPT_HOME_PATH" "$DETECTED_FILE_PATH" - # Copy PowerShell script to a directory within the user's home folder. - # This will enable the PowerShell script to be accessed and executed by the Windows VM. - cp "$PS_SCRIPT_PATH" "$PS_SCRIPT_HOME_PATH" + # Copy PowerShell script to a directory within the user's home folder. + # This will enable the PowerShell script to be accessed and executed by the Windows VM. + cp "$PS_SCRIPT_PATH" "$PS_SCRIPT_HOME_PATH" - # Enumerate over each officially supported application. - for APPLICATION in ./apps/*; do - # Extract the name of the application from the absolute path of the folder. - APPLICATION="$(basename "$APPLICATION")" + # Enumerate over each officially supported application. + for APPLICATION in ./apps/*; do + # Extract the name of the application from the absolute path of the folder. + APPLICATION="$(basename "$APPLICATION")" - # Source 'Info' File Containing: - # - The Application Name (FULL_NAME) - # - The Shortcut Name (NAME) - # - Application Categories (CATEGORIES) - # - Executable Path (WIN_EXECUTABLE) - # - Supported MIME Types (MIME_TYPES) - # - Application Icon (ICON) - # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. - source "./apps/${APPLICATION}/info" + # Source 'Info' File Containing: + # - The Application Name (FULL_NAME) + # - The Shortcut Name (NAME) + # - Application Categories (CATEGORIES) + # - Executable Path (WIN_EXECUTABLE) + # - Supported MIME Types (MIME_TYPES) + # - Application Icon (ICON) + # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. + source "./apps/${APPLICATION}/info" - # Append commands to batch file. - echo "IF EXIST \"${WIN_EXECUTABLE}\" ECHO ${APPLICATION} >> ${TMP_INST_FILE_PATH_WIN}" >> "$BATCH_SCRIPT_PATH" - done + # Append commands to batch file. + echo "IF EXIST \"${WIN_EXECUTABLE}\" ECHO ${APPLICATION} >> ${TMP_INST_FILE_PATH_WIN}" >>"$BATCH_SCRIPT_PATH" + done - # Append a command to the batch script to run the PowerShell script and store it's output in the 'detected' file. - # shellcheck disable=SC2129 # Silence warning regarding repeated redirects. - echo "powershell.exe -ExecutionPolicy Bypass -File ${PS_SCRIPT_HOME_PATH_WIN} > ${DETECTED_FILE_PATH_WIN}" >> "$BATCH_SCRIPT_PATH" + # Append a command to the batch script to run the PowerShell script and store it's output in the 'detected' file. + # shellcheck disable=SC2129 # Silence warning regarding repeated redirects. + echo "powershell.exe -ExecutionPolicy Bypass -File ${PS_SCRIPT_HOME_PATH_WIN} > ${DETECTED_FILE_PATH_WIN}" >>"$BATCH_SCRIPT_PATH" - # Append a command to the batch script to rename the temporary file containing the names of all detected officially supported applications. - echo "RENAME ${TMP_INST_FILE_PATH_WIN} installed" >> "$BATCH_SCRIPT_PATH" + # Append a command to the batch script to rename the temporary file containing the names of all detected officially supported applications. + echo "RENAME ${TMP_INST_FILE_PATH_WIN} installed" >>"$BATCH_SCRIPT_PATH" - # Append a command to the batch script to terminate the remote desktop session once all previous commands are complete. - echo "tsdiscon" >> "$BATCH_SCRIPT_PATH" + # Append a command to the batch script to terminate the remote desktop session once all previous commands are complete. + echo "tsdiscon" >>"$BATCH_SCRIPT_PATH" - # Silently execute the batch script within the Windows VM in the background (Log Output To File) - # Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session. - # [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server. - # shellcheck disable=SC2140,SC2027 # Disable warnings regarding unquoted strings. - "$FREERDP_COMMAND" \ -/cert:tofu \ -/d:"$RDP_DOMAIN" \ -/u:"$RDP_USER" \ -/p:"$RDP_PASS" \ -/scale:"$RDP_SCALE" \ -+auto-reconnect \ -+home-drive \ --wallpaper \ -+dynamic-resolution \ -/app:\ -program:"C:\Windows\System32\cmd.exe",\ -cmd:"/C "$BATCH_SCRIPT_PATH_WIN"" \ -/v:"$RDP_IP" &>"$FREERDP_LOG" & + # Silently execute the batch script within the Windows VM in the background (Log Output To File) + # Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session. + # [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server. + # shellcheck disable=SC2140,SC2027 # Disable warnings regarding unquoted strings. + "$FREERDP_COMMAND" \ + /cert:tofu \ + /d:"$RDP_DOMAIN" \ + /u:"$RDP_USER" \ + /p:"$RDP_PASS" \ + /scale:"$RDP_SCALE" \ + +auto-reconnect \ + +home-drive \ + -wallpaper \ + +dynamic-resolution \ + /app:program:"C:\Windows\System32\cmd.exe",cmd:"/C "$BATCH_SCRIPT_PATH_WIN"" \ + /v:"$RDP_IP" &>"$FREERDP_LOG" & - # Store the FreeRDP process ID. - FREERDP_PROC=$! + # Store the FreeRDP process ID. + FREERDP_PROC=$! - # Initialise the time counter. - ELAPSED_TIME=0 + # Initialise the time counter. + ELAPSED_TIME=0 - # Wait a maximum of 60 seconds for the batch script to finish running. - while [ $ELAPSED_TIME -lt 60 ]; do - # Check if the FreeRDP process is complete or if the 'installed' file exists. - if ! ps -p "$FREERDP_PROC" &>/dev/null || [ -f "$INST_FILE_PATH" ]; then - break - fi + # Wait a maximum of 60 seconds for the batch script to finish running. + while [ $ELAPSED_TIME -lt 60 ]; do + # Check if the FreeRDP process is complete or if the 'installed' file exists. + if ! ps -p "$FREERDP_PROC" &>/dev/null || [ -f "$INST_FILE_PATH" ]; then + break + fi - # Wait for 5 seconds. - sleep 5 - ELAPSED_TIME=$((ELAPSED_TIME + 5)) - done + # Wait for 5 seconds. + sleep 5 + ELAPSED_TIME=$((ELAPSED_TIME + 5)) + done - # Check if the FreeRDP process is not complete. - if ps -p "$FREERDP_PROC" &>/dev/null; then - # SIGKILL FreeRDP. - kill -9 "$FREERDP_PROC" - fi + # Check if the FreeRDP process is not complete. + if ps -p "$FREERDP_PROC" &>/dev/null; then + # SIGKILL FreeRDP. + kill -9 "$FREERDP_PROC" + fi - # Check if test file does not exist. - if ! [ -f "$INST_FILE_PATH" ]; then - # Complete the previous line. - echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" + # Check if test file does not exist. + if ! [ -f "$INST_FILE_PATH" ]; then + # Complete the previous line. + echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n" - # Display the error type. - echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}APPLICATION QUERY FAILURE.${CLEAR_TEXT}" + # Display the error type. + echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}APPLICATION QUERY FAILURE.${CLEAR_TEXT}" - # Display the error details. - echo -e "${INFO_TEXT}Failed to query Windows VM '${VM_NAME}' for installed applications.${CLEAR_TEXT}" + # Display the error details. + echo -e "${INFO_TEXT}Failed to query Windows VM '${VM_NAME}' for installed applications.${CLEAR_TEXT}" - # Display the suggested action(s). - echo "--------------------------------------------------------------------------------" - echo -e "Please view the log at ${COMMAND_TEXT}${FREERDP_LOG}${CLEAR_TEXT}." - echo "--------------------------------------------------------------------------------" + # Display the suggested action(s). + echo "--------------------------------------------------------------------------------" + echo -e "Please view the log at ${COMMAND_TEXT}${FREERDP_LOG}${CLEAR_TEXT}." + echo "--------------------------------------------------------------------------------" - # Terminate the script. - return "$EC_APPQUERY_FAIL" - fi + # Terminate the script. + return "$EC_APPQUERY_FAIL" + fi - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waConfigureWindows' # Role: Create an application entry for launching Windows VM via Remote Desktop. function waConfigureWindows() { - # Print feedback. - echo -n "Creating an application entry for Windows VM... " + # Print feedback. + echo -n "Creating an application entry for Windows VM... " - # Declare variables. - local WIN_BASH="" # Stores the bash script to launch the Windows VM. - local WIN_DESKTOP="" # Stores the '.desktop' file to launch the Windows VM. + # Declare variables. + local WIN_BASH="" # Stores the bash script to launch the Windows VM. + local WIN_DESKTOP="" # Stores the '.desktop' file to launch the Windows VM. - # Populate variables. - WIN_BASH="\ + # Populate variables. + WIN_BASH="\ #!/usr/bin/env bash ${BIN_PATH}/winapps windows" - WIN_DESKTOP="\ + WIN_DESKTOP="\ [Desktop Entry] Name=Windows Exec=${BIN_PATH}/winapps windows %F @@ -930,50 +925,50 @@ Icon=${APPDATA_PATH}/icons/windows.svg StartupWMClass=Microsoft Windows Comment=Microsoft Windows VM" - # Copy the 'Windows' icon. - $SUDO cp "./icons/windows.svg" "${APPDATA_PATH}/icons/windows.svg" + # Copy the 'Windows' icon. + $SUDO cp "./icons/windows.svg" "${APPDATA_PATH}/icons/windows.svg" - # Write the desktop entry content to a file. - echo "$WIN_DESKTOP" | $SUDO tee "${APP_PATH}/windows.desktop" &>/dev/null + # Write the desktop entry content to a file. + echo "$WIN_DESKTOP" | $SUDO tee "${APP_PATH}/windows.desktop" &>/dev/null - # Write the bash script to a file. - echo "$WIN_BASH" | $SUDO tee "${BIN_PATH}/windows" &>/dev/null + # Write the bash script to a file. + echo "$WIN_BASH" | $SUDO tee "${BIN_PATH}/windows" &>/dev/null - # Mark the bash script as executable. - $SUDO chmod a+x "${BIN_PATH}/windows" + # Mark the bash script as executable. + $SUDO chmod a+x "${BIN_PATH}/windows" - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" } # Name: 'waConfigureApp' # Role: Create application entries for a given application installed on the Windows VM. function waConfigureApp() { - # Declare variables. - local APP_ICON="" # Stores the path to the application icon. - local APP_BASH="" # Stores the bash script used to launch the application. - local APP_DESKTOP_FILE="" # Stores the '.desktop' file used to launch the application. + # Declare variables. + local APP_ICON="" # Stores the path to the application icon. + local APP_BASH="" # Stores the bash script used to launch the application. + local APP_DESKTOP_FILE="" # Stores the '.desktop' file used to launch the application. - # Source 'Info' File Containing: - # - The Application Name (FULL_NAME) - # - The Shortcut Nsame (NAME) - # - Application Categories (CATEGORIES) - # - Executable Path (WIN_EXECUTABLE) - # - Supported MIME Types (MIME_TYPES) - # - Application Icon (ICON) - # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. - source "${APPDATA_PATH}/apps/${1}/info" + # Source 'Info' File Containing: + # - The Application Name (FULL_NAME) + # - The Shortcut Nsame (NAME) + # - Application Categories (CATEGORIES) + # - Executable Path (WIN_EXECUTABLE) + # - Supported MIME Types (MIME_TYPES) + # - Application Icon (ICON) + # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. + source "${APPDATA_PATH}/apps/${1}/info" - # Determine path to application icon using arguments passed to function. - APP_ICON="${APPDATA_PATH}/apps/${1}/icon.${2}" + # Determine path to application icon using arguments passed to function. + APP_ICON="${APPDATA_PATH}/apps/${1}/icon.${2}" - # Determine the content of the bash script for the application. - APP_BASH="\ + # Determine the content of the bash script for the application. + APP_BASH="\ #!/usr/bin/env bash ${BIN_PATH}/winapps ${1}" - # Determine the content of the '.desktop' file for the application. - APP_DESKTOP_FILE="\ + # Determine the content of the '.desktop' file for the application. + APP_DESKTOP_FILE="\ [Desktop Entry] Name=${NAME} Exec=${BIN_PATH}/winapps ${1} %F @@ -985,214 +980,214 @@ Comment=${FULL_NAME} Categories=${CATEGORIES} MimeType=${MIME_TYPES}" - # Store the '.desktop' file for the application. - echo "$APP_DESKTOP_FILE" | $SUDO tee "${APP_PATH}/${1}.desktop" &>/dev/null + # Store the '.desktop' file for the application. + echo "$APP_DESKTOP_FILE" | $SUDO tee "${APP_PATH}/${1}.desktop" &>/dev/null - # Store the bash script for the application. - echo "$APP_BASH" | $SUDO tee "${BIN_PATH}/${1}" &>/dev/null + # Store the bash script for the application. + echo "$APP_BASH" | $SUDO tee "${BIN_PATH}/${1}" &>/dev/null - # Mark bash script as executable. - $SUDO chmod a+x "${BIN_PATH}/${1}" + # Mark bash script as executable. + $SUDO chmod a+x "${BIN_PATH}/${1}" } # Name: 'waConfigureOfficiallySupported' # Role: Create application entries for officially supported applications installed on the Windows VM. function waConfigureOfficiallySupported() { - # Declare variables. - local OSA_LIST=() # Stores a list of all officially supported applications installed on the Windows VM. + # Declare variables. + local OSA_LIST=() # Stores a list of all officially supported applications installed on the Windows VM. - # Read the list of officially supported applications that are installed on the Windows VM into an array, returning an empty array if no such files exist. - # This will remove leading and trailing whitespace characters as well as ignore empty lines. - readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' "$INST_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true) + # Read the list of officially supported applications that are installed on the Windows VM into an array, returning an empty array if no such files exist. + # This will remove leading and trailing whitespace characters as well as ignore empty lines. + readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' "$INST_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true) - # Create application entries for each officially supported application. - for OSA in "${OSA_LIST[@]}"; do - # Print feedback. - echo -n "Creating an application entry for ${OSA}... " + # Create application entries for each officially supported application. + for OSA in "${OSA_LIST[@]}"; do + # Print feedback. + echo -n "Creating an application entry for ${OSA}... " - # Copy application icon and information. - $SUDO cp -r "./apps/${OSA}" "${APPDATA_PATH}/apps" + # Copy application icon and information. + $SUDO cp -r "./apps/${OSA}" "${APPDATA_PATH}/apps" - # Configure the application. - waConfigureApp "$OSA" svg + # Configure the application. + waConfigureApp "$OSA" svg - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" - done + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + done - # Delete 'install' file. - rm -f "$INST_FILE_PATH" + # Delete 'install' file. + rm -f "$INST_FILE_PATH" } # Name: 'waConfigureApps' # Role: Allow the user to select which officially supported applications to configure. function waConfigureApps() { - # Declare variables. - local OSA_LIST=() # Stores a list of all officially supported applications installed on the Windows VM. - local APPS=() # Stores a list of both the simplified and full names of each installed officially supported application. - local OPTIONS=() # Stores a list of options presented to the user. - local APP_INSTALL="" # Stores the option selected by the user. - local SELECTED_APPS=() # Stores the officially supported applications selected by the user. - local TEMP_ARRAY=() # Temporary array used for sorting elements of an array. + # Declare variables. + local OSA_LIST=() # Stores a list of all officially supported applications installed on the Windows VM. + local APPS=() # Stores a list of both the simplified and full names of each installed officially supported application. + local OPTIONS=() # Stores a list of options presented to the user. + local APP_INSTALL="" # Stores the option selected by the user. + local SELECTED_APPS=() # Stores the officially supported applications selected by the user. + local TEMP_ARRAY=() # Temporary array used for sorting elements of an array. - # Read the list of officially supported applications that are installed on the Windows VM into an array, returning an empty array if no such files exist. - # This will remove leading and trailing whitespace characters as well as ignore empty lines. - readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' "$INST_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true) + # Read the list of officially supported applications that are installed on the Windows VM into an array, returning an empty array if no such files exist. + # This will remove leading and trailing whitespace characters as well as ignore empty lines. + readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' "$INST_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true) - # Loop over each officially supported application installed on the Windows VM. - for OSA in "${OSA_LIST[@]}"; do - # Source 'Info' File Containing: - # - The Application Name (FULL_NAME) - # - The Shortcut Nsame (NAME) - # - Application Categories (CATEGORIES) - # - Executable Path (WIN_EXECUTABLE) - # - Supported MIME Types (MIME_TYPES) - # - Application Icon (ICON) - # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. - source "./apps/${OSA}/info" + # Loop over each officially supported application installed on the Windows VM. + for OSA in "${OSA_LIST[@]}"; do + # Source 'Info' File Containing: + # - The Application Name (FULL_NAME) + # - The Shortcut Nsame (NAME) + # - Application Categories (CATEGORIES) + # - Executable Path (WIN_EXECUTABLE) + # - Supported MIME Types (MIME_TYPES) + # - Application Icon (ICON) + # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. + source "./apps/${OSA}/info" - # Add both the simplified and full name of the application to an array. - APPS+=("${FULL_NAME} (${OSA})") + # Add both the simplified and full name of the application to an array. + APPS+=("${FULL_NAME} (${OSA})") - # Extract the executable file name (e.g. 'MyApp.exe') from the absolute path. - WIN_EXECUTABLE="${WIN_EXECUTABLE##*\\}" + # Extract the executable file name (e.g. 'MyApp.exe') from the absolute path. + WIN_EXECUTABLE="${WIN_EXECUTABLE##*\\}" - # Trim any leading or trailing whitespace characters from the executable file name. - read -r WIN_EXECUTABLE <<< "$(echo "$WIN_EXECUTABLE" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + # Trim any leading or trailing whitespace characters from the executable file name. + read -r WIN_EXECUTABLE <<<"$(echo "$WIN_EXECUTABLE" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - # Add the executable file name (in lowercase) to the array. - INSTALLED_EXES+=("${WIN_EXECUTABLE,,}") - done + # Add the executable file name (in lowercase) to the array. + INSTALLED_EXES+=("${WIN_EXECUTABLE,,}") + done - # Sort the 'APPS' array in alphabetical order. - IFS=$'\n' - # shellcheck disable=SC2207 # Silence warnings regarding preferred use of 'mapfile' or 'read -a'. - TEMP_ARRAY=($(sort <<<"${APPS[*]}")) - unset IFS - APPS=("${TEMP_ARRAY[@]}") + # Sort the 'APPS' array in alphabetical order. + IFS=$'\n' + # shellcheck disable=SC2207 # Silence warnings regarding preferred use of 'mapfile' or 'read -a'. + TEMP_ARRAY=($(sort <<<"${APPS[*]}")) + unset IFS + APPS=("${TEMP_ARRAY[@]}") - # Prompt user to select which officially supported applications to configure. - OPTIONS=( - "Set up all detected officially supported applications" - "Choose specific officially supported applications to set up" - "Skip setting up any officially supported applications" - ) - inqMenu "How would you like to handle officially supported applications?" OPTIONS APP_INSTALL + # Prompt user to select which officially supported applications to configure. + OPTIONS=( + "Set up all detected officially supported applications" + "Choose specific officially supported applications to set up" + "Skip setting up any officially supported applications" + ) + inqMenu "How would you like to handle officially supported applications?" OPTIONS APP_INSTALL - # Remove unselected officially supported applications from the 'install' file. - if [[ "$APP_INSTALL" == "Choose specific officially supported applications to set up" ]]; then - inqChkBx "Which officially supported applications would you like to set up?" APPS SELECTED_APPS + # Remove unselected officially supported applications from the 'install' file. + if [[ $APP_INSTALL == "Choose specific officially supported applications to set up" ]]; then + inqChkBx "Which officially supported applications would you like to set up?" APPS SELECTED_APPS - # Clear/create the 'install' file. - echo "" >"$INST_FILE_PATH" + # Clear/create the 'install' file. + echo "" >"$INST_FILE_PATH" - # Add each selected officially supported application back to the 'install' file. - for SELECTED_APP in "${SELECTED_APPS[@]}"; do - # Capture the substring within (but not including) the parentheses. - # This substring represents the officially supported application name (see above loop). - SELECTED_APP="${SELECTED_APP##*(}" - SELECTED_APP="${SELECTED_APP%%)}" + # Add each selected officially supported application back to the 'install' file. + for SELECTED_APP in "${SELECTED_APPS[@]}"; do + # Capture the substring within (but not including) the parentheses. + # This substring represents the officially supported application name (see above loop). + SELECTED_APP="${SELECTED_APP##*(}" + SELECTED_APP="${SELECTED_APP%%)}" - # Add the substring back to the 'install' file. - echo "$SELECTED_APP" >> "$INST_FILE_PATH" - done - fi + # Add the substring back to the 'install' file. + echo "$SELECTED_APP" >>"$INST_FILE_PATH" + done + fi - # Configure selected (or all) officially supported applications. - if [[ "$APP_INSTALL" != "Skip setting up any officially supported applications" ]]; then - waConfigureOfficiallySupported - fi + # Configure selected (or all) officially supported applications. + if [[ $APP_INSTALL != "Skip setting up any officially supported applications" ]]; then + waConfigureOfficiallySupported + fi } # Name: 'waConfigureDetectedApps' # Role: Allow the user to select which detected applications to configure. function waConfigureDetectedApps() { - # Declare variables. - local APPS=() # Stores a list of both the simplified and full names of each detected application. - local EXE_FILENAME="" # Stores the executable filename of a given detected application. - local EXE_FILENAME_NOEXT="" # Stores the executable filename without the file extension of a given detected application. - local EXE_FILENAME_LOWERCASE="" # Stores the executable filename of a given detected application in lowercase letters only. - local OPTIONS=() # Stores a list of options presented to the user. - local APP_INSTALL="" # Stores the option selected by the user. - local SELECTED_APPS=() # Detected applications selected by the user. - local APP_DESKTOP_FILE="" # Stores the '.desktop' file used to launch the application. - local TEMP_ARRAY=() # Temporary array used for sorting elements of an array. + # Declare variables. + local APPS=() # Stores a list of both the simplified and full names of each detected application. + local EXE_FILENAME="" # Stores the executable filename of a given detected application. + local EXE_FILENAME_NOEXT="" # Stores the executable filename without the file extension of a given detected application. + local EXE_FILENAME_LOWERCASE="" # Stores the executable filename of a given detected application in lowercase letters only. + local OPTIONS=() # Stores a list of options presented to the user. + local APP_INSTALL="" # Stores the option selected by the user. + local SELECTED_APPS=() # Detected applications selected by the user. + local APP_DESKTOP_FILE="" # Stores the '.desktop' file used to launch the application. + local TEMP_ARRAY=() # Temporary array used for sorting elements of an array. - if [ -f "$DETECTED_FILE_PATH" ]; then - # On UNIX systems, lines are terminated with a newline character (\n). - # On WINDOWS systems, lines are terminated with both a carriage return (\r) and a newline (\n) character. - # Remove all carriage returns (\r) within the 'detected' file, as the file was written by the Windows VM. - sed -i 's/\r//g' "$DETECTED_FILE_PATH" + if [ -f "$DETECTED_FILE_PATH" ]; then + # On UNIX systems, lines are terminated with a newline character (\n). + # On WINDOWS systems, lines are terminated with both a carriage return (\r) and a newline (\n) character. + # Remove all carriage returns (\r) within the 'detected' file, as the file was written by the Windows VM. + sed -i 's/\r//g' "$DETECTED_FILE_PATH" - # Import the detected application information: - # - Application Names (NAMES) - # - Application Icons in base64 (ICONS) - # - Application Executable Paths (EXES) - # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. - source "$DETECTED_FILE_PATH" + # Import the detected application information: + # - Application Names (NAMES) + # - Application Icons in base64 (ICONS) + # - Application Executable Paths (EXES) + # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck. + source "$DETECTED_FILE_PATH" - # shellcheck disable=SC2153 # Silence warnings regarding possible misspellings. - for INDEX in "${!NAMES[@]}"; do - # Extract the executable file name (e.g. 'MyApp.exe'). - EXE_FILENAME=${EXES[$INDEX]##*\\} + # shellcheck disable=SC2153 # Silence warnings regarding possible misspellings. + for INDEX in "${!NAMES[@]}"; do + # Extract the executable file name (e.g. 'MyApp.exe'). + EXE_FILENAME=${EXES[$INDEX]##*\\} - # Convert the executable file name to lower-case (e.g. 'myapp.exe'). - EXE_FILENAME_LOWERCASE="${EXE_FILENAME,,}" + # Convert the executable file name to lower-case (e.g. 'myapp.exe'). + EXE_FILENAME_LOWERCASE="${EXE_FILENAME,,}" - # Remove the file extension (e.g. 'MyApp'). - EXE_FILENAME_NOEXT="${EXE_FILENAME%.*}" + # Remove the file extension (e.g. 'MyApp'). + EXE_FILENAME_NOEXT="${EXE_FILENAME%.*}" - # Check if the executable was previously configured as part of setting up officially supported applications. - if [[ ! " ${INSTALLED_EXES[*]} " == *" ${EXE_FILENAME_LOWERCASE} "* ]]; then - # If not previously configured, add the application to the list of detected applications. - APPS+=("${NAMES[$INDEX]} (${EXE_FILENAME_NOEXT})") - fi - done + # Check if the executable was previously configured as part of setting up officially supported applications. + if [[ " ${INSTALLED_EXES[*]} " != *" ${EXE_FILENAME_LOWERCASE} "* ]]; then + # If not previously configured, add the application to the list of detected applications. + APPS+=("${NAMES[$INDEX]} (${EXE_FILENAME_NOEXT})") + fi + done - # Sort the 'APPS' array in alphabetical order. - IFS=$'\n' - # shellcheck disable=SC2207 # Silence warnings regarding preferred use of 'mapfile' or 'read -a'. - TEMP_ARRAY=($(sort <<<"${APPS[*]}")) - unset IFS - APPS=("${TEMP_ARRAY[@]}") + # Sort the 'APPS' array in alphabetical order. + IFS=$'\n' + # shellcheck disable=SC2207 # Silence warnings regarding preferred use of 'mapfile' or 'read -a'. + TEMP_ARRAY=($(sort <<<"${APPS[*]}")) + unset IFS + APPS=("${TEMP_ARRAY[@]}") - # Prompt user to select which other detected applications to configure. - OPTIONS=( - "Set up all detected applications" - "Select which applications to set up" - "Do not set up any applications" - ) - inqMenu "How would you like to handle other detected applications?" OPTIONS APP_INSTALL + # Prompt user to select which other detected applications to configure. + OPTIONS=( + "Set up all detected applications" + "Select which applications to set up" + "Do not set up any applications" + ) + inqMenu "How would you like to handle other detected applications?" OPTIONS APP_INSTALL - # Store selected detected applications. - if [[ "$APP_INSTALL" == "Select which applications to set up" ]]; then - inqChkBx "Which other applications would you like to set up?" APPS SELECTED_APPS - elif [[ "$APP_INSTALL" == "Set up all detected applications" ]]; then - readarray -t SELECTED_APPS <<<"${APPS[@]}" - fi + # Store selected detected applications. + if [[ $APP_INSTALL == "Select which applications to set up" ]]; then + inqChkBx "Which other applications would you like to set up?" APPS SELECTED_APPS + elif [[ $APP_INSTALL == "Set up all detected applications" ]]; then + readarray -t SELECTED_APPS <<<"${APPS[@]}" + fi - for SELECTED_APP in "${SELECTED_APPS[@]}"; do - # Capture the substring within (but not including) the parentheses. - # This substring represents the executable filename without the file extension (see above loop). - EXE_FILENAME_NOEXT="${SELECTED_APP##*(}" - EXE_FILENAME_NOEXT="${EXE_FILENAME_NOEXT%%)}" + for SELECTED_APP in "${SELECTED_APPS[@]}"; do + # Capture the substring within (but not including) the parentheses. + # This substring represents the executable filename without the file extension (see above loop). + EXE_FILENAME_NOEXT="${SELECTED_APP##*(}" + EXE_FILENAME_NOEXT="${EXE_FILENAME_NOEXT%%)}" - # Capture the substring prior to the space and parentheses. - # This substring represents the detected application name (see above loop). - PROGRAM_NAME="${SELECTED_APP% (*}" + # Capture the substring prior to the space and parentheses. + # This substring represents the detected application name (see above loop). + PROGRAM_NAME="${SELECTED_APP% (*}" - # Loop through all detected applications to find the detected application being processed. - for INDEX in "${!NAMES[@]}"; do - # Check for a matching detected application entry. - if [[ "${NAMES[$INDEX]}" == "$PROGRAM_NAME" ]] && [[ "${EXES[$INDEX]}" == *"\\$EXE_FILENAME_NOEXT"* ]]; then - # Print feedback. - echo -n "Creating an application entry for ${PROGRAM_NAME}... " + # Loop through all detected applications to find the detected application being processed. + for INDEX in "${!NAMES[@]}"; do + # Check for a matching detected application entry. + if [[ ${NAMES[$INDEX]} == "$PROGRAM_NAME" ]] && [[ ${EXES[$INDEX]} == *"\\$EXE_FILENAME_NOEXT"* ]]; then + # Print feedback. + echo -n "Creating an application entry for ${PROGRAM_NAME}... " - # Create directory to store application icon and information. - $SUDO mkdir -p "${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}" + # Create directory to store application icon and information. + $SUDO mkdir -p "${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}" - # Determine the content of the '.desktop' file for the application. - APP_DESKTOP_FILE="\ + # Determine the content of the '.desktop' file for the application. + APP_DESKTOP_FILE="\ # GNOME Shortcut Name NAME=\"${PROGRAM_NAME}\" # Used for Descriptions and Window Class @@ -1204,163 +1199,163 @@ CATEGORIES=\"WinApps\" # GNOME MIME Types MIME_TYPES=\"\"" - # Store the '.desktop' file for the application. - echo "$APP_DESKTOP_FILE" | $SUDO tee "${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}/info" &>/dev/null + # Store the '.desktop' file for the application. + echo "$APP_DESKTOP_FILE" | $SUDO tee "${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}/info" &>/dev/null - # Write application icon to file. - echo "${ICONS[$INDEX]}" | base64 -d | $SUDO tee "${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}/icon.ico" &>/dev/null + # Write application icon to file. + echo "${ICONS[$INDEX]}" | base64 -d | $SUDO tee "${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}/icon.ico" &>/dev/null - # Configure the application. - waConfigureApp "$EXE_FILENAME_NOEXT" ico + # Configure the application. + waConfigureApp "$EXE_FILENAME_NOEXT" ico - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" - fi - done - done - fi + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + fi + done + done + fi } # Name: 'waInstall' # Role: Installs WinApps. function waInstall() { - # Print feedback. - echo -e "${BOLD_TEXT}Installing WinApps.${CLEAR_TEXT}" + # Print feedback. + echo -e "${BOLD_TEXT}Installing WinApps.${CLEAR_TEXT}" - # Check for existing conflicting WinApps installations. - waCheckExistingInstall + # Check for existing conflicting WinApps installations. + waCheckExistingInstall - # Load the WinApps configuration file. - waLoadConfig + # Load the WinApps configuration file. + waLoadConfig - # Check for missing dependencies. - waCheckDependencies + # Check for missing dependencies. + waCheckDependencies - # Update $MULTI_FLAG. - if [[ "$MULTIMON" == "true" ]]; then - MULTI_FLAG="/multimon" - else - MULTI_FLAG="+span" - fi + # Update $MULTI_FLAG. + if [[ $MULTIMON == "true" ]]; then + MULTI_FLAG="/multimon" + else + MULTI_FLAG="+span" + fi - # Append additional FreeRDP flags if required. - if [[ -n "$RDP_FLAGS" ]]; then - FREERDP_COMMAND="${FREERDP_COMMAND} ${RDP_FLAGS}" - fi + # Append additional FreeRDP flags if required. + if [[ -n $RDP_FLAGS ]]; then + FREERDP_COMMAND="${FREERDP_COMMAND} ${RDP_FLAGS}" + fi - # Check the group membership of the current user. - waCheckGroupMembership + # Check the group membership of the current user. + waCheckGroupMembership - # Check if the Windows VM is powered on. - waCheckVMRunning + # Check if the Windows VM is powered on. + waCheckVMRunning - # Check if the Windows VM is contactable. - waCheckVMContactable + # Check if the Windows VM is contactable. + waCheckVMContactable - # Test RDP access to the Windows VM. - waCheckRDPAccess + # Test RDP access to the Windows VM. + waCheckRDPAccess - # Create required directories. - $SUDO mkdir -p "$BIN_PATH" - $SUDO mkdir -p "$APP_PATH" - $SUDO mkdir -p "$APPDATA_PATH/apps" - $SUDO mkdir -p "$APPDATA_PATH/icons" + # Create required directories. + $SUDO mkdir -p "$BIN_PATH" + $SUDO mkdir -p "$APP_PATH" + $SUDO mkdir -p "$APPDATA_PATH/apps" + $SUDO mkdir -p "$APPDATA_PATH/icons" - # Check for installed applications. - waFindInstalled + # Check for installed applications. + waFindInstalled - # Install the WinApps bash script. - $SUDO cp "./bin/winapps" "${BIN_PATH}/winapps" + # Install the WinApps bash script. + $SUDO cp "./bin/winapps" "${BIN_PATH}/winapps" - # Configure the Windows VM application launcher. - waConfigureWindows + # Configure the Windows VM application launcher. + waConfigureWindows - if [ "$OPT_AOSA" -eq 1 ]; then - # Automatically configure all officially supported applications. - waConfigureOfficiallySupported - else - # Configure officially supported applications. - waConfigureApps + if [ "$OPT_AOSA" -eq 1 ]; then + # Automatically configure all officially supported applications. + waConfigureOfficiallySupported + else + # Configure officially supported applications. + waConfigureApps - # Configure other detected applications. - waConfigureDetectedApps - fi + # Configure other detected applications. + waConfigureDetectedApps + fi - # Print feedback. - echo -e "${SUCCESS_TEXT}INSTALLATION COMPLETE.${CLEAR_TEXT}" + # Print feedback. + echo -e "${SUCCESS_TEXT}INSTALLATION COMPLETE.${CLEAR_TEXT}" } # Name: 'waUninstall' # Role: Uninstalls WinApps. function waUninstall() { - # Print feedback. - [ "$OPT_SYSTEM" -eq 1 ] && echo -e "${BOLD_TEXT}REMOVING SYSTEM INSTALLATION.${CLEAR_TEXT}" - [ "$OPT_USER" -eq 1 ] && echo -e "${BOLD_TEXT}REMOVING USER INSTALLATION.${CLEAR_TEXT}" + # Print feedback. + [ "$OPT_SYSTEM" -eq 1 ] && echo -e "${BOLD_TEXT}REMOVING SYSTEM INSTALLATION.${CLEAR_TEXT}" + [ "$OPT_USER" -eq 1 ] && echo -e "${BOLD_TEXT}REMOVING USER INSTALLATION.${CLEAR_TEXT}" - # Declare variables. - local WINAPPS_DESKTOP_FILES=() # Stores a list of '.desktop' file paths. - local WINAPPS_APP_BASH_SCRIPTS=() # Stores a list of bash script paths. - local DESKTOP_FILE_NAME="" # Stores the name of the '.desktop' file for the application. - local BASH_SCRIPT_NAME="" # Stores the name of the application. + # Declare variables. + local WINAPPS_DESKTOP_FILES=() # Stores a list of '.desktop' file paths. + local WINAPPS_APP_BASH_SCRIPTS=() # Stores a list of bash script paths. + local DESKTOP_FILE_NAME="" # Stores the name of the '.desktop' file for the application. + local BASH_SCRIPT_NAME="" # Stores the name of the application. - # Remove the 'WinApps' bash script. - $SUDO rm -f "${BIN_PATH}/winapps" + # Remove the 'WinApps' bash script. + $SUDO rm -f "${BIN_PATH}/winapps" - # Remove WinApps configuration data, temporary files and logs. - rm -rf "$USER_APPDATA_PATH" + # Remove WinApps configuration data, temporary files and logs. + rm -rf "$USER_APPDATA_PATH" - # Remove application icons and shortcuts. - $SUDO rm -rf "$APPDATA_PATH" + # Remove application icons and shortcuts. + $SUDO rm -rf "$APPDATA_PATH" - # Store '.desktop' files containing "${BIN_PATH}/winapps" in an array, returning an empty array if no such files exist. - readarray -t WINAPPS_DESKTOP_FILES < <(grep -l -d skip "${BIN_PATH}/winapps" "${APP_PATH}/"* 2>/dev/null || true) + # Store '.desktop' files containing "${BIN_PATH}/winapps" in an array, returning an empty array if no such files exist. + readarray -t WINAPPS_DESKTOP_FILES < <(grep -l -d skip "${BIN_PATH}/winapps" "${APP_PATH}/"* 2>/dev/null || true) - # Remove each '.desktop' file. - for DESKTOP_FILE_PATH in "${WINAPPS_DESKTOP_FILES[@]}"; do - # Trim leading and trailing whitespace from '.desktop' file path. - DESKTOP_FILE_PATH=$(echo "$DESKTOP_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + # Remove each '.desktop' file. + for DESKTOP_FILE_PATH in "${WINAPPS_DESKTOP_FILES[@]}"; do + # Trim leading and trailing whitespace from '.desktop' file path. + DESKTOP_FILE_PATH=$(echo "$DESKTOP_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - # Extract the file name. - DESKTOP_FILE_NAME=$(basename "$DESKTOP_FILE_PATH" | sed 's/\.[^.]*$//') + # Extract the file name. + DESKTOP_FILE_NAME=$(basename "$DESKTOP_FILE_PATH" | sed 's/\.[^.]*$//') - # Print feedback. - echo -n "Removing '.desktop' file for '${DESKTOP_FILE_NAME}'... " + # Print feedback. + echo -n "Removing '.desktop' file for '${DESKTOP_FILE_NAME}'... " - # Delete the file. - $SUDO rm "$DESKTOP_FILE_PATH" + # Delete the file. + $SUDO rm "$DESKTOP_FILE_PATH" - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" - done + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + done - # Store the paths of bash scripts calling 'WinApps' to launch specific applications in an array, returning an empty array if no such files exist. - readarray -t WINAPPS_APP_BASH_SCRIPTS < <(grep -l -d skip "${BIN_PATH}/winapps" "${BIN_PATH}/"* 2>/dev/null || true) + # Store the paths of bash scripts calling 'WinApps' to launch specific applications in an array, returning an empty array if no such files exist. + readarray -t WINAPPS_APP_BASH_SCRIPTS < <(grep -l -d skip "${BIN_PATH}/winapps" "${BIN_PATH}/"* 2>/dev/null || true) - # Remove each bash script. - for BASH_SCRIPT_PATH in "${WINAPPS_APP_BASH_SCRIPTS[@]}"; do - # Trim leading and trailing whitespace from bash script path. - BASH_SCRIPT_PATH=$(echo "$BASH_SCRIPT_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + # Remove each bash script. + for BASH_SCRIPT_PATH in "${WINAPPS_APP_BASH_SCRIPTS[@]}"; do + # Trim leading and trailing whitespace from bash script path. + BASH_SCRIPT_PATH=$(echo "$BASH_SCRIPT_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - # Extract the file name. - BASH_SCRIPT_NAME=$(basename "$BASH_SCRIPT_PATH" | sed 's/\.[^.]*$//') + # Extract the file name. + BASH_SCRIPT_NAME=$(basename "$BASH_SCRIPT_PATH" | sed 's/\.[^.]*$//') - # Print feedback. - echo -n "Removing bash script for '${BASH_SCRIPT_NAME}'... " + # Print feedback. + echo -n "Removing bash script for '${BASH_SCRIPT_NAME}'... " - # Delete the file. - $SUDO rm "$BASH_SCRIPT_PATH" + # Delete the file. + $SUDO rm "$BASH_SCRIPT_PATH" - # Print feedback. - echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" - done + # Print feedback. + echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}" + done - # Print caveats. - echo -e "\n${INFO_TEXT}Please note your WinApps configuration file was not removed.${CLEAR_TEXT}" - echo -e "${INFO_TEXT}You can remove this manually by running:${CLEAR_TEXT}" - echo -e "${COMMAND_TEXT}rm ${CONFIG_PATH}${CLEAR_TEXT}\n" + # Print caveats. + echo -e "\n${INFO_TEXT}Please note your WinApps configuration file was not removed.${CLEAR_TEXT}" + echo -e "${INFO_TEXT}You can remove this manually by running:${CLEAR_TEXT}" + echo -e "${COMMAND_TEXT}rm ${CONFIG_PATH}${CLEAR_TEXT}\n" - # Print feedback. - echo -e "${SUCCESS_TEXT}UNINSTALLATION COMPLETE.${CLEAR_TEXT}" + # Print feedback. + echo -e "${SUCCESS_TEXT}UNINSTALLATION COMPLETE.${CLEAR_TEXT}" } ### SEQUENTIAL LOGIC ### @@ -1388,9 +1383,9 @@ waConfigurePathsAndPermissions # Install or uninstall WinApps. if [ "$OPT_UNINSTALL" -eq 1 ]; then - waUninstall + waUninstall else - waInstall + waInstall fi exit 0