File Coverage

blib/lib/Mail/Box/Parser/Lines.pm
Criterion Covered Total %
statement 64 90 71.1
branch 19 34 55.8
condition 13 31 41.9
subroutine 11 15 73.3
pod 7 8 87.5
total 114 178 64.0


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::Box::Parser::Lines;{
13             our $VERSION = '4.04';
14             }
15              
16 8     8   1420 use parent 'Mail::Box::Parser';
  8         36  
  8         66  
17              
18 8     8   725 use strict;
  8         19  
  8         204  
19 8     8   34 use warnings;
  8         13  
  8         468  
20              
21 8     8   43 use Log::Report 'mail-message', import => [ qw/__x panic warning/ ];
  8         15  
  8         61  
22              
23 8     8   1437 use Mail::Message::Field ();
  8         17  
  8         12715  
24              
25             #--------------------
26              
27             sub init(@)
28 16     16 0 44 { my ($self, $args) = @_;
29 16         80 $self->SUPER::init($args);
30              
31 16 50       68 $self->{MBPL_lines} = $args->{lines} or panic "No lines";
32 16 50       61 $self->{MBPL_source} = $args->{source} or panic "No source";
33 16         75 $self;
34             }
35              
36             #--------------------
37              
38 110     110 1 228 sub lines() { $_[0]->{MBPL_lines} }
39 0     0 1 0 sub source() { $_[0]->{MBPL_source} }
40              
41             #--------------------
42              
43             my $is_empty_line = qr/^\015?\012?$/;
44              
45             sub readHeader()
46 36     36 1 62 { my $self = shift;
47 36         88 my $lines = $self->lines;
48 36         62 my @ret;
49              
50             LINE:
51 36         105 while(@$lines)
52 138         264 { my $line = shift @$lines;
53 138 100       834 last if $line =~ $is_empty_line;
54              
55 102         476 my ($name, $body) = split /\s*\:\s*/, $line, 2;
56              
57 102 50       222 unless(defined $body)
58 0         0 { warning __x"unexpected end of header in {source}:\n {line}", source => $self->source, line => $line;
59              
60 0 0 0     0 if(@ret && $self->fixHeaderErrors)
61 0         0 { $ret[-1][1] .= ' '.$line; # glue err line to previous field
62 0         0 next LINE;
63             }
64              
65 0         0 unshift @$lines, $line;
66 0         0 last LINE;
67             }
68              
69             # Collect folded lines
70 102   66     472 $body .= shift @$lines
71             while @$lines && $lines->[0] =~ m!^[ \t]!;
72              
73 102         1552 push @ret, [ $name, $body ];
74             }
75              
76 36         150 (undef, undef, @ret);
77             }
78              
79             sub _is_good_end()
80 0     0   0 { my $self = shift;
81              
82             # No seps, then when have to trust it.
83 0   0     0 my $sep = $self->activeSeparator // return 1;
84              
85             # Find first non-empty line on specified location.
86 0         0 my $lines = $self->lines;
87 0         0 my $skip = 0;
88 0   0     0 while($skip < @$lines && $lines->[$skip] =~ $is_empty_line) { $skip++ }
  0         0  
89 0 0       0 $skip < @$lines or return 1;
90              
91 0         0 my $line = $lines->[$skip];
92              
93 0 0 0     0 substr($line, 0, length $sep) eq $sep
94             && ($sep ne 'From ' || $line =~ m/ (?:19[6-9]|20[0-3])[0-9]\b/ );
95             }
96              
97             sub readSeparator()
98 29     29 1 55 { my $self = shift;
99 29   50     95 my $sep = $self->activeSeparator // return ();
100 29         70 my $lines = $self->lines;
101              
102 29         41 my $skip = 0;
103 29   33     431 while($skip < @$lines && $lines->[$skip] =~ $is_empty_line) { $skip++ }
  0         0  
104              
105 29 50       106 $skip < @$lines
106             or return ();
107              
108 29         54 my $line = $lines->[$skip];
109 29 50       84 substr($line, 0, length $sep) eq $sep
110             or return ();
111              
112 29         64 splice @$lines, 0, $skip+1;
113 29         114 (undef, $line);
114             }
115              
116             sub _read_stripped_lines(;$$)
117 45     45   107 { my ($self, $exp_chars, $exp_lines) = @_;
118 45         172 my $seps = $self->separators;
119 45         143 my $lines = $self->lines;
120 45         79 my $take = [];
121              
122 45 100       98 if(@$seps)
123             {
124             LINE:
125 29         71 while(1)
126 120 50       1595 { my $line = shift @$lines or last LINE;
127              
128 120         213 foreach my $sep (@$seps)
129 135 100       346 { substr($line, 0, length $sep) eq $sep or next;
130              
131             # Some apps fail to escape take starting with From
132 29 50 33     98 next if $sep eq 'From ' && $line !~ m/ 19[789][0-9]| 20[0-9][0-9]/;
133              
134 29         62 unshift @$lines, $line; # keep separator
135 29         64 last LINE;
136             }
137              
138 91         154 push @$take, $line;
139             }
140              
141 29 100 66     212 if(@$take && $take->[-1] =~ s/\015?\012\z//)
142             { # Keep an empty line to signal the existence of a preamble, but
143             # remove a second.
144 22 100 100     165 pop @$take if @$seps==1 && @$take > 1 && length($take->[-1])==0;
      100        
145             }
146             }
147             else # File without separators.
148 16         38 { $take = $lines;
149             }
150              
151 45 50       188 if($self->stripGt)
152 0         0 { s/^\>(\>*From\s)/$1/ for @$take;
153             }
154              
155 45 50       155 unless($self->trusted)
156 0         0 { s/\015// for @$take; # remove \r, keep \n
157             }
158              
159 45         91 $take;
160             }
161              
162             sub bodyAsString(;$$)
163 0     0 1 0 { my ($self, $exp_chars, $exp_lines) = @_;
164 0         0 my $take = $self->_read_stripped_lines($exp_chars, $exp_lines);
165 0         0 return (undef, undef, join('', @$take));
166             }
167              
168             sub bodyAsList(;$$)
169 45     45 1 107 { my ($self, $exp_chars, $exp_lines) = @_;
170 45         113 my $take = $self->_read_stripped_lines($exp_chars, $exp_lines);
171 45         149 (undef, undef, $take);
172             }
173              
174             sub bodyAsFile($;$$)
175 0     0 1   { my ($self, $out, $exp_chars, $exp_lines) = @_;
176 0           my $take = $self->_read_stripped_lines($exp_chars, $exp_lines);
177 0           $out->print($_) for @$take;
178 0           (undef, undef, scalar @$take);
179             }
180              
181             1;