snatchmail source

#!/bin/tcsh -f
#	snatchmail 0.4.1	(27-Jan-2011)
#	Calls `fetchmail` to get mail from POP3 servers, clean up messages
#	downloaded more than a (configurable) number of days ago.
#	Assumes you are not running fetchmail as a daemon.  And that you are
#	only polling POP3 servers (at least in the default config file we use).
#	Copyright (C) MMX, MMXI  Dario Niedermann <>
#	This program is free software: you can redistribute it and/or modify
#	it under the terms of  the GNU General Public License  version 3  as
#	published by the Free Software Foundation at:
#		<>
#	This program is distributed  in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY;  without even the implied warranty of
#	GNU General Public License for more details.

#---------------------- Edit next 3 lines to taste:
set daysToWait = 7	# days before flushing downloaded messages
set fmailOpts =	"--bad-header accept"
set smVerbose =	0	# 0 = quiet, 1 = verbose, >1 = debug

#---------------------- Nothing interesting below, for most end users 
if ( ! $smVerbose ) set fmailOpts = "-s $fmailOpts"
if ( $smVerbose > 1 ) then
	echo $0:t": fetchmail will be called" \
		"with these options: '$fmailOpts'" >/dev/stderr
set alreadyRunning =	8	# err code for "another fetchmail is running"

# Check if we have a suitable md5 hasher installed:
set md5prog = ''
set suitableMD5progs = ( openssl,dgst,-md5 md5sum )
foreach prg ( $suitableMD5progs )
	set whichMd5 = `which $prg:s/,/./:r`
	if ( $? == 0 ) then
		set md5prog = $whichMd5:h/$prg:gas/,/ /
		if ( $smVerbose > 1 ) echo $0:t": '$md5prog'" \
			'will be used for hashing' >/dev/stderr
if ( $md5prog:q == '' ) then
	echo $0:t': no suitable md5 hasher found:'\
		'Please install "openssl" or "md5sum"'\
	exit 72

if ( $?FETCHMAILHOME ) then
	set base = $FETCHMAILHOME/
	set smHome = $FETCHMAILHOME
	set pidFile = $smHome/
	set base = ~/.
	set smHome = $HOME
	# 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
		set stateDir = /var/run	# Linux et al.
		set stateDir = /etc	# Other Unixes supported by fetchmail
	set pidFile = $stateDir/
endif	# TODO: compatibility with $HOME_ETC env var

set cfg = ${base}fetchmailrc	# standard fetchmail config file
set fDir = ${base}sm_fetchids.d	# here we keep a collection of old fetchid files
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"
	if ( -e $smHome/.fetchids ) \
		cp $smHome/.fetchids $fDir/.current.fetchids
set curFids = ${fDir}/.current.fetchids		# the current fetchids file

# The "fetching" fetchmail: just download new mail, if any.
# No flushing for now.
fetchmail -k --uidl $fmailOpts -i $curFids
set fmResult = $?

# if fetchmail returned error, exit with fm's return code:
if ( $fmResult > 1 ) exit $fmResult
# Else...  Is another copy of this script running already?
if ( -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:
set previousFids = `ls -1ct $fDir |head -n1`

alias	RestoreConfigFile "mv $cfg.away $cfg"
if ( $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.
alias 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:
set newFetchidsHash =	`$md5prog $curFids |md5prune`
# Then get previous fetchids file hash:
set prevFetchidsHash =	`$md5prog $fDir/$previousFids |md5prune`
# If the hashes say that the contents are different, copy the
# "current fetchids" file into our collection:
if ( $newFetchidsHash != $prevFetchidsHash ) then
	cp $curFids $fDir/`date +%Y%m%d%H%M%S`.fetchids

# Now... do we have in our collection some fetchIDs files
# that have been there untouched for more than X days?
@ minutes = 1440 * $daysToWait
set idsToClean = \
 `find $fDir -type f -name '[0-9]*.fetchids' -mmin +$minutes`

if ( $#idsToClean == 0 ) then		# but if there are none,
	if ( $smVerbose ) echo $0:t': No ripe old messages to flush, exiting.'
	RestoreConfigFile		# goodbye
	exit $fmResult

if ( $smVerbose ) then
	echo $0:t': ----- FLUSHING PHASE -----'
	echo $0:t": 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

# is another fetchmail instance running?
while ( -e $pidFile || -e ~/ )
	sleep 3				# wait... (it'll error out OR use a
					# different config file anyway, since 
end					# 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'
set fmResult = $?

#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
set verboseRm = ''; if ( $smVerbose > 1 ) set verboseRm = 'v'
if ( $fmResult < 2 ) rm -f$verboseRm $idsToClean
exit $fmResult
“Snatchmail” is Copyright © Dario Niedermann
released with NO WARRANTY under the terms of the GPLv3 license.

Home Valid HTML 4.01 Strict  Valid CSS!