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

Alex Vandiver alexmv at bestpractical.com
Thu May 7 14:39:50 EDT 2015


The branch 4.4/multiple-reply-to was deleted and repushed:
       was 395e0ca730b634f50c8227576fa1620aafbb8eb2
       now b78d166c931ea0f39ccc5f5f7d4ff374dc81462f

 1:  15a0c3b !  1:  e0497a2 RT::Interface::Email doesn't need to be an Exporter
    @@ -8,8 +8,8 @@
     --- a/docs/UPGRADING-4.4
     +++ b/docs/UPGRADING-4.4
     @@
    - The support for C<jsmin> (via the C<$JSMinPath> configuration) has been
    - removed in favor of a built-in solution.
    + Estimated, and Time Left will no longer be copied.  This simplifies time
    + reporting.
      
     +=item *
     +
    @@ -23,8 +23,8 @@
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
     @@
    - use Mail::Mailer ();
    - use Text::ParseWords qw/shellwords/;
    + use RT::Util 'safe_run_child';
    + use File::Spec;
      
     -BEGIN {
     -    use base 'Exporter';
 2:  9b135a2 !  2:  e3399f0 Reorder functions to more obviously split sending and receiving mail
    @@ -9,9 +9,9 @@
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
     @@
    - use UNIVERSAL::require;
    - use Mail::Mailer ();
      use Text::ParseWords qw/shellwords/;
    + use RT::Util 'safe_run_child';
    + use File::Spec;
     +use MIME::Words ();
      
      =head1 NAME
    @@ -32,7 +32,7 @@
     -    my $head = shift;
     -
     -    # If this instance of RT sent it our, we don't want to take it in
    --    my $RTLoop = $head->get("X-RT-Loop-Prevention") || "";
    +-    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;
    @@ -101,14 +101,18 @@
      
     -=head2 CheckForAutoGenerated HEAD
      
    --Takes a HEAD object of L<MIME::Head> class and returns true if message
    --is autogenerated. Checks 'Precedence' and 'X-FC-Machinegenerated'
    --fields of the head in tests.
    +-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 CheckForAutoGenerated {
     -    my $head = shift;
    +-
    +-    if (grep { /^(bulk|junk)/i } $head->get_all("Precedence")) {
    +-        return (1);
    +-    }
     +sub Gateway {
     +    my $argsref = shift;
     +    my %args    = (
    @@ -119,17 +123,18 @@
     +        %$argsref
     +    );
      
    --    my $Precedence = $head->get("Precedence") || "";
    --    if ( $Precedence =~ /^(bulk|junk)/i ) {
    --        return (1);
    --    }
    -+    my $SystemTicket;
    -+    my $Right;
    - 
     -    # 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);
    +-    }
    ++    my $SystemTicket;
    ++    my $Right;
    + 
    +-    # First Class mailer uses this as a clue.
    +-    my $FCJunk = $head->get("X-FC-Machinegenerated") || "";
    +-    if ( $FCJunk =~ /^true/i ) {
     -        return (1);
     +    # Validate the action
     +    my ( $status, @actions ) = IsCorrectAction( $args{'action'} );
    @@ -144,17 +149,18 @@
     +        );
          }
      
    --    # First Class mailer uses this as a clue.
    --    my $FCJunk = $head->get("X-FC-Machinegenerated") || "";
    --    if ( $FCJunk =~ /^true/i ) {
    --        return (1);
    +-    return (0);
    +-}
    +-
     +    my $parser = RT::EmailParser->new();
     +    $parser->SmartParseMIMEEntityFromScalar(
     +        Message => $args{'message'},
     +        Decode => 0,
     +        Exact => 1,
     +    );
    -+
    + 
    +-sub CheckForBounce {
    +-    my $head = shift;
     +    my $Message = $parser->Entity();
     +    unless ($Message) {
     +        MailError(
    @@ -162,24 +168,25 @@
     +            Explanation => "RT couldn't process the message below",
     +            Attach      => $args{'message'}
     +        );
    -+
    + 
    +-    my $ReturnPath = $head->get("Return-path") || "";
    +-    return ( $ReturnPath =~ /<>/ );
    +-}
     +        return ( 0,
     +            "Failed to parse this message. Something is likely badly wrong with the message"
     +        );
    -     }
    - 
    --    return (0);
    --}
    ++    }
    + 
     +    my @mail_plugins = grep $_, RT->Config->Get('MailPlugins');
     +    push @mail_plugins, "Auth::MailFrom" unless @mail_plugins;
     +    @mail_plugins = _LoadPlugins( @mail_plugins );
      
    +-=head2 MailError PARAM HASH
     +    #Set up a queue object
     +    my $SystemQueueObj = RT::Queue->new( RT->SystemUser );
     +    $SystemQueueObj->Load( $args{'queue'} );
      
    --sub CheckForBounce {
    --    my $head = shift;
    +-Sends an error message. Takes a param hash:
     +    my %skip_plugin;
     +    foreach my $class( grep !ref, @mail_plugins ) {
     +        # check if we should apply filter before decoding
    @@ -195,11 +202,10 @@
     +            Actions       => \@actions,
     +        );
      
    --    my $ReturnPath = $head->get("Return-path") || "";
    --    return ( $ReturnPath =~ /<>/ );
    --}
    +-=over 4
     +        $skip_plugin{ $class }++;
      
    +-=item From - sender's address, by default is 'CorrespondAddress';
     +        my $Code = do {
     +            no strict 'refs';
     +            *{ $class . "::GetCurrentUser" }{CODE};
    @@ -212,7 +218,7 @@
     +        );
     +        next if $status > 0;
      
    --=head2 MailError PARAM HASH
    +-=item To - recipient, by default is 'OwnerEmail';
     +        if ( $status == -2 ) {
     +            return (1, $msg, undef);
     +        } elsif ( $status == -1 ) {
    @@ -224,20 +230,20 @@
     +    $parser->RescueOutlook;
     +    $parser->_PostProcessNewEntity;
      
    --Sends an error message. Takes a param hash:
    +-=item Bcc - optional Bcc recipients;
     +    my $head = $Message->head;
     +    my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
     +    my $Sender = (ParseSenderAddressFromHead( $head ))[0];
    -+    my $From = $head->get("From");
    ++    my $From = Encode::decode( "UTF-8", $head->get("From") );
     +    chomp $From if defined $From;
      
    --=over 4
    -+    my $MessageId = $head->get('Message-ID')
    +-=item Subject - subject of the message, default is 'There has been an error';
    ++    my $MessageId = Encode::decode( "UTF-8", $head->get('Message-ID') )
     +        || "<no-message-id-". time . rand(2000) .'@'. RT->Config->Get('Organization') .'>';
      
    --=item From - sender's address, by default is 'CorrespondAddress';
    +-=item Explanation - main content of the error, default value is 'Unexplained error';
     +    #Pull apart the subject line
    -+    my $Subject = $head->get('Subject') || '';
    ++    my $Subject = Encode::decode( "UTF-8", $head->get('Subject') || '');
     +    chomp $Subject;
     +    
     +    # Lets check for mail loops of various sorts.
    @@ -250,7 +256,8 @@
     +        MessageId => $MessageId
     +    );
      
    --=item To - recipient, by default is 'OwnerEmail';
    +-=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.
     +    # 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) {
    @@ -258,15 +265,16 @@
     +    }
     +    # }}}
      
    --=item Bcc - optional Bcc recipients;
    +-=item Attach - optional text that attached to the error as 'message/rfc822' part.
     +    $args{'ticket'} ||= ExtractTicketId( $Message );
      
    --=item Subject - subject of the message, default is 'There has been an error';
    +-=item LogLevel - log level under which we should write the subject and
    +-explanation message into the log, by default we log it as critical.
     +    # ExtractTicketId may have been overridden, and edited the Subject
    -+    my $NewSubject = $Message->head->get('Subject');
    ++    my $NewSubject = Encode::decode( "UTF-8", $Message->head->get('Subject') );
     +    chomp $NewSubject;
      
    --=item Explanation - main content of the error, default value is 'Unexplained error';
    +-=back
     +    $SystemTicket = RT::Ticket->new( RT->SystemUser );
     +    $SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
     +    if ( $SystemTicket->id ) {
    @@ -275,41 +283,10 @@
     +        $Right = 'CreateTicket';
     +    }
      
    --=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.
    +-=cut
     +    # 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 );
    -+    }
    - 
    --=item Attach - optional text that attached to the error as 'message/rfc822' part.
    -+    my ($AuthStat, $CurrentUser, $error) = GetAuthenticationLevel(
    -+        MailPlugins   => \@mail_plugins,
    -+        Actions       => \@actions,
    -+        Message       => $Message,
    -+        RawMessageRef => \$args{message},
    -+        SystemTicket  => $SystemTicket,
    -+        SystemQueue   => $SystemQueueObj,
    -+    );
    - 
    --=item LogLevel - log level under which we should write the subject and
    --explanation message into the log, by default we log it as critical.
    -+    # If authentication fails and no new user was created, get out.
    -+    if ( !$CurrentUser || !$CurrentUser->id || $AuthStat == -1 ) {
    - 
    --=back
    -+        # If the plugins refused to create one, they lose.
    -+        unless ( $AuthStat == -1 ) {
    -+            _NoAuthorizedUserFound(
    -+                Right     => $Right,
    -+                Message   => $Message,
    -+                Requestor => $ErrorsTo,
    -+                Queue     => $args{'queue'}
    -+            );
    - 
    --=cut
    -+        }
    -+        return ( 0, "Could not load a valid user", undef );
     +    }
      
     -sub MailError {
    @@ -323,12 +300,50 @@
     -        Attach      => undef,
     -        LogLevel    => 'crit',
     -        @_
    --    );
    --
    ++    my ($AuthStat, $CurrentUser, $error) = GetAuthenticationLevel(
    ++        MailPlugins   => \@mail_plugins,
    ++        Actions       => \@actions,
    ++        Message       => $Message,
    ++        RawMessageRef => \$args{message},
    ++        SystemTicket  => $SystemTicket,
    ++        SystemQueue   => $SystemQueueObj,
    +     );
    + 
     -    $RT::Logger->log(
     -        level   => $args{'LogLevel'},
     -        message => "$args{Subject}: $args{'Explanation'}",
     -    ) if $args{'LogLevel'};
    ++    # If authentication fails and no new user was created, get out.
    ++    if ( !$CurrentUser || !$CurrentUser->id || $AuthStat == -1 ) {
    + 
    +-    # the colons are necessary to make ->build include non-standard headers
    +-    my %entity_args = (
    +-        Type                    => "multipart/mixed",
    +-        From                    => Encode::encode( "UTF-8", $args{'From'} ),
    +-        Bcc                     => Encode::encode( "UTF-8", $args{'Bcc'} ),
    +-        To                      => Encode::encode( "UTF-8", $args{'To'} ),
    +-        Subject                 => EncodeToMIME( String => $args{'Subject'} ),
    +-        'X-RT-Loop-Prevention:' => Encode::encode( "UTF-8", RT->Config->Get('rtname') ),
    +-    );
    ++        # If the plugins refused to create one, they lose.
    ++        unless ( $AuthStat == -1 ) {
    ++            _NoAuthorizedUserFound(
    ++                Right     => $Right,
    ++                Message   => $Message,
    ++                Requestor => $ErrorsTo,
    ++                Queue     => $args{'queue'}
    ++            );
    + 
    +-    # 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 ( 0, "Could not load a valid user", undef );
    +     }
    + 
    +-    my $entity = MIME::Entity->build(%entity_args);
    +-    SetInReplyTo( Message => $entity, InReplyTo => $args{'MIMEObj'} );
     +    # If we got a user, but they don't have the right to say things
     +    if ( $AuthStat == 0 ) {
     +        MailError(
    @@ -348,47 +363,42 @@
     +        );
     +    }
      
    --    # the colons are necessary to make ->build include non-standard headers
    --    my %entity_args = (
    --        Type                    => "multipart/mixed",
    --        From                    => $args{'From'},
    --        Bcc                     => $args{'Bcc'},
    --        To                      => $args{'To'},
    --        Subject                 => $args{'Subject'},
    --        'X-RT-Loop-Prevention:' => RT->Config->Get('rtname'),
    +-    $entity->attach(
    +-        Type    => "text/plain",
    +-        Charset => "UTF-8",
    +-        Data    => Encode::encode( "UTF-8", $args{'Explanation'} . "\n" ),
     -    );
      
    --    # only set precedence if the sysadmin wants us to
    --    if (defined(RT->Config->Get('DefaultErrorMailPrecedence'))) {
    --        $entity_args{'Precedence:'} = RT->Config->Get('DefaultErrorMailPrecedence');
    +-    if ( $args{'MIMEObj'} ) {
    +-        $args{'MIMEObj'}->sync_headers;
    +-        $entity->add_part( $args{'MIMEObj'} );
     +    unless ($should_store_machine_generated_message) {
     +        return ( 0, $result, undef );
          }
      
    --    my $entity = MIME::Entity->build(%entity_args);
    --    SetInReplyTo( Message => $entity, InReplyTo => $args{'MIMEObj'} );
    --
    --    $entity->attach( Data => $args{'Explanation'} . "\n" );
    +-    if ( $args{'Attach'} ) {
    +-        $entity->attach( Data => Encode::encode( "UTF-8", $args{'Attach'} ), Type => 'message/rfc822' );
    ++    $head->replace('X-RT-Interface' => 'Email');
    + 
    +-    }
     +    # if plugin's updated SystemTicket then update arguments
     +    $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
      
    --    if ( $args{'MIMEObj'} ) {
    --        $args{'MIMEObj'}->sync_headers;
    --        $entity->add_part( $args{'MIMEObj'} );
    --    }
    +-    SendEmail( Entity => $entity, Bounce => 1 );
    +-}
     +    my $Ticket = RT::Ticket->new($CurrentUser);
      
    --    if ( $args{'Attach'} ) {
    --        $entity->attach( Data => $args{'Attach'}, Type => 'message/rfc822' );
     +    if ( !$args{'ticket'} && grep /^(comment|correspond)$/, @actions )
     +    {
      
    --    }
    +-=head2 SendEmail Entity => undef, [ Bounce => 0, Ticket => undef, Transaction => undef ]
     +        my @Cc;
     +        my @Requestors = ( $CurrentUser->id );
      
    --    SendEmail( Entity => $entity, Bounce => 1 );
    --}
    +-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.
     +        if (RT->Config->Get('ParseNewMessageForTicketCcs')) {
     +            @Cc = ParseCcAddressesFromHead(
     +                Head        => $head,
    @@ -397,9 +407,8 @@
     +            );
     +        }
      
    -+        $head->replace('X-RT-Interface' => 'Email');
    - 
    --=head2 SendEmail Entity => undef, [ Bounce => 0, Ticket => undef, Transaction => undef ]
    +-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.
     +        my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
     +            Queue     => $SystemQueueObj->Id,
     +            Subject   => $NewSubject,
    @@ -417,22 +426,19 @@
     +            return ( 0, "Ticket creation From: $From failed: $ErrStr", $Ticket );
     +        }
      
    --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.
    +-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.
     +        # 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;
      
    --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.
    +-Returns 1 on success, 0 on error or -1 if message has no recipients
    +-and hasn't been sent.
     +    } elsif ( $args{'ticket'} ) {
      
    --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.
    +-=head3 Signing and Encrypting
     +        $Ticket->Load( $args{'ticket'} );
     +        unless ( $Ticket->Id ) {
     +            my $error = "Could not find a ticket with id " . $args{'ticket'};
    @@ -443,8 +449,11 @@
     +                MIMEObj     => $Message
     +            );
      
    --Returns 1 on success, 0 on error or -1 if message has no recipients
    --and hasn't been sent.
    +-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.
     +            return ( 0, $error );
     +        }
     +        $args{'ticket'} = $Ticket->id;
    @@ -452,26 +461,23 @@
     +        return ( 1, "Success", $Ticket );
     +    }
      
    --=head3 Signing and Encrypting
    +-The following precedence of arguments are used to figure out if
    +-the message should be encrypted and/or signed:
     +    # }}}
      
    --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.
    +-* if Sign or Encrypt argument is defined then its value is used
     +    my $unsafe_actions = RT->Config->Get('UnsafeEmailCommands');
     +    foreach my $action (@actions) {
      
    --The following precedence of arguments are used to figure out if
    --the message should be encrypted and/or signed:
    +-* else if Transaction's first attachment has X-RT-Sign or X-RT-Encrypt
    +-header field then it's value is used
     +        #   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) {
      
    --* if Sign or Encrypt argument is defined then its value is used
    +-* else properties of a queue of the Ticket are used.
     +                #Warn the sender that we couldn't actually submit the comment.
     +                MailError(
     +                    To          => $ErrorsTo,
    @@ -495,23 +501,20 @@
     +    return ( 1, "Success", $Ticket );
     +}
      
    --* else if Transaction's first attachment has X-RT-Sign or X-RT-Encrypt
    --header field then it's value is used
    +-=cut
     +=head2 IsCorrectAction
    - 
    --* else properties of a queue of the Ticket are used.
    -+Returns a list of valid actions we've found for this message
    - 
    - =cut
      
     -sub WillSignEncrypt {
     -    my %args = @_;
     -    my $attachment = delete $args{Attachment};
     -    my $ticket     = delete $args{Ticket};
    --
    ++Returns a list of valid actions we've found for this message
    + 
     -    if ( not RT->Config->Get('Crypt')->{'Enable'} ) {
     -        $args{Sign} = $args{Encrypt} = 0;
     -        return wantarray ? %args : 0;
    ++=cut
    ++
     +sub IsCorrectAction {
     +    my $action = shift;
     +    my @actions = grep $_, split /-/, $action;
    @@ -601,7 +604,7 @@
     +    # Initalize AuthStat so comparisons work correctly
     +    $AuthStat = -9999999;
      
    --    my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
    +-    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;
    @@ -621,7 +624,7 @@
     -    if (my $precedence = RT->Config->Get('DefaultMailPrecedence')
     -        and !$args{'Entity'}->head->get("Precedence")
     -    ) {
    --        $args{'Entity'}->head->set( 'Precedence', $precedence );
    +-        $args{'Entity'}->head->replace( 'Precedence', Encode::encode("UTF-8",$precedence) );
     -    }
     -
     -    if ( $TransactionObj && !$TicketObj
    @@ -635,15 +638,15 @@
     -        require RT::Date;
     -        my $date = RT::Date->new( RT->SystemUser );
     -        $date->SetToNow;
    --        $head->set( 'Date', $date->RFC2822( Timezone => 'server' ) );
    +-        $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->set( 'MIME-Version', '1.0' );
    +-        $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->set( 'Content-Transfer-Encoding', '8bit' );
    +-        $head->replace( 'Content-Transfer-Encoding', '8bit' );
     -    }
     -
     -    if ( RT->Config->Get('Crypt')->{'Enable'} ) {
    @@ -674,14 +677,15 @@
     -            my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
     -
     -            if ($TicketObj) {
    --                my $QueueName = $TicketObj->QueueObj->Name;
    --                my $QueueAddressOverride = $Overrides->{$QueueName};
    +-                my $Queue = $TicketObj->QueueObj;
    +-                my $QueueAddressOverride = $Overrides->{$Queue->id}
    +-                    || $Overrides->{$Queue->Name};
     -
     -                if ($QueueAddressOverride) {
     -                    $OutgoingMailAddress = $QueueAddressOverride;
     -                } else {
    --                    $OutgoingMailAddress ||= $TicketObj->QueueObj->CorrespondAddress
    --                                             || RT->Config->Get('CorrespondAddress');
    +-                    $OutgoingMailAddress ||= $Queue->CorrespondAddress
    +-                        || RT->Config->Get('CorrespondAddress');
     -                }
     -            }
     -            elsif ($Overrides->{'Default'}) {
    @@ -767,7 +771,9 @@
     -                _RecordSendEmailFailure( $TicketObj );
     -            }
     -            return 0;
    --        }
    ++            no strict 'refs';
    ++            $Code = *{ $_ . "::GetCurrentUser" }{CODE};
    +         }
     -    }
     -    return 1;
     -}
    @@ -776,7 +782,7 @@
     -
     -Loads a template. Parses it using arguments if it's not empty.
     -Returns a tuple (L<RT::Template> object, error message).
    --
    + 
     -Note that even if a template object is returned MIMEObj method
     -may return undef for empty templates.
     -
    @@ -830,10 +836,10 @@
     -        return -1;
     -    }
     -
    --    $mail->head->set( $_ => Encode::encode_utf8( $args{ $_ } ) )
    +-    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ $_ } ) )
     -        foreach grep defined $args{$_}, qw(To Cc Bcc From);
     -
    --    $mail->head->set( $_ => $args{ExtraHeaders}{$_} )
    +-    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ExtraHeaders}{$_} ) )
     -        foreach keys %{ $args{ExtraHeaders} };
     -
     -    SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
    @@ -877,17 +883,13 @@
     -        $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
     -    }
     -    else {
    --        my $txns = $ticket->Transactions;
    --        $txns->Limit(
    --            FIELD => 'Type',
    --            VALUE => $_,
    --        ) for qw(Create Correspond);
    --
    --        while ( my $txn = $txns->Next ) {
    --            $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
    -+            no strict 'refs';
    -+            $Code = *{ $_ . "::GetCurrentUser" }{CODE};
    -         }
    +-        $attachments->LimitByTicket( $ticket->id );
    +-        $attachments->Limit(
    +-            ALIAS         => $attachments->TransactionAlias,
    +-            FIELD         => 'Type',
    +-            OPERATOR      => 'IN',
    +-            VALUE         => [ qw(Create Correspond) ],
    +-        );
     -    }
     -    return $attachments;
     -}
    @@ -908,7 +910,7 @@
     -had been filtered out.
     -
     -=cut
    - 
    +-
     -sub SignEncrypt {
     -    my %args = (
     -        Entity => undef,
    @@ -918,7 +920,7 @@
     -    );
     -    return 1 unless $args{'Sign'} || $args{'Encrypt'};
     -
    --    my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
    +-    my $msgid = Encode::decode( "UTF-8", $args{'Entity'}->head->get('Message-ID') || '' );
     -    chomp $msgid;
     -
     -    $RT::Logger->debug("$msgid Signing message") if $args{'Sign'};
    @@ -1073,13 +1075,8 @@
     -    $max = int( $max / 3 ) * 3;
     -
     -    chomp $value;
    --
    + 
     -    if ( $max <= 0 ) {
    - 
    --        # gives an error...
    --        $RT::Logger->crit("Can't encode! Charset or encoding too big.");
    --        return ($value);
    --    }
     +    # Notify the RT Admin of the failure.
     +    MailError(
     +        To          => RT->Config->Get('OwnerEmail'),
    @@ -1088,18 +1085,20 @@
     +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}]}).
      
    --    return ($value) if $value =~ /^(?:[\t\x20-\x7e]|\x0D*\x0A[ \t])+$/s;
    +-        # 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'}]}.
      
    --    $value =~ s/\s+$//;
    +-    return ($value) if $value =~ /^(?:[\t\x20-\x7e]|\x0D*\x0A[ \t])+$/s;
     +EOT
     +        MIMEObj  => $args{'Message'},
     +        LogLevel => 'error'
     +    );
      
    --    # we need perl string to split thing char by char
    --    Encode::_utf8_on($value) unless Encode::is_utf8($value);
    +-    $value =~ s/\s+$//;
     +    # Also notify the requestor that his request has been dropped.
     +    if ($args{'Requestor'} ne RT->Config->Get('OwnerEmail')) {
     +    MailError(
    @@ -1142,10 +1141,21 @@
      
      Takes a hash containing QueueObj, Head and CurrentUser objects.
     @@
    - 
    - =cut
    - 
    --sub ParseErrorsToAddressFromHead {
    +         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;
     +
    @@ -1154,7 +1164,7 @@
     +    foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
     +
     +        # If there's a header of that name
    -+        my $headerobj = $head->get($header);
    ++        my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
     +        if ($headerobj) {
     +            my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
     +
    @@ -1261,7 +1271,7 @@
     +        # 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',    $Sender );
    ++        $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
     +        $head->replace( 'RT-DetectedAutoGenerated', 'true' );
     +    }
     +    return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
    @@ -1276,66 +1286,76 @@
     +=cut
     +
     +sub CheckForLoops {
    -     my $head = shift;
    - 
    --    #Figure out who's sending this message.
    ++    my $head = shift;
    ++
     +    # If this instance of RT sent it our, we don't want to take it in
    -+    my $RTLoop = $head->get("X-RT-Loop-Prevention") || "";
    ++    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;
     +    }
    - 
    --    foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
    ++
     +    # 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 = $head->get($header);
    +-        my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
     -        if ($headerobj) {
     -            my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
    -+=head2 CheckForSuspiciousSender HEAD
    - 
    +-
     -            # If it's got actual useful content...
     -            return ($addr) if ($addr);
     -        }
     -    }
     -}
    -+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 CheckForSuspiciousSender {
    -+    my $head = shift;
    +-
    +-
    ++    #if it's from a postmaster or mailer daemon, it's likely a bounce.
      
     -=head2 ParseAddressFromHeader ADDRESS
    -+    #if it's from a postmaster or mailer daemon, it's likely a bounce.
    - 
    --Takes an address from C<$head->get('Line')> and returns a tuple: user at host, friendly name
     +    #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
    -+    #TODO: search through the whole email and find the right Ticket ID.
    ++    my ( $From, $junk ) = ParseSenderAddressFromHead($head);
      
     -sub ParseAddressFromHeader {
     -    my $Addr = shift;
    -+    my ( $From, $junk ) = ParseSenderAddressFromHead($head);
    ++    # 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 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 "" ))
    @@ -1356,9 +1376,9 @@
      
     -Gets a head object and list of addresses.
     -Deletes addresses from To, Cc or Bcc fields.
    -+Takes a HEAD object of L<MIME::Head> class and returns true if message
    -+is autogenerated. Checks 'Precedence' and 'X-FC-Machinegenerated'
    -+fields of the head in tests.
    ++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
      
    @@ -1368,12 +1388,11 @@
     -    my %skip = map { lc $_ => 1 } @_;
      
     -    foreach my $field ( qw(To Cc Bcc) ) {
    --        $head->set( $field =>
    +-        $head->replace( $field => Encode::encode( "UTF-8",
     -            join ', ', map $_->format, grep !$skip{ lc $_->address },
    --                Email::Address->parse( $head->get( $field ) )
    +-                Email::Address->parse( Encode::decode( "UTF-8", $head->get( $field ) ) ) )
     -        );
    -+    my $Precedence = $head->get("Precedence") || "";
    -+    if ( $Precedence =~ /^(bulk|junk)/i ) {
    ++    if (grep { /^(bulk|junk)/i } $head->get_all("Precedence")) {
     +        return (1);
          }
     -}
    @@ -1406,7 +1425,7 @@
     -    my $get_header = sub {
     -        my @res;
     -        if ( $args{'InReplyTo'}->isa('MIME::Entity') ) {
    --            @res = $args{'InReplyTo'}->head->get( shift );
    +-            @res = map {Encode::decode("UTF-8", $_)} $args{'InReplyTo'}->head->get( shift );
     -        } else {
     -            @res = $args{'InReplyTo'}->GetHeader( shift ) || '';
     -        }
    @@ -1439,14 +1458,15 @@
     -        if @references > 10;
      
     -    my $mail = $args{'Message'};
    --    $mail->head->set( 'In-Reply-To' => Encode::encode_utf8(join ' ', @rtid? (@rtid) : (@id)) ) if @id || @rtid;
    --    $mail->head->set( 'References' => Encode::encode_utf8(join ' ', @references) );
    +-    $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) );
     +    return (0);
      }
      
     -sub PseudoReference {
     -    my $ticket = shift;
     -    return '<RT-Ticket-'. $ticket->id .'@'. RT->Config->Get('Organization') .'>';
    ++
     +sub CheckForBounce {
     +    my $head = shift;
     +
    @@ -1476,7 +1496,7 @@
     +        @_
     +    );
     +
    -+    my $From = $args{Message}->head->get("From");
    ++    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 );
    @@ -1512,7 +1532,13 @@
     -    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:
    @@ -1562,22 +1588,17 @@
     +    # the colons are necessary to make ->build include non-standard headers
     +    my %entity_args = (
     +        Type                    => "multipart/mixed",
    -+        From                    => $args{'From'},
    -+        Bcc                     => $args{'Bcc'},
    -+        To                      => $args{'To'},
    -+        Subject                 => $args{'Subject'},
    -+        'X-RT-Loop-Prevention:' => RT->Config->Get('rtname'),
    ++        From                    => Encode::encode( "UTF-8", $args{'From'} ),
    ++        Bcc                     => Encode::encode( "UTF-8", $args{'Bcc'} ),
    ++        To                      => Encode::encode( "UTF-8", $args{'To'} ),
    ++        Subject                 => EncodeToMIME( String => $args{'Subject'} ),
    ++        'X-RT-Loop-Prevention:' => Encode::encode( "UTF-8", RT->Config->Get('rtname') ),
     +    );
    - 
    --    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/;
    ++
     +    # only set precedence if the sysadmin wants us to
     +    if (defined(RT->Config->Get('DefaultErrorMailPrecedence'))) {
    -+        $entity_args{'Precedence:'} = RT->Config->Get('DefaultErrorMailPrecedence');
    ++        $entity_args{'Precedence:'} =
    ++            Encode::encode( "UTF-8", RT->Config->Get('DefaultErrorMailPrecedence') );
          }
     -    return $subject if $subject =~ /\[$tag_re\s+#$id\]/;
      
    @@ -1588,7 +1609,11 @@
     +    my $entity = MIME::Entity->build(%entity_args);
     +    SetInReplyTo( Message => $entity, InReplyTo => $args{'MIMEObj'} );
      
    -+    $entity->attach( Data => $args{'Explanation'} . "\n" );
    ++    $entity->attach(
    ++        Type    => "text/plain",
    ++        Charset => "UTF-8",
    ++        Data    => Encode::encode( "UTF-8", $args{'Explanation'} . "\n" ),
    ++    );
      
     -=head2 Gateway ARGSREF
     +    if ( $args{'MIMEObj'} ) {
    @@ -1597,7 +1622,7 @@
     +    }
      
     +    if ( $args{'Attach'} ) {
    -+        $entity->attach( Data => $args{'Attach'}, Type => 'message/rfc822' );
    ++        $entity->attach( Data => Encode::encode( "UTF-8", $args{'Attach'} ), Type => 'message/rfc822' );
      
     -Takes parameters:
     +    }
    @@ -1708,7 +1733,7 @@
     -        message => undef,
     -        %$argsref
     -    );
    -+    my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
    ++    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;
    @@ -1741,7 +1766,7 @@
     +    if (my $precedence = RT->Config->Get('DefaultMailPrecedence')
     +        and !$args{'Entity'}->head->get("Precedence")
     +    ) {
    -+        $args{'Entity'}->head->set( 'Precedence', $precedence );
    ++        $args{'Entity'}->head->replace( 'Precedence', Encode::encode("UTF-8",$precedence) );
          }
      
     -    my $parser = RT::EmailParser->new();
    @@ -1768,15 +1793,15 @@
     +        require RT::Date;
     +        my $date = RT::Date->new( RT->SystemUser );
     +        $date->SetToNow;
    -+        $head->set( 'Date', $date->RFC2822( Timezone => 'server' ) );
    ++        $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->set( 'MIME-Version', '1.0' );
    ++        $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->set( 'Content-Transfer-Encoding', '8bit' );
    ++        $head->replace( 'Content-Transfer-Encoding', '8bit' );
     +    }
      
     -        return ( 0,
    @@ -1829,14 +1854,15 @@
     +            my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
     +
     +            if ($TicketObj) {
    -+                my $QueueName = $TicketObj->QueueObj->Name;
    -+                my $QueueAddressOverride = $Overrides->{$QueueName};
    ++                my $Queue = $TicketObj->QueueObj;
    ++                my $QueueAddressOverride = $Overrides->{$Queue->id}
    ++                    || $Overrides->{$Queue->Name};
     +
     +                if ($QueueAddressOverride) {
     +                    $OutgoingMailAddress = $QueueAddressOverride;
     +                } else {
    -+                    $OutgoingMailAddress ||= $TicketObj->QueueObj->CorrespondAddress
    -+                                             || RT->Config->Get('CorrespondAddress');
    ++                    $OutgoingMailAddress ||= $Queue->CorrespondAddress
    ++                        || RT->Config->Get('CorrespondAddress');
     +                }
     +            }
     +            elsif ($Overrides->{'Default'}) {
    @@ -1857,18 +1883,18 @@
     +            $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';
      
    --        $skip_plugin{ $class }++;
    -+            # if something wrong with $mail->print we will get PIPE signal, handle it
    -+            local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
    - 
     -        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 )
    @@ -1941,17 +1967,17 @@
     -    my $head = $Message->head;
     -    my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
     -    my $Sender = (ParseSenderAddressFromHead( $head ))[0];
    --    my $From = $head->get("From");
    +-    my $From = Encode::decode( "UTF-8", $head->get("From") );
     -    chomp $From if defined $From;
     +=head2 PrepareEmailUsingTemplate Template => '', Arguments => {}
      
    --    my $MessageId = $head->get('Message-ID')
    +-    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 = $head->get('Subject') || '';
    +-    my $Subject = Encode::decode( "UTF-8", $head->get('Subject') || '');
     -    chomp $Subject;
     -    
     -    # Lets check for mail loops of various sorts.
    @@ -1991,7 +2017,7 @@
     +    return (undef, $msg) unless $status;
      
     -    # ExtractTicketId may have been overridden, and edited the Subject
    --    my $NewSubject = $Message->head->get('Subject');
    +-    my $NewSubject = Encode::decode( "UTF-8", $Message->head->get('Subject') );
     -    chomp $NewSubject;
     +    return $template;
     +}
    @@ -2073,10 +2099,10 @@
     -            undef
     -        );
     -    }
    -+    $mail->head->set( $_ => Encode::encode_utf8( $args{ $_ } ) )
    ++    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ $_ } ) )
     +        foreach grep defined $args{$_}, qw(To Cc Bcc From);
      
    -+    $mail->head->set( $_ => $args{ExtraHeaders}{$_} )
    ++    $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ExtraHeaders}{$_} ) )
     +        foreach keys %{ $args{ExtraHeaders} };
      
     -    unless ($should_store_machine_generated_message) {
    @@ -2084,21 +2110,27 @@
     -    }
     +    SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
      
    +-    $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;
    -+    return SendEmail( Entity => $mail );
    -+}
    ++=head2 GetForwardFrom Ticket => undef, Transaction => undef
      
     -    my $Ticket = RT::Ticket->new($CurrentUser);
    -+=head2 GetForwardFrom Ticket => undef, Transaction => undef
    ++Resolve the From field to use in forward mail
      
     -    if ( !$args{'ticket'} && grep /^(comment|correspond)$/, @actions )
     -    {
    -+Resolve the From field to use in forward mail
    ++=cut
      
     -        my @Cc;
     -        my @Requestors = ( $CurrentUser->id );
    -+=cut
    ++sub GetForwardFrom {
    ++    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    ++    my $txn    = $args{Transaction};
    ++    my $ticket = $args{Ticket} || $txn->Object;
      
     -        if (RT->Config->Get('ParseNewMessageForTicketCcs')) {
     -            @Cc = ParseCcAddressesFromHead(
    @@ -2107,12 +2139,6 @@
     -                QueueObj    => $SystemQueueObj
     -            );
     -        }
    -+sub GetForwardFrom {
    -+    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    -+    my $txn    = $args{Transaction};
    -+    my $ticket = $args{Ticket} || $txn->Object;
    - 
    --        $head->replace('X-RT-Interface' => 'Email');
     +    if ( RT->Config->Get('ForwardFromUser') ) {
     +        return ( $txn || $ticket )->CurrentUser->EmailAddress;
     +    }
    @@ -2145,14 +2171,9 @@
     -        @actions = grep !/^(comment|correspond)$/, @actions;
     -        $args{'ticket'} = $id;
     +Resolve the Attachments to forward
    -+
    + 
    +-    } elsif ( $args{'ticket'} ) {
     +=cut
    - 
    --    } elsif ( $args{'ticket'} ) {
    -+sub GetForwardAttachments {
    -+    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    -+    my $txn    = $args{Transaction};
    -+    my $ticket = $args{Ticket} || $txn->Object;
      
     -        $Ticket->Load( $args{'ticket'} );
     -        unless ( $Ticket->Id ) {
    @@ -2163,24 +2184,28 @@
     -                Explanation => $error,
     -                MIMEObj     => $Message
     -            );
    ++sub GetForwardAttachments {
    ++    my %args   = ( Ticket => undef, Transaction => undef, @_ );
    ++    my $txn    = $args{Transaction};
    ++    my $ticket = $args{Ticket} || $txn->Object;
    + 
    +-            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 {
    -+        my $txns = $ticket->Transactions;
    -+        $txns->Limit(
    -+            FIELD => 'Type',
    -+            VALUE => $_,
    -+        ) for qw(Create Correspond);
    - 
    --            return ( 0, $error );
    -+        while ( my $txn = $txns->Next ) {
    -+            $attachments->Limit( FIELD => 'TransactionId', VALUE => $txn->id );
    -         }
    --        $args{'ticket'} = $Ticket->id;
    --    } else {
    --        return ( 1, "Success", $Ticket );
    ++        $attachments->LimitByTicket( $ticket->id );
    ++        $attachments->Limit(
    ++            ALIAS         => $attachments->TransactionAlias,
    ++            FIELD         => 'Type',
    ++            OPERATOR      => 'IN',
    ++            VALUE         => [ qw(Create Correspond) ],
    ++        );
          }
     +    return $attachments;
     +}
    @@ -2288,7 +2313,7 @@
     -    # if plugin returns AuthStat -2 we skip action
     -    # NOTE: this is experimental API and it would be changed
     -    my %skip_action = ();
    -+    my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
    ++    my $msgid = Encode::decode( "UTF-8", $args{'Entity'}->head->get('Message-ID') || '' );
     +    chomp $msgid;
      
     -    # Since this needs loading, no matter what
    @@ -2392,7 +2417,7 @@
     +        $RT::Logger->error("Couldn't send 'Error to RT owner: public key'");
     +    }
      
    --    my $From = $args{Message}->head->get("From");
    +-    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 );
    @@ -2435,22 +2460,16 @@
     +        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'};
    -+
    -+    return 1;
    - }
    - 
    --=head2 _NoAuthorizedUserFound
    -+=head2 DeleteRecipientsFromHead HEAD RECIPIENTS
    - 
    --Emails the RT Owner and the requestor when the auth plugins return "No auth user found"
    -+Gets a head object and list of addresses.
    -+Deletes addresses from To, Cc or Bcc fields.
    - 
    - =cut
      
     -sub _NoAuthorizedUserFound {
     -    my %args = (
    @@ -2460,9 +2479,8 @@
     -        Queue     => undef,
     -        @_
     -    );
    -+sub DeleteRecipientsFromHead {
    -+    my $head = shift;
    -+    my %skip = map { lc $_ => 1 } @_;
    ++    return 1;
    ++}
      
     -    # Notify the RT Admin of the failure.
     -    MailError(
    @@ -2471,24 +2489,18 @@
     -        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}]}).
    -+    foreach my $field ( qw(To Cc Bcc) ) {
    -+        $head->set( $field =>
    -+            join ', ', map $_->format, grep !$skip{ lc $_->address },
    -+                Email::Address->parse( $head->get( $field ) )
    -+        );
    -+    }
    -+}
    ++=head2 DeleteRecipientsFromHead HEAD RECIPIENTS
      
     -You might need to grant 'Everyone' the right '@{[$args{Right}]}' for the
     -queue @{[$args{'Queue'}]}.
    -+=head2 EncodeToMIME
    ++Gets a head object and list of addresses.
    ++Deletes addresses from To, Cc or Bcc fields.
      
     -EOT
     -        MIMEObj  => $args{'Message'},
     -        LogLevel => 'error'
     -    );
    -+Takes a hash with a String and a Charset. Returns the string encoded
    -+according to RFC2047, using B (base64 based) encoding.
    ++=cut
      
     -    # Also notify the requestor that his request has been dropped.
     -    if ($args{'Requestor'} ne RT->Config->Get('OwnerEmail')) {
    @@ -2498,37 +2510,62 @@
     -        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.
    -+String must be a perl string, octets are returned.
    ++sub DeleteRecipientsFromHead {
    ++    my $head = shift;
    ++    my %skip = map { lc $_ => 1 } @_;
      
     -EOT
     -        MIMEObj  => $args{'Message'},
     -        LogLevel => 'error'
    +-    );
    ++    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 ) ) ) )
    ++        );
    +     }
    + }
    + 
    +-=head2 _HandleMachineGeneratedMail
    ++=head2 EncodeToMIME
    + 
    +-Takes named params:
    +-    Message
    +-    ErrorsTo
    +-    Subject
    ++Takes a hash with a String and a Charset. Returns the string encoded
    ++according to RFC2047, using B (base64 based) encoding.
    + 
    +-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)" );
    ++String must be a perl string, octets are returned.
    ++
     +If Charset is not provided then $EmailOutputEncoding config option
     +is used, or "latin-1" if that is not set.
    -+
    -+=cut
    -+
    + 
    + =cut
    + 
    +-sub _HandleMachineGeneratedMail {
    +-    my %args = ( Message => undef, ErrorsTo => undef, Subject => undef, MessageId => undef, @_ );
    +-    my $head = $args{'Message'}->head;
    +-    my $ErrorsTo = $args{'ErrorsTo'};
     +sub EncodeToMIME {
     +    my %args = (
     +        String => undef,
     +        Charset  => 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 _HandleMachineGeneratedMail
    +-    my $IsBounce = CheckForBounce($head);
     +    # using RFC2047 notation, sec 2.
     +    # encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
      
    --Takes named params:
    --    Message
    --    ErrorsTo
    --    Subject
    +-    my $IsAutoGenerated = CheckForAutoGenerated($head);
     +    # An 'encoded-word' may not be more than 75 characters long
     +    #
     +    # MIME encoding increases 4/3*(number of bytes), and always in multiples
    @@ -2544,34 +2581,28 @@
     +    );
     +    $max = int( $max / 3 ) * 3;
      
    --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)" );
    +-    my $IsSuspiciousSender = CheckForSuspiciousSender($head);
     +    chomp $value;
      
    --=cut
    +-    my $IsALoop = CheckForLoops($head);
     +    if ( $max <= 0 ) {
      
    --sub _HandleMachineGeneratedMail {
    --    my %args = ( Message => undef, ErrorsTo => undef, Subject => undef, MessageId => undef, @_ );
    --    my $head = $args{'Message'}->head;
    --    my $ErrorsTo = $args{'ErrorsTo'};
    +-    my $SquelchReplies = 0;
     +        # gives an error...
     +        $RT::Logger->crit("Can't encode! Charset or encoding too big.");
     +        return ($value);
     +    }
      
    --    my $IsBounce = CheckForBounce($head);
    +-    my $owner_mail = RT->Config->Get('OwnerEmail');
     +    return ($value) if $value =~ /^(?:[\t\x20-\x7e]|\x0D*\x0A[ \t])+$/s;
      
    --    my $IsAutoGenerated = CheckForAutoGenerated($head);
    +-    #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;
     +    $value =~ s/\s+$//;
    - 
    --    my $IsSuspiciousSender = CheckForSuspiciousSender($head);
    -+    # we need perl string to split thing char by char
    -+    Encode::_utf8_on($value) unless Encode::is_utf8($value);
    - 
    --    my $IsALoop = CheckForLoops($head);
    ++
     +    my ( $tmp, @chunks ) = ( '', () );
     +    while ( length $value ) {
     +        my $char = substr( $value, 0, 1, '' );
    @@ -2581,10 +2612,12 @@
     +            $tmp = '';
     +        }
     +        $tmp .= $octets;
    -+    }
    +     }
     +    push @chunks, $tmp if length $tmp;
      
    --    my $SquelchReplies = 0;
    +-    # 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.");
     +    # encode an join chuncks
     +    $value = join "\n ",
     +        map MIME::Words::encode_mimeword( $_, $encoding, $charset ),
    @@ -2592,7 +2625,15 @@
     +    return ($value);
     +}
      
    --    my $owner_mail = RT->Config->Get('OwnerEmail');
    +-        #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}
    +-            );
    +-        }
     +sub GenMessageId {
     +    my %args = (
     +        Ticket      => undef,
    @@ -2605,19 +2646,23 @@
     +    my $scrip_id = ( ref $args{'Scrip'}? $args{'Scrip'}->id : $args{'Scrip'} ) || 0;
     +    my $sent = ( ref $args{'ScripAction'}? $args{'ScripAction'}->{'_Message_ID'} : 0 ) || 0;
      
    --    #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;
    +-        #Do we actually want to store it?
    +-        return ( 0, $ErrorsTo, "Message Bounced", $IsALoop )
    +-            unless RT->Config->Get('StoreLoops');
     -    }
     +    return "<rt-". $RT::VERSION ."-". $$ ."-". CORE::time() ."-". int(rand(2000)) .'.'
     +        . $ticket_id ."-". $scrip_id ."-". $sent ."@". $org .">" ;
     +}
      
    --    # 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.");
    +-    # 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');
    +-    }
     +sub SetInReplyTo {
     +    my %args = (
     +        Message   => undef,
    @@ -2627,27 +2672,25 @@
     +    );
     +    return unless $args{'Message'} && $args{'InReplyTo'};
      
    --        #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}
    --            );
    +-    if ($SquelchReplies) {
     +    my $get_header = sub {
     +        my @res;
     +        if ( $args{'InReplyTo'}->isa('MIME::Entity') ) {
    -+            @res = $args{'InReplyTo'}->head->get( shift );
    ++            @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;
     +    };
      
    --        #Do we actually want to store it?
    --        return ( 0, $ErrorsTo, "Message Bounced", $IsALoop )
    --            unless RT->Config->Get('StoreLoops');
    +-        # 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' );
     +    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');
    @@ -2655,38 +2698,18 @@
     +    unless ( @references ) {
     +        @references = $get_header->('In-Reply-To');
          }
    --
    --    # 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, $ErrorsTo, "Handled machine detection", $IsALoop );
     +    push @references, @id, @rtid;
     +    if ( $args{'Ticket'} ) {
     +        my $pseudo_ref = PseudoReference( $args{'Ticket'} );
     +        push @references, $pseudo_ref unless grep $_ eq $pseudo_ref, @references;
    -     }
    ++    }
     +    splice @references, 4, -6
     +        if @references > 10;
    - 
    --    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',    $Sender );
    --        $head->replace( 'RT-DetectedAutoGenerated', 'true' );
    --    }
    --    return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
    ++
     +    my $mail = $args{'Message'};
    -+    $mail->head->set( 'In-Reply-To' => Encode::encode_utf8(join ' ', @rtid? (@rtid) : (@id)) ) if @id || @rtid;
    -+    $mail->head->set( 'References' => Encode::encode_utf8(join ' ', @references) );
    ++    $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
 3:  670a057 !  3:  2a14662 Adjust POD headers to reflect split
    @@ -113,8 +113,8 @@
     -=head2 CheckForAutoGenerated HEAD
     +=head3 CheckForAutoGenerated HEAD
      
    - Takes a HEAD object of L<MIME::Head> class and returns true if message
    - is autogenerated. Checks 'Precedence' and 'X-FC-Machinegenerated'
    + Takes a HEAD object of L<MIME::Head> class and returns true if message is
    + autogenerated. Checks C<Precedence>, C<Auto-Submitted>, and
     @@
          return ( 1, "Success" );
      }
    @@ -205,6 +205,6 @@
     -=head2 ConvertHTMLToText HTML
     +=head3 ConvertHTMLToText HTML
      
    - Takes HTML and converts it to plain text.  Appropriate for generating a
    - plain text part from an HTML part of an email.  Returns undef if
    + Takes HTML characters and converts it to plain text characters.
    + Appropriate for generating a plain text part from an HTML part of an
     
 4:  5dfc009 =  4:  99624a2 Update MailPlugins to only reference existant modules
 5:  b9ed3c2 =  5:  00d0850 Move MailPlugins documentation out of rt-mailgate, where is is mostly irrelevant
 6:  e3d18e3 !  6:  8400ff9 Remove duplicate ParseCcAddressesFromHead in RT::EmailParser
    @@ -47,8 +47,8 @@
     -
     -    my (@Addresses);
     -
    --    my @ToObjs = Email::Address->parse( $self->Head->get('To') );
    --    my @CcObjs = Email::Address->parse( $self->Head->get('Cc') );
    +-    my @ToObjs = Email::Address->parse( Encode::decode( "UTF-8", $self->Head->get('To') ) );
    +-    my @CcObjs = Email::Address->parse( Encode::decode( "UTF-8", $self->Head->get('Cc') ) );
     -
     -    foreach my $AddrObj ( @ToObjs, @CcObjs ) {
     -        my $Address = $AddrObj->address;
 7:  a93449c !  7:  7b68589 Use Scope::Upper to allow returns from Gateway from nested subs
    @@ -15,8 +15,8 @@
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
     @@
    - use Mail::Mailer ();
    - use Text::ParseWords qw/shellwords/;
    + use RT::Util 'safe_run_child';
    + use File::Spec;
      use MIME::Words ();
     +use Scope::Upper qw/unwind HERE/;
      
    @@ -148,8 +148,8 @@
     +    FAILURE( $result )
     +        unless $should_store_machine_generated_message;
      
    -     # if plugin's updated SystemTicket then update arguments
    -     $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
    +     $head->replace('X-RT-Interface' => 'Email');
    + 
     @@
                      Explanation => $ErrStr,
                      MIMEObj     => $Message
 8:  f993bae =  8:  d358013 Add a base email plugin role, which provides TMPFAIL/FAILURE/SUCCESS
 9:  c2e65a5 =  9:  aee7869 Check if each named mail plugin DOES the mail plugin role
10:  a997949 ! 10:  049f74d Return values of ApplyBeforeDecode plugins' GetCurrentUser are irrelevant
    @@ -44,7 +44,7 @@
          # attach the original encrypted message
     @@
          my %seen;
    -     $args{'Message'}->head->replace( 'X-RT-Privacy' => $_ )
    +     $args{'Message'}->head->replace( 'X-RT-Privacy' => Encode::encode( "UTF-8", $_ ) )
              foreach grep !$seen{$_}++, @found;
     -
     -    return 1;
11:  588a509 ! 11:  cae6a89 Push bounce short-circuiting down into _HandleMachineGeneratedMail
    @@ -47,9 +47,9 @@
     -    FAILURE( $result )
     -        unless $should_store_machine_generated_message;
     -
    +     $head->replace('X-RT-Interface' => 'Email');
    + 
          # if plugin's updated SystemTicket then update arguments
    -     $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
    - 
     @@
              }
      
    @@ -61,7 +61,7 @@
      
          # Squelch replies if necessary
     @@
    -         $head->replace( 'RT-Squelch-Replies-To',    $Sender );
    +         $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
              $head->replace( 'RT-DetectedAutoGenerated', 'true' );
          }
     -    return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
