License Perl CPAN codecov

[!IMPORTANT] 2024年2月2日の時点でこのリポジトリのデフォルトブランチは5-stable (Sisimai 5)になりました。 もし古いバージョンを使いたい場合は4-stable[^1] ブランチを見てください。またmainmasterブランチはもうこのリポジトリでは使用していません。 [^1]: 4系をcloneする場合はgit clone -b 4-stable https://github.com/sisimai/p5-sisimai.git

[!CAUTION] Sisimai 4.25.14p11およびそれ以前のバージョンには 正規表現に関する脆弱性 ReDoS: CVE-2022-4891があります。 該当するバージョンをお使いの場合はv4.25.14p12以降へアップグレードしてください。

[!WARNING] Sisimai 5はPerl 5.26以上が必要です。インストール/アップグレードを実行する前にperl -vコマンドで システムに入っているPerlのバージョンを確認してください。

[!NOTE] SisimaiはPerlモジュールまたはRuby Gemですが、PHPやPython、GoやRustなどJSONを読める言語であれば どのような環境においても解析結果を得ることでバウンスの発生状況を捉えるのにとても有用です。

What is sisimai

Sisimai(シシマイ)は複雑で多種多様なバウンスメールを解析してバウンスした理由や宛先メールアドレスなど 配信が失敗した結果を構造化データで出力するライブラリでJSONでの出力も可能です

The key features of Sisimai

Command line demo

次の画像のように、Perl版シシマイ(p5-sisimai)はコマンドラインから簡単にバウンスメールを解析すること ができます。

Setting Up Sisimai

System requirements

シシマイの動作環境についての詳細はSisimai | シシマイを使ってみる をご覧ください。

Install

From CPAN

$ cpanm --sudo Sisimai
--> Working on Sisimai
Fetching http://www.cpan.org/authors/id/A/AK/AKXLIX/Sisimai-5.6.0.tar.gz ... OK
...
1 distribution installed
$ perldoc -l Sisimai
/usr/local/lib/perl5/site_perl/5.30.0/Sisimai.pm

From GitHub

[!WARNING] Sisimai 5はPerl 5.26以上が必要です。インストール/アップグレードを実行する前にperl -vコマンドで システムに入っているPerlバージョンを確認してください。

$ perl -v

This is perl 5, version 30, subversion 0 (v5.30.0) built for darwin-2level

Copyright 1987-2019, Larry Wall
...

$ cd /usr/local/src
$ git clone https://github.com/sisimai/p5-sisimai.git
$ cd ./p5-sisimai

$ make install-from-local
./cpanm --sudo . || ( make cpm && ./cpm install --sudo -v . )
--> Working on .
Configuring Sisimai-v5.6.0 ... OK
Building and testing Sisimai-v5.6.0 ... Password: <sudo password here>
OK
Successfully installed Sisimai-v5.6.0
1 distribution installed

$ perl -MSisimai -lE 'print Sisimai->version'
5.6.0

Usage

Basic usage

下記のようにSisimaiのrise()メソッドをmboxかMaildir/のPATHを引数にして実行すると解析結果が配列 リファレンスで返ってきます。v4.25.6から元データとなった電子メールファイルへのPATHを保持するorigin が利用できます。

#! /usr/bin/env perl
use Sisimai;
my $v = Sisimai->rise('/path/to/mbox'); # またはMaildir/へのPATH

# v4.23.0からSisimaiクラスのrise()メソッドとdump()メソッドはPATH以外にもバウンスメール全体を文字列
# として読めるようになりました
use IO::File;
my $r = '';
my $f = IO::File->new('/path/to/mbox'); # またはMaildir/へのPATH
{ local $/ = undef; $r = <$f>; $f->close }
my $v = Sisimai->rise(\$r);

# もし"delivered"(配信成功)となる解析結果も必要な場合は以下に示すとおりrise()メソッドに"delivered"
# オプションを指定してください
my $v = Sisimai->rise('/path/to/mbox', 'delivered' => 1);

# v5.0.0からSisimaiはバウンス理由が"vacation"となる解析結果をデフォルトで返さなくなりました。もし
# "vacation"となる解析結果も必要な場合は次のコードで示すようにrise()メソッドに"vacation"オプション
# を指定してください。
my $v = Sisimai->rise('/path/to/mbox', 'vacation' => 1);

