#! /bin/bash
#
# -- pine-gpg-filter-0.1, 2006-07-04; Martin A. Brown <martin@linux-ip.net>
#
# -- released under the GPL
#
# -- $DESCRIPTION
#
#    Act as a pine filter which can handle multiple roles.
#
# -- ChangeLog
#
#    0.1 2006-07-04; -MAB
#        initial version; great U.S. Independence Day activity
#
# -- Contributors
#
# -- BUGS/TODO
#
#    - need to add --comment option to GnuPG command(s)
#    - next version needs to be able to automatically figure out the
#      local-user ID to employ

# -- script global variables
#
VERBOSE="${PGF_VERBOSE:-0}"
INFORM=1
NOTICE=2
DEBUG=3

STDOUT=1
STDERR=2

VERSION=0.1
NAME=pine-gpg-filter
PGF_URL="http://linux-ip.net/sw/pine-gpg-filter/"

REQUIRED_UTILITIES="gpg"

# -- remove these from our environment for sanity
#
unset GPG_OPT_LU GPG_OPT_RECIPIENTS PGF_RECIPIENTS PGF_MODE PGF_TO_SELF

# PGF_COMMENT="$NAME-$VERSION ($PGF_URL)"
PGF_COMMENT="pgf-$VERSION ($PGF_URL)"
PGF_RESULTFILE="/dev/stderr"

# -- and set up our internal flags
#

unset S E V D
S="--sign"
E="--encrypt"
D="--decrypt"
V="--verify"


# -- my favorite shell functions (and their offspring)
#
# - - - - - - - - - 
  gripe () {
# - - - - - - - - - 
#
  local descriptor=$1   && shift
  printf -- "%s\n" "$@" >&${!descriptor}
}

abort  () { gripe STDERR "$@"; exit 1;     }

inform () { test "$VERBOSE" -ge "$INFORM" && gripe STDERR "I: $@"; }
notice () { test "$VERBOSE" -ge "$NOTICE" && gripe STDERR "N: $@"; }
debug  () { test "$VERBOSE" -ge "$DEBUG"  && gripe STDERR "D: $@"; }

# - - - - - - - - - - -
  version  () {
# - - - - - - - - - - -
#
  gripe STDOUT "${0##*/}-${VERSION}"
  exit 0
}

# - - - - - - - - - - -
  usage  () {
# - - - - - - - - - - -
#
  local descriptor=$1   && shift
  cat >&${!descriptor} <<-EOUSAGE
Usage: ${0##*/} [ options ] <arg> [ <arg> ... ]

Options (defaults marked, or in parentheses):
  -h, --help               Print out this handy help screen.
  -L, --long-usage         Provide help and some more hints.
  -q, --quiet              Reset verbosity to none.
  -v, --verbose            Increase verbosity, can be used multiple times.
  -V, --version            Print version information.
  -e, --encrypt            Ask GPG to encrypt the input.
  -s, --sign               Ask GPG to sign the input.
  -d, --decrypt            Ask GPG to decrypt the input.
  -y, --verify             Ask GPG to verify the input.
  -t, --to-self            Also encrypt-to-self (ignored if not needed).
  -R, --resultfile         Pine reports command output via _RESULTFILE_.
  -L, --local-user         Specify a local username.

EOUSAGE
}

# - - - - - - - - - - -
  short_usage  () {
# - - - - - - - - - - -
#
  local descriptor="${1:-STDOUT}"  && shift
  usage $descriptor
  test "$#" -gt 0 && abort "$@"
  exit 0
}

# - - - - - - - - - - -
  long_usage  () {
# - - - - - - - - - - -
#
  local descriptor="${1:-STDOUT}"  && shift
  usage $descriptor
  cat >&${!descriptor} <<-EOUSAGE
${0##*/}-${VERSION} README

Description
===========

Options
=======

Examples
========


EOUSAGE

exit 0

}

# - - - - - - - - - - -
  parse_options () {
# - - - - - - - - - - -
#
  command getopt                    \
    --unquoted                      \
    --name "${0##*/}"               \
    --options "${OPTIONS}"          \
    --longoptions "${LONGOPTIONS}"  \
    -- "$@"
}

# - - - - - - - - - - -
  verify_support  () {
# - - - - - - - - - - -
#
# -- all we want to do is make sure that all of our support
# programs exist.
#
  local program
  local programs="$@"
  local N
  local ok=0           # -- return 0, if everything is OK!
  
  for program in $programs; do
    N=$( which "$program" 2>/dev/null )
    if test -z "$N"; then
      ok=1
      gripe STDERR "Could not locate necessary support program:  $program"
    fi
  done
  return $ok
}

# -- check to make sure that we have a few key utilities before
#    launching off the command line processing
#
verify_support which \
  || abort "${0##*/}: Cannot proceed without \"which\" utility, quitting."

verify_support getopt \
  || abort "${0##*/}: Cannot process command line options, quitting."

# - - - - - - - - - - -
# main () {
# - - - - - - - - - - -
#
# -- normalize the command-line options
#
OPTIONS="vqhLVesdytnc:R:D:L:"
LONGOPTIONS="verbose,quiet,help,long-usage,version"
LONGOPTIONS="$LONGOPTIONS,encrypt,sign,decrypt,verify,to-self,no-comment"
LONGOPTIONS="$LONGOPTIONS,comment:,resultfile:,local-user:"

# -- calling parse_options twice seems silly.  It is.  It allows, us
#    however, to control the error reporting to the user a bit more
#    carefully.  All the OCD kids are doing it.
#
parse_options "$@" >/dev/null \
  || abort "Try \"${0##*/} --help\" for more information."

set -- $( parse_options "$@" )

# -- Bump up verbosity a notch if we are connected to a terminal;
#    generally, users like to know a bit more of what's going on...
#
tty --silent         && let VERBOSE=VERBOSE+1

# -- examples of options/arguments should look like this now:
# 
while test "$#" -gt "0" ; do
   case "$1" in

      -h | --h*    ) short_usage                              ;;
      -L | --long* ) long_usage                               ;;
      -V | --vers* ) version                                  ;;
      -v | --verb* ) let VERBOSE=VERBOSE+1                    ;;
      -q | --q*    ) VERBOSE=0                                ;;

      -s | --s*    ) PGF_MODE="S ${PGF_MODE}"                 ;;
      -e | --e*    ) PGF_MODE="${PGF_MODE} E"                 ;;
      -d | --decr* ) PGF_MODE="D"                             ;;
      -y | --veri* ) PGF_MODE="V"                             ;;
      -t | --t*    ) PGF_TO_SELF="true"                       ;;

      -n | --n*    ) PGF_NOCOMMENT="true"   && shift          ;;
      -c | --c*    ) PGF_COMMENT="$2"       && shift          ;;
      -R | --r*    ) PGF_RESULTFILE="$2"    && shift          ;;
      -L | --loca* ) PGF_LOCAL_USER="$2"    && shift          ;;

             --    ) shift                  && break          ;;
             -*    ) short_usage STDERR "Unknown option: $1"  ;;

   esac
   shift
