smtpd – サンプル SMTP サーバ¶
目的: | SMTP サーバを実装するクラスを提供する |
---|---|
利用できるバージョン: | 2.1 以上 |
smtpd モジュールは SMTP (簡易メール転送プロトコル) サーバを構築するためのクラスを提供します。それは smtplib が使用するプロトコルのサーバ側のプログラムです。
SMTP サーバ¶
SMTPServer は全てのサンプルサーバのベースクラスになります。それはクライアントとの通信を制御してデータを受信します。そして、データを完全に受信すると、そのメッセージ操作をオーバライドする便利なフックを提供します。
コンストラクタの引数はクライアントからの接続を listen するローカルアドレスとプロキシのリモートアドレスです。 process_message() メソッドは派生したサブクラスでオーバーライドされるフックとして提供されます。そのメソッドはメッセージを完全に受信したときに呼び出されて、次の引数を受け取ります。
peer
クライアントのアドレス、IP アドレスと入力ポート番号のタプルです。
mailfrom
メッセージエンベロープから取り出された “from” の情報です。メッセージが配信されるときにクライアントからサーバへ渡されます。この情報は必ずしもメールヘッダの From と一致するというわけではありません。
rcpttos
メッセージエンベロープの受取人のリストです。繰り返しますが、この情報も必ずしもメールヘッダの To と一致するわけではありません。例えば BCC の場合が一致しません。
data
RFC 2822 完全準拠のメッセージ本文です。
process_message() のデフォルト実装は NotImplementedError を発生させるので、 SMTPServer を使用して実行するには、サブクラスとそのメソッドの実装を作成する必要があります。最初のサンプルは受信したメッセージに関する情報を表示するサーバです。
import smtpd
import asyncore
class CustomSMTPServer(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data):
print 'Receiving message from:', peer
print 'Message addressed from:', mailfrom
print 'Message addressed to :', rcpttos
print 'Message length :', len(data)
return
server = CustomSMTPServer(('127.0.0.1', 1025), None)
asyncore.loop()
SMTPServer は asyncore を使用するので、そのサーバを実行するために asyncore.loop() を呼び出します。
ここでデータを送信するクライアントが必要です。 smtplib ページから適応するサンプルを使用して、ローカルホストの 1025 番ポートで実行したテストサーバへデータを送信するクライアントを設定します。
import smtplib
import email.utils
from email.mime.text import MIMEText
# メッセージを作成する
msg = MIMEText('This is the body of the message.')
msg['To'] = email.utils.formataddr(('Recipient', 'recipient@example.com'))
msg['From'] = email.utils.formataddr(('Author', 'author@example.com'))
msg['Subject'] = 'Simple test message'
server = smtplib.SMTP('127.0.0.1', 1025)
server.set_debuglevel(True) # show communication with the server
try:
server.sendmail('author@example.com', ['recipient@example.com'], msg.as_string())
finally:
server.quit()
あるターミナルで smtpd_custom.py を、別のターミナルで smtpd_senddata.py を実行すると次のようになります。
$ python smtpd_senddata.py
send: 'ehlo farnsworth.local\r\n'
reply: '502 Error: command "EHLO" not implemented\r\n'
reply: retcode (502); Msg: Error: command "EHLO" not implemented
send: 'helo farnsworth.local\r\n'
reply: '250 farnsworth.local\r\n'
reply: retcode (250); Msg: farnsworth.local
send: 'mail FROM:<author@example.com>\r\n'
reply: '250 Ok\r\n'
reply: retcode (250); Msg: Ok
send: 'rcpt TO:<recipient@example.com>\r\n'
reply: '250 Ok\r\n'
reply: retcode (250); Msg: Ok
send: 'data\r\n'
reply: '354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: End data with <CR><LF>.<CR><LF>
data: (354, 'End data with <CR><LF>.<CR><LF>')
send: 'Content-Type: text/plain; charset="us-ascii"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo: Recipient <recipient@example.com>\r\nFrom: Author <author@example.com>\r\nSubject: Simple test message\r\n\r\nThis is the body of the message.\r\n.\r\n'
reply: '250 Ok\r\n'
reply: retcode (250); Msg: Ok
data: (250, 'Ok')
send: 'quit\r\n'
reply: '221 Bye\r\n'
reply: retcode (221); Msg: Bye
と
$ python smtpd_custom.py
Receiving message from: ('127.0.0.1', 58541)
Message addressed from: author@example.com
Message addressed to : ['recipient@example.com']
Message length : 229
入力メッセージのポート番号は毎回変わります。 rcpttos 引数は値のリストで mailfrom は文字列であることに注意してください。
Note
サーバを停止するには Ctrl-C を入力してください。
DebuggingServer¶
上述したサンプルは process_message() に渡された引数を表示しますが smtpd は DebuggingServer という、さらに完璧なデバッグのために特別に設計されたサーバも提供します。それは入力メッセージを標準出力へ全て表示してから処理を停止します(実際のメールサーバへプロキシのようにメッセージは送信しません)。
import smtpd
import asyncore
server = smtpd.DebuggingServer(('127.0.0.1', 1025), None)
asyncore.loop()
先ほど使用した smtpd_senddata.py クライアントプログラムを使用すると DebuggingServer の出力は次のようになります。
$ python smtpd_debug.py
---------- MESSAGE FOLLOWS ----------
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
To: Recipient <recipient@example.com>
From: Author <author@example.com>
Subject: Simple test message
X-Peer: 127.0.0.1
This is the body of the message.
------------ END MESSAGE ------------
PureProxy¶
PureProxy クラスは真っ直ぐで単純なプロキシサーバを実装します。入力メッセージはコンストラクタの引数として渡された上流のサーバへ転送されます。
Warning
標準ライブラリドキュメントには “このプログラムの実行はオープンリレーについて理解する良い機会なので慎重に行ってください” とあります。
プロキシサーバの設定はまさにデバッグサーバと同じぐらい簡単です。
import smtpd
import asyncore
server = smtpd.PureProxy(('127.0.0.1', 1025), ('mail', 25))
asyncore.loop()
このサンプルは何も出力しないのでプロキシサーバが動作しているかを調べるためにメールサーバのログを見る必要があります。
Oct 19 19:16:34 homer sendmail[6785]: m9JNGXJb006785: from=<author@example.com>, size=248, class=0, nrcpts=1, msgid=<200810192316.m9JNGXJb006785@homer.example.com>, proto=ESMTP, daemon=MTA, relay=[192.168.1.17]
MailmanProxy¶
さらに smtpd は Mailman のフロントエンドとして動作する特別なプロキシを提供します。それはローカルの Mailman 設定がそのアドレスを認識する場合、直接的に操作されます。一方、そのメッセージはプロキシへ配送されます。
See also
- smtpd
- 本モジュールの標準ライブラリドキュメント
- smtplib
- クライアントインタフェースを提供する
- メールメッセージを構文解析する
- asyncore
- 非同期サーバを実装するベースモジュール
- RFC 2822
- メールメッセージフォーマットを定義する
- GNU Mailman mailing list software
- メールを扱う Python ソフトウェアの優れたサンプル