#!/usr/bin/env bash
#
# X11 front-end to generate a certificate request and mail it
# with S/MIME to a known CA for signing.
#
# Copyright (c) 2005 Riccardo Murri <riccardo.murri@ictp.it> and
#                    Antonio Messina <antonio.messina@ictp.it>
#                    for the EGRID/Grid@TS project.
#
#
# Configuration variables
# -----------------------
#
# Uses the following configuration variables, taken either
# from the environment, or from the configuration file
# ``ra.conf``:
#
#   CA_SUBMIT_EMAIL
#     **Required.**  The email address to send certificate requests to;
#     it will be S/MIME signed with the RA certificate.
#
#   RA_ROOT_DIR
#     The root of the install tree; it is expected to have a 
#     certain format, described in `Deployment tree`_.
#     Defaults to the parent directory of the one where the
#     script resides.
#
#   RA_CACERTS_DIR
#     the directory where certificates of the CA are stored.
#     The certificate's filename is the (last) issuer Common Name
#     field. 
#
#   RA_CONF_DIR
#     the directory where configuration values
#     are stored; if not set, looks for the config file
#     ``ra.conf`` in the current directory,
#     ``$HOME/ra``, the directory whence the script is run
#     and its parent dir.
#
#   RA_SMIME_DIR
#     root directory of Mutt's S/MIME certificates and keys
#     storage.  Defaults to the priv/smime tree in the RA tree.
#
#   RA_ENCFS_STORAGE_DIR
#     If set, try to mount this directory as an EncFS filesystem
#     on the directory ``$RA_ROOT_DIR/priv`` and abort if it fails.
#
#
# msmtp configuration
# -------------------
# 
# In order to send mail to the CA you must configure the smtp server msmtp.
# This task is done through the main menu, or by editing the 
# ``$RA_CONF_DIR/msmtp.conf`` file, which is in the format documented
# by the ``msmtp`` manual.
# 
# The recognized configuration statements are:
#
#   host SMTP_SERVER
#     **Required.** smtp relay server hostname
#   port SMTP_PORT
#     Defaults to 25 (or 465 if SSL/TLS is enabled); do not change 
#     unless you have a non-standard setup.
#   tls SMTP_TLS # <-- on|off
#     whether to use SSL/TLS encrypted connections; defaults to 'off'.
#     If SSL/TLS is enabled, then SMTP port is changed to 465/tcp.
#   auth SMTP_AUTH # <-- on|off
#     whether to use SMTP authentication.  Defaults to 'off'.
#   user SMTP_AUTH_USER
#     Username for authentication with the SMTP server; only used 
#     if SMTP_AUTH is set to 'on'.
#   password SMTP_AUTH_PASSWD
#     The password for the user SMTP_AUTH_USER, to use in SMTP 
#     authentication; only used if SMTP_AUTH is set to 'on'.
#
#
# Deployment tree
# ---------------
#
# All the files needed by this script reside within the
# same directory tree, so that they can easily be moved around,
# and -in particular- can be stored in a floppy or USB key that
# can be easily put off-line and stored in a safe place.
#
# ::
#
#   $RA_ROOT_DIR/
#    |
#    |-- bin/
#    |    `-- ra-request-certificate  ..This script.
#    |-- conf/
#    |    |-- ra.conf   ..Sets configuration variables [optional].
#    |    `-- openssl/  ..Files in this directory are sections of the OpenSSL
#    |         |          config file that is used to generate requests.
#    |         |-- host_certificate_extensions.conf
#    |         |-- host_certificate_request_extensions.conf
#    |         |-- req.conf
#    |         |-- user_certificate_extensions.conf
#    |         `-- user_certificate_request_extensions.conf
#    |-- pub/
#    |    `-- cacerts/    ..Where CA certificates are stored.
#    `-- priv/                         
#         |-- certs/      ..Where certificate keys and requests are stored.
#         |    |-- ou=host/  
#         |    `-- ou=people/
#         |-- smime/      ..Mutt S/MIME certificate and keys storage.
#         `-- tmp/        ..Temporary files (won't use the system-wide one
#                           to be sure we don't leak any sensitive info).
#
#
# Configuration dependencies
# --------------------------
#
# It is currently assumed that the ``mutt`` e-mail client is
# properly configured for S/MIME and that the CA public key has
# already been imported in the S/MIME CA bundle.
#
#
# Revision history
# ----------------
#
# $Id: ra-request-certificate.sh 1261 2005-10-19 10:59:37Z rmurri $
# 
# $Log: ra-request-certificate.sh,v $
# Revision 1.1  2005/07/18 17:33:22  rmurri
# Initial revision
#
# Revision 1.1  2005/07/15 15:30:21  rmurri
# * bin/ra-request-certificate -> ./ra-request-certificate.sh per essere
#   coerenti con il modo in cui viene installato.
#
# Revision 1.22  2005/07/14 11:59:28  rmurri
# * BUGFIX: dopo lo spostamento di smtp_loadconfig
#
# Revision 1.21  2005/07/14 11:47:45  rmurri
# * chiede di slezionare la chiave RA da usare all'inizio - c'è un bug
#   in mutt per cui non si riesce a selezionare dopo, se
#   ``smime_default_*`` non è impostato;
# * quando cambia la passphrase sulla chiave, mette permessi 0600
# * usa percorsi assoluti
#
# Revision 1.20  2005/07/14 11:05:17  rmurri
# * agginta speigazione a 'save' di smtp advanced configuration
#
# Revision 1.19  2005/07/14 10:57:11  rmurri
# * aggiunta la possibilità di cambiare la passphrase per le chiavi
#   S/MIME memorizzate nel db smime/keys/
#
# Revision 1.18  2005/07/13 17:27:53  amessina
# * bugfix smime_keys
#
# Revision 1.17  2005/07/13 17:13:10  amessina
# * Modificata funzione add_ra_cert: usiamo smime_keys add_chain invece
#   che smime_keys add_pem. Di conseguenza e' stata eliminata la
#   funzione add_bag_attributes. Ora *richiediamo* file separati. E'
#   necessario anche avere il certificato della CA, che avra' nome
#   uguale al CN dell'issuer.
#
# Revision 1.16  2005/07/13 15:31:20  rmurri
# * BUGFIX: cerca la giusta stringa per testare se i bag attribs sono presenti
#
# Revision 1.15  2005/07/13 15:27:14  rmurri
# * corretto typo nella precedente versione di name_label_from_certificate
#
# Revision 1.14  2005/07/13 15:05:58  rmurri
# * semplificata ``name_label_from_certificate``
# * aggiustamenti nella documentazione
#
# Revision 1.13  2005/07/13 14:15:09  rmurri
# * non usa più ``smtp.conf`` ma solo ``msmtp.conf``
# * aggiustati i msg per l'utente
# * la configurazione di smtp non parte più in automatico se SMTP_SERVER
#   non è impostata, ma chiede all'utente di selezionarla dal menu
#   principale (in modo che il fuoco dell'utente sia su quello)
# * aggiunta opzione ``--wrap`` per le invocazioni di Xdialog in ``die``
#   e ``message``.
#
# Revision 1.12  2005/07/12 15:48:54  amessina
# * aggiunta funzione add_bag_attributes_to_cert_and_key che aggiunge al
#   certificato ed alla chiave dei Bag Attribuges nel caso siano
#   assenti, al fine di lanciare smime_keys, che altrimenti non
#   terminerebbe correttamente.
#
# Revision 1.11  2005/07/12 15:27:54  amessina
# * aggiunta dipendenza da msmtp (require_command msmtp)
#
# Revision 1.10  2005/07/12 15:24:11  amessina
# * Migliorato supporto per msmtp.
#
# Revision 1.9  2005/07/11 17:11:49  amessina
# * Aggiunto supporto per msmtp. FIXME: msmtp pare che mandi il comando
#   EHLO e, poi, ma a questo punto credo dopo ere geologiche, manda
#   HELO. Questo fa si' che con alcuni (spero pochi) mailserver non
#   funziona. Tra questi c'e' smtp.ictp.it (ARGH!)
#
# Revision 1.8  2005/06/17 15:06:52  rmurri
# * check that it's running in a UTF-8 locale
#
# Revision 1.7  2005/06/09 15:21:31  rmurri
# * opzioni corrette per mutt
# * controlla se può scrivere nella propria directory, per evitare i
#   problemi con i mount read-only.
#
# Revision 1.6  2005/06/09 14:28:08  rmurri
# * correct MX checkl both for NIKHEF- and BIND9-host
#
# Revision 1.5  2005/06/08 19:55:23  rmurri
# * email sia in DN che in subjectAltName
# * DNS host name anche in subjectAltName
#
# Revision 1.4  2005/06/08 17:44:19  rmurri
# * ad_ra_cert funzionante fino alla parte di invocare smime_keys
#
# Revision 1.3  2005/06/08 17:30:40  rmurri
# * troppi bugfix per elencarli qui; funzionano le funzioni di richiesta
#   dei certificati utente e host
#
# Revision 1.2  2005/06/08 11:11:42  angleto
# *** empty log message ***
#
# Revision 1.1  2005/05/30 20:03:46  rmurri
#
#

