File Coverage

lib/Mail/AuthenticationResults/Header/Base.pm
Criterion Covered Total %
statement 266 267 98.5
branch 131 146 92.4
condition 33 39 84.6
subroutine 40 41 97.5
pod 25 25 100.0
total 495 518 95.7


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 30     30   289 use strict;
  30         57  
  30         1100  
6 30     30   143 use warnings;
  30         48  
  30         2167  
7             our $VERSION = '2.20260216'; # VERSION
8 30     30   176 use Scalar::Util qw{ weaken refaddr };
  30         73  
  30         2086  
9 30     30   24030 use JSON;
  30         472677  
  30         245  
10 30     30   5058 use Carp;
  30         68  
  30         2410  
11 30     30   20693 use Clone qw{ clone };
  30         14970  
  30         2358  
12              
13 30     30   16649 use Mail::AuthenticationResults::Header::Group;
  30         88  
  30         1054  
14 30     30   15727 use Mail::AuthenticationResults::FoldableHeader;
  30         98  
  30         1374  
15              
16 30     30   13493 use Mail::AuthenticationResults::Header::Entry;
  30         82  
  30         1004  
17 30     30   13713 use Mail::AuthenticationResults::Header::SubEntry;
  30         84  
  30         1101  
18 30     30   12815 use Mail::AuthenticationResults::Header::Comment;
  30         99  
  30         101076  