12:  1887b21 ! 12:  afe6659 $IsALoop is now unused in Gateway
    @@ -16,7 +16,7 @@
              ErrorsTo => $ErrorsTo,
              Subject  => $Subject,
     @@
    -         $head->replace( 'RT-Squelch-Replies-To',    $Sender );
    +         $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
              $head->replace( 'RT-DetectedAutoGenerated', 'true' );
          }
     -    return ( $ErrorsTo, $IsALoop );
13:  314ad1b = 13:  dc1f8fe Stop moving RT-Squelch-Replies-To aside
14:  19607b9 ! 14:  03979a3 Simplify _HandleMachineGeneratedMail logic
    @@ -16,11 +16,11 @@
          my $head = $Message->head;
     -    my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
          my $Sender = (ParseSenderAddressFromHead( $head ))[0];
    -     my $From = $head->get("From");
    +     my $From = Encode::decode( "UTF-8", $head->get("From") );
          chomp $From if defined $From;
     @@
          #Pull apart the subject line
    -     my $Subject = $head->get('Subject') || '';
    +     my $Subject = Encode::decode( "UTF-8", $head->get('Subject') || '');
          chomp $Subject;
     -    
     +
    @@ -99,7 +99,7 @@
     +    # 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',    $Sender );
    ++    $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
     +    $head->replace( 'RT-DetectedAutoGenerated', 'true' );
      
     -        # Squelch replies to the sender, and also leave a clue to
    @@ -108,7 +108,7 @@
     -        # 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',    $Sender );
    +-        $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
     -        $head->replace( 'RT-DetectedAutoGenerated', 'true' );
     -    }
     -    return ( $ErrorsTo );
