[Rt-commit] rt branch, 4.4/multiple-reply-to, repushed

? sunnavy sunnavy at bestpractical.com
Mon Jul 6 12:47:08 EDT 2015


The branch 4.4/multiple-reply-to was deleted and repushed:
       was 4bf016c7e53eaa56ca927a5d302606df2835ae7e
       now c179f586d0d2c90590d4953fe307a255949fe598

 1:  e0497a2 !  1:  2164243 RT::Interface::Email doesn't need to be an Exporter
    @@ -8,8 +8,8 @@
     --- a/docs/UPGRADING-4.4
     +++ b/docs/UPGRADING-4.4
     @@
    - Estimated, and Time Left will no longer be copied.  This simplifies time
    - reporting.
    + Custom fields with categories will be split out into hierarchical custom
    + fields.
      
     +=item *
     +
    @@ -34,7 +34,6 @@
     -    # as well as any optionally exported functions
     -    @EXPORT_OK = qw(
     -        &CreateUser
    --        &GetMessageContent
     -        &CheckForLoops
     -        &CheckForSuspiciousSender
     -        &CheckForAutoGenerated
 2:  e3399f0 !  2:  3205741 Reorder functions to more obviously split sending and receiving mail
    @@ -42,9 +42,9 @@
     -    # to RT instance B which sends a mail to ...
     -    return undef;
     -}
    --
    ++=head2 Gateway ARGSREF
    + 
     -=head2 CheckForSuspiciousSender HEAD
    -+=head2 Gateway ARGSREF
      
     -Takes a HEAD object of L<MIME::Head> class and returns true if sender
     -is suspicious. Suspicious means mailer daemon.
    @@ -615,24 +615,50 @@
     -        $RT::Logger->info( $msgid . " No recipients found. Not sending." );
     -        return -1;
     -    }
    --
    ++    # if plugin returns AuthStat -2 we skip action
    ++    # NOTE: this is experimental API and it would be changed
    ++    my %skip_action = ();
    + 
     -    if ($args{'Entity'}->head->get('X-RT-Squelch')) {
     -        $RT::Logger->info( $msgid . " Squelch header found. Not sending." );
     -        return -1;
     -    }
    --
    ++    # Since this needs loading, no matter what
    ++    foreach (@{ $args{MailPlugins} }) {
    ++        my ($Code, $NewAuthStat);
    ++        if ( ref($_) eq "CODE" ) {
    ++            $Code = $_;
    ++        } else {
    ++            no strict 'refs';
    ++            $Code = *{ $_ . "::GetCurrentUser" }{CODE};
    ++        }
    + 
     -    if (my $precedence = RT->Config->Get('DefaultMailPrecedence')
     -        and !$args{'Entity'}->head->get("Precedence")
     -    ) {
     -        $args{'Entity'}->head->replace( 'Precedence', Encode::encode("UTF-8",$precedence) );
     -    }
    --
    ++        foreach my $action (@{ $args{Actions} }) {
    ++            ( $CurrentUser, $NewAuthStat ) = $Code->(
    ++                Message       => $args{Message},
    ++                RawMessageRef => $args{RawMessageRef},
    ++                CurrentUser   => $CurrentUser,
    ++                AuthLevel     => $AuthStat,
    ++                Action        => $action,
    ++                Ticket        => $args{SystemTicket},
    ++                Queue         => $args{SystemQueue},
    ++            );
    + 
     -    if ( $TransactionObj && !$TicketObj
     -        && $TransactionObj->ObjectType eq 'RT::Ticket' )
     -    {
     -        $TicketObj = $TransactionObj->Object;
     -    }
    --
    ++# You get the highest level of authentication you were assigned, unless you get the magic -1
    ++# If a module returns a "-1" then we discard the ticket, so.
    ++            $AuthStat = $NewAuthStat
    ++                if ( $NewAuthStat > $AuthStat or $NewAuthStat == -1 or $NewAuthStat == -2 );
    + 
     -    my $head = $args{'Entity'}->head;
     -    unless ( $head->get('Date') ) {
     -        require RT::Date;
    @@ -648,7 +674,10 @@
     -        # fsck.com #5959: Since RT sends 8bit mail, we should say so.
     -        $head->replace( 'Content-Transfer-Encoding', '8bit' );
     -    }
    --
    ++            last if $AuthStat == -1;
    ++            $skip_action{$action}++ if $AuthStat == -2;
    ++        }
    + 
     -    if ( RT->Config->Get('Crypt')->{'Enable'} ) {
     -        %args = WillSignEncrypt(
     -            %args,
    @@ -657,30 +686,41 @@
     -        );
     -        my $res = SignEncrypt( %args );
     -        return $res unless $res > 0;
    --    }
    --
    ++        # strip actions we should skip
    ++        @{$args{Actions}} = grep !$skip_action{$_}, @{$args{Actions}}
    ++            if $AuthStat == -2;
    ++        last unless @{$args{Actions}};
    ++
    ++        last if $AuthStat == -1;
    +     }
    + 
     -    my $mail_command = RT->Config->Get('MailCommand');
    --
    ++    return $AuthStat if !wantarray;
    + 
     -    # if it is a sub routine, we just return it;
     -    return $mail_command->($args{'Entity'}) if UNIVERSAL::isa( $mail_command, 'CODE' );
    --
    ++    return ($AuthStat, $CurrentUser, $error);
    ++}
    + 
     -    if ( $mail_command eq 'sendmailpipe' ) {
     -        my $path = RT->Config->Get('SendmailPath');
     -        my @args = shellwords(RT->Config->Get('SendmailArguments'));
     -        push @args, "-t" unless grep {$_ eq "-t"} @args;
    --
    + 
     -        # SetOutgoingMailFrom and bounces conflict, since they both want -f
     -        if ( $args{'Bounce'} ) {
     -            push @args, shellwords(RT->Config->Get('SendmailBounceArguments'));
     -        } elsif ( my $MailFrom = RT->Config->Get('SetOutgoingMailFrom') ) {
     -            my $OutgoingMailAddress = $MailFrom =~ /\@/ ? $MailFrom : undef;
     -            my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
    --
    ++=head2 _NoAuthorizedUserFound
    + 
     -            if ($TicketObj) {
     -                my $Queue = $TicketObj->QueueObj;
     -                my $QueueAddressOverride = $Overrides->{$Queue->id}
     -                    || $Overrides->{$Queue->Name};
    --
    ++Emails the RT Owner and the requestor when the auth plugins return "No auth user found"
    + 
     -                if ($QueueAddressOverride) {
     -                    $OutgoingMailAddress = $QueueAddressOverride;
     -                } else {
    @@ -691,11 +731,20 @@
     -            elsif ($Overrides->{'Default'}) {
     -                $OutgoingMailAddress = $Overrides->{'Default'};
     -            }
    --
    ++=cut
    + 
     -            push @args, "-f", $OutgoingMailAddress
     -                if $OutgoingMailAddress;
     -        }
    --
    ++sub _NoAuthorizedUserFound {
    ++    my %args = (
    ++        Right     => undef,
    ++        Message   => undef,
    ++        Requestor => undef,
    ++        Queue     => undef,
    ++        @_
    ++    );
    + 
     -        # VERP
     -        if ( $TransactionObj and
     -             my $prefix = RT->Config->Get('VERPPrefix') and
    @@ -706,22 +755,49 @@
     -            $from =~ s/\s//g;
     -            push @args, "-f", "$prefix$from\@$domain";
     -        }
    --
    ++    # Notify the RT Admin of the failure.
    ++    MailError(
    ++        To          => RT->Config->Get('OwnerEmail'),
    ++        Subject     => "Could not load a valid user",
    ++        Explanation => <<EOT,
    ++RT could not load a valid user, and RT's configuration does not allow
    ++for the creation of a new user for this email (@{[$args{Requestor}]}).
    + 
     -        eval {
     -            # don't ignore CHLD signal to get proper exit code
     -            local $SIG{'CHLD'} = 'DEFAULT';
    --
    ++You might need to grant 'Everyone' the right '@{[$args{Right}]}' for the
    ++queue @{[$args{'Queue'}]}.
    + 
     -            # if something wrong with $mail->print we will get PIPE signal, handle it
     -            local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
    --
    ++EOT
    ++        MIMEObj  => $args{'Message'},
    ++        LogLevel => 'error'
    ++    );
    + 
     -            require IPC::Open2;
     -            my ($mail, $stdout);
     -            my $pid = IPC::Open2::open2( $stdout, $mail, $path, @args )
     -                or die "couldn't execute program: $!";
    --
    ++    # Also notify the requestor that his request has been dropped.
    ++    if ($args{'Requestor'} ne RT->Config->Get('OwnerEmail')) {
    ++    MailError(
    ++        To          => $args{'Requestor'},
    ++        Subject     => "Could not load a valid user",
    ++        Explanation => <<EOT,
    ++RT could not load a valid user, and RT's configuration does not allow
    ++for the creation of a new user for your email.
    + 
     -            $args{'Entity'}->print($mail);
     -            close $mail or die "close pipe failed: $!";
    --
    ++EOT
    ++        MIMEObj  => $args{'Message'},
    ++        LogLevel => 'error'
    ++    );
    ++    }
    ++}
    + 
     -            waitpid($pid, 0);
     -            if ($?) {
     -                # sendmail exit statuses mostly errors with data not software
    @@ -739,12 +815,47 @@
     -            }
     -            return 0;
     -        }
    --    }
    --    else {
    +-    } elsif ( $mail_command eq 'mbox' ) {
    +-        my $now = RT::Date->new(RT->SystemUser);
    +-        $now->SetToNow;
    ++sub CreateUser {
    ++    my ( $Username, $Address, $Name, $ErrorsTo, $entity ) = @_;
    + 
    +-        state $logfile;
    +-        unless ($logfile) {
    +-            my $when = $now->ISO( Timezone => "server" );
    +-            $when =~ s/\s+/-/g;
    +-            $logfile = "$RT::VarPath/$when.mbox";
    +-            $RT::Logger->info("Storing outgoing emails in $logfile");
    +-        }
    ++    my $NewUser = RT::User->new( RT->SystemUser );
    + 
    +-        my $fh;
    +-        unless (open($fh, ">>", $logfile)) {
    +-            $RT::Logger->crit( "Can't open mbox file $logfile: $!" );
    +-            return 0;
    ++    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);
    +         }
    +-        my $content = $args{Entity}->stringify;
    +-        $content =~ s/^(>*From )/>$1/mg;
    +-        print $fh "From $ENV{USER}\@localhost  ".localtime."\n";
    +-        print $fh $content, "\n";
    +-        close $fh;
    +-    } else {
     -        local ($ENV{'MAILADDRESS'}, $ENV{'PERL_MAILERS'});
    -+    # if plugin returns AuthStat -2 we skip action
    -+    # NOTE: this is experimental API and it would be changed
    -+    my %skip_action = ();
      
     -        my @mailer_args = ($mail_command);
     -        if ( $mail_command eq 'sendmail' ) {
    @@ -756,45 +867,78 @@
     -                $Mail::Mailer::testfile::config{outfile} = File::Temp->new;
     -                $RT::Logger->info("Storing outgoing emails in $Mail::Mailer::testfile::config{outfile}");
     -            }
    -+    # Since this needs loading, no matter what
    -+    foreach (@{ $args{MailPlugins} }) {
    -+        my ($Code, $NewAuthStat);
    -+        if ( ref($_) eq "CODE" ) {
    -+            $Code = $_;
    -         } else {
    +-        } else {
     -            push @mailer_args, RT->Config->Get('MailParams');
    --        }
    --
    ++        unless ( $NewUser->Id ) {
    ++            $NewUser->LoadByEmail($Address);
    +         }
    + 
     -        unless ( $args{'Entity'}->send( @mailer_args ) ) {
     -            $RT::Logger->crit( "$msgid: Could not send mail." );
     -            if ( $TicketObj ) {
     -                _RecordSendEmailFailure( $TicketObj );
     -            }
     -            return 0;
    -+            no strict 'refs';
    -+            $Code = *{ $_ . "::GetCurrentUser" }{CODE};
    ++        unless ( $NewUser->Id ) {
    ++            MailError(
    ++                To          => $ErrorsTo,
    ++                Subject     => "User could not be created",
    ++                Explanation =>
    ++                    "User creation failed in mailgateway: $Message",
    ++                MIMEObj  => $entity,
    ++                LogLevel => 'crit',
    ++            );
              }
    --    }
    +     }
     -    return 1;
     -}
    --
    + 
     -=head2 PrepareEmailUsingTemplate Template => '', Arguments => {}
    --
    ++    #Load the new user object
    ++    my $CurrentUser = RT::CurrentUser->new;
    ++    $CurrentUser->LoadByEmail( $Address );
    + 
     -Loads a template. Parses it using arguments if it's not empty.
     -Returns a tuple (L<RT::Template> object, error message).
    ++    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  => $entity,
    ++            LogLevel => 'crit'
    ++        );
    ++    }
      
     -Note that even if a template object is returned MIMEObj method
     -may return undef for empty templates.
    --
    --=cut
    --
    ++    return $CurrentUser;
    ++}
    ++
    ++
    ++=head2 ParseCcAddressesFromHead HASH
    ++
    ++Takes a hash containing QueueObj, Head and CurrentUser objects.
    ++Returns a list of all email addresses in the To and Cc
    ++headers b<except> the current Queue's email addresses, the CurrentUser's
    ++email address  and anything that the configuration sub RT::IsRTAddress matches.
    + 
    + =cut
    + 
     -sub PrepareEmailUsingTemplate {
    --    my %args = (
    ++sub ParseCcAddressesFromHead {
    +     my %args = (
     -        Template => '',
     -        Arguments => {},
    --        @_
    --    );
    --
    ++        Head        => undef,
    ++        QueueObj    => undef,
    ++        CurrentUser => undef,
    +         @_
    +     );
    + 
     -    my $template = RT::Template->new( RT->SystemUser );
     -    $template->LoadGlobalTemplate( $args{'Template'} );
     -    unless ( $template->id ) {
    @@ -804,16 +948,25 @@
     -
     -    my ($status, $msg) = $template->Parse( %{ $args{'Arguments'} } );
     -    return (undef, $msg) unless $status;
    --
    ++    my $current_address = lc $args{'CurrentUser'}->EmailAddress;
    ++    my $user = $args{'CurrentUser'}->UserObj;
    + 
     -    return $template;
    --}
    --
    ++    return
    ++        grep $_ ne $current_address && !RT::EmailParser->IsRTAddress( $_ ),
    ++        map lc $user->CanonicalizeEmailAddress( $_->address ),
    ++        map RT::EmailParser->CleanupAddresses( Email::Address->parse(
    ++              Encode::decode( "UTF-8", $args{'Head'}->get( $_ ) ) ) ),
    ++        qw(To Cc);
    + }
    + 
     -=head2 SendEmailUsingTemplate Template => '', Arguments => {}, From => CorrespondAddress, To => '', Cc => '', Bcc => ''
    --
    + 
     -Sends email using a template, takes name of template, arguments for it and recipients.
    --
    + 
     -=cut
    --
    ++=head2 ParseSenderAddressFromHead HEAD
    + 
     -sub SendEmailUsingTemplate {
     -    my %args = (
     -        Template => '',
    @@ -826,58 +979,104 @@
     -        ExtraHeaders => {},
     -        @_
     -    );
    --
    ++Takes a MIME::Header object. Returns (user at host, friendly name, errors)
    ++where the first two values are the From (evaluated in order of
    ++Reply-To:, From:, Sender).
    + 
     -    my ($template, $msg) = PrepareEmailUsingTemplate( %args );
     -    return (0, $msg) unless $template;
    --
    ++A list of error messages may be returned even when a Sender value is
    ++found, since it could be a parse error for another (checked earlier)
    ++sender field. In this case, the errors aren't fatal, but may be useful
    ++to investigate the parse failure.
    + 
     -    my $mail = $template->MIMEObj;
     -    unless ( $mail ) {
     -        $RT::Logger->info("Message is not sent as template #". $template->id ." is empty");
     -        return -1;
     -    }
    --
    ++=cut
    + 
     -    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ $_ } ) )
     -        foreach grep defined $args{$_}, qw(To Cc Bcc From);
    --
    ++sub ParseSenderAddressFromHead {
    ++    my $head = shift;
    ++    my @sender_headers = ('Reply-To', 'From', 'Sender');
    ++    my @errors;  # Accumulate any errors
    + 
     -    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ExtraHeaders}{$_} ) )
     -        foreach keys %{ $args{ExtraHeaders} };
    --
    ++    #Figure out who's sending this message.
    ++    foreach my $header ( @sender_headers ) {
    ++        my $addr_line = Encode::decode( "UTF-8", $head->get($header) ) || next;
    ++        my ($addr, $name) = ParseAddressFromHeader( $addr_line );
    ++        # only return if the address is not empty
    ++        return ($addr, $name, @errors) if $addr;
    + 
     -    SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
    --
    ++        chomp $addr_line;
    ++        push @errors, "$header: $addr_line";
    ++    }
    + 
     -    return SendEmail( Entity => $mail );
    --}
    --
    ++    return (undef, undef, @errors);
    + }
    + 
     -=head2 GetForwardFrom Ticket => undef, Transaction => undef
    --
    ++=head2 ParseErrorsToAddressFromHead HEAD
    + 
     -Resolve the From field to use in forward mail
    --
    --=cut
    --
    ++Takes a MIME::Header object. Return a single value : user at host
    ++of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
    ++From:, Sender)
    + 
    + =cut
    + 
     -sub GetForwardFrom {
     -    my %args   = ( Ticket => undef, Transaction => undef, @_ );
     -    my $txn    = $args{Transaction};
     -    my $ticket = $args{Ticket} || $txn->Object;
    --
    ++sub ParseErrorsToAddressFromHead {
    ++    my $head = shift;
    + 
     -    if ( RT->Config->Get('ForwardFromUser') ) {
     -        return ( $txn || $ticket )->CurrentUser->EmailAddress;
     -    }
     -    else {
     -        return $ticket->QueueObj->CorrespondAddress
     -          || RT->Config->Get('CorrespondAddress');
    --    }
    --}
    --
    ++    #Figure out who's sending this message.
    ++
    ++    foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
    ++
    ++        # If there's a header of that name
    ++        my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
    ++        if ($headerobj) {
    ++            my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
    ++
    ++            # If it's got actual useful content...
    ++            return ($addr) if ($addr);
    ++        }
    +     }
    + }
    + 
     -=head2 GetForwardAttachments Ticket => undef, Transaction => undef
    --
    + 
     -Resolve the Attachments to forward
    --
    --=cut
    --
    ++
    ++=head2 ParseAddressFromHeader ADDRESS
    ++
    ++Takes an address from C<$head->get('Line')> and returns a tuple: user at host, friendly name
    + 
    + =cut
    + 
     -sub GetForwardAttachments {
     -    my %args   = ( Ticket => undef, Transaction => undef, @_ );
     -    my $txn    = $args{Transaction};
     -    my $ticket = $args{Ticket} || $txn->Object;
    --
    ++sub ParseAddressFromHeader {
    ++    my $Addr = shift;
    + 
     -    my $attachments = RT::Attachments->new( $ticket->CurrentUser );
     -    if ($txn) {
     -        $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
    @@ -893,24 +1092,41 @@
     -    }
     -    return $attachments;
     -}
    --
    --
    ++    # Some broken mailers send:  ""Vincent, Jesse"" <jesse at fsck.com>. Hate
    ++    $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
    ++    my @Addresses = RT::EmailParser->ParseEmailAddress($Addr);
    + 
    ++    my ($AddrObj) = grep ref $_, @Addresses;
    ++    unless ( $AddrObj ) {
    ++        return ( undef, undef );
    ++    }
    + 
     -=head2 SignEncrypt Entity => undef, Sign => 0, Encrypt => 0
    --
    ++    return ( $AddrObj->address, $AddrObj->phrase );
    ++}
    + 
     -Signs and encrypts message using L<RT::Crypt>, but as well handle errors
     -with users' keys.
    --
    ++=head2 _HandleMachineGeneratedMail
    + 
     -If a recipient has no key or has other problems with it, then the
     -unction sends a error to him using 'Error: public key' template.
     -Also, notifies RT's owner using template 'Error to RT owner: public key'
     -to inform that there are problems with users' keys. Then we filter
     -all bad recipients and retry.
    --
    ++Takes named params:
    ++    Message
    ++    ErrorsTo
    ++    Subject
    + 
     -Returns 1 on success, 0 on error and -1 if all recipients are bad and
     -had been filtered out.
    --
    --=cut
    --
    ++Checks the message to see if it's a bounce, if it looks like a loop, if it's autogenerated, etc.
    ++Returns a triple of ("Should we continue (boolean)", "New value for $ErrorsTo", "Status message",
    ++"This message appears to be a loop (boolean)" );
    + 
    + =cut
    + 
     -sub SignEncrypt {
     -    my %args = (
     -        Entity => undef,
    @@ -919,20 +1135,28 @@
     -        @_
     -    );
     -    return 1 unless $args{'Sign'} || $args{'Encrypt'};
    --
    ++sub _HandleMachineGeneratedMail {
    ++    my %args = ( Message => undef, ErrorsTo => undef, Subject => undef, MessageId => undef, @_ );
    ++    my $head = $args{'Message'}->head;
    ++    my $ErrorsTo = $args{'ErrorsTo'};
    + 
     -    my $msgid = Encode::decode( "UTF-8", $args{'Entity'}->head->get('Message-ID') || '' );
     -    chomp $msgid;
    --
    ++    my $IsBounce = CheckForBounce($head);
    + 
     -    $RT::Logger->debug("$msgid Signing message") if $args{'Sign'};
     -    $RT::Logger->debug("$msgid Encrypting message") if $args{'Encrypt'};
    --
    ++    my $IsAutoGenerated = CheckForAutoGenerated($head);
    + 
     -    my %res = RT::Crypt->SignEncrypt( %args );
     -    return 1 unless $res{'exit_code'};
    --
    ++    my $IsSuspiciousSender = CheckForSuspiciousSender($head);
    + 
     -    my @status = RT::Crypt->ParseStatus(
     -        Protocol => $res{'Protocol'}, Status => $res{'status'},
     -    );
    --
    ++    my $IsALoop = CheckForLoops($head);
    + 
     -    my @bad_recipients;
     -    foreach my $line ( @status ) {
     -        # if the passphrase fails, either you have a bad passphrase
    @@ -949,23 +1173,11 @@
     -        push @bad_recipients, $line;
     -    }
     -    return 0 unless @bad_recipients;
    -+        foreach my $action (@{ $args{Actions} }) {
    -+            ( $CurrentUser, $NewAuthStat ) = $Code->(
    -+                Message       => $args{Message},
    -+                RawMessageRef => $args{RawMessageRef},
    -+                CurrentUser   => $CurrentUser,
    -+                AuthLevel     => $AuthStat,
    -+                Action        => $action,
    -+                Ticket        => $args{SystemTicket},
    -+                Queue         => $args{SystemQueue},
    -+            );
    ++    my $SquelchReplies = 0;
      
     -    $_->{'AddressObj'} = (Email::Address->parse( $_->{'Recipient'} ))[0]
     -        foreach @bad_recipients;
    -+# You get the highest level of authentication you were assigned, unless you get the magic -1
    -+# If a module returns a "-1" then we discard the ticket, so.
    -+            $AuthStat = $NewAuthStat
    -+                if ( $NewAuthStat > $AuthStat or $NewAuthStat == -1 or $NewAuthStat == -2 );
    ++    my $owner_mail = RT->Config->Get('OwnerEmail');
      
     -    foreach my $recipient ( @bad_recipients ) {
     -        my $status = SendEmailUsingTemplate(
    @@ -979,11 +1191,14 @@
     -        );
     -        unless ( $status ) {
     -            $RT::Logger->error("Couldn't send 'Error: public key'");
    -+            last if $AuthStat == -1;
    -+            $skip_action{$action}++ if $AuthStat == -2;
    -         }
    --    }
    --
    +-        }
    ++    #If the message is autogenerated, we need to know, so we can not
    ++    # send mail to the sender
    ++    if ( $IsBounce || $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) {
    ++        $SquelchReplies = 1;
    ++        $ErrorsTo       = $owner_mail;
    +     }
    + 
     -    my $status = SendEmailUsingTemplate(
     -        To        => RT->Config->Get('OwnerEmail'),
     -        Template  => 'Error to RT owner: public key',
    @@ -996,15 +1211,23 @@
     -    unless ( $status ) {
     -        $RT::Logger->error("Couldn't send 'Error to RT owner: public key'");
     -    }
    ++    # Warn someone if it's a loop, before we drop it on the ground
    ++    if ($IsALoop) {
    ++        $RT::Logger->crit("RT Received mail (".$args{MessageId}.") from itself.");
      
     -    DeleteRecipientsFromHead(
     -        $args{'Entity'}->head,
     -        map $_->{'AddressObj'}->address, @bad_recipients
     -    );
    -+        # strip actions we should skip
    -+        @{$args{Actions}} = grep !$skip_action{$_}, @{$args{Actions}}
    -+            if $AuthStat == -2;
    -+        last unless @{$args{Actions}};
    ++        #Should we mail it to RTOwner?
    ++        if ( RT->Config->Get('LoopsToRTOwner') ) {
    ++            MailError(
    ++                To          => $owner_mail,
    ++                Subject     => "RT Bounce: ".$args{'Subject'},
    ++                Explanation => "RT thinks this message may be a bounce",
    ++                MIMEObj     => $args{Message}
    ++            );
    ++        }
      
     -    unless ( $args{'Entity'}->head->get('To')
     -          || $args{'Entity'}->head->get('Cc')
    @@ -1012,53 +1235,87 @@
     -    {
     -        $RT::Logger->debug("$msgid No recipients that have public key, not sending");
     -        return -1;
    -+        last if $AuthStat == -1;
    ++        #Do we actually want to store it?
    ++        return ( 0, $ErrorsTo, "Message Bounced", $IsALoop )
    ++            unless RT->Config->Get('StoreLoops');
          }
      
     -    # redo without broken recipients
     -    %res = RT::Crypt->SignEncrypt( %args );
     -    return 0 if $res{'exit_code'};
    -+    return $AuthStat if !wantarray;
    ++    # Squelch replies if necessary
    ++    # Don't let the user stuff the RT-Squelch-Replies-To header.
    ++    if ( $head->get('RT-Squelch-Replies-To') ) {
    ++        $head->replace(
    ++            'RT-Relocated-Squelch-Replies-To',
    ++            $head->get('RT-Squelch-Replies-To')
    ++        );
    ++        $head->delete('RT-Squelch-Replies-To');
    ++    }
      
     -    return 1;
    -+    return ($AuthStat, $CurrentUser, $error);
    ++    if ($SquelchReplies) {
    ++
    ++        # Squelch replies to the sender, and also leave a clue to
    ++        # allow us to squelch ALL outbound messages. This way we
    ++        # can punt the logic of "what to do when we get a bounce"
    ++        # to the scrip. We might want to notify nobody. Or just
    ++        # the RT Owner. Or maybe all Privileged watchers.
    ++        my ( $Sender, $junk ) = ParseSenderAddressFromHead($head);
    ++        $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
    ++        $head->replace( 'RT-DetectedAutoGenerated', 'true' );
    ++    }
    ++    return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
      }
      
     -use MIME::Words ();
    --
    ++=head2 CheckForLoops HEAD
    + 
     -=head2 EncodeToMIME
    --
    ++Takes a HEAD object of L<MIME::Head> class and returns true if the
    ++message's been sent by this RT instance. Uses "X-RT-Loop-Prevention"
    ++field of the head for test.
    + 
     -Takes a hash with a String and a Charset. Returns the string encoded
     -according to RFC2047, using B (base64 based) encoding.
    --
    ++=cut
    + 
     -String must be a perl string, octets are returned.
    -+=head2 _NoAuthorizedUserFound
    ++sub CheckForLoops {
    ++    my $head = shift;
      
     -If Charset is not provided then $EmailOutputEncoding config option
     -is used, or "latin-1" if that is not set.
    -+Emails the RT Owner and the requestor when the auth plugins return "No auth user found"
    - 
    - =cut
    ++    # If this instance of RT sent it our, we don't want to take it in
    ++    my $RTLoop = Encode::decode( "UTF-8", $head->get("X-RT-Loop-Prevention") || "" );
    ++    chomp ($RTLoop); # remove that newline
    ++    if ( $RTLoop eq RT->Config->Get('rtname') ) {
    ++        return 1;
    ++    }
    + 
    +-=cut
    ++    # TODO: We might not trap the case where RT instance A sends a mail
    ++    # to RT instance B which sends a mail to ...
    ++    return undef;
    ++}
      
     -sub EncodeToMIME {
    -+sub _NoAuthorizedUserFound {
    -     my %args = (
    +-    my %args = (
     -        String => undef,
     -        Charset  => undef,
    -+        Right     => undef,
    -+        Message   => undef,
    -+        Requestor => undef,
    -+        Queue     => undef,
    -         @_
    -     );
    +-        @_
    +-    );
     -    my $value = $args{'String'};
     -    return $value unless $value; # 0 is perfect ascii
     -    my $charset  = $args{'Charset'} || RT->Config->Get('EmailOutputEncoding');
     -    my $encoding = 'B';
    --
    ++=head2 CheckForSuspiciousSender HEAD
    + 
     -    # using RFC2047 notation, sec 2.
     -    # encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
    --
    ++Takes a HEAD object of L<MIME::Head> class and returns true if sender
    ++is suspicious. Suspicious means mailer daemon.
    + 
     -    # An 'encoded-word' may not be more than 75 characters long
     -    #
     -    # MIME encoding increases 4/3*(number of bytes), and always in multiples
    @@ -1073,40 +1330,41 @@
     -        ) / 4
     -    );
     -    $max = int( $max / 3 ) * 3;
    --
    ++See also L</ParseSenderAddressFromHead>.
    + 
     -    chomp $value;
    ++=cut
      
     -    if ( $max <= 0 ) {
    -+    # Notify the RT Admin of the failure.
    -+    MailError(
    -+        To          => RT->Config->Get('OwnerEmail'),
    -+        Subject     => "Could not load a valid user",
    -+        Explanation => <<EOT,
    -+RT could not load a valid user, and RT's configuration does not allow
    -+for the creation of a new user for this email (@{[$args{Requestor}]}).
    ++sub CheckForSuspiciousSender {
    ++    my $head = shift;
      
     -        # gives an error...
     -        $RT::Logger->crit("Can't encode! Charset or encoding too big.");
     -        return ($value);
     -    }
    -+You might need to grant 'Everyone' the right '@{[$args{Right}]}' for the
    -+queue @{[$args{'Queue'}]}.
    ++    #if it's from a postmaster or mailer daemon, it's likely a bounce.
      
     -    return ($value) if $value =~ /^(?:[\t\x20-\x7e]|\x0D*\x0A[ \t])+$/s;
    -+EOT
    -+        MIMEObj  => $args{'Message'},
    -+        LogLevel => 'error'
    -+    );
    ++    #TODO: better algorithms needed here - there is no standards for
    ++    #bounces, so it's very difficult to separate them from anything
    ++    #else.  At the other hand, the Return-To address is only ment to be
    ++    #used as an error channel, we might want to put up a separate
    ++    #Return-To address which is treated differently.
      
     -    $value =~ s/\s+$//;
    -+    # Also notify the requestor that his request has been dropped.
    -+    if ($args{'Requestor'} ne RT->Config->Get('OwnerEmail')) {
    -+    MailError(
    -+        To          => $args{'Requestor'},
    -+        Subject     => "Could not load a valid user",
    -+        Explanation => <<EOT,
    -+RT could not load a valid user, and RT's configuration does not allow
    -+for the creation of a new user for your email.
    ++    #TODO: search through the whole email and find the right Ticket ID.
    ++
    ++    my ( $From, $junk ) = ParseSenderAddressFromHead($head);
    ++
    ++    # If unparseable (non-ASCII), $From can come back undef
    ++    return undef if not defined $From;
    ++
    ++    if (   ( $From =~ /^mailer-daemon\@/i )
    ++        or ( $From =~ /^postmaster\@/i )
    ++        or ( $From eq "" ))
    ++    {
    ++        return (1);
      
     -    my ( $tmp, @chunks ) = ( '', () );
     -    while ( length $value ) {
    @@ -1117,356 +1375,94 @@
     -            $tmp = '';
     -        }
     -        $tmp .= $octets;
    -+EOT
    -+        MIMEObj  => $args{'Message'},
    -+        LogLevel => 'error'
    -+    );
          }
     -    push @chunks, $tmp if length $tmp;
    --
    + 
     -    # encode an join chuncks
     -    $value = join "\n ",
     -        map MIME::Words::encode_mimeword( $_, $encoding, $charset ),
     -        @chunks;
     -    return ($value);
    - }
    - 
    - sub CreateUser {
    -@@
    - }
    - 
    - 
    --
    - =head2 ParseCcAddressesFromHead HASH
    - 
    - Takes a hash containing QueueObj, Head and CurrentUser objects.
    -@@
    -         push @errors, "$header: $addr_line";
    -     }
    - 
    --    return (undef, undef, @errors);
    -+    return (undef, undef, @errors);
    -+}
    -+
    -+=head2 ParseErrorsToAddressFromHead HEAD
    -+
    -+Takes a MIME::Header object. Return a single value : user at host
    -+of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
    -+From:, Sender)
    -+
    -+=cut
    -+
    -+sub ParseErrorsToAddressFromHead {
    -+    my $head = shift;
    -+
    -+    #Figure out who's sending this message.
    -+
    -+    foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
    -+
    -+        # If there's a header of that name
    -+        my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
    -+        if ($headerobj) {
    -+            my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
    -+
    -+            # If it's got actual useful content...
    -+            return ($addr) if ($addr);
    -+        }
    -+    }
    -+}
    -+
    -+
    -+
    -+=head2 ParseAddressFromHeader ADDRESS
    -+
    -+Takes an address from C<$head->get('Line')> and returns a tuple: user at host, friendly name
    -+
    -+=cut
    -+
    -+sub ParseAddressFromHeader {
    -+    my $Addr = shift;
    -+
    -+    # Some broken mailers send:  ""Vincent, Jesse"" <jesse at fsck.com>. Hate
    -+    $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
    -+    my @Addresses = RT::EmailParser->ParseEmailAddress($Addr);
    -+
    -+    my ($AddrObj) = grep ref $_, @Addresses;
    -+    unless ( $AddrObj ) {
    -+        return ( undef, undef );
    -+    }
    -+
    -+    return ( $AddrObj->address, $AddrObj->phrase );
    -+}
    -+
    -+=head2 _HandleMachineGeneratedMail
    -+
    -+Takes named params:
    -+    Message
    -+    ErrorsTo
    -+    Subject
    -+
    -+Checks the message to see if it's a bounce, if it looks like a loop, if it's autogenerated, etc.
    -+Returns a triple of ("Should we continue (boolean)", "New value for $ErrorsTo", "Status message",
    -+"This message appears to be a loop (boolean)" );
    -+
    -+=cut
    -+
    -+sub _HandleMachineGeneratedMail {
    -+    my %args = ( Message => undef, ErrorsTo => undef, Subject => undef, MessageId => undef, @_ );
    -+    my $head = $args{'Message'}->head;
    -+    my $ErrorsTo = $args{'ErrorsTo'};
    -+
    -+    my $IsBounce = CheckForBounce($head);
    -+
    -+    my $IsAutoGenerated = CheckForAutoGenerated($head);
    -+
    -+    my $IsSuspiciousSender = CheckForSuspiciousSender($head);
    -+
    -+    my $IsALoop = CheckForLoops($head);
    -+
    -+    my $SquelchReplies = 0;
    -+
    -+    my $owner_mail = RT->Config->Get('OwnerEmail');
    -+
    -+    #If the message is autogenerated, we need to know, so we can not
    -+    # send mail to the sender
    -+    if ( $IsBounce || $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) {
    -+        $SquelchReplies = 1;
    -+        $ErrorsTo       = $owner_mail;
    -+    }
    -+
    -+    # Warn someone if it's a loop, before we drop it on the ground
    -+    if ($IsALoop) {
    -+        $RT::Logger->crit("RT Received mail (".$args{MessageId}.") from itself.");
    -+
    -+        #Should we mail it to RTOwner?
    -+        if ( RT->Config->Get('LoopsToRTOwner') ) {
    -+            MailError(
    -+                To          => $owner_mail,
    -+                Subject     => "RT Bounce: ".$args{'Subject'},
    -+                Explanation => "RT thinks this message may be a bounce",
    -+                MIMEObj     => $args{Message}
    -+            );
    -+        }
    -+
    -+        #Do we actually want to store it?
    -+        return ( 0, $ErrorsTo, "Message Bounced", $IsALoop )
    -+            unless RT->Config->Get('StoreLoops');
    -+    }
    -+
    -+    # Squelch replies if necessary
    -+    # Don't let the user stuff the RT-Squelch-Replies-To header.
    -+    if ( $head->get('RT-Squelch-Replies-To') ) {
    -+        $head->replace(
    -+            'RT-Relocated-Squelch-Replies-To',
    -+            $head->get('RT-Squelch-Replies-To')
    -+        );
    -+        $head->delete('RT-Squelch-Replies-To');
    -+    }
    -+
    -+    if ($SquelchReplies) {
    -+
    -+        # Squelch replies to the sender, and also leave a clue to
    -+        # allow us to squelch ALL outbound messages. This way we
    -+        # can punt the logic of "what to do when we get a bounce"
    -+        # to the scrip. We might want to notify nobody. Or just
    -+        # the RT Owner. Or maybe all Privileged watchers.
    -+        my ( $Sender, $junk ) = ParseSenderAddressFromHead($head);
    -+        $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
    -+        $head->replace( 'RT-DetectedAutoGenerated', 'true' );
    -+    }
    -+    return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
    -+}
    -+
    -+=head2 CheckForLoops HEAD
    -+
    -+Takes a HEAD object of L<MIME::Head> class and returns true if the
    -+message's been sent by this RT instance. Uses "X-RT-Loop-Prevention"
    -+field of the head for test.
    -+
    -+=cut
    -+
    -+sub CheckForLoops {
    -+    my $head = shift;
    -+
    -+    # If this instance of RT sent it our, we don't want to take it in
    -+    my $RTLoop = Encode::decode( "UTF-8", $head->get("X-RT-Loop-Prevention") || "" );
    -+    chomp ($RTLoop); # remove that newline
    -+    if ( $RTLoop eq RT->Config->Get('rtname') ) {
    -+        return 1;
    -+    }
    -+
    -+    # TODO: We might not trap the case where RT instance A sends a mail
    -+    # to RT instance B which sends a mail to ...
     +    return undef;
      }
      
    --=head2 ParseErrorsToAddressFromHead HEAD
    -+=head2 CheckForSuspiciousSender HEAD
    - 
    --Takes a MIME::Header object. Return a single value : user at host
    --of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
    --From:, Sender)
    -+Takes a HEAD object of L<MIME::Head> class and returns true if sender
    -+is suspicious. Suspicious means mailer daemon.
    -+
    -+See also L</ParseSenderAddressFromHead>.
    - 
    - =cut
    - 
    --sub ParseErrorsToAddressFromHead {
    -+sub CheckForSuspiciousSender {
    -     my $head = shift;
    - 
    --    #Figure out who's sending this message.
    --
    --    foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
    --
    --        # If there's a header of that name
    --        my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
    --        if ($headerobj) {
    --            my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
    --
    --            # If it's got actual useful content...
    --            return ($addr) if ($addr);
    --        }
    --    }
    --}
    --
    --
    -+    #if it's from a postmaster or mailer daemon, it's likely a bounce.
    - 
    --=head2 ParseAddressFromHeader ADDRESS
    -+    #TODO: better algorithms needed here - there is no standards for
    -+    #bounces, so it's very difficult to separate them from anything
    -+    #else.  At the other hand, the Return-To address is only ment to be
    -+    #used as an error channel, we might want to put up a separate
    -+    #Return-To address which is treated differently.
    - 
    --Takes an address from C<$head->get('Line')> and returns a tuple: user at host, friendly name
    -+    #TODO: search through the whole email and find the right Ticket ID.
    - 
    --=cut
    -+    my ( $From, $junk ) = ParseSenderAddressFromHead($head);
    - 
    --sub ParseAddressFromHeader {
    --    my $Addr = shift;
    -+    # If unparseable (non-ASCII), $From can come back undef
    -+    return undef if not defined $From;
    - 
    --    # Some broken mailers send:  ""Vincent, Jesse"" <jesse at fsck.com>. Hate
    --    $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
    --    my @Addresses = RT::EmailParser->ParseEmailAddress($Addr);
    -+    if (   ( $From =~ /^mailer-daemon\@/i )
    -+        or ( $From =~ /^postmaster\@/i )
    -+        or ( $From eq "" ))
    -+    {
    -+        return (1);
    - 
    --    my ($AddrObj) = grep ref $_, @Addresses;
    --    unless ( $AddrObj ) {
    --        return ( undef, undef );
    -     }
    - 
    --    return ( $AddrObj->address, $AddrObj->phrase );
    -+    return undef;
    - }
    - 
    --=head2 DeleteRecipientsFromHead HEAD RECIPIENTS
    +-sub CreateUser {
    +-    my ( $Username, $Address, $Name, $ErrorsTo, $entity ) = @_;
     +=head2 CheckForAutoGenerated HEAD
      
    --Gets a head object and list of addresses.
    --Deletes addresses from To, Cc or Bcc fields.
    +-    my $NewUser = RT::User->new( RT->SystemUser );
     +Takes a HEAD object of L<MIME::Head> class and returns true if message is
     +autogenerated. Checks C<Precedence>, C<Auto-Submitted>, and
     +C<X-FC-Machinegenerated> fields of the head in tests.
      
    - =cut
    - 
    --sub DeleteRecipientsFromHead {
    +-    my ( $Val, $Message ) = $NewUser->Create(
    +-        Name => ( $Username || $Address ),
    +-        EmailAddress => $Address,
    +-        RealName     => $Name,
    +-        Password     => undef,
    +-        Privileged   => 0,
    +-        Comments     => 'Autocreated on ticket submission',
    +-    );
    ++=cut
    + 
    +-    unless ($Val) {
     +sub CheckForAutoGenerated {
    -     my $head = shift;
    --    my %skip = map { lc $_ => 1 } @_;
    - 
    --    foreach my $field ( qw(To Cc Bcc) ) {
    --        $head->replace( $field => Encode::encode( "UTF-8",
    --            join ', ', map $_->format, grep !$skip{ lc $_->address },
    --                Email::Address->parse( Encode::decode( "UTF-8", $head->get( $field ) ) ) )
    --        );
    ++    my $head = shift;
    + 
    +-        # Deal with the race condition of two account creations at once
    +-        if ($Username) {
    +-            $NewUser->LoadByName($Username);
    +-        }
     +    if (grep { /^(bulk|junk)/i } $head->get_all("Precedence")) {
     +        return (1);
    -     }
    --}
    --
    --sub GenMessageId {
    --    my %args = (
    --        Ticket      => undef,
    --        Scrip       => undef,
    --        ScripAction => undef,
    --        @_
    --    );
    --    my $org = RT->Config->Get('Organization');
    --    my $ticket_id = ( ref $args{'Ticket'}? $args{'Ticket'}->id : $args{'Ticket'} ) || 0;
    --    my $scrip_id = ( ref $args{'Scrip'}? $args{'Scrip'}->id : $args{'Scrip'} ) || 0;
    --    my $sent = ( ref $args{'ScripAction'}? $args{'ScripAction'}->{'_Message_ID'} : 0 ) || 0;
    --
    --    return "<rt-". $RT::VERSION ."-". $$ ."-". CORE::time() ."-". int(rand(2000)) .'.'
    --        . $ticket_id ."-". $scrip_id ."-". $sent ."@". $org .">" ;
    --}
    --
    --sub SetInReplyTo {
    --    my %args = (
    --        Message   => undef,
    --        InReplyTo => undef,
    --        Ticket    => undef,
    --        @_
    --    );
    --    return unless $args{'Message'} && $args{'InReplyTo'};
    --
    --    my $get_header = sub {
    --        my @res;
    --        if ( $args{'InReplyTo'}->isa('MIME::Entity') ) {
    --            @res = map {Encode::decode("UTF-8", $_)} $args{'InReplyTo'}->head->get( shift );
    --        } else {
    --            @res = $args{'InReplyTo'}->GetHeader( shift ) || '';
    ++    }
    + 
    +-        unless ( $NewUser->Id ) {
    +-            $NewUser->LoadByEmail($Address);
     -        }
    --        return grep length, map { split /\s+/m, $_ } grep defined, @res;
    --    };
    - 
    --    my @id = $get_header->('Message-ID');
    --    #XXX: custom header should begin with X- otherwise is violation of the standard
    --    my @rtid = $get_header->('RT-Message-ID');
    --    my @references = $get_header->('References');
    --    unless ( @references ) {
    --        @references = $get_header->('In-Reply-To');
     +    # Per RFC3834, any Auto-Submitted header which is not "no" means
     +    # it is auto-generated.
     +    my $AutoSubmitted = $head->get("Auto-Submitted") || "";
     +    if ( length $AutoSubmitted and $AutoSubmitted ne "no" ) {
     +        return (1);
    -     }
    --    push @references, @id, @rtid;
    --    if ( $args{'Ticket'} ) {
    --        my $pseudo_ref = PseudoReference( $args{'Ticket'} );
    --        push @references, $pseudo_ref unless grep $_ eq $pseudo_ref, @references;
    -+
    ++    }
    + 
    +-        unless ( $NewUser->Id ) {
    +-            MailError(
    +-                To          => $ErrorsTo,
    +-                Subject     => "User could not be created",
    +-                Explanation =>
    +-                    "User creation failed in mailgateway: $Message",
    +-                MIMEObj  => $entity,
    +-                LogLevel => 'crit',
    +-            );
    +-        }
     +    # First Class mailer uses this as a clue.
     +    my $FCJunk = $head->get("X-FC-Machinegenerated") || "";
     +    if ( $FCJunk =~ /^true/i ) {
     +        return (1);
          }
    --    splice @references, 4, -6
    --        if @references > 10;
    - 
    --    my $mail = $args{'Message'};
    --    $mail->head->replace( 'In-Reply-To' => Encode::encode( "UTF-8", join ' ', @rtid? (@rtid) : (@id)) ) if @id || @rtid;
    --    $mail->head->replace( 'References' => Encode::encode( "UTF-8", join ' ', @references) );
    + 
    +-    #Load the new user object
    +-    my $CurrentUser = RT::CurrentUser->new;
    +-    $CurrentUser->LoadByEmail( $Address );
     +    return (0);
    - }
    - 
    --sub PseudoReference {
    --    my $ticket = shift;
    --    return '<RT-Ticket-'. $ticket->id .'@'. RT->Config->Get('Organization') .'>';
    -+
    ++}
    + 
    +-    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  => $entity,
    +-            LogLevel => 'crit'
    +-        );
    +-    }
    + 
    +-    return $CurrentUser;
     +sub CheckForBounce {
     +    my $head = shift;
     +
    @@ -1474,18 +1470,116 @@
     +    return ( $ReturnPath =~ /<>/ );
      }
      
    - =head2 ExtractTicketId
    -@@
    -     return $id;
    + 
    ++=head2 ExtractTicketId
    + 
    +-=head2 ParseCcAddressesFromHead HASH
    ++Passed a MIME::Entity.  Returns a ticket id or undef to signal 'new ticket'.
    + 
    +-Takes a hash containing QueueObj, Head and CurrentUser objects.
    +-Returns a list of all email addresses in the To and Cc
    +-headers b<except> the current Queue's email addresses, the CurrentUser's
    +-email address  and anything that the configuration sub RT::IsRTAddress matches.
    ++This is a great entry point if you need to customize how ticket ids are
    ++handled for your site. RT-Extension-RepliesToResolved demonstrates one
    ++possible use for this extension.
    + 
    +-=cut
    ++If the Subject of this ticket is modified, it will be reloaded by the
    ++mail gateway code before Ticket creation.
    + 
    +-sub ParseCcAddressesFromHead {
    +-    my %args = (
    +-        Head        => undef,
    +-        QueueObj    => undef,
    +-        CurrentUser => undef,
    +-        @_
    +-    );
    ++=cut
    + 
    +-    my $current_address = lc $args{'CurrentUser'}->EmailAddress;
    +-    my $user = $args{'CurrentUser'}->UserObj;
    ++sub ExtractTicketId {
    ++    my $entity = shift;
    + 
    +-    return
    +-        grep $_ ne $current_address && !RT::EmailParser->IsRTAddress( $_ ),
    +-        map lc $user->CanonicalizeEmailAddress( $_->address ),
    +-        map RT::EmailParser->CleanupAddresses( Email::Address->parse(
    +-              Encode::decode( "UTF-8", $args{'Head'}->get( $_ ) ) ) ),
    +-        qw(To Cc);
    ++    my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') || '' );
    ++    chomp $subject;
    ++    return ParseTicketId( $subject );
      }
      
    --sub AddSubjectTag {
    --    my $subject = shift;
    --    my $ticket  = shift;
    --    unless ( ref $ticket ) {
    --        my $tmp = RT::Ticket->new( RT->SystemUser );
    --        $tmp->Load( $ticket );
    --        $ticket = $tmp;
    ++=head2 ParseTicketId
    + 
    ++Takes a string and searches for [subjecttag #id]
    + 
    +-=head2 ParseSenderAddressFromHead HEAD
    +-
    +-Takes a MIME::Header object. Returns (user at host, friendly name, errors)
    +-where the first two values are the From (evaluated in order of
    +-Reply-To:, From:, Sender).
    +-
    +-A list of error messages may be returned even when a Sender value is
    +-found, since it could be a parse error for another (checked earlier)
    +-sender field. In this case, the errors aren't fatal, but may be useful
    +-to investigate the parse failure.
    ++Returns the id if a match is found.  Otherwise returns undef.
    + 
    + =cut
    + 
    +-sub ParseSenderAddressFromHead {
    +-    my $head = shift;
    +-    my @sender_headers = ('Reply-To', 'From', 'Sender');
    +-    my @errors;  # Accumulate any errors
    ++sub ParseTicketId {
    ++    my $Subject = shift;
    + 
    +-    #Figure out who's sending this message.
    +-    foreach my $header ( @sender_headers ) {
    +-        my $addr_line = Encode::decode( "UTF-8", $head->get($header) ) || next;
    +-        my ($addr, $name) = ParseAddressFromHeader( $addr_line );
    +-        # only return if the address is not empty
    +-        return ($addr, $name, @errors) if $addr;
    ++    my $rtname = RT->Config->Get('rtname');
    ++    my $test_name = RT->Config->Get('EmailSubjectTagRegex') || qr/\Q$rtname\E/i;
    + 
    +-        chomp $addr_line;
    +-        push @errors, "$header: $addr_line";
    ++    # We use @captures and pull out the last capture value to guard against
    ++    # someone using (...) instead of (?:...) in $EmailSubjectTagRegex.
    ++    my $id;
    ++    if ( my @captures = $Subject =~ /\[$test_name\s+\#(\d+)\s*\]/i ) {
    ++        $id = $captures[-1];
    ++    } else {
    ++        foreach my $tag ( RT->System->SubjectTag ) {
    ++            next unless my @captures = $Subject =~ /\[\Q$tag\E\s+\#(\d+)\s*\]/i;
    ++            $id = $captures[-1];
    ++            last;
    ++        }
    +     }
    ++    return undef unless $id;
    + 
    +-    return (undef, undef, @errors);
    ++    $RT::Logger->debug("Found a ticket ID. It's $id");
    ++    return $id;
    + }
    + 
    +-=head2 ParseErrorsToAddressFromHead HEAD
    +-
    +-Takes a MIME::Header object. Return a single value : user at host
    +-of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
    +-From:, Sender)
    +-
    +-=cut
    +-
    +-sub ParseErrorsToAddressFromHead {
    +-    my $head = shift;
    +-
    +-    #Figure out who's sending this message.
     +sub _RunUnsafeAction {
     +    my %args = (
     +        Action      => undef,
    @@ -1495,9 +1589,14 @@
     +        CurrentUser => undef,
     +        @_
     +    );
    -+
    + 
    +-    foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
     +    my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
    -+
    + 
    +-        # If there's a header of that name
    +-        my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
    +-        if ($headerobj) {
    +-            my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
     +    if ( $args{'Action'} =~ /^take$/i ) {
     +        my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
     +        unless ($status) {
    @@ -1514,7 +1613,9 @@
     +        if ($new_status) {
     +            my ( $status, $msg ) = $args{'Ticket'}->SetStatus($new_status);
     +            unless ($status) {
    -+
    + 
    +-            # If it's got actual useful content...
    +-            return ($addr) if ($addr);
     +                #Warn the sender that we couldn't actually submit the comment.
     +                MailError(
     +                    To          => $args{'ErrorsTo'},
    @@ -1524,51 +1625,76 @@
     +                );
     +                return ( 0, "Ticket not resolved, by email From: $From" );
     +            }
    -+        }
    +         }
     +    } else {
     +        return ( 0, "Not supported unsafe action $args{'Action'}, by email From: $From", $args{'Ticket'} );
          }
    --    my $id = $ticket->id;
    --    my $queue_tag = $ticket->QueueObj->SubjectTag;
     +    return ( 1, "Success" );
    -+}
    - 
    --    my $tag_re = RT->Config->Get('EmailSubjectTagRegex');
    --    unless ( $tag_re ) {
    --        my $tag = $queue_tag || RT->Config->Get('rtname');
    --        $tag_re = qr/\Q$tag\E/;
    --    } elsif ( $queue_tag ) {
    --        $tag_re = qr/$tag_re|\Q$queue_tag\E/;
    + }
    + 
     +=head2 MailError PARAM HASH
    -+
    + 
     +Sends an error message. Takes a param hash:
    -+
    + 
    +-=head2 ParseAddressFromHeader ADDRESS
    +-
    +-Takes an address from C<$head->get('Line')> and returns a tuple: user at host, friendly name
     +=over 4
    -+
    + 
    +-=cut
     +=item From - sender's address, by default is 'CorrespondAddress';
    -+
    + 
    +-sub ParseAddressFromHeader {
    +-    my $Addr = shift;
     +=item To - recipient, by default is 'OwnerEmail';
    -+
    + 
    +-    # Some broken mailers send:  ""Vincent, Jesse"" <jesse at fsck.com>. Hate
    +-    $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
    +-    my @Addresses = RT::EmailParser->ParseEmailAddress($Addr);
     +=item Bcc - optional Bcc recipients;
    -+
    + 
    +-    my ($AddrObj) = grep ref $_, @Addresses;
    +-    unless ( $AddrObj ) {
    +-        return ( undef, undef );
    +-    }
     +=item Subject - subject of the message, default is 'There has been an error';
    -+
    + 
    +-    return ( $AddrObj->address, $AddrObj->phrase );
    +-}
     +=item Explanation - main content of the error, default value is 'Unexplained error';
    -+
    + 
    +-=head2 DeleteRecipientsFromHead HEAD RECIPIENTS
     +=item MIMEObj - optional MIME entity that's attached to the error mail, as well we
     +add 'In-Reply-To' field to the error that points to this message.
    -+
    + 
    +-Gets a head object and list of addresses.
    +-Deletes addresses from To, Cc or Bcc fields.
     +=item Attach - optional text that attached to the error as 'message/rfc822' part.
    -+
    + 
    +-=cut
     +=item LogLevel - log level under which we should write the subject and
     +explanation message into the log, by default we log it as critical.
    -+
    + 
    +-sub DeleteRecipientsFromHead {
    +-    my $head = shift;
    +-    my %skip = map { lc $_ => 1 } @_;
     +=back
    -+
    + 
    +-    foreach my $field ( qw(To Cc Bcc) ) {
    +-        $head->replace( $field => Encode::encode( "UTF-8",
    +-            join ', ', map $_->format, grep !$skip{ lc $_->address },
    +-                Email::Address->parse( Encode::decode( "UTF-8", $head->get( $field ) ) ) )
    +-        );
    +-    }
    +-}
     +=cut
    -+
    + 
    +-sub GenMessageId {
     +sub MailError {
    -+    my %args = (
    +     my %args = (
    +-        Ticket      => undef,
    +-        Scrip       => undef,
    +-        ScripAction => undef,
     +        To          => RT->Config->Get('OwnerEmail'),
     +        Bcc         => undef,
     +        From        => RT->Config->Get('CorrespondAddress'),
    @@ -1577,14 +1703,27 @@
     +        MIMEObj     => undef,
     +        Attach      => undef,
     +        LogLevel    => 'crit',
    -+        @_
    -+    );
    -+
    +         @_
    +     );
    +-    my $org = RT->Config->Get('Organization');
    +-    my $ticket_id = ( ref $args{'Ticket'}? $args{'Ticket'}->id : $args{'Ticket'} ) || 0;
    +-    my $scrip_id = ( ref $args{'Scrip'}? $args{'Scrip'}->id : $args{'Scrip'} ) || 0;
    +-    my $sent = ( ref $args{'ScripAction'}? $args{'ScripAction'}->{'_Message_ID'} : 0 ) || 0;
    + 
    +-    return "<rt-". $RT::VERSION ."-". $$ ."-". CORE::time() ."-". int(rand(2000)) .'.'
    +-        . $ticket_id ."-". $scrip_id ."-". $sent ."@". $org .">" ;
    +-}
     +    $RT::Logger->log(
     +        level   => $args{'LogLevel'},
     +        message => "$args{Subject}: $args{'Explanation'}",
     +    ) if $args{'LogLevel'};
    -+
    + 
    +-sub SetInReplyTo {
    +-    my %args = (
    +-        Message   => undef,
    +-        InReplyTo => undef,
    +-        Ticket    => undef,
    +-        @_
     +    # the colons are necessary to make ->build include non-standard headers
     +    my %entity_args = (
     +        Type                    => "multipart/mixed",
    @@ -1593,99 +1732,166 @@
     +        To                      => Encode::encode( "UTF-8", $args{'To'} ),
     +        Subject                 => EncodeToMIME( String => $args{'Subject'} ),
     +        'X-RT-Loop-Prevention:' => Encode::encode( "UTF-8", RT->Config->Get('rtname') ),
    -+    );
    -+
    +     );
    +-    return unless $args{'Message'} && $args{'InReplyTo'};
    +-
    +-    my $get_header = sub {
    +-        my @res;
    +-        if ( $args{'InReplyTo'}->isa('MIME::Entity') ) {
    +-            @res = map {Encode::decode("UTF-8", $_)} $args{'InReplyTo'}->head->get( shift );
    +-        } else {
    +-            @res = $args{'InReplyTo'}->GetHeader( shift ) || '';
    +-        }
    +-        return grep length, map { split /\s+/m, $_ } grep defined, @res;
    +-    };
    + 
    +-    my @id = $get_header->('Message-ID');
    +-    #XXX: custom header should begin with X- otherwise is violation of the standard
    +-    my @rtid = $get_header->('RT-Message-ID');
    +-    my @references = $get_header->('References');
    +-    unless ( @references ) {
    +-        @references = $get_header->('In-Reply-To');
    +-    }
    +-    push @references, @id, @rtid;
    +-    if ( $args{'Ticket'} ) {
    +-        my $pseudo_ref = PseudoReference( $args{'Ticket'} );
    +-        push @references, $pseudo_ref unless grep $_ eq $pseudo_ref, @references;
     +    # only set precedence if the sysadmin wants us to
     +    if (defined(RT->Config->Get('DefaultErrorMailPrecedence'))) {
     +        $entity_args{'Precedence:'} =
     +            Encode::encode( "UTF-8", RT->Config->Get('DefaultErrorMailPrecedence') );
          }
    --    return $subject if $subject =~ /\[$tag_re\s+#$id\]/;
    - 
    --    $subject =~ s/(\r\n|\n|\s)/ /g;
    --    chomp $subject;
    --    return "[". ($queue_tag || RT->Config->Get('rtname')) ." #$id] $subject";
    +-    splice @references, 4, -6
    +-        if @references > 10;
    + 
    +-    my $mail = $args{'Message'};
    +-    $mail->head->replace( 'In-Reply-To' => Encode::encode( "UTF-8", join ' ', @rtid? (@rtid) : (@id)) ) if @id || @rtid;
    +-    $mail->head->replace( 'References' => Encode::encode( "UTF-8", join ' ', @references) );
     -}
     +    my $entity = MIME::Entity->build(%entity_args);
     +    SetInReplyTo( Message => $entity, InReplyTo => $args{'MIMEObj'} );
      
    +-sub PseudoReference {
    +-    my $ticket = shift;
    +-    return '<RT-Ticket-'. $ticket->id .'@'. RT->Config->Get('Organization') .'>';
    +-}
     +    $entity->attach(
     +        Type    => "text/plain",
     +        Charset => "UTF-8",
     +        Data    => Encode::encode( "UTF-8", $args{'Explanation'} . "\n" ),
     +    );
      
    --=head2 Gateway ARGSREF
    +-=head2 ExtractTicketId
     +    if ( $args{'MIMEObj'} ) {
     +        $args{'MIMEObj'}->sync_headers;
     +        $entity->add_part( $args{'MIMEObj'} );
     +    }
      
    +-Passed a MIME::Entity.  Returns a ticket id or undef to signal 'new ticket'.
     +    if ( $args{'Attach'} ) {
     +        $entity->attach( Data => Encode::encode( "UTF-8", $args{'Attach'} ), Type => 'message/rfc822' );
      
    --Takes parameters:
    -+    }
    - 
    --    action
    --    queue
    --    message
    +-This is a great entry point if you need to customize how ticket ids are
    +-handled for your site. RT-Extension-RepliesToResolved demonstrates one
    +-possible use for this extension.
    ++    }
    + 
    +-If the Subject of this ticket is modified, it will be reloaded by the
    +-mail gateway code before Ticket creation.
     +    SendEmail( Entity => $entity, Bounce => 1 );
     +}
      
    - 
    --This performs all the "guts" of the mail rt-mailgate program, and is
    --designed to be called from the web interface with a message, user
    --object, and so on.
    +-=cut
    + 
    +-sub ExtractTicketId {
    +-    my $entity = shift;
     +=head2 SendEmail Entity => undef, [ Bounce => 0, Ticket => undef, Transaction => undef ]
      
    --Can also take an optional 'ticket' parameter; this ticket id overrides
    --any ticket id found in the subject.
    +-    my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') || '' );
    +-    chomp $subject;
    +-    return ParseTicketId( $subject );
    +-}
     +Sends an email (passed as a L<MIME::Entity> object C<ENTITY>) using
     +RT's outgoing mail configuration. If C<BOUNCE> is passed, and is a
     +true value, the message will be marked as an autogenerated error, if
     +possible. Sets Date field of the head to now if it's not set.
      
    --Returns:
    +-=head2 ParseTicketId
     +If the C<X-RT-Squelch> header is set to any true value, the mail will
     +not be sent. One use is to let extensions easily cancel outgoing mail.
      
    --    An array of:
    +-Takes a string and searches for [subjecttag #id]
     +Ticket and Transaction arguments are optional. If Transaction is
     +specified and Ticket is not then ticket of the transaction is
     +used, but only if the transaction belongs to a ticket.
      
    --    (status code, message, optional ticket object)
    +-Returns the id if a match is found.  Otherwise returns undef.
     +Returns 1 on success, 0 on error or -1 if message has no recipients
     +and hasn't been sent.
      
    --    status code is a numeric value.
    +-=cut
     +=head3 Signing and Encrypting
      
    --      for temporary failures, the status code should be -75
    +-sub ParseTicketId {
    +-    my $Subject = shift;
     +This function as well signs and/or encrypts the message according to
     +headers of a transaction's attachment or properties of a ticket's queue.
     +To get full access to the configuration Ticket and/or Transaction
     +arguments must be provided, but you can force behaviour using Sign
     +and/or Encrypt arguments.
      
    --      for permanent failures which are handled by RT, the status code
    --      should be 0
    +-    my $rtname = RT->Config->Get('rtname');
    +-    my $test_name = RT->Config->Get('EmailSubjectTagRegex') || qr/\Q$rtname\E/i;
     +The following precedence of arguments are used to figure out if
     +the message should be encrypted and/or signed:
      
    --      for succces, the status code should be 1
    +-    # We use @captures and pull out the last capture value to guard against
    +-    # someone using (...) instead of (?:...) in $EmailSubjectTagRegex.
    +-    my $id;
    +-    if ( my @captures = $Subject =~ /\[$test_name\s+\#(\d+)\s*\]/i ) {
    +-        $id = $captures[-1];
    +-    } else {
    +-        foreach my $tag ( RT->System->SubjectTag ) {
    +-            next unless my @captures = $Subject =~ /\[\Q$tag\E\s+\#(\d+)\s*\]/i;
    +-            $id = $captures[-1];
    +-            last;
    +-        }
    +-    }
    +-    return undef unless $id;
     +* if Sign or Encrypt argument is defined then its value is used
      
    +-    $RT::Logger->debug("Found a ticket ID. It's $id");
    +-    return $id;
    +-}
     +* else if Transaction's first attachment has X-RT-Sign or X-RT-Encrypt
     +header field then it's value is used
      
    +-sub AddSubjectTag {
    +-    my $subject = shift;
    +-    my $ticket  = shift;
    +-    unless ( ref $ticket ) {
    +-        my $tmp = RT::Ticket->new( RT->SystemUser );
    +-        $tmp->Load( $ticket );
    +-        $ticket = $tmp;
    +-    }
    +-    my $id = $ticket->id;
    +-    my $queue_tag = $ticket->QueueObj->SubjectTag;
     +* else properties of a queue of the Ticket are used.
      
    - =cut
    - 
    --sub _LoadPlugins {
    --    my @mail_plugins = @_;
    +-    my $tag_re = RT->Config->Get('EmailSubjectTagRegex');
    +-    unless ( $tag_re ) {
    +-        my $tag = $queue_tag || RT->Config->Get('rtname');
    +-        $tag_re = qr/\Q$tag\E/;
    +-    } elsif ( $queue_tag ) {
    +-        $tag_re = qr/$tag_re|\Q$queue_tag\E/;
    +-    }
    +-    return $subject if $subject =~ /\[$tag_re\s+#$id\]/;
    ++=cut
    + 
    +-    $subject =~ s/(\r\n|\n|\s)/ /g;
    +-    chomp $subject;
    +-    return "[". ($queue_tag || RT->Config->Get('rtname')) ." #$id] $subject";
    +-}
     +sub SendEmail {
     +    my (%args) = (
     +        Entity => undef,
    @@ -1694,6 +1900,150 @@
     +        Transaction => undef,
     +        @_,
     +    );
    + 
    ++    my $TicketObj = $args{'Ticket'};
    ++    my $TransactionObj = $args{'Transaction'};
    + 
    +-=head2 Gateway ARGSREF
    ++    unless ( $args{'Entity'} ) {
    ++        $RT::Logger->crit( "Could not send mail without 'Entity' object" );
    ++        return 0;
    ++    }
    + 
    ++    my $msgid = Encode::decode( "UTF-8", $args{'Entity'}->head->get('Message-ID') || '' );
    ++    chomp $msgid;
    ++    
    ++    # If we don't have any recipients to send to, don't send a message;
    ++    unless ( $args{'Entity'}->head->get('To')
    ++        || $args{'Entity'}->head->get('Cc')
    ++        || $args{'Entity'}->head->get('Bcc') )
    ++    {
    ++        $RT::Logger->info( $msgid . " No recipients found. Not sending." );
    ++        return -1;
    ++    }
    + 
    +-Takes parameters:
    ++    if ($args{'Entity'}->head->get('X-RT-Squelch')) {
    ++        $RT::Logger->info( $msgid . " Squelch header found. Not sending." );
    ++        return -1;
    ++    }
    + 
    +-    action
    +-    queue
    +-    message
    ++    if (my $precedence = RT->Config->Get('DefaultMailPrecedence')
    ++        and !$args{'Entity'}->head->get("Precedence")
    ++    ) {
    ++        $args{'Entity'}->head->replace( 'Precedence', Encode::encode("UTF-8",$precedence) );
    ++    }
    + 
    ++    if ( $TransactionObj && !$TicketObj
    ++        && $TransactionObj->ObjectType eq 'RT::Ticket' )
    ++    {
    ++        $TicketObj = $TransactionObj->Object;
    ++    }
    + 
    +-This performs all the "guts" of the mail rt-mailgate program, and is
    +-designed to be called from the web interface with a message, user
    +-object, and so on.
    ++    my $head = $args{'Entity'}->head;
    ++    unless ( $head->get('Date') ) {
    ++        require RT::Date;
    ++        my $date = RT::Date->new( RT->SystemUser );
    ++        $date->SetToNow;
    ++        $head->replace( 'Date', Encode::encode("UTF-8",$date->RFC2822( Timezone => 'server' ) ) );
    ++    }
    ++    unless ( $head->get('MIME-Version') ) {
    ++        # We should never have to set the MIME-Version header
    ++        $head->replace( 'MIME-Version', '1.0' );
    ++    }
    ++    unless ( $head->get('Content-Transfer-Encoding') ) {
    ++        # fsck.com #5959: Since RT sends 8bit mail, we should say so.
    ++        $head->replace( 'Content-Transfer-Encoding', '8bit' );
    ++    }
    + 
    +-Can also take an optional 'ticket' parameter; this ticket id overrides
    +-any ticket id found in the subject.
    ++    if ( RT->Config->Get('Crypt')->{'Enable'} ) {
    ++        %args = WillSignEncrypt(
    ++            %args,
    ++            Attachment => $TransactionObj ? $TransactionObj->Attachments->First : undef,
    ++            Ticket     => $TicketObj,
    ++        );
    ++        my $res = SignEncrypt( %args );
    ++        return $res unless $res > 0;
    ++    }
    + 
    +-Returns:
    ++    my $mail_command = RT->Config->Get('MailCommand');
    + 
    +-    An array of:
    ++    # if it is a sub routine, we just return it;
    ++    return $mail_command->($args{'Entity'}) if UNIVERSAL::isa( $mail_command, 'CODE' );
    + 
    +-    (status code, message, optional ticket object)
    ++    if ( $mail_command eq 'sendmailpipe' ) {
    ++        my $path = RT->Config->Get('SendmailPath');
    ++        my @args = shellwords(RT->Config->Get('SendmailArguments'));
    ++        push @args, "-t" unless grep {$_ eq "-t"} @args;
    + 
    +-    status code is a numeric value.
    ++        # SetOutgoingMailFrom and bounces conflict, since they both want -f
    ++        if ( $args{'Bounce'} ) {
    ++            push @args, shellwords(RT->Config->Get('SendmailBounceArguments'));
    ++        } elsif ( my $MailFrom = RT->Config->Get('SetOutgoingMailFrom') ) {
    ++            my $OutgoingMailAddress = $MailFrom =~ /\@/ ? $MailFrom : undef;
    ++            my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
    + 
    +-      for temporary failures, the status code should be -75
    ++            if ($TicketObj) {
    ++                my $Queue = $TicketObj->QueueObj;
    ++                my $QueueAddressOverride = $Overrides->{$Queue->id}
    ++                    || $Overrides->{$Queue->Name};
    + 
    +-      for permanent failures which are handled by RT, the status code
    +-      should be 0
    ++                if ($QueueAddressOverride) {
    ++                    $OutgoingMailAddress = $QueueAddressOverride;
    ++                } else {
    ++                    $OutgoingMailAddress ||= $Queue->CorrespondAddress
    ++                        || RT->Config->Get('CorrespondAddress');
    ++                }
    ++            }
    ++            elsif ($Overrides->{'Default'}) {
    ++                $OutgoingMailAddress = $Overrides->{'Default'};
    ++            }
    + 
    +-      for succces, the status code should be 1
    ++            push @args, "-f", $OutgoingMailAddress
    ++                if $OutgoingMailAddress;
    ++        }
    + 
    ++        # VERP
    ++        if ( $TransactionObj and
    ++             my $prefix = RT->Config->Get('VERPPrefix') and
    ++             my $domain = RT->Config->Get('VERPDomain') )
    ++        {
    ++            my $from = $TransactionObj->CreatorObj->EmailAddress;
    ++            $from =~ s/@/=/g;
    ++            $from =~ s/\s//g;
    ++            push @args, "-f", "$prefix$from\@$domain";
    ++        }
    + 
    ++        eval {
    ++            # don't ignore CHLD signal to get proper exit code
    ++            local $SIG{'CHLD'} = 'DEFAULT';
    + 
    +-=cut
    ++            # if something wrong with $mail->print we will get PIPE signal, handle it
    ++            local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
    + 
    +-sub _LoadPlugins {
    +-    my @mail_plugins = @_;
    ++            require IPC::Open2;
    ++            my ($mail, $stdout);
    ++            my $pid = IPC::Open2::open2( $stdout, $mail, $path, @args )
    ++                or die "couldn't execute program: $!";
      
     -    my @res;
     -    foreach my $plugin (@mail_plugins) {
    @@ -1705,25 +2055,37 @@
     -                unless $Class =~ /^RT::/;
     -            $Class->require or
     -                do { $RT::Logger->error("Couldn't load $Class: $@"); next };
    -+    my $TicketObj = $args{'Ticket'};
    -+    my $TransactionObj = $args{'Transaction'};
    ++            $args{'Entity'}->print($mail);
    ++            close $mail or die "close pipe failed: $!";
      
     -            no strict 'refs';
     -            unless ( defined *{ $Class . "::GetCurrentUser" }{CODE} ) {
     -                $RT::Logger->crit( "No GetCurrentUser code found in $Class module");
     -                next;
    --            }
    ++            waitpid($pid, 0);
    ++            if ($?) {
    ++                # sendmail exit statuses mostly errors with data not software
    ++                # TODO: status parsing: core dump, exit on signal or EX_*
    ++                my $msg = "$msgid: `$path @args` exited with code ". ($?>>8);
    ++                $msg = ", interrupted by signal ". ($?&127) if $?&127;
    ++                $RT::Logger->error( $msg );
    ++                die $msg;
    +             }
     -            push @res, $Class;
     -        } else {
     -            $RT::Logger->crit( "$plugin - is not class name or code reference");
    --        }
    -+    unless ( $args{'Entity'} ) {
    -+        $RT::Logger->crit( "Could not send mail without 'Entity' object" );
    -+        return 0;
    -     }
    ++        };
    ++        if ( $@ ) {
    ++            $RT::Logger->crit( "$msgid: Could not send mail with command `$path @args`: " . $@ );
    ++            if ( $TicketObj ) {
    ++                _RecordSendEmailFailure( $TicketObj );
    ++            }
    ++            return 0;
    +         }
    +-    }
     -    return @res;
     -}
    - 
    +-
     -sub Gateway {
     -    my $argsref = shift;
     -    my %args    = (
    @@ -1733,25 +2095,13 @@
     -        message => undef,
     -        %$argsref
     -    );
    -+    my $msgid = Encode::decode( "UTF-8", $args{'Entity'}->head->get('Message-ID') || '' );
    -+    chomp $msgid;
    -+    
    -+    # If we don't have any recipients to send to, don't send a message;
    -+    unless ( $args{'Entity'}->head->get('To')
    -+        || $args{'Entity'}->head->get('Cc')
    -+        || $args{'Entity'}->head->get('Bcc') )
    -+    {
    -+        $RT::Logger->info( $msgid . " No recipients found. Not sending." );
    -+        return -1;
    -+    }
    ++    } elsif ( $mail_command eq 'mbox' ) {
    ++        my $now = RT::Date->new(RT->SystemUser);
    ++        $now->SetToNow;
      
     -    my $SystemTicket;
     -    my $Right;
    -+    if ($args{'Entity'}->head->get('X-RT-Squelch')) {
    -+        $RT::Logger->info( $msgid . " Squelch header found. Not sending." );
    -+        return -1;
    -+    }
    - 
    +-
     -    # Validate the action
     -    my ( $status, @actions ) = IsCorrectAction( $args{'action'} );
     -    unless ($status) {
    @@ -1763,24 +2113,15 @@
     -                . $args{'queue'},
     -            undef
     -        );
    -+    if (my $precedence = RT->Config->Get('DefaultMailPrecedence')
    -+        and !$args{'Entity'}->head->get("Precedence")
    -+    ) {
    -+        $args{'Entity'}->head->replace( 'Precedence', Encode::encode("UTF-8",$precedence) );
    -     }
    - 
    +-    }
    +-
     -    my $parser = RT::EmailParser->new();
     -    $parser->SmartParseMIMEEntityFromScalar(
     -        Message => $args{'message'},
     -        Decode => 0,
     -        Exact => 1,
     -    );
    -+    if ( $TransactionObj && !$TicketObj
    -+        && $TransactionObj->ObjectType eq 'RT::Ticket' )
    -+    {
    -+        $TicketObj = $TransactionObj->Object;
    -+    }
    - 
    +-
     -    my $Message = $parser->Entity();
     -    unless ($Message) {
     -        MailError(
    @@ -1788,45 +2129,20 @@
     -            Explanation => "RT couldn't process the message below",
     -            Attach      => $args{'message'}
     -        );
    -+    my $head = $args{'Entity'}->head;
    -+    unless ( $head->get('Date') ) {
    -+        require RT::Date;
    -+        my $date = RT::Date->new( RT->SystemUser );
    -+        $date->SetToNow;
    -+        $head->replace( 'Date', Encode::encode("UTF-8",$date->RFC2822( Timezone => 'server' ) ) );
    -+    }
    -+    unless ( $head->get('MIME-Version') ) {
    -+        # We should never have to set the MIME-Version header
    -+        $head->replace( 'MIME-Version', '1.0' );
    -+    }
    -+    unless ( $head->get('Content-Transfer-Encoding') ) {
    -+        # fsck.com #5959: Since RT sends 8bit mail, we should say so.
    -+        $head->replace( 'Content-Transfer-Encoding', '8bit' );
    -+    }
    - 
    +-
     -        return ( 0,
     -            "Failed to parse this message. Something is likely badly wrong with the message"
    -+    if ( RT->Config->Get('Crypt')->{'Enable'} ) {
    -+        %args = WillSignEncrypt(
    -+            %args,
    -+            Attachment => $TransactionObj ? $TransactionObj->Attachments->First : undef,
    -+            Ticket     => $TicketObj,
    -         );
    -+        my $res = SignEncrypt( %args );
    -+        return $res unless $res > 0;
    -     }
    - 
    +-        );
    +-    }
    +-
     -    my @mail_plugins = grep $_, RT->Config->Get('MailPlugins');
     -    push @mail_plugins, "Auth::MailFrom" unless @mail_plugins;
     -    @mail_plugins = _LoadPlugins( @mail_plugins );
    -+    my $mail_command = RT->Config->Get('MailCommand');
    - 
    +-
     -    #Set up a queue object
     -    my $SystemQueueObj = RT::Queue->new( RT->SystemUser );
     -    $SystemQueueObj->Load( $args{'queue'} );
    -+    # if it is a sub routine, we just return it;
    -+    return $mail_command->($args{'Entity'}) if UNIVERSAL::isa( $mail_command, 'CODE' );
    - 
    +-
     -    my %skip_plugin;
     -    foreach my $class( grep !ref, @mail_plugins ) {
     -        # check if we should apply filter before decoding
    @@ -1841,78 +2157,13 @@
     -            Queue         => $SystemQueueObj,
     -            Actions       => \@actions,
     -        );
    -+    if ( $mail_command eq 'sendmailpipe' ) {
    -+        my $path = RT->Config->Get('SendmailPath');
    -+        my @args = shellwords(RT->Config->Get('SendmailArguments'));
    -+        push @args, "-t" unless grep {$_ eq "-t"} @args;
    -+
    -+        # SetOutgoingMailFrom and bounces conflict, since they both want -f
    -+        if ( $args{'Bounce'} ) {
    -+            push @args, shellwords(RT->Config->Get('SendmailBounceArguments'));
    -+        } elsif ( my $MailFrom = RT->Config->Get('SetOutgoingMailFrom') ) {
    -+            my $OutgoingMailAddress = $MailFrom =~ /\@/ ? $MailFrom : undef;
    -+            my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
    -+
    -+            if ($TicketObj) {
    -+                my $Queue = $TicketObj->QueueObj;
    -+                my $QueueAddressOverride = $Overrides->{$Queue->id}
    -+                    || $Overrides->{$Queue->Name};
    -+
    -+                if ($QueueAddressOverride) {
    -+                    $OutgoingMailAddress = $QueueAddressOverride;
    -+                } else {
    -+                    $OutgoingMailAddress ||= $Queue->CorrespondAddress
    -+                        || RT->Config->Get('CorrespondAddress');
    -+                }
    -+            }
    -+            elsif ($Overrides->{'Default'}) {
    -+                $OutgoingMailAddress = $Overrides->{'Default'};
    -+            }
    -+
    -+            push @args, "-f", $OutgoingMailAddress
    -+                if $OutgoingMailAddress;
    -+        }
    -+
    -+        # VERP
    -+        if ( $TransactionObj and
    -+             my $prefix = RT->Config->Get('VERPPrefix') and
    -+             my $domain = RT->Config->Get('VERPDomain') )
    -+        {
    -+            my $from = $TransactionObj->CreatorObj->EmailAddress;
    -+            $from =~ s/@/=/g;
    -+            $from =~ s/\s//g;
    -+            push @args, "-f", "$prefix$from\@$domain";
    -+        }
    - 
    +-
     -        $skip_plugin{ $class }++;
    -+        eval {
    -+            # don't ignore CHLD signal to get proper exit code
    -+            local $SIG{'CHLD'} = 'DEFAULT';
    - 
    +-
     -        my $Code = do {
     -            no strict 'refs';
     -            *{ $class . "::GetCurrentUser" }{CODE};
    -+            # if something wrong with $mail->print we will get PIPE signal, handle it
    -+            local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
    -+
    -+            require IPC::Open2;
    -+            my ($mail, $stdout);
    -+            my $pid = IPC::Open2::open2( $stdout, $mail, $path, @args )
    -+                or die "couldn't execute program: $!";
    -+
    -+            $args{'Entity'}->print($mail);
    -+            close $mail or die "close pipe failed: $!";
    -+
    -+            waitpid($pid, 0);
    -+            if ($?) {
    -+                # sendmail exit statuses mostly errors with data not software
    -+                # TODO: status parsing: core dump, exit on signal or EX_*
    -+                my $msg = "$msgid: `$path @args` exited with code ". ($?>>8);
    -+                $msg = ", interrupted by signal ". ($?&127) if $?&127;
    -+                $RT::Logger->error( $msg );
    -+                die $msg;
    -+            }
    -         };
    +-        };
     -        my ($status, $msg) = $Code->(
     -            Message       => $Message,
     -            RawMessageRef => \$args{'message'},
    @@ -1920,21 +2171,105 @@
     -            Actions       => \@actions,
     -        );
     -        next if $status > 0;
    -+        if ( $@ ) {
    -+            $RT::Logger->crit( "$msgid: Could not send mail with command `$path @args`: " . $@ );
    -+            if ( $TicketObj ) {
    -+                _RecordSendEmailFailure( $TicketObj );
    -+            }
    -+            return 0;
    -+        }
    -+    }
    -+    else {
    -+        local ($ENV{'MAILADDRESS'}, $ENV{'PERL_MAILERS'});
    - 
    +-
     -        if ( $status == -2 ) {
     -            return (1, $msg, undef);
     -        } elsif ( $status == -1 ) {
     -            return (0, $msg, undef);
    ++        state $logfile;
    ++        unless ($logfile) {
    ++            my $when = $now->ISO( Timezone => "server" );
    ++            $when =~ s/\s+/-/g;
    ++            $logfile = "$RT::VarPath/$when.mbox";
    ++            $RT::Logger->info("Storing outgoing emails in $logfile");
    +         }
    +-    }
    +-    @mail_plugins = grep !$skip_plugin{"$_"}, @mail_plugins;
    +-    $parser->_DecodeBodies;
    +-    $parser->RescueOutlook;
    +-    $parser->_PostProcessNewEntity;
    +-
    +-    my $head = $Message->head;
    +-    my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
    +-    my $Sender = (ParseSenderAddressFromHead( $head ))[0];
    +-    my $From = Encode::decode( "UTF-8", $head->get("From") );
    +-    chomp $From if defined $From;
    +-
    +-    my $MessageId = Encode::decode( "UTF-8", $head->get('Message-ID') )
    +-        || "<no-message-id-". time . rand(2000) .'@'. RT->Config->Get('Organization') .'>';
    +-
    +-    #Pull apart the subject line
    +-    my $Subject = Encode::decode( "UTF-8", $head->get('Subject') || '');
    +-    chomp $Subject;
    +-    
    +-    # Lets check for mail loops of various sorts.
    +-    my ($should_store_machine_generated_message, $IsALoop, $result);
    +-    ( $should_store_machine_generated_message, $ErrorsTo, $result, $IsALoop ) =
    +-      _HandleMachineGeneratedMail(
    +-        Message  => $Message,
    +-        ErrorsTo => $ErrorsTo,
    +-        Subject  => $Subject,
    +-        MessageId => $MessageId
    +-    );
    +-
    +-    # Do not pass loop messages to MailPlugins, to make sure the loop
    +-    # is broken, unless $RT::StoreLoops is set.
    +-    if ($IsALoop && !$should_store_machine_generated_message) {
    +-        return ( 0, $result, undef );
    +-    }
    +-    # }}}
    +-
    +-    $args{'ticket'} ||= ExtractTicketId( $Message );
    +-
    +-    # ExtractTicketId may have been overridden, and edited the Subject
    +-    my $NewSubject = Encode::decode( "UTF-8", $Message->head->get('Subject') );
    +-    chomp $NewSubject;
    + 
    +-    $SystemTicket = RT::Ticket->new( RT->SystemUser );
    +-    $SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
    +-    if ( $SystemTicket->id ) {
    +-        $Right = 'ReplyToTicket';
    ++        my $fh;
    ++        unless (open($fh, ">>", $logfile)) {
    ++            $RT::Logger->crit( "Can't open mbox file $logfile: $!" );
    ++            return 0;
    ++        }
    ++        my $content = $args{Entity}->stringify;
    ++        $content =~ s/^(>*From )/>$1/mg;
    ++        print $fh "From $ENV{USER}\@localhost  ".localtime."\n";
    ++        print $fh $content, "\n";
    ++        close $fh;
    +     } else {
    +-        $Right = 'CreateTicket';
    +-    }
    +-
    +-    # We can safely have no queue of we have a known-good ticket
    +-    unless ( $SystemTicket->id || $SystemQueueObj->id ) {
    +-        return ( -75, "RT couldn't find the queue: " . $args{'queue'}, undef );
    +-    }
    +-
    +-    my ($AuthStat, $CurrentUser, $error) = GetAuthenticationLevel(
    +-        MailPlugins   => \@mail_plugins,
    +-        Actions       => \@actions,
    +-        Message       => $Message,
    +-        RawMessageRef => \$args{message},
    +-        SystemTicket  => $SystemTicket,
    +-        SystemQueue   => $SystemQueueObj,
    +-    );
    +-
    +-    # If authentication fails and no new user was created, get out.
    +-    if ( !$CurrentUser || !$CurrentUser->id || $AuthStat == -1 ) {
    +-
    +-        # If the plugins refused to create one, they lose.
    +-        unless ( $AuthStat == -1 ) {
    +-            _NoAuthorizedUserFound(
    +-                Right     => $Right,
    +-                Message   => $Message,
    +-                Requestor => $ErrorsTo,
    +-                Queue     => $args{'queue'}
    +-            );
    ++        local ($ENV{'MAILADDRESS'}, $ENV{'PERL_MAILERS'});
    + 
     +        my @mailer_args = ($mail_command);
     +        if ( $mail_command eq 'sendmail' ) {
     +            $ENV{'PERL_MAILERS'} = RT->Config->Get('SendmailPath');
    @@ -1947,139 +2282,9 @@
     +            }
     +        } else {
     +            push @mailer_args, RT->Config->Get('MailParams');
    -+        }
    -+
    -+        unless ( $args{'Entity'}->send( @mailer_args ) ) {
    -+            $RT::Logger->crit( "$msgid: Could not send mail." );
    -+            if ( $TicketObj ) {
    -+                _RecordSendEmailFailure( $TicketObj );
    -+            }
    -+            return 0;
              }
    -     }
    --    @mail_plugins = grep !$skip_plugin{"$_"}, @mail_plugins;
    --    $parser->_DecodeBodies;
    --    $parser->RescueOutlook;
    --    $parser->_PostProcessNewEntity;
    -+    return 1;
    -+}
    - 
    --    my $head = $Message->head;
    --    my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
    --    my $Sender = (ParseSenderAddressFromHead( $head ))[0];
    --    my $From = Encode::decode( "UTF-8", $head->get("From") );
    --    chomp $From if defined $From;
    -+=head2 PrepareEmailUsingTemplate Template => '', Arguments => {}
    - 
    --    my $MessageId = Encode::decode( "UTF-8", $head->get('Message-ID') )
    --        || "<no-message-id-". time . rand(2000) .'@'. RT->Config->Get('Organization') .'>';
    -+Loads a template. Parses it using arguments if it's not empty.
    -+Returns a tuple (L<RT::Template> object, error message).
    - 
    --    #Pull apart the subject line
    --    my $Subject = Encode::decode( "UTF-8", $head->get('Subject') || '');
    --    chomp $Subject;
    --    
    --    # Lets check for mail loops of various sorts.
    --    my ($should_store_machine_generated_message, $IsALoop, $result);
    --    ( $should_store_machine_generated_message, $ErrorsTo, $result, $IsALoop ) =
    --      _HandleMachineGeneratedMail(
    --        Message  => $Message,
    --        ErrorsTo => $ErrorsTo,
    --        Subject  => $Subject,
    --        MessageId => $MessageId
    -+Note that even if a template object is returned MIMEObj method
    -+may return undef for empty templates.
    -+
    -+=cut
    -+
    -+sub PrepareEmailUsingTemplate {
    -+    my %args = (
    -+        Template => '',
    -+        Arguments => {},
    -+        @_
    -     );
    - 
    --    # Do not pass loop messages to MailPlugins, to make sure the loop
    --    # is broken, unless $RT::StoreLoops is set.
    --    if ($IsALoop && !$should_store_machine_generated_message) {
    --        return ( 0, $result, undef );
    -+    my $template = RT::Template->new( RT->SystemUser );
    -+    $template->LoadGlobalTemplate( $args{'Template'} );
    -+    unless ( $template->id ) {
    -+        return (undef, "Couldn't load template '". $args{'Template'} ."'");
    -     }
    --    # }}}
    -+    return $template if $template->IsEmpty;
    - 
    --    $args{'ticket'} ||= ExtractTicketId( $Message );
    -+    my ($status, $msg) = $template->Parse( %{ $args{'Arguments'} } );
    -+    return (undef, $msg) unless $status;
    - 
    --    # ExtractTicketId may have been overridden, and edited the Subject
    --    my $NewSubject = Encode::decode( "UTF-8", $Message->head->get('Subject') );
    --    chomp $NewSubject;
    -+    return $template;
    -+}
    - 
    --    $SystemTicket = RT::Ticket->new( RT->SystemUser );
    --    $SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
    --    if ( $SystemTicket->id ) {
    --        $Right = 'ReplyToTicket';
    --    } else {
    --        $Right = 'CreateTicket';
    --    }
    -+=head2 SendEmailUsingTemplate Template => '', Arguments => {}, From => CorrespondAddress, To => '', Cc => '', Bcc => ''
    - 
    --    # We can safely have no queue of we have a known-good ticket
    --    unless ( $SystemTicket->id || $SystemQueueObj->id ) {
    --        return ( -75, "RT couldn't find the queue: " . $args{'queue'}, undef );
    --    }
    -+Sends email using a template, takes name of template, arguments for it and recipients.
    - 
    --    my ($AuthStat, $CurrentUser, $error) = GetAuthenticationLevel(
    --        MailPlugins   => \@mail_plugins,
    --        Actions       => \@actions,
    --        Message       => $Message,
    --        RawMessageRef => \$args{message},
    --        SystemTicket  => $SystemTicket,
    --        SystemQueue   => $SystemQueueObj,
    --    );
    -+=cut
    - 
    --    # If authentication fails and no new user was created, get out.
    --    if ( !$CurrentUser || !$CurrentUser->id || $AuthStat == -1 ) {
    -+sub SendEmailUsingTemplate {
    -+    my %args = (
    -+        Template => '',
    -+        Arguments => {},
    -+        To => undef,
    -+        Cc => undef,
    -+        Bcc => undef,
    -+        From => RT->Config->Get('CorrespondAddress'),
    -+        InReplyTo => undef,
    -+        ExtraHeaders => {},
    -+        @_
    -+    );
    - 
    --        # If the plugins refused to create one, they lose.
    --        unless ( $AuthStat == -1 ) {
    --            _NoAuthorizedUserFound(
    --                Right     => $Right,
    --                Message   => $Message,
    --                Requestor => $ErrorsTo,
    --                Queue     => $args{'queue'}
    --            );
    -+    my ($template, $msg) = PrepareEmailUsingTemplate( %args );
    -+    return (0, $msg) unless $template;
    - 
    --        }
     -        return ( 0, "Could not load a valid user", undef );
    -+    my $mail = $template->MIMEObj;
    -+    unless ( $mail ) {
    -+        $RT::Logger->info("Message is not sent as template #". $template->id ." is empty");
    -+        return -1;
    -     }
    +-    }
      
     -    # If we got a user, but they don't have the right to say things
     -    if ( $AuthStat == 0 ) {
    @@ -2099,38 +2304,36 @@
     -            undef
     -        );
     -    }
    -+    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ $_ } ) )
    -+        foreach grep defined $args{$_}, qw(To Cc Bcc From);
    - 
    -+    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ExtraHeaders}{$_} ) )
    -+        foreach keys %{ $args{ExtraHeaders} };
    - 
    +-
    +-
     -    unless ($should_store_machine_generated_message) {
     -        return ( 0, $result, undef );
    --    }
    -+    SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
    ++        unless ( $args{'Entity'}->send( @mailer_args ) ) {
    ++            $RT::Logger->crit( "$msgid: Could not send mail." );
    ++            if ( $TicketObj ) {
    ++                _RecordSendEmailFailure( $TicketObj );
    ++            }
    ++            return 0;
    ++        }
    +     }
    ++    return 1;
    ++}
      
     -    $head->replace('X-RT-Interface' => 'Email');
    -+    return SendEmail( Entity => $mail );
    -+}
    - 
    +-
     -    # if plugin's updated SystemTicket then update arguments
     -    $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
    -+=head2 GetForwardFrom Ticket => undef, Transaction => undef
    - 
    +-
     -    my $Ticket = RT::Ticket->new($CurrentUser);
    -+Resolve the From field to use in forward mail
    - 
    +-
     -    if ( !$args{'ticket'} && grep /^(comment|correspond)$/, @actions )
     -    {
    -+=cut
    ++=head2 PrepareEmailUsingTemplate Template => '', Arguments => {}
      
     -        my @Cc;
     -        my @Requestors = ( $CurrentUser->id );
    -+sub GetForwardFrom {
    -+    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    -+    my $txn    = $args{Transaction};
    -+    my $ticket = $args{Ticket} || $txn->Object;
    ++Loads a template. Parses it using arguments if it's not empty.
    ++Returns a tuple (L<RT::Template> object, error message).
      
     -        if (RT->Config->Get('ParseNewMessageForTicketCcs')) {
     -            @Cc = ParseCcAddressesFromHead(
    @@ -2139,14 +2342,8 @@
     -                QueueObj    => $SystemQueueObj
     -            );
     -        }
    -+    if ( RT->Config->Get('ForwardFromUser') ) {
    -+        return ( $txn || $ticket )->CurrentUser->EmailAddress;
    -+    }
    -+    else {
    -+        return $ticket->QueueObj->CorrespondAddress
    -+          || RT->Config->Get('CorrespondAddress');
    -+    }
    -+}
    ++Note that even if a template object is returned MIMEObj method
    ++may return undef for empty templates.
      
     -        my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
     -            Queue     => $SystemQueueObj->Id,
    @@ -2164,16 +2361,26 @@
     -            );
     -            return ( 0, "Ticket creation From: $From failed: $ErrStr", $Ticket );
     -        }
    -+=head2 GetForwardAttachments Ticket => undef, Transaction => undef
    ++=cut
      
     -        # strip comments&corresponds from the actions we don't need
     -        # to record them if we've created the ticket just now
     -        @actions = grep !/^(comment|correspond)$/, @actions;
     -        $args{'ticket'} = $id;
    -+Resolve the Attachments to forward
    ++sub PrepareEmailUsingTemplate {
    ++    my %args = (
    ++        Template => '',
    ++        Arguments => {},
    ++        @_
    ++    );
      
     -    } elsif ( $args{'ticket'} ) {
    -+=cut
    ++    my $template = RT::Template->new( RT->SystemUser );
    ++    $template->LoadGlobalTemplate( $args{'Template'} );
    ++    unless ( $template->id ) {
    ++        return (undef, "Couldn't load template '". $args{'Template'} ."'");
    ++    }
    ++    return $template if $template->IsEmpty;
      
     -        $Ticket->Load( $args{'ticket'} );
     -        unless ( $Ticket->Id ) {
    @@ -2184,52 +2391,31 @@
     -                Explanation => $error,
     -                MIMEObj     => $Message
     -            );
    -+sub GetForwardAttachments {
    -+    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    -+    my $txn    = $args{Transaction};
    -+    my $ticket = $args{Ticket} || $txn->Object;
    ++    my ($status, $msg) = $template->Parse( %{ $args{'Arguments'} } );
    ++    return (undef, $msg) unless $status;
      
     -            return ( 0, $error );
     -        }
     -        $args{'ticket'} = $Ticket->id;
     -    } else {
     -        return ( 1, "Success", $Ticket );
    -+    my $attachments = RT::Attachments->new( $ticket->CurrentUser );
    -+    if ($txn) {
    -+        $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
    -+    }
    -+    else {
    -+        $attachments->LimitByTicket( $ticket->id );
    -+        $attachments->Limit(
    -+            ALIAS         => $attachments->TransactionAlias,
    -+            FIELD         => 'Type',
    -+            OPERATOR      => 'IN',
    -+            VALUE         => [ qw(Create Correspond) ],
    -+        );
    -     }
    -+    return $attachments;
    +-    }
    ++    return $template;
     +}
      
     -    # }}}
    -+sub WillSignEncrypt {
    -+    my %args = @_;
    -+    my $attachment = delete $args{Attachment};
    -+    my $ticket     = delete $args{Ticket};
    ++=head2 SendEmailUsingTemplate Template => '', Arguments => {}, From => CorrespondAddress, To => '', Cc => '', Bcc => ''
      
     -    my $unsafe_actions = RT->Config->Get('UnsafeEmailCommands');
     -    foreach my $action (@actions) {
    -+    if ( not RT->Config->Get('Crypt')->{'Enable'} ) {
    -+        $args{Sign} = $args{Encrypt} = 0;
    -+        return wantarray ? %args : 0;
    -+    }
    ++Sends email using a template, takes name of template, arguments for it and recipients.
      
     -        #   If the action is comment, add a comment.
     -        if ( $action =~ /^(?:comment|correspond)$/i ) {
     -            my $method = ucfirst lc $action;
     -            my ( $status, $msg ) = $Ticket->$method( MIMEObj => $Message );
     -            unless ($status) {
    -+    for my $argument ( qw(Sign Encrypt) ) {
    -+        next if defined $args{ $argument };
    ++=cut
      
     -                #Warn the sender that we couldn't actually submit the comment.
     -                MailError(
    @@ -2249,6 +2435,157 @@
     -                CurrentUser => $CurrentUser,
     -            );
     -            return ($status, $msg, $Ticket) unless $status == 1;
    +-        }
    ++sub SendEmailUsingTemplate {
    ++    my %args = (
    ++        Template => '',
    ++        Arguments => {},
    ++        To => undef,
    ++        Cc => undef,
    ++        Bcc => undef,
    ++        From => RT->Config->Get('CorrespondAddress'),
    ++        InReplyTo => undef,
    ++        ExtraHeaders => {},
    ++        @_
    ++    );
    ++
    ++    my ($template, $msg) = PrepareEmailUsingTemplate( %args );
    ++    return (0, $msg) unless $template;
    ++
    ++    my $mail = $template->MIMEObj;
    ++    unless ( $mail ) {
    ++        $RT::Logger->info("Message is not sent as template #". $template->id ." is empty");
    ++        return -1;
    +     }
    +-    return ( 1, "Success", $Ticket );
    ++
    ++    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ $_ } ) )
    ++        foreach grep defined $args{$_}, qw(To Cc Bcc From);
    ++
    ++    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ExtraHeaders}{$_} ) )
    ++        foreach keys %{ $args{ExtraHeaders} };
    ++
    ++    SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
    ++
    ++    return SendEmail( Entity => $mail );
    + }
    + 
    +-=head2 GetAuthenticationLevel
    ++=head2 GetForwardFrom Ticket => undef, Transaction => undef
    + 
    +-    # Authentication Level
    +-    # -1 - Get out.  this user has been explicitly declined
    +-    # 0 - User may not do anything (Not used at the moment)
    +-    # 1 - Normal user
    +-    # 2 - User is allowed to specify status updates etc. a la enhanced-mailgate
    ++Resolve the From field to use in forward mail
    + 
    + =cut
    + 
    +-sub GetAuthenticationLevel {
    +-    my %args = (
    +-        MailPlugins   => [],
    +-        Actions       => [],
    +-        Message       => undef,
    +-        RawMessageRef => undef,
    +-        SystemTicket  => undef,
    +-        SystemQueue   => undef,
    +-        @_,
    +-    );
    ++sub GetForwardFrom {
    ++    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    ++    my $txn    = $args{Transaction};
    ++    my $ticket = $args{Ticket} || $txn->Object;
    + 
    +-    my ( $CurrentUser, $AuthStat, $error );
    ++    if ( RT->Config->Get('ForwardFromUser') ) {
    ++        return ( $txn || $ticket )->CurrentUser->EmailAddress;
    ++    }
    ++    else {
    ++        return $ticket->QueueObj->CorrespondAddress
    ++          || RT->Config->Get('CorrespondAddress');
    ++    }
    ++}
    + 
    +-    # Initalize AuthStat so comparisons work correctly
    +-    $AuthStat = -9999999;
    ++=head2 GetForwardAttachments Ticket => undef, Transaction => undef
    + 
    +-    # if plugin returns AuthStat -2 we skip action
    +-    # NOTE: this is experimental API and it would be changed
    +-    my %skip_action = ();
    ++Resolve the Attachments to forward
    + 
    +-    # Since this needs loading, no matter what
    +-    foreach (@{ $args{MailPlugins} }) {
    +-        my ($Code, $NewAuthStat);
    +-        if ( ref($_) eq "CODE" ) {
    +-            $Code = $_;
    +-        } else {
    +-            no strict 'refs';
    +-            $Code = *{ $_ . "::GetCurrentUser" }{CODE};
    +-        }
    ++=cut
    + 
    +-        foreach my $action (@{ $args{Actions} }) {
    +-            ( $CurrentUser, $NewAuthStat ) = $Code->(
    +-                Message       => $args{Message},
    +-                RawMessageRef => $args{RawMessageRef},
    +-                CurrentUser   => $CurrentUser,
    +-                AuthLevel     => $AuthStat,
    +-                Action        => $action,
    +-                Ticket        => $args{SystemTicket},
    +-                Queue         => $args{SystemQueue},
    +-            );
    ++sub GetForwardAttachments {
    ++    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    ++    my $txn    = $args{Transaction};
    ++    my $ticket = $args{Ticket} || $txn->Object;
    + 
    +-# You get the highest level of authentication you were assigned, unless you get the magic -1
    +-# If a module returns a "-1" then we discard the ticket, so.
    +-            $AuthStat = $NewAuthStat
    +-                if ( $NewAuthStat > $AuthStat or $NewAuthStat == -1 or $NewAuthStat == -2 );
    ++    my $attachments = RT::Attachments->new( $ticket->CurrentUser );
    ++    if ($txn) {
    ++        $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
    ++    }
    ++    else {
    ++        $attachments->LimitByTicket( $ticket->id );
    ++        $attachments->Limit(
    ++            ALIAS         => $attachments->TransactionAlias,
    ++            FIELD         => 'Type',
    ++            OPERATOR      => 'IN',
    ++            VALUE         => [ qw(Create Correspond) ],
    ++        );
    ++    }
    ++    return $attachments;
    ++}
    + 
    +-            last if $AuthStat == -1;
    +-            $skip_action{$action}++ if $AuthStat == -2;
    +-        }
    + 
    +-        # strip actions we should skip
    +-        @{$args{Actions}} = grep !$skip_action{$_}, @{$args{Actions}}
    +-            if $AuthStat == -2;
    +-        last unless @{$args{Actions}};
    ++sub WillSignEncrypt {
    ++    my %args = @_;
    ++    my $attachment = delete $args{Attachment};
    ++    my $ticket     = delete $args{Ticket};
    + 
    +-        last if $AuthStat == -1;
    ++    if ( not RT->Config->Get('Crypt')->{'Enable'} ) {
    ++        $args{Sign} = $args{Encrypt} = 0;
    ++        return wantarray ? %args : 0;
    +     }
    + 
    +-    return $AuthStat if !wantarray;
    ++    for my $argument ( qw(Sign Encrypt) ) {
    ++        next if defined $args{ $argument };
    + 
    +-    return ($AuthStat, $CurrentUser, $error);
     +        if ( $attachment and defined $attachment->GetHeader("X-RT-$argument") ) {
     +            $args{$argument} = $attachment->GetHeader("X-RT-$argument");
     +        } elsif ( $ticket and $argument eq "Encrypt" ) {
    @@ -2259,21 +2596,15 @@
     +            # to 0 or 1; thus this path is only taken for messages
     +            # generated _not_ via the web UI.
     +            $args{Sign} = $ticket->QueueObj->SignAuto();
    -         }
    -     }
    --    return ( 1, "Success", $Ticket );
    ++        }
    ++    }
     +
     +    return wantarray ? %args : ($args{Sign} || $args{Encrypt});
      }
      
    --=head2 GetAuthenticationLevel
    +-sub _RunUnsafeAction {
     +=head2 SignEncrypt Entity => undef, Sign => 0, Encrypt => 0
    - 
    --    # Authentication Level
    --    # -1 - Get out.  this user has been explicitly declined
    --    # 0 - User may not do anything (Not used at the moment)
    --    # 1 - Normal user
    --    # 2 - User is allowed to specify status updates etc. a la enhanced-mailgate
    ++
     +Signs and encrypts message using L<RT::Crypt>, but as well handle errors
     +with users' keys.
     +
    @@ -2285,72 +2616,47 @@
     +
     +Returns 1 on success, 0 on error and -1 if all recipients are bad and
     +had been filtered out.
    - 
    - =cut
    - 
    --sub GetAuthenticationLevel {
    ++
    ++=cut
    ++
     +sub SignEncrypt {
          my %args = (
    --        MailPlugins   => [],
    --        Actions       => [],
    --        Message       => undef,
    --        RawMessageRef => undef,
    --        SystemTicket  => undef,
    --        SystemQueue   => undef,
    --        @_,
    +-        Action      => undef,
    +-        ErrorsTo    => undef,
    +-        Message     => undef,
    +-        Ticket      => undef,
    +-        CurrentUser => undef,
     +        Entity => undef,
     +        Sign => 0,
     +        Encrypt => 0,
    -+        @_
    +         @_
          );
     +    return 1 unless $args{'Sign'} || $args{'Encrypt'};
      
    --    my ( $CurrentUser, $AuthStat, $error );
    --
    --    # Initalize AuthStat so comparisons work correctly
    --    $AuthStat = -9999999;
    --
    --    # if plugin returns AuthStat -2 we skip action
    --    # NOTE: this is experimental API and it would be changed
    --    my %skip_action = ();
    +-    my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
     +    my $msgid = Encode::decode( "UTF-8", $args{'Entity'}->head->get('Message-ID') || '' );
     +    chomp $msgid;
      
    --    # Since this needs loading, no matter what
    --    foreach (@{ $args{MailPlugins} }) {
    --        my ($Code, $NewAuthStat);
    --        if ( ref($_) eq "CODE" ) {
    --            $Code = $_;
    --        } else {
    --            no strict 'refs';
    --            $Code = *{ $_ . "::GetCurrentUser" }{CODE};
    --        }
    +-    if ( $args{'Action'} =~ /^take$/i ) {
    +-        my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
    +-        unless ($status) {
    +-            MailError(
    +-                To          => $args{'ErrorsTo'},
    +-                Subject     => "Ticket not taken",
    +-                Explanation => $msg,
    +-                MIMEObj     => $args{'Message'}
    +-            );
    +-            return ( 0, "Ticket not taken, by email From: $From" );
     +    $RT::Logger->debug("$msgid Signing message") if $args{'Sign'};
     +    $RT::Logger->debug("$msgid Encrypting message") if $args{'Encrypt'};
    - 
    --        foreach my $action (@{ $args{Actions} }) {
    --            ( $CurrentUser, $NewAuthStat ) = $Code->(
    --                Message       => $args{Message},
    --                RawMessageRef => $args{RawMessageRef},
    --                CurrentUser   => $CurrentUser,
    --                AuthLevel     => $AuthStat,
    --                Action        => $action,
    --                Ticket        => $args{SystemTicket},
    --                Queue         => $args{SystemQueue},
    --            );
    ++
     +    my %res = RT::Crypt->SignEncrypt( %args );
     +    return 1 unless $res{'exit_code'};
    - 
    --# You get the highest level of authentication you were assigned, unless you get the magic -1
    --# If a module returns a "-1" then we discard the ticket, so.
    --            $AuthStat = $NewAuthStat
    --                if ( $NewAuthStat > $AuthStat or $NewAuthStat == -1 or $NewAuthStat == -2 );
    ++
     +    my @status = RT::Crypt->ParseStatus(
     +        Protocol => $res{'Protocol'}, Status => $res{'status'},
     +    );
    - 
    --            last if $AuthStat == -1;
    --            $skip_action{$action}++ if $AuthStat == -2;
    ++
     +    my @bad_recipients;
     +    foreach my $line ( @status ) {
     +        # if the passphrase fails, either you have a bad passphrase
    @@ -2361,26 +2667,30 @@
     +            $RT::Logger->error( "$line->{'Status'} PASSPHRASE: $line->{'Message'}" );
     +            return 0;
              }
    --
    --        # strip actions we should skip
    --        @{$args{Actions}} = grep !$skip_action{$_}, @{$args{Actions}}
    --            if $AuthStat == -2;
    --        last unless @{$args{Actions}};
    --
    --        last if $AuthStat == -1;
    +-    } elsif ( $args{'Action'} =~ /^resolve$/i ) {
    +-        my $new_status = $args{'Ticket'}->FirstInactiveStatus;
    +-        if ($new_status) {
    +-            my ( $status, $msg ) = $args{'Ticket'}->SetStatus($new_status);
    +-            unless ($status) {
     +        next unless ($line->{'Operation'}||'') eq 'RecipientsCheck';
     +        next if $line->{'Status'} eq 'DONE';
     +        $RT::Logger->error( $line->{'Message'} );
     +        push @bad_recipients, $line;
    -     }
    ++    }
     +    return 0 unless @bad_recipients;
      
    --    return $AuthStat if !wantarray;
    +-                #Warn the sender that we couldn't actually submit the comment.
    +-                MailError(
    +-                    To          => $args{'ErrorsTo'},
    +-                    Subject     => "Ticket not resolved",
    +-                    Explanation => $msg,
    +-                    MIMEObj     => $args{'Message'}
    +-                );
    +-                return ( 0, "Ticket not resolved, by email From: $From" );
    +-            }
     +    $_->{'AddressObj'} = (Email::Address->parse( $_->{'Recipient'} ))[0]
     +        foreach @bad_recipients;
    - 
    --    return ($AuthStat, $CurrentUser, $error);
    --}
    ++
     +    foreach my $recipient ( @bad_recipients ) {
     +        my $status = SendEmailUsingTemplate(
     +            To        => $recipient->{'AddressObj'}->address,
    @@ -2393,17 +2703,14 @@
     +        );
     +        unless ( $status ) {
     +            $RT::Logger->error("Couldn't send 'Error: public key'");
    -+        }
    -+    }
    - 
    --sub _RunUnsafeAction {
    --    my %args = (
    --        Action      => undef,
    --        ErrorsTo    => undef,
    --        Message     => undef,
    --        Ticket      => undef,
    --        CurrentUser => undef,
    --        @_
    +         }
    +-    } else {
    +-        return ( 0, "Not supported unsafe action $args{'Action'}, by email From: $From", $args{'Ticket'} );
    +     }
    +-    return ( 1, "Success" );
    +-}
    + 
    +-=head2 _NoAuthorizedUserFound
     +    my $status = SendEmailUsingTemplate(
     +        To        => RT->Config->Get('OwnerEmail'),
     +        Template  => 'Error to RT owner: public key',
    @@ -2412,64 +2719,32 @@
     +            TicketObj      => $args{'Ticket'},
     +            TransactionObj => $args{'Transaction'},
     +        },
    -     );
    ++    );
     +    unless ( $status ) {
     +        $RT::Logger->error("Couldn't send 'Error to RT owner: public key'");
     +    }
    - 
    --    my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
    --
    --    if ( $args{'Action'} =~ /^take$/i ) {
    --        my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
    --        unless ($status) {
    --            MailError(
    --                To          => $args{'ErrorsTo'},
    --                Subject     => "Ticket not taken",
    --                Explanation => $msg,
    --                MIMEObj     => $args{'Message'}
    --            );
    --            return ( 0, "Ticket not taken, by email From: $From" );
    --        }
    --    } elsif ( $args{'Action'} =~ /^resolve$/i ) {
    --        my $new_status = $args{'Ticket'}->FirstInactiveStatus;
    --        if ($new_status) {
    --            my ( $status, $msg ) = $args{'Ticket'}->SetStatus($new_status);
    --            unless ($status) {
    ++
     +    DeleteRecipientsFromHead(
     +        $args{'Entity'}->head,
     +        map $_->{'AddressObj'}->address, @bad_recipients
     +    );
    - 
    --                #Warn the sender that we couldn't actually submit the comment.
    --                MailError(
    --                    To          => $args{'ErrorsTo'},
    --                    Subject     => "Ticket not resolved",
    --                    Explanation => $msg,
    --                    MIMEObj     => $args{'Message'}
    --                );
    --                return ( 0, "Ticket not resolved, by email From: $From" );
    --            }
    --        }
    --    } else {
    --        return ( 0, "Not supported unsafe action $args{'Action'}, by email From: $From", $args{'Ticket'} );
    ++
     +    unless ( $args{'Entity'}->head->get('To')
     +          || $args{'Entity'}->head->get('Cc')
     +          || $args{'Entity'}->head->get('Bcc') )
     +    {
     +        $RT::Logger->debug("$msgid No recipients that have public key, not sending");
     +        return -1;
    -     }
    --    return ( 1, "Success" );
    --}
    --
    --=head2 _NoAuthorizedUserFound
    ++    }
      
     -Emails the RT Owner and the requestor when the auth plugins return "No auth user found"
    --
    --=cut
     +    # redo without broken recipients
     +    %res = RT::Crypt->SignEncrypt( %args );
     +    return 0 if $res{'exit_code'};
    + 
    +-=cut
    ++    return 1;
    ++}
      
     -sub _NoAuthorizedUserFound {
     -    my %args = (
    @@ -2479,8 +2754,6 @@
     -        Queue     => undef,
     -        @_
     -    );
    -+    return 1;
    -+}
      
     -    # Notify the RT Admin of the failure.
     -    MailError(
    @@ -2699,6 +2972,7 @@
     +        @references = $get_header->('In-Reply-To');
          }
     -    return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
    +-}
     +    push @references, @id, @rtid;
     +    if ( $args{'Ticket'} ) {
     +        my $pseudo_ref = PseudoReference( $args{'Ticket'} );
    @@ -2706,19 +2980,18 @@
     +    }
     +    splice @references, 4, -6
     +        if @references > 10;
    -+
    + 
    +-=head2 IsCorrectAction
     +    my $mail = $args{'Message'};
     +    $mail->head->replace( 'In-Reply-To' => Encode::encode( "UTF-8", join ' ', @rtid? (@rtid) : (@id)) ) if @id || @rtid;
     +    $mail->head->replace( 'References' => Encode::encode( "UTF-8", join ' ', @references) );
    - }
    - 
    --=head2 IsCorrectAction
    ++}
    + 
    +-Returns a list of valid actions we've found for this message
     +sub PseudoReference {
     +    my $ticket = shift;
     +    return '<RT-Ticket-'. $ticket->id .'@'. RT->Config->Get('Organization') .'>';
     +}
    - 
    --Returns a list of valid actions we've found for this message
      
     -=cut
     +sub AddSubjectTag {
    @@ -2753,5 +3026,9 @@
     +    return "[". ($queue_tag || RT->Config->Get('rtname')) ." #$id] $subject";
      }
      
    ++
    ++
      sub _RecordSendEmailFailure {
    +     my $ticket = shift;
    +     if ($ticket) {
     
 3:  2a14662 !  3:  3820f12 Adjust POD headers to reflect split
    @@ -14,8 +14,8 @@
     +
     +=head3 Gateway ARGSREF
      
    + 
      Takes parameters:
    - 
     @@
          return ( 1, "Success", $Ticket );
      }
    @@ -35,8 +35,8 @@
          # Authentication Level
          # -1 - Get out.  this user has been explicitly declined
     @@
    -     return ($AuthStat, $CurrentUser, $error);
    - }
    + }
    + 
      
     -=head2 _NoAuthorizedUserFound
     +=head3 _NoAuthorizedUserFound
    @@ -181,8 +181,8 @@
      Signs and encrypts message using L<RT::Crypt>, but as well handle errors
      with users' keys.
     @@
    -     return 1;
    - }
    + }
    + 
      
     -=head2 DeleteRecipientsFromHead HEAD RECIPIENTS
     +=head3 DeleteRecipientsFromHead HEAD RECIPIENTS
 4:  99624a2 =  4:  cdd7fc6 Update MailPlugins to only reference existant modules
 5:  00d0850 =  5:  28d4d8d Move MailPlugins documentation out of rt-mailgate, where is is mostly irrelevant
 6:  8400ff9 =  6:  4fb779c Remove duplicate ParseCcAddressesFromHead in RT::EmailParser
 7:  7b68589 =  7:  7cf851d Use Scope::Upper to allow returns from Gateway from nested subs
 8:  d358013 =  8:  6e71490 Add a base email plugin role, which provides TMPFAIL/FAILURE/SUCCESS
 9:  aee7869 =  9:  783cfae Check if each named mail plugin DOES the mail plugin role
10:  049f74d = 10:  7f449ab Return values of ApplyBeforeDecode plugins' GetCurrentUser are irrelevant
11:  cae6a89 = 11:  4f3ecec Push bounce short-circuiting down into _HandleMachineGeneratedMail
12:  afe6659 = 12:  12dd661 $IsALoop is now unused in Gateway
13:  dc1f8fe = 13:  0a620d9 Stop moving RT-Squelch-Replies-To aside
14:  03979a3 = 14:  e02dbd8 Simplify _HandleMachineGeneratedMail logic
15:  1a1d88f = 15:  dbe2a63 Use different method names rather than an ApplyBeforeDecode method
16:  202a69f = 16:  3f2ed6f Use FAILURE to abort from GetCurrentUser, rather than a magic -1 value
17:  19f7ec5 ! 17:  2d5fa31 Remove the unused $error variable
    @@ -22,5 +22,5 @@
     +    return ($AuthStat, $CurrentUser);
      }
      
    - =head3 _NoAuthorizedUserFound
    + 
     
18:  332f4d2 = 18:  0454c42 Move $Right to where it is used
19:  a418722 = 19:  d34399b Move SystemTicket definition to where it is first used
20:  8ae942f = 20:  771cfe1 Move NewSubject to where it is used
21:  24bf62a = 21:  6eed8e2 Remove CreateUser, merging to form a more featureful LoadOrCreateByEmail
22:  6dd0a29 = 22:  9bf127f Always create the user; this simplifies ACL checking greatly
23:  39d8b60 ! 23:  9c1e527 Split authentication from authorization
    @@ -189,7 +189,7 @@
     +    return $AuthStat;
      }
      
    - =head3 _NoAuthorizedUserFound
    + 
     
     diff --git a/lib/RT/Interface/Email/Auth/MailFrom.pm b/lib/RT/Interface/Email/Auth/MailFrom.pm
     --- a/lib/RT/Interface/Email/Auth/MailFrom.pm
24:  d6b673a ! 24:  26bf806 Remove no-longer-used _NoAuthorizedUserFound
    @@ -10,8 +10,8 @@
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
     @@
    -     return $AuthStat;
      }
    + 
      
     -=head3 _NoAuthorizedUserFound
     -