PROG="`basename $0`"

# die EXITCODE [MSG]
#
debug () {
    [ "$DEBUG" != "" ] && >&2 echo "DEBUG: $1" 
}

die () {
	local rc=$1; shift
	
	echo -n "$PROG: " 1>&2
	if [ $# -ne 0 ]; then
		echo "$@" 1>&2
	else
		cat 1>&2
	fi
	
	exit $rc
}


have_command () {
	type -p "$1" >&/dev/null
}
have_no_command () {
	! have_command "$@"
}

require_command () {
	local cmd="$1"
	if have_no_command $cmd; then
		die 1 "Cannot find '$cmd' program in the system PATH (`echo $PATH | tr ':' ' '`); please install '$cmd' or correct PATH."
	fi
}



### check Xdialog
require_command Xdialog

# now that we have Xdialog, use a graphical error reporting
function die () {
	local rc="$1"; shift

	# XXX: should use --textbox if not args
	# given, like the text-only ``die`` does.
	Xdialog \
		--title "$PROG" \
		--backtitle "Fatal error - exiting" \
		--wrap \
		--msgbox "$(echo $@)" 0x0
	exit $rc
}


### configuration

# provide some defaults
TMPDIR=${TMPDIR:-/tmp}

for dir in \
	$RA_ROOT_DIR \
	`pwd` \
	~/ra \
	`dirname $0` \
	`dirname $0`/.. \
	;
do
#  echo "DEBUG: trying RA_CONF candidate: ${dir}" 1>&2
  if test -d $dir/conf && test -d $dir/conf/openssl; then
	  RA_CONF_DIR="$dir/conf"
	  break
  fi
done
if test -z "$RA_CONF_DIR"; then
	die 1 'Cannot find configuration directory - aborting'
fi

for dir in \
	$RA_ROOT_DIR \
	$RA_CONF_DIR/.. \
	`dirname $0` \
	`dirname $0`/.. \
	~/ra \
	;
do
  if test -d $dir/priv && test -d $dir/priv/certs; then
	  RA_ROOT_DIR="$dir"
	  break
  fi
done

if test -d $RA_ROOT_DIR/priv/smime; then
	RA_SMIME_DIR=$RA_ROOT_DIR/priv/smime
fi

if [ -d $RA_ROOT_DIR/pub/cacerts ]; then
    RA_CACERTS_DIR=$RA_ROOT_DIR/pub/cacerts
fi

# override with configuration file
if test -e $RA_CONF_DIR/ra.conf; then
	source $RA_CONF_DIR/ra.conf \
		|| die 1 "Error parsing configuration file '$RA_CONF_DIR/ra.conf' - aborting"
fi


# sanity checks
if test -z "$CA_SUBMIT_EMAIL"; then
	die 1 "Variable CA_SUBMIT_EMAIL is empty after parsing configuration file '$RA_CONF_DIR/ra.conf' - aborting"
fi

if test -z "$RA_ROOT_DIR"; then
	die 1 "Variable RA_ROOT_DIR is empty after parsing configuration file '$RA_CONF_DIR/ra.conf' - aborting"
fi

if test -z "$RA_SMIME_DIR"; then
	die 1 "Variable RA_SMIME_DIR is empty after parsing configuration file '$RA_CONF_DIR/ra.conf' - aborting"
fi

if test -z "$RA_CACERTS_DIR"; then
    die 1 "Variable RA_CACERTS_DIR is empty after parsing configuration file '$RA_CONF_DIR/ra.conf' - aborting"
fi

### check execution environment

require_command host
require_command mutt
require_command msmtp
require_command openssl
require_command touch 
require_command urxvt
require_command awk


# check that UTF-8 is enabled
if ! locale -k charmap | egrep -qi 'UTF-?8'; then
	function first () { echo "$1"; }
	utf8_locales="`locale -a | egrep -i 'UTF-?8'`"
	first_utf8_locale="`first $utf8_locales`"
	
	# try to forcibly set the first UTF-8 locale
	if test -n "$first_utf8_locale"; then
		export LC_ALL="$first_utf8_locale"
	fi

	# check that it actually is available
	if ! locale -k charmap | egrep -qi 'UTF-?8'; then
		die 1 \
			"Cannot switch to UTF-8 enabled locale; aborting for now.  ${utf8_locales:+Please, select one of the available UTF-8 locales ($utf8_locales) and run this script again.}"
	fi
fi



if ! host www.google.com >&/dev/null; then
	message \
      'No network found' \
      "This script requires a working network connection, and in the current setup the host 'www.google.com' cannot be resolved to an IP address.  The 'netcardconfig program will now be started'; please use it to configure your network."
	sudo netcardconfig
fi

# second attempt, if this fails, then abort
if ! host www.google.com >&/dev/null; then
	die 1 \
      "This script requires a working network connection, and in the current setup the host 'www.google.com' cannot be resolved to an IP address.  Please contact your systems or network administrator to configure the network correctly, then start this program again."
fi

# check if we are connected to a TTY
if ! (test -t 0 && test -t 1); then
	run_in_term='urxvt -e'
else
	run_in_term=''
fi

# check that tree is RW
test=TEST.${RANDOM}.$$
touch $RA_ROOT_DIR/$test \
	|| die 1 "Cannot write to the root directory '$RA_ROOT_DIR' - if it's on removable media, please check that media is not mounted read-only.  Aborting, for now."
rm -f $RA_ROOT_DIR/$test


### widgets
#
# the tty-oriented widgets 'dialog' and 'whiptail' do NOT
# presently support UTF-8; Xdialog does, so we do presently
# require X... the conversational phase with 'openssl' is
# skipped - we run in 'prompt=no' mode...
# 

# set TTY fd for use by widgets when included in backticks
: ${TTY:=$(tty)}


main_menu () {
	Xdialog \
		--stdout \
		--title "$PROG" \
		--backtitle 'Main menu' \
		--no-cancel \
		--menu \
		"Choose an action to perform from the list below." \
		0x0 9 \
		'newuser'    'Request a new USER certificate' \
		'newhost'    'Request a new HOST certificate' \
		'passphrase' 'Change passphrase on a RA person private key' \
		'import'     'Add an RA person certificate' \
		'netconfig'  'Configure the network' \
		'smtpconfig' 'Configure the server for outgoing mails' \
		'poweroff'   'Session finished, shut down computer.' \
		'exit'       'Exit this program'
}


message () {
	Xdialog \
		--title "$PROG" \
		--backtitle "${1:-$PROG}" \
		--wrap \
		--msgbox "$2" 0x0
}


# ask PROMPT MSG [INITIAL]
# ask_password PROMPT MSG
#
# Prompt the user with PROMPT, and output the user-inputted
# string on stdout on exit.  MSG is presented as an explanation
# string if widget permits. 
#
# If INITIAL is given, then it is used to fill the user input field
# at start.
#
# Returns 0 on successful completion, and 1 if user canceled
# input operation.
# 
# 'ask_password' does the same, but does not echo characters
# on the screen.
#
ask () {
	DIALOG_ESC=1 Xdialog \
		--stdout \
		--title "$PROG" \
		--backtitle "$1" \
		--inputbox "$2" \
		0x0 \
		"$3"
}

ask_password () {
	DIALOG_ESC=1 Xdialog \
		--stdout \
		--title "$PROG" \
		--backtitle "$1" \
		--password \
		--inputbox "$2" \
		0x0
}


# select_file PATTERN [MSG]
#
# Prompts the user to select a file from a list,
# showing the file names matching PATTERN.
#
# The file selector box is titled MSG, or 
# 'Select a certificate' if MSG is not given.
#
select_file () {
	local pattern="$1"
	local msg="$2"

	DIALOG_ESC=1 Xdialog \
		2>&1 1>$TTY \
		--title  "$PROG" \
		--backtitle "${msg:-Select a certificate}" \
		--fselect "$pattern" \
		0x0
}


# certificate_menu MSG FILE [FILE ...]
#
# Prompt the user with a menu, showing each FILE name
# as given on the command line, and the certificate subject
# extracted from the FILE.  The user may then select 
# the certificate he wants, whose FILE name will be printed 
# on stdout.
#
# First parameter MSG is printed as the menu heading.
#
certificate_menu () {
	local msg="$1"; shift

	(for cert in "$@"; do
		echo $cert;
		print_friendly_certificate_subject $cert;
	done) \
		| xargs Xdialog \
			--stdout \
		    --title "$PROG" \
		    --backtitle 'Select a certificate' \
		    --menubox "$msg" 0x0 0 
}



### more functions
print_friendly_certificate_subject () {
	local cert="$1"; shift
	
	openssl x509 -in $cert -noout -subject -nameopt RFC2253 \
		| sed -e 's/^subject=  *//i;'
}


print_fullname_from_certificate () {
	local cert="$1"; shift
	
	basename "`openssl x509 -in $cert -noout -subject`" \
	  | sed -e 's/^CN= *//;'
}


print_email_from_certificate () {
	local cert="$1"; shift

	openssl x509 -noout -email -in "$cert"
}


name_label_from_certificate () {
	local certfile="$1"; shift

	basename "`openssl x509 -subject -noout -in "$certfile"`" \
		| sed -e 's/CN= *//;' \
		| tr '[[:upper:]]' '[[:lower:]]' \
		| tr ' ' '_'
}
	
print_request_subject () {
	local file="$1"; shift

	openssl req -noout -text -in "$file" 2>/dev/null \
		| grep Subject: \
		| sed -e 's/^[[:space:]]*Subject: //;'
}

remove_excess_whitespace () {
	echo "$@" \
		| tr -s '[[:space:]]' \
		| sed -e 's/^[[:space:]][[:space:]]*//;' \
		| sed -e 's/[[:space:]][[:space:]]*$//'
}

	# is this NIKHEF or BIND9 host?
if host -V >&/dev/null; then
		# NIKHEF host, good!
	function get_hostname_from_ip () {
		local hostname ip
		ip="$1"
		hostname="`host -t PTR $ip 2>/dev/null \
						| grep -i 'name' | cut -d' ' -f2`"
		if [ -z "$hostname" ]; then
			echo $ip
			return 1
		else
			echo $hostname
			return 0
		fi
	}
	function get_ip_from_hostname () {
		local hostname ip
		hostname="$1"
		ip="`host -t A $hostname 2>/dev/null \
					| (read name a ip; echo $ip)`"
		if [ -z "$ip" ]; then
			return 1
		else
			echo $ip
			return 0
		fi
	}
	function has_mx () {
		local mx="$1"; shift

		host -t MX "$mx" | grep -q MX;
	}
else
		# BIND9 host
	function get_hostname_from_ip () {
		ip="$1"
		hostname="`host -t PTR $ip 2>/dev/null \
						| grep -i 'name' \
                        | cut -d' ' -f5 \
                        | sed -e 's/\.$//'`"
		if [ -z "$hostname" ]; then
			echo $ip
			return 1
		else
			echo $hostname
			return 0
		fi
	}
	function get_ip_from_hostname () {
		local hostname ip
		hostname="$1"
		ip="`host -t A $hostname 2>/dev/null \
					| cut -d' ' -f4`"
			# BIND9 'host' will echo to stdout
			# even if the host is non-existant...
		if [ "$ip" = 'found:' ]; then
			return 1
		else
			echo $ip
			return 0
		fi
	}
	function has_mx () {
		local mx="$1"; shift

		host -t MX "$mx" | grep -q 'mail is handled'
	}
fi			


is_valid_email () {
	local email="$1"
	local maildomain

	if ! expr match "$email" \
		'[a-zA-Z0-9][a-zA-Z0-9\._+=-]*@[a-zA-Z0-9][a-zA-Z0-9\.-]*' \
		>&/dev/null; 
	then
		# not a valid rfc822 addr spec
		return 10
	fi

	maildomain="$(echo $email | cut -d@ -f2)"
	if ! has_mx "$maildomain"; then
		# mail exchanger not existent
		return 11
	fi

	# if we get to this point, all tests completed successfully
	return 0
}

is_valid_hostname () {
	local hostname="$1"
	local ip

	ip="$(get_ip_from_hostname $hostname)"
	if test -z "$ip"; then
		return 20
	fi

	if [ "$hostname" != "$(get_hostname_from_ip $ip)" ]; then
		return 21
	fi

	# if we get to this point, all tests completed successfully
	return 0
}


ask_username () {
	local name_init="$1"
	local reply
	local rc

	while test -z "$reply"; do
		reply=$(ask \
			'Full name of the user' \
			'Type in the name of the user, whose certificate 
         	is about to be requested.  The name will appear 
         	in the CN part of the subject *exactly* as typed below.' \
		    "$name_init")
		rc=$?
		if [ $rc -ne 0 ]; then
			# pass on error code
			return $rc
		fi
		reply="$(remove_excess_whitespace $reply)"
	done

	echo $reply
}

ask_email () {
	local email_prompt="$1"
	local email_init="$2"
	local reply
	local rc=1

	until [ $rc -eq 0 ]; do
		reply=$(ask \
			'E-mail address of the user' \
			"Type in the e-mail address of ${email_prompt:-the user}.  The e-mail 
             address will be embedded in the certificate *exactly* 
             as typed below." \
		     "$email_init")
		rc=$?
		if [ $rc -ne 0 ]; then
			# pass on error code
			return $rc
		fi
		reply="$(remove_excess_whitespace $reply)"
		is_valid_email "$reply"
		rc=$?
		case $rc in
			10) 
				message 'Error: not RFC822 address' \
					'The e-mail address must follow RFC822 addrspec
                     format, that is, it should be of the type
                     "name@mail.domain" with no quotes, angle brackets,
                     or spaces in it.  Please type the e-mail
                     address again.'
				if [ $? -ne 0 ]; then
					return 1
				fi
				;;
			11) 
				message 'Error: mail domain cannot be found' \
					"Cannot find the MX host for the mail domain 
                     '$(echo $reply | cut -d@ -f2)'; please ensure
                     that the address was typed correctly, and that 
                     the network is up and running."
				if [ $? -ne 0 ]; then
					return 1
				fi
				;;
		esac
	done

	echo $reply
}

ask_hostname () {
	local dnsname_init="$1"
	local reply
	local rc=1

	until [ $rc -eq 0 ];  do
		reply=$(ask \
			'Fully-qualified DNS host name' \
			'Type in the fully-qualified DNS host name of the host
			 whose certificate is about to be requested.  This host name
             must be canonical, i.e., have DNS forward (name to IP, A record) 
             and reverse (IP to name, PTR record) resolution, or the CA 
             will not sign  the certificate.' \
		     "$dnsname_init")
		rc=$?
		if [ $rc -ne 0 ]; then
			# pass on error code
			return $rc
		fi

		reply="$(remove_excess_whitespace $reply)"
		is_valid_hostname "$reply"
		rc=$?
		case $rc in
			20) 
				message 'Error: cannot find IP address for host' \
					"Cannot find an IP address for host '$reply'
                     via a DNS query; please check that the DNS
                     resolver is configured correctly in the file
                     '/etc/resolv.conf' (ask your network administrator)
                     and that you have typed the host name correctly."
				if [ $? -ne 0 ]; then
					return 1
				fi
				;;
			21) 
				message 'Error: wrong DNS reverse resolution' \
					"An IP address for host '$reply' was found, but
                     that address does not resolve back to the original
                     name; please consult your network administrator:
                     to-be-certified hosts must have both DNS forward 
                     (name to IP, A record) and reverse (IP to name, 
                     PTR record) resolution, or the CA will not sign 
                     the certificate."
				if [ $? -ne 0 ]; then
					return 1
				fi
				;;
		esac
	done

	echo $reply
}

