File Coverage

blib/lib/Mail/DKIM/KeyValueList.pm
Criterion Covered Total %
statement 101 109 92.6
branch 31 36 86.1
condition 5 8 62.5
subroutine 9 10 90.0
pod 0 7 0.0
total 146 170 85.8


line stmt bran cond sub pod time code
1             package Mail::DKIM::KeyValueList;
2 15     15   137 use strict;
  15         49  
  15         588  
3 15     15   74 use warnings;
  15         29  
  15         9746  
4             our $VERSION = '1.20240923'; # VERSION
5             # ABSTRACT: Represents a Key/Value list
6              
7             # Copyright 2005-2007 Messiah College. All rights reserved.
8             # Jason Long
9              
10             # Copyright (c) 2004 Anthony D. Urso. All rights reserved.
11             # This program is free software; you can redistribute it and/or
12             # modify it under the same terms as Perl itself.
13              
14 15     15   112 use Carp;
  15         27  
  15         30885  
15              
16             sub new {
17 0     0 0 0 my $class = shift;
18 0         0 my %args = @_;
19              
20 0         0 my $self = bless \%args, $class;
21 0         0 return $self;
22             }
23              
24             sub parse {
25 1270     1270 0 2632 my $self_or_class = shift;
26 1270 50       9667 croak 'wrong number of arguments' unless ( @_ == 1 );
27 1270         2828 my ($string) = @_;
28              
29 1270 100       5918 my $self = ref($self_or_class) ? $self_or_class : $self_or_class->new;
30              
31 1270         4301 $self->{tags} = [];
32 1270         5357 $self->{tags_by_name} = {};
33 1270         7018 foreach my $raw_tag ( split /;/, $string, -1 ) {
34 8410         20703 my $tag = { raw => $raw_tag };
35 8410         15262 push @{ $self->{tags} }, $tag;
  8410         20069  
36              
37             # strip preceding and trailing whitespace
38 8410         121082 $raw_tag =~ s/^\s+|\s*$//g;
39              
40 8410 100       20018 next if ( $raw_tag eq '' );
41              
42 8384         37463 my ( $tagname, $value ) = split( /\s*=\s*/, $raw_tag, 2 );
43 8384 100       22083 unless ( defined $value ) {
44 5         76 die "syntax error\n";
45             }
46              
47 8379         20762 $tag->{name} = $tagname;
48 8379         14792 $tag->{value} = $value;
49              
50 8379         23441 $self->{tags_by_name}->{$tagname} = $tag;
51             }
52              
53 1265         5918 return $self;
54             }
55              
56             sub clone {
57 421     421 0 755 my $self = shift;
58 421         1683 my $str = $self->as_string;
59 421         2333 return ref($self)->parse($str);
60             }
61              
62             sub get_tag {
63 25622     25622 0 39017 my $self = shift;
64 25622         45817 my ($tagname) = @_;
65              
66 25622 100       63626 if ( $self->{tags_by_name}->{$tagname} ) {
67 17766         60987 return $self->{tags_by_name}->{$tagname}->{value};
68             }
69 7856         22317 return undef;
70             }
71              
72             sub set_tag {
73 6051     6051 0 9589 my $self = shift;
74 6051         17423 my ( $tagname, $value ) = @_;
75              
76 6051 50       18955 if ( $tagname =~ /[;=\015\012\t ]/ ) {
77 0         0 croak 'invalid tag name';
78             }
79              
80 6051 100       12446 if ( defined $value ) {
81 2738 50       7051 if ( $value =~ /;/ ) {
82 0         0 croak 'invalid tag value';
83             }
84 2738 50       7217 if ( $value =~ /\015\012[^\t ]/ ) {
85 0         0 croak 'invalid tag value';
86             }
87              
88 2738 100       8880 if ( $self->{tags_by_name}->{$tagname} ) {
89 369         1064 $self->{tags_by_name}->{$tagname}->{value} = $value;
90             my ( $rawname, $rawvalue ) =
91 369         1705 split( /=/, $self->{tags_by_name}->{$tagname}->{raw}, 2 );
92 369         1564 $self->{tags_by_name}->{$tagname}->{raw} = "$rawname=$value";
93             }
94             else {
95 2369         11512 my $tag = {
96             name => $tagname,
97             value => $value,
98             raw => " $tagname=$value"
99             };
100 2369         4454 push @{ $self->{tags} }, $tag;
  2369         6838  
101 2369         8512 $self->{tags_by_name}->{$tagname} = $tag;
102             }
103             }
104             else {
105 3313 50       7741 if ( $self->{tags_by_name}->{$tagname} ) {
106 0         0 delete $self->{tags_by_name}->{$tagname};
107             }
108 3313         5022 @{ $self->{tags} } = grep { $_->{name} ne $tagname } @{ $self->{tags} };
  3313         9802  
  4788         12128  
  3313         7888  
109             }
110             }
111              
112             sub as_string {
113 939     939 0 1613 my $self = shift;
114 939 100       2637 if ($Mail::DKIM::SORTTAGS) {
115 96         146 return join( ';', sort map { $_->{raw} } @{ $self->{tags} } );
  736         2783  
  96         256  
116             }
117 843         1639 return join( ';', map { $_->{raw} } @{ $self->{tags} } );
  6844         24497  
  843         2344  
118             }
119              
120             # Start - length of the signature's prefix
121             # Margin - how far to the right the text can go
122             # Insert - characters to insert when wrapping a line
123             # Tags - special processing for tags
124             # Default - how to handle unspecified tags
125             # PreserveNames - if set, the name= part of the tag will be preserved
126             sub wrap {
127 129     129 0 238 my $self = shift;
128 129         614 my %args = @_;
129              
130 129         256 my $TEXTWRAP_CLASS = 'Mail::DKIM::TextWrap';
131 129 100       1360 return unless ( UNIVERSAL::can( $TEXTWRAP_CLASS, 'new' ) );
132              
133 3         13 my $result = '';
134             my $wrap = $TEXTWRAP_CLASS->new(
135             Output => \$result,
136             Separator => $args{Insert} || "\015\012\t",
137             Margin => $args{Margin} || 72,
138 3   50     37 cur => $args{Start} || 0,
      50        
      50        
139             );
140 3         5 my $did_first;
141 3         6 foreach my $tag ( @{ $self->{tags} } ) {
  3         13  
142 20         72 my $tagname = $tag->{name};
143 20   100     101 my $tagtype = $args{Tags}->{$tagname} || $args{Default} || '';
144              
145 20         51 $wrap->{Break} = undef;
146 20         31 $wrap->{BreakBefore} = undef;
147 20 100       64 $did_first ? $wrap->add(';') : ( $did_first = 1 );
148              
149 20         72 my ( $raw_name, $raw_value ) = split( /=/, $tag->{raw}, 2 );
150 20 100       52 unless ( $args{PreserveNames} ) {
151 6         21 $wrap->flush; #allow a break before the tag name
152 6         29 $raw_name =~ s/^\s*/ /;
153 6         28 $raw_name =~ s/\s+$//;
154             }
155 20         73 $wrap->add( $raw_name . '=' );
156              
157 20 100       87 if ( $tagtype eq 'b64' ) {
    100          
    100          
158 3         31 $raw_value =~ s/\s+//gs; #removes all whitespace
159 3         13 $wrap->flush;
160 3         15 $wrap->{Break} = qr/./;
161             }
162             elsif ( $tagtype eq 'list' ) {
163 1         16 $raw_value =~ s/\s+/ /gs; #reduces any whitespace to single space
164 1         38 $raw_value =~ s/^\s|\s$//g; #trims preceding/trailing spaces
165 1         14 $raw_value =~ s/\s*:\s*/:/g;
166 1         6 $wrap->flush;
167 1         4 $wrap->{Break} = qr/[\s]/;
168 1         5 $wrap->{BreakBefore} = qr/[:]/;
169             }
170             elsif ( $tagtype eq '' ) {
171 4         19 $raw_value =~ s/\s+/ /gs; #reduces any whitespace to single space
172 4         24 $raw_value =~ s/^\s|\s$//g; #trims preceding/trailing spaces
173 4         15 $wrap->flush;
174 4         18 $wrap->{Break} = qr/\s/;
175             }
176 20         54 $wrap->add($raw_value);
177             }
178              
179 3         16 $wrap->finish;
180 3         11 parse( $self, $result );
181 3         40 return;
182             }
183              
184             1;
185              
186             __END__