15:  531b96b = 15:  1a1d88f Use different method names rather than an ApplyBeforeDecode method
16:  70daad9 = 16:  202a69f Use FAILURE to abort from GetCurrentUser, rather than a magic -1 value
17:  5a0942b = 17:  19f7ec5 Remove the unused $error variable
18:  8cabccc = 18:  332f4d2 Move $Right to where it is used
19:  52e24c3 ! 19:  a418722 Move SystemTicket definition to where it is first used
    @@ -15,7 +15,7 @@
          my ( $status, @actions ) = IsCorrectAction( $args{'action'} );
          TMPFAIL(
     @@
    -     my $NewSubject = $Message->head->get('Subject');
    +     my $NewSubject = Encode::decode( "UTF-8", $Message->head->get('Subject') );
          chomp $NewSubject;
      
     -    $SystemTicket = RT::Ticket->new( RT->SystemUser );
20:  6f62839 ! 20:  8ae942f Move NewSubject to where it is used
    @@ -10,18 +10,18 @@
          $args{'ticket'} ||= ExtractTicketId( $Message );
      
     -    # ExtractTicketId may have been overridden, and edited the Subject
    --    my $NewSubject = $Message->head->get('Subject');
    +-    my $NewSubject = Encode::decode( "UTF-8", $Message->head->get('Subject') );
     -    chomp $NewSubject;
     -
          my $SystemTicket = RT::Ticket->new( RT->SystemUser );
          $SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
          my $Right;
     @@
    - 
    -         $head->replace('X-RT-Interface' => 'Email');
    +             );
    +         }
      
     +        # ExtractTicketId may have been overridden, and edited the Subject
    -+        my $NewSubject = $head->get('Subject');
    ++        my $NewSubject = Encode::decode( "UTF-8", $head->get('Subject') );
     +        chomp $NewSubject;
     +
              my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