### smtpconfig

parse_msmtp_conf () {
	local conffile="$1"

	if ! test -e "$conffile"; then
		return 1
	fi

	local account
	local tmpfile="`mktemp`" \
		|| die 1 "Cannot make temporary file"
	local cmd arg

	(while read cmd arg; do
		case "$cmd" in
			host) echo "SMTP_SERVER='$arg';" >> $tmpfile;;
			port) echo "SMTP_PORT='$arg';" >> $tmpfile;;
			tls) echo "SMTP_TLS='$arg';" >> $tmpfile;;
			auth) echo "SMTP_AUTH='$arg';" >> $tmpfile;;
			user) echo "SMTP_AUTH_USER='$arg';" >> $tmpfile;;
			password) echo "SMTP_AUTH_PASSWD='$arg';" >> $tmpfile;;
			*) ;;
		esac
		done) <$conffile

	source $tmpfile \
		|| die 1 "Internal error: cannot source file '$tmpfile' - aborting."
	rm $tmpfile
}

smtp_loadconfig () {
# smtp configuration
    SMTP_SERVER=''
    SMTP_AUTH=off
    SMTP_PORT=25
    SMTP_AUTH_USER=''
    SMTP_AUTH_PASSWD=''
    SMTP_TLS=off
    if test -e $RA_CONF_DIR/msmtp.conf; then
		parse_msmtp_conf $RA_CONF_DIR/msmtp.conf
		if [ $? -ne 0 ]; then
			die 1 "Error parsing configuration file '$RA_CONF_DIR/msmtp.conf' - aborting"
		fi
    fi
}

