import rt 3.2.2
[freeside.git] / rt / lib / RT / EmailParser.pm
index e9a00f1..8ed810c 100644 (file)
@@ -1,8 +1,14 @@
-# BEGIN LICENSE BLOCK
+# {{{ BEGIN BPS TAGGED BLOCK
 # 
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+# COPYRIGHT:
+#  
+# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC 
+#                                          <jesse@bestpractical.com>
 # 
-# (Except where explictly superceded by other copyright notices)
+# (Except where explicitly superseded by other copyright notices)
+# 
+# 
+# LICENSE:
 # 
 # This work is made available to you under the terms of Version 2 of
 # the GNU General Public License. A copy of that license should have
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # General Public License for more details.
 # 
-# Unless otherwise specified, all modifications, corrections or
-# extensions to this work which alter its source code become the
-# property of Best Practical Solutions, LLC when submitted for
-# inclusion in the work.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# 
 # 
+# CONTRIBUTION SUBMISSION POLICY:
 # 
-# END LICENSE BLOCK
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+# 
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+# 
+# }}} END BPS TAGGED BLOCK
 package RT::EmailParser;
 
 
@@ -151,11 +173,62 @@ sub CheckForAutoGenerated {
 
 # }}}
 
+
+=head2 SmartParseMIMEEntityFromScalar { Message => SCALAR_REF, Decode => BOOL }
+
+Parse a message stored in a scalar from scalar_ref
+
+
+=cut
+
+sub SmartParseMIMEEntityFromScalar {
+    my $self = shift;
+    my %args = ( Message => undef, Decode => 1, @_ );
+
+    my ( $fh, $temp_file );
+    eval {
+
+        for ( 1 .. 10 ) {
+
+            # on NFS and NTFS, it is possible that tempfile() conflicts
+            # with other processes, causing a race condition. we try to
+            # accommodate this by pausing and retrying.
+            last
+              if ( $fh, $temp_file ) =
+              eval { File::Temp::tempfile( undef, UNLINK => 0 ) };
+            sleep 1;
+        }
+        if ($fh) {
+
+            #thank you, windows                      
+            binmode $fh;
+            $fh->autoflush(1);
+            print $fh $args{'Message'};
+            close($fh);
+            if ( -f $temp_file ) {
+
+                # We have to trust the temp file's name -- untaint it
+                $temp_file =~ /(.*)/;
+                $self->ParseMIMEEntityFromFile( $1, $args{'Decode'} );
+                unlink($1);
+            }
+        }
+    };
+
+    #If for some reason we weren't able to parse the message using a temp file
+    # try it with a scalar
+    if ( $@ || !$self->Entity ) {
+        $self->ParseMIMEEntityFromScalar( $args{'Message'}, $args{'Decode'} );
+    }
+
+}
+
 # {{{ sub ParseMIMEEntityFromSTDIN
 
 sub ParseMIMEEntityFromSTDIN {
     my $self = shift;
-    return $self->ParseMIMEEntityFromFileHandle(\*STDIN);
+    my $postprocess = (@_ ? shift : 1);
+    return $self->ParseMIMEEntityFromFileHandle(\*STDIN, $postprocess);
 }
 
 # }}}
@@ -174,11 +247,11 @@ Returns false if it loses.
 sub ParseMIMEEntityFromScalar {
     my $self = shift;
     my $message = shift;
-
-    $self->_DoParse('parse_data', $message);
-
+    my $postprocess = (@_ ? shift : 1);
+    $self->_ParseMIMEEntity($message,'parse_data', $postprocess);
 }
 
+
 # {{{ ParseMIMEEntityFromFilehandle *FH
 
 =head2 ParseMIMEEntityFromFilehandle *FH
@@ -190,9 +263,8 @@ Parses a mime entity from a filehandle passed in as an argument
 sub ParseMIMEEntityFromFileHandle {
     my $self = shift;
     my $filehandle = shift;
-
-    $self->_DoParse('parse', $filehandle);
-
+    my $postprocess = (@_ ? shift : 1);
+    $self->_ParseMIMEEntity($filehandle,'parse', $postprocess);
 }
 
 # }}}
@@ -207,27 +279,19 @@ Parses a mime entity from a filename passed in as an argument
 
 sub ParseMIMEEntityFromFile {
     my $self = shift;
-
     my $file = shift;
-    $self->_DoParse('parse_open', $file);
+    my $postprocess = (@_ ? shift : 1);
+    $self->_ParseMIMEEntity($file,'parse_open',$postprocess);
 }
 
 # }}}
-
-# {{{ _DoParse 
-
-=head2 _DoParse PARSEMETHOD CONTENT
-
-
-A helper for the various parsers to turn around and do the dispatch to the actual parser
-
-=cut
-
-sub _DoParse {
+#
+#  {{{ _ParseMIMEEntity {
+sub _ParseMIMEEntity {
     my $self = shift;
+    my $message = shift;
     my $method = shift;
-    my $file = shift;
-
+    my $postprocess = shift;
     # Create a new parser object:
 
     my $parser = MIME::Parser->new();
@@ -235,22 +299,23 @@ sub _DoParse {
 
 
     # TODO: XXX 3.0 we really need to wrap this in an eval { }
-
-    unless ( $self->{'entity'} = $parser->$method($file) ) {
-
+    unless ( $self->{'entity'} = $parser->$method($message) ) {
+        $RT::Logger->crit("Couldn't parse MIME stream and extract the submessages");
         # Try again, this time without extracting nested messages
         $parser->extract_nested_messages(0);
-        unless ( $self->{'entity'} = $parser->$method($file) ) {
+        unless ( $self->{'entity'} = $parser->$method($message) ) {
             $RT::Logger->crit("couldn't parse MIME stream");
             return ( undef);
         }
     }
-    $self->_PostProcessNewEntity();
-    return (1);
+    if ($postprocess) {
+    $self->_PostProcessNewEntity() ;
+    }
+
 }
 
-# }}}
 
+# }}}
 
 # {{{ _PostProcessNewEntity 
 
@@ -402,6 +467,8 @@ sub ParseAddressFromHeader {
     my $self = shift;
     my $Addr = shift;
 
+    # Perl 5.8.0 breaks when doing regex matches on utf8
+    Encode::_utf8_off($Addr) if $] == 5.008;
     my @Addresses = Mail::Address->parse($Addr);
 
     my $AddrObj = $Addresses[0];
@@ -593,7 +660,7 @@ A private instance method which sets up a mime parser to do its job
 sub _SetupMIMEParser {
     my $self   = shift;
     my $parser = shift;
-
+    
     # Set up output directory for files:
 
     my $tmpdir = File::Temp::tempdir( TMPDIR => 1, CLEANUP => 1 );
@@ -612,6 +679,15 @@ sub _SetupMIMEParser {
     # do _not_ store each msg as in-core scalar;
 
     $parser->output_to_core(0);
+
+    # From the MIME::Parser docs:
+    # "Normally, tmpfiles are created when needed during parsing, and destroyed automatically when they go out of scope"
+    # Turns out that the default is to recycle tempfiles
+    # Temp files should never be recycled, especially when running under perl taint checking
+    
+    $parser->tmp_recycling(0);
+    
+
 }
 
 # }}}