21:  6d715aa = 21:  24bf62a Remove CreateUser, merging to form a more featureful LoadOrCreateByEmail
22:  e9e5e8b ! 22:  6dd0a29 Always create the user; this simplifies ACL checking greatly
    @@ -25,6 +25,24 @@
         Unprivileged users in such cases is worth the notable code
         simplification.
     
    +diff --git a/docs/UPGRADING-4.4 b/docs/UPGRADING-4.4
    +--- a/docs/UPGRADING-4.4
    ++++ b/docs/UPGRADING-4.4
    +@@
    + 
    + RT::Interface::Email no longer exports functions.
    + 
    ++=item *
    ++
    ++Incoming email now always creates users for the C<From:> address.
    ++Previously, if the C<CreateTicket> right was not granted to C<Everyone>
    ++or C<Unprivileged>, the email would be rejected without a user; now, the
    ++user to be created even when the mail is rejected.
    ++
    + =back
    + 
    + =cut
    +
     diff --git a/lib/RT/Interface/Email.pm b/lib/RT/Interface/Email.pm
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
23:  e82d433 ! 23:  39d8b60 Split authentication from authorization
    @@ -13,11 +13,10 @@
     --- a/docs/UPGRADING-4.4
     +++ b/docs/UPGRADING-4.4
     @@
    - 
    - RT::Interface::Email no longer exports functions.
    + or C<Unprivileged>, the email would be rejected without a user; now, the
    + user to be created even when the mail is rejected.
      
     +=item *
    -+
     +The L<RT_Config/@MailPlugins> functionality has been rewritten; mail
     +plugins written for previous versions of RT will not function as
     +expected.  See F<docs/extending/mail_plugins.pod>
