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 38     38   2660388 use strict;
  38         349  
  38         797  
4 38     38   829 use 5.005;
  38         110  
5 38     38   165 use Carp;
  38         82  
  38         2007  
6 38     38   13118 use FileHandle::Unget;
  38         327370  
  38         2444  
7 38     38   285 use File::Spec;
  38         83  
  38         702  
8 38     38   3533 use File::Temp;
  38         62728  
  38         2510  
9             sub _dprint;
10              
11 38     38   14877 use Mail::Mbox::MessageParser::MetaInfo;
  38         109  
  38         1468  
12 38     38   12765 use Mail::Mbox::MessageParser::Config;
  38         91  
  38         935  
13              
14 38     38   14435 use Mail::Mbox::MessageParser::Perl;
  38         90  
  38         1544  
15 38     38   14960 use Mail::Mbox::MessageParser::Grep;
  38         109  
  38         1626  
16 38     38   13730 use Mail::Mbox::MessageParser::Cache;
  38         116  
  38         1747  
17              
18 38     38   242 use vars qw( @ISA $VERSION $_DEBUG );
  38         73  
  38         1552  
19 38     38   217 use vars qw( $_CACHE $UPDATING_CACHE );
  38         71  
  38         139333  
20              
21             @ISA = qw(Exporter);
22              
23             $VERSION = sprintf "%d.%02d%02d", q/1.51.11/ =~ /(\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 5749 50   5749   13964 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 404     404 1 331496 my ($proto, $options, $cache_options) = @_;
61              
62 404   33     2961 my $class = ref($proto) || $proto;
63              
64             carp "You must provide either a file name or a file handle"
65 404 50 33     2263 unless defined $options->{'file_name'} || defined $options->{'file_handle'};
66              
67             # Can't use grep or cache unless there is a filename
68 404 50       1490 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 404 50       1308 if defined $options->{'debug'};
76              
77 404         873 my ($file_type, $need_to_close_filehandle, $error, $endline);
78              
79             ($options->{'file_handle'}, $file_type, $need_to_close_filehandle, $error, $endline) =
80 404         1587 _PREPARE_FILE_HANDLE($options->{'file_name'}, $options->{'file_handle'});
81              
82 392 0 0     1382 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 392 100       949 $options->{'enable_grep'} = 0 if _IS_COMPRESSED_TYPE($file_type);
96              
97 392 50       1398 $options->{'enable_cache'} = 1 unless defined $options->{'enable_cache'};;
98 392 50       1059 $options->{'enable_grep'} = 1 unless defined $options->{'enable_grep'};;
99              
100 392         1258 my $self = undef;
101              
102 392 100       1233 if ($options->{'enable_cache'})
103             {
104 257         4003 $self = new Mail::Mbox::MessageParser::Cache($options, $cache_options);
105              
106 257 50       939 unless (ref $self)
107             {
108 0         0 warn "Couldn't instantiate Mail::Mbox::MessageParser::Cache: $self";
109 0         0 $self = undef;
110             }
111              
112 257 50       673 if ($UPDATING_CACHE)
113             {
114 257         850 _dprint "Couldn't instantiate Mail::Mbox::MessageParser::Cache: " .
115             "Updating cache";
116 257         564 $self = undef;
117             }
118             }
119              
120 392 100 66     2576 if (!defined $self && $options->{'enable_grep'})
121             {
122 15         133 $self = new Mail::Mbox::MessageParser::Grep($options);
123              
124 15 50       46 unless (ref $self)
125             {
126 15 50       71 if ($self =~ /not installed/)
127             {
128 15         52 _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 15         34 $self = undef;
135             }
136             }
137              
138 392 50       1075 if (!defined $self)
139             {
140 392         4516 $self = new Mail::Mbox::MessageParser::Perl($options);
141              
142 392 50       1299 warn "Couldn't instantiate Mail::Mbox::MessageParser::Perl: $self"
143             unless ref $self;
144             }
145              
146 392 50       1065 die "Couldn't instantiate any mailbox parser implementation"
147             unless defined $self;
148              
149 392         1607 _dprint "Instantiate mailbox parser implementation: " . ref $self;
150              
151 392         2088 $self->_print_debug_information();
152              
153 392         1600 $self->_read_prologue();
154              
155 392         1511 $self->{'need_to_close_filehandle'} = $need_to_close_filehandle;
156              
157 392         994 $self->{'endline'} = $endline;
158              
159 392         2208 return $self;
160             }
161              
162             #-------------------------------------------------------------------------------
163              
164             sub _init
165             {
166 649     649   1580 my $self = shift;
167              
168 649         2090 $self->{'email_line_number'} = 0;
169 649         1830 $self->{'email_offset'} = 0;
170 649         2269 $self->{'email_length'} = 0;
171 649         2331 $self->{'email_number'} = 0;
172             }
173              
174             #-------------------------------------------------------------------------------
175              
176             sub DESTROY
177             {
178 392     392   2211543 my $self = shift;
179              
180 392 100       14786 $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 404     404   854 my $file_name = shift;
194 404         761 my $file_handle = shift;
195              
196 404         1315 _dprint "Preparing file handle";
197              
198 404 100       1180 if (defined $file_handle)
199             {
200             # Promote this to a FileHandle::Unget if it isn't already
201 380 100       4044 $file_handle = new FileHandle::Unget($file_handle)
202             unless UNIVERSAL::isa($file_handle, 'FileHandle::Unget');
203              
204 380         10606 binmode $file_handle;
205              
206 380         15050 my $file_type = _GET_FILE_TYPE(\$file_handle);
207 380         1709 _dprint "Filehandle file type: $file_type";
208              
209             # Do decompression if we need to
210 380 100       1297 if (_IS_COMPRESSED_TYPE($file_type))
211             {
212 90         387 my ($decompressed_file_handle,$error) =
213             _DO_DECOMPRESSION($file_handle, $file_type);
214              
215 78 50       403 return ($file_handle,$file_type,0,$error,undef)
216             unless defined $decompressed_file_handle;
217              
218 78 50       798 return ($decompressed_file_handle,$file_type,0,"Not a mailbox",undef)
219             if _GET_FILE_TYPE(\$decompressed_file_handle) ne 'mailbox';
220              
221 78         333 my $endline;
222 78         1074 ($endline, $error) = _GET_ENDLINE(\$decompressed_file_handle);
223              
224 78         1320 return ($decompressed_file_handle,$file_type,0,$error,$endline);
225             }
226             else
227             {
228 290         854 _dprint "Filehandle is not compressed";
229              
230 290         803 my ($endline, $error) = _GET_ENDLINE(\$file_handle);
231              
232 290 50 33     1292 return ($file_handle,$file_type,0,"Not a mailbox",$endline)
233             if !eof($file_handle) && $file_type ne 'mailbox';
234              
235 290         11470 return ($file_handle,$file_type,0,$error,$endline);
236             }
237             }
238             else
239             {
240 24         87 my $file_type = _GET_FILE_TYPE(\$file_name);
241 24         118 _dprint "Filename \"$file_name\" file type: $file_type";
242              
243 24         68 my ($opened_file_handle,$error) =
244             _OPEN_FILE_HANDLE($file_name, $file_type);
245              
246 24 50       79 return ($file_handle,$file_type,0,$error,undef)
247             unless defined $opened_file_handle;
248              
249 24         43 my $endline;
250 24         90 ($endline, $error) = _GET_ENDLINE(\$opened_file_handle);
251              
252 24 100       60 if (_IS_COMPRESSED_TYPE($file_type))
253             {
254 3 50       19 return ($opened_file_handle,$file_type,1,"Not a mailbox",$endline)
255             if _GET_FILE_TYPE(\$opened_file_handle) ne 'mailbox';
256              
257 3         32 return ($opened_file_handle,$file_type,1,$error,$endline);
258             }
259             else
260             {
261 21 50       71 return ($opened_file_handle,$file_type,1,"Not a mailbox",$endline)
262             if $file_type ne 'mailbox';
263              
264 21         94 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 24     24   44 my $file_name = shift;
276 24         44 my $file_type = shift;
277              
278 24         73 _dprint "Opening file \"$file_name\"";
279              
280             # Non-compressed file
281 24 100       74 unless (_IS_COMPRESSED_TYPE($file_type))
282             {
283 21         79 my $file_handle = new FileHandle::Unget($file_name);
284 21 50       2151 return (undef,"Can't open $file_name: $!") unless defined $file_handle;
285              
286 21         82 binmode $file_handle;
287              
288 21         710 _dprint "File \"$file_name\" is not compressed";
289              
290 21         68 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 3 50       16 unless defined $Mail::Mbox::MessageParser::Config{'programs'}{$file_type};
296              
297 3         11 my $filter_command = qq{"$Mail::Mbox::MessageParser::Config{'programs'}{$file_type}" -cd "$file_name" |};
298              
299 3         12 _dprint "Calling \"$filter_command\" to decompress file \"$file_name\".";
300              
301 3         5 my $oldstderr;
302 3 50       77 open $oldstderr,">&STDERR" or die "Can't save STDERR: $!\n";
303 3 50       143 open STDERR,">" . File::Spec->devnull()
304             or die "Can't redirect STDERR to " . File::Spec->devnull() . ": $!\n";
305              
306 3         18 my $file_handle = new FileHandle::Unget($filter_command);
307              
308 3 50       6085 return (undef,"Can't execute \"$filter_command\" for file \"$file_name\": $!")
309             unless defined $file_handle;
310              
311 3         69 binmode $file_handle;
312              
313 3 50       379 open STDERR, '>&', $oldstderr or die "Can't restore STDERR: $!\n";
314              
315 3 50       24 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 3         4708 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 485     485   1248 my $file_name_or_handle_ref = shift;
331              
332             # Open the file if we need to
333 485         965 my $file_handle_ref;
334 485         973 my $need_to_close_filehandle = 0;
335              
336 485 100       2104 if (ref $file_name_or_handle_ref eq 'SCALAR')
337             {
338 24         166 my $temp = new FileHandle::Unget($$file_name_or_handle_ref);
339 24 50       2985 return 'unknown' unless defined $temp;
340 24         55 $file_handle_ref = \$temp;
341              
342 24         46 $need_to_close_filehandle = 1;
343             }
344             else
345             {
346 461         981 $file_handle_ref = $file_name_or_handle_ref;
347             }
348              
349            
350             # Read test characters
351 485         1611 my $test_chars = '';
352 485         932 my $readResult;
353              
354 485   100     3813 while(index($test_chars,"\n\n") == -1 && index($test_chars,"\r\n\r\n") == -1)
355             {
356 499         2376 $readResult =
357             read($$file_handle_ref,$test_chars,4000,CORE::length($test_chars));
358              
359 499 50 33     342062 last unless defined $readResult && $readResult != 0;
360              
361 499 100       2913 last if _IS_BINARY_MAILBOX(\$test_chars);
362              
363 406 50       2801 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 485 100       1892 if($need_to_close_filehandle)
378             {
379 24         79 $$file_handle_ref->close();
380             }
381             else
382             {
383 461         2055 $$file_handle_ref->ungets($test_chars);
384             }
385              
386 485 50 33     9623 return 'unknown' unless defined $readResult && $readResult != 0;
387              
388              
389 485 100       1428 unless (_IS_BINARY_MAILBOX(\$test_chars))
390             {
391 392 50       1225 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 93 100       520 return 'bzip2' if substr($test_chars, 0, 3) eq 'BZh';
397 46 50       377 return 'bzip' if substr($test_chars, 0, 2) eq 'BZ';
398 46 100       296 return 'xz' if substr($test_chars, 1, 4) eq '7zXZ';
399 31 50       194 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 31 50 33     500 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 392     392   939 my $file_name_or_handle_ref = shift;
417              
418             # Open the file if we need to
419 392         635 my $file_handle_ref;
420 392         800 my $need_to_close_filehandle = 0;
421              
422 392 50       1260 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 392         772 $file_handle_ref = $file_name_or_handle_ref;
433             }
434              
435            
436             # Read test characters
437 392         1635 my $test_chars = '';
438 392         759 my $readResult;
439              
440 392   66     2863 while(index($test_chars,"\n") == -1 && index($test_chars,"\r\n") == -1)
441             {
442 392         1910 $readResult =
443             read($$file_handle_ref,$test_chars,4000,CORE::length($test_chars));
444              
445 392 50 33     23713 last unless defined $readResult && $readResult != 0;
446              
447 392 50       1227 last if _IS_BINARY_MAILBOX(\$test_chars);
448              
449 392 50       1912 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 392 50       1030 if($need_to_close_filehandle)
464             {
465 0         0 $$file_handle_ref->close();
466             }
467             else
468             {
469 392         1348 $$file_handle_ref->ungets($test_chars);
470             }
471              
472 392 50 33     4494 return undef unless defined $readResult && $readResult != 0; ## no critic (ProhibitExplicitReturnUndef)
473              
474 392 50       1055 return undef if _IS_BINARY_MAILBOX(\$test_chars); ## no critic (ProhibitExplicitReturnUndef)
475              
476 392         945 my $windows_count = 0;
477              
478 392         2843 while ($test_chars =~ /\r\n/gs)
479             {
480 3232         5929 $windows_count++;
481             }
482              
483 392         836 my $unix_count = 0;
484              
485 392         3034 while ($test_chars =~ /(?
486             {
487 31543         72056 $unix_count++;
488             }
489              
490 392         4012 _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 392 100 66     3585 if($windows_count > 0 && $unix_count == 0)
    50 33        
494             {
495 32         154 return "\r\n", undef;
496             }
497             elsif($windows_count == 0 && $unix_count > 0)
498             {
499 360         2110 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 820     820   1424 my $file_type = shift;
515            
516 820         2400 local $" = '|';
517              
518 820         3514 my @types = qw( gzip bzip bzip2 xz lzip compress );
519 820         2773 my $file_type_pattern = "(@types)";
520              
521 820         7335 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 90     90   207 my $parent = shift;
531 90         289 my $child = new FileHandle::Unget;
532              
533 90 50       7618 pipe $parent, $child or die;
534              
535 90         92136 my $pid = fork();
536 90 50       3257 return undef unless defined $pid; ## no critic (ProhibitExplicitReturnUndef)
537              
538 90 100       2070 if ($pid)
539             {
540 78         6232 close $child;
541             }
542             else
543             {
544 12         1275 close $parent;
545 12 50       3907 open(STDOUT, ">&=" . fileno($child)) or die;
546             }
547              
548 90         32415 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', TMPDIR => 1, 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 90     90   219 my $file_handle = shift;
590 90         361 my $file_type = shift;
591              
592             return (undef,"Can't decompress file handle--no decompressor available")
593 90 50       376 unless defined $Mail::Mbox::MessageParser::Config{'programs'}{$file_type};
594              
595 90         386 my $filter_command = qq{"$Mail::Mbox::MessageParser::Config{'programs'}{$file_type}" -cd};
596              
597 90         395 _dprint "Calling \"$filter_command\" to decompress filehandle";
598              
599             # Implicit fork
600 90         358 my $decompressed_file_handle = new FileHandle::Unget;
601 90         5583 my $pid = _pipe_from_fork($decompressed_file_handle);
602              
603 90 50       615 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 90 100       366 unless ($pid)
615             {
616 12 50       22948 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 12         383 binmode $front_of_pipe;
620              
621 12         1163 print $front_of_pipe (<$file_handle>);
622              
623 12 50       8440 $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 12         18326 close $front_of_pipe;
630              
631 12         5309 exit;
632             }
633              
634 78         933 binmode $decompressed_file_handle;
635              
636             # In parent
637 78         6778 return ($decompressed_file_handle,undef);
638             }
639              
640             #-------------------------------------------------------------------------------
641              
642             sub _DO_DECOMPRESSION
643             {
644 90     90   173 my $file_handle = shift;
645 90         190 my $file_type = shift;
646              
647 90 50       510 if ($^O eq 'MSWin32')
648             {
649 0         0 return _DO_WINDOWS_DECOMPRESSION($file_handle,$file_type);
650             }
651             else
652             {
653 90         353 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 1768     1768   3243 my ($start, $data_length);
664              
665             # Unix line endings
666             {
667 1768         2611 $start = 0;
  1768         2642  
668              
669             # Handle newlines at the start
670 1768         2724 while (index(${$_[0]}, "\n\n", $start) == $start) {
  1768         6416  
671 0         0 $start += 2;
672             }
673              
674 1768         3012 $data_length = index(${$_[0]}, "\n\n", $start) - $start;
  1768         4696  
675             }
676              
677             # If we didn't succeed with Unix line endings, try DOS line endings
678 1768 100       4218 if ($data_length == -1)
679             {
680             # Handle newlines at the start
681 356         625 $start = 0;
682              
683 356         521 while (index(${$_[0]}, "\r\n\r\n", $start) == $start) {
  356         1373  
684 0         0 $start += 4;
685             }
686              
687 356         631 $data_length = index(${$_[0]}, "\r\n\r\n", $start) - $start;
  356         1031  
688             }
689              
690             # Didn't find any kind of empty line. Use the whole buffer.
691 1768 100       3502 $data_length = CORE::length(${$_[0]}) - $start if $data_length == -1;
  228         460  
692              
693 1768         2433 my $bin_length = substr(${$_[0]}, $start ,$data_length) =~ tr/[\t\n\x20-\x7e]//c;
  1768         7471  
694              
695 1768         3424 my $non_bin_length = $data_length - $bin_length;
696              
697 1768         6542 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 392     392   796 my $test_characters = shift;
709              
710 392 50 33     11029 if ($$test_characters =~ /$Mail::Mbox::MessageParser::Config{'from_pattern'}/im &&
711             $$test_characters =~ /^(Received[ :]|Date:|Subject:|X-Status:|Status:|To:)/sm)
712             {
713 392         3158 return 1;
714             }
715             else
716             {
717 0         0 return 0;
718             }
719             }
720              
721             #-------------------------------------------------------------------------------
722              
723             sub reset
724             {
725 40     40 1 58 my $self = shift;
726              
727 40 50       106 if (_IS_A_PIPE($self->{'file_handle'}))
728             {
729 0         0 _dprint "Avoiding seek() on a pipe";
730             }
731             else
732             {
733 40         1933 seek $self->{'file_handle'}, length($self->{'prologue'}), 0
734             }
735              
736 40         1772 $self->{'email_line_number'} = 0;
737 40         72 $self->{'email_offset'} = 0;
738 40         72 $self->{'email_length'} = 0;
739 40         110 $self->{'email_number'} = 0;
740             }
741              
742             #-------------------------------------------------------------------------------
743              
744             # Ceci n'set pas une pipe
745             sub _IS_A_PIPE
746             {
747 40     40   62 my $file_handle = shift;
748              
749 40   33     1445 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 966     966 1 1331 my $self = shift;
758              
759 966         6296 return $self->{'endline'};
760             }
761              
762             #-------------------------------------------------------------------------------
763              
764             sub prologue
765             {
766 306     306 1 2684 my $self = shift;
767              
768 306         1033 return $self->{'prologue'};
769             }
770              
771             #-------------------------------------------------------------------------------
772              
773             sub _print_debug_information
774             {
775 392 50   392   1019 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 360     360 1 574 my $self = shift;
817              
818 360         1131 return $self->{'email_line_number'};
819             }
820              
821             #-------------------------------------------------------------------------------
822              
823             sub number
824             {
825 360     360 1 991 my $self = shift;
826              
827 360         1241 return $self->{'email_number'};
828             }
829              
830             #-------------------------------------------------------------------------------
831              
832             # The length of the last email read
833             sub length
834             {
835 360     360 1 488 my $self = shift;
836              
837 360         1043 return $self->{'email_length'};
838             }
839              
840             #-------------------------------------------------------------------------------
841              
842             # The offset of the last email read
843             sub offset
844             {
845 360     360 1 486 my $self = shift;
846              
847 360         1026 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 2264     2264 1 3421 my $self = shift;
862              
863 2264 100       5465 if ($UPDATING_CACHE)
864             {
865 1381         5067 _dprint "Storing data into cache, length " . $self->{'email_length'};
866              
867 1381         2311 my $_CACHE = $Mail::Mbox::MessageParser::Cache::_CACHE;
868              
869             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'length'} =
870 1381         6394 $self->{'email_length'};
871              
872             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'line_number'} =
873 1381         3816 $self->{'email_line_number'};
874              
875             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'offset'} =
876 1381         3573 $self->{'email_offset'};
877 1381         3461 $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'validated'} =
878             1;
879              
880 1381         2484 $_CACHE->{$self->{'file_name'}}{'modified'} = 1;
881              
882 1381 100       3474 if ($self->end_of_file())
883             {
884 253         13363 $UPDATING_CACHE = 0;
885              
886             # Last one is always validated
887 253         1054 $_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   112 my $email_header = shift;
907 60         109 my $header_name = shift;
908 60         109 my $endline = shift;
909              
910 60 50       164 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     319 local $^W = 0 if $] >= 5.006 && $] < 5.008;
915              
916 60 50 33     246 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         846 my @matches = $$email_header =~ /^($header_name\s.*$endline(?:\s.*$endline)*)/igm;
923              
924 60 100       220 if (@matches)
925             {
926 30 50       194 return wantarray ? @matches : shift @matches;
927             }
928              
929 30 50 33     155 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         108 return undef; ## no critic (ProhibitExplicitReturnUndef)
936             }
937              
938             1;
939              
940             __END__