init III
This commit is contained in:
319
Perl OTRS/Kernel/cpan-lib/Sisimai/Message/JSON.pm
Normal file
319
Perl OTRS/Kernel/cpan-lib/Sisimai/Message/JSON.pm
Normal file
@@ -0,0 +1,319 @@
|
||||
package Sisimai::Message::JSON;
|
||||
use parent 'Sisimai::Message';
|
||||
use feature ':5.10';
|
||||
use strict;
|
||||
use warnings;
|
||||
use Sisimai::Order::JSON;
|
||||
use JSON;
|
||||
|
||||
my $ToBeLoaded = [];
|
||||
my $TryOnFirst = [];
|
||||
my $DefaultSet = Sisimai::Order::JSON->default;
|
||||
my $ObjectKeys = Sisimai::Order::JSON->by('keyname');
|
||||
|
||||
sub make {
|
||||
# Make data structure from decoded JSON object
|
||||
# @param [Hash] argvs Bounce object
|
||||
# @options argvs [Hash] data Decoded JSON
|
||||
# @options argvs [Array] load User defined MTA module list
|
||||
# @options argvs [Array] order The order of MTA modules
|
||||
# @options argvs [Code] hook Reference to callback method
|
||||
# @return [Hash] Resolved data structure
|
||||
my $class = shift;
|
||||
my $argvs = { @_ };
|
||||
|
||||
my $methodargv = {};
|
||||
my $hookmethod = $argvs->{'hook'} || undef;
|
||||
my $bouncedata = undef;
|
||||
my $processing = {
|
||||
'from' => '', # From_ line
|
||||
'header' => {}, # Email header
|
||||
'rfc822' => '', # Original message part
|
||||
'ds' => [], # Parsed data, Delivery Status
|
||||
'catch' => undef, # Data parsed by callback method
|
||||
};
|
||||
|
||||
$methodargv = {
|
||||
'load' => $argvs->{'load'} || [],
|
||||
'order' => $argvs->{'order'} || [],
|
||||
};
|
||||
$ToBeLoaded = __PACKAGE__->load(%$methodargv);
|
||||
$TryOnFirst = __PACKAGE__->makeorder($argvs->{'data'});
|
||||
|
||||
# Rewrite message body for detecting the bounce reason
|
||||
$methodargv = { 'hook' => $hookmethod, 'json' => $argvs->{'data'} };
|
||||
$bouncedata = __PACKAGE__->parse(%$methodargv);
|
||||
|
||||
return undef unless $bouncedata;
|
||||
return undef unless keys %$bouncedata;
|
||||
|
||||
$processing->{'ds'} = $bouncedata->{'ds'};
|
||||
$processing->{'catch'} = $bouncedata->{'catch'};
|
||||
$processing->{'rfc822'} = $bouncedata->{'rfc822'} || {};
|
||||
return $processing;
|
||||
}
|
||||
|
||||
sub load {
|
||||
# Load MTA modules which specified at 'order' and 'load' in the argument
|
||||
# @param [Hash] argvs Module information to be loaded
|
||||
# @options argvs [Array] load User defined MTA module list
|
||||
# @options argvs [Array] order The order of MTA modules
|
||||
# @return [Array] Module list
|
||||
# @since v4.20.0
|
||||
my $class = shift;
|
||||
my $argvs = { @_ };
|
||||
|
||||
my @modulelist = ();
|
||||
my $tobeloaded = [];
|
||||
my $modulepath = '';
|
||||
|
||||
for my $e ('load', 'order') {
|
||||
# The order of MTA modules specified by user
|
||||
next unless exists $argvs->{ $e };
|
||||
next unless ref $argvs->{ $e } eq 'ARRAY';
|
||||
next unless scalar @{ $argvs->{ $e } };
|
||||
|
||||
push @modulelist, @{ $argvs->{'order'} } if $e eq 'order';
|
||||
next unless $e eq 'load';
|
||||
|
||||
# Load user defined MTA module
|
||||
for my $v ( @{ $argvs->{'load'} } ) {
|
||||
# Load user defined MTA module
|
||||
eval {
|
||||
($modulepath = $v) =~ s|::|/|g;
|
||||
require $modulepath.'.pm';
|
||||
};
|
||||
next if $@;
|
||||
push @$tobeloaded, $v;
|
||||
}
|
||||
}
|
||||
|
||||
for my $e ( @modulelist ) {
|
||||
# Append the custom order of MTA modules
|
||||
next if grep { $e eq $_ } @$tobeloaded;
|
||||
push @$tobeloaded, $e;
|
||||
}
|
||||
return $tobeloaded;
|
||||
}
|
||||
|
||||
sub makeorder {
|
||||
# Check the decoded JSON strucutre for detecting MTA modules and returns the
|
||||
# order of modules to be called.
|
||||
# @param [Hash] argvs Decoded JSON object
|
||||
# @return [Array] Order of MTA modules
|
||||
my $class = shift;
|
||||
my $argvs = shift || return [];
|
||||
my $order = [];
|
||||
|
||||
return [] unless ref $argvs eq 'HASH';
|
||||
return [] unless scalar keys %$argvs;
|
||||
|
||||
# Seek some key names from given argument
|
||||
for my $e ( keys %$ObjectKeys ) {
|
||||
# Get MTA module list matched with a specified key
|
||||
next unless exists $argvs->{ $e };
|
||||
|
||||
# Matched and push it into the order list
|
||||
push @$order, @{ $ObjectKeys->{ $e } };
|
||||
last;
|
||||
}
|
||||
return $order;
|
||||
}
|
||||
|
||||
sub parse {
|
||||
# Parse bounce object with each MTA module
|
||||
# @param [Hash] argvs Processing message entity.
|
||||
# @param options argvs [Hash] json Decoded bounce object
|
||||
# @param options argvs [Code] hook Hook method to be called
|
||||
# @return [Hash] Parsed and structured bounce mails
|
||||
my $class = shift;
|
||||
my $argvs = { @_ };
|
||||
|
||||
my $bouncedata = $argvs->{'json'} || {};
|
||||
my $hookmethod = $argvs->{'hook'} || undef;
|
||||
my $havecaught = undef;
|
||||
my $haveloaded = {};
|
||||
my $hasadapted = undef;
|
||||
my $modulepath = undef;
|
||||
|
||||
if( ref $hookmethod eq 'CODE' ) {
|
||||
# Call hook method
|
||||
my $p = {
|
||||
'datasrc' => 'json',
|
||||
'headers' => undef,
|
||||
'message' => undef,
|
||||
'bounces' => $argvs->{'json'},
|
||||
};
|
||||
eval { $havecaught = $hookmethod->($p) };
|
||||
warn sprintf(" ***warning: Something is wrong in hook method:%s", $@) if $@;
|
||||
}
|
||||
|
||||
ADAPTOR: while(1) {
|
||||
# 1. User-Defined Module
|
||||
# 2. MTA Module Candidates to be tried on first
|
||||
# 3. Sisimai::Bite::JSON::*
|
||||
#
|
||||
USER_DEFINED: for my $r ( @$ToBeLoaded ) {
|
||||
# Call user defined MTA modules
|
||||
next if exists $haveloaded->{ $r };
|
||||
eval {
|
||||
($modulepath = $r) =~ s|::|/|g;
|
||||
require $modulepath.'.pm';
|
||||
};
|
||||
if( $@ ) {
|
||||
warn sprintf(" ***warning: Failed to load %s: %s", $r, $@);
|
||||
next;
|
||||
}
|
||||
$hasadapted = $r->adapt($bouncedata);
|
||||
$haveloaded->{ $r } = 1;
|
||||
last(ADAPTOR) if $hasadapted;
|
||||
}
|
||||
|
||||
TRY_ON_FIRST: while( my $r = shift @$TryOnFirst ) {
|
||||
# Try MTA module candidates which are detected from object key names
|
||||
next if exists $haveloaded->{ $r };
|
||||
($modulepath = $r) =~ s|::|/|g;
|
||||
require $modulepath.'.pm';
|
||||
next if $@;
|
||||
|
||||
$hasadapted = $r->adapt($bouncedata);
|
||||
$haveloaded->{ $r } = 1;
|
||||
last(ADAPTOR) if $hasadapted;
|
||||
}
|
||||
|
||||
DEFAULT_LIST: for my $r ( @$DefaultSet ) {
|
||||
# Default order of MTA modules
|
||||
next if exists $haveloaded->{ $r };
|
||||
($modulepath = $r) =~ s|::|/|g;
|
||||
require $modulepath.'.pm';
|
||||
next if $@;
|
||||
|
||||
$hasadapted = $r->adapt($bouncedata);
|
||||
$haveloaded->{ $r } = 1;
|
||||
last(ADAPTOR) if $hasadapted;
|
||||
}
|
||||
last; # as of now, we have no sample json data for coding this block
|
||||
|
||||
} # End of while(ADAPTOR)
|
||||
|
||||
$hasadapted->{'catch'} = $havecaught if $hasadapted;
|
||||
return $hasadapted;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=encoding utf-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Sisimai::Message::JSON - Convert bounce object (decoded JSON) to data structure.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use JSON;
|
||||
use Sisimai::Message;
|
||||
|
||||
my $jsonparser = JSON->new;
|
||||
my $jsonobject = $jsonparser->decode($jsonstring);
|
||||
my $messageobj = Sisimai::Message->new('data' => $jsonobject, 'input' => 'json');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Sisimai::Message::JSON convert bounce object (decode JSON) to data structure.
|
||||
When the email given as a argument of "new()" method is not a decoded JSON, the
|
||||
method returns "undef".
|
||||
|
||||
=head1 CLASS METHODS
|
||||
|
||||
=head2 C<B<new(I<Hash reference>)>>
|
||||
|
||||
C<new()> is a constructor of Sisimai::Message
|
||||
|
||||
my $jsonparser = JSON->new;
|
||||
my $jsonstring = '{"neko":2, "nyaan": "meow", ...}';
|
||||
my $jsonobject = $jsonparser->decode($jsonstring);
|
||||
my $messageobj = Sisimai::Message->new('data' => $jsonobject, 'input' => 'json');
|
||||
|
||||
If you have implemented a custom MTA module and use it, set the value of "load"
|
||||
in the argument of this method as an array reference like following code:
|
||||
|
||||
my $messageobj = Sisimai::Message->new(
|
||||
'data' => $jsonobject,
|
||||
'load' => ['Your::Custom::MTA::Module']
|
||||
'input' => 'json',
|
||||
);
|
||||
|
||||
Beginning from v4.19.0, `hook` argument is available to callback user defined
|
||||
method like the following codes:
|
||||
|
||||
my $callbackto = sub {
|
||||
my $argv = shift;
|
||||
my $data = { 'feedback-id' => '', 'account-id' => '' };
|
||||
my $mesg = $argv->{'message'} || {};
|
||||
|
||||
if( exists $mesg->{'feedbackId'} ) {
|
||||
$data->{'feedback-id'} = $mesg->{'feedback-Id'};
|
||||
}
|
||||
|
||||
if( exists $mesg->{'sendingAccountId'} ) {
|
||||
$data->{'account-id'} = $mesg->{'sendingAccountId'};
|
||||
}
|
||||
return $data;
|
||||
};
|
||||
my $messageobj = Sisimai::Message->new(
|
||||
'data' => $jsonobject,
|
||||
'hook' => $callbackto,
|
||||
'input' => 'json' );
|
||||
print $message->catch->{'feedback-id'}; # 01010157e48fa03f-c7e948fe-...
|
||||
|
||||
=head1 INSTANCE METHODS
|
||||
|
||||
=head2 C<B<(from)>>
|
||||
|
||||
C<from()> returns empty string
|
||||
|
||||
print $message->from; # ''
|
||||
|
||||
=head2 C<B<header()>>
|
||||
|
||||
C<header()> returns empty Hash
|
||||
|
||||
print $message->header; # {}
|
||||
|
||||
=head2 C<B<ds()>>
|
||||
|
||||
C<ds()> returns an array reference which include contents of delivery status.
|
||||
|
||||
for my $e ( @{ $message->ds } ) {
|
||||
print $e->{'status'}; # 5.1.1
|
||||
print $e->{'recipient'};# neko@example.jp
|
||||
}
|
||||
|
||||
=head2 C<B<rfc822()>>
|
||||
|
||||
C<rfc822()> returns a hash reference which include the header part of the original
|
||||
message.
|
||||
|
||||
print $message->rfc822->{'from'}; # cat@example.com
|
||||
print $message->rfc822->{'to'}; # neko@example.jp
|
||||
|
||||
=head2 C<B<catch()>>
|
||||
|
||||
C<catch()> returns any data generated by user-defined method passed at the `hook`
|
||||
argument of new() constructor.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
azumakuniyuki
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (C) 2014-2018 azumakuniyuki, All rights reserved.
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
This software is distributed under The BSD 2-Clause License.
|
||||
|
||||
=cut
|
||||
|
||||
Reference in New Issue
Block a user