24:  d1c4c8b = 24:  d6b673a Remove no-longer-used _NoAuthorizedUserFound
25:  afadc73 = 25:  7408373 Remove now-unused $Right variable, previously used by _NoAuthorizedUserFound
26:  73f1c62 ! 26:  ddd1c03 Fail if the first action is unauthenticated
    @@ -17,7 +17,7 @@
      
          my $head = $Message->head;
     -    my $Sender = (ParseSenderAddressFromHead( $head ))[0];
    -     my $From = $head->get("From");
    +     my $From = Encode::decode( "UTF-8", $head->get("From") );
          chomp $From if defined $From;
      
     @@
    @@ -53,9 +53,9 @@
     -        );
     -    }
     -
    +     $head->replace('X-RT-Interface' => 'Email');
    + 
          # if plugin's updated SystemTicket then update arguments
    -     $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
    - 
     @@
      
      sub CheckACL {
27:  a5fc795 = 27:  03d0af9 Notify the owner on common mis-configurations
28:  6163947 = 28:  12115ef Remove extra error in mail-gateway
29:  87b474c = 29:  096152b Add back a warning that is now lacking
30:  c9d8ab4 ! 30:  3079bf8 Stop passing mail plugins around; load them lazily in one Plugins() method
    @@ -6,7 +6,7 @@
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
     @@
    - use Text::ParseWords qw/shellwords/;
    + use File::Spec;
      use MIME::Words ();
      use Scope::Upper qw/unwind HERE/;
     +use 5.010;
31:  60d8040 ! 31:  5ccf5a1 Split action handling into plugin classes
    @@ -106,8 +106,8 @@
          my $parser = RT::EmailParser->new();
          $parser->SmartParseMIMEEntityFromScalar(
     @@
    -         Queue         => $SystemQueueObj,
    -     );
    + 
    +     $head->replace('X-RT-Interface' => 'Email');
      
     -    # if plugin's updated SystemTicket then update arguments
     -    $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
    @@ -128,10 +128,8 @@
     -            );
     -        }
     -
    --        $head->replace('X-RT-Interface' => 'Email');
    --
     -        # ExtractTicketId may have been overridden, and edited the Subject
    --        my $NewSubject = $head->get('Subject');
    +-        my $NewSubject = Encode::decode( "UTF-8", $head->get('Subject') );
     -        chomp $NewSubject;
     -
     -        my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
    @@ -281,7 +279,8 @@
     -    return
     -        grep $_ ne $current_address && !RT::EmailParser->IsRTAddress( $_ ),
     -        map lc $user->CanonicalizeEmailAddress( $_->address ),
    --        map RT::EmailParser->CleanupAddresses( Email::Address->parse( $args{'Head'}->get( $_ ) ) ),
    +-        map RT::EmailParser->CleanupAddresses( Email::Address->parse(
    +-              Encode::decode( "UTF-8", $args{'Head'}->get( $_ ) ) ) ),
     -        qw(To Cc);
     +    my ($code) = Plugins(Method => "Handle" . ucfirst(delete $args{Action}));
     +    $code->(%args);
    @@ -306,7 +305,7 @@
     -        @_
     -    );
     -
    --    my $From = $args{Message}->head->get("From");
    +-    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 );
    @@ -428,14 +427,13 @@
     +        @Cc =
     +            grep $_ ne $current_address && !RT::EmailParser->IsRTAddress( $_ ),
     +            map lc $user->CanonicalizeEmailAddress( $_->address ),
    -+            map RT::EmailParser->CleanupAddresses( Email::Address->parse( $head->get( $_ ) ) ),
    ++            map RT::EmailParser->CleanupAddresses( Email::Address->parse(
    ++                  Encode::decode( "UTF-8", $head->get( $_ ) ) ) ),
     +            qw(To Cc);
     +    }
     +
    -+    $head->replace('X-RT-Interface' => 'Email');
    -+
     +    # ExtractTicketId may have been overridden, and edited the Subject
    -+    my $subject = $head->get('Subject');
    ++    my $subject = Encode::decode( "UTF-8", $head->get('Subject') );
     +    chomp $subject;
     +
     +    my ( $id, $Transaction, $ErrStr ) = $args{Ticket}->Create(
    @@ -578,7 +576,7 @@
     +        FAILURE( $error );
     +    }
     +
    -+    my $From = $args{Message}->head->get("From");
    ++    my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
     +
     +    my $new_status = $args{'Ticket'}->FirstInactiveStatus;
     +    return unless $new_status;
    @@ -680,7 +678,7 @@
     +        FAILURE( $error );
     +    }
     +
    -+    my $From = $args{Message}->head->get("From");
    ++    my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
     +
     +    my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{Ticket}->CurrentUser->id );
     +    return if $status;