if( defined $v ) {
    for my $e ( @$v ) {
        print ref $e;                   # Sisimai::Fact
        print ref $e->recipient;        # Sisimai::Address
        print ref $e->timestamp;        # Sisimai::Time

        print $e->addresser->address;   # "michitsuna@example.org" # From
        print $e->recipient->address;   # "kijitora@example.jp"    # To
        print $e->recipient->host;      # "example.jp"
        print $e->deliverystatus;       # "5.1.1"
        print $e->replycode;            # "550"
        print $e->reason;               # "userunknown"
        print $e->origin;               # "/var/spool/bounce/new/1740074341.eml"
        print $e->hardbounce;           # 0

        my $h = $e->damn();             # Hashリファレンスに変換
        my $j = $e->dump('json');       # JSON(文字列)に変換
        print $e->dump('json');         # JSON化したバウンスメールの解析結果を表示
    }
}

Convert to JSON

下記のようにSisimaiのdump()メソッドをmboxかMaildir/のPATHを引数にして実行すると解析結果が文字列 (JSON)で返ってきます。

# メールボックスまたはMaildir/から解析した結果をJSONにする
my $j = Sisimai->dump('/path/to/mbox'); # またはMaildir/へのPATH
                                        # dump()メソッドはv4.1.27で追加されました
print $j;                               # JSON化した解析結果を表示

# dump()メソッドは"delivered"オプションや"vacation"オプションも指定可能
my $j = Sisimai->dump('/path/to/mbox', 'delivered' => 1, 'vacation' => 1);

Callback feature

Sisimai->riseSisimai->dumpc___引数(c_が三個/魚用の釣り針に見える)はコールバック機能 で呼び出されるコードリファンレンスを保持する配列リファレンスです。 c___の1番目の要素にはSisimai::Message->siftで呼び出されるコードリファレンスでメールヘッダと本文 に対して行う処理を、2番目の要素には、解析対象のメールファイルに対して行う処理をそれぞれ入れます。

各コードリファレンスで処理した結果はSisimai::Fact->catchを通して得られます。

[0] メールヘッダと本文に対して

c___に渡す配列リファレンスの最初の要素に入れたコードリファレンスはSisimai::Message->sift()で 呼び出されます。

#! /usr/bin/env perl
use Sisimai;
my $code = sub {
    my $args = shift;               # (*Hash)
    my $head = $args->{'headers'};  # (*Hash)  メールヘッダー
    my $body = $args->{'message'};  # (String) メールの本文
    my $adds = { 'x-mailer' => '', 'queue-id' => '' };

    if( $body =~ m/^X-Postfix-Queue-ID:\s*(.+)$/m ) {
        $adds->{'queue-id'} = $1;
    }

    $adds->{'x-mailer'} = $head->{'x-mailer'} || '';
    return $adds;
};
my $data = Sisimai->rise('/path/to/mbox', 'c___' => [$code, undef]);
my $json = Sisimai->dump('/path/to/mbox', 'c___' => [$code, undef]);

print $data->[0]->catch->{'x-mailer'};    # "Apple Mail (2.1283)"
print $data->[0]->catch->{'queue-id'};    # "43f4KX6WR7z1xcMG"

[1] 各メールのファイルに対して

Sisimai->rise()Sisimai->dump()の両メソッドに渡せる引数c___(配列リファレンス)の2番目に入れた コードリファレンスは解析したメールのファイルごとに呼び出されます。

