File Coverage

blib/lib/Mail/Message/Field/Attribute.pm
Criterion Covered Total %
statement 120 123 97.5
branch 56 62 90.3
condition 15 22 68.1
subroutine 21 21 100.0
pod 10 11 90.9
total 222 239 92.8


line stmt bran cond sub pod time code
1             # Copyrights 2001-2021 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution Mail-Message. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package Mail::Message::Field::Attribute;
10 21     21   914 use vars '$VERSION';
  21         49  
  21         1264  
11             $VERSION = '3.011';
12              
13 21     21   156 use base 'Mail::Reporter';
  21         47  
  21         2695  
14              
15 21     21   148 use strict;
  21         76  
  21         461  
16 21     21   119 use warnings;
  21         90  
  21         642  
17              
18 21     21   771 use Encode ();
  21         11114  
  21         441  
19 21     21   111 use Carp;
  21         51  
  21         1488  
20              
21              
22 21     21   149 use Carp 'cluck';
  21         54  
  21         3843  
23             use overload
24 265     265   1177 '""' => sub {shift->value}
25 158     158   1684 , cmp => sub { my ($self, $other) = @_;
26 158 50 0     903 UNIVERSAL::isa($other, 'Mail::Message::Field')
27             ? (lc($_[0])->name cmp lc($_[1]->name) || $_[0]->value cmp $_[1]->value)
28             : $_[0]->value cmp $_[1]
29             }
30 21     21   169 , fallback => 1;
  21         48  
  21         295  
