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-2022 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.03.
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   643 use vars '$VERSION';
  21         43  
  21         1092  
11             $VERSION = '3.012';
12              
13 21     21   116 use base 'Mail::Reporter';
  21         38  
  21         2234  
14              
15 21     21   124 use strict;
  21         44  
  21         361  
16 21     21   95 use warnings;
  21         48  
  21         480  
17              
18 21     21   567 use Encode ();
  21         8532  
  21         352  
19 21     21   90 use Carp;
  21         45  
  21         1303  
20              
21              
22 21     21   113 use Carp 'cluck';
  21         46  
  21         3176  
23             use overload
24 265     265   1202 '""' => sub {shift->value}
25 158     158   1542 , cmp => sub { my ($self, $other) = @_;
26 158 50 0     791 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   138 , fallback => 1;
  21         62  
  21         251  
31              
32              
33              
34             sub new($$@)
35 120     120 1 3196 { my ($class, $attr) = (shift, shift);
36 120 100       352 my $value = @_ % 2 == 1 ? shift : undef;
37 120         464 $class->SUPER::new(attr => $attr, value => $value, @_);
38             }
39              
40             sub init($$)
41 120     120 0 246 { my ($self, $args) = @_;
42 120         337 $self->SUPER::init($args);
43              
44 120         395 my ($attr, $value, $cont) = @$args{ qw/attr value use_continuations/ };
45              
46 120 100       727 my $name = ($attr =~ m/^(.*?)(?:\*\d+)?\*?\s*\=\s*/ ? $1 : $attr);
47 120 50       471 $self->log(WARNING => "Illegal character in parameter name '$name'.")
48             if $name !~ m/^[!#-'*+\-.0-9A-Z^-~]+$/;
49              
50 120         239 $self->{MMFF_name} = $name;
51 120 100       314 $self->{MMFF_usecont} = defined $cont ? $cont : 1;
52 120 100       283 $self->{MMFF_charset} = $args->{charset} if defined $args->{charset};
53 120 100       258 $self->{MMFF_language} = $args->{language} if defined $args->{language};
54              
55 120 100       504 $self->value(defined $value ? "$value" : ''); # enforce stringification
56 120 100       405 $self->addComponent($attr) if $attr ne $name;
57              
58 120         397 $self;
59             }
60              
61             #------------------------------------------
62              
63              
64 69     69 1 949 sub name() { shift->{MMFF_name} }
65              
66              
67             sub value(;$)
68 581     581 1 3319 { my $self = shift;
69 581 100       1041 if(@_)
70 128         193 { delete $self->{MMFF_cont};
71 128         307 return $self->{MMFF_value} = shift;
72             }
73 453 100       1975 exists $self->{MMFF_value} ? $self->{MMFF_value} : $self->decode;
74             }
75              
76              
77             sub addComponent($)
78 81     81 1 1975 { my ($self, $component) = @_;
79 81         161 delete $self->{MMFF_value};
80              
81 81         304 my ($name, $value) = split /\=/, $component, 2;
82 81 100 100     371 if( substr($name, -1) eq '*' && $value =~ m/^([^']*)\'([^']*)\'/ )
83 6 100       19 { $self->{MMFF_charset} = length $1 ? $1 : undef;
84 6 100       19 $self->{MMFF_language} = length $2 ? $2 : undef;
85             }
86              
87 81 100       253 if( $name =~ m/\*([0-9]+)\*?$/ )
88 8         24 { $self->{MMFF_cont}[$1] = $component }
89 73         184 else { $self->{MMFF_cont} = [ $component ] }
90              
91 81         155 $component;
92             }
93              
94              
95 7     7 1 28 sub charset() { shift->{MMFF_charset} }
96              
97              
98 7     7 1 29 sub language() { shift->{MMFF_language} }
99              
100              
101             sub string()
102 63     63 1 5887 { my $self = shift;
103 63   66     286 my $cont = $self->{MMFF_cont} || $self->encode;
104 63 100       438 return @$cont if wantarray;
105 9 50       22 return [] unless @$cont;
106              
107 9         17 local $" = "; ";
108 9         55 "; @$cont";
109             }
110              
111             #------------------------------------------
112              
113              
114             sub encode()
115 48     48 1 85 { my $self = shift;
116 48         111 my $value = $self->{MMFF_value};
117              
118 48         81 my @lines;
119 48         93 my ($pre, $encoded);
120              
121 48   100     246 my $charset = $self->{MMFF_charset} || '';
122 48   100     185 my $lang = $self->{MMFF_language} || '';
123 48         120 my $name = $self->{MMFF_name};
124 48         91 my $cont = $self->{MMFF_usecont};
125              
126 48 100 66     397 if($charset || $lang)
    50          
127 6         16 { $pre = "$name*0*=$charset'$lang'";
128 6         19 $value = Encode::encode($charset, $value, 0);
129 6         2501 $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         108 { $pre = "$name*0=";
137 42         109 $value =~ s/"/\\"/g;
138 42         133 $encoded = 0;
139             }
140              
141 48 100       167 if($encoded)
    50          
142             { # Use encoding
143 6         30 my @c = split //, $value;
144 6         13 while(@c)
145 169         214 { my $c = shift @c;
146 169 100       343 $c = '%'. sprintf "%02X", ord $c
147             unless $c =~ m/[a-zA-Z0-9]/;
148              
149 169 100 100     317 if($cont && length($pre) + length($c)> 76)
150 2         3 { push @lines, $pre;
151 2         7 $pre = $name . '*' . @lines . '*=' . $c;
152             }
153 167         266 else { $pre .= $c }
154             }
155 6         13 push @lines, $pre;
156             }
157             elsif($cont)
158             { # Simple string, but with continuations
159 42         60 while(1)
160 43         266 { push @lines, $pre.'"'. substr($value, 0, 75-length($pre), '') .'"';
161 43 100       147 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       246 $lines[0] =~ s/\*0// if @lines==1;
172 48         228 $self->{MMFF_cont} = \@lines;
173             }
174              
175              
176             sub decode()
177 80     80 1 156 { my $self = shift;
178 80         140 my $value = '';
179              
180 80         121 foreach my $cont ( @{$self->{MMFF_cont}} )
  80         199  
181 86 100       222 { unless(defined $cont)
182 1         3 { $value .= "[continuation missing]";
183 1         3 next;
184             }
185              
186 85         322 (my $name, local $_) = split /\=/, $cont, 2;
187              
188 85 100       493 if(substr($name, -1) eq '*')
    100          
    100          
189 12         40 { s/^[^']*\'[^']*\'//;
190 12         26 s/\%([a-fA-F0-9]{2})/chr hex $1/ge;
  47         106  
191             }
192 1         4 elsif( s/^\"(.*)\"$/$1/ ) { s/\\\"/"/g }
193 2         7 elsif( s/^\'(.*)\'$/$1/ ) { s/\\\'/'/g }
194              
195 85         240 $value .= $_;
196             }
197              
198 80         169 my $charset = $self->{MMFF_charset};
199 80 100       207 $value = Encode::decode($charset, $value, 0) if $charset;
200              
201 80         1109 $self->{MMFF_value} = $value;
202             }
203              
204             #------------------------------------------
205              
206              
207             sub mergeComponent($)
208 1     1 1 3 { my ($self, $comp) = @_;
209             my $cont = $self->{MMFF_cont}
210 1 50       4 or croak "ERROR: Too late to merge: value already changed.";
211              
212             defined $_ && $self->addComponent($_)
213 1   33     2 foreach @{$comp->{MMFF_cont}};
  1         5  
214              
215 1         4 $self;
216             }
217              
218             1;