32:  b707acb ! 32: 2f24bb09 Plugins may alter @actions; ensure action is valid prior to calling
    @@ -1,6 +1,6 @@
     Author: Alex Vandiver <alexmv at bestpractical.com>
     
    -    It is possible that plugins will alter @actions; ensure that we have a valid action before calling it
    +    Plugins may alter @actions; ensure action is valid prior to calling
     
     diff --git a/lib/RT/Interface/Email.pm b/lib/RT/Interface/Email.pm
     --- a/lib/RT/Interface/Email.pm
33:  82c43c3 = 33:  a6838f4 Split default authentication from default authorization
34:  3430b59 ! 34:  4651652 Local'ize MailErrors to avoid having to pass $ErrorsTo everywhere
    @@ -138,11 +138,11 @@
     @@
          my %entity_args = (
              Type                    => "multipart/mixed",
    -         From                    => $args{'From'},
    --        Bcc                     => $args{'Bcc'},
    -         To                      => $args{'To'},
    -         Subject                 => $args{'Subject'},
    -         'X-RT-Loop-Prevention:' => RT->Config->Get('rtname'),
    +         From                    => Encode::encode( "UTF-8", $args{'From'} ),
    +-        Bcc                     => Encode::encode( "UTF-8", $args{'Bcc'} ),
    +         To                      => Encode::encode( "UTF-8", $args{'To'} ),
    +         Subject                 => EncodeToMIME( String => $args{'Subject'} ),
    +         'X-RT-Loop-Prevention:' => Encode::encode( "UTF-8", RT->Config->Get('rtname') ),
     
     diff --git a/lib/RT/Interface/Email/Action/Defaults.pm b/lib/RT/Interface/Email/Action/Defaults.pm
     --- a/lib/RT/Interface/Email/Action/Defaults.pm
35:  a3b032a = 35:  3539367 Remove the warning about the deprecated Auth::GnuPG/Auth::SMIME plugins
36:  8127768 = 36:  384caa6 Allow lazy adding of Auth::MailFrom if no other GetCurrentUser plugins exist
37:  dae5c31 = 37:  b33b8a4 There is no reason to not always enable Auth::Crypt
38:  3c0d075 ! 38:  59f1c3c Make Crypt not an Auth:: plugin, but hardcoded
    @@ -27,8 +27,8 @@
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
     @@
    - use strict;
      use warnings;
    + use 5.010;
      
     +use RT::Interface::Email::Crypt;
      use Email::Address;
    @@ -66,7 +66,7 @@
     -#
     -# COPYRIGHT:
     -#
    --# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    +-# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     -#                                          <sales at bestpractical.com>
     -#
     -# (Except where explicitly superseded by other copyright notices)
    @@ -231,7 +231,7 @@
     -
     -        foreach my $protocol ( @check_protocols ) {
     -            my @status = grep defined && length,
    --                $part->head->get( "X-RT-$protocol-Status" );
    +-                map Encode::decode( "UTF-8", $_), $part->head->get( "X-RT-$protocol-Status" );
     -            next unless @status;
     -
     -            push @found, $protocol;
    @@ -242,20 +242,20 @@
     -                }
     -                if ( $_->{Operation} eq 'Verify' && $_->{Status} eq 'DONE' ) {
     -                    $part->head->replace(
    --                        'X-RT-Incoming-Signature' => $_->{UserString}
    +-                        'X-RT-Incoming-Signature' => Encode::encode( "UTF-8", $_->{UserString} )
     -                    );
     -                }
     -            }
     -        }
     -
     -        $part->head->replace(
    --            'X-RT-Incoming-Encryption' => 
    +-            'X-RT-Incoming-Encryption' =>
     -                $decrypted ? 'Success' : 'Not encrypted'
     -        );
     -    }
     -
     -    my %seen;
    --    $args{'Message'}->head->replace( 'X-RT-Privacy' => $_ )
    +-    $args{'Message'}->head->replace( 'X-RT-Privacy' => Encode::encode( "UTF-8", $_ ) )
     -        foreach grep !$seen{$_}++, @found;
     -}
     -
    @@ -356,7 +356,7 @@
     +#
     +# COPYRIGHT:
     +#
    -+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +#                                          <sales at bestpractical.com>
     +#
     +# (Except where explicitly superseded by other copyright notices)
    @@ -481,7 +481,7 @@
     +
     +        foreach my $protocol ( @check_protocols ) {
     +            my @status = grep defined && length,
    -+                $part->head->get( "X-RT-$protocol-Status" );
    ++                map Encode::decode( "UTF-8", $_), $part->head->get( "X-RT-$protocol-Status" );
     +            next unless @status;
     +
     +            push @found, $protocol;
    @@ -492,20 +492,20 @@
     +                }
     +                if ( $_->{Operation} eq 'Verify' && $_->{Status} eq 'DONE' ) {
     +                    $part->head->replace(
    -+                        'X-RT-Incoming-Signature' => $_->{UserString}
    ++                        'X-RT-Incoming-Signature' => Encode::encode( "UTF-8", $_->{UserString} )
     +                    );
     +                }
     +            }
     +        }
     +
     +        $part->head->replace(
    -+            'X-RT-Incoming-Encryption' => 
    ++            'X-RT-Incoming-Encryption' =>
     +                $decrypted ? 'Success' : 'Not encrypted'
     +        );
     +    }
     +
     +    my %seen;
    -+    $args{'Message'}->head->replace( 'X-RT-Privacy' => $_ )
    ++    $args{'Message'}->head->replace( 'X-RT-Privacy' => Encode::encode( "UTF-8", $_ ) )
     +        foreach grep !$seen{$_}++, @found;
     +}
     +
