12.2.13 Esempi

Ecco alcuni esempi su come usare il package email per leggere, scrivere ed inviare semplici messaggi di posta elettronica, così come complessi messaggi MIME.

Iniziamo col vedere come creare e inviare un semplice messaggio di testo:

# importa smtplib per l'attuale funzione di invio
import smtplib

# importa i moduli email necessari
from email.MIMEText import MIMEText

#  Apre un file di testo per la lettura. Per questo esempio supponiamo 
#+ che il file di testo contenga solo caratteri ASCII.
fp = open(textfile, 'rb')
# Crea un messaggio in puro testo
msg = MIMEText(fp.read())
fp.close()

# me == indirizzo di chi invia l'email
# you == indirizzo di chi riceve l'email
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you

#  Invia il messaggio tramite il nostro server SMTP ma non include 
#+ l'intestazione della busta. 
s = smtplib.SMTP()
s.connect()
s.sendmail(me, [you], msg.as_string())
s.close()

Ecco un esempio di come inviare un messaggio MIME contenente un gruppo di foto di famiglia che possono essere recuperate in una directory:

# importa smtplib per l'attuale funzione di invio
import smtplib

# Ecco i moduli del package email di cui abbiamo bisogno
from email.MIMEImage import MIMEImage
from email.MIMEMultipart import MIMEMultipart

COMMASPACE = ', '

# Crea il messaggio email contenitore (outer).
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == indirizzo email di chi invia
# family = la lista di tutti gli indirizzi email destinatari
msg['From'] = me
msg['To'] = COMMASPACE.join(family)
msg.preamble = 'Our family reunion'
# Fa in modo che il messaggio termini con un fine riga
msg.epilogue = ''

# Si presume che i file di immagine siano tutti in formato PNG
for file in pngfiles:
    #  Apre i file in modo binario.  Lascia che la classe MIMEImage 
    #+ automaticamente individui il tipo di immagine specifico.
    fp = open(file, 'rb')
    img = MIMEImage(fp.read())
    fp.close()
    msg.attach(img)

# Invia l'email tramite il nostro server SMTP.
s = smtplib.SMTP()
s.connect()
s.sendmail(me, family, msg.as_string())
s.close()

Ecco un esempio di come inviare l'intero contenuto di una directory come messaggio email: 12.4

#!/usr/bin/env python

"""Invia il contenuto di una directory come messaggio MIME.

Uso: dirmail [opzioni] from to [to ...]*

Opzioni:
    -h / --help
        Stampa questo messaggio ed esce.

    -d directory
    --directory=directory
        Invia il contenuto della directory specificata altrimenti usa
        la directory corrente. Vengono inviati solo i file regolari 
        della directory e non vengono coinvolte le sotto directory.

`from' è l'indirizzo email di chi invia il messaggio.

`to' è l'indirizzo email del destinatario del messaggio. Possono essere 
passati più destinatari.

L'email viene inviata tramite inoltro al vostro server SMTP locale che 
quindi esegue il normale processo di consegna. La vostra macchina locale 
deve avere in esecuzione un server SMTP.
"""

import sys
import os
import getopt
import smtplib
# Per l'individuazione del tipo MIME in base a nome e estensione del file
import mimetypes

from email import Encoders
from email.Message import Message
from email.MIMEAudio import MIMEAudio
from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
from email.MIMEText import MIMEText

COMMASPACE = ', '

