import rt 3.0.12
[freeside.git] / rt / lib / RT / EmailParser.pm
index 49f3d55..e9a00f1 100644 (file)
@@ -35,7 +35,8 @@ use File::Temp qw/tempdir/;
 
 =head1 NAME
 
-  RT::Interface::CLI - helper functions for creating a commandline RT interface
+  RT::EmailParser - helper functions for parsing parts from incoming
+  email messages
 
 =head1 SYNOPSIS
 
@@ -95,7 +96,7 @@ sub CheckForLoops {
     #If this instance of RT sent it our, we don't want to take it in
     my $RTLoop = $head->get("X-RT-Loop-Prevention") || "";
     chomp($RTLoop);    #remove that newline
-    if ( $RTLoop =~ /^$RT::rtname/ ) {
+    if ( $RTLoop =~ /^\Q$RT::rtname\E/o ) {
         return (1);
     }
 
@@ -159,28 +160,23 @@ sub ParseMIMEEntityFromSTDIN {
 
 # }}}
 
+=head2 ParseMIMEEntityFromScalar  $message
+
+Takes either a scalar or a reference to a scalr which contains a stringified MIME message.
+Parses it.
+
+Returns true if it wins.
+Returns false if it loses.
+
+
+=cut
 
 sub ParseMIMEEntityFromScalar {
     my $self = shift;
     my $message = shift;
 
-    # Create a new parser object:
+    $self->_DoParse('parse_data', $message);
 
-    my $parser = MIME::Parser->new();
-    $self->_SetupMIMEParser($parser);
-
-
-    # TODO: XXX 3.0 we really need to wrap this in an eval { }
-    unless ( $self->{'entity'} = $parser->parse_data($message) ) {
-        # Try again, this time without extracting nested messages
-        $parser->extract_nested_messages(0);
-        unless ( $self->{'entity'} = $parser->parse_data($message) ) {
-            $RT::Logger->crit("couldn't parse MIME stream");
-            return ( undef);
-        }
-    }
-    $self->_PostProcessNewEntity();
-    return (1);
 }
 
 # {{{ ParseMIMEEntityFromFilehandle *FH
@@ -195,6 +191,43 @@ sub ParseMIMEEntityFromFileHandle {
     my $self = shift;
     my $filehandle = shift;
 
+    $self->_DoParse('parse', $filehandle);
+
+}
+
+# }}}
+
+# {{{ ParseMIMEEntityFromFile
+
+=head2 ParseMIMEEntityFromFile 
+
+Parses a mime entity from a filename passed in as an argument
+
+=cut
+
+sub ParseMIMEEntityFromFile {
+    my $self = shift;
+
+    my $file = shift;
+    $self->_DoParse('parse_open', $file);
+}
+
+# }}}
+
+# {{{ _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 {
+    my $self = shift;
+    my $method = shift;
+    my $file = shift;
+
     # Create a new parser object:
 
     my $parser = MIME::Parser->new();
@@ -203,11 +236,11 @@ sub ParseMIMEEntityFromFileHandle {
 
     # TODO: XXX 3.0 we really need to wrap this in an eval { }
 
-    unless ( $self->{'entity'} = $parser->parse($filehandle) ) {
+    unless ( $self->{'entity'} = $parser->$method($file) ) {
 
         # Try again, this time without extracting nested messages
         $parser->extract_nested_messages(0);
-        unless ( $self->{'entity'} = $parser->parse($filehandle) ) {
+        unless ( $self->{'entity'} = $parser->$method($file) ) {
             $RT::Logger->crit("couldn't parse MIME stream");
             return ( undef);
         }
@@ -218,6 +251,7 @@ sub ParseMIMEEntityFromFileHandle {
 
 # }}}
 
+
 # {{{ _PostProcessNewEntity 
 
 =head2 _PostProcessNewEntity
@@ -250,7 +284,7 @@ sub ParseTicketId {
 
     my $Subject = shift;
 
-    if ( $Subject =~ s/\[$RT::rtname \#(\d+)\s*\]//i ) {
+    if ( $Subject =~ s/\[\Q$RT::rtname\E\s+\#(\d+)\s*\]//i ) {
         my $id = $1;
         $RT::Logger->debug("Found a ticket ID. It's $id");
         return ($id);
@@ -262,205 +296,6 @@ sub ParseTicketId {
 
 # }}}
 
-# {{{ sub MailError 
-
-=head2 MailError { }
-
-
-# TODO this doesn't belong here.
-# TODO doc this
-
-
-=cut
-
-
-sub MailError {
-    my $self = shift;
-
-    my %args = (
-        To          => $RT::OwnerEmail,
-        Bcc         => undef,
-        From        => $RT::CorrespondAddress,
-        Subject     => 'There has been an error',
-        Explanation => 'Unexplained error',
-        MIMEObj     => undef,
-        LogLevel    => 'crit',
-        @_
-    );
-
-    $RT::Logger->log(
-        level   => $args{'LogLevel'},
-        message => $args{'Explanation'}
-    );
-    my $entity = MIME::Entity->build(
-        Type                   => "multipart/mixed",
-        From                   => $args{'From'},
-        Bcc                    => $args{'Bcc'},
-        To                     => $args{'To'},
-        Subject                => $args{'Subject'},
-        'X-RT-Loop-Prevention' => $RT::rtname,
-    );
-
-    $entity->attach( Data => $args{'Explanation'} . "\n" );
-
-    my $mimeobj = $args{'MIMEObj'};
-    $mimeobj->sync_headers();
-    $entity->add_part($mimeobj);
-
-    if ( $RT::MailCommand eq 'sendmailpipe' ) {
-        open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" ) || return (0);
-        print MAIL $entity->as_string;
-        close(MAIL);
-    }
-    else {
-        $entity->send( $RT::MailCommand, $RT::MailParams );
-    }
-}
-
-# }}}
-
-
-
-# {{{ sub GetCurrentUser 
-
-sub GetCurrentUser {
-    my $self     = shift;
-    my $ErrorsTo = shift;
-
-    my %UserInfo = ();
-
-    #Suck the address of the sender out of the header
-    my ( $Address, $Name ) = $self->ParseSenderAddressFromHead();
-
-    my $tempuser = RT::User->new($RT::SystemUser);
-
-    #This will apply local address canonicalization rules
-    $Address = $tempuser->CanonicalizeEmailAddress($Address);
-
-    #If desired, synchronize with an external database
-    my $UserFoundInExternalDatabase = 0;
-
-    # Username is the 'Name' attribute of the user that RT uses for things
-    # like authentication
-    my $Username = undef;
-    ( $UserFoundInExternalDatabase, %UserInfo ) =
-      $self->LookupExternalUserInfo( $Address, $Name );
-
-    $Address  = $UserInfo{'EmailAddress'};
-    $Username = $UserInfo{'Name'};
-
-    #Get us a currentuser object to work with. 
-    my $CurrentUser = RT::CurrentUser->new();
-
-    # First try looking up by a username, if we got one from the external
-    # db lookup. Next, try looking up by email address. Failing that,
-    # try looking up by users who have this user's email address as their
-    # username.
-
-    if ($Username) {
-        $CurrentUser->LoadByName($Username);
-    }
-
-    unless ( $CurrentUser->Id ) {
-        $CurrentUser->LoadByEmail($Address);
-    }
-
-    #If we can't get it by email address, try by name.  
-    unless ( $CurrentUser->Id ) {
-        $CurrentUser->LoadByName($Address);
-    }
-
-    unless ( $CurrentUser->Id ) {
-
-        #If we couldn't load a user, determine whether to create a user
-
-        # {{{ If we require an incoming address to be found in the external
-        # user database, reject the incoming message appropriately
-        if ( $RT::SenderMustExistInExternalDatabase
-             && !$UserFoundInExternalDatabase ) {
-
-            my $Message =
-              "Sender's email address was not found in the user database.";
-
-            # {{{  This code useful only if you've defined an AutoRejectRequest template
-
-            require RT::Template;
-            my $template = new RT::Template($RT::Nobody);
-            $template->Load('AutoRejectRequest');
-            $Message = $template->Content || $Message;
-
-            # }}}
-
-            MailError(
-                 To      => $ErrorsTo,
-                 Subject => "Ticket Creation failed: user could not be created",
-                 Explanation => $Message,
-                 MIMEObj     => $self->Entity,
-                 LogLevel    => 'notice' );
-
-            return ($CurrentUser);
-
-        }
-
-        # }}}
-
-        else {
-            my $NewUser = RT::User->new($RT::SystemUser);
-
-            my ( $Val, $Message ) = $NewUser->Create(
-                                  Name => ( $Username || $Address ),
-                                  EmailAddress => $Address,
-                                  RealName     => "$Name",
-                                  Password     => undef,
-                                  Privileged   => 0,
-                                  Comments => 'Autocreated on ticket submission'
-            );
-
-            unless ($Val) {
-
-                # Deal with the race condition of two account creations at once
-                #
-                if ($Username) {
-                    $NewUser->LoadByName($Username);
-                }
-
-                unless ( $NewUser->Id ) {
-                    $NewUser->LoadByEmail($Address);
-                }
-
-                unless ( $NewUser->Id ) {
-                    MailError(To          => $ErrorsTo,
-                              Subject     => "User could not be created",
-                              Explanation =>
-                                "User creation failed in mailgateway: $Message",
-                              MIMEObj  => $self->Entity,
-                              LogLevel => 'crit' );
-                }
-            }
-        }
-
-        #Load the new user object
-        $CurrentUser->LoadByEmail($Address);
-
-        unless ( $CurrentUser->id ) {
-            $RT::Logger->warning(
-                               "Couldn't load user '$Address'." . "giving up" );
-            MailError(
-                   To          => $ErrorsTo,
-                   Subject     => "User could not be loaded",
-                   Explanation =>
-                     "User  '$Address' could not be loaded in the mail gateway",
-                   MIMEObj  => $self->Entity,
-                   LogLevel => 'crit' );
-
-        }
-    }
-
-    return ($CurrentUser);
-
-}
-
-# }}}
 
 
 # {{{ ParseCcAddressesFromHead 
@@ -756,26 +591,38 @@ A private instance method which sets up a mime parser to do its job
     ## Over max size and return them
 
 sub _SetupMIMEParser {
-    my $self = shift;
+    my $self   = shift;
     my $parser = shift;
-    my $AttachmentDir = File::Temp::tempdir( TMPDIR => 1, CLEANUP => 1 );
 
     # Set up output directory for files:
-    $parser->output_dir("$AttachmentDir");
 
-    #If someone includes a message, don't extract it
+    my $tmpdir = File::Temp::tempdir( TMPDIR => 1, CLEANUP => 1 );
+    push ( @{ $self->{'AttachmentDirs'} }, $tmpdir );
+    $parser->output_dir($tmpdir);
+    $parser->filer->ignore_filename(1);
+
+    #If someone includes a message, extract it
     $parser->extract_nested_messages(1);
 
+    $parser->extract_uuencode(1);    ### default is false
+
     # Set up the prefix for files with auto-generated names:
     $parser->output_prefix("part");
 
-    # If content length is <= 50000 bytes, store each msg as in-core scalar;
-    # Else, write to a disk file (the default action):
+    # do _not_ store each msg as in-core scalar;
 
-    $parser->output_to_core(50000);
+    $parser->output_to_core(0);
 }
+
 # }}}
 
+sub DESTROY {
+    my $self = shift;
+    File::Path::rmtree([@{$self->{'AttachmentDirs'}}],0,1);
+}
+
+
+
 eval "require RT::EmailParser_Vendor";
 die $@ if ($@ && $@ !~ qr{^Can't locate RT/EmailParser_Vendor.pm});
 eval "require RT::EmailParser_Local";