File Coverage

blib/lib/Mail/Message/Field/Attribute.pm
Criterion Covered Total %
statement 112 115 97.3
branch 54 60 90.0
condition 16 21 76.1
subroutine 19 19 100.0
pod 10 11 90.9
total 211 226 93.3


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution Mail-Message version 4.04.
2             # The POD got stripped from this file by OODoc version 3.06.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2001-2026 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11              
12             package Mail::Message::Field::Attribute;{
13             our $VERSION = '4.04';
14             }
15              
16 28     28   1236 use parent 'Mail::Reporter';
  28         70  
  28         214  
17              
18 28     28   2418 use strict;
  28         121  
  28         987  
19 28     28   186 use warnings;
  28         57  
  28         2006  
20              
21 28     28   219 use Log::Report 'mail-message', import => [ qw/__x error warning/ ];
  28         81  
  28         258  
22              
23 28     28   5475 use Encode ();
  28         62  
  28         5575  
24              
25             #--------------------
26              
27             use overload
28 417     417   1573 '""' => sub { $_[0]->value },
29             cmp => sub {
30 203     203   2867 my ($self, $other) = @_;
31 203 50 0     1312 UNIVERSAL::isa($other, 'Mail::Message::Field')
32             ? (lc($_[0])->name cmp lc($_[1]->name) || $_[0]->value cmp $_[1]->value)
33             : $_[0]->value cmp $_[1];
34             },
35 28     28   256 fallback => 1;
  28         73  
  28         551  