my $path = '/path/to/maildir';
my $code = sub {
    my $args = shift;           # (*Hash)
    my $kind = $args->{'kind'}; # (String)  Sisimai::Mail->kind
    my $mail = $args->{'mail'}; # (*String) Entire email message
    my $path = $args->{'path'}; # (String)  Sisimai::Mail->path
    my $fact = $args->{'fact'}; # (*Array)  List of Sisimai::Fact

    for my $e ( @$fact ) {
        # "catch"アクセサの中に独自の情報を保存する
        $e->{'catch'} ||= {};
        $e->{'catch'}->{'size'} = length $$mail;
        $e->{'catch'}->{'kind'} = ucfirst $kind;

        if( $$mail =~ /^Return-Path: (.+)$/m ) {
            # Return-Path: <MAILER-DAEMON>
            $e->{'catch'}->{'return-path'} = $1;
        }

        # "X-Sisimai-Parsed:"ヘッダーを追加して別のPATHに元メールを保存する
        my $a = sprintf("X-Sisimai-Parsed: %d\n", scalar @$fact);
        my $p = sprintf("/path/to/another/directory/sisimai-%s.eml", $e->token);
        my $f = IO::File->new($p, 'w');
        my $v = $$mail; $v =~ s/^(From:.+)$/$a$1/m;
        print $f $v; $f->close;
    }

    # 解析が終わったらMaildir/にあるファイルを削除する
    unlink $path if $kind eq 'maildir';

    # 特に何か値をReturnする必要はない
};

my $list = Sisimai->rise($path, 'c___' => [undef, $code]);
print $list->[0]->{'catch'}->{'size'};          # 2202
print $list->[0]->{'catch'}->{'kind'};          # "Maildir"
print $list->[0]->{'catch'}->{'return-path'};   # "<MAILER-DAEMON>"

コールバック機能のより詳細な使い方は Sisimai | 解析方法 - コールバック機能をご覧ください。

One-Liner

Sisimai 4.1.27から登場したdump()メソッドを使うとワンライナーでJSON化した解析結果が得られます。

$ perl -MSisimai -lE 'print Sisimai->dump(shift)' /path/to/mbox

Output example

[
  {
    "destination": "google.example.com",
    "lhost": "gmail-smtp-in.l.google.com",
    "hardbounce": 0,
    "reason": "authfailure",
    "catch": null,
    "addresser": "michitsuna@example.jp",
    "alias": "nekochan@example.co.jp",
    "decodedby": "Postfix",
    "command": "DATA",
    "senderdomain": "example.jp",
    "listid": "",
    "action": "failed",
    "feedbacktype": "",
    "messageid": "hwK7pzjzJtz0RF9Y@relay3.example.com",
    "origin": "./gmail-5.7.26.eml",
    "recipient": "kijitora@google.example.com",
    "rhost": "gmail-smtp-in.l.google.com",
    "subject": "Nyaan",
    "timezoneoffset": "+0900",
    "replycode": 550,
    "token": "84656774898baa90660be3e12fe0526e108d4473",
    "toxic": false,
    "diagnostictype": "SMTP",
    "timestamp": 1650119685,
    "diagnosticcode": "host gmail-smtp-in.l.google.com[64.233.187.27] said: This mail has been blocked because the sender is unauthenticated. Gmail requires all senders to authenticate with either SPF or DKIM. Authentication results: DKIM = did not pass SPF [relay3.example.com] with ip: [192.0.2.22] = did not pass For instructions on setting up authentication, go to https://support.google.com/mail/answer/81126#authentication c2-202200202020202020222222cat.127 - gsmtp (in reply to end of DATA command)",
    "deliverystatus": "5.7.26"
  }
]

Differences between Sisimai 4 and Sisimai 5

Sisimai 4.25.16p1Sisimai 5には下記のような違いがあります。 それぞれの詳細はSisimai | 違いの一覧を参照してください。

Features

Sisimai 5.0.0からPerl 5.26.0以上が必要になります。

| 機能 | Sisimai 4 | Sisimai 5 | |------------------------------------------------------|--------------------|---------------------| | 動作環境(Perl) | 5.10 - | 5.26 - | | 元メールファイルを操作可能なコールバック機能 | なし | あり^3 | | 解析エンジン(MTA/ESPモジュール)の数 | 68 | 60 | | 検出可能なバウンス理由の数 | 29 | 34 | | 依存もジュール数(Perlのコアモジュールを除く) | 2 モジュール | 2 モジュール | | ソースコードの行数 | 10,800 行 | 9,750 行 | | テスト件数(t/とxt/ディレクトリ) | 270,000 件 | 340,000 件 | | 1秒間に解析できるバウンスメール数[^4] | 750 通 | 750 通 | | ライセンス | 2条項BSD | 2条項BSD | | 開発会社による商用サポート | 提供中 | 提供中 |

