On Thu, Mar 21, 2013 at 09:25:49AM +0000, James Griffin wrote:
> [--------- Wed 20.Mar'13 at 18:08:49 -0500  David Champion :---------]
> 
> > * On 20 Mar 2013, Chris Green wrote: 
> > > 
> > > Has the mutt handling of this changed in the last few versions?
> > 
> > Not that I know of, and I doubt it.
> > 
> > > The python way of doing this is correct according to the RFC as far as I
> > > can tell, there *should* be a blank line between the end of a message
> > > and the 'From ' starting the next message.  Thus the python library I'm
> > > using does add this blank line.
> > > 
> > > However mutt detects this as an error and outputs the messsage about the
> > > mailbox being modified.  
> > 
> > There absolutely should be a blank line.  I think though that the order
> > is wrong: mutt expects that a message (i.e. a From_ line) appears at
> > the old EOF marker, and that the EOF marker is on/after a blank line.
> > I think that if you adjust your filter to write the message and then a
> > blank line instead of a blank line and then a message, the error will go
> > away.
> > 
> > -- 
> > David Champion • d...@bikeshed.us
> 
> Chris, do you have a site where this python script is viewable? just so
> we can see how it works. It might help clarify the issue and of course
> it's interesting to see how people have come with ideas and used
> languages/scripts to handle mail.
> 
It uses the standard Python libraries, I have attached the code (there's
nothing sensitive in there).

The relevant bit that appends the message to the mbox is:-


#
#
# Deliver a message to a local mbox
#
def deliverMboxMsg(dest, msg, log):
    #
    #
    # Create the destination mbox instance
    #
    mbx = mailbox.mbox(dest, factory=None)

    log.info("From: " + msg.get("From", "unknown"))
    log.info("Destination is: " + dest)
    #
    #
    # Lock the mbox while we append to it
    #

    for tries in xrange(3):
        try:
            mbx.lock()
            #
            #
            # Append the incoming message to the appropriate mbox
            #
            mbx.add(msg)
            #
            #
            # now set the modified time later than the access time (which is 
'now') so
            # that mutt will see new mail in the mbox.
            #
            os.utime(dest, ((time.time()), (time.time() + 5)))
            mbx.unlock()
            break

        except mailbox.ExternalClashError:
            log.info("Destination locked, try " + str(tries))
            time.sleep(1)

    else: # get here if we ran out of tries
        log.warn("Failed to lock destination after 3 attempts, giving up")



As you can see I'm using the 'off the shelf' standard Python library
class mailbox.mbox to deliver the message.  If this is doing it wrong
then I'm very surprised.

-- 
Chris Green
#!/usr/bin/python
#
#
# Mail filtering script, new version October 2010
#
import email
import mailbox
import os
import sys
import time
import mailLib
#
#
# Redirect any exceptions to a file
#
sys.stderr = open("/home/chris/tmp/mail.err", 'a')
#
#
# Some constants (i.e. configuration)
#
home = "/home/chris"
logfile = home + "/tmp/mail.log"
filtfile = home + "/.mutt/filter"
mldir = home + "/Mail/"
indir = mldir + "In/"
judir = mldir + "Ju/"
#
#
# Set to log to mail.log in ~/tmp with name 'filter' and the envelope/from
#
log = mailLib.initLog("filter")
#
#
# Initialise destination mailbox name to empty
#
dest = ""
#
#
# Read the message from standard input and make a message object from it
#
# msg = email.message_from_string(sys.stdin.read())
msg = mailbox.mboxMessage(sys.stdin.read())
#
#
# Extract the To:, Cc: and Subject: headers and the envelope/from
#
msgcc = msg.get("Cc", "unknown").lower()
msgto = msg.get("To", "unknown").lower()
msgsb = msg.get("Subject", "unknown")
msgfm = msg.get("From", "unknown").lower()
#
#
# See if it's in our filter file
#
f = open(filtfile, 'r')
for ln in f:                    # for each line in filter
    if ln[0] == '#':            # ignore comments
        continue
    #
    #
    # split the line into fields
    #
    fld = ln.split()
    nm = fld[0]             # name/alias
    #
    #
    # deal with possible flags after the destination directory
    #
    if (':' in fld[1]):
        xx = fld[1].split(':')
        dd = xx[0] + "/"
        fg = xx[1]
    else:
        dd = fld[1] + "/"       # destination directory
        fg = ""
    tocc = fld[2].lower()   # list address
    #
    #
    # see if the filter To/CC column matches the message To: or Cc:
    # for DBA lists (flag d) we also look in the From: address
    #
    if (tocc in msgcc or tocc in msgto or ((fg == "d") and (tocc in msgfm))):
        if (fg == "d"):
            msg.__setitem__("Cc", msgfm)    # so mutt will recognise it as a list
        dest = mldir + dd + nm
        #
        #
        # Strip out list name from subject if it's there
        #
        if len(fld) > 3:
            # msgText = msgText.replace('[' + fld[3] + ']', '', 1)
            msg.replace_header("Subject", msgsb.replace('[' + fld[3] + ']', ''))
        #
        #
        # Handle the subject filtering if present
        #
        if len(fld) > 4:
            if not fld[4] in msgsb.lower():
                continue        # not a match if the string isn't found
        break                   # assume message can't be on multiple lists
#
#
# if destination mb name hasn't been set yet then set to the default 
# junk (mail with 'chris' in destination will get to 'inbox')
#
if dest == "":
    dest = judir + "junk"

mailLib.deliverMboxMsg(dest, msg, log)
import mailbox
import logging
import logging.handlers
import os
import time
#
#
# log a message
#
def initLog(name):
    log = logging.getLogger(name)
    log.setLevel(logging.DEBUG)
    f = logging.handlers.RotatingFileHandler("/home/chris/tmp/mail.log", 'a', 1000000, 4)
    f.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    f.setFormatter(formatter)
    log.addHandler(f)
    return log
#
#
# Deliver a message to a local mbox
#
def deliverMboxMsg(dest, msg, log):
    #
    #
    # Create the destination mbox instance
    #
    mbx = mailbox.mbox(dest, factory=None)

    log.info("From: " + msg.get("From", "unknown"))
    log.info("Destination is: " + dest)
    #
    #
    # Lock the mbox while we append to it
    #
    for tries in xrange(3):
        try:
            mbx.lock()
            #
            #
            # Append the incoming message to the appropriate mbox
            #
            mbx.add(msg)
            #
            #
            # now set the modified time later than the access time (which is 'now') so
            # that mutt will see new mail in the mbox.
            #
            os.utime(dest, ((time.time()), (time.time() + 5)))
            mbx.unlock()
            break

        except mailbox.ExternalClashError:
            log.info("Destination locked, try " + str(tries))
            time.sleep(1)

    else: # get here if we ran out of tries
        log.warn("Failed to lock destination after 3 attempts, giving up")

Reply via email to