36              
37              
38             #--------------------
39              
40             sub new($$@)
41 133     133 1 284919 { my ($class, $attr) = (shift, shift);
42 133 100       483 my $value = @_ % 2 == 1 ? shift : undef;
43 133         651 $class->SUPER::new(attr => $attr, value => $value, @_);
44             }
45              
46             sub init($$)
47 133     133 0 331 { my ($self, $args) = @_;
48 133         447 $self->SUPER::init($args);
49              
50 133         467 my ($attr, $value, $cont) = @$args{ qw/attr value use_continuations/ };
51              
52 133 100       2456 my $name = ($attr =~ m/^(.*?)(?:\*\d+)?\*?\s*\=\s*/ ? $1 : $attr);
53 133 50       653 warning __x"illegal character in parameter name '{name}'.", name => $name
54             if $name !~ m/^[!#-'*+\-.0-9A-Z^-~]+$/;
55              
56 133         929 $self->{MMFF_name} = $name;
57 133   100     611 $self->{MMFF_usecont} = $cont // 1;
58 133 100       391 $self->{MMFF_charset} = $args->{charset} if defined $args->{charset};
59 133 100       433 $self->{MMFF_language} = $args->{language} if defined $args->{language};
60              
61 133 100       584 $self->value(defined $value ? "$value" : ''); # enforce stringification
62 133 100       628 $self->addComponent($attr) if $attr ne $name;
63              
64 133         643 $self;
65             }
66              
67             #--------------------
68              
69 79     79 1 2773 sub name() { $_[0]->{MMFF_name} }
70              
71              
72             sub value(;$)
73 791     791 1 4740 { my $self = shift;
74 791 100       2566 if(@_)
75 141         280 { delete $self->{MMFF_cont};
76 141         422 return $self->{MMFF_value} = shift;
77             }
78 650 100       3222 exists $self->{MMFF_value} ? $self->{MMFF_value} : $self->decode;
79             }
80              
81              
82             sub addComponent($)
83 91     91 1 2668 { my ($self, $component) = @_;
84 91 50       247 defined $component or return;
85 91         236 delete $self->{MMFF_value};
86              
87 91         523 my ($name, $value) = split /\=/, $component, 2;
88 91 100 100     481 if( substr($name, -1) eq '*' && $value =~ m/^([^']*)\'([^']*)\'/ )
89 6 100       28 { $self->{MMFF_charset} = length $1 ? $1 : undef;
90 6 100       23 $self->{MMFF_language} = length $2 ? $2 : undef;
91             }
92              
93 91 100       282 if( $name =~ m/\*([0-9]+)\*?$/ )
94 8         26 { $self->{MMFF_cont}[$1] = $component }
95 83         280 else { $self->{MMFF_cont} = [ $component ] }
96              
97 91         228 $component;
98             }
99              
100              
101 7     7 1 43 sub charset() { $_[0]->{MMFF_charset} }
102              
103              
104 7     7 1 97 sub language() { $_[0]->{MMFF_language} }
105              
106              
107             sub string()
108 66     66 1 12053 { my $self = shift;
109 66   66     366 my $cont = $self->{MMFF_cont} || $self->encode;
110 66 100       584 wantarray? @$cont : (join '; ', '', @$cont);
111             }
112              
113             #--------------------
114              
115             sub encode()
116 51     51 1 100 { my $self = shift;
117 51         147 my $value = $self->{MMFF_value};
118              
119 51         111 my @lines;
120 51         95 my ($pre, $encoded);
121              
122 51   100     296 my $charset = $self->{MMFF_charset} || '';
123 51   100     250 my $lang = $self->{MMFF_language} || '';
124 51         124 my $name = $self->{MMFF_name};
125 51         113 my $cont = $self->{MMFF_usecont};
126              
127 51 100 66     452 if($charset || $lang)
    50          
128 6         35 { $pre = "$name*0*=$charset'$lang'";
129 6         107 $value = Encode::encode($charset, $value, 0);
130 6         4199 $encoded = 1;
131             }
132             elsif(grep m/[^\x20-\x7E]/, $value)
133 0         0 { $pre = "$name*0*=''";
134 0         0 $encoded = 1;
135             }
136             else
137 45         110 { $pre = "$name*0=";
138 45         125 $value =~ s/"/\\"/g;
139 45         117 $encoded = 0;
140             }
141              
142 51 100       202 if($encoded)
    50          
143             { # Use encoding
144 6         36 my @c = split //, $value;
145 6         25 while(@c)
146 169         259 { my $c = shift @c;
147 169 100       361 $c = '%'. sprintf "%02X", ord $c
148             unless $c =~ m/[a-zA-Z0-9]/;
149              
150 169 100 100     322 if($cont && length($pre) + length($c)> 76)
151 2         5 { push @lines, $pre;
152 2         5 $pre = $name . '*' . @lines . '*=' . $c;
153             }
154 167         264 else { $pre .= $c }
155             }
156 6         20 push @lines, $pre;
157             }
158             elsif($cont)
159             { # Simple string, but with continuations
160 45         83 while(1)
161 46         249 { push @lines, $pre.'"'. substr($value, 0, 75-length($pre), '') .'"';
162 46 100       198 length $value or last;
163 1         5 $pre = $name . '*' . @lines . '=';
164             }
165              
166             }
167             else
168             { # Single string only
169 0         0 push @lines, $pre . $value;
170             }
171              
172 51 100       353 $lines[0] =~ s/\*0// if @lines==1;
173 51         275 $self->{MMFF_cont} = \@lines;
174             }
175              
176              
177             sub decode()
178 90     90 1 194 { my $self = shift;
179 90         212 my $value = '';
180              
181 90         174 foreach my $cont ( @{$self->{MMFF_cont}} )
  90         373  
182 96 100       276 { unless(defined $cont)
183 1         5 { $value .= "[continuation missing]";
184 1         4 next;
185             }
186              
187 95         395 (my $name, local $_) = split /\=/, $cont, 2;
188              
189 95 100       641 if(substr($name, -1) eq '*')
    100          
    100          
190 12         51 { s/^[^']*\'[^']*\'//;
191 12         42 s/\%([a-fA-F0-9]{2})/chr hex $1/ge;
  47         85  
192             }
193 1         4 elsif( s/^\"(.*)\"$/$1/ ) { s/\\\"/"/g }
194 2         11 elsif( s/^\'(.*)\'$/$1/ ) { s/\\\'/'/g }
195              
196 95         320 $value .= $_;
197             }
198              
199 90         251 my $charset = $self->{MMFF_charset};
200 90 100       341 $value = Encode::decode($charset, $value, 0) if $charset;
201              
202 90         1328 $self->{MMFF_value} = $value;
203             }
204              
205             #--------------------
206              
207             sub mergeComponent($)
208 1     1 1 3 { my ($self, $comp) = @_;
209             my $cont = $self->{MMFF_cont}
210 1 50       5 or error __x"too late to merge: value already changed.";
211              
212 1         2 $self->addComponent($_) for @{$comp->{MMFF_cont}};
  1         4  
213 1         4 $self;
214             }
215              
216             1;