[^4]: macOS Monterey/1.6GHz Dual-Core Intel Core i5/16GB-RAM/Perl 5.30

Decoding Method

いくつかの解析メソッド名、クラス名、パラメーター名がSisimai 5で変更になっています。解析済みデータの 各項目はLIBSISIMAI.ORG/JA/DATAを参照してください。

| 解析用メソッド周辺の変更箇所 | Sisimai 4 | Sisimai 5 | |------------------------------------------------------|--------------------|---------------------| | 解析メソッド名 | Sisimai->make | Sisimai->rise | | 出力メソッド名 | Sisimai->dump | Sisimai->dump | | 解析メソッドが返すオブジェクトのクラス | Sisimai::Data | Sisimai::Fact | | コールバック用のパラメーター名 | hook | c___^5 | | ハードバウンスかソフトバウンスかを識別するメソッド名 | softbounce | hardbounce | | "vacation"をデフォルトで検出するかどうか | 検出する | 検出しない | | Sisimai::Messageがオブジェクトを返すかどうか | 返す | 返さない | | MIME解析用クラスの名前 | Sisimai::MIME | Sisimai::RFC2045 | | SMTPセッションの解析をするかどうか | しない | する^6 |

MTA/ESP Module Names

Sisimai 5で3個のESPモジュール名(解析エンジン)が変更になりました。詳細はMTA/ESPモジュールの一覧/ LIBSISIMAI.ORG/JA/ENGINEを参照してください。

| Sisimai:: | Sisimai 4 | Sisimai 5 | |-------------------------------------------------|-------------------------|---------------------| | Apple iCloud Mail (added at v5.1.0) | なし | Rhost::Apple | | Microsoft Exchange Online | Rhost::ExchangeOnline | Rhost::Microsoft | | Google Workspace | Rhost::GoogleApps | Rhost::Google | | Tencent | Rhost::TencentQQ | Rhost::Tencent | | Yahoo Mail (added at v5.1.0) | なし | Rhost::YahooInc | | Zoho (added at v5.5.0) | なし | Rhost::Zoho | | DragonFly Mail Agent (added at v5.1.0) | なし | Lhost::DragonFly | | Mimecast (added at v5.5.0) | なし | Lhost::Mimecast |

Bounce Reasons

Sisimai 5では新たに5個のバウンス理由が増えました。検出可能なバウンス理由の一覧は LIBSISIMAI.ORG/JA/REASONを参照してください。

| バウンスした理由 | Sisimai 4 | Sisimai 5 | |------------------------------------------------------|--------------------|---------------------| | ドメイン認証によるもの(SPF,DKIM,DMARC) | SecurityError | AuthFailure | | 送信者のドメイン・IPアドレスの低いレピュテーション | Blocked | BadReputation | | PTRレコードが未設定または無効なPTRレコード | Blocked | RequirePTR | | RFCに準拠していないメール^7 | SecurityError | NotCompliantRFC | | STARTTLS関連のエラー (added at v5.2.0) | SecurityError | FailedSTARTTLS | | 単位時間の流量制限・送信速度が速すぎる | SecurityError | RateLimited | | セッションあたりの受信者数制限や接続数を超過 | TooManyConn | RateLimited | | メールが大きすぎる(ExceedLimit) | ExceedLimit | EmailTooLarge | | メールが大きすぎる(MesgTooBig) | MesgTooBig | EmailTooLarge | | 宛先がサプレッションリストに一致 (added at v5.2.0) | OnHold | Suppressed |

Contributing

Bug report

もしもSisimaiにバグを発見した場合はIssuesにて連絡を いただけると助かります。

Emails could not be decoded

Sisimaiで解析できないバウンスメールは set-of-emails/to-be-debugged-because/sisimai-cannot-parse-yetリポジトリに追加してPull-Requestを送ってください。

Other Information

See also

Author

@azumakuniyuki

Copyright

Copyright (C) 2014-2026 azumakuniyuki, All Rights Reserved.

License

This software is distributed under The BSD 2-Clause License.