File Coverage

blib/lib/MIME/Parser/Filer.pm
Criterion Covered Total %
statement 153 167 91.6
branch 49 72 68.0
condition 12 29 41.3
subroutine 31 33 93.9
pod 15 19 78.9
total 260 320 81.2


line stmt bran cond sub pod time code
1             package MIME::Parser::Filer;
2              
3             =head1 NAME
4              
5             MIME::Parser::Filer - manage file-output of the parser
6              
7              
8             =head1 SYNOPSIS
9              
10             Before reading further, you should see L to make sure that
11             you understand where this module fits into the grand scheme of things.
12             Go on, do it now. I'll wait.
13              
14             Ready? Ok... now read L<"DESCRIPTION"> below, and everything else
15             should make sense.
16              
17              
18             =head2 Public interface
19              
20             ### Create a "filer" of the desired class:
21             my $filer = MIME::Parser::FileInto->new($dir);
22             my $filer = MIME::Parser::FileUnder->new($basedir);
23             ...
24              
25             ### Want added security? Don't let outsiders name your files:
26             $filer->ignore_filename(1);
27              
28             ### Prepare for the parsing of a new top-level message:
29             $filer->init_parse;
30              
31             ### Return the path where this message's data should be placed:
32             $path = $filer->output_path($head);
33              
34              
35             =head2 Semi-public interface
36              
37             These methods might be overridden or ignored in some subclasses,
38             so they don't all make sense in all circumstances:
39              
40             ### Tweak the mapping from content-type to extension:
41             $emap = $filer->output_extension_map;
42             $emap->{"text/html"} = ".htm";
43              
44              
45              
46              
47             =head1 DESCRIPTION
48              
49              
50             =head2 How this class is used when parsing
51              
52             When a MIME::Parser decides that it wants to output a file to disk,
53             it uses its "Filer" object -- an instance of a MIME::Parser::Filer
54             subclass -- to determine where to put the file.
55              
56             Every parser has a single Filer object, which it uses for all
57             parsing. You can get the Filer for a given $parser like this:
58              
59             $filer = $parser->filer;
60              
61             At the beginning of each C, the filer's internal state
62             is reset by the parser:
63              
64             $parser->filer->init_parse;
65              
66             The parser can then get a path for each entity in the message
67             by handing that entity's header (a MIME::Head) to the filer
68             and having it do the work, like this:
69              
70             $new_file = $parser->filer->output_path($head);
71              
72             Since it's nice to be able to clean up after a parse (especially
73             a failed parse), the parser tells the filer when it has actually
74             used a path:
75              
76             $parser->filer->purgeable($new_file);
77              
78             Then, if you want to clean up the files which were created for a
79             particular parse (and also any directories that the Filer created),
80             you would do this:
81              
82             $parser->filer->purge;
83              
84              
85              
86             =head2 Writing your own subclasses
87              
88             There are two standard "Filer" subclasses (see below):
89             B, which throws all files from all parses
90             into the same directory, and B (preferred), which
91             creates a subdirectory for each message. Hopefully, these will be
92             sufficient for most uses, but just in case...
93              
94             The only method you have to override is L:
95              
96             $filer->output_path($head);
97              
98             This method is invoked by MIME::Parser when it wants to put a
99             decoded message body in an output file. The method should return a
100             path to the file to create. Failure is indicated by throwing an
101             exception.
102              
103             The path returned by C should be "ready for open()":
104             any necessary parent directories need to exist at that point.
105             These directories can be created by the Filer, if course, and they
106             should be marked as B if a purge should delete them.
107              
108             Actually, if your issue is more I the files go than
109             what they're named, you can use the default L
110             method and just override one of its components:
111              
112             $dir = $filer->output_dir($head);
113             $name = $filer->output_filename($head);
114             ...
115              
116              
117              
118             =head1 PUBLIC INTERFACE
119              
120              
121             =head2 MIME::Parser::Filer
122              
123             This is the abstract superclass of all "filer" objects.
124              
125             =over 4
126              
127             =cut
128              
129 20     20   142659 use strict;
  20         45  
  20         927  