ask_smtp_server () {
    local smtp_server="$1"
    local reply
    local rc
    while test -z $reply; do
	reply=$(ask \
	    'SMTP server host name' \
	    'Type in the fully-qualified DNS name of the SMTP server. 
         (Ask your network administrator if you do not know what to
         type in here.)' \
	    "$smtp_server")
	rc=$?
	if [ $rc -ne 0 ]; then
	    debug "esco da ask_smtp_server con un return $rc"
	    return  $rc
	fi
    done
    echo $reply
}

ask_smtp_auth () { 
    local smtp_auth="$1"
    local reply
    local rc
    while test -z $reply; do
	reply=$(Xdialog --stdout \
	    --title "$PROG" --backtitle 'SMTP AUTHentication' \
	     --yesno 'Do you want SMTP AUTHentication?'  0x0 )
	rc=$?
	if [ $rc == 0 ]; then
	    reply=on
	elif [ $rc == 1 ];
	    then 
	    reply=off
	else
	    return $rc
	fi
    done
    echo $reply
}

ask_smtp_port () { 
    local smtp_port="$1"
    local reply
    local rc
    while test -z $reply; do
	reply=$(ask \
	    'TCP PORT for $SMTP_SERVER' \
	    'Type in the port of the smtp server' \
	    "$smtp_port")
	rc=$?
	if [ $rc -ne 0 ]; then
	    return  $rc
	fi
    done
    echo $reply
}

ask_smtp_auth_user () { 
	local name_init="$1"
	local reply
	local rc

	reply=$(ask \
	    'login name for SMTP_SERVER ($SMTP_SERVER)' \
	    'Type in the username  for log in the  smtp server.' \
	    "$name_init")
	rc=$?
	if [ $rc -ne 0 ]; then
			# pass on error code
	    return $rc
	fi

	echo $reply
}

ask_smtp_auth_passwd () { 
	local name_init="$1"
	local reply
	local rc

	reply=$(ask_password \
	    'password  for SMTP server ' \
	    'Type in the password for the username '"$SMTP_AUTH_USER"'  \
	for log in the  smtp server.' )
	rc=$?
	if [ $rc -ne 0 ]; then
			# pass on error code
	    return $rc
	fi

	echo $reply
}

ask_smtp_tls () { 
    local reply
    local rc
    while test -z $reply; do
	reply=$(Xdialog --stdout \
	    --title "$PROG" \
		--backtitle 'Use TLS?' \
	     --yesno 'Do you want use TLS to connect to to the SMTP server?' \
		0x0 )
	rc=$?
	if [ $rc == 0 ]; then
	    reply=on
	elif [ $rc == 1 ];
	    then 
	    reply=off
	else
	    return $rc
	fi
    done
    echo $reply
}


smtp_req_menu () {
    Xdialog \
	--stdout \
	--title "$PROG" \
	--backtitle 'SMTP server configuration' \
	--menu \
	"Please fill the DNS name of your SMTP relay host in the form
         below; select the 'Advanced configuration' option if your 
         server requires authentication, SSL/TLS or a non-standard port." \
	    0x0 5 \
	    'smtpserver' "SMTP server: ${SMTP_SERVER:-<REQUIRED>}" \
	    'smtpadv'	 "SMTP advanced configuration (authentication etc...)" \
		'done'	     "Write configuration to file and proceed"
}


smtp_advanced_menu () {
    Xdialog \
	--stdout \
	--title "$PROG" \
	--backtitle 'SMTP server advanced configuration' \
	--menu \
	"Please fill in the fields in the form below, by selecting each
	row in turn. Select 'Done' when all necessary fields have
	their correct value."\
	    0x0 9 \
	    'smtpserver' "SMTP server: ${SMTP_SERVER:-<REQUIRED>}" \
	    'smtpport'   "SMTP port: $SMTP_PORT" \
	    'smtpauth'   "Use SMTP AUTHentication?: ${SMTP_AUTH:-<on|off>}" \
	    'smtpuser'   "User for SMTP AUTH: ${SMTP_AUTH_USER:-<required if SMTP AUTHentication is on}" \
	    'smtppasswd' "Password for SMTP AUTH (required if SMTP AUTHentication is on)" \
	    'smtptls'    "Use SSL/TLS for SMTP connections: ${SMTP_TLS:-<on|off>}" \
		'save'	     'Save configuration and return to main menu.'
}

ask_for_smtp_req_parameters () {
    local done
    local saved_value
    local result
    local x
    until test -n "$done"; do

		x="$(smtp_req_menu)" || return $?
		case "$x" in
			smtpserver)
				saved_value="$SMTP_SERVER"
				SMTP_SERVER="$(ask_smtp_server "$SMTP_SERVER")"
				if [ $? -ne 0 ]; then
					SMTP_SERVER="$saved_value"
				fi
				if [ -z "$(get_ip_from_hostname $SMTP_SERVER)" ]
					then
					message 'Invalid hostname' \
						"'$SMTP_SERVER' is not a valid hostname!"
					SMTP_SERVER="$saved_value"
				fi
				;;
			smtpadv)
				ask_for_smtp_advanced_parameters
				y=$?
				debug "ask_for_smtp_advanced_parameters return $y, output '$x'"
				if [ $y == 1 ]; then
					return $y
				fi		
				;;
			done)
				if [ -n "$SMTP_SERVER" ] \
					&& get_ip_from_hostname "$SMTP_SERVER" >&/dev/null
				then
					done=yes
	                smtp_saveconfig
				else
					message 'Incomplete form' \
						"Not all fields were filled, please fill in a value for SMTP_SERVER, or hit the 'Cancel' button if you wish to go back to the main menu."
				fi
				;;
		esac
		done
}

