#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# envs - manage user-submitted scripts and apps
# forked from tilde.team

# Copyright 2018-2019, Ben Harris <ben@tilde.team>
# Copyright 2019, Sven Kinne <creme@envs.net>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License at <http://www.gnu.org/licenses/> for
# more details.

# Usage: envs [-h|--help]

# ---------------------------------------------------------------------------
export TERM=xterm-256color

PROGNAME=${0##*/}
VERSION="0.0.2"


clean_up() { # Perform pre-exit housekeeping
  return
}


error_exit() {
  echo -e "${1:-"unknown Error"}" >&2
  clean_up
  exit 1
}


graceful_exit() {
  clean_up
  exit
}


signal_exit() { # Handle trapped signals
  case $1 in
    INT)
      error_exit "program interrupted by user" ;;
    TERM)
      printf '\n%s: program terminated\n' "$PROGNAME" >&2
      graceful_exit ;;
    *)
      error_exit "$PROGNAME: terminating on unknown signal" ;;
  esac
}


prompt_confirm() {
  while true; do
    read -r -n 1 -p "${1:-continue?} [y/n]: " REPLY
    case "$REPLY" in
      [yY]) echo ; return 0 ;;
      [nN]) echo ; return 1 ;;
      *) printf ' \033[31m %s \n\033[0m' "invalid input"
    esac
  done
}


usage() {
  printf '\nusage: %s [help|list|submit|about|script_name]\n' "$PROGNAME"
  printf '  %s list                 - show a list of approved userscripts\n' "$PROGNAME"
  printf '  %s submit               - start the submission flow for your own script\n' "$PROGNAME"
  [[ $(id -u) == 0 ]] && {
    printf '  %s approve              - enter the approval queue\n' "$PROGNAME"
    printf '  %s revoke <script_name> - send a script back to the author and remove from /envs/bin\n' "$PROGNAME"
  }
  printf '  %s about <script_name>  - get the description for script_name\n' "$PROGNAME"
  printf '  %s <script_name>        - run script_name with all remaining args are passed to the script\n' "$PROGNAME"

  if [[ :$PATH: != *:"/envs/bin":* ]] ; then
    printf "\nadd /envs/bin to your PATH to use approved scripts without this wrapper\n"
    printf "if you're using bash, run the following to add it quickly\n"
    printf "  echo 'export PATH=\$PATH:/envs/bin' >> ~/.bashrc && source ~/.bashrc\n"
  fi
}


help_message() {
  cat <<- EOF
$PROGNAME (ver. $VERSION)
wrapper for user-submitted scripts
supports user submission and admin approval
$(usage)
EOF
  return
}


verify_script_name() {
  [[ $1 == "" ]] && error_exit "please start over and enter the script name"
  if [[ "$(type "$1" > /dev/null 2>&1)" ]]; then
    error_exit "$1 already exists. rename your script and try again."
  fi
  [[ -x /envs/bin/"$1" ]] && error_exit "$1 is already taken. rename your script and try again."
  case "$1" in
    help|about|description|desc|list|ls|submit|apropos|approve|revoke)
      error_exit "$1 is a subcommand of envs. rename your script and try again.";;
    *)
      return;;
  esac
}


submission_checklist() {
  cat <<- EOF
requirements for submitting a user script or program:

  - placed in your ~/bin
  - executable
  - responds to help or --help
  - no name collisions with existing scripts or $PROGNAME subcommands

EOF
}


mail_body() {
  cat <<- EOF
Subject: envs script submission from $USER
From: $USER@envs.net
To: creme@envs.net

envs script submission from $USER

script name: $1

description:
-----------------------------------------------------------------------

$2

-----------------------------------------------------------------------
you'll find the script and description in: /envs/pending-submissions/$USER/$1

run this to see the approval queue:
sudo envs approve
EOF
}


# Trap signals
trap "signal_exit TERM" TERM HUP
trap "signal_exit INT"  INT


