smtplib - Client Simple Mail Transfer Protocol

Scopo: Interazione con i server SMTP, inclusa la spedizione email

smtp include la classe SMTP, che può essere usata per comunicare con i server di posta per inviare posta.

Gli indirizzi mail, nomi host e indirizzi IP negli esempi seguenti sono stati oscurati, ma per il resto gli script illustrano la sequenza dei comandi e delle risposte accuratamente.

Inviare un Messaggio Email

L'uso più comune di SMTP è la connessione a un server di posta e l'invio di una messaggio. Il nome host del server di posta e la porta possono essere passate al costruttore, oppure si può invocare esplicitamente connect(). Una volta connessi, si chiami sendmail() con i parametri di busta e corpo del messaggio. Il testo del messaggio dovrebbe essere completamente formato e conforme alle specifiche RFC 5322, visto che smtplib non modifica in alcun modo i contenuti o le intestazioni. Il che vuol dire che le intestazioni From e To devono essere aggiunte dal chiamante.

# smtplib_sendmail.py

import smtplib
import email.utils
from email.mime.text import MIMEText

# Create the message
msg = MIMEText('Questo il corpo del messaggio.')
msg['To'] = email.utils.formataddr(('Recipient',
                                    'recipient@example.com'))
msg['From'] = email.utils.formataddr(('Author',
                                      'author@example.com'))
msg['Subject'] = 'Semplice messaggio di prova'

server = smtplib.SMTP('localhost', 1025)
server.set_debuglevel(True)  # mostra la comunicazione con il server
try:
    server.sendmail('author@example.com',
                    ['recipient@example.com'],
                    msg.as_string())
finally:
    server.quit()

In questo esempio, è stato attivato anche il debug per mostrare la comunicazione tra client e server. Altrimenti questo esempio non produrrebbe alcun risultato.

$ python3 smtplib_sendmail.py

send: 'ehlo 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa\nSIZE 33554432\nHELP'
send: 'mail FROM:<author@example.com> size=236\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'rcpt TO:<recipient@example.com>\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'data\r\n'
reply: b'354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: b'End data with <CR><LF>.<CR><LF>'
data: (354, b'End data with <CR><LF>.<CR><LF>')
send: b'Content-Type: text/plain; charset="us-ascii"\r\nMIME-Ver
sion: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo: Recipient <r
ecipient@example.com>\r\nFrom: Author <author@example.com>\r\nSu
bject: Simple test message\r\n\r\nThis is the body of the messag
e.\r\n.\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
data: (250, b'OK')
send: 'quit\r\n'
reply: b'221 Bye\r\n'
reply: retcode (221); Msg: b'Bye'

Il secondo argomento per sendmail(), i destinatari, sono passati come lista. E' possibile includere nell'elenco un qualsiasi numero di indirizzi per consegnare a ognuno di essi il messaggio. Visto che le informazioni della busta sono separate dalle intestazioni di messaggio, è possibile aggiungere qualcuno in copia per conoscenza (BCC) includendolo nell'argomento del metodo, ma non nell'intestazione del messaggio.

Autenticazione e Crittografia

La classe SMTP include anche autenticazione e crittografia TLS (Sicurezza a livello di trasporto), qualora il server li supporti. Per determinare se il server supporta TLS, si chiami ehlo() direttamente per fare identificare il client al server e richiedere quali estensioni sono disponibili. Successivamente si chiami has_extn() per verificare i risultati. Dopo che è partito TLS, ehlo() deve essere chiamato nuovamente prima dell'autenticazione. Molti provider che ospitano server di posta ora supportano solamente connessioni basate su TLS. Per la comunicazione con tali server, si utilizzi SMTP_SSL per partire con una connessione criptata.

# smtplib_authenticated.py

import smtplib
import email.utils
from email.mime.text import MIMEText
import getpass

# Chiede all'utente le informazioni di connessione
to_email = input('Destinatario: ')
servername = input('Nome server mail: ')
serverport = input('Porta del server: ')
if serverport:
    serverport = int(serverport)
else:
    serverport = 25
use_tls = input('Usare TLS? (si/no): ').lower()
username = input('Nome utente mail: ')
password = getpass.getpass("%s's password: " % username)

# Crea il messaggio
msg = MIMEText('Messaggio di prova da PyMOTW3-it.')
msg.set_unixfrom('author')
msg['To'] = email.utils.formataddr(('Recipient', to_email))
msg['From'] = email.utils.formataddr(('Author',
                                      'author@example.com'))
msg['Subject'] = 'Prova da PyMOTW3-it'

if use_tls.startswith('s'):
    print('inizio con una connessione sicura')
    server = smtplib.SMTP_SSL(servername, serverport)
else:
    print('inizio con una connessione non sicura')
    server = smtplib.SMTP(servername, serverport)
try:
    server.set_debuglevel(True)

    # Ci identifichiamo, chiedendo al server quali caratteristiche supporta
    server.ehlo()

    # Se è possibile criptare la sessione, si fa
    if server.has_extn('STARTTLS'):
        print('(partenza di TLS)')
        server.starttls()
        server.ehlo()  # nuova identificazione su connessione TLS
    else:
        print('(no STARTTLS)')

    if server.has_extn('AUTH'):
        print('(autenticazione)')
        server.login(username, password)
    else:
        print('(no AUTH)')

    server.sendmail('author@example.com',
                    [to_email],
                    msg.as_string())
finally:
    server.quit()

Esempi seguenti rispettivamente con connessione non sicura e connessione sicura. L'estensione STARTTLS non compare nella risposta a EHLO dopo che viene abilitato TLS.

$ python3 smtplib_authenticated.py

