#!/usr/bin/env bash # --------------------------------------------------------------------------- # envs - manage user-submitted scripts and apps # forked from tilde.team # Copyright 2018, Ben Harris # 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 for # more details. # Usage: envs [-h|--help] # --------------------------------------------------------------------------- export TERM=xterm-256color PROGNAME=${0##*/} VERSION="0.0.1" 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 - send a script back to the author and remove from /envs/bin\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 # Check for root UID # 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' 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 # Main logic graceful_exit