#!/usr/bin/env bash # --------------------------------------------------------------------------- # tilde - manage user-submitted scripts and apps # 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: tilde [-h|--help] # --------------------------------------------------------------------------- PROGNAME=${0##*/} VERSION="0.1.0" # check coreutils and wrap stat for portability if stat -c"%U" /dev/null >/dev/null 2>/dev/null ; then # GNU environment stat_func () { stat -c '%U' "$1" } else # BSD environment stat_func () { stat -f %Su "$1" } fi error_exit() { printf "%s\n" "${1:-"unknown Error"}" >&2 exit 1 } signal_exit() { # Handle trapped signals case $1 in INT) error_exit "program interrupted by user" ;; TERM) printf "\n%s: program terminated" "$PROGNAME" >&2 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]) printf "\n" ; return 0 ;; [nN]) printf "\n" ; return 1 ;; *) printf " \033[31m %s \n\033[0m" "invalid input" ;; esac done } help_message() { printf "%s version %s\n" "$PROGNAME" "$VERSION" printf "wrapper for user-submitted scripts\n" printf "supports submission and admin approval\n" usage } usage() { printf "\nusage: %s [help|list|submit|about|script_name]\n\n" "$PROGNAME" printf " list - show a list of approved userscripts\n" printf " submit - start the submission flow for your own script\n" if [[ $(id -u) == 0 ]]; then printf " approve - enter the approval queue\n" printf " revoke - send a script back to the author and remove from /tilde/bin\n" fi printf " about - get the description for script_name\n" printf " - run script_name with all remaining args are passed to the script\n" if [[ :$PATH: != *:"/tilde/bin":* ]] ; then printf "\nadd /tilde/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:/tilde/bin' >> ~/.bashrc && source ~/.bashrc\n" fi } verify_script_name() { [[ $1 == "" ]] && \ error_exit "please start over and enter the script name" [[ $(type "$1" > /dev/null 2>&1) ]] && \ error_exit "$1 already exists. rename your script and try again." [[ -x /tilde/bin/$1 ]] && \ error_exit "$1 is already taken. rename your script and try again." case $1 in about|description|list|ls|submit|about|help|apropos|submit|approve) error_exit "$1 is a subcommand of tilde. 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: tilde script submission from ${USER} From: ${USER}@${HOSTNAME} To: root@${HOSTNAME} tilde script submission from ${USER} script name: $1 description: ----------------------------------------------------------------------- $2 ----------------------------------------------------------------------- you'll find the script and description in: /tilde/pending-submissions/$USER/$1 run this to see the approval queue: sudo tilde 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; exit ;; -v | --version) printf "%s" "$VERSION" ;; -* | --*) usage error_exit "Unknown option $1" ;; list | ls) printf "available scripts:\n\n" for scr in /tilde/bin/*; do if [ -f "$scr" ]; then script_name=$(basename "$scr") target=$(readlink -f "$scr") printf "%s by %s\n" "$script_name" "$(stat_func "$target")" cat "/tilde/descriptions/$script_name" printf "\n" fi done ;; about | apropos | description) if [[ -f "/tilde/descriptions/$2" ]]; then cat "/tilde/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?" || 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 "cool, found your script\n" [[ -x "/tilde/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?" || exit # submit now mkdir -p "/tilde/pending-submissions/$USER/$script_name" ln -s "$HOME/bin/$script_name" "/tilde/pending-submissions/$USER/$script_name/$script_name" printf "%s" "$description" > "/tilde/pending-submissions/$USER/$script_name/description.txt" mail_body "$script_name" "$description" | sendmail root printf "script submitted. thanks! :)\n" ;; approve) [[ $(id -u) != 0 ]] && error_exit "re-run this as root to access the approval queue" printf "welcome to the approval queue\n\n" for user in /tilde/pending-submissions/*; do for scr in $user/*; do user=$(basename "$user") script_name=$(basename "$scr") [[ -f "$scr/approved" ]] && continue script="$scr/$script_name" if [ -f "$script" ]; then printf "%s by %s\n" "$script_name" "$user" cat "$scr/description.txt" prompt_confirm "approve?" || continue ln -s "$(readlink -f "$script")" "/tilde/bin/$script_name" cp "$scr/description.txt" "/tilde/descriptions/$script_name" touch "$scr/approved" chmod 664 /tilde/descriptions/* printf "your submission of %s has been approved and is now available at /tilde/bin/%s" "$script_name" "$script_name" \ | sendmail "$user" fi done done printf "~~done for now~~\n" ;; revoke) [[ $(id -u) != 0 ]] && \ error_exit "re-run this as sudo to access the revoke menu" [[ -f /tilde/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 "/tilde/bin/$2") author=$(stat_func "$original_script") rm "/tilde/bin/$2" rm "/tilde/descriptions/$2" rm -rf "/tilde/pending-submissions/$author/$2" printf "your script %s has been returned because: %s\nfeel free to resubmit\n" "$2" "$reason" \ | sendmail "$author" printf "%s revoked and returned to author" "$2" ;; *) if [[ -x "/tilde/bin/$1" ]]; then prog="/tilde/bin/$1" shift exec "$prog" "$@" else [[ $1 == "" ]] || \ printf "%s not found. try %s list to see what's available\n" "$1" "$PROGNAME" help_message exit fi ;; esac