ask_for_smtp_advanced_parameters () {
    local done
    local saved_value
    local x
    local rc
    until test -n "$done"; do
	x="$(smtp_advanced_menu)" || return $?
	case "$x" in
	    smtpserver)
		saved_value="$SMTP_SERVER"
		SMTP_SERVER="$(ask_smtp_server "$SMTP_SERVER")"
		if [ $? -ne 0 ]; then
		    SMTP_SERVER="$saved_value"
		fi
		if [ -z "$(get_ip_from_hostname $SMTP_SERVER)" ]
			then
			message 'Invalid hostname' \
				"'$SMTP_SERVER' is not a valid hostname!"
			SMTP_SERVER="$saved_value"
		fi
		;;
	    smtpauth)   
		saved_value="$SMTP_AUTH"
		SMTP_AUTH="$(ask_smtp_auth "$SMTP_AUTH")"
		if [ $? -ne 0 ]; then
		    SMTP_AUTH="$saved_value"
		fi
		if [ "$SMTP_AUTH" = "on" ]; then
		    SMTP_PORT=465
		else
		    SMTP_PORT=25
		fi
		;;
	    smtpport)
		saved_value="$SMTP_PORT"
		SMTP_PORT="$(ask_smtp_port "$SMTP_PORT")"
		if [ $? -ne 0 ]; then
		    SMTP_PORT="$saved_value"
		fi		
		;;
	    smtpuser)   
		saved_value="$SMTP_AUTH_USER"
		SMTP_AUTH_USER="$(ask_smtp_auth_user "$SMTP_AUTH_USER")"
		if [ $? -ne 0 ]; then
		    SMTP_AUTH_USER="$saved_value"
		fi
		;;
	    smtppasswd) 
		saved_value="$SMTP_AUTH_PASSWD"
		SMTP_AUTH_PASSWD="$(ask_smtp_auth_passwd "$SMTP_AUTH_PASSWD")"
		if [ $? -ne 0 ]; then
		    SMTP_AUTH_PASSWD="$saved_value"
		fi
		;;
	    smtptls)    
		saved_value="$SMTP_TLS"
		SMTP_TLS="$(ask_smtp_tls "$SMTP_TLS")"
		if [ $? -ne 0 ]; then
		    SMTP_TLS="$saved_value"
		fi
		;;
	    save)
		if [ -n "$SMTP_SERVER" ] && get_ip_from_hostname "$SMTP_SERVER" >&/dev/null
		    then
		    smtp_saveconfig
		    return 1
		else
		    message 'Incomplete form' \
			"Not all fields were filled, please fill in values for the SMTP_SERVER"
		fi
		;;
	    "")
		break
		;;
	esac
    done
}


smtpconfig () {
	smtp_loadconfig
	ask_for_smtp_req_parameters
	y=$?
	debug "ask_for_smtp_req_parameters return $y, output $x"
	[ $y -ne 0 ] && return $y
	
}

smtp_saveconfig () {
#cat > $RA_CONF_DIR/smtp.conf <<EOF
#SMTP_SERVER=$SMTP_SERVER
#SMTP_PORT=$SMTP_PORT
#SMTP_AUTH=$SMTP_AUTH
#SMTP_AUTH_USER=$SMTP_AUTH_USER
#SMTP_AUTH_PASSWD=$SMTP_AUTH_PASSWD
#SMTP_TLS=$SMTP_TLS
#EOF
	debug "Writing msmtp.conf with values:
SMTP_SERVER=$SMTP_SERVER
SMTP_PORT=$SMTP_PORT
SMTP_AUTH=$SMTP_AUTH
SMTP_AUTH_USER=$SMTP_AUTH_USER
SMTP_AUTH_PASSWD=$SMTP_AUTH_PASSWD
SMTP_TLS=$SMTP_TLS
"
	cat > $RA_CONF_DIR/msmtp.conf <<EOF
account default
host $SMTP_SERVER
auth $SMTP_AUTH
tls $SMTP_TLS
EOF
	if [ "$SMTP_PORT" != "25" ]; then 
		cat >> $RA_CONF_DIR/msmtp.conf <<EOF
port $SMTP_PORT
EOF
	fi
	if [ "$SMTP_AUTH" = on ]; then
		cat >> $RA_CONF_DIR/msmtp.conf <<EOF
user $SMTP_AUTH_USER
password $SMTP_AUTH_PASSWD
EOF
	fi
	chmod 600 $RA_CONF_DIR/msmtp.conf
	message 'SMTP configuration saved' \
      "SMTP configuration saved in file '$RA_CONF_DIR/msmtp.conf'."
}




### newuser

