init III
This commit is contained in:
557
Perl OTRS/Kernel/cpan-lib/Sisimai/Reason.pm
Normal file
557
Perl OTRS/Kernel/cpan-lib/Sisimai/Reason.pm
Normal file
@@ -0,0 +1,557 @@
|
||||
package Sisimai::Reason;
|
||||
use feature ':5.10';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $GetRetried = __PACKAGE__->retry;
|
||||
my $ClassOrder = [
|
||||
[qw|MailboxFull MesgTooBig ExceedLimit Suspend HasMoved NoRelaying UserUnknown
|
||||
Filtered Rejected HostUnknown SpamDetected TooManyConn Blocked
|
||||
|],
|
||||
[qw|MailboxFull SpamDetected PolicyViolation VirusDetected SecurityError
|
||||
SystemError NetworkError Suspend Expired ContentError SystemFull
|
||||
NotAccept MailerError
|
||||
|],
|
||||
[qw|MailboxFull MesgTooBig ExceedLimit Suspend UserUnknown Filtered Rejected
|
||||
HostUnknown SpamDetected TooManyConn Blocked SpamDetected SecurityError
|
||||
SystemError NetworkError Suspend Expired ContentError HasMoved SystemFull
|
||||
NotAccept MailerError NoRelaying SyntaxError OnHold
|
||||
|],
|
||||
];
|
||||
sub retry {
|
||||
# Reason list better to retry detecting an error reason
|
||||
# @return [Array] Reason list
|
||||
return [qw|undefined onhold systemerror securityerror networkerror hostunknown userunknown|];
|
||||
}
|
||||
|
||||
sub index {
|
||||
# All the error reason list Sisimai support
|
||||
# @return [Array] Reason list
|
||||
return [qw|
|
||||
Blocked ContentError ExceedLimit Expired Filtered HasMoved HostUnknown
|
||||
MailboxFull MailerError MesgTooBig NetworkError NotAccept OnHold
|
||||
Rejected NoRelaying SpamDetected VirusDetected PolicyViolation SecurityError
|
||||
Suspend SystemError SystemFull TooManyConn UserUnknown SyntaxError
|
||||
|];
|
||||
}
|
||||
|
||||
sub get {
|
||||
# Detect the bounce reason
|
||||
# @param [Sisimai::Data] argvs Parsed email object
|
||||
# @return [String, Undef] Bounce reason or Undef if the argument
|
||||
# is missing or invalid object
|
||||
# @see anotherone
|
||||
my $class = shift;
|
||||
my $argvs = shift // return undef;
|
||||
return undef unless ref $argvs eq 'Sisimai::Data';
|
||||
|
||||
unless( grep { $argvs->reason eq $_ } @$GetRetried ) {
|
||||
# Return reason text already decided except reason match with the
|
||||
# regular expression of ->retry() method.
|
||||
return $argvs->reason if $argvs->reason;
|
||||
}
|
||||
return 'delivered' if substr($argvs->deliverystatus, 0, 2) eq '2.';
|
||||
|
||||
my $statuscode = $argvs->deliverystatus || '';
|
||||
my $reasontext = '';
|
||||
my $modulepath = '';
|
||||
|
||||
if( $argvs->diagnostictype eq 'SMTP' || $argvs->diagnostictype eq '' ) {
|
||||
# Diagnostic-Code: SMTP; ... or empty value
|
||||
for my $e ( @{ $ClassOrder->[0] } ) {
|
||||
# Check the value of Diagnostic-Code: and the value of Status:, it is a
|
||||
# deliverystats, with true() method in each Sisimai::Reason::* class.
|
||||
my $p = 'Sisimai::Reason::'.$e;
|
||||
($modulepath = $p) =~ s|::|/|g;
|
||||
require $modulepath.'.pm';
|
||||
|
||||
next unless $p->true($argvs);
|
||||
$reasontext = $p->text;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if( not $reasontext || $reasontext eq 'undefined' ) {
|
||||
# Bounce reason is not detected yet.
|
||||
$reasontext = __PACKAGE__->anotherone($argvs);
|
||||
$reasontext = '' if $reasontext eq 'undefined';
|
||||
$reasontext ||= 'expired' if $argvs->action eq 'delayed';
|
||||
|
||||
unless( $reasontext ) {
|
||||
# Try to match with message patterns in Sisimai::Reason::Vacation
|
||||
require Sisimai::Reason::Vacation;
|
||||
$reasontext = 'vacation' if Sisimai::Reason::Vacation->match(lc $argvs->diagnosticcode);
|
||||
}
|
||||
$reasontext ||= 'onhold' if $argvs->diagnosticcode;
|
||||
$reasontext ||= 'undefined';
|
||||
}
|
||||
return $reasontext;
|
||||
}
|
||||
|
||||
sub anotherone {
|
||||
# Detect the other bounce reason, fall back method for get()
|
||||
# @param [Sisimai::Data] argvs Parsed email object
|
||||
# @return [String, Undef] Bounce reason or Undef if the argument
|
||||
# is missing or invalid object
|
||||
# @see get
|
||||
my $class = shift;
|
||||
my $argvs = shift // return undef;
|
||||
|
||||
return undef unless ref $argvs eq 'Sisimai::Data';
|
||||
return $argvs->reason if $argvs->reason;
|
||||
|
||||
my $diagnostic = lc $argvs->diagnosticcode // '';
|
||||
my $statuscode = $argvs->deliverystatus // '';
|
||||
my $commandtxt = $argvs->smtpcommand // '';
|
||||
my $trytomatch = undef;
|
||||
my $reasontext = '';
|
||||
my $modulepath = '';
|
||||
|
||||
require Sisimai::SMTP::Status;
|
||||
$reasontext = Sisimai::SMTP::Status->name($statuscode);
|
||||
|
||||
TRY_TO_MATCH: while(1) {
|
||||
$trytomatch ||= 1 if $reasontext eq '';
|
||||
$trytomatch ||= 1 if grep { $reasontext eq $_ } @$GetRetried;
|
||||
$trytomatch ||= 1 if $argvs->diagnostictype ne 'SMTP';
|
||||
last unless $trytomatch;
|
||||
|
||||
# Could not decide the reason by the value of Status:
|
||||
for my $e ( @{ $ClassOrder->[1] } ) {
|
||||
# Trying to match with other patterns in Sisimai::Reason::* classes
|
||||
my $p = 'Sisimai::Reason::'.$e;
|
||||
($modulepath = $p) =~ s|::|/|g;
|
||||
require $modulepath.'.pm';
|
||||
|
||||
next unless $p->match($diagnostic);
|
||||
$reasontext = lc $e;
|
||||
last;
|
||||
}
|
||||
|
||||
if( not $reasontext ) {
|
||||
# Check the value of Status:
|
||||
my $v = substr($statuscode, 0, 3);
|
||||
if( $v eq '5.6' || $v eq '4.6' ) {
|
||||
# X.6.0 Other or undefined media error
|
||||
$reasontext = 'contenterror';
|
||||
|
||||
} elsif( $v eq '5.7' || $v eq '4.7' ) {
|
||||
# X.7.0 Other or undefined security status
|
||||
$reasontext = 'securityerror';
|
||||
|
||||
} elsif( $argvs->diagnostictype eq 'X-UNIX' || $argvs->diagnostictype eq 'X-POSTFIX' ) {
|
||||
# Diagnostic-Code: X-UNIX; ...
|
||||
$reasontext = 'mailererror';
|
||||
|
||||
} else {
|
||||
# 50X Syntax Error?
|
||||
require Sisimai::Reason::SyntaxError;
|
||||
$reasontext = 'syntaxerror' if Sisimai::Reason::SyntaxError->true($argvs);
|
||||
}
|
||||
}
|
||||
|
||||
if( not $reasontext ) {
|
||||
# Check the value of Action: field, first
|
||||
if( $argvs->action =~ /\A(?:delayed|expired)/ ) {
|
||||
# Action: delayed, expired
|
||||
$reasontext = 'expired';
|
||||
|
||||
} else {
|
||||
# Check the value of SMTP command
|
||||
if( $commandtxt eq 'EHLO' || $commandtxt eq 'HELO' ) {
|
||||
# Rejected at connection or after EHLO|HELO
|
||||
$reasontext = 'blocked';
|
||||
}
|
||||
}
|
||||
}
|
||||
last(TRY_TO_MATCH);
|
||||
}
|
||||
return $reasontext;
|
||||
}
|
||||
|
||||
sub match {
|
||||
# Detect the bounce reason from given text
|
||||
# @param [String] argv1 Error message
|
||||
# @return [String] Bounce reason
|
||||
my $class = shift;
|
||||
my $argv1 = shift // return undef;
|
||||
|
||||
require Sisimai::SMTP::Status;
|
||||
my $modulepath = '';
|
||||
my $reasontext = '';
|
||||
my $statuscode = Sisimai::SMTP::Status->find($argv1);
|
||||
my $diagnostic = lc $argv1;
|
||||
my $typestring = '';
|
||||
$typestring = uc($1) if uc($argv1) =~ /\A(SMTP|X-.+);/;
|
||||
|
||||
# Diagnostic-Code: SMTP; ... or empty value
|
||||
for my $e ( @{ $ClassOrder->[2] } ) {
|
||||
# Check the value of Diagnostic-Code: and the value of Status:, it is a
|
||||
# deliverystats, with true() method in each Sisimai::Reason::* class.
|
||||
my $p = 'Sisimai::Reason::'.$e;
|
||||
($modulepath = $p) =~ s|::|/|g;
|
||||
require $modulepath.'.pm';
|
||||
|
||||
next unless $p->match($diagnostic);
|
||||
$reasontext = $p->text;
|
||||
last;
|
||||
}
|
||||
|
||||
if( not $reasontext ) {
|
||||
# Check the value of $typestring
|
||||
if( $typestring eq 'X-UNIX' ) {
|
||||
# X-Unix; ...
|
||||
$reasontext = 'mailererror';
|
||||
|
||||
} else {
|
||||
# Detect the bounce reason from "Status:" code
|
||||
$reasontext = Sisimai::SMTP::Status->name($statuscode) || 'undefined';
|
||||
}
|
||||
}
|
||||
return $reasontext;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=encoding utf-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Sisimai::Reason - Detect the bounce reason
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Sisimai::Reason;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Sisimai::Reason detects the bounce reason from the content of Sisimai::Data
|
||||
object as an argument of get() method. This class is called only Sisimai::Data
|
||||
class.
|
||||
|
||||
=head1 CLASS METHODS
|
||||
|
||||
=head2 C<B<get(I<Sisimai::Data Object>)>>
|
||||
|
||||
C<get()> detects the bounce reason.
|
||||
|
||||
=head2 C<B<anotherone(I<Sisimai::Data object>)>>
|
||||
|
||||
C<anotherone()> is a method for detecting the bounce reason, it works as a fall
|
||||
back method of get() and called only from get() method.
|
||||
|
||||
C<match()> detects the bounce reason from given text as a error message.
|
||||
|
||||
=head2 C<B<match(I<String>)>>
|
||||
|
||||
C<match()> is a method for detecting the bounce reason from the string given as
|
||||
an argument of the method. However, this method is low analytical precision.
|
||||
|
||||
=head1 LIST OF BOUNCE REASONS
|
||||
|
||||
C<Sisimai::Reason->get()> detects the reason of bounce with parsing the bounced
|
||||
messages. The following reasons will be set in the value of C<reason> property
|
||||
of Sisimai::Data instance.
|
||||
|
||||
=head2 C<blocked>
|
||||
|
||||
This is the error that SMTP connection was rejected due to a client IP address
|
||||
or a hostname, or the parameter of C<HELO/EHLO> command. This reason has added
|
||||
in Sisimai 4.0.0 and does not exist in any version of bounceHammer.
|
||||
|
||||
<kijitora@example.net>:
|
||||
Connected to 192.0.2.112 but my name was rejected.
|
||||
Remote host said: 501 5.0.0 Invalid domain name
|
||||
|
||||
=head2 C<contenterror>
|
||||
|
||||
This is the error that a destination mail server has rejected email due to
|
||||
header format of the email like the following. Sisimai will set C<contenterror>
|
||||
to the reason of email bounce if the value of Status: field in a bounce email
|
||||
is C<5.6.*>.
|
||||
|
||||
=over
|
||||
|
||||
=item - 8 bit data in message header
|
||||
|
||||
=item - Too many “Received” headers
|
||||
|
||||
=item - Invalid MIME headers
|
||||
|
||||
=back
|
||||
|
||||
... while talking to g5.example.net.:
|
||||
>>> DATA
|
||||
<<< 550 5.6.9 improper use of 8-bit data in message header
|
||||
554 5.0.0 Service unavailable
|
||||
|
||||
=head2 C<delivered>
|
||||
|
||||
This is NOT AN ERROR and means the message you sent has delivered to recipients
|
||||
successfully.
|
||||
|
||||
Final-Recipient: rfc822; kijitora@neko.nyaan.jp
|
||||
Action: deliverable
|
||||
Status: 2.1.5
|
||||
Remote-MTA: dns; home.neko.nyaan.jp
|
||||
Diagnostic-Code: SMTP; 250 2.1.5 OK
|
||||
|
||||
=head2 C<exceedlimit>
|
||||
|
||||
This is the error that a message was rejected due to an email exceeded the
|
||||
limit. The value of D.S.N. is C<5.2.3>. This reason is almost the same as
|
||||
C<MesgTooBig>, we think.
|
||||
|
||||
... while talking to mx.example.org.:
|
||||
>>> MAIL From:<kijitora@example.co.jp> SIZE=16600348
|
||||
<<< 552 5.2.3 Message size exceeds fixed maximum message size (10485760)
|
||||
554 5.0.0 Service unavailable
|
||||
|
||||
=head2 C<expired>
|
||||
|
||||
This is the error that delivery time has expired due to connection failure or
|
||||
network error and the message you sent has been in the queue for long time.
|
||||
|
||||
=head2 C<feedback>
|
||||
|
||||
The message you sent was forwarded to the sender as a complaint message from
|
||||
your mailbox provider. When Sismai has set C<feedback> to the reason, the value
|
||||
of C<feedbacktype> is also set like the following parsed data.
|
||||
|
||||
=head2 C<filtered>
|
||||
|
||||
This is the error that an email has been rejected by a header content after
|
||||
SMTP DATA command.
|
||||
In Japanese cellular phones, the error will incur that a sender's email address
|
||||
or a domain is rejected by recipient's email configuration. Sisimai will set
|
||||
C<filtered> to the reason of email bounce if the value of Status: field in a
|
||||
bounce email is C<5.2.0> or C<5.2.1>.
|
||||
|
||||
This error reason is almost the same as UserUnknown.
|
||||
|
||||
... while talking to mfsmax.ntt.example.ne.jp.:
|
||||
>>> DATA
|
||||
<<< 550 Unknown user kijitora@ntt.example.ne.jp
|
||||
554 5.0.0 Service unavailable
|
||||
|
||||
=head2 C<hasmoved>
|
||||
|
||||
This is the error that a user's mailbox has moved (and is not forwarded
|
||||
automatically). Sisimai will set C<hasmoved> to the reason of email bounce if
|
||||
the value of Status: field in a bounce email is C<5.1.6>.
|
||||
|
||||
<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)
|
||||
|
||||
=head2 C<hostunknown>
|
||||
|
||||
This is the error that a domain part (Right hand side of @ sign) of a
|
||||
recipient's email address does not exist. In many case, the domain part is
|
||||
misspelled, or the domain name has been expired. Sisimai will set C<hostunknown>
|
||||
to the reason of email bounce if the value of Status: field in a bounce mail is
|
||||
C<5.1.2>.
|
||||
|
||||
Your message to the following recipients cannot be delivered:
|
||||
|
||||
<kijitora@example.cat>:
|
||||
<<< No such domain.
|
||||
|
||||
=head2 C<mailboxfull>
|
||||
|
||||
This is the error that a recipient's mailbox is full. Sisimai will set
|
||||
C<mailboxfull> to the reason of email bounce if the value of Status: field in a
|
||||
bounce email is C<4.2.2> or C<5.2.2>.
|
||||
|
||||
Action: failed
|
||||
Status: 5.2.2
|
||||
Diagnostic-Code: smtp;550 5.2.2 <kijitora@example.jp>... Mailbox Full
|
||||
|
||||
=head2 C<mailererror>
|
||||
|
||||
This is the error that a mailer program has not exited successfully or exited
|
||||
unexpectedly on a destination mail server.
|
||||
|
||||
X-Actual-Recipient: X-Unix; |/home/kijitora/mail/catch.php
|
||||
Diagnostic-Code: X-Unix; 255
|
||||
|
||||
=head2 C<mesgtoobig>
|
||||
|
||||
This is the error that a sent email size is too big for a destination mail
|
||||
server. In many case, There are many attachment files with email, or the file
|
||||
size is too large. Sisimai will set C<mesgtoobig> to the reason of email bounce
|
||||
if the value of Status: field in a bounce email is C<5.3.4>.
|
||||
|
||||
Action: failure
|
||||
Status: 553 Exceeded maximum inbound message size
|
||||
|
||||
=head2 C<networkerror>
|
||||
|
||||
This is the error that SMTP connection failed due to DNS look up failure or
|
||||
other network problems. This reason has added in Sisimai 4.1.12 and does not
|
||||
exist in any version of bounceHammer.
|
||||
|
||||
A message is delayed for more than 10 minutes for the following
|
||||
list of recipients:
|
||||
|
||||
kijitora@neko.example.jp: Network error on destination MXs
|
||||
|
||||
=head2 C<norelaying>
|
||||
|
||||
This is the error that SMTP connection rejected with error message
|
||||
C<Relaying Denied>. This reason does not exist in any version of bounceHammer.
|
||||
|
||||
... while talking to mailin-01.mx.example.com.:
|
||||
>>> RCPT To:<kijitora@example.org>
|
||||
<<< 554 5.7.1 <kijitora@example.org>: Relay access denied
|
||||
554 5.0.0 Service unavailable
|
||||
|
||||
=head2 C<notaccept>
|
||||
|
||||
This is the error that a destination mail server does ( or can ) not accept any
|
||||
email. In many case, the server is high load or under the maintenance. Sisimai
|
||||
will set C<notaccept> to the reason of email bounce if the value of Status:
|
||||
field in a bounce email is C<5.3.2> or the value of SMTP reply code is 556.
|
||||
|
||||
=head2 C<onhold>
|
||||
|
||||
Sisimai will set C<onhold> to the reason of email bounce if there is no (or
|
||||
less) detailed information about email bounce for judging the reason.
|
||||
|
||||
=head2 C<policyviolation>
|
||||
|
||||
This is the error that a policy violation was detected on a destination mail
|
||||
server. This reason has been divided from C<securityerror> at Sisimai 4.22.0.
|
||||
|
||||
570 5.7.7 Email not accepted for policy reasons
|
||||
|
||||
=head2 C<rejected>
|
||||
|
||||
This is the error that a connection to destination server was rejected by a
|
||||
sender's email address (envelope from). Sisimai set C<rejected> to the reason
|
||||
of email bounce if the value of Status: field in a bounce email is C<5.1.8> or
|
||||
the connection has been rejected due to the argument of SMTP MAIL command.
|
||||
|
||||
<kijitora@example.org>:
|
||||
Connected to 192.0.2.225 but sender was rejected.
|
||||
Remote host said: 550 5.7.1 <root@nijo.example.jp>... Access denied
|
||||
|
||||
=head2 C<securityerror>
|
||||
|
||||
This is the error that a security violation was detected on a destination mail
|
||||
server. Depends on the security policy on the server, a sender's email address
|
||||
is camouflaged address. Sisimai will set C<securityerror> to the reason of email
|
||||
bounce if the value of Status: field in a bounce email is C<5.7.*>.
|
||||
|
||||
Status: 5.7.0
|
||||
Remote-MTA: DNS; gmail-smtp-in.l.google.com
|
||||
Diagnostic-Code: SMTP; 552-5.7.0 Our system detected an illegal attachment on your message. Please
|
||||
|
||||
=head2 C<spamdetected>
|
||||
|
||||
This is the error that the message you sent was rejected by C<spam> filter which
|
||||
is running on the remote host. This reason has added in Sisimai 4.1.25 and does
|
||||
not exist in any version of bounceHammer.
|
||||
|
||||
Action: failed
|
||||
Status: 5.7.1
|
||||
Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, UBE, id=00000-00-000
|
||||
Last-Attempt-Date: Thu, 9 Apr 2008 23:34:45 +0900 (JST)
|
||||
|
||||
=head2 C<suspend>
|
||||
|
||||
This is the error that a recipient account is being suspended due to unpaid or
|
||||
other reasons.
|
||||
|
||||
=head2 C<syntaxerror>
|
||||
|
||||
This is the error that a destination mail server could not recognize SMTP command
|
||||
which is sent from a sender's MTA. This reason has been added at Sisimai v4.17.0.
|
||||
|
||||
=over
|
||||
|
||||
=item - 503 Improper sequence of commands
|
||||
|
||||
=item - 504 Command parameter not implemented
|
||||
|
||||
=back
|
||||
|
||||
=head2 C<systemerror>
|
||||
|
||||
This is the error that an email has bounced due to system error on the remote
|
||||
host such as LDAP connection failure or other internal system error.
|
||||
|
||||
<kijitora@example.net>:
|
||||
Unable to contact LDAP server. (#4.4.3)I'm not going to try again; this
|
||||
message has been in the queue too long.
|
||||
|
||||
=head2 C<systemfull>
|
||||
|
||||
This is the error that a destination mail server's disk (or spool) is full.
|
||||
Sisimai will set C<systemfull> to the reason of email bounce if the value of
|
||||
Status: field in a bounce email is C<4.3.1> or C<5.3.1>.
|
||||
|
||||
=head2 C<toomanyconn>
|
||||
|
||||
This is the error that SMTP connection was rejected temporarily due to too many
|
||||
concurrency connections to the remote server. This reason has added in Sisimai
|
||||
4.1.26 and does not exist in any version of bounceHammer.
|
||||
|
||||
<kijitora@example.ne.jp>: host mx02.example.ne.jp[192.0.1.20] said:
|
||||
452 4.3.2 Connection rate limit exceeded. (in reply to MAIL FROM command)
|
||||
|
||||
=head2 C<userunknown>
|
||||
|
||||
This is the error that a local part (Left hand side of @ sign) of a recipient's
|
||||
email address does not exist. In many case, a user has changed internet service
|
||||
provider, or has quit company, or the local part is misspelled. Sisimai will set
|
||||
C<userunknown> to the reason of email bounce if the value of Status: field in a
|
||||
bounce email is C<5.1.1>, or connection was refused at SMTP RCPT command, or
|
||||
the contents of Diagnostic-Code: field represents that it is unknown user.
|
||||
|
||||
<kijitora@example.co.jp>: host mx01.example.co.jp[192.0.2.8] said:
|
||||
550 5.1.1 Address rejected kijitora@example.co.jp (in reply to
|
||||
RCPT TO command)
|
||||
|
||||
=head2 C<undefined>
|
||||
|
||||
Sisimai could not detect the error reason. In many case, error message is
|
||||
written in non-English or there are no enough error message in a bounce email
|
||||
to decide the reason.
|
||||
|
||||
=head2 C<vacation>
|
||||
|
||||
This is the reason that the recipient is out of office. The bounce message is
|
||||
generated and returned from auto responder program. This reason has added in
|
||||
Sisimai 4.1.28 and does not exist in any version of bounceHammer.
|
||||
|
||||
=head2 C<virusdetected>
|
||||
|
||||
This is the error that an email was rejected by a virus scanner at a destination
|
||||
mail server. This reason has been divided from C<securityerror> at Sisimai 4.22.
|
||||
|
||||
Your message was infected with a virus. You should download a virus
|
||||
scanner and check your computer for viruses.
|
||||
|
||||
Sender: <sironeko@libsisimai.org>
|
||||
Recipient: <kijitora@example.jp>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Sisimai::ARF>
|
||||
L<http://tools.ietf.org/html/rfc5965>
|
||||
L<https://libsisimai.org/en/reason/>
|
||||
|
||||
=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