smtpd - SMTP Server
Scopo: Include classi per implementare server SMTP
Il modulo smtpd include classi per la costruzione di semplici server SMTP. Rappresenta il lato server del protocollo usato da smtplib.
Classe Base del Server Mail
La classe base per tutti gli esempi di server forniti è SMTPServer
. Gestisce la comunicazione con il client, riceve dati in arrivo, e fornisce un comodo aggancio di sovrascrittura per elaborare il messaggio quando lo stesso è completamente disponibile.
Gli argomenti del costruttore sono l'indirizzo locale per rilevare le connessioni e l'indirizzo remoto dove messaggi in transito dovrebbero essere consegnati. Il metodo process_message()
viene fornito come aggancio perchè possa essere sovrascritto da una classe derivata. Viene chiamato quando il messaggio è completamente ricevuto, e viene corredato dei seguenti argomenti:
NOME ARGOMENTO | DESCRIZIONE |
---|---|
peer |
indirizzo del client, una tupla che contiene IP e porta in arrivo |
mailfrom |
informazioni della parte "from" della busta del messaggio, dato al server dal client quando il messaggio viene consegnato. Non necess ariamente trova sempre corrispondenza con l'intestazione From |
rcpttos |
lista dei destinatari dalla busta del messaggio. Non necessariamente trova corrispondenza con l'intestazione To , specialmente se il destinatario è in copia conoscenza nascosta (bcc) |
data |
Il corpo del messaggio completo RFC 5322 |
L'implementazione predefinita di process_message()
solleva NotImplementedError
. L'esempio che segue definisce una sottoclasse che sovrascrive il metodo per stampare informazioni circa i messaggi ricevuti.
# smtpd_custom.py
import smtpd
import asyncore
class CustomSMTPServer(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
print('Messaggio in ricezione da:', peer)
print('Mittente del messaggio :', mailfrom)
print('Messaggio indirizzato a :', rcpttos)
print('Lunghezza del messaggio :', len(data))
server = CustomSMTPServer(('127.0.0.1', 1025), None)
asyncore.loop()
SMTPServer
usa asyncore, quindi per eseguire il server si chiami asyncore.loop()
.
Per la dimostrazione del server occorre un client. Si può adattare uno degli esempi da smtplib per creare un client per inviare dati al server di test, in esecuzione localmente sulla porta 1025.
# smtpd_senddata.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',
'destinatario@esempio.com'))
msg['From'] = email.utils.formataddr(('Author',
'autore@esempio.com'))
msg['Subject'] = 'Semplice messaggio di test'
server = smtplib.SMTP('127.0.0.1', 1025)
server.set_debuglevel(True) # show communication with the server
try:
server.sendmail('autore@esempio.com',
['destinatario@esempio.com'],
msg.as_string())
finally:
server.quit()
Per verificare i programmi, si esegua smtpd_custom.py
in un terminale, quindi smtpd_senddata.py
in un altro.
$ python3 smtpd_custom.py Messaggio in ricezione da: ('127.0.0.1', 43994) Mittente del messaggio : autore@esempio.com Messaggio indirizzato a : ['destinatario@esempio.com'] Lunghezza del messaggio : 237
Il risultato di debug da smtpd_senddata.py
mostra tutte le comunicazioni con il server.
$ python3 smtpd_senddata.py send: 'ehlo [127.0.1.1]\r\n' reply: b'250-robby-desktop\r\n' reply: b'250-SIZE 33554432\r\n' reply: b'250-8BITMIME\r\n' reply: b'250 HELP\r\n' reply: retcode (250); Msg: b'robby-desktop\nSIZE 33554432\n8BITMIME\nHELP' send: 'mail FROM:<autore@esempio.com> size=244\r\n' reply: b'250 OK\r\n' reply: retcode (250); Msg: b'OK' send: 'rcpt TO:<destinatario@esempio.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-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo: Recipient <destinatario@esempio.com>\r\nFrom: Author <autore@esempio.com>\r\nSubject: Semplice messaggio di test\r\n\r\nQuesto il corpo del messaggio.\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'
Per interrompere il server, si prema Ctrl-C
.
Server di Debug
L'esempio precedente mostra gli argomenti a process_message()
, ma smtpd include anche un server progettato specificamente per un debug più completo: DebuggingServer
. Stampa l'intero messaggio in arrivo alla console, quindi interrompe l'elaborazione (non smista il messaggio a un server mail reale).
# smtpd_debug.py
import smtpd
import asyncore
server = smtpd.DebuggingServer(('127.0.0.1', 1025), None)
asyncore.loop()
Usando il programma client smtpd_senddata.py
qui sopra, il risultato è:
---------- MESSAGE FOLLOWS ---------- mail options: ['SIZE=244'] b'Content-Type: text/plain; charset="us-ascii"' b'MIME-Version: 1.0' b'Content-Transfer-Encoding: 7bit' b'To: Recipient <destinatario@esempio.com>' b'From: Author <autore@esempio.com>' b'Subject: Semplice messaggio di test' b'X-Peer: 127.0.0.1' b'' b'Questo il corpo del messaggio.' ------------ END MESSAGE ------------
Proxy Server
La classe PureProxy
implementa un proxy server. I messaggi in arrivo vengono inoltrati a monte al server passato come argomento al costruttore.
I passi per impostare il proxy server sono simili a quelli del server di debug.
# smtpd_proxy.py
import smtpd
import asyncore
server = smtpd.PureProxy(('127.0.0.1', 1025), ('mail', 25))
asyncore.loop()
Non viene stampato alcun risultato, quindi per verificarne il funzionamento occorre cercare nei log del server mail.
Vedere anche: