File Coverage

blib/lib/Email/Simple/Header.pm
Criterion Covered Total %
statement 141 145 97.2
branch 61 72 84.7
condition 12 21 57.1
subroutine 21 21 100.0
pod 11 11 100.0
total 246 270 91.1


line stmt bran cond sub pod time code
1 22     22   126157 use strict;
  22         53  
  22         556  
2 22     22   96 use warnings;
  22         32  
  22         812  
3             package Email::Simple::Header;
4             # ABSTRACT: the header of an Email::Simple message
5             $Email::Simple::Header::VERSION = '2.217'; # TRIAL
6 22     22   106 use Carp ();
  22         37  
  22         42084  
7              
8             our @CARP_NOT = qw(Email::Simple);
9              
10             require Email::Simple;
11              
12             #pod =head1 SYNOPSIS
13             #pod
14             #pod my $email = Email::Simple->new($text);
15             #pod
16             #pod my $header = $email->header_obj;
17             #pod print $header->as_string;
18             #pod
19             #pod =head1 DESCRIPTION
20             #pod
21             #pod This method implements the headers of an Email::Simple object. It is a very
22             #pod minimal interface, and is mostly for private consumption at the moment.
23             #pod
24             #pod =method new
25             #pod
26             #pod my $header = Email::Simple::Header->new($head, \%arg);
27             #pod
28             #pod C<$head> is a string containing a valid email header, or a reference to such a
29             #pod string. If a reference is passed in, don't expect that it won't be altered.
30             #pod
31             #pod Valid arguments are:
32             #pod
33             #pod crlf - the header's newline; defaults to CRLF
34             #pod
35             #pod =cut
36              
37             # We need to be able to:
38             # * get all values by lc name
39             # * produce all pairs, with case intact
40              
41             sub new {
42 58     58 1 7941 my ($class, $head, $arg) = @_;
43              
44 58 100       133 my $head_ref = ref $head ? $head : \$head;
45              
46 58   100     188 my $self = { mycrlf => $arg->{crlf} || "\x0d\x0a", };
47              
48 58         146 my $headers = $class->_header_to_list($head_ref, $self->{mycrlf});
49              
50             # for my $header (@$headers) {
51             # push @{ $self->{order} }, $header->[0];
52             # push @{ $self->{head}{ $header->[0] } }, $header->[1];
53             # }
54             #
55             # $self->{header_names} = { map { lc $_ => $_ } keys %{ $self->{head} } };
56 58         112 $self->{headers} = $headers;
57              
58 58         235 bless $self => $class;
59             }
60              
61             sub _header_to_list {
62 58     58   108 my ($self, $head, $mycrlf) = @_;
63              
64 58 100       72 Carp::carp 'Header with wide characters' if ${$head} =~ /[^\x00-\xFF]/;
  58         647  
65              
66 58         126 my @headers;
67              
68 58         148 my $crlf = Email::Simple->__crlf_re;
69              
70 58         717 while ($$head =~ m/\G(.+?)$crlf/go) {
71 443         831 local $_ = $1;
72              
73 443 100 100     1727 if (/^\s+/ or not /^([^:]+):\s*(.*)/) {
74             # This is a continuation line. We fold it onto the end of
75             # the previous header.
76 49 100       81 next if !@headers; # Well, that sucks. We're continuing nothing?
77              
78 48         148 (my $trimmed = $_) =~ s/^\s+//;
79 48 100       157 $headers[-1][0] .= $headers[-1][0] =~ /\S/ ? " $trimmed" : $trimmed;
80 48         225 $headers[-1][1] .= "$mycrlf$_";
81             } else {
82 394         1948 push @headers, $1, [ $2, $_ ];
83             }
84             }
85              
86 58         174 return \@headers;
87             }
88              
89             #pod =method as_string
90             #pod
91             #pod my $string = $header->as_string(\%arg);
92             #pod
93             #pod This returns a stringified version of the header.
94             #pod
95             #pod =cut
96              
97             # RFC 2822, 3.6:
98             # ...for the purposes of this standard, header fields SHOULD NOT be reordered
99             # when a message is transported or transformed. More importantly, the trace
100             # header fields and resent header fields MUST NOT be reordered, and SHOULD be
101             # kept in blocks prepended to the message.
102              
103             sub as_string {
104 53     53 1 100 my ($self, $arg) = @_;
105 53   50     225 $arg ||= {};
106              
107 53         77 my $header_str = '';
108              
109 53         83 my $headers = $self->{headers};
110              
111 53         113 my $fold_arg = {
112             # at => (exists $arg->{fold_at} ? $arg->{fold_at} : $self->default_fold_at),
113             # indent => (exists $arg->{fold_indent} ? $arg->{fold_indent} : $self->default_fold_indent),
114             at => $self->_default_fold_at,
115             indent => $self->_default_fold_indent,
116             };
117              
118 53         150 for (my $i = 0; $i < @$headers; $i += 2) {
119 459 100       690 if (ref $headers->[ $i + 1 ]) {
120 400         557 $header_str .= $headers->[ $i + 1 ][1] . $self->crlf;
121             } else {
122 59         116 my $header = "$headers->[$i]: $headers->[$i + 1]";
123              
124 59         97 $header_str .= $self->_fold($header, $fold_arg);
125             }
126             }
127              
128 53         189 return $header_str;
129             }
130              
131             #pod =method header_names
132             #pod
133             #pod This method returns a list of the unique header names found in this header, in
134             #pod no particular order.
135             #pod
136             #pod =cut
137              
138             sub header_names {
139 6     6 1 12 my $headers = $_[0]->{headers};
140              
141 6         6 my %seen;
142 14         48 grep { !$seen{ lc $_ }++ }
143 6         25 map { $headers->[ $_ * 2 ] } 0 .. @$headers / 2 - 1;
  14         25  
144             }
145              
146             #pod =method header_raw_pairs
147             #pod
148             #pod my @pairs = $header->header_raw_pairs;
149             #pod my $first_name = $pairs[0];
150             #pod my $first_value = $pairs[1];
151             #pod
152             #pod This method returns a list of all the field/value pairs in the header, in the
153             #pod order that they appear in the header. (Remember: don't try assigning that to a
154             #pod hash. Some fields may appear more than once!)
155             #pod
156             #pod =method header_pairs
157             #pod
158             #pod L is another name for L, which was the original
159             #pod name for the method and which you'll see most often. In general, though, it's
160             #pod better to be explicit and use L. (In Email::MIME,
161             #pod L exists for letting the library do the header decoding for
162             #pod you.)
163             #pod
164             #pod =cut
165              
166             sub header_raw_pairs {
167 12     12 1 14 my ($self) = @_;
168              
169 12         16 my @pairs = map {; _str_value($_) } @{ $self->{headers} };
  92         105  
  12         26  
170              
171 12         100 return @pairs;
172             }
173              
174             sub header_pairs {
175 12     12 1 20 my ($self) = @_;
176 12         26 $self->header_raw_pairs;
177             }
178              
179             #pod =method header_raw
180             #pod
181             #pod my $first_value = $header->header_raw($field);
182             #pod my $nth_value = $header->header_raw($field, $index);
183             #pod my @all_values = $header->header_raw($field);
184             #pod
185             #pod This method returns the value or values of the given header field. If the
186             #pod named field does not appear in the header, this method returns false.
187             #pod
188             #pod =method header
189             #pod
190             #pod This method just calls C. It's the older name for C,
191             #pod but it can be a problem because L, a subclass of Email::Simple,
192             #pod makes C
return the header's decoded value.
193             #pod
194             #pod =cut
195              
196 165 100   165   546 sub _str_value { return ref $_[0] ? $_[0][0] : $_[0] }
197              
198             sub header_raw {
199 78     78 1 6172 my ($self, $field, $index) = @_;
200              
201 78         143 my $headers = $self->{headers};
202 78         140 my $lc_field = lc $field;
203              
204 78 100 66     236 if (wantarray and not defined $index) {
205 22         36 return map { _str_value($headers->[ $_ * 2 + 1 ]) }
206 10         37 grep { lc $headers->[ $_ * 2 ] eq $lc_field } 0 .. @$headers / 2 - 1;
  53         96  
207             } else {
208 68 100       135 $index = 0 unless defined $index;
209 68         159 my $max = @$headers / 2 - 1;
210 68 100       190 my @indexes = $index >= 0 ? (0 .. $max) : reverse(0 .. $max);
211 68 100       140 $index = -1-$index if $index < 0;
212 68         142 for (@indexes) {
213 1 100   1   545 next unless lc $headers->[ $_ * 2 ] eq $lc_field;
  1         13  
  1         12  
  253         471  
214 95 100       207 return _str_value($headers->[ $_ * 2 + 1 ]) if $index-- == 0;
215             }
216 17         21516 return undef;
217             }
218             }
219              
220             *header = \&header_raw;
221              
222             #pod =method header_raw_set
223             #pod
224             #pod $header->header_raw_set($field => @values);
225             #pod
226             #pod This method updates the value of the given header. Existing headers have their
227             #pod values set in place. Additional headers are added at the end. If no values
228             #pod are given to set, the header will be removed from to the message entirely.
229             #pod
230             #pod =method header_set
231             #pod
232             #pod L is another name for L, which was the original
233             #pod name for the method and which you'll see most often. In general, though, it's
234             #pod better to be explicit and use L. (In Email::MIME,
235             #pod L exists for letting the library do the header encoding for
236             #pod you.)
237             #pod
238             #pod =cut
239              
240             # Header fields are lines composed of a field name, followed by a colon (":"),
241             # followed by a field body, and terminated by CRLF. A field name MUST be
242             # composed of printable US-ASCII characters (i.e., characters that have values
243             # between 33 and 126, inclusive), except colon. A field body may be composed
244             # of any US-ASCII characters, except for CR and LF.
245              
246             # However, a field body may contain CRLF when used in header "folding" and
247             # "unfolding" as described in section 2.2.3.
248              
249             sub header_raw_set {
250 26     26 1 79 my ($self, $field, @data) = @_;
251              
252 26 100       193 Carp::carp "Header name '$field' with wide characters" if $field =~ /[^\x00-\xFF]/;
253 26 100       214 Carp::carp "Value for '$field' header with wide characters" if grep /[^\x00-\xFF]/, @data;
254              
255             # I hate this block. -- rjbs, 2006-10-06
256 26 50       120 if ($Email::Simple::GROUCHY) {
257 0 0       0 Carp::croak "field name contains illegal characters"
258             unless $field =~ /^[\x21-\x39\x3b-\x7e]+$/;
259 0 0       0 Carp::carp "field name is not limited to hyphens and alphanumerics"
260             unless $field =~ /^[\w-]+$/;
261             }
262              
263 26         60 my $headers = $self->{headers};
264              
265 26         50 my $lc_field = lc $field;
266 154         253 my @indices = grep { lc $headers->[$_] eq $lc_field }
267 26         87 map { $_ * 2 } 0 .. @$headers / 2 - 1;
  154         209  
268              
269 26 100       91 if (@indices > @data) {
    100          
270 2         3 my $overage = @indices - @data;
271 2         6 splice @{$headers}, $_, 2 for reverse @indices[ -$overage .. -1 ];
  3         6  
272 2         5 pop @indices for (1 .. $overage);
273             } elsif (@data > @indices) {
274 15         33 my $underage = @data - @indices;
275 15         46 for (1 .. $underage) {
276 15         33 push @$headers, $field, undef; # temporary value
277 15         33 push @indices, $#$headers - 1;
278             }
279             }
280              
281 26         62 for (0 .. $#indices) {
282 29         62 $headers->[ $indices[$_] + 1 ] = $data[$_];
283             }
284              
285 26 100       104 return wantarray ? @data : $data[0];
286             }
287              
288             sub header_set {
289 17     17 1 40 my ($self, $field, @data) = @_;
290 17         42 $self->header_raw_set($field, @data);
291             }
292              
293             #pod =method header_raw_prepend
294             #pod
295             #pod $header->header_raw_prepend($field => $value);
296             #pod
297             #pod This method adds a new instance of the name field as the first field in the
298             #pod header.
299             #pod
300             #pod =cut
301              
302             sub header_raw_prepend {
303 9     9 1 18 my ($self, $field, $value) = @_;
304              
305 9 50       14 Carp::confess("tried to prepend raw header with undefined field name")
306             unless defined $field;
307              
308 9 50       14 Carp::confess(qq{tried to prepend raw header "$field" with undefined value})
309             unless defined $value;
310              
311 9         11 unshift @{ $self->{headers} }, $field => $value;
  9         23  
312              
313 9         24 return;
314             }
315              
316             #pod =method header_rename
317             #pod
318             #pod $header->header_rename($field, $new_name, $nth);
319             #pod
320             #pod This renames the named field to the new name. If C<$nth> is given, only the
321             #pod Ith instance of the field will be renamed. It is fatal to rename an
322             #pod instance that does not exist. The first instance of a header is the 0th.
323             #pod
324             #pod If C<$nth> is omitted, all instances of the header are renamed.
325             #pod
326             #pod When picking headers to rename, C<$field> is matched case insensitively. So,
327             #pod given this header:
328             #pod
329             #pod happythoughts: yes
330             #pod HappyThoughts: so many
331             #pod hapPyThouGhts: forever
332             #pod
333             #pod Then this code...
334             #pod
335             #pod $header->rename_header('happythoughts', 'Delights');
336             #pod
337             #pod ...will result in this:
338             #pod
339             #pod Delights: yes
340             #pod Delights: so many
341             #pod Delights: forever
342             #pod
343             #pod Headers may be rewrapped as a result of renaming.
344             #pod
345             #pod =cut
346              
347             sub header_rename {
348 9     9 1 2328 my ($self, $field, $new_name, $n) = @_;
349              
350 9         15 my $headers = $self->{headers};
351 9         12 my $lc_field = lc $field;
352              
353 51         81 my @indices = grep { lc $headers->[$_] eq $lc_field }
354 9         24 map { $_ * 2 } 0 .. @$headers / 2 - 1;
  51         65  
355              
356 9 100       20 if (defined $n) {
357 5 100       10 if ($n < 0) { Carp::confess("negative header index makes no sense") }
  1         166  
358 4 100       10 if ($n > $#indices) { Carp::confess("$n exceeds count of $field headers") }
  1         80  
359              
360 3         6 @indices = $indices[$n];
361             }
362              
363 7         10 for my $i (@indices) {
364 14         17 $headers->[$i] = $new_name;
365 14 100       28 if (ref $headers->[$i + 1]) {
366 9         30 $headers->[$i + 1] = $headers->[ $i + 1 ][0];
367             }
368             }
369              
370 7         14 return;
371             }
372              
373             #pod =method crlf
374             #pod
375             #pod This method returns the newline string used in the header.
376             #pod
377             #pod =cut
378              
379 454     454 1 1084 sub crlf { $_[0]->{mycrlf} }
380              
381             # =method fold
382             #
383             # my $folded = $header->fold($line, \%arg);
384             #
385             # Given a header string, this method returns a folded version, if the string is
386             # long enough to warrant folding. This method is used internally.
387             #
388             # Valid arguments are:
389             #
390             # at - fold lines to be no longer than this length, if possible
391             # if given and false, never fold headers
392             # indent - indent lines with this string
393              
394             # =cut
395              
396             sub _fold {
397 59     59   133 my ($self, $line, $arg) = @_;
398 59   50     100 $arg ||= {};
399              
400 59 50       99 $arg->{at} = $self->_default_fold_at unless exists $arg->{at};
401              
402 59 50       84 $arg->{indent} = $self->_default_fold_indent unless exists $arg->{indent};
403              
404 59   33     108 my $indent = $arg->{indent} || $self->_default_fold_indent;
405              
406             # We will not folder headers if...
407             # * the header has vertical whitespace
408             # * all vertical whitespace is followed by horizontal whitespace or END
409 59 100       137 if ($line =~ /\n/) {
410 21 100       51 if ($line =~ s/\n([^\s\t])/\n$indent$1/g) {
411 1         184 Carp::carp("bad space in header: newline followed by non-space: $line");
412             } else {
413 20 100       53 $line .= $self->crlf unless $line =~ /\n$/;
414 20         55 return $line;
415             }
416             }
417              
418 39 50 33     169 return $line . $self->crlf unless $arg->{at} and $arg->{at} > 0;
419              
420 39   33     90 my $limit = ($arg->{at} || $self->_default_fold_at) - 1;
421              
422 39 100       94 return $line . $self->crlf if length $line <= $limit;
423              
424 3         9 return $self->__fold_objless($line, $limit, $indent, $self->crlf);
425              
426             }
427              
428             sub __fold_objless {
429 28     28   60 my ($self, $line, $limit, $indent, $crlf) = @_;
430              
431             # We know it will not contain any new lines at present
432 28         34 my $folded = "";
433 28         66 while (length $line) {
434 33 50       267 if ($line =~ s/^(.{0,$limit})(\s|\z)//) {
435 33         80 $folded .= $1 . $crlf;
436 33 100       100 $folded .= $indent if length $line;
437             } else {
438             # Basically nothing we can do. :(
439 0         0 $folded .= $line . $crlf;
440 0         0 last;
441             }
442             }
443              
444 28         89 return $folded;
445             }
446              
447             # =method default_fold_at
448             #
449             # This method (provided for subclassing) returns the default length at which to
450             # try to fold header lines. The default default is 78.
451             #
452             # =cut
453              
454 53     53   100 sub _default_fold_at { 78 }
455              
456             # =method default_fold_indent
457             #
458             # This method (provided for subclassing) returns the default string used to
459             # indent folded headers. The default default is a single space.
460             #
461             # =cut
462              
463 53     53   122 sub _default_fold_indent { " " }
464              
465             1;
466              
467             __END__