Re: Some parsing/generation issues of email in Python 3

Hans-Peter Jansen <[email protected]> Sun, 12 Jun 2016 23:30:42 +0200
Newsgroups gmane.comp.python.mime.devel
Message-ID <1606484.fv4zu21fkq@xrated>
This is a multi-part message in MIME format.

--nextPart2731470.3KRVOMmn7t
Content-Transfer-Encoding: 7Bit
Content-Type: text/plain; charset="us-ascii"

Hi Matt,

thanks for your hints.

On Sonntag, 12. Juni 2016 13:01:13 Matthew Dixon Cowles wrote:
> Pete,
> 
> > Note, that the subject line is missing. In my real filter, it left
> > the current execution frame, and execution continued one or two
> > level up the stack.
> 
> The only thing I've run into that's like that is when an exception is
> raised in a function that I didn't think would do that and was caught
> in a place that I didn't expect.

Yes, sure, that's a more common situation.

> That it happens with the logging module and a non-ASCII encoding but
> only when run in an environment with very few environment variables
> also suggests something like that to me.
> 
> I realize that that's not what your example code suggests, but if it
> were obvious it would already be fixed.

Yes, of course. 

BTW, I even tried to recreate the way, that postfix ought to run the filter 
with no positive result, either. There's something fishy going on here. 

Meanwhile, I've reduced the code to the bare minimum (attached). I also 
noticed, that the example mail is displayed in funny ways in my MUA, therefore 
attached again gzipped..

Thanks,
Pete
--nextPart2731470.3KRVOMmn7t
Content-Disposition: attachment; filename="umlaut-subject-2.mail.gz"
Content-Transfer-Encoding: base64
Content-Type: application/gzip; name="umlaut-subject-2.mail.gz"

H4sIAEnTXVcAA41QTU/CQBC991dMvOrSUhI/qrUGlCiJioIx8bZsB3ahzDa7U0B/vVs9oPHi7c3M
e5P33jNy40iMJesMLnS9vGpcXckOIV9GQ2fXGdxK8mKMjA5GASL94U3tf1iTZrZExRm8GoQS5YrN
xqBDsGUQ5cXLdChOi34xu39MVfpg357yvMjBKA1rNIQwqZ1Uum93cDeGVjPFCld2XUTXkjGDieQj
6HZh1BCkSfcYur0s7WXJGRwmaZJEA0uMxGLqgsE5OnFDypaGFhmc9A3v7+91+Ma44ziYN3QOSkvn
kfODxgvplTEHUaSZa5/F8Xa77fC3kU6JsTbVHOM5eg6hP4QJL11Agjdxy5pbajOTCIl8WFQNLZBi
/xVtZnd7JEz9axA/KiPRdiYqi15ppMLL9UCjWmGZs2sw+gR7F70u1gEAAA==

--nextPart2731470.3KRVOMmn7t
Content-Disposition: attachment; filename="mail_filter_test.py"
Content-Transfer-Encoding: 7Bit
Content-Type: text/x-python; charset="UTF-8"; name="mail_filter_test.py"

#!/usr/bin/env python3

import sys
import getopt
import logging
import subprocess

import email
import email.policy
import email.header
import email.generator

logfile = '/tmp/mail_filter_test.log'
logformat = '%(asctime)s [%(name)s] %(levelname)5s: %(message)s'
encoding = 'utf-8'

# setup logging
log = logging.getLogger('mail_filter_test')

EX_TEMPFAIL = 75        # queue and retry
SILLY_BEHAVIOR = 1

if SILLY_BEHAVIOR:
    logging.basicConfig(
        level = logging.DEBUG,
        format = logformat,
        filename = logfile,
    )
else:
    log.setLevel(logging.DEBUG)
    filelog = logging.FileHandler(logfile, encoding = encoding)
    filelog.setLevel(logging.DEBUG)
    filelog.setFormatter(logging.Formatter(logformat))
    log.addHandler(filelog)


def decode_header(value):
    if value is not None:
        value = str(email.header.make_header(email.header.decode_header(value)))
    return value


def mail_filter(sender, recipients):
    log.debug('parse message')
    msg = email.message_from_binary_file(sys.stdin.buffer)
    subject = decode_header(msg.get('subject'))
    log.debug('subject: %s', subject)
    sendmail = ['/usr/sbin/sendmail', '-G', '-i', '-f', sender, '--'] + recipients
    log.debug('call %s', sendmail)
    p = subprocess.Popen(sendmail, stdin = subprocess.PIPE)
    email.generator.BytesGenerator(p.stdin).flatten(msg)
    p.stdin.close()
    return p.wait()


if __name__ == '__main__':
    try:
        optlist, recipients = getopt.getopt(sys.argv[1:], 'f:', 'from=')
    except getopt.error as msg:
        print(msg, file = sys.stderr, flush = True)
        sys.exit(EX_TEMPFAIL)

    sender = None
    for opt, par in optlist:
        if opt in ('-f', '--from'):
            sender = par

    if not sender or not recipients:
        print('Usage: %s -f from-address to-addresses.. < mail' % sys.argv[0],
              file = sys.stderr, flush = True)
        sys.exit(EX_TEMPFAIL)

    sys.exit(mail_filter(sender, recipients))

--nextPart2731470.3KRVOMmn7t
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline