File Coverage

blib/lib/Mail/MboxParser.pm
Criterion Covered Total %
statement 175 257 68.0
branch 57 104 54.8
condition 33 49 67.3
subroutine 22 27 81.4
pod 8 15 53.3
total 295 452 65.2


line stmt bran cond sub pod time code
1             # Mail::MboxParser - object-oriented access to UNIX-mailboxes
2             #
3             # Copyright (C) 2001 Tassilo v. Parseval
4             # This program is free software; you can redistribute it and/or
5             # modify it under the same terms as Perl itself.
6              
7             # Version: $Id: MboxParser.pm,v 1.54 2002/03/01 09:34:39 parkerpine Exp $
8              
9             package Mail::MboxParser;
10              
11             require 5.004;
12              
13 17     17   128690 use base 'Mail::MboxParser::Base';
  17         40  
  17         18407  
14              
15             # ----------------------------------------------------------------
16              
17             =head1 NAME
18              
19             Mail::MboxParser - read-only access to UNIX-mailboxes
20              
21             =head1 SYNOPSIS
22              
23             use Mail::MboxParser;
24              
25             my $parseropts = {
26             enable_cache => 1,
27             enable_grep => 1,
28             cache_file_name => 'mail/cache-file',
29             };
30             my $mb = Mail::MboxParser->new('some_mailbox',
31             decode => 'ALL',
32             parseropts => $parseropts);
33              
34             # -----------
35            
36             # slurping
37             for my $msg ($mb->get_messages) {
38             print $msg->header->{subject}, "\n";
39             $msg->store_all_attachments(path => '/tmp');
40             }
41              
42             # iterating
43             while (my $msg = $mb->next_message) {
44             print $msg->header->{subject}, "\n";
45             # ...
46             }
47              
48             # we forgot to do something with the messages
49             $mb->rewind;
50             while (my $msg = $mb->next_message) {
51             # iterate again
52             # ...
53             }
54              
55             # subscripting one message after the other
56             for my $idx (0 .. $mb->nmsgs - 1) {
57             my $msg = $mb->get_message($idx);
58             }
59              
60             =head1 DESCRIPTION
61              
62             This module attempts to provide a simplified access to standard UNIX-mailboxes.
63             It offers only a subset of methods to get 'straight to the point'. More
64             sophisticated things can still be done by invoking any method from MIME::Tools
65             on the appropriate return values.
66              
67             Mail::MboxParser has not been derived from Mail::Box and thus isn't acquainted
68             with it in any way. It, however, incorporates some invaluable hints by the
69             author of Mail::Box, Mark Overmeer.
70              
71             =head1 METHODS
72              
73             See also the section ERROR-HANDLING much further below.
74              
75             More to that, see the relevant manpages of Mail::MboxParser::Mail,
76             Mail::MboxParser::Mail::Body and Mail::MboxParser::Mail::Convertable for a
77             description of the methods for these objects.
78              
79             =cut
80              
81 17     17   91 use strict;
  17         36  
  17         471  
82 17     17   15479 use Mail::MboxParser::Mail;
  17         61  
  17         1668  
83 17     17   27674 use File::Temp qw/tempfile/;
  17         502358  
  17         1597  
84 17     17   166 use Symbol;
  17         40  
  17         1200  
85 17     17   181 use Carp;
  17         38  
  17         1216  
86 17     17   111 use IO::Seekable;
  17         38  
  17         949  
87              
88 17     17   233 use base qw(Exporter);
  17         36  
  17         1612  
89 17     17   97 use vars qw($VERSION @EXPORT @ISA);
  17         32  
  17         1977  
90             $VERSION = "0.55";
91             @EXPORT = qw();
92             @ISA = qw(Mail::MboxParser::Base);
93              
94             use constant
95 17   50 17   1867 HAVE_MSGPARSER => eval { require Mail::Mbox::MessageParser; 1 } || 0;
  17         1457  
  17         4270  