Recipient: doug@pymotw.com
Mail server name: localhost
Server port: 1025
Use TLS? (yes/no): no
Mail username: test
test's password:
starting with an insecure connection
send: 'ehlo 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa\nSIZE 33554432\nHELP'
(no STARTTLS)
(no AUTH)
send: 'mail FROM:<author@example.com> size=220\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'rcpt TO:<doug@pymotw.com>\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'data\r\n'
reply: b'354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: b'End data with <CR><LF>.<CR><LF>'
data: (354, b'End data with <CR><LF>.<CR><LF>')
send: b'Content-Type: text/plain; charset="us-ascii"\r\n
MIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo:
Recipient <doug@pymotw.com>\r\nFrom: Author <author@example.com>
\r\nSubject: Test from PyMOTW\r\n\r\nTest message from PyMOTW.
\r\n.\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
data: (250, b'OK')
send: 'quit\r\n'
reply: b'221 Bye\r\n'
reply: retcode (221); Msg: b'Bye'
$ python3 smtplib_authenticated.py

Recipient: doug@pymotw.com
Mail server name: mail.isp.net
Server port: 465
Use TLS? (yes/no): yes
Mail username: doughellmann@isp.net
doughellmann@isp.net's password:
starting with a secure connection
send: 'ehlo 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-mail.isp.net\r\n'
reply: b'250-PIPELINING\r\n'
reply: b'250-SIZE 71000000\r\n'
reply: b'250-ENHANCEDSTATUSCODES\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-AUTH PLAIN LOGIN\r\n'
reply: b'250 AUTH=PLAIN LOGIN\r\n'
reply: retcode (250); Msg: b'mail.isp.net\nPIPELINING\nSIZE
71000000\nENHANCEDSTATUSCODES\n8BITMIME\nAUTH PLAIN LOGIN\n
AUTH=PLAIN LOGIN'
(no STARTTLS)
(logging in)
send: 'AUTH PLAIN AGRvdWdoZWxsbWFubkBmYXN0bWFpbC5mbQBUTUZ3MDBmZmF
zdG1haWw=\r\n'
reply: b'235 2.0.0 OK\r\n'
reply: retcode (235); Msg: b'2.0.0 OK'
send: 'mail FROM:<author@example.com> size=220\r\n'
reply: b'250 2.1.0 Ok\r\n'
reply: retcode (250); Msg: b'2.1.0 Ok'
send: 'rcpt TO:<doug@pymotw.com>\r\n'
reply: b'250 2.1.5 Ok\r\n'
reply: retcode (250); Msg: b'2.1.5 Ok'
send: 'data\r\n'
reply: b'354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: b'End data with <CR><LF>.<CR><LF>'
data: (354, b'End data with <CR><LF>.<CR><LF>')
send: b'Content-Type: text/plain; charset="us-ascii"\r\n
MIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo:
Recipient <doug@pymotw.com>\r\nFrom: Author <author@example.com>
\r\nSubject: Test from PyMOTW\r\n\r\nTest message from PyMOTW.
\r\n.\r\n'
reply: b'250 2.0.0 Ok: queued as A0EF7F2983\r\n'
reply: retcode (250); Msg: b'2.0.0 Ok: queued as A0EF7F2983'
data: (250, b'2.0.0 Ok: queued as A0EF7F2983')
send: 'quit\r\n'
reply: b'221 2.0.0 Bye\r\n'
reply: retcode (221); Msg: b'2.0.0 Bye'

Verificare un Indirizzo Email

Il protocollo SMTP include un comando per chiedere a un server se un indirizzo è valido. In genere VRFY è disabilitato per prevenire chi vuole compiere azioni di spam dal trovare indirizzi email legittimi, ma se è abilitato un client può passare al server un indirizzo e ricevere un codice di stato che indica la validità assieme al nome completo dell'utente, se disponibile.

# smtplib_verify.py

import smtplib

server = smtplib.SMTP('mail')
server.set_debuglevel(True)  # mostra la comunicazione con il server
try:
    dhellmann_result = server.verify('dhellmann')
    notthere_result = server.verify('notthere')
finally:
    server.quit()

print('dhellmann:', dhellmann_result)
print('notthere :', notthere_result)

Come mostrato nelle ultime due righe del risultato, l'indirizzo dhellmann è valido ma notthere no.

$ python3 smtplib_verify.py

send: 'vrfy <dhellmann>\r\n'
reply: '250 2.1.5 Doug Hellmann <dhellmann@mail>\r\n'
reply: retcode (250); Msg: 2.1.5 Doug Hellmann <dhellmann@mail>
send: 'vrfy <notthere>\r\n'
reply: '550 5.1.1 <notthere>... User unknown\r\n'
reply: retcode (550); Msg: 5.1.1 <notthere>... User unknown
send: 'quit\r\n'
reply: '221 2.0.0 mail closing connection\r\n'
reply: retcode (221); Msg: 2.0.0 mail closing connection
dhellmann: (250, '2.1.5 Doug Hellmann <dhellmann@mail>')
notthere : (550, '5.1.1 <notthere>... User unknown')

Vedere anche:

smtplib
La documentazione della libreria standard per questo modulo.
RFC 821
Le specifiche SMTP (Simple Mail Transfer Protocol)
RFC 1869
Estensioni di servizio SMTP per il protocollo base
RFC 822
Le specifiche di formato per il messaggio email originale: "Standard for the Format of ARPA Internet Text Messages"
RFC 5322
"Internet Message Format", aggiornamenti al formato del messaggio email
email
Modulo della libreria standard per costruire e analizzare messaggi email
smptd
Implementa un semplice server SMTP