39:  ddd76e6 ! 39:  9af3360 Move RejectOnUnencrypted to being a mail plugin
    @@ -26,10 +26,10 @@
      be used in outgoing emails.  At this moment, only one protocol can be
      used to protect outgoing emails.
      
    --Set C<RejectOnUnencrypted> to true if all incoming email must be
    +-Set C<RejectOnUnencrypted> to 1 if all incoming email must be
     -properly encrypted.  All unencrypted emails will be rejected by RT.
     -
    - Set C<RejectOnMissingPrivateKey> to false if you don't want to reject
    + Set C<RejectOnMissingPrivateKey> to 0 if you don't want to reject
      emails encrypted for key RT doesn't have and can not decrypt.
      
     @@
    @@ -162,8 +162,8 @@
      use strict;
      use warnings;
      
    --use RT::Test::SMIME tests => undef, config => 'Set( %Crypt, RejectOnUnencrypted => 1 );';
    -+use RT::Test::SMIME tests => undef, config => 'Set( @MailPlugins, "Authz::RequireEncrypted" );';
    +-use RT::Test::SMIME tests => undef, actual_server => 1, config => 'Set( %Crypt, RejectOnUnencrypted => 1 );';
    ++use RT::Test::SMIME tests => undef, actual_server => 1, config => 'Set( @MailPlugins, "Authz::RequireEncrypted" );';
      my $test = 'RT::Test::SMIME';
      
      use IPC::Run3 'run3';
