#!/usr/bin/env bash

# shellcheck shell=bash

################################################################################
######################### Main function (script init) ##########################
################################################################################

function __main__() {

  local _FUNCTION_ID="__main__"
  local _STATE="0"

  # Stores the current date.
  readonly _cdate=$(date +%Y%m%d)

  # Variables related to the log file. Divided into three parts due
  # to the better possibility of manipulation for the user.
  # shellcheck disable=SC2154
  readonly _log_file="${_init_name}.${_cdate}.log"
  # shellcheck disable=SC2154,SC2034
  readonly _log_stdout="${_log}/stdout.log"
  # shellcheck disable=SC2154,SC2034
  readonly _log_path="${_log}/${_log_file}"
  # shellcheck disable=SC2154,SC2034
  readonly _scan_output="${_log}/scan.out"

  # shellcheck disable=SC2154
  readonly _user_agent_fd="${_src}/useragents"

  # shellcheck disable=SC2154
  readonly _referer_fd="${_src}/referers"

  # We check if we are a root user.
  # if [[ "$EUID" -ne 0 ]] ; then
  #
  #   printf "EUID is not equal 0 (no root user)\\n"
  #   _exit_ "1"
  #
  # fi

  # Path to import file.
  # shellcheck disable=SC2154
  readonly _import_path="${_src}/import"

  # External configuration file (-c|--config script param).
  config=""
  load_state="0"

  # Declaration of output variables (--debug and --verbose params).
  stdout_mode=""
  # shellcheck disable=SC2034
  verbose_mode="false"

  # Enable/disable output colors.
  # shellcheck disable=SC2034
  s_color="true"

  # Default 'curl' command.
  local _curl_base=()
  local _curl_base_remote=()

  # Default 'domain' variables.
  local url_state="0"
  local url_type=""

  # shellcheck disable=SC2034
  # Default remote 'ip'.
  local _host_ip=""

  # Default destination host variables.
  # shellcheck disable=SC2034
  local _destination_host=""

  # Default `curl timers` variables.
  local _curl_timers="0"

  # Default 'ssl' variables.
  local ssl_state="0"

  # Default 'ssl test' variables.
  local ssl_test_state="0"
  local _last_http_scheme="http"

  # Default 'ssl' variables.
  local debug_ssl_state="0"

  # Default 'headers' variables.
  local dump_headers_state="0"

  # Default 'body' variables.
  local dump_body_state="0"

  # Default 'method' variables.
  local req_method_state="0"
  local req_method_type=("GET")
  local req_method_post=""
  local req_method_post_value=()
  local req_method_post_value_status="0"

  # Default 'header' variables.
  local req_header_state="0"
  local req_header_type=()
  local req_header_type_all=()
  local req_header_type_all_info=""
  local req_header_type_all_info_fin=""

  # Default 'proxy' variables.
  local proxy_state="0"
  local proxy_type=""

  # Default 'resolve' variables.
  local resolve_state="0"
  local resolve_type=""
  local resolve_type_ip=""
  local resolve_type_port=""
  local resolve_type_port_num=""
  local _resolve_type_data=""

  # Default 'interface' variables.
  local iface_state="0"
  local iface_type=""

  # Default state for all external security tools.
  local all_scans_state="0"

  # Default 'observatory' variables.
  local observatory_state="0"

  # Default 'ssllabs' variables.
  local ssllabs_state="0"

  # Default 'mixed-content' variables.
  local mixed_content_state="0"

  # Default 'nmap-nse' variables.
  local nse_state="0"

  # Default 'waf' variables.
  local waf_state="0"

  # Default 'subfinder' variables.
  local subfinder_state="0"

  # Default 'nghttp' variables.
  local nghttp2_state="0"

  # Default 'openssl' variables.
  local openssl_bin_state=0
  local openssl_bin_path=""
  local _openssl=""

  # Default 'cache-bypass' variables.
  local cache_bypass_state="0"
  local cache_bypass_type=""

  # Set default User-Agent.
  # shellcheck disable=SC2034
  local user_agent_state="0"
  local _user_agent=""

  # Set Referer.
  # shellcheck disable=SC2034
  local referer_state="0"
  local _referer=""

  # Default 'authentication' variable.
  # shellcheck disable=SC2034
  local auth_state="0"
  local _auth_type=""
  local _auth_type_curl=""

  # Set default http version.
  # shellcheck disable=SC2034
  local http_version_state="0"
  local _http_version=""
  local _http_version_type_sep="--"
  local _http_version_type=""
  local _http_version_msg=""

  local _http_version_type_curl=""

  # Set default ssl/tls.
  # shellcheck disable=SC2034
  local ssl_version_state="0"
  local _ssl_version=""
  local _ssl_version_type_sep="-"
  local _ssl_version_type=""
  local _ssl_version_msg=""
  local _ssl_version_openssl_type=""

  local _ssl_version_type_curl=""
  local _ssl_version_openssl_curl=""

  # shellcheck disable=SC2034
  local _ssl_connect_debug=""

  # shellcheck disable=SC2034
  local debug_conn_state="0"
  # shellcheck disable=SC2034
  local _curl_conn_debug=""

  # Set default ciphers.
  # shellcheck disable=SC2034
  local ciphers_state="0"
  local _ciphers_type=""

  # Set redirects limit.
  local max_redirects_state="0"
  local _redirects=10

  # Set default scan type.
  # shellcheck disable=SC2034
  local _scan_type=""

  # Default OpenSSL version state.
  _openssl_version_state="0"

  # Default timeout value.
  local _timeout="15"

  # Default ip-mask value.
  local _hide_src_ip_state="0"

  # shellcheck disable=SC2034
  local _rd="0;0;0"

  # shellcheck disable=SC2034
  local _ccmd=()

  # Set default colors for printf.
  # For more info please see:
  #   - https://github.com/trimstray/htrace.sh/wiki/Colors
  #   - recommended terminal color schemes:
  #     - deep (https://github.com/mbadolato/iTerm2-Color-Schemes#deep)
  if [[ "$HTRACE_COLORS" == "on" ]] ; then

    # shellcheck disable=SC2034
    local trgb_3012="1;2;30"
    # shellcheck disable=SC2034
    local trgb_3021="0;1;30"
    # shellcheck disable=SC2034
    local trgb_3111="1;1;31"
    # shellcheck disable=SC2034
    local trgb_3100="0;0;31"
    # shellcheck disable=SC2034
    local trgb_3211="1;1;32"
    # shellcheck disable=SC2034
    local trgb_3200="0;0;32"
    # shellcheck disable=SC2034
    local trgb_3341="4;1;33"
    # shellcheck disable=SC2034
    local trgb_3300="0;0;33"
    # shellcheck disable=SC2034
    local trgb_3310="0;1;33"
    # shellcheck disable=SC2034
    local trgb_3612="1;2;36"
    # shellcheck disable=SC2034
    # local trgb_3722="2;2;37"
    local trgb_3722="$trgb_3021"
    # shellcheck disable=SC2034
    local trgb_3722b="2;2;37"
    # shellcheck disable=SC2034
    local trgb_3720="2;0;37"
    # shellcheck disable=SC2034
    local trgb_3811="1;1;38"
    # shellcheck disable=SC2034
    # local trgb_3812="1;2;32"
    local trgb_3812="$trgb_3722b"
    # shellcheck disable=SC2034
    local trgb_3843="4;3;38"
    # shellcheck disable=SC2034
    local trgb_40631="1;31;40"
    # shellcheck disable=SC2034
    # local trgb_40337="3;37;40"
    local trgb_40337="3;37;20"
    # shellcheck disable=SC2034
    local trgb_40636="6;36;40"
    # shellcheck disable=SC2034
    local trgb_44139="1;39;44"
    # shellcheck disable=SC2034
    local trgb_9312="1;2;93"
    # shellcheck disable=SC2034
    local trgb_9420="2;0;94"

  else

    # shellcheck disable=SC2034
    local trgb_3012="$_rd"
    # shellcheck disable=SC2034
    local trgb_3021="$_rd"
    # shellcheck disable=SC2034
    local trgb_3111="$_rd"
    # shellcheck disable=SC2034
    local trgb_3100="$_rd"
    # shellcheck disable=SC2034
    local trgb_3211="$_rd"
    # shellcheck disable=SC2034
    local trgb_3200="$_rd"
    # shellcheck disable=SC2034
    local trgb_3341="$_rd"
    # shellcheck disable=SC2034
    local trgb_3300="$_rd"
    # shellcheck disable=SC2034
    local trgb_3310="$_rd"
    # shellcheck disable=SC2034
    local trgb_3612="$_rd"
    # shellcheck disable=SC2034
    local trgb_3722="$_rd"
    # shellcheck disable=SC2034
    local trgb_3720="$_rd"
    # shellcheck disable=SC2034
    local trgb_3811="$_rd"
    # shellcheck disable=SC2034
    local trgb_3812="$_rd"
    # shellcheck disable=SC2034
    local trgb_40631="$_rd"
    # shellcheck disable=SC2034
    local trgb_40337="$_rd"
    # shellcheck disable=SC2034
    local trgb_40636="$_rd"
    # shellcheck disable=SC2034
    local trgb_44139="$_rd"
    # shellcheck disable=SC2034
    local trgb_9312="$_rd"
    # shellcheck disable=SC2034
    local trgb_9420="$_rd"

  fi

  if [[ "$HIDE_SRC_IP" == "on" ]] ; then

    _hide_src_ip_state="1"

  fi

  # We place here used commands at script runtime, as strings to anything
  # unnecessarily run.
  readonly commands=(basename dirname stat ps date grep cut sed awk chown \
                     chmod mkdir curl openssl dig mmdblookup bc jq fmt \
                     testssl.sh observatory ssllabs-scan nghttp \
                     mixed-content-scan nmap wafw00f subfinder)

  # If you intend to specify the full path to the command we do it like:
  # readonly exec_gzip="/bin/gzip"

  # Stores the names of the missing commands.
  missing_hash=()
  missing_counter="0"

  for i in "${commands[@]}" ; do

    if [[ -n "$i" ]] ; then

      hash "$i" >/dev/null 2>&1 ; state="$?"

      # If the command was not found put it in the array
      if [[ "$state" -ne 0 ]] ; then

        missing_hash+=("$i")
        ((missing_counter++))

      fi

    fi

  done

  # It is a good idea to terminate the script at this stage
  # with information for the user to fix the errors if at least one
  # of the required commands in the commands array is not found.
  if [[ "$missing_counter" -gt 0 ]] ; then

    printf "not found in PATH: %s\\n" "${missing_hash[*]}" >&2
    _exit_ "1"

  fi

  # Running tasks before start user functions.
  _before_init

  # shellcheck disable=SC2154
  _logger "init" \
    "init '${_init_name}' in '${_init_directory}'" && \
  _logger "info" \
    "__init_params[] = (${__init_params[*]})," \
    "__script_params[] = (${__script_params[*]})"

  # Include import file.
  _load "null" "$_import_path"

  # Specifies the call parameters of the script, the exact description
  # can be found in _help_ and file README.md.
  local _short_opt="u:shbM:H:p:r:i:ta"
  local _long_opt="help,version,examples,url:,ssl,headers,body,"
        _long_opt+="req-method:,req-header:,proxy:,resolve:,iface:,timers,"
        _long_opt+="all-scans,testssl,observatory,ssllabs,mixed-content,"
        _long_opt+="nse,waf,dns,http2,conn-debug,ssl-bin:,ssl-debug,"
        _long_opt+="cache-bypass:,user-agent:,referer:,auth:,httpv:,tlsv:,ciph:,"
        _long_opt+="max-redirects:,timeout:,hide-src-ip"

  _GETOPT_PARAMS=$(getopt -o "${_short_opt}" --long "${_long_opt}" \
                   -n "${_init_name}" -- "${__script_params[@]}")

  # With this structure, in the case of problems with the parameters placed
  # in the _GETOPT_PARAMS variable we finish the script. Keep this in mind
  # because it has some consequences - the __main __() function will not be
  # executed.

  # Ends an error if the parameter or its argument is not valid.
  _getopt_state="$?"
  if [[ "$_getopt_state" != 0 ]] ; then
    _exit_ "1"
  # Ends if no parameter is specified.
  elif [[ "${#__script_params[@]}" -eq 0 ]] ; then
    _exit_ "0"
  fi

  eval set -- "$_GETOPT_PARAMS"
  while true ; do

    case $1 in

      --help)

        _help_

        shift ; _exit_ "0" ;;

      --version)

        # shellcheck disable=SC2154
        printf "%s, %s\\n" "htrace.sh" "$_version"

        shift ; _exit_ "0" ;;

      --examples)

        _examples_

        shift ; _exit_ "0" ;;

      -u|--url)

        export url_state=1

        export url_type="${2}"

        shift 2 ;;

      -s|--ssl)

        export ssl_state=1

        shift ;;

      -h|--headers)

        export dump_headers_state=1

        shift ;;

      -b|--body)

        export dump_body_state=1

        shift ;;

      -M|--req-method)

        export req_method_state=1

        export req_method_type=("${2}")

        shift 2 ;;

      -H|--req-header)

        export req_header_state=1

        export req_header_type=("${2}")

        shift 2 ;;

      -p|--proxy)

        export proxy_state=1

        export proxy_type="${2}"

        shift 2 ;;

      -r|--resolve)

        export resolve_state=1

        export resolve_type="${2}"

        shift 2 ;;

      -i|--iface)

        export iface_state=1

        export iface_type="${2}"

        shift 2 ;;

      -t|--timers)

        export _curl_timers=1

        shift ;;

      -a|--all-scans)

        export all_scans_state=1

        shift ;;

      --testssl)

        export ssl_test_state=1

        shift ;;

      --observatory)

        export observatory_state=1

        shift ;;

      --ssllabs)

        export ssllabs_state=1

        shift ;;

      --mixed-content)

        export mixed_content_state=1

        shift ;;

      --nse)

        export nse_state=1

        shift ;;

      --waf)

        export waf_state=1

        shift ;;

      --dns)

        export subfinder_state=1

        shift ;;

      --http2)

        export nghttp2_state=1

        shift ;;

      --conn-debug)

        export debug_conn_state=1

        shift ;;

      --ssl-bin)

        export openssl_bin_state=1

        export openssl_bin_path="${2}"

        shift 2 ;;

      --ssl-debug)

        export debug_ssl_state=1

        shift ;;

      --cache-bypass)

        export cache_bypass_state=1

        export cache_bypass_type="${2}"

        shift 2 ;;

      --user-agent)

        export user_agent_state=1

        export _user_agent="${2}"

        HTTP_USER_AGENT="off"

        shift 2 ;;

      --referer)

        export referer_state=1

        export _referer="${2}"

        HTTP_REFERER="off"

        shift 2 ;;

      --auth)

        export auth_state=1

        export _auth_type="${2}"

        shift 2 ;;

      --httpv)

        export _http_version_state=1

        export _http_version="${2}"

        shift 2 ;;

      --tlsv)

        export _ssl_version_state=1

        export _ssl_version="${2}"

        shift 2 ;;

      --ciph)

        export ciphers_state=1

        export _ciphers_type="${2}"

        shift 2 ;;

      --max-redirects)

        export max_redirects_state=1

        export _redirects="${2}"

        shift 2 ;;

      --timeout)

        export timeout_state=1

        export _timeout="${2}"

        shift 2 ;;

      --hide-src-ip)

        export _hide_src_ip_state=1

        shift ;;

      *)

        if [[ "$2" == "-" ]] || [[ -n "$2" ]] ; then

          printf "%s: invalid option -- '%s'\\n" "$_init_name" "$2"
          _exit_ "1"

        # elif [[ -z "$2" ]] ; then break ; fi
        else break ; fi

        ;;

    esac

  done

  # If you run the script in debug mode, the information
  # will be displayed on the screen from this point.
  if [[ "$stdout_mode" == "debug" ]] ; then

    _logger "info" \
      "${_FUNCTION_ID}()" \
      "starting debug mode"

  fi

  ################################# USER SPACE #################################
  # ````````````````````````````````````````````````````````````````````````````
  # Put here all your variable declarations, function calls
  # and all the other code blocks.

  # In this section we add external file (for -c|--config script param).
  if [[ "$load_state" -eq 1 ]] ; then _load "head" "$config" ; fi

  # shellcheck disable=SC2034
  # Generate random value.
  _random=$(date +"%s")

  if [[ "$all_scans_state" -eq 1 ]] ; then

    export ssl_test_state=1
    export observatory_state=1
    export ssllabs_state=1
    export mixed_content_state=1
    export nse_state=1
    export waf_state=1
    export subfinder_state=1
    export nghttp2_state=1

  fi

  # shellcheck disable=SC2154
  if [[ "${req_method_type[*]}" == *"POST"* ]] ; then

    # shellcheck disable=SC2207
    req_method_post=($(echo "${req_method_type[@]}" | awk -v FS="(:|:)" '{print $1}'))
    # shellcheck disable=SC2207
    req_method_post_value=($(echo "${req_method_type[@]}" | cut -d ':' -f2-))

    if [[ "${req_method_post[*]}" == "POST" ]] ; then

      req_method_type=("POST")

    fi

    if [[ -z "${req_method_post_value[*]}" ]] || \
       [[ "${req_method_post[*]}" == "${req_method_post_value[*]}" ]] ; then

      req_method_post_value=()
      req_method_post_value_status="1"

      req_method_type=("GET")

    fi

  fi

  # shellcheck disable=SC2154
  # if [[ "${req_method_type[@]}" != "GET" ]] && \
  #    [[ "${req_method_type[@]}" != "HEAD" ]] && \
  #    [[ "${req_method_type[@]}" != "POST" ]] && \
  #    [[ "${req_method_type[@]}" != "PUT" ]] && \
  #    [[ "${req_method_type[@]}" != "DELETE" ]] && \
  #    [[ "${req_method_type[@]}" != "CONNECT" ]] && \
  #    [[ "${req_method_type[@]}" != "OPTIONS" ]] && \
  #    [[ "${req_method_type[@]}" != "TRACE" ]] ; then

  if [[ -z "${req_method_type[*]}" ]] ; then

    req_method_type=("GET")

  fi

  IFS_ORIG="$IFS"

  if [[ "$req_header_state" -eq 1 ]] ; then

    IFS=$'\n'

    # shellcheck disable=SC2001,SC2207
    req_header_type_raw=($(echo "${req_header_type[@]}" | sed 's/ ; /\n/g'))

    IFS=$IFS_ORIG

    for i in "${req_header_type_raw[@]}" ; do

      req_header_type_all_info+="$i, "

      req_header_type_all+=("-H '${i}'")

    done

    # shellcheck disable=SC2001
    req_header_type_all_info_fin=$(echo "$req_header_type_all_info" | sed '$s/, $//')

  fi

  # shellcheck disable=SC2154
  if [[ "$proxy_state" -eq 1 ]] ; then

    # shellcheck disable=SC2034
    if [[ "${req_method_type[*]}" == "POST" ]] ; then

      # shellcheck disable=SC2034
      _curl_base=("curl --proxy $proxy_type --request ${req_method_type[@]} -d ${req_method_post_value[@]}")

    else

      # shellcheck disable=SC2034
      _curl_base=("curl --proxy $proxy_type --request ${req_method_type[@]}")

    fi

    # shellcheck disable=SC2034
    _curl_base_remote=("curl --proxy $proxy_type")

  else

    # shellcheck disable=SC2034
    if [[ "${req_method_type[*]}" == "POST" ]] ; then

      # shellcheck disable=SC2034
      _curl_base=("curl --request ${req_method_type[@]} -d ${req_method_post_value[@]}")

    else

      # shellcheck disable=SC2034
      _curl_base=("curl --request ${req_method_type[@]}")

    fi

    # shellcheck disable=SC2034
    _curl_base_remote=("curl")

  fi

  if [[ "$HTTP_USER_AGENT" == "on" ]] ; then

    # Load user-agents file.
    if [[ "$user_agent_state" -eq 0 ]] ; then

      _load "null" "$_user_agent_fd"

      # Pick a random entry from the user agents list.
      _user_agent=${USER_AGENTS[$_random % ${#USER_AGENTS[@]}]}

    fi

  fi

  if [[ "$HTTP_REFERER" == "on" ]] ; then

    # Load referers file.
    if [[ "$referer_state" -eq 0 ]] ; then

      RND_STRING=(\
        "bing" \
        "duckduckgo" \
        "wiki" \
        "twitter" \
        "search.creativecommons" \
        "gibiru" \
        "archive" \
        "searchencrypt" \
        "yandex" \
        "stargpage" \
        "swisscows" \
        "google" \
        "boardreader" \
        "feedly" \
        "slideshare"
        )

      # shellcheck disable=SC2034
      _referer_string=${RND_STRING[$_random % ${#RND_STRING[@]}]}

      _load "null" "$_referer_fd"

      # Pick a random entry from the user agents list.
      _referer=${REFERERS[$_random % ${#REFERERS[@]}]}

    fi

  fi

  # Array that stores the names of variables used that are part of the script
  # call parameters (_GETOPT_PARAMS). Useful when checking whether all
  # or selected parameters without which the script can not work properly
  # have been used. Do not add the load_state variable to the _opt_values array,
  # which is supported above.
  _opt_values=("url_state" "url_type")

  # Checking the value of the variables (if they are unset or empty):
  #   - variables for call parameters
  #   - variables from the additional configuration files
  if [[ "${#_opt_values[@]}" -ne 0 ]] ; then

    for i in "${_opt_values[@]}" ; do

      _i="" ; eval _i='$'"$i"

      _logger "info" \
        "${_FUNCTION_ID}()" \
        "$i: '$_i'"

      if [[ -z "$_i" ]] ; then

        _sprintf "stop" "error of argument value: '$i' is unset or empty"

        _logger "stop" \
          "${_FUNCTION_ID}()" \
          "error of argument value: '$i' is unset or empty"

      fi

    done

  fi

  if [[ "$openssl_bin_state" -eq 0 ]] ; then

    if [[ -z "$HTRACE_SSL_BIN_PATH" ]] ; then

      _openssl=$(command -v openssl)

    else

      _openssl="$HTRACE_SSL_BIN_PATH"

    fi

  fi

  # shellcheck disable=2154
  if [[ "$openssl_bin_state" -eq 1 ]] ; then

    _openssl="$openssl_bin_path"

    if [[ -e "$_openssl" ]] ; then

      # OpenSSL version.
      _openssl_version=$($_openssl version -a | head -n 1 | grep 'OpenSSL\|LibreSSL' | awk '{print $2}')
      _openssl_status="$?"

      if [[ "$_openssl_status" -eq 1 ]] ; then

        _openssl=$(command -v openssl)
        openssl_bin_state=0

      fi

    else

      printf '\n  %s %s\n\n' \
              "Not found openssl binary in" "$_openssl"

      _logger "stop" \
        "${_FUNCTION_ID}()" \
        "Not found openssl binary in $_openssl"

    fi

  else

    # OpenSSL version.
    _openssl_version=$($_openssl version -a | head -n 1 | grep 'OpenSSL\|LibreSSL' | awk '{print $2}')
    _openssl_status="$?"

    openssl_bin_state=0

  fi

  # Strip domain name - first check.
  DomainStrip "$url_type"

  # shellcheck disable=SC2154
  # Set -u|--url value to variable.
  local _http_redirect="${_http_proto}${_http_url_with_args}"

  # shellcheck disable=SC2154
  printf '\n    \e['${trgb_44139}'m %s %s \e[m' \
         "$_init_name" "$_version"

  if [[ "$_openssl_version" == "1.0.2-chacha" ]] || \
     [[ "$_openssl_version" == "1.0.2g" ]] || \
     [[ "$_openssl_version" == "1.0.2k-fips" ]] || \
     [[ "$_openssl_version" == "1.1.0g" ]] || \
     [[ "$_openssl_version" == "1.1.0h" ]] || \
     [[ "$_openssl_version" == "1.1.0j" ]] || \
     [[ "$_openssl_version" == "1.1.1" ]] || \
     [[ "$_openssl_version" == "1.1.1a" ]] || \
     [[ "$_openssl_version" == "1.1.1b" ]] || \
     [[ "$_openssl_version" == "1.1.1c" ]] || \
     [[ "$_openssl_version" == "1.1.1d" ]] || \
     [[ "$_openssl_version" == "1.1.1e" ]] || \
     [[ "$_openssl_version" == "1.1.1f" ]] || \
     [[ "$_openssl_version" == "1.1.1g" ]] ; then

    _openssl_version_status="ok"
    _openssl_version_state="1"

  else

    _openssl_version_status="not tested"
    _openssl_version_state="0"

  fi

  if [[ "$_openssl_version_state" -eq 1 ]] ; then

    # printf ' (%s, %s : \e['${trgb_3211}'m%s\e[m)\n\n' \
    #        "$_openssl" "$_openssl_version" "$_openssl_version_status"
    printf ' (openssl %s : \e['${trgb_3211}'m%s\e[m)\n\n' \
           "$_openssl_version" "$_openssl_version_status"

  else

    printf ' (openssl %s : \e['${trgb_3111}'m%s\e[m)\n\n' \
           "$_openssl_version" "$_openssl_version_status"

  fi

  if [[ "$resolve_state" -eq 1 ]] ; then

    resolve_type_ip=$(echo "$resolve_type" | awk -v FS="(:|:)" '{print $1}')
    resolve_type_port=$(echo "$resolve_type" | awk -v FS="(:|:)" '{print $2}')

    if [[ -z "$resolve_type_port" ]] ; then

      # shellcheck disable=SC2154
      if [[ "$_http_proto_only" == "http" ]] ; then

        resolve_type_port_num="80"

      else

        resolve_type_port_num="443"

      fi

    else

      resolve_type_port_num="$resolve_type_port"

    fi

    # shellcheck disable=SC2154
    _resolve_type_data="${_domain_name}:${resolve_type_port_num}:${resolve_type_ip}"
    _resolve_type_curl="--resolve $_resolve_type_data"

  else

    _resolve_type_data=""
    _resolve_type_curl=""

  fi

  if [[ "$iface_state" -eq 1 ]] ; then

    _interface_type_curl="--interface $iface_type"

  else

    _interface_type_curl=""

  fi

  if [[ "$_curl_timers" -eq 1 ]] ; then

    # shellcheck disable=SC2034
    CURL_TIMERS="on"

  fi

  if [[ "$referer_state" -eq 1 ]] ; then

    _referer_curl="--referer $_referer"

  fi

  if [[ "$auth_state" -eq 1 ]] ; then

    _h_auth_type=$(echo "${_auth_type}" | cut -d ":" -f1 | sed 's/ //g')
    _h_auth_key=$(echo "${_auth_type}" | cut -d ":" -f2 | sed 's/ //g')
    _h_auth_value=$(echo "${_auth_type}" | cut -d ":" -f3 | sed 's/ //g')

    if [[ -n "$_h_auth_key" ]] && \
       [[ -n "$_h_auth_value" ]] ; then

      if [[ "$_h_auth_type" == "basic" ]] ; then

        _auth_type_curl="--user ${_h_auth_key}:${_h_auth_value}"

      elif [[ "$_h_auth_type" == "proxy" ]] ; then

        if [[ "$proxy_state" -eq 1 ]] ; then

          _auth_type_curl="--proxy-user ${_h_auth_key}:${_h_auth_value}"

        else

          _auth_type_curl="--user ${_h_auth_key}:${_h_auth_value}"

        fi

      fi

    elif [[ -n "$_h_auth_key" ]] && \
         [[ -z "$_h_auth_value" ]] ; then

      if [[ "$_h_auth_type" == "cookie" ]] ; then

        if [[ -e "$_h_auth_key" ]] ; then

          _auth_type_curl="--cookie ${_h_auth_key}"

        else

          _auth_type_curl=""

        fi

      fi

    elif [[ -z "$_h_auth_key" ]] && \
         [[ -z "$_h_auth_value" ]] ; then

      if [[ "$_h_auth_type" == "cookie" ]] ; then

        if [[ -e "$HTRACE_COOKIE_PATH" ]] ; then

          _auth_type_curl="--cookie ${HTRACE_COOKIE_PATH}"

        else

          _auth_type_curl=""

        fi

      fi

    else

      _auth_type_curl=""

    fi

  else

    _auth_type_curl=""

  fi

  if [[ "$_http_version_state" -eq 1 ]] ; then

    if [[ "$_http_version" == "1.0" ]] || \
       [[ "$_http_version" == "1.1" ]] || \
       [[ "$_http_version" == "2" ]] || \
       [[ "$_http_version" == "3" ]] ; then

      _http_version_type="http${_http_version}"
      _http_version_msg="$_http_version"

    else

      _http_version_type_sep=""
      _http_version_type=""
      _http_version_msg="not supported"

    fi

    _http_version_type_curl="${_http_version_type_sep}${_http_version_type}"

  fi

  if [[ "$_ssl_version_state" -eq 1 ]] ; then

    if [[ "$_ssl_version" == "ssl2" ]] || \
       [[ "$_ssl_version" == "ssl3" ]] ; then

      # Default values:
      _ssl_version_type_sep=""
      _ssl_version_type=""
      _ssl_version_msg=""
      _ssl_version_openssl_type=""

    elif [[ "$_ssl_version" == "1.0" ]] ; then

      _ssl_version_type="tlsv1.0"
      _ssl_version_openssl_type="tls1"
      _ssl_version_msg="$_ssl_version"

    elif [[ "$_ssl_version" == "1.1" ]] || \
         [[ "$_ssl_version" == "1.2" ]] ; then

      _ssl_version_type="tlsv${_ssl_version}"
      _ssl_version_openssl_type="tls${_ssl_version//./_}"
      _ssl_version_msg="$_ssl_version"

    elif [[ "$_ssl_version" == "1.3" ]] ; then

      if [[ "$_openssl_version" == "1.1.1"* ]] ; then

        _ssl_version_type="tlsv${_ssl_version}"
        _ssl_version_openssl_type="tls${_ssl_version//./_}"
        _ssl_version_msg="$_ssl_version"

      else

        # Default values:
        _ssl_version_type_sep=""
        _ssl_version_type=""
        _ssl_version_msg="not supported"
        _ssl_version_openssl_type=""

      fi

    else

      # Default values:
      _ssl_version_type_sep=""
      _ssl_version_type=""
      _ssl_version_msg=""
      _ssl_version_openssl_type=""

    fi

    _ssl_version_type_curl="${_ssl_version_type_sep}${_ssl_version_type}"
    # shellcheck disable=SC2034
    _ssl_version_openssl_curl="${_ssl_version_type_sep}${_ssl_version_openssl_type}"

  fi

  # shellcheck disable=2154
  if [[ "$ciphers_state" -eq 1 ]] ; then

    if [[ -n "$_ciphers_type" ]] ; then

      # shellcheck disable=2034
      _ciphers_type_curl="--ciphers $_ciphers_type"
      # shellcheck disable=2034
      _ciphers_openssl_curl="-cipher $_ciphers_type"

    else

      # shellcheck disable=2034
      _ciphers_type_curl=""
      # shellcheck disable=2034
      _ciphers_openssl_curl=""

    fi

  fi

  # For -r|--resolve:
  _curl_base+=("$_resolve_type_curl")
  _curl_base_remote+=("$_resolve_type_curl")

  # For --iface:
  _curl_base+=("$_interface_type_curl")
  _curl_base_remote+=("$_interface_type_curl")

  # For --referer:
  _curl_base+=("$_referer_curl")
  _curl_base_remote+=("$_referer_curl")

  # For --auth:
  _curl_base+=("$_auth_type_curl")
  _curl_base_remote+=("$_auth_type_curl")

  # For --http:
  _curl_base+=("$_http_version_type_curl")
  _curl_base_remote+=("$_http_version_type_curl")

  # For --tlsv:
  _curl_base+=("$_ssl_version_type_curl")
  _curl_base_remote+=("$_ssl_version_type_curl")

  # For --ciph:
  _curl_base+=("$_ciphers_type_curl")
  _curl_base_remote+=("$_ciphers_type_curl ")

  printf '\n    URI: \e['${trgb_3341}'m%s\e['${trgb_3341}'m%s\e[m\n' \
         "$_http_proto" "$_http_url_with_args"

  # Show http(s) domain trace.
  if [[ "${req_method_type[*]}" == "POST" ]] ; then

    if [[ "$req_method_post_value_status" -eq 1 ]] ; then

      printf '\n         » request-method: \e['${trgb_40631}'m%s\e[m\n' \
             "${req_method_type[@]}"

    else

      printf '\n         » request-method: \e['${trgb_40631}'m%s\e[m\n                           \e['${trgb_40337}'m%s\e[m\n' \
             "${req_method_type[@]}" "$(echo "${req_method_post_value[@]}" | tr -d "'")"

    fi

  else

    printf '\n         » request-method: \e['${trgb_40631}'m%s\e[m\n' \
           "${req_method_type[@]}"

  fi

  if [[ "$_http_version_state" -eq 1 ]] ; then

    if [[ -n "$_http_version_type" ]] || \
       [[ "$_ssl_version_msg" == "not supported" ]] ; then

      printf '         » http-version:   \e['${trgb_40337}'m%s\e[m\n' \
             "$_http_version_msg"

    fi

  fi

  if [[ "$debug_conn_state" -eq 1 ]] ; then

    printf '         » conn-debug:     \e['${trgb_40337}'m%s\e[m\n' \
           "on"

  fi

  if [[ "$debug_ssl_state" -eq 1 ]] ; then

    printf '         » ssl-debug:      \e['${trgb_40337}'m%s\e[m\n' \
           "on"

  fi

  if [[ "$auth_state" -eq 1 ]] ; then

    if [[ -n "$_auth_type_curl" ]] ; then

      printf '         » authentication: \e['${trgb_40631}'m%s\e[m\n                           \e['${trgb_40337}'m%s:%s\e[m\n' \
             "$_h_auth_type" "$_h_auth_key" "$_h_auth_value"

    fi

  fi

  if [[ "$req_header_state" -eq 1 ]] ; then

    printf '         » request-header: \e['${trgb_40337}'m%s\e[m\n' \
           "${req_header_type_all_info_fin}"

  fi

  if [[ "$user_agent_state" -eq 1 ]] ; then

    printf '         » user-agent:     \e['${trgb_40337}'m%s\e[m\n' \
           "$_user_agent"

  elif [[ "$user_agent_state" -eq 0 ]] && \
       [[ "$HTTP_USER_AGENT" == "on" ]] ; then

    printf '         » user-agent:     \e['${trgb_40337}'m%s\e[m\n' \
           "$_user_agent"

  fi

  if [[ "$referer_state" -eq 1 ]] ; then

    printf '         » referer:        \e['${trgb_40337}'m%s\e[m\n' \
           "$_referer"

  elif [[ "$referer_state" -eq 0 ]] && \
       [[ "$HTTP_REFERER" == "on" ]] ; then

    printf '         » referer:        \e['${trgb_40337}'m%s\e[m\n' \
           "$_referer"

  fi

  if [[ "$cache_bypass_state" -eq 1 ]] ; then

    printf '         » cache-bypass:   \e['${trgb_40337}'m%s\e[m\n' \
           "$cache_bypass_type"

  fi

  if [[ "$_ssl_version_state" -eq 1 ]] ; then

    if [[ -n "$_ssl_version_type" ]] || \
       [[ "$_ssl_version_msg" == "not supported" ]] ; then

      printf '         » tls-version:    \e['${trgb_40337}'m%s\e[m\n' \
             "$_ssl_version_msg"

    fi

  fi

  if [[ "$ciphers_state" -eq 1 ]] ; then

    if [[ -n "$_ciphers_type" ]] ; then

      printf '         » ciphersuite:    \e['${trgb_40337}'m%s\e[m\n' \
             "$_ciphers_type"

    fi

  fi

  if [[ "$resolve_state" -eq 1 ]] ; then

    printf '         » force-resolve:  \e['${trgb_40337}'m%s\e[m\n' \
           "$_resolve_type_data"

  fi

  if [[ "$iface_state" -eq 1 ]] ; then

    printf '         » interface:      \e['${trgb_40337}'m%s\e[m\n' \
           "$iface_type"

  fi

  # Start tracing.
  local _http_code="300"

  # Temporary variables.
  local _req_num=0
  # shellcheck disable=SC2034
  local _full_conn_time="0.000000"

  # shellcheck disable=SC2034
  local _rcounter=1
  # shellcheck disable=SC2034
  local _hcounter=1
  # shellcheck disable=SC2034
  local _scounter=1

  # Core mechanism of htrace.sh - redirects.
  while [[ ${_http_code} =~ ^3[0-9]{2}$ ]] ; do

    # Prevention of redirection loops.
    if [[ "$_rcounter" -gt "$_redirects" ]] ; then

      if [[ "$max_redirects_state" -eq 1 ]] ; then

        printf '\n    %s: %s\n\n' \
               "Max redirects" "$_redirects"

        _logger "stop" \
          "${_FUNCTION_ID}()" \
          "Max redirects: $_redirects"

      else

        printf '\n    %s %s\n\n' \
               "Redirect loop: limit is" "$_redirects"

        _logger "stop" \
          "${_FUNCTION_ID}()" \
          "Redirect loop: limit is $_redirects"

      fi

    else

      # shellcheck disable=SC2034,SC2154
      _req_num=$((_req_num + 1))

      _http_last="$_http_redirect"

      # Init trace function.
      InitTrace "$_http_redirect"

      # shellcheck disable=SC2154
      if [[ "$debug_ssl_state" -eq 1 ]] && [[ "$_http_scheme" == "https" ]] ; then

        echo

      fi

      _rcounter=$((_rcounter + 1))

    fi

  done

  # shellcheck disable=SC2154
  if [[ "$debug_ssl_state" -eq 1 ]] && [[ "$_http_scheme" == "https" ]] ; then

    true

  else

    echo

  fi

  # shellcheck disable=SC2154
  if [[ "$ssl_test_state" -eq 1 ]] ; then

    if [[ "$_last_http_scheme" == "https" ]] ; then

      DomainStrip "$_http_last"

      _init_function "DomainScanTestssl \"${_http_proto}${_domain_name}\""

      _fstate="$?"

      if [[ "$_fstate" -ne 0 ]] ; then

        # shellcheck disable=SC2086,SC2154
        printf '\r\n    %s\n\n' "Connection error"

        tput sgr0

      fi

      echo

    else

      # shellcheck disable=SC2086,SC2154
      printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error (https not detected)"

      tput sgr0

    fi

  fi

  if [[ "$observatory_state" -eq 1 ]] ; then

      DomainStrip "$_http_last"

      _init_function "DomainScanObservatory \"${_domain_name}\""

      _fstate="$?"

      if [[ "$_fstate" -ne 0 ]] ; then

        # shellcheck disable=SC2086,SC2154
        printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error"

        tput sgr0

      fi

      echo

  fi

  if [[ "$ssllabs_state" -eq 1 ]] ; then

    # shellcheck disable=SC2154
    if [[ "$_last_http_scheme" == "https" ]] ; then

      DomainStrip "$_http_last"

      _init_function "DomainScanSsllabs \"${_http_proto}${_domain_name}\""

      _fstate="$?"

      if [[ "$_fstate" -ne 0 ]] ; then

        # shellcheck disable=SC2086,SC2154
        printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error"

        tput sgr0

      else

        printf '\r         \e['${_rd}'m›\e[m %s: %s\n' \
               "Full Report Url" \
               "https://www.ssllabs.com/ssltest/analyze.html?d=$_domain_name"

        tput sgr0

      fi

    else

      # shellcheck disable=SC2086,SC2154
      printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error (https not detected)"

      tput sgr0

    fi

    echo

  fi

  if [[ "$mixed_content_state" -eq 1 ]] ; then

    # shellcheck disable=SC2154
    if [[ "$_last_http_scheme" == "https" ]] ; then

      local _fd_empty="0"

      DomainStrip "$_http_last"

      _init_function "DomainScanMixedContent \"${_http_proto}${_domain_name}\""

      _fstate="$?"

      if [[ "$_fstate" -ne 0 ]] ; then

        # shellcheck disable=SC2154
        if [[ "$_fd_empty" -eq "0" ]] ; then

          # shellcheck disable=SC2086,SC2154
          # printf '\r         \e['${_rd}'m›\e[m \e['${trgb_3211}'m%s\e[m\n' "Non-secure resources not found"
          printf '\r         › %s\n' "Non-secure resources not found"

          tput sgr0

        else

          # shellcheck disable=SC2086,SC2154
          printf '\r         \e['${_rd}'m›\e[m \e['${trgb_3111}'m%s\e[m\n' "Connection error"

          tput sgr0

        fi

      else

        # shellcheck disable=SC2154
        if [[ "$_fd_empty" -eq "0" ]] ; then

          # shellcheck disable=SC2086,SC2154
          # printf '\r         \e['${_rd}'m›\e[m \e['${trgb_3211}'m%s\e[m\n' "Non-secure resources not found"
          printf '\r         › %s\n' "Non-secure resources not found"

          tput sgr0

        else

          printf '\r'
          tput sgr0

        fi

      fi

    else

      # shellcheck disable=SC2086,SC2154
      printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error (https not detected)"

      tput sgr0

    fi

    echo

  fi

  if [[ "$nse_state" -eq 1 ]] ; then

    DomainStrip "$_http_last"

    # shellcheck disable=SC2154
    _init_function "DomainScanNmapNse \"${_domain_name}\" \"${_remote_port}\""

    _fstate="$?"

    if [[ "$_fstate" -ne 0 ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error"

      tput sgr0

    fi

    echo

  fi

  if [[ "$waf_state" -eq 1 ]] ; then

    DomainStrip "$_http_last"

    # shellcheck disable=SC2154
    _init_function "DomainScanWafw00f \"${_http_proto}${_domain_name}\""

    _fstate="$?"

    if [[ "$_fstate" -ne 0 ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error"

      tput sgr0

    fi

    echo

  fi

  if [[ "$subfinder_state" -eq 1 ]] ; then

    DomainStrip "$_http_last"

    # shellcheck disable=SC2154
    _init_function "DomainScanSubFinder \"${_domain_name}\""

    _fstate="$?"

    if [[ "$_fstate" -ne 0 ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error"

      tput sgr0

    fi

    echo

    if [[ "$ZONE_TRANSFER_TYPE" != "null" ]] ; then

      # shellcheck disable=SC2154
      _init_function "DomainZoneTransfer \"${_domain_name}\""

      _fstate="$?"

      if [[ "$_fstate" -ne 0 ]] ; then

        # shellcheck disable=SC2086,SC2154
        printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error"

        tput sgr0

      fi

      echo

    fi

  fi

  if [[ "$nghttp2_state" -eq 1 ]] ; then

    DomainStrip "$_http_last"

    # shellcheck disable=SC2154
    _init_function "DomainNghttp2 \"${_http_proto}${_domain_name}\""

    _fstate="$?"

    if [[ "$_fstate" -ne 0 ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '\r\n    \e['${trgb_3111}'m%s\e[m\n\n' "Connection error"

      tput sgr0

    fi

    echo

  fi

  _after_init

  # ````````````````````````````````````````````````````````````````````````````

  return "$_STATE"

}
