File Coverage

blib/lib/Mail/Mbox/MessageParser.pm
Criterion Covered Total %
statement 287 348 82.4
branch 104 190 54.7
condition 25 78 32.0
subroutine 38 41 92.6
pod 10 10 100.0
total 464 667 69.5


line stmt bran cond sub pod time code
1             package Mail::Mbox::MessageParser;
2              
3 42     42   2930000 use strict;
  42         463  
  42         963  
4 42     42   986 use 5.005;
  42         137  
5 42     42   201 use Carp;
  42         106  
  42         2265  
6 42     42   16543 use FileHandle::Unget;
  42         351643  
  42         2312  
7 42     42   312 use File::Spec;
  42         83  
  42         798  
8 42     42   6853 use File::Temp;
  42         62283  
  42         2741  
9             sub _dprint;
10              
11 42     42   17552 use Mail::Mbox::MessageParser::MetaInfo;
  42         117  
  42         1713  
12 42     42   14932 use Mail::Mbox::MessageParser::Config;
  42         101  
  42         1048  
13              
14 42     42   16359 use Mail::Mbox::MessageParser::Perl;
  42         94  
  42         1813  
15 42     42   17460 use Mail::Mbox::MessageParser::Grep;
  42         113  
  42         1823  
16 42     42   16229 use Mail::Mbox::MessageParser::Cache;
  42         116  
  42         2027  
17              
18 42     42   294 use vars qw( @ISA $VERSION $_DEBUG );
  42         80  
  42         1811  
19 42     42   202 use vars qw( $_CACHE $UPDATING_CACHE );
  42         56  
  42         144724  