def usage(code, msg=''):
    print >> sys.stderr, __doc__
    if msg:
        print >> sys.stderr, msg
    sys.exit(code)

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory='])
    except getopt.error, msg:
        usage(1, msg)

    dir = os.curdir
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage(0)
        elif opt in ('-d', '--directory'):
            dir = arg

    if len(args) < 2:
        usage(1)

    sender = args[0]
    recips = args[1:]
    
    # Crea il messaggio contenitore (outer)
    outer = MIMEMultipart()
    outer['Subject'] = 'Contents of directory %s' % os.path.abspath(dir)
    outer['To'] = COMMASPACE.join(recips)
    outer['From'] = sender
    outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
    # Fa in modo che il messaggio termini con un fine riga
    outer.epilogue = ''

    for filename in os.listdir(dir):
        path = os.path.join(dir, filename)
        if not os.path.isfile(path):
            continue
        #  Individua il tipo del contenuto in base all'estensione del 
        #+ file. La codifica verrà ignorata, anche se dovremmo effettuare 
        #+ un controllo su cosette come file compressi e gzip.
        ctype, encoding = mimetypes.guess_type(path)
        if ctype is None or encoding is not None:
            #  Non si capisce il tipo o il file è codificato (compresso) 
            #+ quindi si usa un generico tipo bag-of-bits.
            ctype = 'application/octet-stream'
        maintype, subtype = ctype.split('/', 1)
        if maintype == 'text':
            fp = open(path)
            # Notate: dovrebbe gestire la tipologia del carattere usato
            msg = MIMEText(fp.read(), _subtype=subtype)
            fp.close()
        elif maintype == 'image':
            fp = open(path, 'rb')
            msg = MIMEImage(fp.read(), _subtype=subtype)
            fp.close()
        elif maintype == 'audio':
            fp = open(path, 'rb')
            msg = MIMEAudio(fp.read(), _subtype=subtype)
            fp.close()
        else:
            fp = open(path, 'rb')
            msg = MIMEBase(maintype, subtype)
            msg.set_payload(fp.read())
            fp.close()
            # Codifica il carico utile usando Base64
            Encoders.encode_base64(msg)
        # Imposta il parametro del nome del file
        msg.add_header('Content-Disposition', 'attachment', filename=filename)
        outer.attach(msg)

    # Adesso invia il messaggio
    s = smtplib.SMTP()
    s.connect()
    s.sendmail(sender, recips, outer.as_string())
    s.close()

if __name__ == '__main__':
    main()

Infine, ecco un esempio di come scompattare un messaggio MIME come il precedente, in una directory di file:

#!/usr/bin/env python

"""Scompatta un messaggio MIME in una directory di file.

Uso: unpackmail [opzioni] msgfile

Opzioni:
    -h / --help
        stampa questo messaggio ed esce.

    -d directory
    --directory=directory
        Scompatta il messaggio MIME nella directory indicata che verrà
        creata se non già esistente.

msgfile è il percorso del file che contiene il messaggio MIME.
"""

import sys
import os
import getopt
import errno
import mimetypes
import email

def usage(code, msg=''):
    print >> sys.stderr, __doc__
    if msg:
        print >> sys.stderr, msg
    sys.exit(code)

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory='])
    except getopt.error, msg:
        usage(1, msg)

    dir = os.curdir
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage(0)
        elif opt in ('-d', '--directory'):
            dir = arg

    try:
        msgfile = args[0]
    except IndexError:
        usage(1)

    try:
        os.mkdir(dir)
    except OSError, e:
        # Ignora l'errore che evidenzia l'esistenza della directory
        if e.errno <> errno.EEXIST: raise

    fp = open(msgfile)
    msg = email.message_from_file(fp)
    fp.close()

    counter = 1
    for part in msg.walk():
        # multipart/* sono semplici contenitori
        if part.get_content_maintype() == 'multipart':
            continue
        #  Le applicazioni dovrebbero veramente controllare a fondo il 
        #+ nome del file fornito, in modo che il messaggio di posta 
        #+ elettronica non possa essere usato per sovrascrivere file 
        #+ importanti
        filename = part.get_filename()
        if not filename:
            ext = mimetypes.guess_extension(part.get_type())
            if not ext:
                # Use a generic bag-of-bits extension
                ext = '.bin'
            filename = 'part-%03d%s' % (counter, ext)
        counter += 1
        fp = open(os.path.join(dir, filename), 'wb')
        fp.write(part.get_payload(decode=1))
        fp.close()

if __name__ == '__main__':
    main()


Footnotes

... email:12.4
Un ringraziamento a Matthew Dixon Cowles per l'originale ispirazione e gli esempi.
Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.