done

verify_support $REQUIRED_UTILITIES \
  || abort "${0##*/}: Missing utilities, cannot continue, quitting."

PGF_RECIPIENTS="$@"

test -n "$PGF_RECIPIENTS" \
  || short_usage STDERR "No recipient address(es) specified."

test -n "$PGF_RECIPIENTS" \
  || short_usage STDERR "One of encrypt, sign, decrypt or verify must be specified."

# -- actions taken after sanity checking

# -- iterate over the list of recipients and add them to the list
#    of addresses to which to encrypt the message
#
for RECIP in $PGF_RECIPIENTS ; do

  GPG_OPT_RECIPS="$GPG_OPT_RECIPS --recipient $RECIP"

done

# -- find out if the user specified an alternate email address and
#    specify that user ID for the following commands.  This is not necessary
#    for the --decrypt and --verify options and GnuPG-1.4.x simply ignores
#    the extraneous option.
#
if test -n "$PGF_LOCAL_USER" ; then

  GPG_OPT_LU="--local-user $PGF_LOCAL_USER"

  test -n "$PGF_TO_SELF" \
    && GPG_OPT_LU="$GPG_OPT_LU --no-encrypt-to --encrypt-to $PGF_LOCAL_USER"

fi

# -- Let's see about suppressing that pretty little "comment" string,
#    shall we?  The --comment (which is completely sugar) is included
#    in the header of the PGP encrypted (or signed) data.
#   
test -n "$PGF_NOCOMMENT"  && PGF_COMMENT=""

# -- send all STDERR nonsense from the gpg binary to pine's result
#    if the argument has been specified (otherwise, we'll just leave
#    STDERR alone)
#
test      -n "$PGF_RESULTFILE" \
  && exec 2> "$PGF_RESULTFILE"

# -- build up the primary action string for passing to GnuPG
#
GPG_OPT_ACTION="--armor"

for ACTION in $PGF_MODE ; do

  GPG_OPT_ACTION="$GPG_OPT_ACTION ${!ACTION}"

done

GPG_OPT_ACTION="$GPG_OPT_ACTION --comment"

# -- And now, we are simply going to call GPG and pass this off.
#    We rely on GnuPG's error code to propagate back to pine, since
#    we are not handling any temporary files (if we can avoid it).
#
case "$PGF_MODE" in

   # -- Handle the case where we might be encrypting something
   #    we might also be signing it, and we must specify recipients
   #    in any case where we are encrypting.
   #
  *E) exec gpg $GPG_OPT_ACTION "$PGF_COMMENT" $GPG_OPT_LU $GPG_OPT_RECIPS ;;

   # -- Handle all other cases, (signing only, verify and decrypt)
   #
   *) exec gpg $GPG_OPT_ACTION "$PGF_COMMENT" $GPG_OPT_LU                 ;;

esac

# -- end of file
