メールの解析、構造化されたデータやJSON文字列の取得など、 シシマイが使える環境にバウンスメールを用意すれば直ぐに解析結果を得られる 最も簡単で基本的な使い方を紹介します。
標準入力(STDIN)からバウンスメールのデータを読み込み、 シシマイで解析する方法の記述例です。 PerlやRubyのワンライナーでパイプラインを経由して 読み込む場合に便利な入力方法です。
変数からバウンスメールのデータを読み込み シシマイで解析する方法の記述例です。バウンスメール をデータベースやジョブキューシステムから読み出して 解析する場合に便利な方法です。
Sisimaiクラスのdump()とmake()メソッドは、 第一引数にファイルハンドルSTDINを指定すると、 標準入力からバウンスメールのデータを読み込み、解析結果を返します。
% cat ./path/to/email | perl -MSisimai -lE 'print Sisimai->dump(STDIN)' | jq [ { "listid": "", "smtpagent": "Sendmail", ...
% cat ./path/to/email | perl -MSisimai -lE 'print $_->recipient->address for @{ Sisimai->make(STDIN) }' kijitora@example.jp sironeko@example.org
Ruby版Sisimaiも同様に、Sisimaiクラスのdump()とmake()メソッドの 第一引数にSTDINオブジェクトを指定すると、 標準入力からバウンスメールのデータを読み込み、解析結果を返します。
% cat ./path/to/email | ruby -Ilib -rsisimai -e 'puts Sisimai.dump(STDIN)' | jq [ { "catch": "", "token": "d295b517a3ad6f8748031e1068e07ebd089c0277", ...
% cat ./path/to/email | ruby -rsisimai -e 'p Sisimai.make(STDIN)' [#<Sisimai::Data:0x007fa1b0993050 @addresser=#<Sisimai::Address:0x007fa1b0990e40 @user="kijitora", ...
Sisimaiのmake()とdump()の第一引数に変数 (Perlではスカラーリファレンス・RubyではStringオブジェクト)を指定することによって、 ファイルとしてのメールや標準入力以外からバウンスメールのデータを読み込むことができます。
バウンスメールをデータベースやジョブキューシステムから読み出す場合に有用なこの機能 (Sisimai::Mail::Memory)はSisimai 4.23.0で実装されました。
make()とdump()の第一引数にスカラーリファレンスを指定すると、 Sisimai内部ではSisimai::Mail::Memoryが呼び出され、メールのファイルや標準入力から読み込んだ のと同様、解析結果が得られます。
#! /usr/bin/env perl
use Sisimai;
my $f = 'From MAILER-DAEMON Thu Dec 22 17:54:04 2015...'; # バウンスメール全体
my $v = Sisimai->make(\$f);
for my $e ( @$v ) {
print $e->action; # delayed
print $e->deliverystatus; # 4.7.0
print $e->reason; # expired
print $e->diagnosticcode; # Envelope expired
print $e->softbounce; # 1
}
Ruby版Sisimaiではmake()とdump()の第一引数に Stringオブジェクトを指定すると、Sisimai内部でSisimai::Mail::Memoryが呼び出され、 メールのファイルや標準入力から読み込んだのと同様、解析結果が得られます。
#! /usr/bin/env ruby
require 'sisimai'
f = 'From MAILER-DAEMON Thu Dec 22 17:54:04 2015...' # バウンスメール全体
v = Sisimai.make(f)
v.each do |e|
puts e.action # delayed
puts e.deliverystatus # 4.7.0
puts e.reason # expired
puts e.diagnosticcode # Envelope expired
puts e.softbounce # 1
end
Sisimaiのmake()とdump()は第二引数にdelivered を指定することによって、配信が成功した事を通知するメールも解析結果に含めることができます。
配送が成功したメールは
github.com/sisimai/set-of-emails
リポジトリの
rfc3464-28.eml
がサンプルとして用意してあります。
ここでは、このメールを解析するサンプルコードを紹介します。
make()とdump()の第二引数にdelivered => 1を指定すると、 配信に成功したメール(配信通知)も解析結果に含まれるようになります。delivered に偽となる値(0,undef,'')を指定した場合は、解析結果は生成されません。
#! /usr/bin/env perl
use Sisimai;
my $f = './set-of-emails/maildir/bsd/rfc3464-28.eml';
my $v = Sisimai->make($f, 'delivered' => 1);
for my $e ( @$v ) {
print $e->action; # deliverable
print $e->deliverystatus; # 2.1.5
print $e->reason; # delivered
print $e->diagnosticcode; # 250 2.1.5 Ok
print $e->softbounce; # -1
}
Perl版と同様に、Ruby版Sisimaiでもmake()とdump()の第二引数に delivered: trueを指定すると、配信に成功したメール(配信通知)も 解析結果に含まれるようになります。delivered に偽となる値(nil,false)を指定した場合は、解析結果は生成されません。
#! /usr/bin/env ruby
require 'sisimai'
f = './set-of-emails/maildir/bsd/rfc3464-28.eml'
v = Sisimai.make(f, delivered: true)
v.each do |e|
puts e.action # deliverable
puts e.deliverystatus # 2.1.5
puts e.reason # delivered
puts e.diagnosticcode # 250 2.1.5 Ok
puts e.softbounce # -1
end
Sisimai 4.19.0から実装されたコールバック機能を使用すると、 Sisimai::DataまたはSisimai::Messageのcatch() メソッドを通して、バウンスメールを独自に処理した結果を取得することができます。 この機能はSisimaiが解析結果に含めない元メッセージ全体や 元メッセージの特定のヘッダの値(例えば配信IDなど)を取り出したりするのに便利です。
Perlのフックメソッド(サブルーチン)にはハッシュリファレンス1つ、 RubyのフックメソッドにはHashオブジェクト1つ が引数として渡されます。引数の構造は以下のようになっています。
キー名 | データ型 | 説明 | メール | JSON |
---|---|---|---|---|
headers | Hash | バウンスメールのヘッダ部分(ハッシュ化済み) | ○ | undef,nil |
message | String | バウンスメールのメール本文(改行・元メッセージを含む) | ○ | undef,nil |
lhost-postfix-16.eml にある以下の内容をコールバックで実行するコードによって取得する例を示します。
Return-Path: <> Received: by 192.0.2.22 with SMTP id fffffff000; Thu, 20 Jan 2011 23:34:45 +0900 (JST) Received: from mx0.example.jp (mx0.example.jp [192.0.2.9]) by mx.example.org with ESMTP id 0000000000000000000000; Thu, 20 Jan 2011 23:34:45 +0900 (JST) Received-SPF: pass (example.org: best guess record for domain of mx0.example.jp designates 192.0.2.9 as permitted sender) client-ip=192.0.2.9; Received: by mx0.example.jp (Postfix) id FFFFFFFFFFFF; Thu, 20 Jan 2011 23:34:45 +0900 (JST) Date: Thu, 20 Jan 2011 23:34:45 +0900 (JST) From: MAILER-DAEMON@example.jp (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: shironeko@cat.example.com Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="FFFFFFFFFF.00000000/mx0.example.jp" Content-Transfer-Encoding: 8bit Message-Id: <000000000000000.FFFFFFFFFFFF@mx0.example.jp> This is a MIME-encapsulated message. --FFFFFFFFFF.00000000/mx0.example.jp Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host mx0.example.jp. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to <postmaster> If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system <kijitora@example.go.jp>: host mx1.example.go.jp[192.0.2.127] said: 550 5.1.6 recipient no longer on server: kijitora@example.go.jp (in reply to RCPT TO command) --FFFFFFFFFF.00000000/mx0.example.jp Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; mx0.example.jp X-Postfix-Queue-ID: FFFFFF000000 X-Postfix-Sender: rfc822; shironeko@cat.example.com ...
フックメソッドには一つの引数が以下のように渡されます。 lhost-postfix-16.eml のヘッダ部分(ハッシュ化済み)とメッセージ部分(文字列)を構造化したものです。 undefになっている部分は、Rubyではnilと読み替えてください。
{ 'headers' => { 'x-mailer' => undef, 'return-path' => '<>', 'content-type' => 'multipart/report; report-type=delivery-status; boundary="FFFFFFFFFF.00000000/mx0.example.jp"', 'subject' => 'Undelivered Mail Returned to Sender', 'date' => 'Thu, 20 Jan 2011 23:34:45 +0900 (JST)', 'to' => 'shironeko@cat.example.com', 'received-spf' => 'pass (example.org: best guess record for domain of mx0.example.jp designates 192.0.2.9 as permitted sender) client-ip=192.0.2.9;', 'reply-to' => undef, 'message-id' => '<000000000000000.FFFFFFFFFFFF@mx0.example.jp>', 'from' => 'MAILER-DAEMON@example.jp (Mail Delivery System)', 'content-transfer-encoding' => '8bit', 'received' => [ 'by 192.0.2.22 with SMTP id fffffff000; Thu, 20 Jan 2011 23:34:45 +0900 (JST)', 'from mx0.example.jp (mx0.example.jp [192.0.2.9]) by mx.example.org with ESMTP id 0000000000000000000000; Thu, 20 Jan 2011 23:34:45 +0900 (JST)', 'by mx0.example.jp (Postfix) id FFFFFFFFFFFF; Thu, 20 Jan 2011 23:34:45 +0900 (JST)' ], 'auto-submitted' => 'auto-replied' }, 'message' => 'This is a MIME-encapsulated message. ... Reporting-MTA: dns; mx0.example.jp X-Postfix-Queue-ID: FFFFFF000000 X-Postfix-Sender: rfc822; shironeko@cat.example.com Arrival-Date: Thu, 20 Jan 2011 23:34:45 +0900 (JST) Final-Recipient: rfc822; kijitora@example.go.jp Action: failed Status: 5.1.6 ... Subject: Nyaaaaan To: KijitoraMessage-Id: <0000000000000.FFFFFF000000@mx0.example.jp> Nyaaaan ' }
Sisimaiクラスのmake()とdump()メソッドに、 実行したい処理内容を書いたサブルーチンのコードリファレンスを第二引数 hookとして渡すことができます。
#! /usr/bin/env perl
use Sisimai;
my $f = './set-of-emails/maildir/bsd/lhost-postfix-16.eml';
my $x = sub {
my $argv = shift;
my $data = { 'queue-id' => '', 'received-spf' => '' };
$data->{'received-spf'} = $argv->{'headers'}->{'received-spf'} || '';
if( $argv->{'message'} =~ m/^X-Postfix-Queue-ID:\s*(.+)$/m ) {
$data->{'queue-id'} = $1;
}
return $data;
};
my $j = Sisimai->dump($f, 'hook' => $x); # JSON文字列を得る
my $v = Sisimai->make($f, 'hook' => $x);
print $v->[0]->{'catch'}->{'received-spf'}; # pass (example.org:...
print $v->[0]->{'catch'}->{'queue-id'}; # FFFFFF000000
Sisimaiクラスのmake()とdump()メソッドに、 実行したい処理内容を書いたProcオブジェクトをhook: として渡すことができます。
#! /usr/bin/env ruby
require 'sisimai'
f = './set-of-emails/maildir/bsd/lhost-postfix-16.eml'
x = lambda do |argv|
data = { 'queue-id' => '', 'received-spf' => '' }
data['received-spf'] = argv['headers']['received-spf'] || ''
if cv = argv['message'].match(/^X-Postfix-Queue-ID:\s*(.+)$/)
data['queue-id'] = cv[1]
end
return data
end
j = Sisimai.dump(f, hook: x) # JSON文字列を得る
v = Sisimai.make(f, hook: x)
puts v[0]['catch']['received-spf'] # pass (example.org: ...
puts v[0]['catch']['queue-id'] # FFFFFF000000
Sisimaiのdump()メソッドにフックメソッドを渡して得たJSON文字列は 次のような内容になります。
[ { "timestamp": 1270043983, "smtpcommand": "RCPT", "subject": "Nyaaan", "recipient": "kijitora@example.jp", "destination": "example.jp", "senderdomain": "example.co.jp", "token": "bb2a5dfb6161f396300b24acd9bf637c1c45a58a", "smtpagent": "qmail", "replycode": "450", "softbounce": 1, "catch": { "received-spf": "pass (example.org: best guess record for domain of mx0.example.jp designates 192.0.2.9 as permitted sender) client-ip=192.0.2.9;", "queue-id": "FFFFFF000000", }, "rhost": "192.0.2.135", "timezoneoffset": "+0900", "addresser": "shironeko@example.co.jp", "diagnostictype": "SMTP", "action": "failed", "listid": "", "feedbacktype": "", "diagnosticcode": "192.0.2.135 does not like recipient. Remote host said: 450 4.2.2... Mailbox Full Giving up on 192.0.2.135. I'm not going to try again; this message has been in the queue too long.", "reason": "mailboxfull", "deliverystatus": "4.2.2", "alias": "", "lhost": "", "messageid": "57D03121-12F7-4801-B30F-9E44B15E56DC@example.co.jp" "origin": "/var/spool/bounce/1.eml" } ]