user_req_menu () {
	Xdialog \
		--stdout \
		--title "$PROG" \
		--backtitle 'User certificate request creation' \
		--menu \
		"Please fill in all fields in the form below,
         by selecting each row in turn. Select 'Done'
         when all fields have their correct value." \
		0x0 6 \
		'fullname'  "User full name: ${USERFULLNAME:-<CHOOSE ONE>}" \
		'email'     "User email: ${USEREMAIL:-<CHOOSE ONE>}"  \
		'done'      "Proceed with request generation"
#		'rakey'     "RA key to use: $RAKEY" \
}

ask_for_user_req_parameters () {
	#unset RAKEY
	unset USERFULLNAME
	unset USEREMAIL
	
	local saved_value
	local done
	until test -n "$done"; do
		case "$(user_req_menu)" in
			#rakey)
			#	saved_value="$RAKEY"
			#	RAKEY=$(select_file $RA_ROOT_DIR/priv/ra-persons/*.pem \
			#		'Select the RA personal key to use') 
			#	if [ $? -ne 0 ]; then
			#		RAKEY="$saved_value"
			#	fi
			#	;;
			fullname)
				saved_value="$USERFULLNAME"
				USERFULLNAME="$(ask_username "$USERFULLNAME")" 
				if [ $? -ne 0 ]; then
					USERFULLNAME="$saved_value"
				fi
				;;
			email)
				saved_value="$USEREMAIL"
				USEREMAIL="$(ask_email \
					'the user, whose certificate is about to be requested' \
                    "$USEREMAIL")" 
				if [ $? -ne 0 ]; then
					USEREMAIL="$saved_value"
				fi
				;;
			done)
				if test -n "$USERFULLNAME" -a -n "$USEREMAIL";
                    #&& [ "$RAKEY" != "<Yet to choose>" ] \
				 then
					done=yes
					debug "ask_for_user_req will return ${ret_val:-0}"
	             else
					message \
						'Incomplete form' \
						"Not all fields were filled, please fill in values for
                         the user's full name and the user's e-mail."
				fi
				;;
			"") done=yes ; ret_val=1;  break;;
		esac
	done

	ret_val=${ret_val:-0}
	return ${ret_val}
}


newuser () { 
    if [ -z "$SMTP_SERVER" ];
	then
		message \
			'SMTP server not configured yet' \
			"No server for outgoing mails has been configured yet.
             Since requesting a new certificate requires sending a mail
             to the CA address <$CA_SUBMIT_EMAIL>, please configure 
             the SMTP server first, by selecting the 'smtpconfig' 
             action from the main menu."
		return 1
    fi

	ask_for_user_req_parameters \
		|| return $?

	#
	# ask for password
	#
	message \
		'About to choose certificate password' \
		"$USERFULLNAME, you will now be prompted for the passphrase 
         to set on your Grid certificate.  Be sure to remember this 
         passphrase as you cannot use your certificate without it, 
         and it cannot be recovered by any means if you forget it.
         The passphrase should be kept secret as it is the only protection
         against unauthorized Grid access and certificate usage; 
         please be warned that improper passphrase custody is sufficient 
         ground for certificate revocation."

	local password
	password="$(ask_password "$USERFULLNAME, choose your password")"

	local confirm
	confirm="$(ask_password 'Enter the same password again' \
                'Please enter the same password again for confirmation.
                 If the two do not match, this certificate
                 generation process will be aborted.')"
	if [ "$password" != "$confirm" ]; then
		message \
			'Certificate request aborted' \
			"The passwords you provided don't match.
             Aborting certificate request and going
             back to main menu."
		return 1
	fi


	#
	# immediately abort on error
	#
	set -e


	# 
	# create directory structure
    #
	outdir="$RA_ROOT_DIR/priv/certs/ou=people/cn=$(echo -n $USERFULLNAME|tr '[[:space:]]' '_'|tr '[[:upper:]]' '[[:lower:]]')"
	mkdir -p $outdir


	#
	# make up conf file by its parts
	#
	cat $RA_CONF_DIR/openssl/openssl.conf > $outdir/openssl.conf

	cat $RA_CONF_DIR/openssl/user_certificate_request_extensions.conf \
		>> $outdir/openssl.conf
	cat >> $outdir/openssl.conf <<EOF

subjectAltName = email:$USEREMAIL
EOF

	cat $RA_CONF_DIR/openssl/req.conf >> $outdir/openssl.conf
	cat >> $outdir/openssl.conf <<EOF

req_extensions = user_certificate_request_extensions
EOF

	cat $RA_CONF_DIR/openssl/distinguished_name.conf >>$outdir/openssl.conf
	cat >> $outdir/openssl.conf <<EOF

organizationalUnitName = "people"
commonName             = "$USERFULLNAME"
emailAddress           = "$USEREMAIL"
EOF


    #
    # generate request
    #
	message \
		'About to generate request' \
		'Next step will invoke OpenSSL to generate certificate request.
         This may take up to some minutes time, depending on the speed 
         of your CPU, as it is a computationally-intensive part.'
	echo "$password" | openssl req \
		-new \
		-passout fd:0 \
		-keyout $outdir/userkey.pem \
		-out $outdir/userreq.pem \
		-config $outdir/openssl.conf

	# 
	# mail request out to CA
	#
	subject="$(print_request_subject $outdir/userreq.pem)"
	ra_fullname="$(print_fullname_from_certificate "$RA_CERT")"
	ra_email="$(print_email_from_certificate "$RA_CERT")"
	message \
		'About to mail request to CA' \
		"A certificate request with subject '$subject' was
         successfully generated.  Next step is to mail the
         request to the CA submit address '$CA_SUBMIT_EMAIL'
         with S/MIME encryption.  You will be asked for the
         RA passphrase to encrypt the mail message."
	$run_in_term  mutt $CA_SUBMIT_EMAIL \
		-e "set realname=\"$ra_fullname\";" \
		-e "set from=\"$ra_email\";" \
		-e "my_hdr From: $ra_fullname <$ra_email>" \
		-e "set folder=~;" \
		-e 'set autoedit; set edit_headers;' \
		-e 'set editor="/bin/true";' \
		-e 'set crypt_autosign=yes;' \
		-e 'set smime_is_default=yes;' \
		-e "set smime_keys=\"$RA_SMIME_DIR/keys\";" \
		-e "set smime_certificates=\"$RA_SMIME_DIR/certificates\";" \
		-e "set smime_ca_location=\"$RA_SMIME_DIR/ca-bundle.crt\";" \
		-e "set smime_default_key=\"$RA_KEY\";" \
		-e "set smime_sign_as=\"$RA_KEY\";" \
        -e "set sendmail=\"/usr/bin/msmtp --from $ra_email -C $RA_CONF_DIR/msmtp.conf\";" \
		-s "USERREQ: $subject" \
		-a $outdir/userreq.pem
}



### newhost

host_req_menu () {
	Xdialog \
		--stdout \
		--title "$PROG" \
		--backtitle 'Host certificate request creation' \
		--menu \
		"Please fill in all fields in the form below,
         by selecting each row in turn. Select 'Done'
         when all fields have their correct value." \
		0x0 6 \
		'dnsname'  \
             "Host fully-qualified DNS name: ${HOSTDNSNAME:-<CHOOSE ONE>}" \
		'email'    "Responsible person email: ${USEREMAIL:-<CHOOSE ONE>}"  \
		'done'     "Proceed with request generation"
}