31              
32              
33              
34             sub new($$@)
35 120     120 1 3297 { my ($class, $attr) = (shift, shift);
36 120 100       364 my $value = @_ % 2 == 1 ? shift : undef;
37 120         483 $class->SUPER::new(attr => $attr, value => $value, @_);
38             }
39              
40             sub init($$)
41 120     120 0 273 { my ($self, $args) = @_;
42 120         448 $self->SUPER::init($args);
43              
44 120         375 my ($attr, $value, $cont) = @$args{ qw/attr value use_continuations/ };
45              
46 120 100       783 my $name = ($attr =~ m/^(.*?)(?:\*\d+)?\*?\s*\=\s*/ ? $1 : $attr);
47 120 50       518 $self->log(WARNING => "Illegal character in parameter name '$name'.")
48             if $name !~ m/^[!#-'*+\-.0-9A-Z^-~]+$/;
49              
50 120         273 $self->{MMFF_name} = $name;
51 120 100       329 $self->{MMFF_usecont} = defined $cont ? $cont : 1;
52 120 100       321 $self->{MMFF_charset} = $args->{charset} if defined $args->{charset};
53 120 100       374 $self->{MMFF_language} = $args->{language} if defined $args->{language};
54              
55 120 100       500 $self->value(defined $value ? "$value" : ''); # enforce stringification
56 120 100       425 $self->addComponent($attr) if $attr ne $name;
57              
58 120         509 $self;
59             }
60              
61             #------------------------------------------
62              
63              
64 69     69 1 872 sub name() { shift->{MMFF_name} }
65              
66              
67             sub value(;$)
68 581     581 1 3594 { my $self = shift;
69 581 100       1229 if(@_)
70 128         262 { delete $self->{MMFF_cont};
71 128         397 return $self->{MMFF_value} = shift;
72             }
73 453 100       2120 exists $self->{MMFF_value} ? $self->{MMFF_value} : $self->decode;
74             }
75              
76              
77             sub addComponent($)
78 81     81 1 1929 { my ($self, $component) = @_;
79 81         212 delete $self->{MMFF_value};
80              
81 81         332 my ($name, $value) = split /\=/, $component, 2;
82 81 100 100     381 if( substr($name, -1) eq '*' && $value =~ m/^([^']*)\'([^']*)\'/ )
83 6 100       27 { $self->{MMFF_charset} = length $1 ? $1 : undef;
84 6 100       20 $self->{MMFF_language} = length $2 ? $2 : undef;
85             }
86              
87 81 100       298 if( $name =~ m/\*([0-9]+)\*?$/ )
88 8         28 { $self->{MMFF_cont}[$1] = $component }
89 73         205 else { $self->{MMFF_cont} = [ $component ] }
90              
91 81         210 $component;
92             }
93              
94              
95 7     7 1 51 sub charset() { shift->{MMFF_charset} }
96              
97              
98 7     7 1 35 sub language() { shift->{MMFF_language} }
99              
100              
101             sub string()
102 63     63 1 6245 { my $self = shift;
103 63   66     298 my $cont = $self->{MMFF_cont} || $self->encode;
104 63 100       498 return @$cont if wantarray;
105 9 50       26 return [] unless @$cont;
106              
107 9         19 local $" = "; ";
108 9         83 "; @$cont";
109             }
110              
111             #------------------------------------------
112              
113              
114             sub encode()
115 48     48 1 95 { my $self = shift;
116 48         123 my $value = $self->{MMFF_value};
117              
118 48         92 my @lines;
119 48         92 my ($pre, $encoded);
120              
121 48   100     265 my $charset = $self->{MMFF_charset} || '';
122 48   100     204 my $lang = $self->{MMFF_language} || '';
123 48         150 my $name = $self->{MMFF_name};
124 48         91 my $cont = $self->{MMFF_usecont};
125              
126 48 100 66     388 if($charset || $lang)
    50          
127 6         18 { $pre = "$name*0*=$charset'$lang'";
128 6         21 $value = Encode::encode($charset, $value, 0);
129 6         3485 $encoded = 1;
130             }
131             elsif(grep m/[^\x20-\x7E]/, $value)
132 0         0 { $pre = "$name*0*=''";
133 0         0 $encoded = 1;
134             }
135             else
136 42         120 { $pre = "$name*0=";
137 42         106 $value =~ s/"/\\"/g;
138 42         88 $encoded = 0;
139             }
140              
141 48 100       157 if($encoded)
    50          
142             { # Use encoding
143 6         46 my @c = split //, $value;
144 6         18 while(@c)
145 169         255 { my $c = shift @c;
146 169 100       462 $c = '%'. sprintf "%02X", ord $c
147             unless $c =~ m/[a-zA-Z0-9]/;
148              
149 169 100 100     398 if($cont && length($pre) + length($c)> 76)
150 2         5 { push @lines, $pre;
151 2         8 $pre = $name . '*' . @lines . '*=' . $c;
152             }
153 167         330 else { $pre .= $c }
154             }
155 6         16 push @lines, $pre;
156             }
157             elsif($cont)
158             { # Simple string, but with continuations
159 42         78 while(1)
160 43         260 { push @lines, $pre.'"'. substr($value, 0, 75-length($pre), '') .'"';
161 43 100       149 last unless length $value;
162 1         4 $pre = $name . '*' . @lines . '=';
163             }
164            
165             }
166             else
167             { # Single string only
168 0         0 push @lines, $pre . $value;
169             }
170              
171 48 100       248 $lines[0] =~ s/\*0// if @lines==1;
172 48         260 $self->{MMFF_cont} = \@lines;
173             }
174              
175              
176             sub decode()
177 80     80 1 168 { my $self = shift;
178 80         158 my $value = '';
179              
180 80         142 foreach my $cont ( @{$self->{MMFF_cont}} )
  80         223  
181 86 100       232 { unless(defined $cont)
182 1         4 { $value .= "[continuation missing]";
183 1         3 next;
184             }
185              
186 85         329 (my $name, local $_) = split /\=/, $cont, 2;
187              
188 85 100       507 if(substr($name, -1) eq '*')
    100          
    100          
189 12         52 { s/^[^']*\'[^']*\'//;
190 12         39 s/\%([a-fA-F0-9]{2})/chr hex $1/ge;
  47         139  
191             }
192 1         5 elsif( s/^\"(.*)\"$/$1/ ) { s/\\\"/"/g }
193 2         7 elsif( s/^\'(.*)\'$/$1/ ) { s/\\\'/'/g }
194              
195 85         262 $value .= $_;
196             }
197              
198 80         184 my $charset = $self->{MMFF_charset};
199 80 100       209 $value = Encode::decode($charset, $value, 0) if $charset;
200              
201 80         1672 $self->{MMFF_value} = $value;
202             }
203              
204             #------------------------------------------
205              
206              
207             sub mergeComponent($)
208 1     1 1 4 { my ($self, $comp) = @_;
209             my $cont = $self->{MMFF_cont}
210 1 50       5 or croak "ERROR: Too late to merge: value already changed.";
211              
212             defined $_ && $self->addComponent($_)
213 1   33     2 foreach @{$comp->{MMFF_cont}};
  1         7  
214              
215 1         10 $self;
216             }
217              
218             1;