The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Sisimai - Mail Analyzing Interface for bounce mails.

SYNOPSIS

    use Sisimai;

DESCRIPTION

Sisimai is a Mail Analyzing Interface for email bounce, is a Perl module to parse RFC5322 bounce mails and generating structured data as JSON from parsed results.

BASIC USAGE

rise('/path/to/mbox')

rise method provides feature for getting parsed data from bounced email messages like following.

    use Sisimai;
    my $v = Sisimai->rise('/path/to/mbox'); # or Path to Maildir/
    #  $v = Sisimai->rise(\'From Mailer-Daemon ...');

    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;   # shironeko@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/2022-2222.eml

            my $h = $e->damn;               # Convert to HASH reference
            my $j = $e->dump('json');       # Convert to JSON string
            my $y = $e->dump('yaml');       # Convert to YAML string
        }

        # Dump entire list as a JSON
        use JSON '-convert_blessed_universally';
        my $json = JSON->new->allow_blessed->convert_blessed;

        printf "%s\n", $json->encode($v);
    }

If you want to get bounce records which reason is "delivered" or "vacation", set "delivered" or "vacation" option to rise() method like the following:

    my $v = Sisimai->rise('/path/to/mbox', 'delivered' => 1, 'vacation' => 1);

Beginning with v5.0.0, sisimai does not return the reulst which "reason" is "vaction" by default. If you want to get bounce records which reason is "vacation", set "vacation" option to rise() method like the following:

    my $v = Sisimai->rise('/path/to/mbox', 'vacation' => 1);

dump('/path/to/mbox')

dump method provides feature to get parsed data from bounced email as JSON.

    use Sisimai;
    my $v = Sisimai->dump('/path/to/mbox'); # or Path to Maildir
    print $v;                               # JSON string

OTHER WAYS TO PARSE

Read email data from STDIN

If you want to pass email data from STDIN, specify STDIN at the first argument of dump() and rise() method like following command:

    % cat ./path/to/bounce.eml | perl -MSisimai -lE 'print Sisimai->dump(STDIN)'

Callback Feature

For email headers and the body

hook argument has been removed at Sisimai 5.0.0. The first element of c___ argument is the successor of hook argument, and is called as a callback method for entire email message like the following codes:

    my $code = sub {
        my $argv = shift;           # (*Hash)
        my $head = $argv->{'head'}; # (*Hash)  Email headers
        my $body = $argv->{'body'}; # (String) Message body
        my $data = {
            'queue-id'   => '',
            'x-mailer'   => '',
            'precedence' => '',
        };

        for my $e ( 'x-mailer', 'precedence' ) {
            # Read some headers of the bounced mail
            next unless exists $head->{ $e };
            $data->{ $e } = $head->{ $e };
        }

        if( $body =~ /^X-Postfix-Queue-ID:\s*(.+)$/m ) {
            # Message body of the bounced email
            $data->{'queue-id'} = $1;
        }

        return $data;
    };

    my $methods = [$code, undef];
    my $sisimai = Sisimai->rise($path, 'c___' => $methods);
    print $sisimai->[0]->{'catch'}->{'x-mailer'};    # "Apple Mail (2.1283)"
    print $sisimai->[0]->{'catch'}->{'queue-id'};    # "2DAEB222022E"
    print $sisimai->[0]->{'catch'}->{'precedence'};  # "bulk"

For each email file

Beginning from v5.0.0, c___ argument is available at Sisimai-rise()> and Sisimai-dump()> method for callback feature. The argument c___ is an array reference to holding two code references for a callback method. The first element of the c___ is called at Sisimai::Message for dealing the entire message body. The second element of the c___ is called at the end of each email file parsing.

    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 $sisi = $args->{'sisi'}; # (*Array)  List of Sisimai::Fact

        for my $e ( @$sisi ) {
            # Insert custom fields into the parsed results
            $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;
            }

            # Append X-Sisimai-Parsed: header and save into other path
            my $a = sprintf("X-Sisimai-Parsed: %d\n", scalar @$sisi);
            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;
        }

        # Remove the email file in Maildir/ after parsed
        unlink $path if $kind eq 'maildir';

        # Need to not return a value
    };
    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>"

OTHER METHODS

engine()

engine method provides table including parser engine list and it's description.

    use Sisimai;
    my $v = Sisimai->engine();
    for my $e ( keys %$v ) {
        print $e;           # Sisimai::MTA::Sendmail
        print $v->{ $e };   # V8Sendmail: /usr/sbin/sendmail
    }

reason()

reason method provides table including all the reasons Sisimai can detect

    use Sisimai;
    my $v = Sisimai->reason();
    for my $e ( keys %$v ) {
        print $e;           # Blocked
        print $v->{ $e };   # 'Email rejected due to client IP address or a hostname'
    }

match()

match method receives an error message as a string and returns a reason name like the following:

    use Sisimai;
    my $v = '550 5.1.1 User unknown';
    my $r = Sisimai->match($v);
    print $r;   # "userunknown"

version()

version method returns the version number of Sisimai.

    use Sisimai;
    print Sisimai->version; # 4.25.0p5

SEE ALSO

Sisimai::Mail - Mailbox or Maildir object
Sisimai::Fact - Parsed data object
https://libsisimai.org/ - Sisimai — Mail Analyzing Interface Library
https://tools.ietf.org/html/rfc3463 - RFC3463: Enhanced Mail System Status Codes
https://tools.ietf.org/html/rfc3464 - RFC3464: An Extensible Message Format for Delivery Status Notifications
https://tools.ietf.org/html/rfc5321 - RFC5321: Simple Mail Transfer Protocol
https://tools.ietf.org/html/rfc5322 - RFC5322: Internet Message Format

REPOSITORY

https://github.com/sisimai/p5-sisimai - Sisimai on GitHub

WEB SITE

https://libsisimai.org/ - Mail Analyzing Interface Library

https://github.com/sisimai/rb-sisimai - Ruby version of Sisimai

AUTHOR

azumakuniyuki

COPYRIGHT

Copyright (C) 2014-2022 azumakuniyuki, All rights reserved.

LICENSE

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