#!/bin/bash -e
#
# Copyright 2010-2014 University of Chicago
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

version () {
    VERSION="$(./_gcp_invokepython "$GC_DIR/gc-ctrl.py" -version)"
    echo "Globus Connect Personal $VERSION"
}

usage () {
    echo
    echo "Usage:
  # Run GUI (performs setup if not already done):
  globusconnectpersonal [ -dir <path> ]

  # Run setup from the command line:
  globusconnectpersonal [ -dir <path> ] -setup <security code>

  # Start without GUI (requires that setup has been run):
  globusconnectpersonal -start|-debug [ -dir <path> ] [ -restrict-paths <string> ] [ -shared-paths <string> ]

  # Manage a running command line instance:
  globusconnectpersonal -stop|-status|-trace [ -dir <path> ]

  # Print version and exit:
  globusconnectpersonal -version"
    echo

    echo "Common Options:
    -dir <path>              specify an alternative directory with
                             configuration files instead of the default
                             \$HOME/.globusonline. You can run only one
                             Globus Connect Personal process with the same
                             configuration. Pass this before -setup when
                             creating a new configuration.

    -gui                     Force use of the graphical user interface, even if
                             detection of a graphical environment fails. Does
                             not affect all commands."
    echo

    echo "CLI Commands:
    -version                 display the version number and exit

    -setup [ARGUMENTS...]    register Globus Connect Personal with Globus.org
                             this calls 'register' with all remaining
                             arguments passed through. Use '-setup --help'
                             for details

    -start                   start Globus Connect Personal and establish connection
    -debug                   start Globus Connect Personal with verbose output
                             to Globus.org
    -stop                    stop Globus Connect Personal
    -pause/-unpause          pause or unpause a running Globus Connect Personal
    -status                  return a status of running Globus Connect Personal
    -trace                   trace a running process of Globus Connect Personal"
    echo

    echo "CLI Options for -start and -debug:
    -restrict-paths <string> A comma separated list of full paths that
                             Globus may access. This will override any paths
                             configured via the GUI. Each path may be prefixed
                             by R and/or W or N, denoting read or write access,
                             or no access. If no prefix is present, RW is
                             assumed. The rules apply to all contents and
                             subdirectories. Order of paths does not matter
                             -- the permissions on the longest matching path
                             will apply.

                             The special character '~' will be replaced by the
                             authenticated user's home directory. Note
                             that if the authenticated user's home
                             directory is not accessible, the home
                             directory and starting path will be set to '/'.

                             Shell globs '*' and '?' and character classes
                             enclosed in '[' and ']' are supported.
                             If a path contains any of these characters, use
                             '\\' to escape them. Literal commas must be
                             escaped with the url encoding '%2C', and literal
                             percent must be escaped with '%25'. An implicit
                             rule denying access to dot files in the users
                             home directory is

                             Examples:
                                rw~/
                                    read write access to user's home directory.
                                    This is set as the default when the
                                    application is first run without a
                                    -restrict-paths or -shared-paths option.
                                r/pub
                                    read only access to directory /pub
                                r/a/*.txt
                                    read only access to any file ending in
                                    '.txt' in directory /a
                                r/a/[a-c]?
                                    read only access to any file in /a with
                                    a two character name and beginning with
                                    'a', 'b', or 'c'.
                                rw~/,r/pub,r/dir_containing_a_comma%2C
                                    read write access to user's home directory,
                                    read only access to /pub, and read only
                                    access to '/dir_containing_a_comma,'.

    -shared-paths <string>   A list of paths in the same format as for
                             restrict-paths, but controlling which paths are
                             allowed to be shared. This should be a subset of
                             restrict-paths.
    "
}

check_is_setup () {
  if [ ! -e "${CONFIG}/lta/config" ] || [ ! -e "${CONFIG}/lta/client-id.txt" ]; then
    echo "You tried to run Globus Connect Personal with explicit startup options without
first completing setup.

Run

  globusconnectpersonal -setup

and follow the instructions. Or, for more help, go to

  https://docs.globus.org/how-to/globus-connect-personal-linux/

and then try to start again.
" >&2
    exit 1
  fi
}

check_is_gui () {
  if [ "$DETECT_GUI" -ne -1 ]; then
    return
  fi

  # detect if the environment is graphical
  #
  # if xhost isn't defined or does not succeed, we're presumably not on a working
  # X setup (yeah, it could be Wayland... just use -gui for that)
  DETECT_GUI=1
  if ! command -v xhost >/dev/null 2>&1; then
     DETECT_GUI=0
  elif ! xhost >/dev/null 2>&1; then
     DETECT_GUI=0
  fi
  if [ "$FORCE_GUI" -eq 0 ]; then
    USE_GUI="$DETECT_GUI"
  fi

  if [ "$FORCE_GUI" -eq 1 ] && [ "$DETECT_GUI" -eq 0 ]; then
    echo "WARNING: -gui used, but GUI detection failed

Will use GUI as requested, but some features may not work as intended" >&2
  fi
}

launch_register () {
  GCP_TMP_DIR="$(mktemp -d)"
  exit_cleanup () { rm -rf "$GCP_TMP_DIR"; }
  trap exit_cleanup EXIT

  # if "USE_GUI" is set, then send the --gui arg
  # could be determined by detection or by the `-gui` (force) option
  check_is_gui
  if [ "$USE_GUI" -eq 1 ] && [ "${SETUP_ARGS[*]}" = "" ]; then
    echo "Running setup in a graphical environment

If you encounter errors or would like a command-line only setup, try
  globusconnectpersonal -setup --no-gui
"
    SETUP_ARGS+=("--gui")
  fi

  if [ "${SETUP_ARGS[*]}" = "" ]; then
    echo "Starting CLI interactive setup

For advanced configuration or High Assurance options, cancel and review
  globusconnectpersonal -setup --help
"
  fi

  # check if stderr suppression was disabled explicitly, but default to yes
  # export so it can be picked up by register
  case "$GCP_FILTER_STDERR" in
    0) export GCP_FILTER_STDERR=0;;
    *) export GCP_FILTER_STDERR=1;;
  esac

  # if running with -e (per the shebang line), turn it off for a second
  set +e
  # because this is a GTK app, it puts all kinds of gunk into stderr which we
  # didn't ask for. Under normal conditions, send stderr to a temp file, then
  # grep out the GTK warnings
  #
  # but we do have GCP_FILTER_STDERR=0 available in case we ever actually
  # need full stderr content
  if [ "$GCP_FILTER_STDERR" -eq 1 ]; then
    err_log="${GCP_TMP_DIR}/register.err"
    # shellcheck disable=SC2086
    "${GLOBUS_LOCATION}/bin/register/register" "${SETUP_ARGS[@]}" 2>"$err_log"
    returncode=$?
    if [ "$returncode" -ne 0 ]; then
      # catch empty lines in addition to these patterns because GTK warnings
      # seem to produce a lot of blank lines
      grep -E -v '^$|:[[:space:]]Gtk-WARNING|^Gtk-Message|^Fontconfig (warning|error)|DeprecationWarning' "$err_log"
    fi
  else
    # shellcheck disable=SC2086
    "${GLOBUS_LOCATION}/bin/register/register" "${SETUP_ARGS[@]}"
    returncode=$?
  fi

  case "$returncode" in
    # codes with no message:
    #       0: success
    #     130: SIGINT (Ctrl+C)
    0)
      # exit 0 only if this is set (means we got called as `-setup` explicitly)
      [ "$EXIT_AFTER_SETUP" -eq 1 ] && exit 0
      ;;
    130)
      exit "$returncode"
      ;;
    *)
      echo "
Setup did not complete successfully.
You may want to check ${GCP_CONFIG_DIR}/register.log for more information"
      exit $returncode
      ;;
  esac
  set -e
}

launch_gc_ctrl () {
  check_is_setup

  exec ./_gcp_invokepython ./gc-ctrl.py "${OPTION}" "${RP}" "${SP}" "${CONFIG}" "forward"
}

launch_gui () {
  check_is_setup
  check_is_gui

  if [ "$USE_GUI" -eq 0 ]; then
    echo "Graphical environment not detected

To launch Globus Connect Personal in CLI mode, use
  globusconnectpersonal -start

Or, if you want to force the use of the GUI, use
  globusconnectpersonal -gui
  " >&2
    exit 1
  fi

  exec ./tclkit ./globusconnect.tcl "${CONFIG}" "${RP}" "${SP}"
}

if [ "$EUID" -eq 0 ]; then
    echo "Running Globus Connect Personal as root is not supported"
    exit 1
fi

hardware_name="$(uname -m)"
case "$hardware_name" in
    x86_64) ARCH=amd64 ;;
    # 32 bit builds were dropped with CentOS 6 EOL
    i386|i686)
        echo "Globus Connect Personal does not support 32-bit architectures"
        echo "  uname -m = $hardware_name"
        exit 1
        ;;
    # we've seen users on ARM devices before, so they are *known* as something
    # we should reject with an error
    aarch64)
        ARCH=arm64

        if [[ $(getconf LONG_BIT) == "32" ]]; then
            echo "Globus Connect Personal will only work on 64 bit aarch"
            exit 1
        fi

        ;;
    # any other platform... it's unknown
    # we might be on something like a CRAY with GCP installed
    # so allow it just in case, but print a loud warning
    # if users complain about this, we can expand the set of known
    # architectures in the future
    *)
        echo "***** WARNING *****
Globus Connect Personal does not recognize this architecture
    uname -m = $hardware_name

It may or may not be supported. Will try to proceed.
*******************"
        # Detection may have failed, try x86_64
        #
        ARCH=amd64
        ;;
esac

umask 0022
EXE_DIR="${PWD}"
# follow links to make script work if symlinked, e.g. into ~/bin
SCRIPT_SOURCE="$(readlink -e "${BASH_SOURCE[0]}")"
cd "$(dirname "$SCRIPT_SOURCE")"
GC_DIR="${PWD}"

CONFIG="${HOME}/.globusonline"
RP="NONE"
SP="NONE"
OPTION=""

export PYTHONUNBUFFERED=1

# setup-related options
SETUP_ARGS=()
EXIT_AFTER_SETUP=0

FORCE_GUI=0
USE_GUI=0
DETECT_GUI=-1


while [ $# -gt 0 ]; do
    case "$1" in
        -rp|-restrict-paths)
            if [ $# -lt 2 ]; then
                usage
                exit 1
            fi
            RP="$2"
            shift 2
            ;;
        -sp|-shared-paths)
            if [ $# -lt 2 ]; then
                usage
                exit 1
            fi
            SP="$2"
            shift 2
            ;;
        -setup)
            OPTION="$1"
            EXIT_AFTER_SETUP=1
            shift 1
            SETUP_ARGS=("$@")
            # consume all remaining args
            while [ $# -gt 0 ]; do shift; done
            ;;
        -start|-stop|-status|-trace|-debug|-pause|-unpause)
                if [ "$OPTION" == "-debug" ] && [ "$1" == "-start" ]; then
                        shift
                        continue
                fi

            OPTION="$1"
            shift
            ;;
        -version)
            version
            exit 0
            ;;
        -dir)
            shift
            CONFIG="$1"
            shift
            cd "${EXE_DIR}"
            if ! cd "${CONFIG}" 2>/dev/null; then
                echo "Could not read the directory '${CONFIG}' with configuration files."
                echo -n "Creating the directory... "
                if ! mkdir -p "${CONFIG}" 2>/dev/null; then
                    echo -e "\nCould not create the directory '${CONFIG}'. Exiting..." >&2
                    exit 1
                fi
                echo "Done"
                cd "${CONFIG}"
            fi
            CONFIG="${PWD}"
            ;;
        -gui)
            USE_GUI=1
            FORCE_GUI=1
            shift
            ;;
        *)
            usage
            exit 0
            ;;
    esac
done

if {
    [ "$RP" != "NONE" ] || [ "$SP" != "NONE" ];
} && {
    [ "$OPTION" != "-start" ] && [ "$OPTION" != "-debug" ];
}; then
    (
        echo "Options -restrict-paths and -shared-paths can only be used"
        echo "with -start or -debug. Run with -help for details."
    ) >&2
    exit 1
fi

export GCP_CONFIG_DIR="${CONFIG}/lta"
export GLOBUS_LOCATION="${GC_DIR}/gt_${ARCH}"
export GCP_RELAYTOOL_LD_LIBRARY_PATH="${GLOBUS_LOCATION}/lib:$LD_LIBRARY_PATH"
cd "${GC_DIR}"

# if we are detached, simulate open stdin that will die when we do
if read -t 0; then
  exec < <(tail -f /dev/null --pid $$)
fi

# Start application
case "$OPTION" in
  "")
    # if no option was given and '{config_dir}/client-id.txt' does not exist, then
    # call setup, but do not 'exec' because the usage is part of the no-args
    # flow
    if [ ! -f "${GCP_CONFIG_DIR}/client-id.txt" ] && [ "x${OPTION}" = "x" ]; then
      echo -e "Detected that setup has not run yet, and '-setup' was not used
Will now attempt to run
  globusconnectpersonal -setup
"
      launch_register
      echo "
Will now start globusconnectpersonal in GUI mode"
    fi
    launch_gui
    ;;
  "-setup")
    launch_register
    ;;
  -start)
    # coerce `./globusconnectpersonal -start -gui` into looking like
    # `./globusconnectpersonal -gui`
    # (this is the only thing which makes sense in this case)
    if [ "$FORCE_GUI" -eq 1 ]; then
      launch_gui
    fi
    ;;
  *)
    ;;
esac

# the above invocations will exec, so this is safe as a fall-through
launch_gc_ctrl
