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   3348349 use strict;
  38         389  
  38         1006  
4 38     38   961 use 5.005;
  38         144  
5 38     38   239 use Carp;
  38         78  
  38         2373  
6 38     38   15336 use FileHandle::Unget;
  38         382909  
  38         2622  
7 38     38   349 use File::Spec;
  38         83  
  38         873  
8 38     38   3598 use File::Temp;
  38         71332  
  38         3021  
9             sub _dprint;
10              
11 38     38   17109 use Mail::Mbox::MessageParser::MetaInfo;
  38         114  
  38         1624  
12 38     38   15882 use Mail::Mbox::MessageParser::Config;
  38         116  
  38         1239  
13              
14 38     38   16048 use Mail::Mbox::MessageParser::Perl;
  38         108  
  38         1799  
15 38     38   16987 use Mail::Mbox::MessageParser::Grep;
  38         139  
  38         1902  
16 38     38   16360 use Mail::Mbox::MessageParser::Cache;
  38         130  
  38         1913  
17              
18 38     38   288 use vars qw( @ISA $VERSION $_DEBUG );
  38         78  
  38         2028  
19 38     38   225 use vars qw( $_CACHE $UPDATING_CACHE );
  38         94  
  38         160805  
20              
21             @ISA = qw(Exporter);
22              
23             $VERSION = sprintf "%d.%02d%02d", q/1.51.8/ =~ /(\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   13448 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 340372 my ($proto, $options, $cache_options) = @_;
61              
62 404   33     2228 my $class = ref($proto) || $proto;
63              
64             carp "You must provide either a file name or a file handle"
65 404 50 33     2004 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       1430 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       1194 if defined $options->{'debug'};
76              
77 404         819 my ($file_type, $need_to_close_filehandle, $error, $endline);
78              
79             ($options->{'file_handle'}, $file_type, $need_to_close_filehandle, $error, $endline) =
80 404         1498 _PREPARE_FILE_HANDLE($options->{'file_name'}, $options->{'file_handle'});
81              
82 392 0 0     1350 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       852 $options->{'enable_grep'} = 0 if _IS_COMPRESSED_TYPE($file_type);
96              
97 392 50       1198 $options->{'enable_cache'} = 1 unless defined $options->{'enable_cache'};;
98 392 50       985 $options->{'enable_grep'} = 1 unless defined $options->{'enable_grep'};;
99              
100 392         1096 my $self = undef;
101              
102 392 100       1144 if ($options->{'enable_cache'})
103             {
104 257         4159 $self = new Mail::Mbox::MessageParser::Cache($options, $cache_options);
105              
106 257 50       732 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       605 if ($UPDATING_CACHE)
113             {
114 257         823 _dprint "Couldn't instantiate Mail::Mbox::MessageParser::Cache: " .
115             "Updating cache";
116 257         502 $self = undef;
117             }
118             }
119              
120 392 100 66     2675 if (!defined $self && $options->{'enable_grep'})
121             {
122 15         113 $self = new Mail::Mbox::MessageParser::Grep($options);
123              
124 15 50       44 unless (ref $self)
125             {
126 15 50       59 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         30 $self = undef;
135             }
136             }
137              
138 392 50       983 if (!defined $self)
139             {
140 392         5577 $self = new Mail::Mbox::MessageParser::Perl($options);
141              
142 392 50       1187 warn "Couldn't instantiate Mail::Mbox::MessageParser::Perl: $self"
143             unless ref $self;
144             }
145              
146 392 50       971 die "Couldn't instantiate any mailbox parser implementation"
147             unless defined $self;
148              
149 392         1443 _dprint "Instantiate mailbox parser implementation: " . ref $self;
150              
151 392         2186 $self->_print_debug_information();
152              
153 392         1491 $self->_read_prologue();
154              
155 392         1386 $self->{'need_to_close_filehandle'} = $need_to_close_filehandle;
156              
157 392         960 $self->{'endline'} = $endline;
158              
159 392         1949 return $self;
160             }
161              
162             #-------------------------------------------------------------------------------
163              
164             sub _init
165             {
166 649     649   1637 my $self = shift;
167              
168 649         2136 $self->{'email_line_number'} = 0;
169 649         5644 $self->{'email_offset'} = 0;
170 649         1830 $self->{'email_length'} = 0;
171 649         2254 $self->{'email_number'} = 0;
172             }
173              
174             #-------------------------------------------------------------------------------
175              
176             sub DESTROY
177             {
178 392     392   2078556 my $self = shift;
179              
180 392 100       13823 $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   761 my $file_name = shift;
194 404         759 my $file_handle = shift;
195              
196 404         1272 _dprint "Preparing file handle";
197              
198 404 100       1048 if (defined $file_handle)
199             {
200             # Promote this to a FileHandle::Unget if it isn't already
201 380 100       3826 $file_handle = new FileHandle::Unget($file_handle)
202             unless UNIVERSAL::isa($file_handle, 'FileHandle::Unget');
203              
204 380         10056 binmode $file_handle;
205              
206 380         14127 my $file_type = _GET_FILE_TYPE(\$file_handle);
207 380         1587 _dprint "Filehandle file type: $file_type";
208              
209             # Do decompression if we need to
210 380 100       1274 if (_IS_COMPRESSED_TYPE($file_type))
211             {
212 90         431 my ($decompressed_file_handle,$error) =
213             _DO_DECOMPRESSION($file_handle, $file_type);
214              
215 78 50       495 return ($file_handle,$file_type,0,$error,undef)
216             unless defined $decompressed_file_handle;
217              
218 78 50       897 return ($decompressed_file_handle,$file_type,0,"Not a mailbox",undef)
219             if _GET_FILE_TYPE(\$decompressed_file_handle) ne 'mailbox';
220              
221 78         259 my $endline;
222 78         475 ($endline, $error) = _GET_ENDLINE(\$decompressed_file_handle);
223              
224 78         1092 return ($decompressed_file_handle,$file_type,0,$error,$endline);
225             }
226             else
227             {
228 290         792 _dprint "Filehandle is not compressed";
229              
230 290         739 my ($endline, $error) = _GET_ENDLINE(\$file_handle);
231              
232 290 50 33     1369 return ($file_handle,$file_type,0,"Not a mailbox",$endline)
233             if !eof($file_handle) && $file_type ne 'mailbox';
234              
235 290         10526 return ($file_handle,$file_type,0,$error,$endline);
236             }
237             }
238             else
239             {
240 24         86 my $file_type = _GET_FILE_TYPE(\$file_name);
241 24         128 _dprint "Filename \"$file_name\" file type: $file_type";
242              
243 24         75 my ($opened_file_handle,$error) =
244             _OPEN_FILE_HANDLE($file_name, $file_type);
245              
246 24 50       84 return ($file_handle,$file_type,0,$error,undef)
247             unless defined $opened_file_handle;
248              
249 24         41 my $endline;
250 24         113 ($endline, $error) = _GET_ENDLINE(\$opened_file_handle);
251              
252 24 100       78 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         33 return ($opened_file_handle,$file_type,1,$error,$endline);
258             }
259             else
260             {
261 21 50       65 return ($opened_file_handle,$file_type,1,"Not a mailbox",$endline)
262             if $file_type ne 'mailbox';
263              
264 21         93 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   45 my $file_name = shift;
276 24         46 my $file_type = shift;
277              
278 24         80 _dprint "Opening file \"$file_name\"";
279              
280             # Non-compressed file
281 24 100       75 unless (_IS_COMPRESSED_TYPE($file_type))
282             {
283 21         81 my $file_handle = new FileHandle::Unget($file_name);
284 21 50       2092 return (undef,"Can't open $file_name: $!") unless defined $file_handle;
285              
286 21         99 binmode $file_handle;
287              
288 21         734 _dprint "File \"$file_name\" is not compressed";
289              
290 21         73 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       20 unless defined $Mail::Mbox::MessageParser::Config{'programs'}{$file_type};
296              
297 3         17 my $filter_command = qq{"$Mail::Mbox::MessageParser::Config{'programs'}{$file_type}" -cd "$file_name" |};
298              
299 3         19 _dprint "Calling \"$filter_command\" to decompress file \"$file_name\".";
300              
301 3         7 my $oldstderr;
302 3 50       103 open $oldstderr,">&STDERR" or die "Can't save STDERR: $!\n";
303 3 50       200 open STDERR,">" . File::Spec->devnull()
304             or die "Can't redirect STDERR to " . File::Spec->devnull() . ": $!\n";
305              
306 3         29 my $file_handle = new FileHandle::Unget($filter_command);
307              
308 3 50       8839 return (undef,"Can't execute \"$filter_command\" for file \"$file_name\": $!")
309             unless defined $file_handle;
310              
311 3         101 binmode $file_handle;
312              
313 3 50       765 open STDERR, '>&', $oldstderr or die "Can't restore STDERR: $!\n";
314              
315 3 50       55 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         5597 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   1352 my $file_name_or_handle_ref = shift;
331              
332             # Open the file if we need to
333 485         889 my $file_handle_ref;
334 485         914 my $need_to_close_filehandle = 0;
335              
336 485 100       2082 if (ref $file_name_or_handle_ref eq 'SCALAR')
337             {
338 24         167 my $temp = new FileHandle::Unget($$file_name_or_handle_ref);
339 24 50       3099 return 'unknown' unless defined $temp;
340 24         52 $file_handle_ref = \$temp;
341              
342 24         54 $need_to_close_filehandle = 1;
343             }
344             else
345             {
346 461         955 $file_handle_ref = $file_name_or_handle_ref;
347             }
348              
349            
350             # Read test characters
351 485         1947 my $test_chars = '';
352 485         945 my $readResult;
353              
354 485   100     3601 while(index($test_chars,"\n\n") == -1 && index($test_chars,"\r\n\r\n") == -1)
355             {
356 499         2844 $readResult =
357             read($$file_handle_ref,$test_chars,4000,CORE::length($test_chars));
358              
359 499 50 33     399631 last unless defined $readResult && $readResult != 0;
360              
361 499 100       1950 last if _IS_BINARY_MAILBOX(\$test_chars);
362              
363 406 50       2925 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       1503 if($need_to_close_filehandle)
378             {
379 24         89 $$file_handle_ref->close();
380             }
381             else
382             {
383 461         2150 $$file_handle_ref->ungets($test_chars);
384             }
385              
386 485 50 33     9379 return 'unknown' unless defined $readResult && $readResult != 0;
387              
388              
389 485 100       1335 unless (_IS_BINARY_MAILBOX(\$test_chars))
390             {
391 392 50       1644 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       605 return 'bzip2' if substr($test_chars, 0, 3) eq 'BZh';
397 46 50       353 return 'bzip' if substr($test_chars, 0, 2) eq 'BZ';
398 46 100       308 return 'xz' if substr($test_chars, 1, 4) eq '7zXZ';
399 31 50       204 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     613 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   871 my $file_name_or_handle_ref = shift;
417              
418             # Open the file if we need to
419 392         629 my $file_handle_ref;
420 392         706 my $need_to_close_filehandle = 0;
421              
422 392 50       1112 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         743 $file_handle_ref = $file_name_or_handle_ref;
433             }
434              
435            
436             # Read test characters
437 392         1272 my $test_chars = '';
438 392         674 my $readResult;
439              
440 392   66     2678 while(index($test_chars,"\n") == -1 && index($test_chars,"\r\n") == -1)
441             {
442 392         1972 $readResult =
443             read($$file_handle_ref,$test_chars,4000,CORE::length($test_chars));
444              
445 392 50 33     24200 last unless defined $readResult && $readResult != 0;
446              
447 392 50       1117 last if _IS_BINARY_MAILBOX(\$test_chars);
448              
449 392 50       1831 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       900 if($need_to_close_filehandle)
464             {
465 0         0 $$file_handle_ref->close();
466             }
467             else
468             {
469 392         1377 $$file_handle_ref->ungets($test_chars);
470             }
471              
472 392 50 33     4821 return undef unless defined $readResult && $readResult != 0; ## no critic (ProhibitExplicitReturnUndef)
473              
474 392 50       1471 return undef if _IS_BINARY_MAILBOX(\$test_chars); ## no critic (ProhibitExplicitReturnUndef)
475              
476 392         785 my $windows_count = 0;
477              
478 392         2098 while ($test_chars =~ /\r\n/gs)
479             {
480 3232         5227 $windows_count++;
481             }
482              
483 392         798 my $unix_count = 0;
484              
485 392         2802 while ($test_chars =~ /(?
486             {
487 31543         68049 $unix_count++;
488             }
489              
490 392         3763 _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     3488 if($windows_count > 0 && $unix_count == 0)
    50 33        
494             {
495 32         129 return "\r\n", undef;
496             }
497             elsif($windows_count == 0 && $unix_count > 0)
498             {
499 360         2153 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   1436 my $file_type = shift;
515            
516 820         2364 local $" = '|';
517              
518 820         5088 my @types = qw( gzip bzip bzip2 xz lzip compress );
519 820         2765 my $file_type_pattern = "(@types)";
520              
521 820         7285 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   197 my $parent = shift;
531 90         327 my $child = new FileHandle::Unget;
532              
533 90 50       8040 pipe $parent, $child or die;
534              
535 90         92831 my $pid = fork();
536 90 50       3937 return undef unless defined $pid; ## no critic (ProhibitExplicitReturnUndef)
537              
538 90 100       2162 if ($pid)
539             {
540 78         7084 close $child;
541             }
542             else
543             {
544 12         1430 close $parent;
545 12 50       4392 open(STDOUT, ">&=" . fileno($child)) or die;
546             }
547              
548 90         34621 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 90     90   208 my $file_handle = shift;
590 90         209 my $file_type = shift;
591              
592             return (undef,"Can't decompress file handle--no decompressor available")
593 90 50       425 unless defined $Mail::Mbox::MessageParser::Config{'programs'}{$file_type};
594              
595 90         429 my $filter_command = qq{"$Mail::Mbox::MessageParser::Config{'programs'}{$file_type}" -cd};
596              
597 90         435 _dprint "Calling \"$filter_command\" to decompress filehandle";
598              
599             # Implicit fork
600 90         450 my $decompressed_file_handle = new FileHandle::Unget;
601 90         6272 my $pid = _pipe_from_fork($decompressed_file_handle);
602              
603 90 50       15011 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       378 unless ($pid)
615             {
616 12 50       27558 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         318 binmode $front_of_pipe;
620              
621 12         1457 print $front_of_pipe (<$file_handle>);
622              
623 12 50       9493 $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         23647 close $front_of_pipe;
630              
631 12         5604 exit;
632             }
633              
634 78         998 binmode $decompressed_file_handle;
635              
636             # In parent
637 78         6242 return ($decompressed_file_handle,undef);
638             }
639              
640             #-------------------------------------------------------------------------------
641              
642             sub _DO_DECOMPRESSION
643             {
644 90     90   205 my $file_handle = shift;
645 90         193 my $file_type = shift;
646              
647 90 50       556 if ($^O eq 'MSWin32')
648             {
649 0         0 return _DO_WINDOWS_DECOMPRESSION($file_handle,$file_type);
650             }
651             else
652             {
653 90         416 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   2894 my ($start, $data_length);
664              
665             # Unix line endings
666             {
667 1768         2392 $start = 0;
  1768         2420  
668              
669             # Handle newlines at the start
670 1768         2422 while (index(${$_[0]}, "\n\n", $start) == $start) {
  1768         6393  
671 0         0 $start += 2;
672             }
673              
674 1768         2571 $data_length = index(${$_[0]}, "\n\n", $start) - $start;
  1768         4404  
675             }
676              
677             # If we didn't succeed with Unix line endings, try DOS line endings
678 1768 100       3712 if ($data_length == -1)
679             {
680             # Handle newlines at the start
681 356         645 $start = 0;
682              
683 356         515 while (index(${$_[0]}, "\r\n\r\n", $start) == $start) {
  356         1301  
684 0         0 $start += 4;
685             }
686              
687 356         578 $data_length = index(${$_[0]}, "\r\n\r\n", $start) - $start;
  356         898  
688             }
689              
690             # Didn't find any kind of empty line. Use the whole buffer.
691 1768 100       3657 $data_length = CORE::length(${$_[0]}) - $start if $data_length == -1;
  228         516  
692              
693 1768         2322 my $bin_length = substr(${$_[0]}, $start ,$data_length) =~ tr/[\t\n\x20-\x7e]//c;
  1768         7426  
694              
695 1768         3031 my $non_bin_length = $data_length - $bin_length;
696              
697 1768         6183 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   903 my $test_characters = shift;
709              
710 392 50 33     11366 if ($$test_characters =~ /$Mail::Mbox::MessageParser::Config{'from_pattern'}/im &&
711             $$test_characters =~ /^(Received[ :]|Date:|Subject:|X-Status:|Status:|To:)/sm)
712             {
713 392         2760 return 1;
714             }
715             else
716             {
717 0         0 return 0;
718             }
719             }
720              
721             #-------------------------------------------------------------------------------
722              
723             sub reset
724             {
725 40     40 1 66 my $self = shift;
726              
727 40 50       114 if (_IS_A_PIPE($self->{'file_handle'}))
728             {
729 0         0 _dprint "Avoiding seek() on a pipe";
730             }
731             else
732             {
733 40         1700 seek $self->{'file_handle'}, length($self->{'prologue'}), 0
734             }
735              
736 40         1392 $self->{'email_line_number'} = 0;
737 40         64 $self->{'email_offset'} = 0;
738 40         72 $self->{'email_length'} = 0;
739 40         89 $self->{'email_number'} = 0;
740             }
741              
742             #-------------------------------------------------------------------------------
743              
744             # Ceci n'set pas une pipe
745             sub _IS_A_PIPE
746             {
747 40     40   86 my $file_handle = shift;
748              
749 40   33     1309 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 1177 my $self = shift;
758              
759 966         5793 return $self->{'endline'};
760             }
761              
762             #-------------------------------------------------------------------------------
763              
764             sub prologue
765             {
766 306     306 1 2853 my $self = shift;
767              
768 306         1163 return $self->{'prologue'};
769             }
770              
771             #-------------------------------------------------------------------------------
772              
773             sub _print_debug_information
774             {
775 392 50   392   1045 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 453 my $self = shift;
817              
818 360         892 return $self->{'email_line_number'};
819             }
820              
821             #-------------------------------------------------------------------------------
822              
823             sub number
824             {
825 360     360 1 872 my $self = shift;
826              
827 360         1049 return $self->{'email_number'};
828             }
829              
830             #-------------------------------------------------------------------------------
831              
832             # The length of the last email read
833             sub length
834             {
835 360     360 1 553 my $self = shift;
836              
837 360         1136 return $self->{'email_length'};
838             }
839              
840             #-------------------------------------------------------------------------------
841              
842             # The offset of the last email read
843             sub offset
844             {
845 360     360 1 469 my $self = shift;
846              
847 360         1078 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 3332 my $self = shift;
862              
863 2264 100       6723 if ($UPDATING_CACHE)
864             {
865 1381         4653 _dprint "Storing data into cache, length " . $self->{'email_length'};
866              
867 1381         2164 my $_CACHE = $Mail::Mbox::MessageParser::Cache::_CACHE;
868              
869             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'length'} =
870 1381         6097 $self->{'email_length'};
871              
872             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'line_number'} =
873 1381         3770 $self->{'email_line_number'};
874              
875             $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'offset'} =
876 1381         3155 $self->{'email_offset'};
877 1381         3162 $_CACHE->{$self->{'file_name'}}{'emails'}[$self->{'email_number'}-1]{'validated'} =
878             1;
879              
880 1381         2301 $_CACHE->{$self->{'file_name'}}{'modified'} = 1;
881              
882 1381 100       3193 if ($self->end_of_file())
883             {
884 253         12129 $UPDATING_CACHE = 0;
885              
886             # Last one is always validated
887 253         1031 $_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   90 my $email_header = shift;
907 60         88 my $header_name = shift;
908 60         81 my $endline = shift;
909              
910 60 50       125 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     256 local $^W = 0 if $] >= 5.006 && $] < 5.008;
915              
916 60 50 33     211 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         668 my @matches = $$email_header =~ /^($header_name\s.*$endline(?:\s.*$endline)*)/igm;
923              
924 60 100       142 if (@matches)
925             {
926 30 50       136 return wantarray ? @matches : shift @matches;
927             }
928              
929 30 50 33     121 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         84 return undef; ## no critic (ProhibitExplicitReturnUndef)
936             }
937              
938             1;
939              
940             __END__