File Coverage

lib/Mail/AuthenticationResults/Header/Base.pm
Criterion Covered Total %
statement 236 237 98.3
branch 126 140 92.8
condition 24 30 80.0
subroutine 32 33 96.9
pod 20 20 100.0
total 438 460 95.4


line stmt bran cond sub pod time code
1             package Mail::AuthenticationResults::Header::Base;
2             # ABSTRACT: Base class for modelling parts of the Authentication Results Header
3              
4             require 5.008;
5 29     29   235 use strict;
  29         71  
  29         821  
6 29     29   141 use warnings;
  29         50  
  29         1203  
7             our $VERSION = '2.20230112'; # VERSION
8 29     29   204 use Scalar::Util qw{ weaken refaddr };
  29         59  
  29         1770  
9 29     29   19636 use JSON;
  29         381878  
  29         162  
10 29     29   3502 use Carp;
  29         61  
  29         1664  
11 29     29   11398 use Clone qw{ clone };
  29         61998  
  29         1754  
12              
13 29     29   10859 use Mail::AuthenticationResults::Header::Group;
  29         76  
  29         893  
14 29     29   12080 use Mail::AuthenticationResults::FoldableHeader;
  29         68  
  29         79764  
15              
16              
17 254     254   2836 sub _HAS_KEY{ return 0; }
18 34     34   2820 sub _HAS_VALUE{ return 0; }
19 277     277   769 sub _HAS_CHILDREN{ return 0; }
20             sub _ALLOWED_CHILDREN{ # uncoverable subroutine
21             # does not run in Base as HAS_CHILDREN returns 0
22 0     0   0 return 0; # uncoverable statement
23             }
24              
25              
26             sub new {
27 1905     1905 1 29743 my ( $class ) = @_;
28 1905         2647 my $self = {};
29 1905         2839 bless $self, $class;
30 1905         3385 return $self;
31             }
32              
33              
34             sub set_key {
35 383     383 1 10054 my ( $self, $key ) = @_;
36 383 100       838 croak 'Does not have key' if ! $self->_HAS_KEY();
37 379 100       767 croak 'Key cannot be undefined' if ! defined $key;
38 377 100       799 croak 'Key cannot be empty' if $key eq q{};
39 375 100       888 croak 'Invalid characters in key' if $key =~ /"/;
40 373 100       675 croak 'Invalid characters in key' if $key =~ /\n/;
41 371 100       667 croak 'Invalid characters in key' if $key =~ /\r/;
42 369         784 $self->{ 'key' } = $key;
43 369         717 return $self;
44             }
45              
46              
47             sub key {
48 1186     1186 1 2679 my ( $self ) = @_;
49 1186 100       1965 croak 'Does not have key' if ! $self->_HAS_KEY();
50 1180 100       2205 return q{} if ! defined $self->{ 'key' }; #5.8
51 1176         2668 return $self->{ 'key' };
52             }
53              
54              
55             sub safe_set_value {
56 35     35 1 2749 my ( $self, $value ) = @_;
57              
58 35 100       74 $value = q{} if ! defined $value;
59              
60 35         66 $value =~ s/\t/ /g;
61 35         51 $value =~ s/\n/ /g;
62 35         59 $value =~ s/\r/ /g;
63 35         51 $value =~ s/\(/ /g;
64 35         44 $value =~ s/\)/ /g;
65 35         47 $value =~ s/\\/ /g;
66 35         47 $value =~ s/"/ /g;
67 35         48 $value =~ s/;/ /g;
68 35         81 $value =~ s/^\s+//;
69 35         70 $value =~ s/\s+$//;
70              
71             #$value =~ s/ /_/g;
72              
73 35         87 $self->set_value( $value );
74 35         124 return $self;
75             }
76              
77              
78             sub set_value {
79 378     378 1 12053 my ( $self, $value ) = @_;
80 378 100       930 croak 'Does not have value' if ! $self->_HAS_VALUE();
81 374 100       766 croak 'Value cannot be undefined' if ! defined $value;
82             #croak 'Value cannot be empty' if $value eq q{};
83 371 100       939 croak 'Invalid characters in value' if $value =~ /"/;
84 368 100       704 croak 'Invalid characters in value' if $value =~ /\n/;
85 365 100       774 croak 'Invalid characters in value' if $value =~ /\r/;
86 362         708 $self->{ 'value' } = $value;
87 362         821 return $self;
88             }
89              
90              
91             sub value {
92 1510     1510 1 11380 my ( $self ) = @_;
93 1510 100       2567 croak 'Does not have value' if ! $self->_HAS_VALUE();
94 1509 100       3223 return q{} if ! defined $self->{ 'value' }; # 5.8
95 1455         3487 return $self->{ 'value' };
96             }
97              
98              
99             sub stringify {
100 499     499 1 17416 my ( $self, $value ) = @_;
101 499         676 my $string = $value;
102 499 100       1359 $string = q{} if ! defined $string; #5.8;
103              
104 499 100       1371 if ( $string =~ /[\s\t \(\);=]/ ) {
105 34         85 $string = '"' . $string . '"';
106             }
107              
108 499         1329 return $string;
109             }
110              
111              
112             sub children {
113 3202     3202 1 9618 my ( $self ) = @_;
114 3202 100       5073 croak 'Does not have children' if ! $self->_HAS_CHILDREN();
115 3200 100       8673 return [] if ! defined $self->{ 'children' }; #5.8
116 1257         2869 return $self->{ 'children' };
117             }
118              
119              
120             sub orphan {
121 19     19 1 5341 my ( $self, $parent ) = @_;
122 19 100       127 croak 'Child does not have a parent' if ! exists $self->{ 'parent' };
123 11         24 delete $self->{ 'parent' };
124 11         18 return;
125             }
126              
127              
128             sub copy_children_from {
129 1     1 1 44 my ( $self, $object ) = @_;
130 1         3 for my $original_entry (@{$object->children()}) {
  1         3  
131 1         41 my $entry = clone $original_entry;
132 1 50       11 $entry->orphan if exists $entry->{ 'parent' };;
133 1         3 $self->add_child( $entry );
134             }
135             }
136              
137              
138             sub add_parent {
139 748     748 1 1123 my ( $self, $parent ) = @_;
140 748 100       1426 return if ( ref $parent eq 'Mail::AuthenticationResults::Header::Group' );
141 384 100       771 croak 'Child already has a parent' if exists $self->{ 'parent' };
142 380 50       712 croak 'Cannot add parent' if ! $parent->_ALLOWED_CHILDREN( $self ); # uncoverable branch true
143             # Does not run as test is also done in add_child before add_parent is called.
144 380         637 $self->{ 'parent' } = $parent;
145 380         1057 weaken $self->{ 'parent' };
146 380         489 return;
147             }
148              
149              
150             sub parent {
151 732     732 1 934 my ( $self ) = @_;
152 732         1105 return $self->{ 'parent' };
153             }
154              
155              
156             sub remove_child {
157 16     16 1 4535 my ( $self, $child ) = @_;
158 16 50       46 croak 'Does not have children' if ! $self->_HAS_CHILDREN();
159 16 50       41 croak 'Cannot add child' if ! $self->_ALLOWED_CHILDREN( $child );
160 16 50       57 croak 'Cannot add a class as its own parent' if refaddr $self == refaddr $child; # uncoverable branch true
161             # Does not run as there are no ALLOWED_CHILDREN results which permit this
162              
163 16         18 my @children;
164 16         21 my $child_removed = 0;
165 16         22 foreach my $mychild ( @{ $self->{ 'children' } } ) {
  16         37  
166 39 100       79 if ( refaddr $child == refaddr $mychild ) {
167 16 100       34 if ( ref $self ne 'Mail::AuthenticationResults::Header::Group' ) {
168 10         27 $child->orphan();
169             }
170 16         24 $child_removed = 1;
171             }
172             else {
173 23         38 push @children, $mychild;
174             }
175             }
176 16         26 my $children = $self->{ 'children' };
177              
178 16 50       32 croak 'Not a child of this class' if ! $child_removed;
179              
180 16         26 $self->{ 'children' } = \@children;
181              
182 16         56 return $self;
183             }
184              
185              
186             sub add_child {
187 822     822 1 40808 my ( $self, $child ) = @_;
188 822 100       1638 croak 'Does not have children' if ! $self->_HAS_CHILDREN();
189 793 100       1690 croak 'Cannot add child' if ! $self->_ALLOWED_CHILDREN( $child );
190 768 50       2121 croak 'Cannot add a class as its own parent' if refaddr $self == refaddr $child; # uncoverable branch true
191             # Does not run as there are no ALLOWED_CHILDREN results which permit this
192              
193 768         1842 $child->add_parent( $self );
194 764         876 push @{ $self->{ 'children' } }, $child;
  764         2029  
195              
196 764         1428 return $child;
197             }
198              
199              
200             sub ancestor {
201 260     260 1 378 my ( $self ) = @_;
202              
203 260         329 my $depth = 0;
204 260         459 my $ancestor = $self->parent();
205 260         335 my $eldest = $self;
206 260         507 while ( defined $ancestor ) {
207 463         527 $eldest = $ancestor;
208 463         653 $ancestor = $ancestor->parent();
209 463         767 $depth++;
210             }
211              
212 260         465 return ( $eldest, $depth );
213             }
214              
215              
216             sub as_string_prefix {
217 260     260 1 409 my ( $self, $header ) = @_;
218              
219 260         517 my ( $eldest, $depth ) = $self->ancestor();
220              
221 260         327 my $indents = 1;
222 260 50       802 if ( $eldest->can( 'indent_by' ) ) {
223 260         517 $indents = $eldest->indent_by();
224             }
225              
226 260         371 my $eol = "\n";
227 260 50       636 if ( $eldest->can( 'eol' ) ) {
228 260         487 $eol = $eldest->eol();
229             }
230              
231 260         438 my $indent = ' ';
232 260         335 my $added = 0;
233 260 50       644 if ( $eldest->can( 'indent_on' ) ) {
234 260 100       593 if ( $eldest->indent_on( ref $self ) ) {
235 89         234 $header->space( $eol );
236 89         310 $header->space( ' ' x ( $indents * $depth ) );
237 89         141 $added = 1;
238             }
239             }
240 260 100       740 $header->space( ' ' ) if ! $added;
241              
242 260         425 return $indent;
243             }
244              
245             sub _as_hashref {
246 46     46   66 my ( $self ) = @_;
247              
248 46         89 my $type = lc ref $self;
249 46         166 $type =~ s/^(.*::)//;
250 46         96 my $hashref = { 'type' => $type };
251              
252 46 100       102 $hashref->{'key'} = $self->key() if $self->_HAS_KEY();
253 46 50       88 $hashref->{'value'} = $self->value() if $self->_HAS_VALUE();
254 46 100       97 if ( $self->_HAS_CHILDREN() ) {
255 34         38 my @children = map { $_->_as_hashref() } @{ $self->children() };
  28         64  
  34         76  
256 34         63 $hashref->{'children'} = \@children;
257             }
258 46         102 return $hashref;
259             }
260              
261              
262             sub as_json {
263 2     2 1 15 my ( $self ) = @_;
264 2         33 my $J = JSON->new();
265 2         13 $J->canonical();
266 2         9 return $J->encode( $self->_as_hashref() );
267             }
268              
269              
270             sub as_string {
271 74     74 1 10235 my ( $self ) = @_;
272 74         281 my $header = Mail::AuthenticationResults::FoldableHeader->new();
273 74         283 $self->build_string( $header );
274 74         180 return $header->as_string();
275             }
276              
277              
278             sub build_string {
279 237     237 1 392 my ( $self, $header ) = @_;
280              
281 237 100       486 if ( ! $self->key() ) {
282 2         4 return;
283             }
284              
285 235         465 $header->string( $self->stringify( $self->key() ) );
286 235 100       568 if ( $self->value() ) {
    100          
    50          
287 214         573 $header->assignment( '=' );
288 214         402 $header->string( $self->stringify( $self->value() ) );
289             }
290             elsif ( $self->value() eq '0' ) {
291 2         8 $header->assignment( '=' );
292 2         6 $header->string( '0' );
293             }
294             elsif ( $self->value() eq q{} ) {
295             # special case none here
296 19 100       45 if ( $self->key() ne 'none' ) {
297 17         55 $header->assignment( '=' );
298 17         43 $header->string( '""' );
299             }
300             }
301 235 50       526 if ( $self->_HAS_CHILDREN() ) { # uncoverable branch false
302             # There are no classes which run this code without having children
303 235         306 foreach my $child ( @{$self->children()} ) {
  235         443  
304 181         505 $child->as_string_prefix( $header );
305 181         486 $child->build_string( $header );
306             }
307             }
308 235         463 return;
309             }
310              
311              
312             sub search {
313 1238     1238 1 2080 my ( $self, $search ) = @_;
314              
315 1238         2084 my $group = Mail::AuthenticationResults::Header::Group->new();
316              
317 1238         1493 my $match = 1;
318              
319 1238 100       2137 if ( exists( $search->{ 'key' } ) ) {
320 792 100       1508 if ( $self->_HAS_KEY() ) {
321 574 100 100     1575 if ( ref $search->{ 'key' } eq 'Regexp' && $self->key() =~ m/$search->{'key'}/ ) {
    100          
322 1   50     7 $match = $match && 1; # uncoverable statement
323             # $match is always 1 at this point, left this way for consistency
324             }
325             elsif ( lc $search->{ 'key' } eq lc $self->key() ) {
326 84   50     308 $match = $match && 1; # uncoverable statement
327             # $match is always 1 at this point, left this way for consistency
328             }
329             else {
330 489         638 $match = 0;
331             }
332             }
333             else {
334 218         300 $match = 0;
335             }
336             }
337              
338 1238 100       2819 if ( exists( $search->{ 'value' } ) ) {
339 694 50       1235 $search->{ 'value' } = '' if ! defined $search->{ 'value' };
340 694 100       1280 if ( $self->_HAS_VALUE() ) {
341 675 100 100     1810 if ( ref $search->{ 'value' } eq 'Regexp' && $self->value() =~ m/$search->{'value'}/ ) {
    100          
342 1   50     5 $match = $match && 1;
343             }
344             elsif ( lc $search->{ 'value' } eq lc $self->value() ) {
345 156   100     394 $match = $match && 1;
346             }
347             else {
348 518         706 $match = 0;
349             }
350             }
351             else {
352 19         39 $match = 0; # uncoverable statement
353             # There are no code paths with the current classes which end up here
354             }
355             }
356              
357 1238 100       2121 if ( exists( $search->{ 'authserv_id' } ) ) {
358 87 100       168 if ( $self->_HAS_VALUE() ) {
359 82 100       174 if ( lc ref $self eq 'mail::authenticationresults::header' ) {
360 16   50     25 my $authserv_id = eval{ $self->value()->value() } || q{};
361 16 100 100     78 if ( ref $search->{ 'authserv_id' } eq 'Regexp' && $authserv_id =~ m/$search->{'authserv_id'}/ ) {
    100          
362 2   50     10 $match = $match && 1;
363             }
364             elsif ( lc $search->{ 'authserv_id' } eq lc $authserv_id ) {
365 3   50     54 $match = $match && 1;
366             }
367             else {
368 11         29 $match = 0;
369             }
370             }
371             else {
372 66         86 $match = 0;
373             }
374             }
375             else {
376 5         8 $match = 0; # uncoverable statement
377             # There are no code paths with the current classes which end up here
378             }
379             }
380              
381 1238 100       1960 if ( exists( $search->{ 'isa' } ) ) {
382 412 100 100     1776 if ( lc ref $self eq 'mail::authenticationresults::header::' . lc $search->{ 'isa' } ) {
    100          
383 119   100     391 $match = $match && 1;
384             }
385             elsif ( lc ref $self eq 'mail::authenticationresults::header' && lc $search->{ 'isa' } eq 'header' ) {
386 5   100     17 $match = $match && 1;
387             }
388             else {
389 288         396 $match = 0;
390             }
391             }
392              
393 1238 100       1908 if ( exists( $search->{ 'has' } ) ) {
394 33         36 foreach my $query ( @{ $search->{ 'has' } } ) {
  33         54  
395 38 100       44 $match = 0 if ( scalar @{ $self->search( $query )->children() } == 0 );
  38         64  
396             }
397             }
398              
399 1238 100       1882 if ( $match ) {
400 170         427 $group->add_child( $self );
401             }
402              
403 1238 100       2307 if ( $self->_HAS_CHILDREN() ) {
404 1004         1116 foreach my $child ( @{$self->children()} ) {
  1004         1532  
405 1088         2286 my $childfound = $child->search( $search );
406 1088 100       1270 if ( scalar @{ $childfound->children() } ) {
  1088         1519  
407 199         404 $group->add_child( $childfound );
408             }
409             }
410             }
411              
412 1238         2357 return $group;
413             }
414              
415             1;
416              
417             __END__