ask_for_host_req_parameters () {
	#unset RAKEY
	unset HOSTDNSNAME
	unset USEREMAIL
	
	local saved_value
	local done
	until test -n "$done"; do
		case "$(host_req_menu)" in
			#rakey)
			#	saved_value="$RAKEY"
			#	RAKEY=$(select_file $RA_ROOT_DIR/priv/ra-persons/*.pem \
			#		'Select the RA personal key to use') 
			#	if [ $? -ne 0 ]; then
			#		RAKEY="$saved_value"
			#	fi
			#	;;
			dnsname)
				saved_value="$HOSTDNSNAME"
				HOSTDNSNAME="$(ask_hostname "$HOSTDNSNAME")" 
				if [ $? -ne 0 ]; then
					HOSTDNSNAME="$saved_value"
				fi
				;;
			email)
				saved_value="$USEREMAIL"
				USEREMAIL="$(ask_email \
					'the user, who will be responsible for this host' \
                    "$USEREMAIL")"
				if [ $? -ne 0 ]; then
					USEREMAIL="$saved_value"
				fi
				;;
			done)
				if test -n "$HOSTDNSNAME" -a -n "$USEREMAIL";
                    #&& [ "$RAKEY" != "<Yet to choose>" ] \
				 then
					done=yes
	             else
					message \
						'Incomplete form' \
						"Not all fields were filled, 
                         please fill in values for
                         the full name of the user 
                         and the user e-mail."
				fi
				;;
			"") done=yes ; ret_val=1;  break;;
		esac
	done

	ret_val=${ret_val:-0}
	return ${ret_val}
}


newhost () {
    if [ -z "$SMTP_SERVER" ];
	then
		message \
			'SMTP server not configured yet' \
			"No server for outgoing mails has been configured yet.
             Since requesting a new certificate requires sending a mail
             to the CA address <$CA_SUBMIT_EMAIL>, please configure 
             the SMTP server first, by selecting the 'smtpconfig' 
             action from the main menu."
		return 1
    fi

	ask_for_host_req_parameters \
		|| return $?

	#
	# immediately abort on error
	#
	set -e


	# 
	# create directory structure
    #
	outdir="$RA_ROOT_DIR/priv/certs/ou=host/cn=$(echo -n $HOSTDNSNAME|tr '[[:upper:]]' '[[:lower:]]')"
	mkdir -p $outdir


	#
	# make up conf file by its parts
	#
	cat $RA_CONF_DIR/openssl/openssl.conf > $outdir/openssl.conf

	cat $RA_CONF_DIR/openssl/host_certificate_request_extensions.conf \
		>> $outdir/openssl.conf
	cat >> $outdir/openssl.conf <<EOF

subjectAltName = email:$USEREMAIL,DNS:$HOSTDNSNAME
EOF

	cat $RA_CONF_DIR/openssl/req.conf >> $outdir/openssl.conf
	cat >> $outdir/openssl.conf <<EOF

req_extensions = host_certificate_request_extensions
EOF

	cat $RA_CONF_DIR/openssl/distinguished_name.conf >>$outdir/openssl.conf
	cat >> $outdir/openssl.conf <<EOF

organizationalUnitName = "host"
commonName             = "$HOSTDNSNAME"
emailAddress           = "$USEREMAIL"
EOF


    #
    # generate request
    #
	message \
		'About to generate request' \
		'Next step will invoke OpenSSL to generate certificate request.
         This may take up to some minutes time, depending on the speed 
         of your CPU, as it is a computationally-intensive part.'
	openssl req \
		-new \
		-nodes \
		-keyout $outdir/hostkey.pem \
		-out $outdir/hostreq.pem \
		-config $outdir/openssl.conf

	# 
	# mail request out to CA
	#
	subject="$(print_request_subject $outdir/hostreq.pem)"
	ra_fullname="`print_fullname_from_certificate "$RA_CERT"`"
	ra_email="`print_email_from_certificate "$RA_CERT"`"
	message \
		'About to mail request to CA' \
		"A certificate request with subject '$subject' was
         successfully generated.  Next step is to mail the
         request to the CA submit address '$CA_SUBMIT_EMAIL'
         with S/MIME encryption.  You will be asked for the
         RA passphrase to encrypt the mail message."
	$run_in_term  mutt $CA_SUBMIT_EMAIL \
		-e "set realname=\"$ra_fullname\";" \
		-e "set from=\"$ra_email\";" \
		-e "my_hdr From: $ra_fullname <$ra_email>" \
		-e "set folder=~;" \
		-e 'set autoedit; set edit_headers;' \
		-e 'set editor="/bin/true";' \
		-e 'set crypt_autosign=yes;' \
		-e 'set smime_is_default=yes;' \
		-e "set smime_keys=\"$RA_SMIME_DIR/keys\";" \
		-e "set smime_certificates=\"$RA_SMIME_DIR/certificates\";" \
		-e "set smime_ca_location=\"$RA_SMIME_DIR/ca-bundle.crt\";" \
		-e "set smime_default_key=\"$RA_KEY\";" \
		-e "set smime_sign_as=\"$RA_KEY\";" \
        -e "set sendmail=\"/usr/bin/msmtp --from $ra_email -C $RA_CONF_DIR/msmtp.conf\";" \
		-s "HOSTREQ: $subject" \
		-a $outdir/hostreq.pem
}


### import



add_ra_cert () {
	local certfile
	local keyfile
	local name

	
	certfile=$(select_file '*.pem' \
		         'Select the RA personal certificate to import') \
		|| return $?
	
	# minimal sanity check
	if ! grep -q 'CERTIFICATE' $certfile; then
		message \
			'Not a certificate file' \
			"This file does not look like a PEM-encoded
             certificate file.  Going back to main menu."
		return 1
	fi
	if grep -q 'PRIVATE KEY' $certfile; then
		die 1 "This file look like a mixed certificate/key file. 
	Two separate certificate/key file are needed."
	else
		keyfile=$(select_file '*.pem' \
                    'Select the RA personal KEY to import') \
			|| return $?
	     # minimal sanity check
		if ! grep -q 'PRIVATE KEY' $keyfile; then
			message \
				'Not a private key file' \
				"This file does not look like a PEM-encoded
             private key file.  Please bear in mind that
             your private KEY is needed, not your public 
             certificate. Going back to main menu."
			return 1
		fi	
	fi
	

	# select cacert file
	issuerdn="`openssl x509 -noout -issuer -in $certfile`"
	cafile="$RA_CACERTS_DIR/`echo $issuerdn|sed -e 's:.*/CN=\(.*\):\1:'|tr ' ' '_'`.pem"


	debug "cafile='$cafile', keyfile='$keyfile', certfile='$certfile'"

	test -e "$cafile" || \
	    die 1 "Unable to find certificate '$cafile'. 
	Please download and save it in the '$RA_CACERTS_DIR' directory"
	
	# we want to use 'smime_keys' to install the RA certificate,
	# but since ``smime_keys`` has no option to set
	# the directory to operate on, we have to trick it
	# into invoking a false mutt...
	cmd=`mktemp` \
		|| die 1 "Cannot make a temporary file - aborting"
	cat > $cmd <<EOF
#!/bin/sh

# fake ``mutt -Q config_var``
var="\$2"

case "\$var" in
  smime_keys) echo smime_keys='"$RA_SMIME_DIR/keys"' ;; 
  smime_certificates) echo smime_certificates='"$RA_SMIME_DIR/certificates"' ;;
  smime_ca_location) echo smime_ca_location='"$RA_SMIME_DIR/ca-bundle.crt"' ;;