20              
21             @ISA = qw(Exporter);
22              
23             $VERSION = sprintf "%d.%02d%02d", q/1.51.10/ =~ /(\d+)/g;
24             $_DEBUG = 0;
25              
26             #-------------------------------------------------------------------------------
27              
28             # The class-wide cache, which will be read and written when necessary. i.e.
29             # read when an folder reader object is created which uses caching, and
30             # written when a different cache is specified, or when the program exits,
31             *_CACHE = \$Mail::Mbox::MessageParser::MetaInfo::_CACHE;
32              
33             *UPDATING_CACHE = \$Mail::Mbox::MessageParser::MetaInfo::UPDATING_CACHE;
34             *SETUP_CACHE = \&Mail::Mbox::MessageParser::MetaInfo::SETUP_CACHE;
35             sub SETUP_CACHE;
36              
37             #-------------------------------------------------------------------------------
38              
39             # Outputs debug messages if $_DEBUG is true.
40              
41             sub _dprint
42             {
43 6949 50   6949   17336 return 1 unless $_DEBUG;
44              
45 0         0 my $message = join '',@_;
46              
47 0         0 foreach my $line (split /\n/, $message)
48             {
49 0         0 warn "DEBUG (" . __PACKAGE__ . "): $line\n";
50             }
51              
52             # Be sure to return 1 so code like '_dprint "blah\n" and exit' works.
53 0         0 return 1;
54             }
55              
56             #-------------------------------------------------------------------------------
57              
58             sub new
59             {
60 495     495 1 352607 my ($proto, $options, $cache_options) = @_;
61              
62 495   33     3195 my $class = ref($proto) || $proto;
63              
64             carp "You must provide either a file name or a file handle"
65 495 50 33     1912 unless defined $options->{'file_name'} || defined $options->{'file_handle'};
66              
67             # Can't use grep or cache unless there is a filename
68 495 50       1460 unless (defined $options->{'file_name'})
69             {
70 0         0 $options->{'enable_cache'} = 0;
71 0         0 $options->{'enable_grep'} = 0;
72             }
73              
74             $_DEBUG = $options->{'debug'}
75 495 50       1297 if defined $options->{'debug'};
76              
77 495         844 my ($file_type, $need_to_close_filehandle, $error, $endline);
78              
79             ($options->{'file_handle'}, $file_type, $need_to_close_filehandle, $error, $endline) =
80 495         1594 _PREPARE_FILE_HANDLE($options->{'file_name'}, $options->{'file_handle'});
81              
82 479 0 0     1603 if (defined $error &&
      33        
      0        
      33        
83             !($error eq 'Not a mailbox' && $options->{'force_processing'}) &&
84             !($error =~ 'Found a mix of unix and Windows line endings' && $options->{'force_processing'})
85             )
86             {
87             # Here I assume the only errors for which the filehandle was opened is
88             # "Not a mailbox" and mixed line endings
89 0 0 0     0 close $options->{'file_handle'}
90             if $error eq 'Not a mailbox' || $error =~ /Found a mix of unix and Windows line endings/;
91 0         0 return $error;
92             }
93              
94             # Grep implementation doesn't support compression right now
95 479 100       1144 $options->{'enable_grep'} = 0 if _IS_COMPRESSED_TYPE($file_type);
96              
97 479 50       1251 $options->{'enable_cache'} = 1 unless defined $options->{'enable_cache'};;
98 479 50       1071 $options->{'enable_grep'} = 1 unless defined $options->{'enable_grep'};;
99              
100 479         755 my $self = undef;
101              
102 479 100       1406 if ($options->{'enable_cache'})
103             {
104 317         4881 $self = new Mail::Mbox::MessageParser::Cache($options, $cache_options);
105              
106 317 50       1111 unless (ref $self)
107             {
108 0         0 warn "Couldn't instantiate Mail::Mbox::MessageParser::Cache: $self";
109 0         0 $self = undef;
110             }
111              
112 317 50       930 if ($UPDATING_CACHE)
113             {
114 317         943 _dprint "Couldn't instantiate Mail::Mbox::MessageParser::Cache: " .
115             "Updating cache";
116 317         600 $self = undef;
117             }
118             }
119              
120 479 100 66     3404 if (!defined $self && $options->{'enable_grep'})
121             {
122 16         135 $self = new Mail::Mbox::MessageParser::Grep($options);
123              
124 16 50       45 unless (ref $self)
125             {
126 16 50       70 if ($self =~ /not installed/)
127             {
128 16         49 _dprint "Couldn't instantiate Mail::Mbox::MessageParser::Grep: $self";
129             }
130             else
131             {
132 0         0 warn "Couldn't instantiate Mail::Mbox::MessageParser::Grep: $self";
133             }
134 16         30 $self = undef;
135             }
136             }
137              
138 479 50       1109 if (!defined $self)
139             {
140 479         5261 $self = new Mail::Mbox::MessageParser::Perl($options);
141              
142 479 50       1296 warn "Couldn't instantiate Mail::Mbox::MessageParser::Perl: $self"
143             unless ref $self;
144             }
145              
146 479 50       1108 die "Couldn't instantiate any mailbox parser implementation"
147             unless defined $self;
148              
149 479         1510 _dprint "Instantiate mailbox parser implementation: " . ref $self;
150              
151 479         2497 $self->_print_debug_information();
152              
153 479         1883 $self->_read_prologue();
154              
155 479         1828 $self->{'need_to_close_filehandle'} = $need_to_close_filehandle;
156              
157 479         1195 $self->{'endline'} = $endline;
158              
159 479         3635 return $self;
160             }
161              
162             #-------------------------------------------------------------------------------
163              
164             sub _init
165             {
166 796     796   1832 my $self = shift;
167              
168 796         2570 $self->{'email_line_number'} = 0;
169 796         2287 $self->{'email_offset'} = 0;
170 796         2415 $self->{'email_length'} = 0;
171 796         2915 $self->{'email_number'} = 0;
172             }
173              
174             #-------------------------------------------------------------------------------
175              
176             sub DESTROY
177             {
178 479     479   2280375 my $self = shift;
179              
180 479 100       19170 $self->{'file_handle'}->close() if $self->{'need_to_close_filehandle'};
181             }
182              
183             #-------------------------------------------------------------------------------
184              
185             # Returns:
186             # - a file handle to the decompressed mailbox
187             # - the file type (see _GET_FILE_TYPE)
188             # - a boolean indicating whether the caller needs to close the file handle
189             # - an error message (or undef)
190             # - the endline: "\n", "\r\n", or undef
191             sub _PREPARE_FILE_HANDLE
192             {
193 495     495   924 my $file_name = shift;
194 495         737 my $file_handle = shift;
195              
196 495         1387 _dprint "Preparing file handle";
197              
198 495 100       1180 if (defined $file_handle)
199             {
200             # Promote this to a FileHandle::Unget if it isn't already
201 469 100       4868 $file_handle = new FileHandle::Unget($file_handle)
202             unless UNIVERSAL::isa($file_handle, 'FileHandle::Unget');
203              
204 469         12186 binmode $file_handle;
205              
206 469         16668 my $file_type = _GET_FILE_TYPE(\$file_handle);
207 469         2150 _dprint "Filehandle file type: $file_type";
208              
209             # Do decompression if we need to
210 469 100       1233 if (_IS_COMPRESSED_TYPE($file_type))
211             {
212 152         722 my ($decompressed_file_handle,$error) =
213             _DO_DECOMPRESSION($file_handle, $file_type);
214              
215 136 50       1427 return ($file_handle,$file_type,0,$error,undef)
216             unless defined $decompressed_file_handle;
217              
218 136 50       2326 return ($decompressed_file_handle,$file_type,0,"Not a mailbox",undef)
219             if _GET_FILE_TYPE(\$decompressed_file_handle) ne 'mailbox';
220              
221 136         345 my $endline;
222 136         1841 ($endline, $error) = _GET_ENDLINE(\$decompressed_file_handle);
223              
224 136         1577 return ($decompressed_file_handle,$file_type,0,$error,$endline);
225             }
226             else
227             {
228 317         752 _dprint "Filehandle is not compressed";
229              
230 317         637 my ($endline, $error) = _GET_ENDLINE(\$file_handle);
231              
232 317 50 33     1129 return ($file_handle,$file_type,0,"Not a mailbox",$endline)
233             if !eof($file_handle) && $file_type ne 'mailbox';
234              
235 317         11013 return ($file_handle,$file_type,0,$error,$endline);
236             }
237             }
238             else
239             {
240 26         59 my $file_type = _GET_FILE_TYPE(\$file_name);
241 26         113 _dprint "Filename \"$file_name\" file type: $file_type";
242              
243 26         61 my ($opened_file_handle,$error) =
244             _OPEN_FILE_HANDLE($file_name, $file_type);
245              
246 26 50       69 return ($file_handle,$file_type,0,$error,undef)
247             unless defined $opened_file_handle;
248              
249 26         35 my $endline;
250 26         77 ($endline, $error) = _GET_ENDLINE(\$opened_file_handle);
251              
252 26 100       55 if (_IS_COMPRESSED_TYPE($file_type))
253             {
254 4 50       29 return ($opened_file_handle,$file_type,1,"Not a mailbox",$endline)
255             if _GET_FILE_TYPE(\$opened_file_handle) ne 'mailbox';
256              
257 4         45 return ($opened_file_handle,$file_type,1,$error,$endline);
258             }
259             else
260             {
261 22 50       53 return ($opened_file_handle,$file_type,1,"Not a mailbox",$endline)
262             if $file_type ne 'mailbox';
263              
264 22         77 return ($opened_file_handle,$file_type,1,$error,$endline);
265             }
266             }
267             }
268              
269             #-------------------------------------------------------------------------------
270              
271             # This function does not analyze the file to determine if it is valid. It only
272             # opens it using a suitable decompresson if necessary.
273             sub _OPEN_FILE_HANDLE
274             {
275 26     26   32 my $file_name = shift;
276 26         44 my $file_type = shift;
277              
278 26         69 _dprint "Opening file \"$file_name\"";
279              
280             # Non-compressed file
281 26 100       56 unless (_IS_COMPRESSED_TYPE($file_type))
282             {
283 22         77 my $file_handle = new FileHandle::Unget($file_name);
284 22 50       2101 return (undef,"Can't open $file_name: $!") unless defined $file_handle;
285              
286 22         71 binmode $file_handle;
287              
288 22         631 _dprint "File \"$file_name\" is not compressed";
289              
290 22         65 return ($file_handle,undef);
291             }
292              
293             # It must be a known compressed file type
294             return (undef,"Can't decompress $file_name--no decompressor available")
295 4 50       15 unless defined $Mail::Mbox::MessageParser::Config{'programs'}{$file_type};
296              
297 4         13 my $filter_command = qq{"$Mail::Mbox::MessageParser::Config{'programs'}{$file_type}" -cd "$file_name" |};
298              
299 4         14 _dprint "Calling \"$filter_command\" to decompress file \"$file_name\".";
300              
301 4         4 my $oldstderr;
302 4 50       108 open $oldstderr,">&STDERR" or die "Can't save STDERR: $!\n";
303 4 50       222 open STDERR,">" . File::Spec->devnull()
304             or die "Can't redirect STDERR to " . File::Spec->devnull() . ": $!\n";
305              
306 4         32 my $file_handle = new FileHandle::Unget($filter_command);
307              
308 4 50       7532 return (undef,"Can't execute \"$filter_command\" for file \"$file_name\": $!")
309             unless defined $file_handle;
310              
311 4         96 binmode $file_handle;
312              
313 4 50       457 open STDERR, '>&', $oldstderr or die "Can't restore STDERR: $!\n";
314              
315 4 50       36 if (eof($file_handle))
316             {
317 0         0 $file_handle->close();
318 0         0 return (undef,"Can't execute \"$filter_command\" for file \"$file_name\"");
319             }
320              
321 4         6287 return ($file_handle, undef);
322             }
323              
324             #-------------------------------------------------------------------------------
325              
326             # Returns: unknown, unknown binary, mailbox, non-mailbox ascii, bzip,
327             # bzip2, gzip, compress
328             sub _GET_FILE_TYPE
329             {
330 635     635   1451 my $file_name_or_handle_ref = shift;
331              
332             # Open the file if we need to
333 635         1155 my $file_handle_ref;
334 635         1066 my $need_to_close_filehandle = 0;
335              
336 635 100       2818 if (ref $file_name_or_handle_ref eq 'SCALAR')
337             {
338 26         122 my $temp = new FileHandle::Unget($$file_name_or_handle_ref);
339 26 50       2731 return 'unknown' unless defined $temp;
340 26         46 $file_handle_ref = \$temp;
341              
342 26         45 $need_to_close_filehandle = 1;
343             }
344             else
345             {
346 609         1212 $file_handle_ref = $file_name_or_handle_ref;
347             }
348              
349            
350             # Read test characters
351 635         2160 my $test_chars = '';
352 635         1249 my $readResult;
353              
354 635   100     4611 while(index($test_chars,"\n\n") == -1 && index($test_chars,"\r\n\r\n") == -1)
355             {
356 649         2575 $readResult =
357             read($$file_handle_ref,$test_chars,4000,CORE::length($test_chars));
358              
359 649 50 33     726172 last unless defined $readResult && $readResult != 0;
360              
361 649 100       3125 last if _IS_BINARY_MAILBOX(\$test_chars);
362              
363 493 50       2731 if(CORE::length($test_chars) >
364             $Mail::Mbox::MessageParser::Config{'max_testchar_buffer_size'})
365             {
366 0 0 0     0 if(index($test_chars,"\n\n") == -1 && index($test_chars,"\r\n\r\n") == -1)
367             {
368 0         0 _dprint "Couldn't find end of first paragraph after " .
369             "$Mail::Mbox::MessageParser::Config{'max_testchar_buffer_size'} bytes."
370             }
371              
372 0         0 last;
373             }
374             }
375              
376              
377 635 100       2284 if($need_to_close_filehandle)
378             {
379 26         71 $$file_handle_ref->close();
380             }
381             else
382             {
383 609         2306 $$file_handle_ref->ungets($test_chars);
384             }
385              
386 635 50 33     12215 return 'unknown' unless defined $readResult && $readResult != 0;
387              
388              
389 635 100       1544 unless (_IS_BINARY_MAILBOX(\$test_chars))
390             {
391 479 50       1106 return 'mailbox' if _IS_MAILBOX(\$test_chars);
392 0         0 return 'non-mailbox ascii';
393             }
394              
395             # See "magic" on unix systems for details on how to identify file types
396 156 100       748 return 'bzip2' if substr($test_chars, 0, 3) eq 'BZh';
397 93 50       584 return 'bzip' if substr($test_chars, 0, 2) eq 'BZ';
398 93 100       856 return 'xz' if substr($test_chars, 1, 4) eq '7zXZ';
399 62 50       292 return 'lzip' if substr($test_chars, 0, 4) eq 'LZIP';
400             # return 'zip' if substr($test_chars, 0, 2) eq 'PK' &&
401             # ord(substr($test_chars,3,1)) == 0003 && ord(substr($test_chars,4,1)) == 0004;
402 62 50 33     827 return 'gzip' if
403             ord(substr($test_chars,0,1)) == oct(37) && ord(substr($test_chars,1,1)) == oct(213);
404 0 0 0     0 return 'compress' if
405             ord(substr($test_chars,0,1)) == oct(37) && ord(substr($test_chars,1,1)) == oct(235);
406              
407 0         0 return 'unknown binary';
408             }
409              
410             #-------------------------------------------------------------------------------
411              
412             # Returns an endline result of either: undef, "\r\n", "\n"
413             # Returns an error message (or undef) as well
414             sub _GET_ENDLINE
415             {
416 479     479   836 my $file_name_or_handle_ref = shift;
417              
418             # Open the file if we need to
419 479         681 my $file_handle_ref;
420 479         698 my $need_to_close_filehandle = 0;
421              
422 479 50       1194 if (ref $file_name_or_handle_ref eq 'SCALAR')
423             {
424 0         0 my $temp = new FileHandle::Unget($$file_name_or_handle_ref);
425 0 0       0 return 'unknown' unless defined $temp;
426 0         0 $file_handle_ref = \$temp;
427              
428 0         0 $need_to_close_filehandle = 1;
429             }
430             else
431             {
432 479         815 $file_handle_ref = $file_name_or_handle_ref;
433             }
434              
435            
436             # Read test characters
437 479         2070 my $test_chars = '';
438 479         1026 my $readResult;
439              
440 479   66     3230 while(index($test_chars,"\n") == -1 && index($test_chars,"\r\n") == -1)
441             {
442 479         2139 $readResult =
443             read($$file_handle_ref,$test_chars,4000,CORE::length($test_chars));
444              
445 479 50 33     28645 last unless defined $readResult && $readResult != 0;
446              
447 479 50       1312 last if _IS_BINARY_MAILBOX(\$test_chars);
448              
449 479 50       2040 if(CORE::length($test_chars) >
450             $Mail::Mbox::MessageParser::Config{'max_testchar_buffer_size'})
451             {
452 0 0 0     0 if(index($test_chars,"\n") == -1 && index($test_chars,"\r\n") == -1)
453             {
454 0         0 _dprint "Couldn't find end of first line after " .
455             "$Mail::Mbox::MessageParser::Config{'max_testchar_buffer_size'} bytes."
456             }
457              
458 0         0 last;
459             }
460             }
461              
462              
463 479 50       1017 if($need_to_close_filehandle)
464             {
465 0         0 $$file_handle_ref->close();
466             }
467             else
468             {
469 479         1273 $$file_handle_ref->ungets($test_chars);
470             }
471              
472 479 50 33     6580 return undef unless defined $readResult && $readResult != 0; ## no critic (ProhibitExplicitReturnUndef)
473              
474 479 50       1104 return undef if _IS_BINARY_MAILBOX(\$test_chars); ## no critic (ProhibitExplicitReturnUndef)
475              
476 479         989 my $windows_count = 0;
477              
478 479         3475 while ($test_chars =~ /\r\n/gs)
479             {
480 3232         4704 $windows_count++;
481             }
482              
483 479         1020 my $unix_count = 0;
484              
485 479         3589 while ($test_chars =~ /(?
486             {
487 39508         75703 $unix_count++;
488             }
489              
490 479         4843 _dprint "Found $unix_count UNIX line endings and $windows_count Windows line endings in a sample of length " .
491             CORE::length($test_chars);
492              
493 479 100 66     3434 if($windows_count > 0 && $unix_count == 0)
    50 33        
494             {
495 32         120 return "\r\n", undef;
496             }
497             elsif($windows_count == 0 && $unix_count > 0)
498             {
499 447         1774 return "\n", undef;
500             }
501             else
502             {
503 0 0       0 return $windows_count > $unix_count ? "\r\n" : "\n", 'Found a mix of unix and Windows line endings.' .
    0          
504             ' Please normalize the line endings using a tool like "dos2unix".' .
505             ' Use the force option to ignore this error and process using ' . ($windows_count > $unix_count ?
506             'Windows' : 'Unix') . ' line endings (best guess).';
507             }
508             }
509              
510             #-------------------------------------------------------------------------------
511              
512             sub _IS_COMPRESSED_TYPE
513             {
514 1000     1000   1548 my $file_type = shift;
515            
516 1000         2911 local $" = '|';
517              
518 1000         5067 my @types = qw( gzip bzip bzip2 xz lzip compress );
519 1000         3197 my $file_type_pattern = "(@types)";
520              
521 1000         8090 return $file_type =~ /^$file_type_pattern$/;
522             }
523              
524             #-------------------------------------------------------------------------------
525              
526             # man perlfork for details
527             # simulate open(FOO, "-|")
528             sub _pipe_from_fork
529             {
530 152     152   336 my $parent = shift;
531 152         415 my $child = new FileHandle::Unget;
532              
533 152 50       11764 pipe $parent, $child or die;
534              
535 152         140866 my $pid = fork();
536 152 50       4166 return undef unless defined $pid; ## no critic (ProhibitExplicitReturnUndef)
537              
538 152 100       3262 if ($pid)
539             {
540 136         10678 close $child;
541             }
542             else
543             {
544 16         1727 close $parent;
545 16 50       6064 open(STDOUT, ">&=" . fileno($child)) or die;
546             }
547              
548 152         55140 return $pid;
549             }
550              
551             #-------------------------------------------------------------------------------
552              
553             sub _DO_WINDOWS_DECOMPRESSION
554             {
555 0     0   0 my $file_handle = shift;
556 0         0 my $file_type = shift;
557              
558             return (undef,"Can't decompress file handle--no decompressor available")
559 0 0       0 unless defined $Mail::Mbox::MessageParser::Config{'programs'}{$file_type};
560              
561 0         0 my $filter_command = qq{"$Mail::Mbox::MessageParser::Config{'programs'}{$file_type}" -cd};
562              
563 0         0 my ($temp_file_handle, $temp_file_name) =
564             File::Temp::tempfile('mail-mbox-messageparser-XXXXXX', SUFFIX => '.tmp', UNLINK => 1);
565              
566 0         0 while(my $line = <$file_handle>)
567             {
568 0         0 print $temp_file_handle $line;
569             }
570              
571 0         0 close $file_handle;
572             # So that it won't be deleted until the program is complete
573             # close $temp_file_handle;
574              
575 0         0 _dprint "Calling \"$filter_command\" to decompress filehandle";
576              
577 0         0 my $decompressed_file_handle =
578             new FileHandle::Unget("$filter_command $temp_file_name |");
579              
580 0         0 binmode $decompressed_file_handle;
581              
582 0         0 return ($decompressed_file_handle,undef);
583             }
584              
585             #-------------------------------------------------------------------------------
586              
587             sub _DO_NONWINDOWS_DECOMPRESSION
588             {
589 152     152   341 my $file_handle = shift;
590 152         327 my $file_type = shift;
591              
592             return (undef,"Can't decompress file handle--no decompressor available")
593 152 50       695 unless defined $Mail::Mbox::MessageParser::Config{'programs'}{$file_type};
594              
595 152         525 my $filter_command = qq{"$Mail::Mbox::MessageParser::Config{'programs'}{$file_type}" -cd};
596              
597 152         565 _dprint "Calling \"$filter_command\" to decompress filehandle";
598              
599             # Implicit fork
600 152         572 my $decompressed_file_handle = new FileHandle::Unget;
601 152         10192 my $pid = _pipe_from_fork($decompressed_file_handle);
602              
603 152 50       791 unless (defined($pid))
604             {
605 0         0 $file_handle->close();
606 0         0 die 'Can\'t fork to decompress file handle';
607             }
608              
609             # In child. Write to the parent, giving it all the data to decompress.
610             # We have to do it this way because other methods (e.g. open2) require us
611             # to feed the filter as we use the filtered data. This method allows us to
612             # keep the remainder of the code the same for both compressed and
613             # uncompressed input.
614 152 100       783 unless ($pid)
615             {
616 16 50       39427 open(my $front_of_pipe, "|$filter_command 2>" . File::Spec->devnull())
617             or return (undef,"Can't execute \"$filter_command\" on file handle: $!");
618              
619 16         591 binmode $front_of_pipe;
620              
621 16         1594 print $front_of_pipe (<$file_handle>);
622              
623 16 50       12059 $file_handle->close()
624             or return (undef,"Can't execute \"$filter_command\" on file handle: $!");
625              
626             # We intentionally don't check for error here. This is because the
627             # parent may have aborted, in which case we let it take care of
628             # error messages. (e.g. Non-mailbox standard input.)
629 16         22917 close $front_of_pipe;
630              
631 16         7811 exit;
632             }
633              
634 136         1718 binmode $decompressed_file_handle;
635              
636             # In parent
637 136         10531 return ($decompressed_file_handle,undef);
638             }
639              
640             #-------------------------------------------------------------------------------
641              
642             sub _DO_DECOMPRESSION
643             {
644 152     152   377 my $file_handle = shift;
645 152         304 my $file_type = shift;
646              
647 152 50       802 if ($^O eq 'MSWin32')
648             {
649 0         0 return _DO_WINDOWS_DECOMPRESSION($file_handle,$file_type);
650             }
651             else
652             {
653 152         719 return _DO_NONWINDOWS_DECOMPRESSION($file_handle,$file_type);
654             }
655             }
656              
657             #-------------------------------------------------------------------------------
658              
659             # Simulates -B, which consumes data on a stream. We only look at the first
660             # 1000 characters because the body may have foreign binary-like characters
661             sub _IS_BINARY_MAILBOX
662             {
663 2242     2242   3510 my ($start, $data_length);
664              
665             # Unix line endings
666             {
667 2242         3009 $start = 0;
  2242         2886  
668              
669             # Handle newlines at the start
670 2242         2822 while (index(${$_[0]}, "\n\n", $start) == $start) {
  2242         7189  
671 0         0 $start += 2;
672             }
673              
674 2242         3152 $data_length = index(${$_[0]}, "\n\n", $start) - $start;
  2242         5360  
675             }
676              
677             # If we didn't succeed with Unix line endings, try DOS line endings
678 2242 100       4868 if ($data_length == -1)
679             {
680             # Handle newlines at the start
681 482         698 $start = 0;
682              
683 482         628 while (index(${$_[0]}, "\r\n\r\n", $start) == $start) {
  482         1761  
684 0         0 $start += 4;
685             }
686              
687 482         773 $data_length = index(${$_[0]}, "\r\n\r\n", $start) - $start;
  482         1264  
688             }
689              
690             # Didn't find any kind of empty line. Use the whole buffer.
691 2242 100       3920 $data_length = CORE::length(${$_[0]}) - $start if $data_length == -1;
  354         687  
692              
693 2242         2888 my $bin_length = substr(${$_[0]}, $start ,$data_length) =~ tr/[\t\n\x20-\x7e]//c;
  2242         8810  
694              
695 2242         3626 my $non_bin_length = $data_length - $bin_length;
696              
697 2242         7206 return (($non_bin_length / $data_length) <= .70);
698             }
699              
700             #-------------------------------------------------------------------------------
701              
702             # Detects whether an ASCII file is a mailbox, based on whether it has a line
703             # whose prefix is 'From' and another line whose prefix is 'Received ',
704             # 'Date:', 'Subject:', 'X-Status:', 'Status:', or 'To:'.
705              
706             sub _IS_MAILBOX
707             {
708 479     479   842 my $test_characters = shift;
709              
710 479 50 33     15434 if ($$test_characters =~ /$Mail::Mbox::MessageParser::Config{'from_pattern'}/im &&
711             $$test_characters =~ /^(Received[ :]|Date:|Subject:|X-Status:|Status:|To:)/sm)
712             {
713 479         3203 return 1;
714             }
715             else
716             {
717 0         0 return 0;
718             }
719             }
720              
721             #-------------------------------------------------------------------------------
722              
723             sub reset
724             {
725 44     44 1 78 my $self = shift;
726              
727 44 50       102 if (_IS_A_PIPE($self->{'file_handle'}))
728             {
729 0         0 _dprint "Avoiding seek() on a pipe";
730             }
731             else
732             {
733 44         1999 seek $self->{'file_handle'}, length($self->{'prologue'}), 0
734             }
735              
736 44         1579 $self->{'email_line_number'} = 0;
737 44         72 $self->{'email_offset'} = 0;
738 44         69 $self->{'email_length'} = 0;
739 44         119 $self->{'email_number'} = 0;
740             }
741              
742             #-------------------------------------------------------------------------------
743              
744             # Ceci n'set pas une pipe
745             sub _IS_A_PIPE
746             {
747 44     44   103 my $file_handle = shift;
748              
749 44   33     1626 return (-t $file_handle || -S $file_handle || -p $file_handle || ## no critic (ProhibitInteractiveTest)
750             !-f $file_handle || !(seek $file_handle, 0, 1));
751             }
752              
753             #-------------------------------------------------------------------------------
754              
755             sub endline
756             {
757 1014     1014 1 1281 my $self = shift;
758              
759 1014         6436 return $self->{'endline'};
760             }
761              
762             #-------------------------------------------------------------------------------
763              
764             sub prologue
765             {
766 385     385 1 3606 my $self = shift;
767              
768 385         1361 return $self->{'prologue'};
769             }
770              
771             #-------------------------------------------------------------------------------
772              
773             sub _print_debug_information
774             {
775 479 50   479   1173 return unless $_DEBUG;
776              
777 0         0 my $self = shift;
778              
779 0         0 _dprint "Version: $VERSION";
780              
781 0         0 foreach my $key (keys %$self)
782             {
783 0         0 my $value = $self->{$key};
784 0 0       0 if (defined $value)
785             {
786 0 0       0 $value = '' unless ref \$value eq 'SCALAR';
787             }
788             else
789             {
790 0         0 $value = '';
791             }
792              
793 0         0 _dprint "$key: $value";
794             }
795             }
796              
797             #-------------------------------------------------------------------------------
798              
799             # Returns true if the file handle has been fully read
800             sub end_of_file
801             {
802 0     0 1 0 my $self = shift;
803              
804             # Reset eof in case the file was appended to. Hopefully this works all the
805             # time. See perldoc -f seek for details.
806 0 0       0 seek($self->{'file_handle'},0,1) if eof $self->{'file_handle'};
807              
808 0         0 return eof $self->{'file_handle'};
809             }
810              
811             #-------------------------------------------------------------------------------
812              
813             # The line number of the last email read
814             sub line_number
815             {
816 378     378 1 513 my $self = shift;
817              
818 378         1036 return $self->{'email_line_number'};
819             }
820              
821             #-------------------------------------------------------------------------------
822              
823             sub number
824             {
825 378     378 1 912 my $self = shift;
826              
827 378         1132 return $self->{'email_number'};
828             }
829              
830             #-------------------------------------------------------------------------------
831              
832             # The length of the last email read
833             sub length
834             {
835 378     378 1 473 my $self = shift;
836              
837 378         918 return $self->{'email_length'};
838             }
839              
840             #-------------------------------------------------------------------------------
841              
842             # The offset of the last email read
843             sub offset
844             {
845 378     378 1 501 my $self = shift;
846              
847 378         967 return $self->{'email_offset'};
848             }
849              
850             #-------------------------------------------------------------------------------
851              
852             sub _read_prologue
853             {
854 0     0   0 die "Derived class must provide an implementation";
855             }
856              
857             #-------------------------------------------------------------------------------
858              
859             sub read_next_email
860             {
861 2581     2581 1 3621 my $self = shift;
862              
863 2581 100       5502 if ($UPDATING_CACHE)
864             {
865 1598         5512 _dprint "Storing data into cache, length " . $self->{'email_length'};
866              
867 1598         2590 my $_CACHE = $Mail::Mbox::MessageParser::Cache::_CACHE;
868              
869             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'length'} =
870 1598         7215 $self->{'email_length'};
871              
872             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'line_number'} =
873 1598         4176 $self->{'email_line_number'};
874              
875             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'offset'} =
876 1598         3669 $self->{'email_offset'};
877 1598         3515 $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'validated'} =
878             1;
879              
880 1598         2563 $_CACHE->{$self->{'file_name'}}{'modified'} = 1;
881              
882 1598 100       3520 if ($self->end_of_file())
883             {
884 313         14887 $UPDATING_CACHE = 0;
885              
886             # Last one is always validated
887 313         1927 $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'validated'} =
888             1;
889             }
890              
891             }
892             }
893              
894             #-------------------------------------------------------------------------------
895              
896             # - Returns header lines in the email header which match the given name.
897             # - Example names: 'From:', 'Received:' or 'From '
898             # - If the calling context wants a list, a list of the matching header lines
899             # are returned. Otherwise, the first (and perhaps only) match is returned.
900             # - Wrapped lines are handled. Look for multiple \n's in the return value(s)
901             # - 'From ' also looks for Gnus 'X-From-Line:' or 'X-Draft-From:'
902              
903             # Stolen from grepmail
904             sub _GET_HEADER_FIELD
905             {
906 60     60   89 my $email_header = shift;
907 60         78 my $header_name = shift;
908 60         85 my $endline = shift;
909              
910 60 50       135 die unless ref $email_header;
911              
912             # Avoid perl 5.6 bug which causes spurious warning even though $email_header
913             # is defined.
914 60 50 33     255 local $^W = 0 if $] >= 5.006 && $] < 5.008;
915              
916 60 50 33     184 if ($header_name =~ /^From$/i &&
917             $$email_header =~ /^((?:From\s|X-From-Line:|X-Draft-From:).*$endline(\s.*$endline)*)/im)
918             {
919 0 0       0 return wantarray ? ($1) : $1;
920             }
921              
922 60         750 my @matches = $$email_header =~ /^($header_name\s.*$endline(?:\s.*$endline)*)/igm;
923              
924 60 100       159 if (@matches)
925             {
926 30 50       119 return wantarray ? @matches : shift @matches;
927             }
928              
929 30 50 33     135 if (lc $header_name eq 'from ' &&
930             $$email_header =~ /^(From\s.*$endline(\s.*$endline)*)/im)
931             {
932 0 0       0 return wantarray ? ($1) : $1;
933             }
934              
935 30         78 return undef; ## no critic (ProhibitExplicitReturnUndef)
936             }
937              
938             1;
939              
940             __END__