25:  7408373 = 25:  5db4d3b Remove now-unused $Right variable, previously used by _NoAuthorizedUserFound
26:  ddd1c03 ! 26:  05271ff Fail if the first action is unauthenticated
    @@ -129,7 +129,7 @@
     +    FAILURE( "You have no permission to $args{Action}" );
      }
      
    - =head3 ParseCcAddressesFromHead HASH
    + 
     
     diff --git a/lib/RT/Interface/Email/Auth/MailFrom.pm b/lib/RT/Interface/Email/Auth/MailFrom.pm
     --- a/lib/RT/Interface/Email/Auth/MailFrom.pm
27:  03d0af9 = 27:  019e5fa Notify the owner on common mis-configurations
28:  12115ef = 28:  efd311e Remove extra error in mail-gateway
29:  096152b = 29:  1269b22 Add back a warning that is now lacking
30:  3079bf8 = 30:  4a83780 Stop passing mail plugins around; load them lazily in one Plugins() method
31:  5ccf5a1 ! 31:  9edfe8c Split action handling into plugin classes
    @@ -245,8 +245,8 @@
              foreach my $plugin (@mail_plugins) {
                  if ( ref($plugin) eq "CODE" ) {
     @@
    -     FAILURE( "You have no permission to $args{Action}" );
      }
    + 
      
     -=head3 ParseCcAddressesFromHead HASH
     -
32: 2f24bb09 = 32:  442f33d Plugins may alter @actions; ensure action is valid prior to calling
33:  a6838f4 = 33:  e1d4d7e Split default authentication from default authorization
34:  4651652 = 34:  dcf1351 Local'ize MailErrors to avoid having to pass $ErrorsTo everywhere
35:  3539367 = 35:  1175342 Remove the warning about the deprecated Auth::GnuPG/Auth::SMIME plugins
36:  384caa6 = 36:  940073d Allow lazy adding of Auth::MailFrom if no other GetCurrentUser plugins exist
37:  b33b8a4 = 37:  5126611 There is no reason to not always enable Auth::Crypt
38:  59f1c3c = 38:  770b585 Make Crypt not an Auth:: plugin, but hardcoded
39:  9af3360 = 39:  43a1ac6 Move RejectOnUnencrypted to being a mail plugin
40:  65d32d4 = 40:  8da28bf Merge ParseAddressFromHeader and RT::EmailParser->ParseEmailAddress
41:  8c71c0f = 41:  4d174b7 Fix callsites of ParseSenderAddressFromHead to be slightly less incomprehensible
42:  b735284 = 42:  5172953 $MessageId is only used in IsMachineGeneratedMail; move it in there
43:  f71f257 ! 43:  1d1320b Merge CheckForSuspiciousSender, CheckForAutoGenerated, and CheckForBounce
    @@ -124,5 +124,5 @@
     +    return 0;
      }
      
    - =head2 ExtractTicketId
    + 
     
44:  b1ea355 ! 44:  2f047f2 Reduce repetition by making MailError handle throwing the FAILURE, as well
    @@ -38,7 +38,7 @@
     -    FAILURE( "You have no permission to $args{Action}" );
      }
      
    - sub HandleAction {
    + 
     @@
              MIMEObj     => undef,
              Attach      => undef,
45:  a008d0a = 45:  2a1a9b5 Move ACL checking for Take and Resolve into their own plugins
46:  140d5c8 ! 46:  1e19855 Update POD for new methods and functionality
    @@ -429,8 +429,8 @@
      =cut
      
     @@
    -     );
      }
    + 
      
     +=head3 HandleAction Action => C<action>, Message => C<message>, Ticket => C<ticket>, Queue => C<queue>
     +
    @@ -508,8 +508,8 @@
      =cut
      
     @@
    -     return 0;
      }
    + 
      
     -=head2 ExtractTicketId
     +=head3 ExtractTicketId
47:  ac37116 = 47:  044fe16 simple test for multiple reply-to addresses
48:  b78d166 = 48:  b4b94b3 take into account multiple sender's addresses
49:  4bf016c = 49:  c179f58 Email::Address 1.900+ has a fix of uninitialized warning



More information about the rt-commit mailing list