40:  7df59ef ! 40:  65d32d4 Merge ParseAddressFromHeader and RT::EmailParser->ParseEmailAddress
    @@ -4,7 +4,7 @@
         
         RT::Interface::Email's ParseAddressFromHeader was merely a thin shim
         around RT::EmailParser->ParseEmailAddress, with the tiny addition of
    -    removing doubled quotes -- which, addording to d5b74f19, had been seen
    +    removing doubled quotes -- which, according to d5b74f19, had been seen
         in the wild.  Move that additional 'feature' into
         RT::EmailParser->ParseEmailAddress, and switch all callsites of
         ParseAddressFromHeader to call that instead.
    @@ -36,7 +36,7 @@
     -    #Figure out who's sending this message.
     -    foreach my $header ( @sender_headers ) {
     +    foreach my $header ( 'Reply-To', 'From', 'Sender' ) {
    -         my $addr_line = $head->get($header) || next;
    +         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;
    @@ -52,11 +52,11 @@
     -    #Figure out who's sending this message.
     -
          foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
    -+        my $value = $head->get($header);
    ++        my $value = Encode::decode( "UTF-8", $head->get($header) );
     +        next unless $value;
      
     -        # If there's a header of that name
    --        my $headerobj = $head->get($header);
    +-        my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
     -        if ($headerobj) {
     -            my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
     -
    @@ -116,10 +116,10 @@
      use strict;
      use warnings;
      
    --use RT::Test nodb => 1, tests => 10;
    +-use RT::Test tests => 11;
     +use RT::Test nodb => 1, tests => undef;
      
    - RT->Config->Set( RTAddressRegexp => qr/^rt\@example.com$/i );
    + ok(require RT::EmailParser);
      
     @@
      ok(eq_array(RT::EmailParser->CullRTAddresses(@before), at after), "CullRTAddresses only culls RT addresses");
41:  0910b6f ! 41:  8c71c0f Fix callsites of ParseSenderAddressFromHead to be slightly less incomprehensible
    @@ -11,7 +11,7 @@
          # the RT Owner. Or maybe all Privileged watchers.
     -    my ( $Sender, $junk ) = ParseSenderAddressFromHead($head);
     +    my ( $Sender ) = ParseSenderAddressFromHead($head);
    -     $head->replace( 'RT-Squelch-Replies-To',    $Sender );
    +     $head->replace( 'RT-Squelch-Replies-To',    Encode::encode("UTF-8", $Sender ) );
          $head->replace( 'RT-DetectedAutoGenerated', 'true' );
      
     @@
42:  6b3c173 ! 42:  b735284 $MessageId is only used in IsMachineGeneratedMail; move it in there
    @@ -10,14 +10,14 @@
     --- a/lib/RT/Interface/Email.pm
     +++ b/lib/RT/Interface/Email.pm
     @@
    -     my $From = $head->get("From");
    +     my $From = Encode::decode( "UTF-8", $head->get("From") );
          chomp $From if defined $From;
      
    --    my $MessageId = $head->get('Message-ID')
    +-    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 = $head->get('Subject') || '';
    +     my $Subject = Encode::decode( "UTF-8", $head->get('Subject') || '');
          chomp $Subject;
     @@
              if IsMachineGeneratedMail(
    @@ -40,7 +40,7 @@
          # 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.");
    -+        my $MessageId = $head->get('Message-ID');
    ++        my $MessageId = Encode::decode( "UTF-8", $head->get('Message-ID') );
     +        $RT::Logger->crit("RT Received mail ($MessageId) from itself.");
      
              #Should we mail it to RTOwner?
43:  48a5394 ! 43:  f71f257 Merge CheckForSuspiciousSender, CheckForAutoGenerated, and CheckForBounce
    @@ -71,18 +71,19 @@
     -
      =head3 CheckForAutoGenerated HEAD
      
    - Takes a HEAD object of L<MIME::Head> class and returns true if message
    --is autogenerated. Checks 'Precedence' and 'X-FC-Machinegenerated'
    --fields of the head in tests.
    -+is autogenerated.  This includes bounces, RFC3834 C<Auto-Submitted>
    -+headers, as well as heuristics including C<Precedence> and
    -+C<X-FC-Machinegenerated> headers.
    + Takes a HEAD object of L<MIME::Head> class and returns true if message is
    +-autogenerated. Checks C<Precedence>, C<Auto-Submitted>, and
    ++autogenerated. Checks C<Precedence>, RFC3834 C<Auto-Submitted>, and
    + C<X-FC-Machinegenerated> fields of the head in tests.
      
      =cut
    - 
    +@@
      sub CheckForAutoGenerated {
          my $head = shift;
      
    +-    if (grep { /^(bulk|junk)/i } $head->get_all("Precedence")) {
    +-        return (1);
    +-    }
     +    # Bounces, via return-path
     +    my $ReturnPath = $head->get("Return-path") || "";
     +    return 1 if $ReturnPath =~ /<>/;
    @@ -94,11 +95,7 @@
     +    return 1 if defined $From and $From eq "";
     +
     +    # Bulk or junk messages are auto-generated
    -     my $Precedence = $head->get("Precedence") || "";
    --    if ( $Precedence =~ /^(bulk|junk)/i ) {
    --        return (1);
    --    }
    -+    return 1 if $Precedence =~ /^(bulk|junk)/i;
    ++    return 1 if grep {/^(bulk|junk)/i} $head->get_all("Precedence");
      
          # Per RFC3834, any Auto-Submitted header which is not "no" means
          # it is auto-generated.
    @@ -116,11 +113,12 @@
     -
     -    return (0);
     -}
    ++    return 1 if $FCJunk =~ /^true/i;
    + 
     -
     -sub CheckForBounce {
     -    my $head = shift;
    -+    return 1 if $FCJunk =~ /^true/i;
    - 
    +-
     -    my $ReturnPath = $head->get("Return-path") || "";
     -    return ( $ReturnPath =~ /<>/ );
     +    return 0;
44:  e211446 ! 44:  b1ea355 Reduce repetition by making MailError handle throwing the FAILURE, as well
    @@ -103,7 +103,7 @@
     -        FAILURE( $error );
          }
      
    -     my $From = $args{Message}->head->get("From");
    +     my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
     @@
          MailError(
              Subject     => "Ticket not resolved",
    @@ -132,7 +132,7 @@
     -        FAILURE( $error );
          }
      
    -     my $From = $args{Message}->head->get("From");
    +     my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
     @@
          MailError(
              Subject     => "Ticket not taken",
45:  2f7826f ! 45:  a008d0a Move ACL checking for Take and Resolve into their own plugins
    @@ -96,7 +96,7 @@
     +        @_,
     +    );
     +
    -     my $From = $args{Message}->head->get("From");
    +     my $From = Encode::decode( "UTF-8", $args{Message}->head->get("From") );
      
          my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{Ticket}->CurrentUser->id );
     
46:  d4dd6ec ! 46:  140d5c8 Update POD for new methods and functionality
    @@ -82,14 +82,23 @@
     +C<use>'s this module.  The module should implement
     +L<RT::Interface::Email::Role>.
     +
    ++=head2 Plugin methods
    ++
    ++L<RT::Interface::Email::Role> defines a number of functions which are
    ++useful for immediately aborting processing.  They include
    ++L<RT::Interface::Email::Role/SUCCESS>,
    ++L<RT::Interface::Email::Role/FAILURE>, and
    ++L<RT::Interface::Email::Role/TMPFAIL>; read their descriptions for
    ++information on how to immediately abort processing from mail plugins.
    ++
     +=head2 Plugin hooks
     +
     +Mail plugins are expected to provide one or more of the following
     +methods:
    -+
    + 
    +-=over 4
     +=head3 BeforeDecrypt
    - 
    --=over 4
    ++
     +Called before the message is decoded or decrypted.  Its return value is
     +ignored; it is passed the following parameters:
     +
    @@ -100,18 +109,24 @@
     -A C<MIME::Entity> object representing the email
     +A L<MIME::Entity> object representing the mail.  This may be modified by
     +the plugin.
    -+
    + 
    +-=item CurrentUser
     +=item RawMessage
    -+
    + 
    +-An C<RT::CurrentUser> object
     +A reference to the string containing the original message.  This should
     +not be modified.
    -+
    + 
    +-=item AuthStat
     +=item Queue
    -+
    + 
    +-The authentication level returned from the previous plugin.
     +A L<RT::Queue>, the C<--queue> argument which was passed L<rt-mailgate>.
    -+
    + 
    +-=item Ticket [OPTIONAL]
     +=item Actions
    -+
    + 
    +-The ticket under discussion
     +An array reference of actions to perform; the C<--action> argument which
     +was passed to L<rt-mailgate>.  This may be modified.
     +
    @@ -147,22 +162,23 @@
     +
     +=back
     +
    - 
    --=item CurrentUser
    - 
    --An C<RT::CurrentUser> object
    ++
    ++
     +=head3 GetCurrentUser
    - 
    --=item AuthStat
    ++
     +This method is called in order on the mail plugins that define it.  The
     +first method to return a L<RT::CurrentUser> value shortcuts all other
     +plugins.  It is passed:
     +
    ++=over
    ++
     +=item Message
    -+
    + 
    +-=item Queue [OPTIONAL]
     +A L<MIME::Entity> object representing the mail.  This may be modified by
     +the plugin.
    -+
    + 
    +-If we don't already have a ticket id, we need to know which queue we're talking about
     +=item RawMessage
     +
     +A reference to the string containing the original message.  This should
    @@ -179,25 +195,20 @@
     +A L<RT::Queue>, the C<--queue> argument which was passed L<rt-mailgate>.
     +
     +=back
    - 
    --The authentication level returned from the previous plugin.
    - 
    --=item Ticket [OPTIONAL]
    ++
    ++
     +=head3 CheckACL
    - 
    --The ticket under discussion
    ++
     +Called to determine authorization -- namely, can the current user
     +complete the action in question?  While RT's standard permission
     +controls apply, this allows a better error message, or more limited
     +restrictions on the email gateway.
    - 
    --=item Queue [OPTIONAL]
    ++
     +Only the I<first> action (if there are more than one defined) is
     +checked, as the process of completing the first action might affect the
     +later actions; consider the case of C<take-correspond>, where the
     +C<correspond> action might only be avilable to owners.
    - 
    --If we don't already have a ticket id, we need to know which queue we're talking about
    ++
     +Each plugin defining this method is called in turn; as soon as one
     +plugin returns true, the rest are short-circuited.  Arguments include:
     +
    @@ -222,6 +233,48 @@
     +A L<RT::Ticket>, the ticket (if any) that has been extracted from the
     +subject.  If there was no ticket id, this value will be a L<RT::Ticket>
     +object with no C<id>.
    ++
    ++=item Queue
    ++
    ++A L<RT::Queue>, the C<--queue> argument which was passed L<rt-mailgate>.
    ++
    ++=back
    ++
    ++
    ++=head3 HandleI<Action>
    ++
    ++For any given action I<foo>, the presence of a subroutine called
    ++C<HandleFoo> signals the ability of the mailgate to handle that action.
    ++The first plugin in to define the method is called, and its return value
    ++ignored.  It is passed:
    ++
    ++=over
    ++
    ++=item Message
    ++
    ++A L<MIME::Entity> object representing the mail.  This may be modified by
    ++the plugin.
    ++
    ++=item Subject
    ++
    ++A string, the original C<Subject> header of the message before it was
    ++modified to extract the ticket id.
    ++
    ++=item CurrentUser
    ++
    ++A L<RT::CurrentUser> object representing the authenticated user.
    ++
    ++=item Ticket
    ++
    ++A L<RT::Ticket>, the ticket (if any) that has been extracted from the
    ++subject.  If there was no ticket id, this value will be a L<RT::Ticket>
    ++object with no C<id>.
    ++
    ++=item TicketId
    ++
    ++The value id that was extracted from the subject; this allows a
    ++non-existant ticket id to be differentiated from no subject id, as both
    ++will present as having an unloaded C<Ticket> argument.
     +
     +=item Queue
     +
    @@ -235,52 +288,10 @@
     -the correspondent) or one, which is the normal mode of operation.
     -Additionally, if C<-1> is returned, then the processing of the plug-ins
     -stops immediately and the message is ignored.
    - 
    -+=head3 HandleI<Action>
    -+
    -+For any given action I<foo>, the presence of a subroutine called
    -+C<HandleFoo> signals the ability of the mailgate to handle that action.
    -+The first plugin in to define the method is called, and its return value
    -+ignored.  It is passed:
    -+
    -+=over
    -+
    -+=item Message
    -+
    -+A L<MIME::Entity> object representing the mail.  This may be modified by
    -+the plugin.
    -+
    -+=item Subject
    -+
    -+A string, the original C<Subject> header of the message before it was
    -+modified to extract the ticket id.
    -+
    -+=item CurrentUser
    -+
    -+A L<RT::CurrentUser> object representing the authenticated user.
    -+
    -+=item Ticket
    -+
    -+A L<RT::Ticket>, the ticket (if any) that has been extracted from the
    -+subject.  If there was no ticket id, this value will be a L<RT::Ticket>
    -+object with no C<id>.
    -+
    -+=item TicketId
    -+
    -+The value id that was extracted from the subject; this allows a
    -+non-existant ticket id to be differentiated from no subject id, as both
    -+will present as having an unloaded C<Ticket> argument.
    -+
    -+=item Queue
    -+
    -+A L<RT::Queue>, the C<--queue> argument which was passed L<rt-mailgate>.
    -+
    -+=back
    -+
     +=head1 SEE ALSO
     +
     +L<RT::Interface::Email::Role>
    -+
    + 
     +=cut
     
     diff --git a/lib/RT/Interface/Email.pm b/lib/RT/Interface/Email.pm
47:  b020c8f < --:  ------- Add pointers to SUCCESS/FAILURE/TMPFAIL in "writing mail plugins" docs
48:  bb657e9 = 47:  ac37116 simple test for multiple reply-to addresses
49:  395e0ca ! 48:  b78d166 take into account multiple sender's addresses
    @@ -133,7 +133,7 @@
      
     +    my @addr;
          foreach my $header ( 'Reply-To', 'From', 'Sender' ) {
    -         my $addr_line = $head->get($header) || next;
    +         my $addr_line = Encode::decode( "UTF-8", $head->get($header) ) || next;
     -        my ($addr) = RT::EmailParser->ParseEmailAddress( $addr_line );
     -        return ($addr->address, $addr->phrase, @errors) if $addr;
     +        push @addr, RT::EmailParser->ParseEmailAddress( $addr_line );



More information about the rt-commit mailing list