130              
131             ### Kit modules:
132 20     20   728 use MIME::Tools qw(:msgtypes);
  20         42  
  20         3408  
133 20     20   149 use File::Spec;
  20         40  
  20         765  
134 20     20   137 use File::Path qw(rmtree);
  20         53  
  20         1213  
135 20     20   955 use MIME::WordDecoder;
  20         52  
  20         58854  
136              
137             ### Output path uniquifiers:
138             my $GFileNo = 0;
139             my $GSubdirNo = 0;
140              
141             ### Map content-type to extension.
142             ### If we can't map "major/minor", we try "major/*", then use "*/*".
143             my %DefaultTypeToExt =
144             qw(
145              
146             application/andrew-inset .ez
147             application/octet-stream .bin
148             application/oda .oda
149             application/pdf .pdf
150             application/pgp .pgp
151             application/postscript .ps
152             application/rtf .rtf
153             application/x-bcpio .bcpio
154             application/x-chess-pgn .pgn
155             application/x-cpio .cpio
156             application/x-csh .csh
157             application/x-dvi .dvi
158             application/x-gtar .gtar
159             application/x-gunzip .gz
160             application/x-hdf .hdf
161             application/x-latex .latex
162             application/x-mif .mif
163             application/x-netcdf .cdf
164             application/x-netcdf .nc
165             application/x-sh .sh
166             application/x-shar .shar
167             application/x-sv4cpio .sv4cpio
168             application/x-sv4crc .sv4crc
169             application/x-tar .tar
170             application/x-tcl .tcl
171             application/x-tex .tex
172             application/x-texinfo .texi
173             application/x-troff .roff
174             application/x-troff .tr
175             application/x-troff-man .man
176             application/x-troff-me .me
177             application/x-troff-ms .ms
178             application/x-ustar .ustar
179             application/x-wais-source .src
180             application/zip .zip
181              
182             audio/basic .snd
183             audio/ulaw .au
184             audio/x-aiff .aiff
185             audio/x-wav .wav
186              
187             image/gif .gif
188             image/ief .ief
189             image/jpeg .jpg
190             image/png .png
191             image/xbm .xbm
192             image/tiff .tif
193             image/x-cmu-raster .ras
194             image/x-portable-anymap .pnm
195             image/x-portable-bitmap .pbm
196             image/x-portable-graymap .pgm
197             image/x-portable-pixmap .ppm
198             image/x-rgb .rgb
199             image/x-xbitmap .xbm
200             image/x-xpixmap .xpm
201             image/x-xwindowdump .xwd
202              
203             text/* .txt
204             text/html .html
205             text/plain .txt
206             text/richtext .rtx
207             text/tab-separated-values .tsv
208             text/x-setext .etx
209             text/x-vcard .vcf
210              
211             video/mpeg .mpg
212             video/quicktime .mov
213             video/x-msvideo .avi
214             video/x-sgi-movie .movie
215              
216             message/* .msg
217              
218             */* .dat
219              
220             );
221              
222             #------------------------------
223              
224             =item new INITARGS...
225              
226             I
227             Create a new outputter for the given parser.
228             Any subsequent arguments are given to init(), which subclasses should
229             override for their own use (the default init does nothing).
230              
231             =cut
232              
233             sub new {
234 79     79 1 240771 my ($class, @initargs) = @_;
235 79         4630 my $self = bless {
236             MPF_Prefix => "msg",
237             MPF_Dir => ".",
238             MPF_Ext => { %DefaultTypeToExt },
239             MPF_Purgeable => [], ### files created by the last parse
240              
241             MPF_MaxName => 80, ### max filename before treated as evil
242             MPF_TrimRoot => 14, ### trim root to this length
243             MPF_TrimExt => 3, ### trim extension to this length
244             }, $class;
245 79         614 $self->init(@initargs);
246 79         340 $self;
247             }
248              
249       1 1   sub init {
250             ### no-op
251             }
252              
253             #------------------------------
254             # My PID, but untainted
255             #
256             sub untainted_pid
257             {
258 56 50   56 0 652 if ($$ =~ /^(\d+)$/) {
259 56         340 return $1;
260             }
261             # Can't happen...
262 0         0 return "0";
263             }
264              
265             #------------------------------
266             #
267             # cleanup_dir
268             #
269             # Instance method, private.
270             # Cleanup a directory, defaulting empty to "."
271             #
272             sub cleanup_dir {
273 78     78 0 169 my ($self, $dir) = @_;
274 78 50 33     484 $dir = '.' if (!defined($dir) || ($dir eq '')); # coerce empty to "."
275 78 50       197 $dir = '/.' if ($dir eq '/'); # coerce "/" so "$dir/$filename" works
276 78         235 $dir =~ s|/$||; # be nice: get rid of any trailing "/"
277 78         322 $dir;
278             }
279              
280             #------------------------------
281              
282             =item results RESULTS
283              
284             I
285             Link this filer to a MIME::Parser::Results object which will
286             tally the messages. Notice that we avoid linking it to the
287             parser to avoid circular reference!
288              
289             =cut
290              
291             sub results {
292 143     143 1 321 my ($self, $results) = @_;
293 143 50       670 $self->{MPF_Results} = $results if (@_ > 1);
294 143         362 $self->{MPF_Results};
295             }
296              
297             ### Log debug messages:
298             sub debug {
299 296     296 0 443 my $self = shift;
300 296 50       926 if (MIME::Tools->debugging()) {
301 0 0       0 if ($self->{MPF_Results}) {
302 0         0 unshift @_, $self->{MPF_Results}->indent;
303 0         0 $self->{MPF_Results}->msg($M_DEBUG, @_);
304             }
305 0         0 MIME::Tools::debug(@_);
306             }
307             }
308              
309             ### Log warning messages:
310             sub whine {
311 10     10 0 20 my $self = shift;
312 10 50       37 if ($self->{MPF_Results}) {
313 10         79 unshift @_, $self->{MPF_Results}->indent;
314 10         61 $self->{MPF_Results}->msg($M_WARNING, @_);
315             }
316 10         71 MIME::Tools::whine(@_);
317             }
318              
319             #------------------------------
320              
321             =item init_parse
322              
323             I
324             Prepare to start parsing a new message.
325             Subclasses should always be sure to invoke the inherited method.
326              
327             =cut
328              
329             sub init_parse {
330 65     65 1 145 my $self = shift;
331 65         225 $self->{MPF_Purgeable} = [];
332             }
333              
334             #------------------------------
335              
336             =item evil_filename FILENAME
337              
338             I
339             Is this an evil filename; i.e., one which should not be used
340             in generating a disk file name? It is if any of these are true:
341              
342             * it is empty or entirely whitespace
343             * it contains leading or trailing whitespace
344             * it is a string of dots: ".", "..", etc.
345             * it contains characters not in the set: "A" - "Z", "a" - "z",
346             "0" - "9", "-", "_", "+", "=", ".", ",", "@", "#",
347             "$", and " ".
348             * it is too long
349              
350             If you just want to change this behavior, you should override
351             this method in the subclass of MIME::Parser::Filer that you use.
352              
353             B at the time this method is invoked, the FILENAME has
354             already been unmime'd into the local character set.
355             If you're using any character set other than ASCII, ISO-8859-*,
356             or UTF-8, the interpretation of the "path" characters might be
357             very different, and you will probably need to override this method.
358             See L for more details.
359              
360             B subclasses of MIME::Parser::Filer which override
361             output_path() might not consult this method; note, however, that
362             the built-in subclasses do consult it.
363              
364             I
365             version. Thanks to Nickolay Saukh for noting that evil is in the
366             eye of the beholder.>
367              
368             =cut
369              
370             sub evil_filename {
371 79     79 1 2555 my ($self, $name) = @_;
372              
373 79         352 $self->debug("is this evil? '$name'");
374              
375 79 50 33     374 return 1 if (!defined($name) or ($name eq '')); ### empty
376 79 100       680 return 1 if ($name =~ m{(^\s)|(\s+\Z)}); ### leading/trailing whitespace
377 69 100       260 return 1 if ($name =~ m{^\.+\Z}); ### dots
378 65 100       258 return 1 if ($name =~ /[^-A-Z0-9_+=.,@\#\$\% ]/i); # Only allow good chars
379             return 1 if ($self->{MPF_MaxName} and
380 52 50 33     305 (length($name) > $self->{MPF_MaxName}));
381 52         134 $self->debug("it's ok");
382 52         154 0;
383             }
384              
385             #------------------------------
386              
387             =item exorcise_filename FILENAME
388              
389             I
390             If a given filename is evil (see L) we try to
391             rescue it by performing some basic operations: shortening it,
392             removing bad characters, etc., and checking each against
393             evil_filename().
394              
395             Returns the exorcised filename (which is guaranteed to not
396             be evil), or undef if it could not be salvaged.
397              
398             B at the time this method is invoked, the FILENAME has
399             already been unmime'd into the local character set.
400             If you're using anything character set other than ASCII, ISO-8859-*,
401             or UTF-8, the interpretation of the "path" characters might be very
402             very different, and you will probably need to override this method.
403             See L for more details.
404              
405             =cut
406              
407             sub exorcise_filename {
408 13     13 1 4888 my ($self, $fname) = @_;
409              
410             ### Isolate to last path element:
411 13         29 my $last = $fname;
412              
413             ### Path separators are / or \
414 13         65 $last =~ s{^.*[/\\]}{};
415              
416             ### Convert semi-evil characters to underscores
417 13         43 $last =~ s/[\/\\\[\]:]/_/g;
418 13 100 66     63 if ($last and !$self->evil_filename($last)) {
419 3         14 $self->debug("looks like I can use the last path element");
420 3         12 return $last;
421             }
422              
423             ### Break last element into root and extension, and truncate:
424 10 100       75 my ($root, $ext) = (($last =~ /^(.*)\.([^\.]+)\Z/)
425             ? ($1, $2)
426             : ($last, ''));
427             ### Delete leading and trailing whitespace
428 10         29 $root =~ s/^\s+//;
429 10         29 $ext =~ s/\s+$//;
430 10   50     48 $root = substr($root, 0, ($self->{MPF_TrimRoot} || 14));
431 10   50     40 $ext = substr($ext, 0, ($self->{MPF_TrimExt} || 3));
432 10 100       52 $ext =~ /^\w+$/ or $ext = "dat";
433 10 50       34 my $trunc = $root . ($ext ? ".$ext" : '');
434 10 100       24 if (!$self->evil_filename($trunc)) {
435 6         13 $self->debug("looks like I can use the truncated last path element");
436 6         27 return $trunc;
437             }
438              
439             ### Remove all bad characters
440 4         23 $trunc =~ s/([^-A-Z0-9_+=.,@\#\$ ])/sprintf("%%%02X", unpack("C", $1))/ige;
  9         62  
441 4 50       13 if (!$self->evil_filename($trunc)) {
442 4         13 $self->debug("looks like I can use a munged version of the truncated last path element");
443 4         16 return $trunc;
444             }
445              
446             ### Hope that works:
447 0         0 undef;
448             }
449              
450             #------------------------------
451              
452             =item find_unused_path DIR, FILENAME
453              
454             I
455             We have decided on an output directory and tentative filename,
456             but there is a chance that it might already exist. Keep
457             adding a numeric suffix "-1", "-2", etc. to the filename
458             until an unused path is found, and then return that path.
459              
460             The suffix is actually added before the first "." in the filename
461             is there is one; for example:
462              
463             picture.gif archive.tar.gz readme
464             picture-1.gif archive-1.tar.gz readme-1
465             picture-2.gif archive-2.tar.gz readme-2
466             ... ... ...
467             picture-10.gif
468             ...
469              
470             This can be a costly operation, and risky if you don't want files
471             renamed, so it is in your best interest to minimize situations
472             where these kinds of collisions occur. Unfortunately, if
473             a multipart message gives all of its parts the same recommended
474             filename, and you are placing them all in the same directory,
475             this method might be unavoidable.
476              
477             =cut
478              
479             sub find_unused_path {
480 90     90 1 293 my ($self, $dir, $fname) = @_;
481 90         159 my $i = 0;
482 90         135 while (1) {
483              
484             ### Create suffixed name (from filename), and see if we can use it:
485 96 100       272 my $suffix = ($i ? "-$i" : "");
486 96         200 my $sname = $fname; $sname =~ s/^(.*?)(\.|\Z)/$1$suffix$2/;
  96         1042  
487 96         1468 my $path = File::Spec->catfile($dir, $sname);
488 96 100       6150 if (! -e $path) { ### it's good!
489 90 100       350 $i and $self->whine("collision with $fname in $dir: using $path");
490 90         650 return $path;
491             }
492 6         28 $self->debug("$path already taken");
493 6         14 } continue { ++$i; }
494             }
495              
496             #------------------------------
497              
498             =item ignore_filename [YESNO]
499              
500             I
501             Return true if we should always ignore recommended filenames in
502             messages, choosing instead to always generate our own filenames.
503             With argument, sets this value.
504              
505             B subclasses of MIME::Parser::Filer which override
506             output_path() might not honor this setting; note, however, that
507             the built-in subclasses honor it.
508              
509             =cut
510              
511             sub ignore_filename {
512 37     37 1 76 my $self = shift;
513 37 50       98 $self->{MPF_IgnoreFilename} = $_[0] if @_;
514 37         206 $self->{MPF_IgnoreFilename};
515             }
516              
517             #------------------------------
518              
519             =item output_dir HEAD
520              
521             I
522             Return the output directory for the given header.
523             The default method returns ".".
524              
525             =cut
526              
527             sub output_dir {
528 0     0 1 0 my ($self, $head) = @_;
529 0         0 return ".";
530             }
531              
532             #------------------------------
533              
534             =item output_filename HEAD
535              
536             I
537             A given recommended filename was either not given, or it was judged
538             to be evil. Return a fake name, possibly using information in the
539             message HEADer. Note that this is just the filename, not the full path.
540              
541             Used by L.
542             If you're using the default C, you probably don't
543             need to worry about avoiding collisions with existing files;
544             we take care of that in L.
545              
546             =cut
547              
548             sub output_filename {
549 53     53 1 114 my ($self, $head) = @_;
550              
551             ### Get the recommended name:
552 53         176 my $recommended = $head->recommended_filename;
553              
554             ### Get content type:
555 53   100     198 my ($type, $subtype) = split m{/}, $head->mime_type; $subtype ||= '';
  53         192  
556              
557             ### Get recommended extension, being quite conservative:
558 53 50 33     241 my $recommended_ext = (($recommended and ($recommended =~ m{(\.\w+)\Z}))
559             ? $1
560             : undef);
561              
562             ### Try and get an extension, honoring a given one first:
563             my $ext = ($recommended_ext ||
564             $self->{MPF_Ext}{"$type/$subtype"} ||
565             $self->{MPF_Ext}{"$type/*"} ||
566 53   0     422 $self->{MPF_Ext}{"*/*"} ||
567             ".dat");
568              
569             ### Get a prefix:
570 53         117 ++$GFileNo;
571 53         202 return ($self->output_prefix . "-" . untainted_pid() . "-$GFileNo$ext");
572             }
573              
574             #------------------------------
575              
576             =item output_prefix [PREFIX]
577              
578             I
579             Get the short string that all filenames for extracted body-parts
580             will begin with (assuming that there is no better "recommended filename").
581             The default is F<"msg">.
582              
583             If PREFIX I given, the current output prefix is returned.
584             If PREFIX I given, the output prefix is set to the new value,
585             and the previous value is returned.
586              
587             Used by L.
588              
589             B subclasses of MIME::Parser::Filer which override
590             output_path() or output_filename() might not honor this setting;
591             note, however, that the built-in subclasses honor it.
592              
593             =cut
594              
595             sub output_prefix {
596 53     53 1 111 my ($self, $prefix) = @_;
597 53 50       143 $self->{MPF_Prefix} = $prefix if (@_ > 1);
598 53         219 $self->{MPF_Prefix};
599             }
600              
601             #------------------------------
602              
603             =item output_type_ext
604              
605             I
606             Return a reference to the hash used by the default
607             L for mapping from content-types
608             to extensions when there is no default extension to use.
609              
610             $emap = $filer->output_typemap;
611             $emap->{'text/plain'} = '.txt';
612             $emap->{'text/html'} = '.html';
613             $emap->{'text/*'} = '.txt';
614             $emap->{'*/*'} = '.dat';
615              
616             B subclasses of MIME::Parser::Filer which override
617             output_path() or output_filename() might not consult this hash;
618             note, however, that the built-in subclasses consult it.
619              
620             =cut
621              
622             sub output_type_ext {
623 0     0 1 0 my $self = shift;
624 0         0 return $self->{MPF_Ext};
625             }
626              
627             #------------------------------
628              
629             =item output_path HEAD
630              
631             I
632             Given a MIME head for a file to be extracted, come up with a good
633             output pathname for the extracted file. This is the only method
634             you need to worry about if you are building a custom filer.
635              
636             The default implementation does a lot of work; subclass
637             implementers I should try to just override its components
638             instead of the whole thing. It works basically as follows:
639              
640             $directory = $self->output_dir($head);
641              
642             $filename = $head->recommended_filename();
643             if (!$filename or
644             $self->ignore_filename() or
645             $self->evil_filename($filename)) {
646             $filename = $self->output_filename($head);
647             }
648              
649             return $self->find_unused_path($directory, $filename);
650              
651             B There are many, many, many ways you might want to control
652             the naming of files, based on your application. If you don't like
653             the behavior of this function, you can easily define your own subclass
654             of MIME::Parser::Filer and override it there.
655              
656             B Nickolay Saukh pointed out that, given the subjective nature of
657             what is "evil", this function really shouldn't I about an evil
658             filename, but maybe just issue a I message. I considered that,
659             but then I thought: if debugging were off, people wouldn't know why
660             (or even if) a given filename had been ignored. In mail robots
661             that depend on externally-provided filenames, this could cause
662             hard-to-diagnose problems. So, the message is still a warning.
663              
664             I
665             implementation, and for making some good suggestions. Thanks also to
666             Achim Bohnet for pointing out that there should be a hookless, OO way of
667             overriding the output path.>
668              
669             =cut
670              
671             sub output_path {
672 90     90 1 185 my ($self, $head) = @_;
673              
674             ### Get the output directory:
675 90         271 my $dir = $self->output_dir($head);
676              
677             ### Get the output filename as UTF-8
678 90         301 my $fname = $head->recommended_filename;
679              
680             ### Can we use it:
681 90 100       390 if (!defined($fname)) {
    50          
    100          
682 53         201 $self->debug("no filename recommended: synthesizing our own");
683 53         178 $fname = $self->output_filename($head);
684             }
685             elsif ($self->ignore_filename) {
686 0         0 $self->debug("ignoring all external filenames: synthesizing our own");
687 0         0 $fname = $self->output_filename($head);
688             }
689             elsif ($self->evil_filename($fname)) {
690              
691             ### Can we save it by just taking the last element?
692 5         64 my $ex = $self->exorcise_filename($fname);
693 5 50 33     68 if (defined($ex) and !$self->evil_filename($ex)) {
694 5         44 $self->whine("Provided filename '$fname' is regarded as evil, ",
695             "but I was able to exorcise it and get something ",
696             "usable.");
697 5         13 $fname = $ex;
698             }
699             else {
700 0         0 $self->whine("Provided filename '$fname' is regarded as evil; ",
701             "I'm ignoring it and supplying my own.");
702 0         0 $fname = $self->output_filename($head);
703             }
704             } else {
705             # Untaint $fname... we know it's not evil
706 32 50       166 if ($fname =~ /^(.*)$/) {
707 32         93 $fname = $1;
708             }
709             }
710              
711 90         365 $self->debug("planning to use '$fname'");
712              
713             ### Resolve collisions and return final path:
714 90         358 return $self->find_unused_path($dir, $fname);
715             }
716              
717             #------------------------------
718              
719             =item purge
720              
721             I
722             Purge all files/directories created by the last parse.
723             This method simply goes through the purgeable list in reverse order
724             (see L) and removes all existing files/directories in it.
725             You should not need to override this method.
726              
727             =cut
728              
729             sub purge {
730 26     26 1 63 my ($self) = @_;
731 26         40 foreach my $path (reverse @{$self->{MPF_Purgeable}}) {
  26         72  
732 73 50       1209 (-e $path) or next; ### must check: might delete DIR before DIR/FILE
733 73         17615 rmtree($path, 0, 1);
734 73 50       2371 (-e $path) and $self->whine("unable to purge: $path");
735             }
736 26         365 1;
737             }
738              
739             #------------------------------
740              
741             =item purgeable [FILE]
742              
743             I
744             Add FILE to the list of "purgeable" files/directories (those which
745             will be removed if you do a C).
746             You should not need to override this method.
747              
748             If FILE is not given, the "purgeable" list is returned.
749             This may be used for more-sophisticated purging.
750              
751             As a special case, invoking this method with a FILE that is an
752             arrayref will replace the purgeable list with a copy of the
753             array's contents, so [] may be used to clear the list.
754              
755             Note that the "purgeable" list is cleared when a parser begins a
756             new parse; therefore, if you want to use purge() to do cleanup,
757             you I do so I starting a new parse!
758              
759             =cut
760              
761             sub purgeable {
762 189     189 1 479 my ($self, $path) = @_;
763 189 100       615 return @{$self->{MPF_Purgeable}} if (@_ == 1);
  25         128  
764              
765 164 100       460 if (ref($path)) { $self->{MPF_Purgeable} = [ @$path ]; }
  65         258  
766 99         157 else { push @{$self->{MPF_Purgeable}}, $path; }
  99         331  
767 164         390 1;
768             }
769              
770             =back
771              
772             =cut
773              
774              
775             #------------------------------------------------------------
776             #------------------------------------------------------------
777              
778             =head2 MIME::Parser::FileInto
779              
780             This concrete subclass of MIME::Parser::Filer supports filing
781             into a given directory.
782              
783             =over 4
784              
785             =cut
786              
787             package MIME::Parser::FileInto;
788              
789 20     20   186 use strict;
  20         43  
  20         762  
790 20     20   117 use vars qw(@ISA);
  20         34  
  20         3499  
791             @ISA = qw(MIME::Parser::Filer);
792              
793             #------------------------------
794              
795             =item init DIRECTORY
796              
797             I
798             Set the directory where all files will go.
799              
800             =cut
801              
802             sub init {
803 75     75   233 my ($self, $dir) = @_;
804 75         277 $self->{MPFI_Dir} = $self->cleanup_dir($dir);
805             }
806              
807             #------------------------------
808             #
809             # output_dir HEAD
810             #
811             # I
812             # Return the output directory where the files go.
813             #
814             sub output_dir {
815 92     92   244 shift->{MPFI_Dir};
816             }
817              
818             =back
819              
820             =cut
821              
822              
823              
824              
825             #------------------------------------------------------------
826             #------------------------------------------------------------
827              
828             =head2 MIME::Parser::FileUnder
829              
830             This concrete subclass of MIME::Parser::Filer supports filing under
831             a given directory, using one subdirectory per message, but with
832             all message parts in the same directory.
833              
834             =over 4
835              
836             =cut
837              
838             package MIME::Parser::FileUnder;
839              
840 20     20   144 use strict;
  20         39  
  20         720  
841 20     20   102 use vars qw(@ISA);
  20         50  
  20         8657  
842             @ISA = qw(MIME::Parser::Filer);
843              
844             #------------------------------
845              
846             =item init BASEDIR, OPTSHASH...
847              
848             I
849             Set the base directory which will contain the message directories.
850             If used, then each parse of begins by creating a new subdirectory
851             of BASEDIR where the actual parts of the message are placed.
852             OPTSHASH can contain the following:
853              
854             =over 4
855              
856             =item DirName
857              
858             Explicitly set the name of the subdirectory which is created.
859             The default is to use the time, process id, and a sequence number,
860             but you might want a predictable directory.
861              
862             =item Purge
863              
864             Automatically purge the contents of the directory (including all
865             subdirectories) before each parse. This is really only needed if
866             using an explicit DirName, and is provided as a convenience only.
867             Currently we use the 1-arg form of File::Path::rmtree; you should
868             familiarize yourself with the caveats therein.
869              
870             =back
871              
872             The output_dir() will return the path to this message-specific directory
873             until the next parse is begun, so you can do this:
874              
875             use File::Path;
876              
877             $parser->output_under("/tmp");
878             $ent = eval { $parser->parse_open($msg); }; ### parse
879             if (!$ent) { ### parse failed
880             rmtree($parser->output_dir);
881             die "parse failed: $@";
882             }
883             else { ### parse succeeded
884             ...do stuff...
885             }
886              
887             =cut
888              
889             sub init {
890 3     3   10 my ($self, $basedir, %opts) = @_;
891              
892 3         52 $self->{MPFU_Base} = $self->cleanup_dir($basedir);
893 3         17 $self->{MPFU_DirName} = $opts{DirName};
894 3         12 $self->{MPFU_Purge} = $opts{Purge};
895             }
896              
897             #------------------------------
898             #
899             # init_parse
900             #
901             # I
902             # Prepare to start parsing a new message.
903             #
904             sub init_parse {
905 3     3   7 my $self = shift;
906              
907             ### Invoke inherited method first!
908 3         21 $self->SUPER::init_parse;
909              
910             ### Determine the subdirectory of their base to use:
911             my $subdir = (defined($self->{MPFU_DirName})
912             ? $self->{MPFU_DirName}
913 3 50       17 : ("msg-" . scalar(time) . "-" .
914             MIME::Parser::Filer::untainted_pid() . "-" . $GSubdirNo++));
915 3         25 $self->debug("subdir = $subdir");
916              
917             ### Determine full path to the per-message output directory:
918 3         71 $self->{MPFU_Dir} = File::Spec->catfile($self->{MPFU_Base}, $subdir);
919              
920             ### Remove and re-create the per-message output directory:
921 3 50       13 rmtree $self->output_dir if $self->{MPFU_Purge};
922 3 50 33     12 (-d $self->output_dir) or
923             mkdir $self->output_dir, 0700 or
924             die "mkdir ".$self->output_dir.": $!\n";
925              
926             ### Add the per-message output directory to the puregables:
927 3         22 $self->purgeable($self->output_dir);
928 3         9 1;
929             }
930              
931             #------------------------------
932             #
933             # output_dir HEAD
934             #
935             # I
936             # Return the output directory that we used for the last parse.
937             #
938             sub output_dir {
939 13     13   940 shift->{MPFU_Dir};
940             }
941              
942             =back
943              
944             =cut
945              
946             1;
947             __END__