19              
20              
21              
22 254     254   2225 sub _HAS_KEY{ return 0; }
23 34     34   3156 sub _HAS_VALUE{ return 0; }
24 285     285   811 sub _HAS_CHILDREN{ return 0; }
25             sub _ALLOWED_CHILDREN{ # uncoverable subroutine
26             # does not run in Base as HAS_CHILDREN returns 0
27 0     0   0 return 0; # uncoverable statement
28             }
29              
30              
31             sub new {
32 1950     1950 1 1780776 my ( $class ) = @_;
33 1950         2626 my $self = {};
34 1950         3011 bless $self, $class;
35 1950         3920 return $self;
36             }
37              
38              
39             sub set_key {
40 389     389 1 7777 my ( $self, $key ) = @_;
41 389 100       990 croak 'Does not have key' if ! $self->_HAS_KEY();
42 385 100       822 croak 'Key cannot be undefined' if ! defined $key;
43 383 100       868 croak 'Key cannot be empty' if $key eq q{};
44 381 100       1040 croak 'Invalid characters in key' if $key =~ /"/;
45 379 100       876 croak 'Invalid characters in key' if $key =~ /\n/;
46 377 100       835 croak 'Invalid characters in key' if $key =~ /\r/;
47 375         1196 $self->{ 'key' } = $key;
48 375         715 return $self;
49             }
50              
51              
52             sub key {
53 1239     1239 1 3374 my ( $self ) = @_;
54 1239 100       3536 croak 'Does not have key' if ! $self->_HAS_KEY();
55 1233 100       2689 return q{} if ! defined $self->{ 'key' }; #5.8
56 1229         3575 return $self->{ 'key' };
57             }
58              
59              
60             sub safe_set_value {
61 39     39 1 2933 my ( $self, $value ) = @_;
62              
63 39 100       192 $value = q{} if ! defined $value;
64              
65 39         100 $value =~ s/\t/ /g;
66 39         63 $value =~ s/\n/ /g;
67 39         63 $value =~ s/\r/ /g;
68 39         62 $value =~ s/\(/ /g;
69 39         58 $value =~ s/\)/ /g;
70 39         68 $value =~ s/\\/ /g;
71 39         51 $value =~ s/"/ /g;
72 39         71 $value =~ s/;/ /g;
73 39         113 $value =~ s/^\s+//;
74 39         106 $value =~ s/\s+$//;
75              
76             #$value =~ s/ /_/g;
77              
78 39         127 $self->set_value( $value );
79 39         104 return $self;
80             }
81              
82              
83             sub set_value {
84 385     385 1 9560 my ( $self, $value ) = @_;
85 385 100       1027 croak 'Does not have value' if ! $self->_HAS_VALUE();
86 381 100       808 croak 'Value cannot be undefined' if ! defined $value;
87             #croak 'Value cannot be empty' if $value eq q{};
88 378 100       1153 croak 'Invalid characters in value' if $value =~ /"/;
89 375 100       940 croak 'Invalid characters in value' if $value =~ /\n/;
90 372 100       937 croak 'Invalid characters in value' if $value =~ /\r/;
91 369         864 $self->{ 'value' } = $value;
92 369         942 return $self;
93             }
94              
95              
96             sub value {
97 1577     1577 1 15398 my ( $self ) = @_;
98 1577 100       3989 croak 'Does not have value' if ! $self->_HAS_VALUE();
99 1576 100       3534 return q{} if ! defined $self->{ 'value' }; # 5.8
100 1517         4691 return $self->{ 'value' };
101             }
102              
103              
104             sub stringify {
105 551     551 1 32248 my ( $self, $value ) = @_;
106 551         979 my $string = $value;
107 551 100       1197 $string = q{} if ! defined $string; #5.8;
108 551         1369 my $strict_quotes = $self->strict_quotes;
109              
110 551 100 100     3956 if ( ( $strict_quotes && $string =~ /[\s\t \(\);=<>@,:\\\/\[\]\?]/ )
      100        
      100        
111             || ( !$strict_quotes && $string =~ /[\s\t \(\);=]/ ) ) {
112 37         85 $string = '"' . $string . '"';
113             }
114              
115 551         2329 return $string;
116             }
117              
118              
119             sub children {
120 3345     3345 1 10291 my ( $self ) = @_;
121 3345 100       6700 croak 'Does not have children' if ! $self->_HAS_CHILDREN();
122 3343 100       9866 return [] if ! defined $self->{ 'children' }; #5.8
123 1336         3492 return $self->{ 'children' };
124             }
125              
126              
127             sub orphan {
128 19     19 1 4519 my ( $self, $parent ) = @_;
129 19 100       122 croak 'Child does not have a parent' if ! exists $self->{ 'parent' };
130 11         16 delete $self->{ 'parent' };
131 11         17 return;
132             }
133              
134              
135             sub copy_children_from {
136 1     1 1 58 my ( $self, $object ) = @_;
137 1         3 for my $original_entry (@{$object->children()}) {
  1         4  
138 1         156 my $entry = clone $original_entry;
139 1 50       20 $entry->orphan if exists $entry->{ 'parent' };;
140 1         5 $self->add_child( $entry );
141             }
142             }
143              
144              
145             sub add_parent {
146 786     786 1 1350 my ( $self, $parent ) = @_;
147 786 100       1810 return if ( ref $parent eq 'Mail::AuthenticationResults::Header::Group' );
148 392 100       887 croak 'Child already has a parent' if exists $self->{ 'parent' };
149 388 50       849 croak 'Cannot add parent' if ! $parent->_ALLOWED_CHILDREN( $self ); # uncoverable branch true
150             # Does not run as test is also done in add_child before add_parent is called.
151 388         693 $self->{ 'parent' } = $parent;
152 388         771 weaken $self->{ 'parent' };
153 388         564 return;
154             }
155              
156              
157             sub parent {
158 2471     2471 1 4039 my ( $self ) = @_;
159 2471         4574 return $self->{ 'parent' };
160             }
161              
162              
163             sub remove_child {
164 16     16 1 3902 my ( $self, $child ) = @_;
165 16 50       35 croak 'Does not have children' if ! $self->_HAS_CHILDREN();
166 16 50       31 croak 'Cannot add child' if ! $self->_ALLOWED_CHILDREN( $child );
167 16 50       32 croak 'Cannot add a class as its own parent' if refaddr $self == refaddr $child; # uncoverable branch true
168             # Does not run as there are no ALLOWED_CHILDREN results which permit this
169              
170 16         18 my @children;
171 16         21 my $child_removed = 0;
172 16         15 foreach my $mychild ( @{ $self->{ 'children' } } ) {
  16         30  
173 39 100       55 if ( refaddr $child == refaddr $mychild ) {
174 16 100       25 if ( ref $self ne 'Mail::AuthenticationResults::Header::Group' ) {
175 10         26 $child->orphan();
176             }
177 16         20 $child_removed = 1;
178             }
179             else {
180 23         30 push @children, $mychild;
181             }
182             }
183 16         19 my $children = $self->{ 'children' };
184              
185 16 50       22 croak 'Not a child of this class' if ! $child_removed;
186              
187 16         66 $self->{ 'children' } = \@children;
188              
189 16         52 return $self;
190             }
191              
192              
193             sub add_child {
194 860     860 1 35498 my ( $self, $child ) = @_;
195 860 100       1989 croak 'Does not have children' if ! $self->_HAS_CHILDREN();
196 831 100       1976 croak 'Cannot add child' if ! $self->_ALLOWED_CHILDREN( $child );
197 806 50       1850 croak 'Cannot add a class as its own parent' if refaddr $self == refaddr $child; # uncoverable branch true
198             # Does not run as there are no ALLOWED_CHILDREN results which permit this
199              
200 806         2624 $child->add_parent( $self );
201 802         1112 push @{ $self->{ 'children' } }, $child;
  802         2342  
202              
203 802         1649 return $child;
204             }
205              
206              
207             sub add_entry {
208 2     2 1 85 my ($self, $key, $value) = @_;
209 2         19 my $child = Mail::AuthenticationResults::Header::Entry->new->set_key($key)->safe_set_value($value);
210 2         17 $self->add_child($child);
211 2         11 return $child;
212             }
213              
214              
215             sub add_sub_entry {
216 2     2 1 781 my ($self, $key, $value) = @_;
217 2         19 my $child = Mail::AuthenticationResults::Header::SubEntry->new->set_key($key)->safe_set_value($value);
218 2         32 $self->add_child($child);
219 1         3 return $child;
220             }
221              
222              
223             sub add_comment {
224 2     2 1 745 my ($self, $value) = @_;
225 2         18 my $child = Mail::AuthenticationResults::Header::Comment->new->safe_set_value($value);
226 2         38 $self->add_child($child);
227 2         10 return $child;
228             }
229              
230              
231             sub ancestor {
232 1256     1256 1 3370 my ( $self ) = @_;
233              
234 1256         1741 my $depth = 0;
235 1256         2620 my $ancestor = $self->parent();
236 1256         1947 my $eldest = $self;
237 1256         2579 while ( defined $ancestor ) {
238 1206         1653 $eldest = $ancestor;
239 1206         2420 $ancestor = $ancestor->parent();
240 1206         2653 $depth++;
241             }
242              
243 1256         2517 return ( $eldest, $depth );
244             }
245              
246              
247             sub strict_quotes {
248 999     999 1 1593 my ( $self ) = @_;
249              
250 999 100       2077 return $self->{ 'strict_quotes' } if defined $self->{ 'strict_quotes' };
251              
252 964         1928 my ( $eldest, $depth ) = $self->ancestor();
253 964 100       2405 return 0 if $depth == 0;
254 448         1019 return $eldest->strict_quotes;
255             }
256              
257              
258             sub set_strict_quotes {
259 2     2 1 7 my ( $self, $value ) = @_;
260 2 50       9 $self->{ 'strict_quotes' } = $value ? 1 : 0;
261 2         7 return $self;
262             }
263              
264              
265             sub as_string_prefix {
266 292     292 1 574 my ( $self, $header ) = @_;
267              
268 292         760 my ( $eldest, $depth ) = $self->ancestor();
269              
270 292         468 my $indents = 1;
271 292 50       1228 if ( $eldest->can( 'indent_by' ) ) {
272 292         890 $indents = $eldest->indent_by();
273             }
274              
275 292         574 my $eol = "\n";
276 292 50       924 if ( $eldest->can( 'eol' ) ) {
277 292         676 $eol = $eldest->eol();
278             }
279              
280 292         613 my $indent = ' ';
281 292         422 my $added = 0;
282 292 50       1028 if ( $eldest->can( 'indent_on' ) ) {
283 292 100       970 if ( $eldest->indent_on( ref $self ) ) {
284 117         424 $header->space( $eol );
285 117         552 $header->space( ' ' x ( $indents * $depth ) );
286 117         218 $added = 1;
287             }
288             }
289 292 100       1316 $header->space( ' ' ) if ! $added;
290              
291 292         636 return $indent;
292             }
293              
294             sub _as_hashref {
295 46     46   114 my ( $self ) = @_;
296              
297 46         116 my $type = lc ref $self;
298 46         266 $type =~ s/^(.*::)//;
299 46         150 my $hashref = { 'type' => $type };
300              
301 46 100       126 $hashref->{'key'} = $self->key() if $self->_HAS_KEY();
302 46 50       143 $hashref->{'value'} = $self->value() if $self->_HAS_VALUE();
303 46 100       116 if ( $self->_HAS_CHILDREN() ) {
304 34         53 my @children = map { $_->_as_hashref() } @{ $self->children() };
  28         77  
  34         82  
305 34         77 $hashref->{'children'} = \@children;
306             }
307 46         133 return $hashref;
308             }
309              
310              
311             sub as_json {
312 2     2 1 36 my ( $self ) = @_;
313 2         64 my $J = JSON->new();
314 2         20 $J->canonical();
315 2         11 return $J->encode( $self->_as_hashref() );
316             }
317              
318              
319             sub as_string {
320 76     76 1 10461 my ( $self ) = @_;
321 76         353 my $header = Mail::AuthenticationResults::FoldableHeader->new();
322 76         242 $self->build_string( $header );
323 76         197 return $header->as_string();
324             }
325              
326              
327             sub build_string {
328 263     263 1 455 my ( $self, $header ) = @_;
329              
330 263 100       766 if ( ! $self->key() ) {
331 2         4 return;
332             }
333              
334 261         582 $header->string( $self->stringify( $self->key() ) );
335 261 100       770 if ( $self->value() ) {
    100          
    50          
336 239         779 $header->assignment( '=' );
337 239         468 $header->string( $self->stringify( $self->value() ) );
338             }
339             elsif ( $self->value() eq '0' ) {
340 2         6 $header->assignment( '=' );
341 2         5 $header->string( '0' );
342             }
343             elsif ( $self->value() eq q{} ) {
344             # special case none here
345 20 100       51 if ( $self->key() ne 'none' ) {
346 18         70 $header->assignment( '=' );
347 18         88 $header->string( '""' );
348             }
349             }
350 261 50       773 if ( $self->_HAS_CHILDREN() ) { # uncoverable branch false
351             # There are no classes which run this code without having children
352 261         365 foreach my $child ( @{$self->children()} ) {
  261         675  
353 197         701 $child->as_string_prefix( $header );
354 197         714 $child->build_string( $header );
355             }
356             }
357 261         679 return;
358             }
359              
360              
361             sub search {
362 1273     1273 1 2434 my ( $self, $search ) = @_;
363              
364 1273         4026 my $group = Mail::AuthenticationResults::Header::Group->new();
365              
366 1273         1845 my $match = 1;
367              
368 1273 100       2691 if ( exists( $search->{ 'key' } ) ) {
369 792 100       1966 if ( $self->_HAS_KEY() ) {
370 574 100 100     2793 if ( ref $search->{ 'key' } eq 'Regexp' && $self->key() =~ m/$search->{'key'}/ ) {
    100          
371 1   50     4 $match = $match && 1; # uncoverable statement
372             # $match is always 1 at this point, left this way for consistency
373             }
374             elsif ( lc $search->{ 'key' } eq lc $self->key() ) {
375 84   50     369 $match = $match && 1; # uncoverable statement
376             # $match is always 1 at this point, left this way for consistency
377             }
378             else {
379 489         746 $match = 0;
380             }
381             }
382             else {
383 218         376 $match = 0;
384             }
385             }
386              
387 1273 100       2726 if ( exists( $search->{ 'value' } ) ) {
388 694 50       1386 $search->{ 'value' } = '' if ! defined $search->{ 'value' };
389 694 100       1562 if ( $self->_HAS_VALUE() ) {
390 675 100 100     2483 if ( ref $search->{ 'value' } eq 'Regexp' && $self->value() =~ m/$search->{'value'}/ ) {
    100          
391 1   50     4 $match = $match && 1;
392             }
393             elsif ( lc $search->{ 'value' } eq lc $self->value() ) {
394 156   100     484 $match = $match && 1;
395             }
396             else {
397 518         809 $match = 0;
398             }
399             }
400             else {
401 19         40 $match = 0; # uncoverable statement
402             # There are no code paths with the current classes which end up here
403             }
404             }
405              
406 1273 100       2606 if ( exists( $search->{ 'authserv_id' } ) ) {
407 87 100       123 if ( $self->_HAS_VALUE() ) {
408 82 100       122 if ( lc ref $self eq 'mail::authenticationresults::header' ) {
409 16   50     15 my $authserv_id = eval{ $self->value()->value() } || q{};
410 16 100 100     53 if ( ref $search->{ 'authserv_id' } eq 'Regexp' && $authserv_id =~ m/$search->{'authserv_id'}/ ) {
    100          
411 2   50     6 $match = $match && 1;
412             }
413             elsif ( lc $search->{ 'authserv_id' } eq lc $authserv_id ) {
414 3   50     8 $match = $match && 1;
415             }
416             else {
417 11         13 $match = 0;
418             }
419             }
420             else {
421 66         58 $match = 0;
422             }
423             }
424             else {
425 5         13 $match = 0; # uncoverable statement
426             # There are no code paths with the current classes which end up here
427             }
428             }
429              
430 1273 100       2423 if ( exists( $search->{ 'isa' } ) ) {
431 447 100 100     2105 if ( lc ref $self eq 'mail::authenticationresults::header::' . lc $search->{ 'isa' } ) {
    100          
432 134   100     469 $match = $match && 1;
433             }
434             elsif ( lc ref $self eq 'mail::authenticationresults::header' && lc $search->{ 'isa' } eq 'header' ) {
435 5   100     17 $match = $match && 1;
436             }
437             else {
438 308         499 $match = 0;
439             }
440             }
441              
442 1273 100       3771 if ( exists( $search->{ 'has' } ) ) {
443 33         32 foreach my $query ( @{ $search->{ 'has' } } ) {
  33         52  
444 38 100       33 $match = 0 if ( scalar @{ $self->search( $query )->children() } == 0 );
  38         63  
445             }
446             }
447              
448 1273 100       2278 if ( $match ) {
449 185         570 $group->add_child( $self );
450             }
451              
452 1273 100       2900 if ( $self->_HAS_CHILDREN() ) {
453 1031         1383 foreach my $child ( @{$self->children()} ) {
  1031         1986  
454 1120         3115 my $childfound = $child->search( $search );
455 1120 100       1533 if ( scalar @{ $childfound->children() } ) {
  1120         1887  
456 214         648 $group->add_child( $childfound );
457             }
458             }
459             }
460              
461 1273         2801 return $group;
462             }
463              
464             1;
465              
466             __END__