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-2023 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 22     22   770 use vars '$VERSION';
  22         54  
  22         1269  
11             $VERSION = '3.013';
12              
13 22     22   127 use base 'Mail::Reporter';
  22         51  
  22         2690  
14              
15 22     22   170 use strict;
  22         57  
  22         487  
16 22     22   113 use warnings;
  22         55  
  22         615  
17              
18 22     22   686 use Encode ();
  22         10505  
  22         398  
19 22     22   114 use Carp;
  22         44  
  22         1458  
20              
21              
22 22     22   137 use Carp 'cluck';
  22         54  
  22         3776  
23             use overload
24 363     363   1035 '""' => sub {shift->value}
25 175     175   1722 , cmp => sub { my ($self, $other) = @_;
26 175 50 0     779 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 22     22   170 , fallback => 1;
  22         61  
  22         330  
31              
32              
33              
34             sub new($$@)
35 128     128 1 3906 { my ($class, $attr) = (shift, shift);
36 128 100       390 my $value = @_ % 2 == 1 ? shift : undef;
37 128         466 $class->SUPER::new(attr => $attr, value => $value, @_);
38             }
39              
40             sub init($$)
41 128     128 0 258 { my ($self, $args) = @_;
42 128         378 $self->SUPER::init($args);
43              
44 128         367 my ($attr, $value, $cont) = @$args{ qw/attr value use_continuations/ };
45              
46 128 100       717 my $name = ($attr =~ m/^(.*?)(?:\*\d+)?\*?\s*\=\s*/ ? $1 : $attr);
47 128 50       509 $self->log(WARNING => "Illegal character in parameter name '$name'.")
48             if $name !~ m/^[!#-'*+\-.0-9A-Z^-~]+$/;
49              
50 128         275 $self->{MMFF_name} = $name;
51 128 100       333 $self->{MMFF_usecont} = defined $cont ? $cont : 1;
52 128 100       288 $self->{MMFF_charset} = $args->{charset} if defined $args->{charset};
53 128 100       281 $self->{MMFF_language} = $args->{language} if defined $args->{language};
54              
55 128 100       431 $self->value(defined $value ? "$value" : ''); # enforce stringification
56 128 100       382 $self->addComponent($attr) if $attr ne $name;
57              
58 128         481 $self;
59             }
60              
61             #------------------------------------------
62              
63              
64 74     74 1 1127 sub name() { shift->{MMFF_name} }
65              
66              
67             sub value(;$)
68 704     704 1 4689 { my $self = shift;
69 704 100       1397 if(@_)
70 136         313 { delete $self->{MMFF_cont};
71 136         354 return $self->{MMFF_value} = shift;
72             }
73 568 100       2484 exists $self->{MMFF_value} ? $self->{MMFF_value} : $self->decode;
74             }
75              
76              
77             sub addComponent($)
78 86     86 1 2443 { my ($self, $component) = @_;
79 86         194 delete $self->{MMFF_value};
80              
81 86         312 my ($name, $value) = split /\=/, $component, 2;
82 86 100 100     349 if( substr($name, -1) eq '*' && $value =~ m/^([^']*)\'([^']*)\'/ )
83 6 100       28 { $self->{MMFF_charset} = length $1 ? $1 : undef;
84 6 100       20 $self->{MMFF_language} = length $2 ? $2 : undef;
85             }
86              
87 86 100       233 if( $name =~ m/\*([0-9]+)\*?$/ )
88 8         28 { $self->{MMFF_cont}[$1] = $component }
89 78         193 else { $self->{MMFF_cont} = [ $component ] }
90              
91 86         176 $component;
92             }
93              
94              
95 7     7 1 37 sub charset() { shift->{MMFF_charset} }
96              
97              
98 7     7 1 33 sub language() { shift->{MMFF_language} }
99              
100              
101             sub string()
102 66     66 1 7793 { my $self = shift;
103 66   66     242 my $cont = $self->{MMFF_cont} || $self->encode;
104 66 100       445 return @$cont if wantarray;
105 9 50       21 return [] unless @$cont;
106              
107 9         21 local $" = "; ";
108 9         61 "; @$cont";
109             }
110              
111             #------------------------------------------
112              
113              
114             sub encode()
115 51     51 1 96 { my $self = shift;
116 51         107 my $value = $self->{MMFF_value};
117              
118 51         79 my @lines;
119 51         96 my ($pre, $encoded);
120              
121 51   100     255 my $charset = $self->{MMFF_charset} || '';
122 51   100     198 my $lang = $self->{MMFF_language} || '';
123 51         130 my $name = $self->{MMFF_name};
124 51         90 my $cont = $self->{MMFF_usecont};
125              
126 51 100 66     343 if($charset || $lang)
    50          
127 6         16 { $pre = "$name*0*=$charset'$lang'";
128 6         20 $value = Encode::encode($charset, $value, 0);
129 6         3081 $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 45         115 { $pre = "$name*0=";
137 45         109 $value =~ s/"/\\"/g;
138 45         84 $encoded = 0;
139             }
140              
141 51 100       143 if($encoded)
    50          
142             { # Use encoding
143 6         39 my @c = split //, $value;
144 6         18 while(@c)
145 169         261 { my $c = shift @c;
146 169 100       458 $c = '%'. sprintf "%02X", ord $c
147             unless $c =~ m/[a-zA-Z0-9]/;
148              
149 169 100 100     390 if($cont && length($pre) + length($c)> 76)
150 2         5 { push @lines, $pre;
151 2         8 $pre = $name . '*' . @lines . '*=' . $c;
152             }
153 167         327 else { $pre .= $c }
154             }
155 6         16 push @lines, $pre;
156             }
157             elsif($cont)
158             { # Simple string, but with continuations
159 45         73 while(1)
160 46         230 { push @lines, $pre.'"'. substr($value, 0, 75-length($pre), '') .'"';
161 46 100       133 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 51 100       236 $lines[0] =~ s/\*0// if @lines==1;
172 51         248 $self->{MMFF_cont} = \@lines;
173             }
174              
175              
176             sub decode()
177 85     85 1 142 { my $self = shift;
178 85         147 my $value = '';
179              
180 85         128 foreach my $cont ( @{$self->{MMFF_cont}} )
  85         220  
181 91 100       217 { unless(defined $cont)
182 1         3 { $value .= "[continuation missing]";
183 1         4 next;
184             }
185              
186 90         298 (my $name, local $_) = split /\=/, $cont, 2;
187              
188 90 100       439 if(substr($name, -1) eq '*')
    100          
    100          
189 12         53 { s/^[^']*\'[^']*\'//;
190 12         29 s/\%([a-fA-F0-9]{2})/chr hex $1/ge;
  47         146  
191             }
192 1         4 elsif( s/^\"(.*)\"$/$1/ ) { s/\\\"/"/g }
193 2         8 elsif( s/^\'(.*)\'$/$1/ ) { s/\\\'/'/g }
194              
195 90         246 $value .= $_;
196             }
197              
198 85         194 my $charset = $self->{MMFF_charset};
199 85 100       201 $value = Encode::decode($charset, $value, 0) if $charset;
200              
201 85         1281 $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       6 or croak "ERROR: Too late to merge: value already changed.";
211              
212             defined $_ && $self->addComponent($_)
213 1   33     3 foreach @{$comp->{MMFF_cont}};
  1         6  
214              
215 1         4 $self;
216             }
217              
218             1;