msmtp
msmtp copied to clipboard
msmtpq: added support of config file for msmtpq script.
To be able to use a config file ~/.msmtpqrc
for msmtpq.
Since I have not yet received feedback about my pull request, this is to friendly ask about it's status. Can you please have a look at it @marlam ?
Hi, sorry I did not yet look at it. The msmtpq script is currently without a maintainer and I really have no opinion on whether it makes sense to have a configuration file for it or not. Can someone else comment on this please?
Hi. Thanks for your response. Of course configuration of msmtpq script can be done via environment variable, but I prefer to have a configuration file similar to msmtp itself. If and only if the configuration file is found, it will be used, so nothing breaks. At the moment I patch the msmtpq script to enable me using the config file, but of course after every new version of msmtp I have to patch it again. Hence I would prefer, if my pull request would be accepted, and have it upstream. Hopefully it can be considered.
fwiw, i'd +1 this idea. the current configuration is via modifying the script itself. very suckless, but hard (or, inconvenient) if people are, e.g, installing from a package manager. also, when upgrading, trying to pull one's changes along can be a pain.
in terms of the code, i worry about source
ing a config file (mainly, because of possible security issues), rather than some (hopefully simple) parsing.
OK, can you update this to also support $XDG_CONFIG_HOME/msmtpq/config
in addition to (or instead of) ~/.msmtpqrc
? To make the freedesktop.org people happy and allow for a cleaner $HOME
.
hi. since i was unhappy about sourcing a config file, i translated some python code i have been working on to bash. "it slices, it dices, it's oversized". (BUT DON'T USE -- I'M STILL DEVELOPING, NOT EVEN USING IT YET MYSELF!!!!)
config-file.patch
diff --git a/scripts/msmtpq/README.msmtpq b/scripts/msmtpq/README.msmtpq
index ee271d4..1d08ea1 100644
--- a/scripts/msmtpq/README.msmtpq
+++ b/scripts/msmtpq/README.msmtpq
@@ -32,7 +32,7 @@ copy msmtpq and msmtp-queue to whatever location is best for you
(I use /usr/local/bin) ; the directory chosen should be on the path
replace the msmtp invocation in your mail client with msmtpq ; e.g. for
-mutt : 'set sendmail = /path/to/msmtpq'
+mutt : 'set sendmail = /path/to/msmtpq' (but, see below)
msmtpq will then take care of the management and routing of outgoing
mail ; normally sending a mail is nearly instantaneous, but a very long
@@ -55,7 +55,17 @@ from msmtpq
Configuration :
-------------
-all config is done by exporting these environment variables
+the msmtpq script defines defaults for the configuration variables.
+these values can then be overwritten by environment variables, a
+system configuration file, and/or a user-specific configuration file
+(each succeeding one taking precedence over the preceding)
+
+user-specific configuration can be done by a config file, or with
+environment variables
+
+Environment Variables :
+
+config can done by exporting these environment variables
set the MSMTP var to point to the location of the msmtp executable
(set this only if necessary ; if it's not on the path)
@@ -68,14 +78,38 @@ circumstances, such as embedded systems, etc. ; otherwise, if you are
running a normal Linux distribution you can leave it as is ; msmtp will
by default be on the execution path
-the MSMTPQ_Q variable should have the location of the queue directory ;
-please note that it's preferable to create the queue directory (with
-0700 permissions) before using these routines ; the Q variable is a
-deprecated alias of MSMTPQ_Q
+the MSMTPQ_Q variable should have the location of the queue directory
+; please note that it's preferable to create the queue directory (with
+0700 permissions) before using these routines ; the Q environment
+variable is a deprecated alias of MSMTPQ_Q
+
+if MSMTPQ_Q is not specified, it is first looked for in its *legacy*
+location, i.e., "~/.msmtp.queue". if that is found, and is a
+directory, that is used. otherwise, if the environment variable
+XDG_DATA_HOME exists, "${XDG_DATA_HOME}/msmtp/msmtp.queue"
the MSMTPQ_LOG variable should have the desired name & location of the
-queue log ; set it to an empty string (export MSMTPQ_LOG="") to disable
-logging ; the LOG variable is a deprecated alias of MSMTPQ_LOG
+queue log ; set it to an empty string (export MSMTPQ_LOG="") to
+disable logging ; the LOG environment variable is a deprecated alias
+of MSMTPQ_LOG
+
+Config File :
+
+each line (leading blanks are ignored) of a config file should either
+- begin with a hash mark ('#'), indicating a comment to be ignored
+- be blank, also ignored
+- start with the name of a configuration variable (MSMTP, MSMTPQ_Q, etc.), then
+ a blank, then a value for the speciried configuration variable
+
+if the system configuration file -- by default /etc/msmtp/msmtp.config
+-- exists, it is parsed
+
+then a user-specific config file is looked for in
+${XDG_CONFIG_HOME}/msmtp, if the XDG_CONFIG_HOME environment variable
+exists, or in ${HOME}/.config/msmtp.
+
+Logging :
+-------
note that the default msmtpq set up creates a separate log for queue
operations ; all operations which modify the queue in any way are logged
@@ -94,71 +128,19 @@ msmtp docs & man page.
to use msmtpq with additional options :
-the vars : EMAIL_CONN_NOTEST (to test for a net connection or not
- before attempting to send a mail)
-
- (please note that the above var is deprecated
- and will be removed in a future version of msmtpq ;
- replace it with EMAIL_CONN_TEST=x
- to perform the same function)
-
+the vars :
EMAIL_CONN_TEST (which test for net connection to use)
EMAIL_QUEUE_QUIET (suppress unnecessary queue 'chatter')
-see below for their values
-
-there are two methods of setting options -
-
-
-- set the vars in the msmtpq script (near the top)
-
- see the script, just beneath the the three setup vars mentioned above
-
-- set the vars externally to the script
-
- define the variables as environment vars, set on the command line
- invoking msmtp
-
-set sendmail = "/path/to/msmtpq"
- # normal config for mutt to use the queue
- # (by default the ping connection test is enabled)
-
-set sendmail = "EMAIL_CONN_TEST=x /path/to/msmtpq"
- # use queue without a net connection test
-set sendmail = "EMAIL_CONN_TEST=p /path/to/msmtpq"
- # use queue with ping connection test (the default)
-set sendmail = "EMAIL_CONN_TEST=P /path/to/msmtpq"
- # use queue with faster ping connection test
- # (to an IP address, without a DNS lookup)
-set sendmail = "EMAIL_CONN_TEST=n /path/to/msmtpq"
- # use queue with netcat connection test
- # (netcat must be installed on user's system)
-set sendmail = "EMAIL_CONN_TEST=s /path/to/msmtpq"
- # use queue with sh sockets connection test
- # (this does *not* work on Debian systems ;
- # socket use is not compiled into bash for
- # security reasons on multiuser systems - it
- # may not be a serious consideration, however, on
- # single user workstations - e.g. on laptops -
- # or in embedded systems)
-
-set sendmail = "EMAIL_QUEUE_QUIET=t /path/to/msmtpq"
- # use queue ; suppress messages and 'chatter'
- # good, apparently, for use w/emacs MUAs
-
-set sendmail = "EMAIL_CONN_TEST=? EMAIL_QUEUE_QUIET=t /path/to/msmtpq"
- # use queue w/connection test spec &
- # suppression of messages and chatter
-
-externally multiple environmental vars may be used on the command
- line, space separated, before the msmtpq invocation ; internally
- to the script multiple vars may be set
+EMAIL_CONN_TEST, if specified, should be one of:
+-- x :: will suppress any testing for a connection
+-- p :: will use a ping test (debian.org) for a connection
+ (this is the default if not otherwise set)
+-- P :: will use a fast ping test (8.8.8.8) for a connection
+-- n :: will use netcat (nc) to test for a connection
+-- s :: will use bash sockets to test for a connection
-note that vars set internally will take precedence over vars set
- externally
-if no option is specified, by either method, the net connection test
- is made using the ping test by default
in summary :
@@ -168,8 +150,7 @@ in summary :
create the queue dir (0700) if necessary
( mkdir -p /path/to/queue/dir )
( chmod 0700 /path/to/queue/dir )
- enter or confirm the values of the three config setup vars in msmtpq
- enter or confirm the values of the three option vars in msmtpq - if desired
+ create system and/or user-specific config files
for mutt users -
set sendmail="/path/to/msmtpq" (or "ANY_ENV_VARS_HERE=t /path/to/msmtpq")
set sendmail_wait = -1
diff --git a/scripts/msmtpq/msmtpq b/scripts/msmtpq/msmtpq
index bcb384e..2371202 100755
--- a/scripts/msmtpq/msmtpq
+++ b/scripts/msmtpq/msmtpq
@@ -1,5 +1,58 @@
#!/usr/bin/env bash
+# XXX todos
+#
+# make sure only one, of the possible that we "by default" use, Q
+# directory and LOG file exist
+#
+# for Q and LOG paths, we do some checking and creating when
+# processing the config file, then again when processing options
+#
+# don't create Q and `dirname LOG` paths if in --q-mgmt
+#
+# xdg wants xdg_data_dirs to be next behind xdg_data_home
+
+
+# incompatibilities, behavior changes
+#
+# removed EMAIL_CONN_NOTEST
+#
+# errors (err()) now go to stderr
+
+
+# design questions
+#
+# if XDG_DATA_HOME does not exist, and if ~/.local/share does not
+# exist, where do we default the queue directory and log file? this
+# is particularly thorny since if one day we use ~/.msmtpq.queue and
+# the next day ~/.local/share shows up and we create our subdirectory
+# there, we may end up with messages sitting "forever" in the former.
+# possibly we should look in all places, and complain if we find more
+# than one candidate?
+
+
+# enhancements for future?
+#
+# work on, clean up, command line argument processing code
+#
+# separate scripts for "queue management" and actually adding a
+# message? probably requires a makefile (to share config code, at
+# least)
+#
+# error-report option to return permanent errors sending e-mail (to
+# user, whatever). this sort of wants msmtp to signal a temporary
+# failure when DNS fails as a result of network connection. on the
+# command line, the name of the sending user, maybe the SYSEXIT_ code,
+# the file name of the message that had the error (only valid until
+# error reporter exits); on stdin, the error message from msmtp to be
+# sent
+#
+# --identifier IDENTIFIER : to use when logging messages. my idea
+# would be to have an MUA invoke msmtpq (in "sendmail" mode) with the
+# message ID of the outgoing message. to correlate message in my
+# outbox (sent messages) with logs (including on next-hop SMTP
+# servers). if msmtp itself would accept that parameter, pass it on.
+
##--------------------------------------------------------------
##
## msmtpq : queue funtions to both use & manage the msmtp queue,
@@ -46,77 +99,113 @@
## if more than one mail per second is sent
##--------------------------------------------------------------
-
+## SYS_CONFIG_FILE is where we expect to find the system config file.
+## SYS_CONFIG_FILE can be null, or point to an invalid path, to
+## disable this functionality. packagers can, e.g., sed this to
+## whatever value is convenient e.g., something like
+# sed '/^SYS_CONFIG_FILE=/s|/etc/msmtp/msmtpq.conf|/usr/share/msmtp/msmtp.conf|'
+SYS_CONFIG_FILE=/etc/msmtp/msmtpq.conf
+
+## more for packagers: default name of user config file, default paths
+## to look for it.
+USER_CONFIG_FILE_NAME=msmtpq
+## path name of a directory under XDG_CONFIG_HOME (or, under
+## ${HOME}/.config, if unset) to find UPSTREAM_USER_CONFIG_FILE. we
+## live in the msmtp directory.
+USER_CONFIG_FILE_XDG_SUBDIRECTORY=msmtp
+## *file name* look for if XDG isn't set, or we can't find a config
+## file there. we append ".conf" to help the user understand what it
+## is.
+USER_CONFIG_FILE_NON_XDG="${HOME}/.${USER_CONFIG_FILE_NAME}.conf"
+
+
+
+# display a message using the arguments to dsp(); null arguments (like
+# "''") cause a blank line to be printed.
log_later() { LOG_LATER_ARGS=( "$@" ) ; }
dsp() { local L ; for L ; do [ -n "$L" ] && echo " $L" || echo ; done ; }
-err() { dsp '' "$@" '' ; exit 1 ; }
+err() { dsp '' "$@" '' > /dev/stderr ; exit 1 ; }
+
+
+## these are the program default values; possibly modified by some
+## environment variables, system-wide defaults, user defaults, command
+## line options (if any implemented). this array is also what we use
+## to decide what option names in a configuration file are legal
+declare -A defaults=(
+ ## only if necessary (in unusual circumstances - e.g. embedded systems),
+ ## enter the location of the msmtp executable (no quotes !!)
+ ## e.g. ( MSMTP=/path/to/msmtp )
+ ## and uncomment the test for its existence
+ [MSMTP]=msmtp
+ ## set the queue var to the location of the msmtp queue directory
+ ## if the queue dir doesn't yet exist, create it (0700)
+ ## before using this script
+ ## e.g. ( mkdir msmtp.queue )
+ ## ( chmod 0700 msmtp.queue )
+ ##
+ ## the queue dir - modify this to reflect where you'd like it to
+ ## be (no quotes !!)
+ [MSMTPQ_Q]=~/.msmtp.queue
+ ## set the queue log file var to the location of the msmtp queue log file
+ ## where it is or where you'd like it to be
+ ## ( note that the LOG setting could be the same as the )
+ ## ( 'logfile' setting in .msmtprc - but there may be )
+ ## ( some advantage in keeping the two logs separate )
+ ## if you don't want the log at all unset (comment out) this var
+ ## LOG=~/log/msmtp.queue.log --> #LOG=~/log/msmtp.queue.log
+ ## (doing so would be inadvisable under most conditions, however)
+ ##
+ ## the queue log file - modify (or comment out) to taste (but no
+ ## quotes !!)
+ [MSMTPQ_LOG]=~/log/msmtp.queue.log
+ ## msmtpq can use the following environment variables :
+ ## EMAIL_CONN_TEST if =x will suppress any testing for a connection
+ ## if =p or unset will use a ping test
+ ## (debian.org) for a connection
+ ## if =P will use a fast ping test (8.8.8.8) for a connection
+ ## if =n will use netcat (nc) to test for a connection
+ ## if =s will use bash sockets to test for a connection
+ [EMAIL_CONN_TEST]=n
+ ## EMAIL_QUEUE_QUIET if set will cause suppression of messages
+ ## and 'chatter' (perhaps useful for some
+ ## of the emacs mail clients)
+ [EMAIL_QUEUE_QUIET]="" # i.e., default is "unset"
+)
+
+
+
+declare -A config_options=(
+ [--no-config]=1
+ [--no-sys-config]=1
+ [--config-file]=1
+ [--sys-config-file]=1
+)
-## ======================================================================================
-## !!! please define or confirm the following three vars !!!
-## !!! before using the msmtpq or msmtp-queue scripts !!!
-## ======================================================================================
##
-## only if necessary (in unusual circumstances - e.g. embedded systems),
-## export the location of the msmtp executable before running this script (no quotes !!)
-## e.g. ( export MSMTP=/path/to/msmtp )
-MSMTP="${MSMTP:-msmtp}"
-"$MSMTP" --version >/dev/null 2>&1 || \
- log_later -e 1 "msmtpq : can't run the msmtp executable [ $MSMTP ]" # if not found - complain ; quit
+## these are environment variables we will import if they are defined
##
-## set the queue var to the location of the msmtp queue directory
-## if the queue dir doesn't yet exist, create it (0700)
-## before using this script
-## e.g. ( mkdir msmtp.queue )
-## ( chmod 0700 msmtp.queue )
-##
-## the queue dir - export this variable to reflect where you'd like it to be (no quotes !!)
-MSMTPQ_Q=${MSMTPQ_Q:-${Q:-~/.msmtp.queue}}
-[ -d "$MSMTPQ_Q" ] || mkdir -m 0700 -p "$MSMTPQ_Q" || \
- err '' "msmtpq : can't find or create msmtp queue directory [ $MSMTPQ_Q ]" '' # if not present - complain ; quit
-##
-## set the queue log file var to the location of the msmtp queue log file
-## where it is or where you'd like it to be
-## ( note that the MSMTPQ_LOG setting could be the same as the )
-## ( 'logfile' setting in .msmtprc - but there may be )
-## ( some advantage in keeping the two logs separate )
-## if you don't want the log at all set the var to an empty string
-## (doing so would be inadvisable under most conditions, however)
-##
-## the queue log file - export this variable to change where logs are stored (but no quotes !!)
-## Set it to "" (empty string) to disable logging.
-if [ ! -v MSMTPQ_LOG ] ; then
- if [ -v LOG ] ; then
- MSMTPQ_LOG="$LOG"
- else
- MSMTPQ_LOG=~/log/msmtp.queue.log
- fi
-fi
-[ -d "$(dirname "$MSMTPQ_LOG")" ] || mkdir -p "$(dirname "$MSMTPQ_LOG")"
-## ======================================================================================
-
-## msmtpq can use the following environment variables :
-## EMAIL_CONN_NOTEST if set will suppress any testing for a connection
-## (the above var is deprecated & will be removed ; use the var below)
-## EMAIL_CONN_TEST if =x will suppress any testing for a connection
-## if =p or unset will use a ping test (debian.org) for a connection
-## if =P will use a fast ping test (8.8.8.8) for a connection
-## if =n will use netcat (nc) to test for a connection
-## if =s will use bash sockets to test for a connection
-## EMAIL_QUEUE_QUIET if set will cause suppression of messages and 'chatter'
-## (perhaps useful for some of the emacs mail clients)
-##
-## ======================================================================================
-## !!! define or confirm the following vars if you wish to set !!!
-## !!! these properties here in the script - the same properties !!!
-## !!! may be set externally, by means of environment variables !!!
-## !!! note the internal variables, if set, will take precedence !!!
-## !!! over properties set via environment variables !!!
-## ======================================================================================
-##
-#EMAIL_CONN_NOTEST=y # deprecated ; use below var
-#EMAIL_CONN_TEST={x| |p|P|n|s} # see settings above for EMAIL_CONN_TEST
-#EMAIL_QUEUE_QUIET=t
-## ======================================================================================
+declare -A environs=(
+ [MSMTP]=MSMTP
+ [MSMTPQ_Q]=MSMTPQ_Q
+ [Q]=MSMTPQ_Q
+ [MSMTPQ_LOG]=MSMTPQ_LOG
+ [LOG]=MSMTPQ_LOG
+)
+
+
+
+
+# for each option in options, where did it come from (defaults, config
+# file name)
+declare -Ag sources
+# the options we determine. options from any user config file take
+# precedence over any system config file, which in turn takes
+# precendence over program defaults. later, these are passed off to
+# various scalar variables (MSMTP, etc.)
+declare -Ag options
+
+# are we being verbose?
+declare -g verbose
umask 077 # set secure permissions on created directories and files
@@ -137,6 +226,195 @@ on_exit() { # unlock the queue on exit if the lock was
## ----------------------------------- (msmtpq & msmtp-queue)
#
+# helper function for setdefaults(): create, if needed, places for
+# things relative to xdg_data_home
+#
+# inputs:
+#
+# $1: the tag in the defaults[] array
+#
+# $2: the legacy location of the directory
+#
+# $3: relative path to the directory in which it should live (relative
+# to XDG)
+#
+# $4: (optional, only if file, rather than directory, is wanted) the
+# file name to live in the directory $2 (in order to set defaults to
+# the path to the actual file, not the path to the directory
+#
+# update:
+#
+# the defaults[] array
+doxdgdata() {
+ if [ -d "$2" ]; then
+ directory="$2"
+ else
+ # it doesn't exist in the legacy place. put whatever it is
+ # somewhere XDG likes. first, make sure we have the XDG data
+ # directory
+ local xdgdata=${XDG_DATA_HOME:-"${HOME}/.local/share"} # XXX hard coded, but from spec
+ local directory="${xdgdata}/$3"
+ if [ ! -d "${xdgdata}" ]; then
+ # shellcheck disable=SC2174
+ mkdir -p -m 0700 "${xdgdata}" # try to get the right file mode here
+ fi
+ if [ ! -d "${directory}" ]; then # XXX hard coded file name
+ # shellcheck disable=SC2174
+ mkdir -p -m 0700 "${directory}"
+ fi
+ fi
+ if [ -n "$4" ]; then
+ defaults["$1"]="${directory}/$4"
+ else
+ defaults["$1"]="${directory}"
+ fi
+}
+
+# now, *if* the old, historic, directory/file doesn't exit, re-think
+# MSMTPQ_Q and MSMTPQ_LOG based on XDG_DATA_HOME. if it is defined,
+# and *that* directory exists, then use that with the relevant
+# subdirectory.
+setdefaults() {
+ doxdgdata MSMTPQ_Q "${defaults[MSMTPQ_Q]}" msmtp/msmtpq.queue
+ doxdgdata MSMTPQ_LOG "$(dirname "${defaults[MSMTPQ_LOG]}")" msmtp msmtp.queue.log
+}
+
+## process the defaults, putting them in options (and setting sources
+## to refect their origin)
+eatdefaults() {
+ for var in "${!defaults[@]}"; do
+ options[${var}]=${defaults[${var}]}
+ sources[${var}]=defaults
+ done
+}
+
+## look for any environment variables
+eatenvirons() {
+ declare -A setted
+
+ for env in "${!environs[@]}"; do
+ index="${environs[${env}]}" # where it goes in array
+ # x=$(eval echo $`echo "${yyy}"`)
+ val=$(eval echo $`echo "${env}"`) # value of environmental variable
+ if [ -n "${val}" ]; then
+ if [ -n "${setted[${index}]}" ]; then
+ err "${index} set by two environment variables: ${env} and ${setted[${index}]}"
+ fi
+ setted[${index}]=${env} # for error checking, keep track of what we've set
+ options[${index}]="${val}"
+ sources[${index}]=environment
+ fi
+ done
+
+ # now, backwards compatibility requires
+ if [ -n "${Q}" ]; then
+ options[MSMTPQ_Q]="${Q}"
+ sources[MSMQPQ_Q]=environment
+ fi
+ if [ -n "${LOG}" ]; then
+ options[MSMTPQ_LOG]="${LOG}"
+ sources[MSMTPQ_LOG]=environment
+ fi
+}
+
+## process the contents of a configuration file. some helps:
+## https://danielfgray.gitlab.io/computers/bash-config/
+## https://askubuntu.com/a/743641
+eatconfig() {
+
+ # dealing with missing terminating newline:
+ # https://stackoverflow.com/a/12919766/1527747
+ while read -r var value || [ -n "${var}" ]; do
+ if [ -z "${var}" ] || [ "${var:0:1}" = "#" ]; then
+ continue
+ fi
+ if [[ ${verbose} -gt 1 ]]; then
+ printf "var %s: %s\n" "${var}" "${value}"
+ fi
+ if [[ ! -v defaults[${var}] ]]; then
+ err "config file \"${1}\": invalid option \"${var}\""
+ fi
+ # if verbose, then if we are replacing something from *one*
+ # configuration file (presumably, the system one) with a
+ # different value from a *different* configuration file
+ # (presumably, the user's), issue a warning
+ if [ -n "${verbose}" ] &&
+ [ -n "${options[${var}]}" ] &&
+ [ ! "${sources[${var}]}" = default ] &&
+ [ ! "${value}" = "${options[${var}]}" ]; then
+ echo "file \"${1}\" overrides \"${var}\" set in \"${sources[${var}]}\""
+ fi
+ sources[${var}]="${1}"
+ options[${var}]=$(eval "$(printf "echo %s" "${value}")")
+ done < "${1}"
+}
+
+
+## using the defaults and config file, set configuration parameters
+## from the options array (which, at this point, has an entry for
+## every possible option, though maybe an "unset" entry for some)
+##
+## then, check some to make sure they are valid
+setoptions() {
+ MSMTP=${options[MSMTP]}
+ if ! "$MSMTP" --version >/dev/null 2>&1; then
+ # if not found, or not executable, complain ; quit
+ log -e 1 "msmtpq : can't run the msmtp executable [ $MSMTP ]"
+ fi
+
+ MSMTPQ_Q=${options[MSMTPQ_Q]}
+ # if not present and can't create - complain ; quit
+ if [ ! -d "$MSMTPQ_Q" ]; then
+ # shellcheck disable=SC2174
+ if ! mkdir -m 0700 -p "$MSMTPQ_Q"; then
+ # if not present and can't create - complain ; quit
+ err '' "msmtpq : can't find or create msmtp queue directory [ $MSMTPQ_Q ]" ''
+ fi
+ fi
+
+ MSMTPQ_LOG=${options[MSMTPQ_LOG]}
+ if [ ! -d "$(dirname "$MSMTPQ_LOG")" ]; then
+ # shellcheck disable=SC2174
+ if ! mkdir -p "$(dirname "$MSMTPQ_LOG")"; then
+ err '' "msmtpq : can't find msmtpq log and" ''\
+ "can't create msmtpq log directory ($(dirname "${MSMTPQ_LOG}"))"
+ fi
+ fi
+ ## ======================================================================================
+
+ ## ======================================================================================
+ ## !!! define or confirm the following vars if you wish to set !!!
+ ## !!! these properties here in the script - the same properties !!!
+ ## !!! may be set externally, by means of environment variables !!!
+ ## !!! note the internal variables, if set, will take precedence !!!
+ ## !!! over properties set via environment variables !!!
+ ## ======================================================================================
+ ##
+ #EMAIL_CONN_TEST={x| |p|P|n|s} # see settings above for EMAIL_CONN_TEST
+ EMAIL_CONN_TEST=${options[EMAIL_CONN_TEST]}
+ # test for a valid value
+ if [ -n "${EMAIL_CONN_TEST}" ]; then
+ if [ ${#EMAIL_CONN_TEST} -ne 1 ] ||
+ [ -n "${EMAIL_CONN_TEST/[xpPns]/}" ]; then
+ err '' "msmtpq: invalid EMAIL_CONN_TEST option \"${EMAIL_CONN_TEST}\" (from \"${sources[EMAIL_CONN_TEST]}\"); must be one of: n p P s x"
+ fi
+ fi
+ EMAIL_QUEUE_QUIET=${options[EMAIL_QUEUE_QUIET]}
+ if [ -n "${EMAIL_QUEUE_QUIET}" ] && [ ! "${EMAIL_QUEUE_QUIET}" = "y" ]; then
+ err '' "msmtpq: illegal EMAIL_QUEUE_QUIET option \"${EMAIL_QUEUE_QUIET}\" (set in \"${sources[EMAIL_QUEUE_QUIET]}\") should be unset or set to 'y'"
+ fi
+}
+
+## dump the options, with their sources
+dumptions() {
+ for opt in "${!options[@]}"; do
+ if [ -z "${sources[${opt}]}" ]; then
+ err '' "program error: no source for option \"${opt}\""
+ fi
+ printf "%s: %s (%s)\n" "${opt}" "${options[${opt}]}" "${sources[${opt}]}"
+ done
+}
+
## make an entry to the queue log file, possibly an error
## (log queue changes only ; not interactive chatter)
## usage : log [ -e errcode ] msg [ msg ... ]
@@ -240,22 +518,42 @@ connect_test() {
## show queue maintenance functions
##
usage() { # <-- error msg
- dsp ''\
- 'usage : msmtp-queue functions' ''\
- ' msmtp-queue < op >'\
- ' ops : -r run (flush) mail queue - all mail in queue'\
- ' -R send selected individual mail(s) in queue'\
- ' -d display (list) queue contents (<-- default)'\
- ' -p purge individual mail(s) from queue'\
- ' -a purge all mail in queue'\
- ' -h this helpful blurt' ''\
- ' ( one op only ; any others ignored )' ''
- if [ -z "$1" ]; then
- exit 0;
- else
- dsp "$@" '';
- exit 1;
- fi
+ dsp ''\
+ 'usage : msmtp-queue functions' ''\
+ ' msmtp-queue { verbose } { config } < op >'\
+ ' verbose : '\
+ ' -v be more verbose (can be repeated)' ''\
+ ' config : at most one each of system and user'\
+ ' --no-config'\
+ ' do not process a user config file'\
+ ' --config-file PATH'\
+ ' use PATH as the user config file'\
+ ' --no-sys-config'\
+ ' do not process a system config file'\
+ ' --sys-config-file PATH'\
+ ' use PATH as the system config file' ''\
+ ' ops : -r run (flush) mail queue - all mail in queue'\
+ ' -R send selected individual mail(s) in queue'\
+ ' -d display (list) queue contents (<-- default)'\
+ ' -p purge individual mail(s) from queue'\
+ ' -a purge all mail in queue'\
+ ' -h this helpful blurt' ''\
+ ' ( one op only ; any others ignored )' ''
+
+ if [ $((verbose)) -gt 0 ]; then
+ dsp ''\
+ 'unless the \"--no-config\" option is specified,'\
+ 'a system configuration file is looked for, and'\
+ 'processed if found, in /etc/msmtp.config and in'\
+ '/etc/msmtp/msmtp.config (only the first found is processed)'
+ fi
+
+ if [ -z "$1" ]; then
+ exit 0;
+ else
+ dsp "$@" '';
+ exit 1;
+ fi
}
## get user [y/n] acknowledgement
@@ -287,7 +585,7 @@ send_queued_mail() { # <-- mail id
if [ -f "${FQP}.msmtp" ] ; then # corresponding .msmtp file found
[ "$EMAIL_CONN_TEST" != 'x' ] && \
- [ -z "$EMAIL_CONN_NOTEST" ] && { # do connection test
+ { # do connection test
connect_test || {
log "mail [ $2 ] [ $1 ] from queue ; couldn't be sent - host not connected"
return
@@ -507,7 +805,7 @@ enqueue_mail() { # <-- all mail args ; mail text via TMP
##
send_mail() { # <-- all mail args ; mail text via TMP
[ "$EMAIL_CONN_TEST" != 'x' ] && \
- [ -z "$EMAIL_CONN_NOTEST" ] && { # do connection test
+ { # do connection test
connect_test || {
log "mail for [ $* ] : couldn't be sent - host not connected"
enqueue_mail "$@" # enqueue the mail
@@ -530,7 +828,123 @@ send_mail() { # <-- all mail args ; mail text via TMP
#
[ -v LOG_LATER_ARGS ] && log "${LOG_LATER_ARGS[@]}"
-if [ ! "$1" = '--q-mgmt' ] ; then # msmtpq - sendmail mode
+
+# verbosity level -- must come first!
+while [ "$1" = "-v" ]; do
+ verbose=$((verbose+1))
+ shift
+done
+
+if [[ ${verbose} -gt 3 ]]; then
+ set -x
+fi
+
+# now, in what mode are we running?
+if [ "$1" = '--q-mgmt' ] ; then # msmtpq - queue enquiry mode
+ qmgmt=true
+ shift # consume this argument
+else
+ qmgmt=""
+fi
+
+# find any configuration files, then compose running configuration
+# (from program defaults, system config, per-user config
+
+# don't assign a value here. we look for it later, as it might be
+# assigned to be unassigned, and we want to notice that
+declare configfile
+declare sysconfig="${SYS_CONFIG_FILE}"
+
+# do any processing needed on defaults
+setdefaults
+
+# set defaults into options
+eatdefaults
+# then, environment variables into options
+eatenvirons
+
+# now, try to find the appropriate config files (system and user)
+
+while [ -n "$1" ] && [ "${config_options[$1]}" ]; do
+ if [ "$1" = "--no-config" ]; then # don't process configuration file(s)
+ if [ -n "${useroptions}" ]; then
+ err 'can only specify one of "--no-config" and "--config-file"'
+ fi
+ useroptions=$((useroptions++))
+ nouserconfig=1
+ shift
+ elif [ "$1" = "--config-file" ]; then # set
+ if [ -n "${useroptions}" ]; then
+ err 'can only specify one of "--no-config" and "--config-file"'
+ fi
+ useroptions=$((useroptions++))
+ userconfig="$2"
+ if [ -d "${userconfig}" ]; then
+ err "--config-file: \"${userconfig}\" is a directory"
+ fi
+ if [ ! -r "${userconfig}" ]; then
+ err "--config-file: \"${userconfig}\" does not exist"
+ fi
+ shift 2
+ elif [ "$1" = "--no-sys-config" ]; then
+ if [ -n "${sysoptions}" ]; then
+ err 'can only specify one of "--no-sys-config" and "--sys-config-file"'
+ fi
+ sysoptions=$((sysoptions++))
+ nosysconfig=1
+ shift
+ elif [ "$1" = "--sys-config-file" ]; then # set
+ if [ -n "${sysoptions}" ]; then
+ err 'can only specify one of "--no-sys-config" and "--sys-config-file"'
+ fi
+ sysoptions=$((sysoptions++))
+ sysconfig="$2"
+ if [ -d "${sysconfig}" ]; then
+ err "--sys-config-file: \"${sysconfig}\" is a directory"
+ fi
+ if [ ! -r "${sysconfig}" ]; then
+ err "--config-file: \"${sysconfig}\" does not exist"
+ fi
+ shift 2
+ fi
+done
+
+if [ -z "${nosysconfig}" ]; then
+ # maybe *not* `-f ${sysconfig}`, if it wasn't specified on the
+ # command line (and does not, in fact, exit): as opposed to the
+ # user configuration file, we look only in SYS_CONFIG_FILE
+ if [ ! -d "${sysconfig}" ] && [ -r "${sysconfig}" ]; then
+ eatconfig "${sysconfig}"
+ fi
+fi
+
+if [ -z "${nouserconfig}" ]; then
+ # if --config-file *not* given on the command line, try to
+ # find it one of the typical places
+ if [ ! -v "userconfig" ]; then
+ # where we should find XDG-compliant file
+ declare xch="${XDG_CONFIG_HOME:-${HOME}/.config}/${USER_CONFIG_FILE_XDG_SUBDIRECTORY}"
+ for configfile in "${xch}/${USER_CONFIG_FILE_NAME}" "${USER_CONFIG_FILE_NON_XDG}"; do
+ if [ ! -d "${configfile}" ] && [ -r "${configfile}" ]; then
+ break; # found one
+ fi
+ done
+ fi
+ # now, like with sys config file, if we *don't* find it, that will
+ # be because it was *not* specified on the command line, and
+ # doesn't exist in the default locations: no problem.
+ if [ ! -d "${userconfig}" ] && [ -r "${userconfig}" ]; then
+ eatconfig "${userconfig}"
+ fi
+fi
+
+setoptions
+
+if [[ ${verbose} -gt 2 ]]; then
+ dumptions
+fi
+
+if [ ! "${qmgmt}" ]; then # msmtpq - sendmail mode
lock_queue # lock here
make_id # make base queue filename id for this mail
# write mail body text to queue .mail file
@@ -543,7 +957,6 @@ if [ ! "$1" = '--q-mgmt' ] ; then # msmtpq - sendmail mode
send_mail "$@" # send the mail if possible, queue it if not
lock_queue -u # unlock here
else # msmtp-queue - queue management mode
- shift # trim off first (--q-mgmt) arg
OP=${1:1} # trim off first char of OP arg
case "$OP" in # sort ops ; run according to spec
r) lock_queue
@@ -554,6 +967,7 @@ else # msmtp-queue - queue management mode
p) select_mail purge ;; # purge individual mail(s) from queue
a) purge_queue ;; # purge all mail in queue
h) usage ;; # show help
+ # otherwise
*) usage "[ -$OP ] is an unknown msmtp-queue option" ;;
esac
fi
diff --git a/scripts/msmtpq/tests/configs/bad-email-conn-test b/scripts/msmtpq/tests/configs/bad-email-conn-test
new file mode 100644
index 0000000..67e7377
--- /dev/null
+++ b/scripts/msmtpq/tests/configs/bad-email-conn-test
@@ -0,0 +1 @@
+EMAIL_CONN_TEST 8
\ No newline at end of file
diff --git a/scripts/msmtpq/tests/configs/bad-email-queue-quiet b/scripts/msmtpq/tests/configs/bad-email-queue-quiet
new file mode 100644
index 0000000..a31f458
--- /dev/null
+++ b/scripts/msmtpq/tests/configs/bad-email-queue-quiet
@@ -0,0 +1 @@
+EMAIL_QUEUE_QUIET n
\ No newline at end of file
diff --git a/scripts/msmtpq/tests/configs/bad.conf b/scripts/msmtpq/tests/configs/bad.conf
new file mode 100644
index 0000000..b7d1dd5
--- /dev/null
+++ b/scripts/msmtpq/tests/configs/bad.conf
@@ -0,0 +1 @@
+BADOPT bad bad bad
\ No newline at end of file
diff --git a/scripts/msmtpq/tests/configs/comments-and-blanks b/scripts/msmtpq/tests/configs/comments-and-blanks
new file mode 100644
index 0000000..e9f96ac
--- /dev/null
+++ b/scripts/msmtpq/tests/configs/comments-and-blanks
@@ -0,0 +1,13 @@
+# this is a comment
+
+# to keep from getting different values depending on where we are
+# printed out by dumptions(), specify these two
+
+MSMTPQ_Q ~/.local/share/msmtp/msmtp.queue
+MSMTPQ_LOG ~/.local/share/msmtp/msmtp.queue.log
+
+# after some blank lines
+
+ # here is another comment
+
+MSMTP echo
diff --git a/scripts/msmtpq/tests/configs/good.conf b/scripts/msmtpq/tests/configs/good.conf
new file mode 100644
index 0000000..95dd3fc
--- /dev/null
+++ b/scripts/msmtpq/tests/configs/good.conf
@@ -0,0 +1,2 @@
+MSMTPQ_Q ./tests/queue
+MSMTPQ_LOG ./tests/log
diff --git a/scripts/msmtpq/tests/configs/no-newline b/scripts/msmtpq/tests/configs/no-newline
new file mode 100644
index 0000000..e4f5904
--- /dev/null
+++ b/scripts/msmtpq/tests/configs/no-newline
@@ -0,0 +1,7 @@
+# to keep from getting different values depending on where we are
+# printed out by dumptions(), specify these two
+
+MSMTPQ_Q ~/.local/share/msmtp/msmtp.queue
+MSMTPQ_LOG ~/.local/share/msmtp/msmtp.queue.log
+
+MSMTP echo
\ No newline at end of file
diff --git a/scripts/msmtpq/tests/cram/tests.t b/scripts/msmtpq/tests/cram/tests.t
new file mode 100644
index 0000000..6017818
--- /dev/null
+++ b/scripts/msmtpq/tests/cram/tests.t
@@ -0,0 +1,155 @@
+these are (intended to be) optional tests that can be run if cram is
+installed. `cram -i ...` is how to update with new tests or new
+results. see
+
+https://bitheap.org/cram/
+
+
+option processing:
+- command-line specified config file is a directory, or does not exist
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --sys-config-file ./tests 2>&1)
+
+ --sys-config-file: "./tests" is a directory
+
+ [1]
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --sys-config-file ./tests/DOESNOTEXIST 2>&1)
+
+ --config-file: "./tests/DOESNOTEXIST" does not exist
+
+ [1]
+
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --config-file ./tests 2>&1)
+
+ --config-file: "./tests" is a directory
+
+ [1]
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --config-file ./tests/DOESNOTEXIST 2>&1)
+
+ --config-file: "./tests/DOESNOTEXIST" does not exist
+
+ [1]
+
+- comments, blank lines, in the config file
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq -v -v -v --q-mgmt --config-file ./tests/configs/comments-and-blanks 2>&1)
+ var MSMTPQ_Q: ~/.local/share/msmtp/msmtp.queue
+ file "./tests/configs/comments-and-blanks" overrides "MSMTPQ_Q" set in "defaults"
+ var MSMTPQ_LOG: ~/.local/share/msmtp/msmtp.queue.log
+ file "./tests/configs/comments-and-blanks" overrides "MSMTPQ_LOG" set in "defaults"
+ var MSMTP: echo
+ file "./tests/configs/comments-and-blanks" overrides "MSMTP" set in "defaults"
+ EMAIL_CONN_TEST: n (defaults)
+ MSMTPQ_LOG: /home/minshall/.local/share/msmtp/msmtp.queue.log (./tests/configs/comments-and-blanks)
+ EMAIL_QUEUE_QUIET: (defaults)
+ MSMTPQ_Q: /home/minshall/.local/share/msmtp/msmtp.queue (./tests/configs/comments-and-blanks)
+ MSMTP: echo (./tests/configs/comments-and-blanks)
+
+
+- config file with no terminating newline
+
+first, make sure the newline is still missing (i.e., not deleted during development/distribution of the package)
+ $ NONEWLINE=./tests/configs/no-newline
+ $ (cd ${TESTDIR}/../.. && tail -1 "${NONEWLINE}" | tr -c -d '\n'| tr '\n' 'n' | (! grep -q n) || (echo "ERROR: a trailing newline suddenly appeared in "${NONEWLINE}"" && ./tests/scripts/exiter 1) 2>&1)
+
+now, test the terminating newline-less file
+ $ (cd ${TESTDIR}/../.. && ./msmtpq -v -v -v --q-mgmt --config-file "${NONEWLINE}" 2>&1)
+ var MSMTPQ_Q: ~/.local/share/msmtp/msmtp.queue
+ file "./tests/configs/no-newline" overrides "MSMTPQ_Q" set in "defaults"
+ var MSMTPQ_LOG: ~/.local/share/msmtp/msmtp.queue.log
+ file "./tests/configs/no-newline" overrides "MSMTPQ_LOG" set in "defaults"
+ var MSMTP: echo
+ file "./tests/configs/no-newline" overrides "MSMTP" set in "defaults"
+ EMAIL_CONN_TEST: n (defaults)
+ MSMTPQ_LOG: /home/minshall/.local/share/msmtp/msmtp.queue.log (./tests/configs/no-newline)
+ EMAIL_QUEUE_QUIET: (defaults)
+ MSMTPQ_Q: /home/minshall/.local/share/msmtp/msmtp.queue (./tests/configs/no-newline)
+ MSMTP: echo (./tests/configs/no-newline)
+
+
+
+- specify both --no-config and --config-file on command line (that's illegal)
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --no-sys-config --sys-config-file ./tests/DOESNOTEXIST 2>&1)
+
+ can only specify one of "--no-sys-config" and "--sys-config-file"
+
+ [1]
+
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --no-config --config-file ./tests/DOESNOTEXIST 2>&1)
+
+ can only specify one of "--no-config" and "--config-file"
+
+ [1]
+
+- good options in config file
+
+- invalid options in config file
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --config-file ./tests/data/bad.conf 2>&1)
+
+ --config-file: "./tests/data/bad.conf" does not exist
+
+ [1]
+
+- can't create queue directory
+- can't create directory to hold log
+
+- good values for EMAIL_CONN_TEST
+
+ $ (cd ${TESTDIR}/../.. && for val in n p P s x; do ./msmtpq --q-mgmt --config-file <(echo EMAIL_CONN_TEST ${val}); done > /dev/null)
+
+- invalid value for EMAIL_CONN_TEST
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --config-file ./tests/configs/bad-email-conn-test 2>&1)
+
+
+ msmtpq: invalid EMAIL_CONN_TEST option "8" (from "./tests/configs/bad-email-conn-test"); must be one of: n p P s x
+
+ [1]
+
+- good value for EMAIL_QUEUE_QUIET
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --config-file <(echo EMAIL_QUEUE_QUIET y) > /dev/null)
+
+- invalid value for EMAIL_QUEUE_QUIET
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --config-file ./tests/configs/bad-email-queue-quiet 2>&1)
+
+
+ msmtpq: illegal EMAIL_QUEUE_QUIET option "n" (set in "./tests/configs/bad-email-queue-quiet") should be unset or set to 'y'
+
+ [1]
+
+- specify both --no-config and --config-file on command line
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --no-config --config-file ./tests/data/good.conf 2>&1)
+
+ can only specify one of "--no-config" and "--config-file"
+
+ [1]
+
+- specify both --no-sys-config and --sys-config-file on command line
+
+ $ (cd ${TESTDIR}/../.. && ./msmtpq --q-mgmt --no-sys-config --sys-config-file ./tests/data/good.conf 2>&1)
+
+ can only specify one of "--no-sys-config" and "--sys-config-file"
+
+ [1]
+
+- XXX test algorithm for finding user config file (with, without XDG...)
+
+- two environment variables setting the same variable
+
+ $ (cd ${TESTDIR}/../.. && (export MSMTPQ_Q=this && export Q=that && ./msmtpq --q-mgmt) 2>&1)
+
+ $ (cd ${TESTDIR}/../.. && (export MSMTPQ_LOG=this && export LOG=that && ./msmtpq --q-mgmt) 2>&1)
+
+running:
+- XXX can't write to log file
+
+- XXX lots else!
diff --git a/scripts/msmtpq/tests/scripts/exiter b/scripts/msmtpq/tests/scripts/exiter
new file mode 100755
index 0000000..6584bf8
--- /dev/null
+++ b/scripts/msmtpq/tests/scripts/exiter
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+# for testing, sometimes, need to exit with a given exit status
+
+exit $1
it has these "features"
- both a system config file and a per-user config file are available
- command line options to disable either or both
- command line options to use different config files
- for developers, if the
cram
test utility (it's pretty nice) is available, there are some tests (currently just of the bits i modified) - it drops support for
EMAIL_CONN_NOTEST
(not exactly a feature) - errors (from
err()
) go to stderr - some of the error reporting is pretty good
- it does try to use
XDG_CONFIG_HOME
andXDG_DATA_HOME
but, it's > 1000 lines (including the tests, but they're not that much, unfortunately).
fwiw, i wouldn't mind owning msmtpq for a bit. there are several things i'd like to do (under supervision/consultation, of course), plus maybe some cleanup, including of my code here.
The msmtpq script does not have a maintainer currently, so go ahead :)