1package Mail::DMARC::Report;
2our $VERSION = '1.20190308'; # VERSION
3use strict;
4use warnings;
5
6use Carp;
7use IO::Compress::Gzip;
8use IO::Compress::Zip;
9
10use parent 'Mail::DMARC::Base';
11
12require Mail::DMARC::Report::Aggregate;
13require Mail::DMARC::Report::Send;
14require Mail::DMARC::Report::Store;
15require Mail::DMARC::Report::Receive;
16require Mail::DMARC::Report::URI;
17
18sub compress {
19    my ( $self, $xml_ref ) = @_;
20    croak "xml is not a reference!" if 'SCALAR' ne ref $xml_ref;
21    my $shrunk;
22    my $zipper = {
23        gz  => \&IO::Compress::Gzip::gzip,    # 2013 draft
24        zip => \&IO::Compress::Zip::zip,      # legacy format
25    };
26# WARNING: changes here MAY require updates in SMTP::assemble_message
27#   my $cf = ( time > 1372662000 ) ? 'gz' : 'zip';    # gz after 7/1/13
28    my $cf = 'gz';
29    $zipper->{$cf}->( $xml_ref, \$shrunk ) or croak "unable to compress: $!";
30    return $shrunk;
31}
32
33sub init {
34    my $self = shift;
35    delete $self->{dmarc};
36    delete $self->{aggregate};
37    return;
38}
39
40sub aggregate {
41    my $self = shift;
42    return $self->{aggregate} if ref $self->{aggregate};
43    return $self->{aggregate} = Mail::DMARC::Report::Aggregate->new();
44}
45
46sub dmarc {
47    my $self = shift;
48    return $self->{dmarc};
49}
50
51sub receive {
52    my $self = shift;
53    return $self->{receive} if ref $self->{receive};
54    return $self->{receive} = Mail::DMARC::Report::Receive->new;
55}
56
57sub sendit {
58    my $self = shift;
59    return $self->{sendit} if ref $self->{sendit};
60    return $self->{sendit} = Mail::DMARC::Report::Send->new();
61}
62
63sub store {
64    my $self = shift;
65    return $self->{store} if ref $self->{store};
66    return $self->{store} = Mail::DMARC::Report::Store->new();
67}
68
69sub uri {
70    my $self = shift;
71    return $self->{uri} if ref $self->{uri};
72    return $self->{uri} = Mail::DMARC::Report::URI->new();
73}
74
75sub save_aggregate {
76    my $self = shift;
77    return $self->store->backend->save_aggregate( $self->aggregate );
78}
79
801;
81
82# ABSTRACT: A DMARC report interface
83
84=pod
85
86=head1 NAME
87
88Mail::DMARC::Report - A DMARC report interface
89
90=head1 VERSION
91
92version 1.20190308
93
94=head1 DESCRIPTION
95
96DMARC reports are information that a DMARC implementing Mail Transfer Agent (MTA) sends to Author Domains and also something that an Author Domain owner receives from other DMARC implementing MTAs. Mail::DMARC supports both roles, as a sender and a receiver.
97
98There are two report types, L<aggregate|Mail::DMARC::Report::Aggregate> and forensic.
99
100=head1 Aggregate Reports
101
102See L<Mail::DMARC::Report::Aggregate>
103
104=head2 Forensic Reports
105
106TODO
107
108=head2 Report Sender
109
110See L<Mail::DMARC::Report::Send>
111
112    1. store reports
113    2. bundle aggregated reports
114    3. format report in XML
115    4. gzip the XML
116    5. deliver report to Author Domain
117
118=head2 Report Receiver
119
120See L<Mail::DMARC::Report::Receive>
121
122    1. accept reports via HTTP or SMTP
123    2. parse the compressed XML message
124    3. store the report
125    4. present stored data
126
127=head2 Verify External Destinations
128
129  1.  Extract the host portion of the authority component of the URI.
130       Call this the "destination host".
131
132   2.  Prepend the string "_report._dmarc".
133
134   3.  Prepend the domain name from which the policy was retrieved.
135
136   4.  Query the DNS for a TXT record at the constructed name.  If the
137       result of this request is a temporary DNS error of some kind
138       (e.g., a timeout), the Mail Receiver MAY elect to temporarily
139       fail the delivery so the verification test can be repeated later.
140
141   5.  If the result includes no TXT resource records or multiple TXT
142       resource records, a positive determination of the external
143       reporting relationship cannot be made; stop.
144
145   6.  Parse the result, if any, as a series of "tag=value" pairs, i.e.,
146       the same overall format as the policy record.  In particular, the
147       "v=DMARC1" tag is mandatory and MUST appear first in the list.
148       If at least that tag is present and the record overall is
149       syntactically valid per Section 6.3, then the external reporting
150       arrangement was authorized by the destination ADMD.
151
152   7.  If a "rua" or "ruf" tag is thus discovered, replace the
153       corresponding value extracted from the domain's DMARC policy
154       record with the one found in this record.  This permits the
155       report receiver to override the report destination.  However, to
156       prevent loops or indirect abuse, the overriding URI MUST use the
157       same destination host from the first step.
158
159=head1 ERROR REPORTS
160
16112.2.4.  Error Reports
162
163When a Mail Receiver is unable to complete delivery of a report via
164any of the URIs listed by the Domain Owner, the Mail Receiver SHOULD
165generate an error message.  An attempt MUST be made to send this
166report to all listed "mailto" URIs and MAY also be sent to any or all
167other listed URIs.
168
169The error report MUST be formatted per [MIME].  A text/plain part
170MUST be included that contains field-value pairs such as those found
171in Section 2 of [DSN].  The fields required, which may appear in any
172order, are:
173
174Report-Date:  A [MAIL]-formatted date expression indicating when the transport failure occurred.
175
176Report-Domain:  The domain-name about which the failed report was generated.
177
178Report-ID:  The Report-ID: that the report tried to use.
179
180Report-Size:  The size, in bytes, of the report that was unable to be
181    sent.  This MUST represent the number of bytes that the Mail
182    Receiver attempted to send.  Where more than one transport system
183    was attempted, the sizes may be different; in such cases, separate
184    error reports MUST be generated so that this value matches the
185    actual attempt that was made.  For example, a "mailto" error
186    report would be sent to the "mailto" URIs with one size, while the
187    "https" reports might be POSTed to those URIs with a different
188    size, as they have different transport and encoding requirements.
189
190Submitter:  The domain-name representing the Mail Receiver that generated, but was unable to submit, the report.
191
192Submitting-URI:  The URI(s) to which the Mail Receiver tried, but failed, to submit the report.
193
194An additional text/plain part MAY be included that gives a human-
195readable explanation of the above, and MAY also include a URI that
196can be used to seek assistance.
197
198[NOTE: A more rigorous syntax specification, including ABNF and
199possible registration of a new media type, will be added here when
200more operational experience is acquired.]
201
202=head1 AFRF reports
203
204=head1 IODEF reports
205
206https://datatracker.ietf.org/doc/draft-kucherawy-dmarc-base/?include_text=1
207
208Section 3.5 Out of Scope:
209
210    This first version of DMARC supports only a single reporting format.
211
212=head1 AUTHORS
213
214=over 4
215
216=item *
217
218Matt Simerson <msimerson@cpan.org>
219
220=item *
221
222Davide Migliavacca <shari@cpan.org>
223
224=back
225
226=head1 COPYRIGHT AND LICENSE
227
228This software is copyright (c) 2018 by Matt Simerson.
229
230This is free software; you can redistribute it and/or modify it under
231the same terms as the Perl 5 programming language system itself.
232
233=cut
234
235__END__
236sub {}
237
238