96            
97             my $from_date = qr/^From (.*)\d{4}\015?$/;
98             my $empty_line = qr/^\015?$/;
99              
100             # ----------------------------------------------------------------
101              
102             =over 4
103              
104             =item B
105              
106             =item B
107              
108             =item B
109              
110             =item B
111              
112             This creates a new MboxParser-object opening the specified 'mailbox' with
113             either absolute or relative path.
114              
115             new() can also take a reference to a variable containing the mailbox either as
116             one string (reference to a scalar) or linewise (reference to an array), or a
117             filehandle from which to read the mailbox.
118              
119             The following option(s) may be useful. The value in brackets below the key is
120             the default if none given.
121              
122             key: | value: | description:
123             ==========|============|===============================
124             decode | 'NEVER' | never decode transfer-encoded
125             (NEVER) | | data
126             |------------|-------------------------------
127             | 'BODY' | will decode body into a human-
128             | | readable format
129             |------------|-------------------------------
130             | 'HEADER' | will decode header fields if
131             | | any is encoded
132             |------------|-------------------------------
133             | 'ALL' | decode any data
134             ==========|============|===============================
135             uudecode | 1 | enable extraction of uuencoded
136             (0) | | attachments in MIME::Parser
137             |------------|-------------------------------
138             | 0 | uuencoded attachments are
139             | | treated as plain body text
140             ==========|============|===============================
141             newline | 'UNIX' | UNIXish line-endings
142             (AUTO) | | ("\n" aka \012)
143             |------------|-------------------------------
144             | 'WIN' | Win32 line-endings
145             | | ("\n\r" aka \012\015)
146             |------------|-------------------------------
147             | 'AUTO' | try to do autodetection
148             |------------|-------------------------------
149             | custom | a user-given value for totally
150             | | borked mailboxes
151             ==========|============|===============================
152             oldparser | 1 | uses the old (and slower)
153             (0) | | parser (but guaranteed to show
154             | | the old behaviour)
155             |------------|-------------------------------
156             | 0 | uses Mail::Mbox::MessageParser
157             ==========|============|===============================
158             parseropts| | see "Specifying parser opts"
159             | | below
160             ==========|============|===============================
161              
162             The I option comes in handy if you have a mbox-file that happens to
163             not conform to the rules of your operating-system's character semantics one way
164             or another. One such scenario: You are using the module under Win but
165             deliberately have mailboxes with UNIX-newlines (or the other way round). If you
166             do not give this option, 'AUTO' is assumed and some basic tests on the mailbox
167             are performed. This autoedection is of course not capable of detecting cases
168             where you use something like '#DELIMITER' as line-ending. It can as to yet only
169             distinguish between UNIX and Win32ish newlines. You may be lucky and it even
170             works for Macintoshs. If you have more extravagant wishes, pass a costum value:
171              
172             my $mb = new Mail::MboxParser ("mbox", newline => '#DELIMITER');
173              
174             You can't use regexes here since internally this relies on the $/ var
175             ($INPUT_RECORD_SEPERATOR, that is).
176            
177             When passing either a scalar-, array-ref or \*STDIN as first-argument, an
178             anonymous tmp-file is created to hold the data. This procedure is hidden away
179             from the user so there is no need to worry about it. Since a tmp-file acts just
180             like an ordinary mailbox-file you don't need to be concerned about loss of data
181             or so once you have been walking through the mailbox-data. No data will be lost
182             and it'll all be fine and smooth.
183              
184             =back
185              
186             =head2 Specifying parser options
187              
188             When available, the module will use C to do the
189             parsing. To get the most speed out of it, you can tweak some of its options.
190             Arguably, you even have to do that in order to make it use caching. Options for
191             the parser are given via the I switch that expects a reference to a
192             hash as values. The values you can specify are:
193              
194             =over 8
195              
196             =item enable_cache
197              
198             When set to a true value, caching is used B if you gave
199             I. There is no default value here!
200              
201             =item cache_file_name
202              
203             The file used for caching. This option is mandatory if I is true.
204              
205             =item enable_grep
206              
207             When set to a true value (which is the default), the extern grep(1) is used to
208             speed up parsing. If your system does not provide a usable grep implementation,
209             it silently falls back to the pure Perl parser.
210              
211             =back
212              
213             When the module was unable to create a C object, it
214             will fall back to the old parser in the hope that the construction of the
215             object then succeeds.
216              
217             =cut
218              
219             sub init (@) {
220 17     17 0 58 my ($self, @args) = @_;
221              
222 17 50       6536 if (@args == 0) {
223 0         0 croak <
224             Error: open needs either a filename, a filehande (as glob-ref) or a
225             (scalar/array)-referece variable as first argument.
226             EOC
227             }
228            
229             # we need odd number of arguments
230 17 50       89 if ((@args % 2) == 0) {
231 0         0 croak <
232             Error: open() can never have an even number of arguments.
233             See 'perldoc Mail::MboxParser' on how to call it.
234             EOC
235             }
236 17         91 $self->open(@args);
237              
238 17         72 $self;
239             }
240              
241             # ----------------------------------------------------------------
242              
243             =over 4
244              
245             =item B
246              
247             Takes exactly the same arguments as new() does just that it can be used to
248             change the characteristics of a mailbox on the fly.
249              
250             =back
251              
252             =cut
253              
254             sub open (@) {
255 17     17 1 79 my ($self, @args) = @_;
256            
257 17         62 local *_;
258              
259 17         50 my $source = shift @args;
260            
261 17         162 $self->{CONFIG} = { @args };
262 17         70 $self->{CURR_POS} = 0;
263              
264 17         33 my ($file_name, $old_filepos);
265            
266             # supposedly a filename
267 17 50 0     96 if (! ref $source) {
    0          
268 17 50       497 if (! -f $source) {
269 0         0 croak <
270             Error: The filename you passed to open() does not refer to an existing file
271             EOC
272             }
273 17         289 my $handle = gensym;
274 17 50       1060 open $handle, "<$source" or
275             croak "Error: Could not open $source for reading: $!";
276 17         60 $self->{READER} = $handle;
277 17         46 $file_name = $source;
278             }
279              
280             # a filehandle
281             elsif (ref $source eq 'GLOB' && seek $source, 0, SEEK_CUR) {
282 0         0 $old_filepos = tell $source;
283 0         0 $self->{READER} = $source;
284             }
285            
286             # else
287             else {
288 0 0       0 (my $fh, $file_name) = tempfile(UNLINK => 1) or croak <
289             Error: Could not create temporary file. This is very weird ($!).
290             EOC
291 0 0       0 if (ref $source eq 'SCALAR') { print $fh ${$source} }
  0 0       0  
  0 0       0  
292 0         0 elsif (ref $source eq 'ARRAY') { print $fh @{$source} }
  0         0  
293 0         0 elsif (ref $source eq 'GLOB') { print $fh $_ while <$source> }
294 0         0 seek $fh, 0, SEEK_SET;
295 0         0 $self->{READER} = $fh;
296             }
297              
298 17 50 100     239 if ($self->{CONFIG}->{oldparser} or ! HAVE_MSGPARSER
      33        
299             or ! defined $file_name) {
300 17         123 binmode $self->{READER};
301 17         99 local $^W = 0;
302 17         84 *get_messages = \&get_messages_old;
303 17         53 *get_message = \&get_message_old;
304 17         55 *next_message = \&next_message_old;
305            
306 17         167 $self->{CONFIG}->{join_string} = "";
307             } else {
308 0         0 local $^W = 0;
309 0         0 *get_messages = \&get_messages_new;
310 0         0 *get_message = \&get_message_new;
311 0         0 *next_message = \&next_message_new;
312              
313 0         0 $self->{CONFIG}->{join_string} = "\n";
314             # check sanity of arguments and capabilities of system:
315             # clean options accordingly
316 0   0     0 my $opts = delete($self->{CONFIG}->{parseropts}) || {enable_grep => 1};
317 0 0       0 $opts->{enable_grep} = 1 if ! exists $self->{enable_grep};
318              
319 0 0       0 if ($opts->{enable_grep}) {
320 0         0 eval { require Mail::Mbox::MessageParser::Grep };
  0         0  
321 0 0       0 delete $opts->{enable_grep} if $@;
322             }
323 0 0       0 if ($opts->{enable_cache}) {
324 0 0       0 delete $opts->{enable_cache} if ! exists $opts->{cache_file_name};
325 0         0 eval { require Mail::Mbox::MessageParser::Cache };
  0         0  
326 0 0       0 delete $opts->{enable_cache} if $@;
327             }
328              
329             Mail::Mbox::MessageParser::SETUP_CACHE(
330 0 0       0 { file_name => $opts->{cache_file_name} }
331             ) if $opts->{enable_cache};
332            
333 0   0     0 $opts->{enable_cache} ||= 0;
334 0         0 $opts->{file_handle} = $self->{READER};
335 0         0 $opts->{file_name} = $file_name;
336 0 0       0 if (not ref($self->{PARSER} = Mail::Mbox::MessageParser->new($opts))) {
337             # when Mail::Mbox::MessageParser object could not be created,
338             # try to fall back to the old parser
339 0         0 my %opt = @args;
340 0         0 $opt{ oldparser } = 1;
341 0         0 delete $opt{ parseropts };
342             # $source could be a GLOB which we need to rewind
343             # if it isn't, the BLOCK-eval should catch it.
344 0         0 eval { seek $source, $old_filepos, SEEK_SET };
  0         0  
345 0         0 return Mail::MboxParser->new($source, %opt);
346             }
347             }
348              
349             # do line-ending stuff
350 17 50       90 if (! exists $self->{CONFIG}->{newline}) {
351 17         77 $self->{CONFIG}->{newline} = 'AUTO';
352             }
353            
354 17         47 my $nl = $self->{CONFIG}->{newline};
355 17 50       139 if ($nl eq 'UNIX') { $self->{NL} = "\012" }
  0 50       0  
    50          
356 0         0 elsif ($nl eq 'WIN') { $self->{NL} = "\015\012" }
357 17         120 elsif ($nl eq 'AUTO') { $self->{NL} = $self->_detect_nl }
358 0         0 else { $self->{NL} = $nl }
359 17         60 $Mail::MboxParser::Mail::NL = $self->{NL};
360              
361 17 50       149 seek $self->{READER}, 0, SEEK_SET if ! $self->{PARSER};
362 17         64 return;
363             }
364              
365             # ----------------------------------------------------------------
366              
367             =over 4
368              
369             =item B
370              
371             Returns an array containing all messages in the mailbox respresented as
372             Mail::MboxParser::Mail objects. This method is _minimally_ quicker than
373             iterating over the mailbox using C but eats much more memory.
374             Memory-usage will grow linearly for each new message detected since this method
375             creates a huge array containing all messages. After creating this array, it
376             will be returned.
377              
378             =back
379              
380             =cut
381              
382             sub get_messages_new() {
383 0     0 0 0 my $self = shift;
384              
385 0         0 my $nl = $self->{NL};
386 0         0 my @messages;
387 0         0 my $p = $self->parser;
388 0         0 $p->reset;
389              
390 0         0 while (! $p->end_of_file) {
391 0         0 my $mailref = $p->read_next_email;
392 0         0 my ($header, $body) = split /$nl$nl/, $$mailref, 2;
393 0         0 push @messages,
394             Mail::MboxParser::Mail->new([ split(/$nl/, $header), '' ],
395             [ split /$nl/, $body ],
396             $self->{CONFIG});
397             }
398 0         0 $p->reset;
399 0         0 return @messages;
400             }
401            
402             sub get_messages_old() {
403 9     9 0 57 my $self = shift;
404              
405 9         95 local $/ = $self->{NL};
406              
407 9         21 my ($in_header, $in_body) = (0, 0);
408 9         18 my $header;
409 9         37 my (@header, @body);
410 9         67 my $h = $self->{READER};
411              
412 9         18 my $got_header;
413              
414             my @messages;
415              
416 9         419 seek $h, 0, SEEK_SET;
417 9         31 local *_;
418 9         299 while (<$h>) {
419              
420             # entering header
421 7354 100 100     25611 if (!$in_body && /$from_date/) {
422 9         35 ($in_header, $in_body) = (1, 0);
423 9         17 $got_header = 0;
424             }
425             # entering body
426 7354 100 100     26091 if ($in_header && /$empty_line/) {
427 73         125 ($in_header, $in_body) = (0, 1);
428 73         95 $got_header = 1;
429             }
430              
431             # just before entering next mail-header or running
432             # out of data, store message in Mail-object
433 7354 100 100     43680 if ((/$from_date/ || eof) && $got_header) {
      100        
434 73 100       238 push @body, $_ if eof; # don't forget last line!!
435 73         2413 my $m = Mail::MboxParser::Mail->new([ @header ], [ @body ], $self->{CONFIG});
436 73         140 push @messages, $m;
437 73         111 ($in_header, $in_body) = (1, 0);
438 73         102 undef $header;
439 73         465 (@header, @body) = ();
440 73         110 $got_header = 0;
441             }
442 7354 50       14069 if ($_) {
443 7354 100 66     22558 push @header, $_ if $in_header && !$got_header;
444 7354 100 66     41769 push @body, $_ if $in_body && $got_header;
445             }
446             }
447            
448 9 50       66 if (exists $self->{CONFIG}->{decode}) {
449 0         0 $Mail::MboxParser::Mail::Config->{decode} = $self->{CONFIG}->{decode};
450             }
451 9         139 return @messages;
452             }
453              
454             # ----------------------------------------------------------------
455              
456             =over 4
457              
458             =item B
459              
460             Returns the n-th message (first message has index 0) in a mailbox. Examine
461             C<$mb-Eerror> which contains an error-string if the message does not exist.
462             In this case, C returns undef.
463              
464             =back
465              
466             =cut
467              
468             sub get_message_new($) {
469 0     0 0 0 my ($self, $num) = @_;
470 0         0 my $oldpos = tell $self->{READER};
471 0         0 my $msg = $self->get_message_old($num);
472 0         0 seek $self->{READER}, $oldpos, SEEK_SET;
473 0         0 return $msg;
474             }
475              
476             sub get_message_old($) {
477 38     38 0 170 my ($self, $num) = @_;
478            
479 38         141 local $/ = $self->{NL};
480            
481 38         131 $self->reset_last;
482 38 100       394 $self->make_index if ! exists $self->{MSG_IDX};
483              
484 38         99 my $tmp_idx = $self->current_pos;
485 38         95 my $pos = $self->get_pos($num);
486            
487 38 50       171 if (my $err = $self->error) {
488 0         0 $self->set_pos($tmp_idx);
489 0         0 $self->{LAST_ERR} = $err;
490 0         0 return;
491             }
492              
493 38         84 $self->set_pos($pos);
494 38         100 my $msg = $self->next_message_old;
495 38         146 $self->set_pos($tmp_idx);
496 38         180 return $msg;
497             }
498              
499             # ----------------------------------------------------------------
500              
501             =over 4
502              
503             =item B
504              
505             This lets you iterate over a mailbox one mail after another. The great
506             advantage over C is the very low memory-comsumption. It will be
507             at a constant level throughout the execution of your script. Secondly, it
508             almost instantly begins spitting out Mail::MboxParser::Mail-objects since it
509             doesn't have to slurp in all mails before returing them.
510              
511             =back
512              
513             =cut
514              
515             sub next_message_new() {
516 0     0 0 0 my $self = shift;
517 0         0 $self->reset_last;
518 0         0 my $p = $self->parser;
519              
520 0 0 0     0 return undef if ref(\$p) eq 'SCALAR' or $p->end_of_file;
521              
522 0         0 seek $self->{READER}, $self->{CURR_POS}, SEEK_SET;
523 0         0 my $nl = $self->{NL};
524 0         0 my $mailref = $p->read_next_email;
525 0         0 my ($header, $body) = split /$nl$nl/, $$mailref, 2;
526 0         0 my $msg = Mail::MboxParser::Mail->new([ split(/$nl/, $header), '' ],
527             [ split /$nl/, $body ],
528             $self->{CONFIG});
529 0         0 $self->{CURR_POS} = $p->offset + $p->length;
530 0         0 return $msg;
531             }
532              
533             sub next_message_old() {
534 78     78 0 5343 my $self = shift;
535 78         244 $self->reset_last;
536              
537 78         311 local $/ = $self->{NL};
538              
539 78         143 my $h = $self->{READER};
540              
541 78         132 my ($in_header, $in_body) = (0, 0);
542 78         182 my $header;
543 78         96 my (@header, @body);
544              
545 78         99 my $got_header = 0;
546              
547 78         690 seek $h, $self->{CURR_POS}, SEEK_SET;
548              
549             # we need to force join_string to "" here because
550             # this method is also invoked by get_message_new():
551 78         110 my %newopts = %{ $self->{CONFIG} };
  78         579  
552 78         169 $newopts{ join_string } = '';
553              
554 78         168 local *_;
555 78         1987 while (<$h>) {
556              
557 8158 100 100     55610 $got_header = 1 if eof($h) || /$empty_line/ and $in_header;
      100        
558              
559 8158 100 100     42435 if (/$from_date/ || eof $h) {
560 152 100       462 push @body, $_ if eof $h;
561 152 100       314 if (! $got_header) {
562 78         145 ($in_header, $in_body) = (1, 0);
563             }
564             else {
565 74         246 $self->{CURR_POS} = tell($h) - length;
566 74         615 return Mail::MboxParser::Mail->new(\@header, \@body, \%newopts);
567             }
568             }
569              
570 8084 100 100     36103 if (/$empty_line/ && $got_header) {
571 1642         2039 ($in_header, $in_body) = (0, 1);
572 1642         2100 $got_header = 1;
573             }
574              
575 8084 100       16016 push @header, $_ if $in_header;
576 8084 100       39193 push @body, $_ if $in_body;
577            
578             }
579             }
580              
581             # ----------------------------------------------------------------
582              
583             =over 4
584              
585             =item B
586              
587             =item B
588              
589             =item B
590              
591             These three methods deal with the position of the internal filehandle backening
592             the mailbox. Once you have iterated over the whole mailbox using
593             C MboxParser has reached the end of the mailbox and you have to
594             do repositioning if you want to iterate again. You could do this with either
595             C or C.
596              
597             $mb->rewind; # equivalent to
598             $mb->set_pos(0);
599              
600             C reveals the current position in the mailbox and can be used to
601             later return to this position if you want to do tricky things. Mark that
602             C does *not* return the current line but rather the current
603             character as returned by Perl's tell() function.
604            
605             my $last_pos;
606             while (my $msg = $mb->next_message) {
607             # ...
608             if ($msg->header->{subject} eq 'I was looking for this') {
609             $last_pos = $mb->current_pos;
610             last; # bail out here and do something else
611             }
612             }
613            
614             # ...
615             # ...
616            
617             # now continue where we stopped:
618             $mb->set_pos($last_pos)
619             while (my $msg = $mb->next_message) {
620             # ...
621             }
622              
623             B Be very careful with these methods when using the parser of
624             C. This parser maintains its own state and you
625             shouldn't expect it to always be in sync with the state of C.
626             If you need some finer control over the parsing, better consider to use the
627             public interface as described in L
628             Mail::Mbox::MessageParser|Mail::Mbox::MessageParser>. Use C to get
629             the underlying parser object.
630              
631             This however may expose you to the same problems turned around:
632             C may loose its sync with its parser when you do that.
633              
634             Therefore: Just avoid any of the above for now and wait till
635             C has a stable interface.
636              
637             =back
638              
639             =cut
640              
641             sub set_pos($) {
642 76     76 1 119 my ($self, $pos) = @_;
643 76         192 $self->reset_last;
644 76         130 $self->{CURR_POS} = $pos;
645             }
646              
647             # ----------------------------------------------------------------
648              
649             sub rewind() {
650 0     0 1 0 my $self = shift;
651 0         0 $self->reset_last;
652 0         0 $self->set_pos(0);
653             }
654              
655             # ----------------------------------------------------------------
656              
657             sub current_pos() {
658 40     40 1 66 my $self = shift;
659 40         124 $self->reset_last;
660 40         88 return $self->{CURR_POS};
661             }
662              
663             # ----------------------------------------------------------------
664              
665             =over 4
666              
667             =item B
668              
669             You can force the creation of a message-index with this method. The
670             message-index is a mapping between the index-number of a message (0 ..
671             $mb->nmsgs - 1) and the byte-position of the filehandle. This is usually done
672             automatically for you once you call C hence the first call for a
673             particular message will be a little slower since the message-index first has to
674             be built. This is, however, done rather quickly.
675              
676             You can have a peek at the index if you are interested. The following produces
677             a nicely padded table (suitable for mailboxes up to 9.9999...GB ;-).
678            
679             $mb->make_index;
680             for (0 .. $mb->nmsgs - 1) {
681             printf "%5.5d => %10.10d\n",
682             $_, $mb->get_pos($_);
683             }
684              
685             =back
686              
687             =cut
688              
689             sub make_index() {
690 6     6 1 15 my $self = shift;
691              
692 6         28 local $/ = $self->{NL};
693            
694 6         30 $self->reset_last;
695 6         14 my $h = $self->{READER};
696            
697 6         42 seek $h, 0, SEEK_SET;
698            
699 6         15 my $c = 0;
700              
701 6         18 local *_;
702 6         332 while (<$h>) {
703 5442 100       24676 $self->{MSG_IDX}->{$c} = tell($h) - length, $c++
704             if /$from_date/;
705             }
706 6         70 seek $h, 0, SEEK_SET;
707             }
708              
709             # ----------------------------------------------------------------
710              
711             =over 4
712              
713             =item B
714              
715             This method takes the index-number of a certain message within the mailbox and
716             returns the corresponding position of the filehandle that represents that start
717             of the file.
718              
719             It is mainly used by C and you wouldn't really have to bother
720             using it yourself except for statistical purpose as demonstrated above along
721             with B.
722              
723             =back
724              
725             =cut
726              
727             sub get_pos($) {
728 38     38 1 53 my ($self, $num) = @_;
729 38         96 $self->reset_last;
730 38 50       106 if (exists $self->{MSG_IDX}) {
731 38 50       116 if (! exists $self->{MSG_IDX}{$num}) {
732 0         0 $self->{LAST_ERR} = "$num: No such message";
733             }
734 38         519 return $self->{MSG_IDX}{$num}
735             }
736 0         0 else { return }
737             }
738              
739             # ----------------------------------------------------------------
740              
741             =over 4
742              
743             =item B
744              
745             Returns the number of messages in a mailbox. You could naturally also call
746             get_messages in scalar-context, but this one wont create new objects. It just
747             counts them and thus it is much quicker and wont eat a lot of memory.
748              
749             =back
750              
751             =cut
752              
753             sub nmsgs() {
754 14     14 1 2976 my $self = shift;
755              
756 14         82 local $/ = $self->{NL};
757              
758 14 50       74 if (not $self->{READER}) { return "No mbox opened" }
  0         0  
759 14 100       81 if (not $self->{NMSGS}) {
760 12         34 my $h = $self->{READER};
761 12         91 seek $h, 0, SEEK_SET;
762 12         56 local *_;
763 12         644 while (<$h>) {
764 10884 100       46910 $self->{NMSGS}++ if /$from_date/;
765             }
766             }
767 14   50     222 return $self->{NMSGS} || 0;
768             }
769              
770             # ----------------------------------------------------------------
771              
772             =over 4
773              
774             =item B
775              
776             Returns the bare C object. If no such object exists
777             returns C.
778              
779             You can use this method to check whether the module actually uses the old or
780             new parser. If C returns a false value, it is using the old parsing
781             routines.
782              
783             =back
784              
785             =cut
786              
787 0     0 1 0 sub parser { shift->{PARSER} }
788              
789             # ----------------------------------------------------------------
790              
791             sub _detect_nl {
792            
793 17     17   55 my $self = shift;
794 17         38 my $h = $self->{READER};
795 17         38 my $newline;
796            
797 17         115 seek $h, 0, SEEK_SET;
798 17         290 while (sysread $h, (my $c), 1) {
799 48 100       304 if (ord($c) == 13) {
    100          
800 1         3 $newline = "\015";
801 1         6 sysread $h, (my $next), 1;
802 1 50       7 $newline .= "\012" if ord($next) == 10;
803 1         3 last;
804             }
805             elsif (ord($c) == 10) {
806 16         40 $newline = "\012";
807 16         49 last;
808             }
809             }
810 17         86 return $newline;
811             }
812              
813             # ----------------------------------------------------------------
814              
815             sub DESTROY {
816 17     17   7246 my $self = shift;
817 17         63 $self->{NMSGS} = undef;
818 17 50       2671 close $self->{READER} if defined $self->{READER};
819             }
820              
821             # ----------------------------------------------------------------
822              
823             1;
824              
825             __END__