# Parse command-line
case "$1" in
  -h | --help | help)
    help_message; graceful_exit ;;

  -v | --version)
    printf '%s\n' "$VERSION" ;;

  '-*' | '--*')
    usage
    error_exit "Unknown option $1" ;;

  list | ls)
    printf 'available scripts:\n\n'
    for scr in /envs/bin/*; do
      script_name=$(basename "$scr")
      target=$(readlink -f "$scr")
      printf '%s%s by %s%s\n' "$(tput setaf 6)" "$script_name" "$(stat -c '%U' "$target")" "$(tput sgr0)"
      cat /envs/descriptions/"$script_name"
      printf '\n'
    done ;;

  about | apropos | description | desc)
    if [[ -f /envs/descriptions/"$2" ]]; then
      cat /envs/descriptions/"$2"
    else
      printf '%s not found. try %s list to see available user scripts.\n' "$2" "$PROGNAME"
    fi
  ;;

  submit)
    printf "hello, %s! so it's time to submit your script?\n" "$USER"
    submission_checklist
    prompt_confirm "are you ready to continue?" || graceful_exit
    printf 'enter the name of your script: '
    read -r script_name

    verify_script_name "$script_name"

    if [[ -x "$HOME"/bin/"$script_name" ]]; then
      printf '\ncool, found your script\n'
      [[ -x /envs/pending-submissions/"$USER"/"$script_name"/"$script_name" ]] && error_exit "you've already submitted $script_name"
    else
      error_exit "$script_name not found in ~/bin"
    fi

    printf 'enter a description of your script:\n'
    read -r description
    printf '\nyour script, along with your description will be sent to the admins for approval\n'
    prompt_confirm "ready to submit?" || graceful_exit

    # submit now
    mkdir -p /envs/pending-submissions/"$USER"/"$script_name"
    ln -s "$HOME"/bin/"$script_name" /envs/pending-submissions/"$USER"/"$script_name"/"$script_name"
    echo "$description" > /envs/pending-submissions/"$USER"/"$script_name"/description.txt
    mail_body "$script_name" "$description" | /usr/sbin/sendmail creme
    printf 'script submitted. thanks! :)\n' ;;

  approve)
    [[ $(id -u) != 0 ]] && error_exit "re-run this as sudo to access the approval queue"

    printf 'welcome to the approval queue\n\n'

    for user in /envs/pending-submissions/*; do
      for scr in "$user"/*; do
        user="$(basename "$user")"
        script_name="$(basename "$scr")"
        [[ -f $scr/approved ]] && continue
        script="$scr"/"$script_name"
        printf '%s by %s\n' "$script_name" "$user"
        cat "$scr"/description.txt
        prompt_confirm "approve?" || continue

        sudo ln -s "$(readlink -f "$script")" /envs/bin/"$script_name"
        sudo cp "$scr"/description.txt /envs/descriptions/"$script_name"
        sudo touch "$scr"/approved
        sudo chmod 664 /envs/descriptions/*
        echo "your submission of $script_name has been approved and is now available at /envs/bin/$script_name" | /usr/sbin/sendmail "$user"
      done
    done
    echo "~~done for now~~" ;;

  revoke)
    [[ "$(id -u)" != 0 ]] && error_exit "re-run this as sudo to access the revoke menu"
    [[ -f /envs/bin/"$2" ]] || error_exit "$2 isn't an approved script"

    prompt_confirm "revoke $2?"
    printf 'please provide a reason: '
    read -r reason

    original_script="$(readlink -f /envs/bin/"$2")"
    author="$(stat -c '%U' "$original_script")"
    sudo rm /envs/{bin,descriptions}/"$2"
    sudo rm -rf /envs/pending-submissions/"$author"/"$2"

    echo -e "your script $2 has been returned because: $reason\nfeel free to resubmit" | /usr/sbin/sendmail "$author"
    printf '%s revoked and returned to author\n' "$2" ;;

  *)
    if [[ -f /envs/bin/"$1" ]]; then
      prog=/envs/bin/"$1"
      shift
      $prog "$@"
      graceful_exit
    else
      [[ "$1" == "" ]] || printf "%s not found. try %s list to see what's available\n" "$1" "$PROGNAME"
      help_message; graceful_exit;
    fi ;;

esac

graceful_exit
