home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / Filer.pm < prev    next >
Encoding:
Perl POD Document  |  2002-06-14  |  25.1 KB  |  923 lines

  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<MIME::Parser> 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 overriden 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<parse()>, 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<MIME::Parser::FileInto>, which throws all files from all parses
  90. into the same directory, and B<MIME::Parser::FileUnder> (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<output_path()|/output_path>:
  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<output_path()> 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<purgeable()> if a purge should delete them.
  107.  
  108. Actually, if your issue is more I<where> the files go than
  109. what they're named, you can use the default L<output_path()|/output_path>
  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. use strict;
  130.  
  131. ### Kit modules:
  132. use MIME::Tools qw(:msgtypes);
  133. use File::Spec;
  134. use File::Path qw(rmtree);
  135. use MIME::WordDecoder;
  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.  
  225. =item new INITARGS...
  226.  
  227. I<Class method, constructor.>
  228. Create a new outputter for the given parser.
  229. Any subsequent arguments are given to init(), which subclasses should
  230. override for their own use (the default init does nothing).
  231.  
  232. =cut
  233.  
  234. sub new {
  235.     my ($class, @initargs) = @_;
  236.     my $self = bless {
  237.     MPF_Prefix    => "msg",
  238.     MPF_Dir       => ".",
  239.     MPF_Ext       => { %DefaultTypeToExt },
  240.     MPF_Purgeable => [],       ### files created by the last parse
  241.  
  242.     MPF_MaxName   => 80,       ### max filename before treated as evil
  243.     MPF_TrimRoot  => 14,       ### trim root to this length
  244.     MPF_TrimExt   => 3,        ### trim extension to this length
  245.     }, $class;
  246.     $self->init(@initargs);
  247.     $self;
  248. }
  249.  
  250. sub init {
  251.     ### no-op
  252. }
  253.  
  254. #------------------------------
  255. #
  256. # cleanup_dir
  257. #
  258. # Instance method, private.
  259. # Cleanup a directory, defaulting empty to "."
  260. #
  261. sub cleanup_dir {
  262.     my ($self, $dir) = @_;
  263.     $dir = '.' if (!defined($dir) || ($dir eq ''));   # coerce empty to "."
  264.     $dir = '/.' if ($dir eq '/');   # coerce "/" so "$dir/$filename" works
  265.     $dir =~ s|/$||;                 # be nice: get rid of any trailing "/"
  266.     $dir;
  267. }
  268.  
  269. #------------------------------
  270.  
  271. =item results RESULTS
  272.  
  273. I<Instance method.>
  274. Link this filer to a MIME::Parser::Results object which will
  275. tally the messages.  Notice that we avoid linking it to the
  276. parser to avoid circular reference!
  277.  
  278. =cut
  279.  
  280. sub results {
  281.     my ($self, $results) = @_;
  282.     $self->{MPF_Results} = $results if (@_ > 1);
  283.     $self->{MPF_Results};
  284. }
  285.  
  286. ### Log debug messages:
  287. sub debug {
  288.     my $self = shift;
  289.     if ($self->{MPF_Results}) {
  290.     unshift @_, $self->{MPF_Results}->indent;
  291.     $self->{MPF_Results}->msg($M_DEBUG, @_);
  292.     }
  293.     MIME::Tools::debug(@_);
  294. }
  295.  
  296. ### Log warning messages:
  297. sub whine {
  298.     my $self = shift;
  299.     if ($self->{MPF_Results}) {
  300.     unshift @_, $self->{MPF_Results}->indent;
  301.     $self->{MPF_Results}->msg($M_WARNING, @_);
  302.     }
  303.     MIME::Tools::whine(@_);
  304. }
  305.  
  306. #------------------------------
  307.  
  308. =item init_parse
  309.  
  310. I<Instance method.>
  311. Prepare to start parsing a new message.
  312. Subclasses should always be sure to invoke the inherited method.
  313.  
  314. =cut
  315.  
  316. sub init_parse {
  317.     my $self = shift;
  318.     $self->{MPF_Purgeable} = [];
  319. }
  320.  
  321. #------------------------------
  322.  
  323. =item evil_filename FILENAME
  324.  
  325. I<Instance method.>
  326. Is this an evil filename; i.e., one which should not be used
  327. in generating a disk file name?  It is if any of these are true:
  328.  
  329.     * it is empty
  330.     * it is a string of dots: ".", "..", etc.
  331.     * it contains a known "path" character: '/' '\' ':' '[' ']'
  332.     * it is too long
  333.  
  334. If you just want to change this behavior, you should override
  335. this method in the subclass of MIME::Parser::Filer that you use.
  336.  
  337. B<Warning:> at the time this method is invoked, the FILENAME has
  338. already been unmime'd into the local character set.
  339. If you're using any character set other than ASCII, ISO-8859-*,
  340. or UTF-8, the interpretation of the "path" characters might be
  341. very different, and you will probably need to override this method.
  342. See L<MIME::WordDecoder/unmime> for more details.
  343.  
  344. B<Note:> subclasses of MIME::Parser::Filer which override
  345. output_path() might not consult this method; note, however, that
  346. the built-in subclasses do consult it.
  347.  
  348. I<Thanks to Andrew Pimlott for finding a real dumb bug in the original
  349. version.  Thanks to Nickolay Saukh for noting that evil is in the
  350. eye of the beholder.>
  351.  
  352. =cut
  353.  
  354. sub evil_filename {
  355.     my ($self, $name) = @_;
  356.  
  357.     $self->debug("is this evil? '$name'");
  358.  
  359.     return 1 if (!defined($name) or ($name eq ''));   ### empty
  360.     return 1 if ($name =~ m{^\.+\Z});         ### dots
  361.     return 1 if ($name =~ tr{\\/:[]}{});      ### path characters
  362.     return 1 if ($self->{MPF_MaxName} and 
  363.          (length($name) > $self->{MPF_MaxName}));
  364.     
  365.     $self->debug("it's ok");
  366.     0;
  367. }
  368.  
  369. #------------------------------
  370.  
  371. =item exorcise_filename FILENAME
  372.  
  373. I<Instance method.>
  374. If a given filename is evil (see L</evil_filename>) we try to
  375. rescue it by performing some basic operations: shortening it,
  376. removing bad characters, etc., and checking each against
  377. evil_filename().
  378.  
  379. Returns the exorcised filename (which is guaranteed to not
  380. be evil), or undef if it could not be salvaged.
  381.  
  382. B<Warning:> at the time this method is invoked, the FILENAME has
  383. already been unmime'd into the local character set.
  384. If you're using anything character set other than ASCII, ISO-8859-*,
  385. or UTF-8, the interpretation of the "path" characters might be very
  386. very different, and you will probably need to override this method.
  387. See L<MIME::WordDecoder/unmime> for more details.
  388.  
  389. =cut
  390.  
  391. sub exorcise_filename {
  392.     my ($self, $fname) = @_;
  393.     
  394.     ### Isolate to last path element:
  395.     my $last = $fname; $last =~ s{^.*[/\\\[\]:]}{};
  396.     if ($last and !$self->evil_filename($last)) {
  397.     $self->debug("looks like I can use the last path element");
  398.     return $last;
  399.     }   
  400.  
  401.     ### Break last element into root and extension, and truncate:
  402.     my ($root, $ext) = (($last =~ /^(.*)\.([^\.]+)\Z/) 
  403.             ? ($1, $2)
  404.             : ($last, ''));
  405.     $root = substr($root, 0, ($self->{MPF_TrimRoot} || 14));
  406.     $ext  = substr($ext,  0, ($self->{MPF_TrimExt}  ||  3));
  407.     $ext =~ /^\w+$/ or $ext = "dat";
  408.     my $trunc = $root . ($ext ? ".$ext" : '');
  409.     if (!$self->evil_filename($trunc)) {
  410.     $self->debug("looks like I can use the truncated last path element");
  411.     return $trunc;
  412.     }
  413.         
  414.     ### Hope that works:
  415.     undef;
  416. }
  417.  
  418. #------------------------------
  419.  
  420. =item find_unused_path DIR, FILENAME
  421.  
  422. I<Instance method, subclasses only.>
  423. We have decided on an output directory and tentative filename,
  424. but there is a chance that it might already exist.  Keep
  425. adding a numeric suffix "-1", "-2", etc. to the filename
  426. until an unused path is found, and then return that path.
  427.  
  428. The suffix is actually added before the first "." in the filename
  429. is there is one; for example:
  430.  
  431.     picture.gif       archive.tar.gz      readme
  432.     picture-1.gif     archive-1.tar.gz    readme-1
  433.     picture-2.gif     archive-2.tar.gz    readme-2
  434.     ...               ...                 ...
  435.     picture-10.gif
  436.     ...
  437.  
  438. This can be a costly operation, and risky if you don't want files
  439. renamed, so it is in your best interest to minimize situations
  440. where these kinds of collisions occur.  Unfortunately, if
  441. a multipart message gives all of its parts the same recommended
  442. filename, and you are placing them all in the same directory,
  443. this method might be unavoidable.
  444.  
  445. =cut
  446.  
  447. sub find_unused_path {
  448.     my ($self, $dir, $fname) = @_;
  449.     my $i = 0;
  450.     while (1) {
  451.  
  452.     ### Create suffixed name (from filename), and see if we can use it:
  453.     my $suffix = ($i ? "-$i" : "");
  454.     my $sname = $fname; $sname =~ s/^(.*?)(\.|\Z)/$1$suffix$2/;
  455.     my $path = File::Spec->catfile($dir, $sname);
  456.     if (! -e $path) {   ### it's good!
  457.         $i and $self->whine("collision with $fname in $dir: using $path");
  458.         return $path;
  459.     }
  460.     $self->debug("$path already taken");
  461.     } continue { ++$i; }
  462. }
  463.  
  464. #------------------------------
  465.  
  466. =item ignore_filename [YESNO]
  467.  
  468. I<Instance method.>
  469. Return true if we should always ignore recommended filenames in
  470. messages, choosing instead to always generate our own filenames.
  471. With argument, sets this value.
  472.  
  473. B<Note:> subclasses of MIME::Parser::Filer which override
  474. output_path() might not honor this setting; note, however, that
  475. the built-in subclasses honor it.
  476.  
  477. =cut
  478.  
  479. sub ignore_filename {
  480.     my $self = shift;
  481.     $self->{MPF_IgnoreFilename} = $_[0] if @_;
  482.     $self->{MPF_IgnoreFilename};
  483. }
  484.  
  485. #------------------------------
  486.  
  487. =item output_dir HEAD
  488.  
  489. I<Instance method.>
  490. Return the output directory for the given header.
  491. The default method returns ".".
  492.  
  493. =cut
  494.  
  495. sub output_dir {
  496.     my ($self, $head) = @_;
  497.     return ".";
  498. }
  499.  
  500. #------------------------------
  501.  
  502. =item output_filename HEAD
  503.  
  504. I<Instance method, subclasses only.>
  505. A given recommended filename was either not given, or it was judged
  506. to be evil.  Return a fake name, possibly using information in the
  507. message HEADer.  Note that this is just the filename, not the full path.
  508.  
  509. Used by L<output_path()|/output_path>.
  510. If you're using the default C<output_path()>, you probably don't
  511. need to worry about avoiding collisions with existing files;
  512. we take care of that in L<find_unused_path()|/find_unused_path>.
  513.  
  514. =cut
  515.  
  516. sub output_filename {
  517.     my ($self, $head) = @_;
  518.  
  519.     ### Get the recommended name:
  520.     my $recommended = unmime $head->recommended_filename;
  521.  
  522.     ### Get content type:
  523.     my ($type, $subtype) = split m{/}, $head->mime_type; $subtype ||= '';
  524.  
  525.     ### Get recommended extension, being quite conservative:
  526.     my $recommended_ext = (($recommended and ($recommended =~ m{(\.\w+)\Z}))
  527.                ? $1 
  528.                : undef);
  529.  
  530.     ### Try and get an extension, honoring a given one first:
  531.     my $ext = ($recommended_ext ||
  532.            $self->{MPF_Ext}{"$type/$subtype"} ||
  533.            $self->{MPF_Ext}{"$type/*"} ||
  534.            $self->{MPF_Ext}{"*/*"} ||
  535.            ".dat");
  536.     
  537.     ### Get a prefix:
  538.     ++$GFileNo;
  539.     return ($self->output_prefix . "-$$-$GFileNo$ext");
  540. }
  541.  
  542. #------------------------------
  543.  
  544. =item output_prefix [PREFIX]
  545.  
  546. I<Instance method.>
  547. Get the short string that all filenames for extracted body-parts
  548. will begin with (assuming that there is no better "recommended filename").
  549. The default is F<"msg">.
  550.  
  551. If PREFIX I<is not> given, the current output prefix is returned.
  552. If PREFIX I<is> given, the output prefix is set to the new value,
  553. and the previous value is returned.
  554.  
  555. Used by L<output_filename()|/output_filename>.
  556.  
  557. B<Note:> subclasses of MIME::Parser::Filer which override
  558. output_path() or output_filename() might not honor this setting;
  559. note, however, that the built-in subclasses honor it.
  560.  
  561. =cut
  562.  
  563. sub output_prefix {
  564.     my ($self, $prefix) = @_;
  565.     $self->{MPF_Prefix} = $prefix if (@_ > 1);
  566.     $self->{MPF_Prefix};
  567. }
  568.  
  569. #------------------------------
  570.  
  571. =item output_type_ext
  572.  
  573. I<Instance method.>
  574. Return a reference to the hash used by the default
  575. L<output_filename()|/output_filename> for mapping from content-types
  576. to extensions when there is no default extension to use.
  577.  
  578.     $emap = $filer->output_typemap;
  579.     $emap->{'text/plain'} = '.txt';
  580.     $emap->{'text/html'}  = '.html';
  581.     $emap->{'text/*'}     = '.txt';
  582.     $emap->{'*/*'}        = '.dat';
  583.  
  584. B<Note:> subclasses of MIME::Parser::Filer which override
  585. output_path() or output_filename() might not consult this hash;
  586. note, however, that the built-in subclasses consult it.
  587.  
  588. =cut
  589.  
  590. sub output_type_ext  {
  591.     my $self = shift;
  592.     return $self->{MPF_Ext};
  593. }
  594.  
  595. #------------------------------
  596.  
  597. =item output_path HEAD
  598.  
  599. I<Instance method, subclasses only.>
  600. Given a MIME head for a file to be extracted, come up with a good
  601. output pathname for the extracted file.  This is the only method
  602. you need to worry about if you are building a custom filer.
  603.  
  604. The default implementation does a lot of work; subclass
  605. implementers I<really> should try to just override its components
  606. instead of the whole thing.  It works basically as follows:
  607.  
  608.     $directory = $self->output_dir($head);
  609.  
  610.     $filename = $head->recommended_filename();
  611.     if (!$filename or
  612.      $self->ignore_filename() or
  613.      $self->evil_filename($filename)) {
  614.      $filename = $self->output_filename($head);
  615.     }
  616.  
  617.     return $self->find_unused_path($directory, $filename);
  618.  
  619. B<Note:> There are many, many, many ways you might want to control
  620. the naming of files, based on your application.  If you don't like
  621. the behavior of this function, you can easily define your own subclass
  622. of MIME::Parser::Filer and override it there.
  623.  
  624. B<Note:> Nickolay Saukh pointed out that, given the subjective nature of
  625. what is "evil", this function really shouldn't I<warn> about an evil
  626. filename, but maybe just issue a I<debug> message.  I considered that,
  627. but then I thought: if debugging were off, people wouldn't know why
  628. (or even if) a given filename had been ignored.  In mail robots
  629. that depend on externally-provided filenames, this could cause
  630. hard-to-diagnose problems.  So, the message is still a warning.
  631.  
  632. I<Thanks to Laurent Amon for pointing out problems with the original
  633. implementation, and for making some good suggestions.  Thanks also to
  634. Achim Bohnet for pointing out that there should be a hookless, OO way of
  635. overriding the output path.>
  636.  
  637. =cut
  638.  
  639. sub output_path {
  640.     my ($self, $head) = @_;
  641.  
  642.     ### Get the output directory:
  643.     my $dir = $self->output_dir($head);
  644.     
  645.     ### Get the output filename, decoding into the local character set:
  646.     my $fname = unmime $head->recommended_filename;
  647.  
  648.     ### Can we use it:
  649.     if    (!defined($fname)) {
  650.     $self->debug("no filename recommended: synthesizing our own");
  651.     $fname = $self->output_filename($head);
  652.     }
  653.     elsif ($self->ignore_filename) {
  654.     $self->debug("ignoring all external filenames: synthesizing our own");
  655.     $fname = $self->output_filename($head);
  656.     }
  657.     elsif ($self->evil_filename($fname)) {
  658.  
  659.     ### Can we save it by just taking the last element?
  660.     my $ex = $self->exorcise_filename($fname);
  661.     if (defined($ex) and !$self->evil_filename($ex)) {
  662.         $self->whine("Provided filename '$fname' is regarded as evil, ",
  663.              "but I was able to exorcise it and get something ",
  664.              "usable.");
  665.         $fname = $ex;
  666.     }
  667.     else {
  668.         $self->whine("Provided filename '$fname' is regarded as evil; ",
  669.              "I'm ignoring it and supplying my own.");
  670.         $fname = $self->output_filename($head);
  671.     }
  672.     }
  673.     $self->debug("planning to use '$fname'");
  674.  
  675.     ### Resolve collisions and return final path:
  676.     return $self->find_unused_path($dir, $fname);
  677. }
  678.  
  679. #------------------------------
  680.  
  681. =item purge
  682.  
  683. I<Instance method, final.>
  684. Purge all files/directories created by the last parse.
  685. This method simply goes through the purgeable list in reverse order
  686. (see L</purgeable>) and removes all existing files/directories in it.
  687. You should not need to override this method.
  688.  
  689. =cut
  690.  
  691. sub purge {
  692.     my ($self) = @_;
  693.     foreach my $path (reverse @{$self->{MPF_Purgeable}}) {
  694.     (-e $path) or next;   ### must check: might delete DIR before DIR/FILE 
  695.     rmtree($path, 0, 1);
  696.     (-e $path) and $self->whine("unable to purge: $path");
  697.     }
  698.     1;
  699. }
  700.  
  701. #------------------------------
  702.  
  703. =item purgeable [FILE]
  704.  
  705. I<Instance method, final.>
  706. Add FILE to the list of "purgeable" files/directories (those which
  707. will be removed if you do a C<purge()>).
  708. You should not need to override this method.
  709.  
  710. If FILE is not given, the "purgeable" list is returned.
  711. This may be used for more-sophisticated purging.
  712.  
  713. As a special case, invoking this method with a FILE that is an
  714. arrayref will replace the purgeable list with a copy of the
  715. array's contents, so [] may be used to clear the list.
  716.  
  717. Note that the "purgeable" list is cleared when a parser begins a
  718. new parse; therefore, if you want to use purge() to do cleanup,
  719. you I<must> do so I<before> starting a new parse!
  720.  
  721. =cut
  722.  
  723. sub purgeable {
  724.     my ($self, $path) = @_;
  725.     return @{$self->{MPF_Purgeable}} if (@_ == 1);   
  726.  
  727.     if (ref($path)) { $self->{MPF_Purgeable} = [ @$path ]; }
  728.     else            { push @{$self->{MPF_Purgeable}}, $path; }
  729.     1;
  730. }
  731.  
  732. =back
  733.  
  734. =cut
  735.  
  736.  
  737. #------------------------------------------------------------
  738. #------------------------------------------------------------
  739.  
  740. =head2 MIME::Parser::FileInto
  741.  
  742. This concrete subclass of MIME::Parser::Filer supports filing
  743. into a given directory.
  744.  
  745. =over 4
  746.  
  747. =cut
  748.  
  749. package MIME::Parser::FileInto;
  750.  
  751. use strict;
  752. use vars qw(@ISA);
  753. @ISA = qw(MIME::Parser::Filer);
  754.  
  755. #------------------------------
  756.  
  757. =item init DIRECTORY
  758.  
  759. I<Instance method, initiallizer.>
  760. Set the directory where all files will go.
  761.  
  762. =cut
  763.  
  764. sub init {
  765.     my ($self, $dir) = @_;
  766.     $self->{MPFI_Dir} = $self->cleanup_dir($dir);
  767. }
  768.  
  769. #------------------------------
  770. #
  771. # output_dir HEAD
  772. #
  773. # I<Instance method, concrete override.>
  774. # Return the output directory where the files go.
  775. #
  776. sub output_dir {
  777.     shift->{MPFI_Dir};
  778. }
  779.  
  780. =back
  781.  
  782. =cut
  783.  
  784.  
  785.  
  786.  
  787. #------------------------------------------------------------
  788. #------------------------------------------------------------
  789.  
  790. =head2 MIME::Parser::FileUnder
  791.  
  792. This concrete subclass of MIME::Parser::Filer supports filing under
  793. a given directory, using one subdirectory per message, but with
  794. all message parts in the same directory.
  795.  
  796. =over 4
  797.  
  798. =cut
  799.  
  800. package MIME::Parser::FileUnder;
  801.  
  802. use strict;
  803. use vars qw(@ISA);
  804. @ISA = qw(MIME::Parser::Filer);
  805.  
  806. #------------------------------
  807.  
  808. =item init BASEDIR, OPTSHASH...
  809.  
  810. I<Instance method, initiallizer.>
  811. Set the base directory which will contain the message directories.
  812. If used, then each parse of begins by creating a new subdirectory
  813. of BASEDIR where the actual parts of the message are placed.
  814. OPTSHASH can contain the following:
  815.  
  816. =over 4
  817.  
  818. =item DirName
  819.  
  820. Explicitly set the name of the subdirectory which is created.
  821. The default is to use the time, process id, and a sequence number,
  822. but you might want a predictable directory.
  823.  
  824. =item Purge
  825.  
  826. Automatically purge the contents of the directory (including all
  827. subdirectories) before each parse.  This is really only needed if
  828. using an explicit DirName, and is provided as a convenience only.
  829. Currently we use the 1-arg form of File::Path::rmtree; you should
  830. familiarize yourself with the caveats therein.
  831.  
  832. =back
  833.  
  834. The output_dir() will return the path to this message-specific directory
  835. until the next parse is begun, so you can do this:
  836.  
  837.     use File::Path;
  838.  
  839.     $parser->output_under("/tmp");
  840.     $ent = eval { $parser->parse_open($msg); };   ### parse
  841.     if (!$ent) {     ### parse failed
  842.     rmtree($parser->output_dir);
  843.     die "parse failed: $@";
  844.     }
  845.     else {               ### parse succeeded
  846.     ...do stuff...
  847.     }
  848.  
  849. =cut
  850.  
  851. sub init {
  852.     my ($self, $basedir, %opts) = @_;
  853.  
  854.     $self->{MPFU_Base}    = $self->cleanup_dir($basedir);
  855.     $self->{MPFU_DirName} = $opts{DirName}; 
  856.     $self->{MPFU_Purge}   = $opts{Purge}; 
  857. }
  858.  
  859. #------------------------------
  860. #
  861. # init_parse
  862. #
  863. # I<Instance method, override.>
  864. # Prepare to start parsing a new message.
  865. #
  866. sub init_parse {
  867.     my $self = shift;
  868.  
  869.     ### Invoke inherited method first!
  870.     $self->SUPER::init_parse;
  871.  
  872.     ### Determine the subdirectory of ther base to use:
  873.     my $subdir = (defined($self->{MPFU_DirName})
  874.           ?       $self->{MPFU_DirName}
  875.           :       ("msg-".scalar(time)."-$$-".$GSubdirNo++));
  876.     $self->debug("subdir = $subdir");
  877.     
  878.     ### Determine full path to the per-message output directory:
  879.     $self->{MPFU_Dir} = File::Spec->catfile($self->{MPFU_Base}, $subdir);
  880.  
  881.     ### Remove and re-create the per-message output directory:
  882.     rmtree $self->output_dir if $self->{MPFU_Purge};
  883.     (-d $self->output_dir) or
  884.     mkdir $self->output_dir, 0700 or 
  885.         die "mkdir ".$self->output_dir.": $!\n";
  886.  
  887.     ### Add the per-message output directory to the puregables:
  888.     $self->purgeable($self->output_dir);
  889.     1;
  890. }
  891.  
  892. #------------------------------
  893. #
  894. # output_dir HEAD
  895. #
  896. # I<Instance method, concrete override.>
  897. # Return the output directory that we used for the last parse.
  898. #
  899. sub output_dir {
  900.     shift->{MPFU_Dir};
  901. }
  902.  
  903. =back
  904.  
  905. =cut
  906.  
  907. 1;
  908. __END__
  909.  
  910.  
  911. =head1 AUTHOR
  912.  
  913. Eryq (F<eryq@zeegee.com>), ZeeGee Software Inc (F<http://www.zeegee.com>).
  914.  
  915. All rights reserved.  This program is free software; you can redistribute
  916. it and/or modify it under the same terms as Perl itself.
  917.  
  918.  
  919. =head1 VERSION
  920.  
  921. $Revision: 5.406 $
  922.  
  923.