snatchmail.m4 source


#V	snatchmail 0.8  --  Copyright (C) 2011-2018 Dario Niedermann

dnl	Calls `fetchmail` to get mail from POP3 servers,  clean up messages
dnl	downloaded more than a (configurable) number of days ago.
dnl	Assumes you are not running fetchmail as a daemon. And that you are
dnl	only polling POP3 servers (at least in the given config file).

#	$Id$
dnl     ________________________________________________________________________

#----------------------------------------------------- User-configurable section
daysToWait=7			# days before flushing downloaded messages
fmailOpts='--bad-header accept'
verbosity=0			# 0 = quiet, 1 = verbose, >1 = debug
unset cfg
#---------------------------------------------- End of User-configurable section


if [ -w /dev/stderr ]; then
		echo $myName: $@  >/dev/stderr
		echo $myName: $@

** -------------------------------------------------------- Command line parsing
	sed -n '/^#V/s/^#V	//p'  "$0"

	echoerr try \`$myName --help\' for usage information.
	exit 1

TEMP=`getopt -o cf:hLVvr:                               \
	-l check-net,fetchmailrc:,help,license,licence  \
	-l version,verbose,retention:                   \
	-n "$myName" -- "$@"` || usageAndExit

eval set -- "$TEMP"
while true ; do
    case "$1" in
		daysToWait="$2"; shift
		cfg="$2"; shift
		checkNetFirst=1 ;;
		verbosity=$((++verbosity)) ;;
		showVersion; exit 0 ;;
		showVersion; echo
		sed -n '/^#%L$/,/^#/p'  "$0" | sed '$d;1d'  \
			| gunzip -c 2>&-}},{{   ;
		exit 0 ;;
		sed -n '/^#%H$/,$p'     "$0" | sed 1d  \ifelse(OPTIMIZE,1,{{
			| gunzip -c  \}})
			| sed "s,%PRG%,$myName,"
		exit 0 ;;
		shift; break ;;
	*)                              **  should never happen:
		echoerr "\'$opt\' -- option unimplemented, ignoring..."

** -------------------------------------------------------------------------/CLI

if [ "$checkNetFirst" -gt 0 ]; then	** if no connection, abort without error
	ping -c1 'PINGSRV' >/dev/null 2>&1 \
		|| exit 0
** else...
[ $verbosity -eq 0 ] && fmailOpts="-s $fmailOpts"       ** silence fetchmail
[ $verbosity -gt 1 ] \
	&& echoerr fetchmail will be called with these options: \"$fmailOpts\"
alreadyRunning=8		** err code for "another fetchmail is running"

** ---------------------------- Check if we have a suitable md5 hasher installed
suitableMD5progs="md5 md5sum openssl"
unset md5prog
for prg in $suitableMD5progs ; do
	whichMd5=`which "$prg"`
	if [ $? -eq 0 ]; then
		[ "$prg" = openssl ] \
			&& md5prog="$md5prog dgst -md5"
		[ "$verbosity" -gt 1 ] \
			&& echoerr \"$md5prog\" will be used for hashing
if [ -z "$md5prog" ]; then
	echoerr 'no suitable md5 hasher found:'\
		'Please install "openssl" or "md5sum"'
	exit 72

if [ -n "$FETCHMAILHOME" ]; then
	** Where's the state directory on this system? We'll need it
	** to check if another fetchmail instance is running:
	if [ -e /var/run ]; then
		stateDir=/var/run	** Linux et al.
		stateDir=/etc		** Other Unixes supported by fetchmail
fi	** TODO:compatibility w/ $HOME_ETC envvar

[ -z "$cfg" ] && cfg="${base}fetchmailrc"	** standard fetchmail configfile

fDir="${base}sm_fetchids.d"	** here we keep collection of old fetchid files
curFids="$fDir/.current.fetchids"		** the current fetchids file

if [ ! -e "$fDir" ]; then	** if the collection dir doesn't exist
	mkdir -m700 "$fDir"	** create it
	** since we also had no ".current.fetchids" file
	** (probably snatchmail's 1st run) copy and use the
	** standard .fetchids file (if any) as our ".current.fetchids"
	[ -e "$smHome/.fetchids" ] \
		&& cp "$smHome/.fetchids"  "$curFids"

** The "fetching" fetchmail: just download new mail, if any.
** No flushing for now.
fetchmail -f "$cfg" -k --uidl $fmailOpts -i $curFids

** if fetchmail returned error, exit with fm's return code:
[ $fmResult -gt 1 ] && exit $fmResult

** Else...  Is another copy of this script running already?
[ -e "${cfg}.away" ] && exit $alreadyRunning	** Harakiri

** Else... should another {sna,fe}tchmail instance be launched
** *while* we're working, it won't be able to interfere:
mv "$cfg" "${cfg}.away"	** 'cause it won't find the default config file we use

** Get name of latest fetchids file we had copied off to our collection:
previousFids=`ls -1ct "$fDir" |head -n1`

	mv "${cfg}.away"  "$cfg"

if [ -z $previousFids ]; then		# if there's none, copy current
	cp "$curFids" "$fDir/00000000000000.fetchids"	# fetchids file there
	exit $fmResult					# and quit
** Else...
** We'll now check if the current fetchids file is different from
** the last one we archived, by checking the respective hashes.
md5prune="tr '[:blank:]' '\n' | sed -n -e '/^[0-9a-f]\{32,32\}$/p'"
** md5prune will heuristically return only the part of the hasher's output
** that looks like an md5 hash, discarding file names and other strings

** get current fetchids file hash:
newFetchidsHash=`$md5prog $curFids | eval $md5prune`
** Then get previous fetchids file hash:
prevFetchidsHash=`$md5prog $fDir/$previousFids | eval $md5prune`
** If the hashes say that the contents are different, copy the
** "current fetchids" file into our collection:
[ "$newFetchidsHash" != "$prevFetchidsHash" ] \
	&& cp "$curFids" "$fDir/`date +%Y%m%d%H%M%S`.fetchids"

** Now... do we have in our collection any fetchIDs files
** that sat there untouched for more than X days?
idsToClean=`find $fDir -type f -name '[0-9]*.fetchids' -mtime +$daysToWait`

if [ -z "$idsToClean" ]; then		# but if there are none,
	[ $verbosity -gt 0 ] && echoerr No ripe old messages to flush, exiting.
	RestoreConfigFile		# goodbye
	exit $fmResult

if [ $verbosity -gt 0 ]; then
	echoerr '----- FLUSHING PHASE -----'
	echoerr cleaning up messages as per $idsToClean

** Now flush those old messages, using the old fetchids & our usual
** (albeit renamed) config file. Since we don't want to fetch any mail at this
** time, and unfortunately fetchmail lacks a 'just flush, don't fetch' mode,
** we use `cat >/dev/null` as a fake MDA.  Yes, it is a bit of a waste.
** Anyhow, if any mail is fetched (& thus devnulled) the deed won't leave any
** trace: fetchmail will keep note of those fetched UIDLs in a temp file that
** will be deleted.
** So the new mail will be fetched again & delivered properly next time.

** Sum all the $idsToClean files into a single file, eliminating repeated lines
** and use that to flush all msgs older than X days. Note that, if new mail has
** arrived on the server during the execution of this script, its (devnulled)
** retrieval won't alter the original $idsToClean files. We must prevent that,
** because - if fetchmail errors out *after* having retrieved some mail, we'd be
** left with a modified (and not deleted) $idsToClean file: its modification
** date would then # become recent, and snatchmail would have to wait X more
** days to clean up the stale UIDLs it contains:
sort -u -o /tmp/.msgs_to_flush.fetchids $idsToClean
chmod 600  /tmp/.msgs_to_flush.fetchids		** newer fetchmail wants this

** is another fetchmail instance running?
while [ -e $pidFile  -o  -e ~/ ]; do
	sleep 3				** wait... (it'll error out OR use a
					** different config file anyway, since
done					** we moved the default conf.file away)

** The "flushing" fetchmail:
fetchmail -F -f "${cfg}.away" -i /tmp/.msgs_to_flush.fetchids $fmailOpts \
	--mda 'cat >/dev/null'

** rm $idsToClean.tmp # always delete the temp $idsToClean copy, errors or not

** If no errors from the flushing fetchmail, also delete the used fetchids file
verboseRm=''; [ $verbosity -gt 1 ] && verboseRm='v'
[ $fmResult -lt 2 ] && rm -f$verboseRm $idsToClean
exit $fmResult
“Snatchmail” is Copyright © Dario Niedermann — Released with no warranty under the terms of the GPLv3 license. Written and tested on Linux using GNU tools.
HomeNo Javascript iconValid HTML 4.01 StrictValid CSS!