File Coverage

lib/Sisimai/Lhost/GoogleWorkspace.pm
Criterion Covered Total %
statement 49 51 96.0
branch 18 20 90.0
condition 10 10 100.0
subroutine 6 6 100.0
pod 2 2 100.0
total 85 89 95.5


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::GoogleWorkspace;
2 40     40   3343 use parent 'Sisimai::Lhost';
  40         66  
  40         227  
3 40     40   2487 use v5.26;
  40         108  
4 40     40   134 use strict;
  40         58  
  40         946  
5 40     40   123 use warnings;
  40         76  
  40         21851  
6              
7 1     1 1 3 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 1048     1048 1 2893 my $class = shift;
16 1048   100     2110 my $mhead = shift // return undef;
17 1047   100     2006 my $mbody = shift // return undef;
18              
19 1046 100 100     4976 return undef if index($$mbody, "\nDiagnostic-Code:") > -1 || index($$mbody, "\nFinal-Recipient:") > -1;
20 297 100       843 return undef unless rindex($mhead->{'from'}, '') > -1;
21 152 50       431 return undef unless index($mhead->{'subject'}, "Delivery Status Notification") > -1;
22              
23 152         238 state $indicators = __PACKAGE__->INDICATORS;
24 152         172 state $boundaries = ["Content-Type: message/rfc822", "Content-Type: text/rfc822-headers"];
25 152         243 state $startingof = {
26             'message' => ["** "],
27             'error' => ["The response was:", "The response from the remote server was:"],
28             };
29 152         183 state $messagesof = {
30             "userunknown" => ["because the address couldn't be found. Check for typos or unnecessary spaces and try again."],
31             };
32              
33 152         480 my $dscontents = [__PACKAGE__->DELIVERYSTATUS];
34 152         579 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
35 152         216 my $entiremesg = "";
36 152         164 my $readcursor = 0; # (Integer) Points the current cursor position
37 152         177 my $recipients = 0;
38              
39 152         1879 for my $e ( split("\n", $emailparts->[0]) ) {
40             # Read error messages and delivery status lines from the head of the email to the previous
41             # line of the beginning of the original message.
42 7584 100       7722 unless( $readcursor ) {
43             # Beginning of the bounce message or message/delivery-status part
44 7524 100       9606 if( index($e, $startingof->{'message'}->[0]) == 0 ) {
45             # ** Message not delivered **
46 5         9 $readcursor |= $indicators->{'deliverystatus'};
47 5         14 $entiremesg .= $e." ";
48             }
49             }
50 7584 100 100     10104 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
51              
52             # ** Message not delivered **
53             # You're sending this from a different address or alias using the 'Send mail as' feature.
54             # The settings for your 'Send mail as' account are misconfigured or out of date. Check those settings and try resending.
55             # Learn more here: https://support.google.com/mail/?p=CustomFromDenied
56             # The response was:
57             # Unspecified Error (SENT_SECOND_EHLO): Smtp server does not advertise AUTH capability
58 35 100       50 next if index($e, "Content-Type: ") == 0;
59 30         42 $entiremesg .= $e." ";
60             }
61              
62 152         755 while( $recipients == 0 ) {
63             # Pick the recipient address from the value of To: header of the original message after
64             # Content-Type: message/rfc822 field
65 152 100       253 my $p0 = index($emailparts->[1], "\nTo:"); last if $p0 < 0;
  152         314  
66 5         15 my $p1 = index($emailparts->[1], "\n", $p0 + 2);
67 5         38 my $cv = Sisimai::Address->s3s4(substr($emailparts->[1], $p0 + 4, $p1 - $p0));
68 5         15 $dscontents->[0]->{'recipient'} = $cv;
69 5         10 $recipients++;
70             }
71 152 100       891 return undef unless $recipients;
72              
73 5         15 $dscontents->[0]->{'diagnosis'} = $entiremesg;
74 5         9 for my $e ( @$dscontents ) {
75 5         12 for my $r ( keys %$messagesof ) {
76             # Guess an reason of the bounce
77 5 50       18 next unless grep { index($e->{'diagnosis'}, $_) > -1 } $messagesof->{ $r }->@*;
  5         23  
78 0         0 $e->{'reason'} = $r; last;
  0         0  
79             }
80             }
81 5         22 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
82             }
83              
84             1;
85             __END__