File Coverage

lib/Sisimai/Lhost/GoogleWorkspace.pm
Criterion Covered Total %
statement 50 52 96.1
branch 18 20 90.0
condition 10 10 100.0
subroutine 6 6 100.0
pod 2 2 100.0
total 86 90 95.5


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::GoogleWorkspace;
2 39     39   4473 use parent 'Sisimai::Lhost';
  39         80  
  39         257  
3 39     39   2987 use v5.26;
  39         124  
4 39     39   225 use strict;
  39         65  
  39         1095  
5 39     39   162 use warnings;
  39         126  
  39         31914  
6              
7 1     1 1 7 sub description { "Google Workspace: https://workspace.google.com/" }
8             sub inquire {
9             # Detect an error from Google Workspace (Transfer from the Google Workspace to the destination host)
10             # @param [Hash] mhead Message headers of a bounce email
11             # @param [String] mbody Message body of a bounce email
12             # @return [Hash] Bounce data list and message/rfc822 part
13             # @return [undef] failed to decode or the arguments are missing
14             # @since v4.21.0
15 1050     1050 1 5569 my $class = shift;
16 1050   100     3470 my $mhead = shift // return undef;
17 1049   100     3156 my $mbody = shift // return undef;
18              
19 1048 100 100     8939 return undef if index($$mbody, "\nDiagnostic-Code:") > -1 || index($$mbody, "\nFinal-Recipient:") > -1;
20 299 100       2277 return undef unless rindex($mhead->{'from'}, '') > -1;
21 152 50       870 return undef unless index($mhead->{'subject'}, "Delivery Status Notification") > -1;
22              
23 152         350 state $indicators = __PACKAGE__->INDICATORS;
24 152         300 state $boundaries = ["Content-Type: message/rfc822", "Content-Type: text/rfc822-headers"];
25 152         370 state $startingof = {
26             'message' => ["** "],
27             'error' => ["The response was:", "The response from the remote server was:"],
28             };
29 152         372 state $messagesof = {
30             "userunknown" => ["because the address couldn't be found. Check for typos or unnecessary spaces and try again."],
31             "notaccept" => ["Null MX"],
32             "networkerror" => [" had no relevant answers.", " responded with code NXDOMAIN"],
33             };
34              
35 152         870 my $dscontents = [__PACKAGE__->DELIVERYSTATUS];
36 152         928 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
37 152         345 my $entiremesg = "";
38 152         380 my $readcursor = 0; # (Integer) Points the current cursor position
39 152         300 my $recipients = 0;
40              
41 152         2886 for my $e ( split("\n", $emailparts->[0]) ) {
42             # Read error messages and delivery status lines from the head of the email to the previous
43             # line of the beginning of the original message.
44 7584 100       12001 unless( $readcursor ) {
45             # Beginning of the bounce message or message/delivery-status part
46 7524 100       14441 if( index($e, $startingof->{'message'}->[0]) == 0 ) {
47             # ** Message not delivered **
48 5         16 $readcursor |= $indicators->{'deliverystatus'};
49 5         15 $entiremesg .= $e." ";
50             }
51             }
52 7584 100 100     16135 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
53              
54             # ** Message not delivered **
55             # You're sending this from a different address or alias using the 'Send mail as' feature.
56             # The settings for your 'Send mail as' account are misconfigured or out of date. Check those settings and try resending.
57             # Learn more here: https://support.google.com/mail/?p=CustomFromDenied
58             # The response was:
59             # Unspecified Error (SENT_SECOND_EHLO): Smtp server does not advertise AUTH capability
60 35 100       101 next if index($e, "Content-Type: ") == 0;
61 30         64 $entiremesg .= $e." ";
62             }
63              
64 152         1336 while( $recipients == 0 ) {
65             # Pick the recipient address from the value of To: header of the original message after
66             # Content-Type: message/rfc822 field
67 152 100       430 my $p0 = index($emailparts->[1], "\nTo:"); last if $p0 < 0;
  152         556  
68 5         17 my $p1 = index($emailparts->[1], "\n", $p0 + 2);
69 5         62 my $cv = Sisimai::Address->s3s4(substr($emailparts->[1], $p0 + 4, $p1 - $p0));
70 5         22 $dscontents->[0]->{'recipient'} = $cv;
71 5         20 $recipients++;
72             }
73 152 100       1627 return undef unless $recipients;
74              
75 5         15 $dscontents->[0]->{'diagnosis'} = $entiremesg;
76 5         14 for my $e ( @$dscontents ) {
77             # Tidy up the error message in e.Diagnosis, Try to detect the bounce reason.
78 5         72 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'});
79              
80 5         28 for my $r ( keys %$messagesof ) {
81             # Guess an reason of the bounce
82 15 50       51 next unless grep { index($e->{'diagnosis'}, $_) > -1 } $messagesof->{ $r }->@*;
  20         80  
83 0         0 $e->{'reason'} = $r; last;
  0         0  
84             }
85             }
86 5         41 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
87             }
88              
89             1;
90             __END__