esac
EOF
	chmod +rx $cmd
	# `smime_keys add_chain` will ask for a label
	name_label_from_certificate "$certfile" \
		| MUTT_CMDLINE=$cmd \
		    smime_keys add_chain "$keyfile" "$certfile" "$cafile" >/dev/null
	#rm -f $cmd
}


### change smime keys passphrase

# change_pem_passphrase FILE DESC
#
# Asks old and new passphrase and changes the passphrase on the
# given PEM file, overwriting it with the new encrypted one.
# Uses DESC as a string describing FILE in user prompts.
#
change_pem_passphrase () {
  local keyfile="$1"
  local desc="$2"

  local oldpass newpass1 newpass2 tmpkeyfile stderrfile rc

  # loading passphrases into shell variables is a little insecure, 
  # as it leaves the password in some memory to be garbage collected...
  # there might be a way to retrieve it, but... well,
  # this is a shell script, i don't think we can make any better...
  oldpass="$(ask_password 'Old passphrase:' \
               "Please type in the CURRENT passphrase for the private
                key ${desc:-file}")" \
					|| return 1

  newpass1="$(ask_password 'NEW passphrase:' \
               "Please type in the desired NEW passphrase for the private
                key ${desc:-file}")" \
					|| return 1

  newpass2="$(ask_password 'Verify NEW passphrase:' \
               "For verification purposes, type in again the same NEW 
                passphrase for the private key ${desc:-file}")" \
					|| return 1

  if [ "$newpass1" != "$newpass2" ]; then
	  message \
		  'Passwords do not match' \
		  "You did not type in twice the same new passphrase; 
           passphrase change aborted. Going back to main menu."
	  return 2
  fi
  unset newpass2

  tmpkeyfile=`mktemp` \
	  || die 1 "Cannot make a temporary file - aborting."
  stderrfile=`mktemp` \
	  || die 1 "Cannot make a temporary file - aborting."

  (
      # catch the error output from commands, and 
      # print message to user...
	  catch_error () {
		  message 'Fatal error' \
			  "Failed to change passphrase;  
         an error occurred in OpenSSL invocation: '$(<$stderrfile)'
         This usually means that you typed a wrong CURRENT passphrase."
		  exit 1
	  }
	  trap 'catch_error' EXIT
	  set -e
	  
	  (echo "$oldpass"; echo "$newpass1"; echo "$newpass1"; ) \
		  | openssl rsa -passin stdin -passout stdin \
	          -des3 -in "$keyfile" -out "$tmpkeyfile" 2>$stderrfile
	  
	  catch_error () {
		  message 'Fatal error' \
			  "Failed to change passphrase;  
         an error occurred: '$(<$stderrfile)'
         Tha passphrase will not be changed."
		  exit 1
	  }
	  chmod 0600 "$tmpkeyfile"
	  rm -f "${keyfile}.old"
	  cp -p "$keyfile" "${keyfile}.old"
	  mv -f "$tmpkeyfile" "$keyfile"
	  
	  set +e
	  trap '' EXIT
  ) 
  rc=$?

  rm -f $tmpkeyfile $stderrfile

  if test $rc -eq 0; then
	  message 'Passphrase changed' \
		  "Passphrase on key ${desc:-file} was successfully changed."
  else
	  message 'Passphrase NOT changed' \
		  "Passphrase on key ${desc:-file} was NOT changed."
  fi

  return ${rc:-0}
}

# select_smime_key FILE MSG
#
# read FILE (which has the format of the ``keys/.index`` file
# created by ``sime_keys``), and prompt the user with MSG to choose 
# a key file based on the email address associated to it.  
# Output the chosen file name and the descriptive tag to stdout.
#
select_smime_key () {
	local indexfile="$1"
	local msg="$2"

	local selected

	selected="$(awk <$indexfile '{print $3, "\"Key for <"$1">\"";}' \
		          | xargs Xdialog --wrap --stdout --menubox "$msg" 0 0 8)" \
						  || return 1
	awk <$indexfile '($3 == "'"$selected"'") { print $2, $3; }'
}


change_smime_key_passphrase_menu () {
	local file_and_desc

	file_and_desc="$(select_smime_key $RA_SMIME_DIR/keys/.index \
        "Please select the private key file whose passphrase
         you want to change now.")" \
		|| return 1
	
	change_pem_passphrase "$RA_SMIME_DIR"/keys/$file_and_desc
}



### main

if test -n "$RA_ENCFS_STORAGE_DIR"; then
	message \
		'About to mount encripted filesystem' \
		"Sensitive data is stored in an encrypted filesystem,
         to protect it from media theft or accidental dispersion.
         You will now be asked for the password to access this
         encrypted filesystem."
	encfs \
		--idle={$RA_ENCFS_IDLE:-15} \
		--extpass=/usr/bin/ssh-askpass \
		$RA_ENCFS_STORAGE_DIR \
		$RA_ROOT_DIR/priv \
	  die 2 "Cannot mount EncFS filesystem '$RA_ENCFS_STORAGE_DIR' on '$RA_ROOT_DIR/priv' - aborting"	
fi

# move TMPDIR to the encrypted storage
if test -d "$RA_ROOT_DIR/priv/tmp"; then
	TMPDIR="$RA_ROOT_DIR/priv/tmp"
else
	TMPDIR=`mktemp -d ${TMPDIR:-/tmp}/ra.XXXXXX` \
		|| die $? 'Cannot create temporary working directory'
fi
chmod 0700 ${TMPDIR:?Null TMPDIR, aborting.}
export TMPDIR

# select key to mail request with
RA_KEY="$(select_smime_key $RA_SMIME_DIR/keys/.index \
            'Please select the RA personal key to
             use for this session. You will use this identity
             to mail out certificate requests to the CA.  
             Later on, when mailing requests, you will be asked
             for the corresponding passphrase.  ')" \
	|| die 1 'An RA key is necessary to proceed.  Exiting now,
           you may run this script again and select an RA key.'
RA_KEY="`echo $RA_KEY | cut -d' ' -f1`"
RA_CERT="$RA_SMIME_DIR/certificates/`basename $RA_KEY`"
export RA_KEY
export RA_CERT
                       
# load smtp server config
smtp_loadconfig


# main loop
unset done
until test -n "$done"
  do
  x="$(main_menu 2>/dev/null)"
   case "$x" in
	   smtpconfig)
		   smtpconfig
		   ;;
	   newuser) 
		   newuser 
		   ;;
	   newhost) 
		   newhost 
		   ;;
	   passphrase)
           change_smime_key_passphrase_menu
		   ;;
	   netconfig) 
		   sudo netcardconfig 
		   ;;
	   poweroff) 
		   # umount encfs
		   test -n "$RA_ENCFS_STORAGE_DIR" \
			   && fusermount -u "$RA_ENCFS_STORAGE_DIR"
		   sudo poweroff 
		   ;;
	   import) 
		   add_ra_cert
		   ;;
	   exit) 
		   # umount encfs
		   test -n "$RA_ENCFS_STORAGE_DIR" \
			   && fusermount -u "$RA_ENCFS_STORAGE_DIR"
		   done="yes" 
           ;;
       *)
		   debug "x='$x'" 
		   ;;     
   esac
 done
exit 0
