[Rt-commit] rt branch, master, updated. rt-4.2.12-494-g377fab4

Shawn Moore shawn at bestpractical.com
Mon Nov 2 11:51:53 EST 2015


The branch, master has been updated
       via  377fab44c895ac8224bf3a5f196abc03623fbeba (commit)
       via  140d5c84e757692692b13803b058c74783a45346 (commit)
       via  a008d0a07a3af1c3aded25bd623923dcff659c75 (commit)
       via  b1ea3555e7c3a59fbabb01646e9b2bebe870579f (commit)
       via  f71f2575ff7ea2468fa77d45191ee6b526432945 (commit)
       via  b73528481f2e556462c2b326c0d15779ef734e7e (commit)
       via  8c71c0f7b799677c530519591d9592506f100cea (commit)
       via  65d32d435937f29abe2e3f5493be85938ef6b77b (commit)
       via  9af33601c6e4855e2302adc01bc4422bc980ebd1 (commit)
       via  59f1c3c67db95efbd3c4dc13402f73cd6cd190d2 (commit)
       via  b33b8a489d0d005eb69430fca1cfd150a53d4481 (commit)
       via  384caa6cc2627f5de5a48c28ae3772ebfbfbd349 (commit)
       via  3539367b2702b1027deb0ec1ed33a2bfe42d86dd (commit)
       via  46516528a8ca8b0a37f4dba0359b2b94ec94f162 (commit)
       via  a6838f4dd0a175c0418e130ee235dbe1720555df (commit)
       via  2f24bb090933b084e635f01be480608ea6108a06 (commit)
       via  5ccf5a14fc05f958a82879d23e062564a8ee1c99 (commit)
       via  3079bf8cb106f7aa1896f1583f2218d80cd0108d (commit)
       via  096152b9879cd3fbc6bc954c355891673208598f (commit)
       via  12115efd55ebffa959d93b34a21c9ed0309d3e90 (commit)
       via  03d0af9a7dfe03ce0f2f86f831bcfe19bb29f5a9 (commit)
       via  ddd1c0350b3e6acaa3c5d82c91837c5372aeac14 (commit)
       via  7408373934527b2916df80b90e41f108a83847b2 (commit)
       via  d6b673a6e606de4ff74e30a3823401940e2cd465 (commit)
       via  39d8b60b3b7afad5e4a40cad9c41148a4421477a (commit)
       via  6dd0a2922889fb050af0d699f406d92447268b0c (commit)
       via  24bf62a57013d15a35d21816eb91497344173033 (commit)
       via  8ae942ffef8812dec4ee448eec3eb2e4d8bb480b (commit)
       via  a418722c92f31047e6f24a6efa3dc6e5a480302a (commit)
       via  332f4d2b7eb6ded846862b013be868eb7f53e4d4 (commit)
       via  19f7ec50a2ac095b697fbb8de2e66b4cfe4e0fac (commit)
       via  202a69f978a5e156c327820d2b3e5786b3bd002d (commit)
       via  1a1d88f59e6e6f7774ac87053fb0e5f93f658a6f (commit)
       via  03979a3ec80070f270da3f8171e89639847415e0 (commit)
       via  dc1f8fea69a40ecf519713acf29586af2a0a22d3 (commit)
       via  afe6659968c1d73d27cf483c21de3c9a62abcb14 (commit)
       via  cae6a8919bc9f3fb02ce41f40eb5d98acdaefb27 (commit)
       via  049f74da3c9902de704135508e1c5abdfddd6003 (commit)
       via  aee78696d7214e4659e1448899d5ec539826c347 (commit)
       via  d3580131f75def77807d00a471aaf278100d2ae3 (commit)
       via  7b685895762b905e3e0959b95caf9780021fe75f (commit)
       via  8400ff9328ce194617cc1173fc4453364d597b13 (commit)
       via  00d085048a8a39864343ce70baade5f9e10b6679 (commit)
       via  99624a2c82a750834ad65625b5222b5c7d593e4f (commit)
       via  2a1466250f6ae44520c2752ae83bb904224fe0bf (commit)
       via  e3399f0d2fb128fb08ff973285d90a5f4077e634 (commit)
       via  e0497a21877e5acd6d86602a65e0b7080ea7debc (commit)
      from  0a2cde28d924a366a77f76d1803908d6b1202d1a (commit)

Summary of changes:
 bin/rt-mailgate.in                                 |   84 +-
 docs/UPGRADING-4.4                                 |   29 +
 docs/extending/mail_plugins.pod                    |  250 ++
 etc/RT_Config.pm.in                                |   17 +-
 lib/RT/Config.pm                                   |   24 +-
 lib/RT/Crypt.pm                                    |   15 -
 lib/RT/Crypt/GnuPG.pm                              |   12 -
 lib/RT/Crypt/SMIME.pm                              |    7 +-
 lib/RT/EmailParser.pm                              |   38 +-
 lib/RT/Interface/Email.pm                          | 2395 ++++++++------------
 lib/RT/Interface/Email/Action/Defaults.pm          |  151 ++
 lib/RT/Interface/Email/Action/Resolve.pm           |  138 ++
 .../{Search.pm => Interface/Email/Action/Take.pm}  |  117 +-
 lib/RT/Interface/Email/Auth/MailFrom.pm            |  141 +-
 lib/RT/Interface/Email/Authz/Default.pm            |  141 ++
 .../Email/Authz/RequireEncrypted.pm}               |   61 +-
 lib/RT/Interface/Email/{Auth => }/Crypt.pm         |   79 +-
 .../{GroupMembers.pm => Interface/Email/Role.pm}   |  121 +-
 lib/RT/Test/GnuPG.pm                               |    1 -
 lib/RT/Test/SMIME.pm                               |    1 -
 lib/RT/Ticket.pm                                   |    2 +-
 lib/RT/User.pm                                     |   66 +-
 sbin/rt-test-dependencies.in                       |    1 +
 share/html/REST/1.0/NoAuth/mail-gateway            |    3 +-
 t/api/emailparser.t                                |   32 +-
 t/mail/gateway.t                                   |   49 +-
 t/mail/gnupg-bad.t                                 |    2 -
 t/mail/smime/reject_on_unencrypted.t               |    2 +-
 28 files changed, 1994 insertions(+), 1985 deletions(-)
 create mode 100644 docs/extending/mail_plugins.pod
 create mode 100644 lib/RT/Interface/Email/Action/Defaults.pm
 create mode 100644 lib/RT/Interface/Email/Action/Resolve.pm
 copy lib/RT/{Search.pm => Interface/Email/Action/Take.pm} (52%)
 create mode 100644 lib/RT/Interface/Email/Authz/Default.pm
 copy lib/RT/{Principals.pm => Interface/Email/Authz/RequireEncrypted.pm} (63%)
 rename lib/RT/Interface/Email/{Auth => }/Crypt.pm (75%)
 copy lib/RT/{GroupMembers.pm => Interface/Email/Role.pm} (50%)

- Log -----------------------------------------------------------------
commit 377fab44c895ac8224bf3a5f196abc03623fbeba
Merge: 0a2cde2 140d5c8
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Mon Nov 2 16:50:15 2015 +0000

    Merge branch '4.4/gateway-refactor'

diff --cc docs/UPGRADING-4.4
index 3827cf4,42e794f..79d3b64
--- a/docs/UPGRADING-4.4
+++ b/docs/UPGRADING-4.4
@@@ -28,26 -28,32 +28,55 @@@ reporting
  
  =item *
  
 +Custom fields with categories will be split out into hierarchical custom
 +fields.
 +
 +=item *
 +
 +Homepage component "Quicksearch" has been renamed to "QueueList" to reflect
 +what it actually is. Please update C<$HomepageComponents> accordingly if you
 +customized it in site config.
 +
 +=item *
 +
 +SLA is in core now, so C<SLA> became a core field. if you installed
 +C<RT::Extension::SLA> before, you need to remove it from your plugins, adjust
 +configs accordingly and run F<etc/upgrade/upgrade-sla>. see also the SLA
 +section in F<RT_Config.pm>.
 +
 +Note that with core SLA, you can't define different set of levels for
 +different queues. i.e. all the queues share the same set of levels defined in
 +C<%ServiceAgreements>.
 +
++=item *
++
+ 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.
+ 
+ =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>
+ 
+ =item *
+ 
+ The C<$UnsafeEmailCommands> option has been replaced with two mail
+ plugins, L<RT::Interface::Email::Action::Take>, and
+ L<RT::Interface::Email::Action::Resolve>.
+ 
+ =item *
+ 
+ The C<RejectOnUnencrypted> option to L<RT_Config/%Crypt> has been
+ replaced with a mail plugin,
+ L<RT::Interface::Email::Authz::RequireEncrypted>.
+ 
  =back
  
  =cut
diff --cc lib/RT/Interface/Email.pm
index 0aa835e,522a33b..1aaa221
--- a/lib/RT/Interface/Email.pm
+++ b/lib/RT/Interface/Email.pm
@@@ -920,299 -777,259 +777,279 @@@ header field then it's value is use
  
  =cut
  
- sub ParseCcAddressesFromHead {
-     my %args = (
-         Head        => undef,
-         QueueObj    => undef,
-         CurrentUser => undef,
-         @_
+ sub SendEmail {
+     my (%args) = (
+         Entity => undef,
+         Bounce => 0,
+         Ticket => undef,
+         Transaction => undef,
+         @_,
      );
  
-     my $current_address = lc $args{'CurrentUser'}->EmailAddress;
-     my $user = $args{'CurrentUser'}->UserObj;
- 
-     return
-         grep $_ ne $current_address && !RT::EmailParser->IsRTAddress( $_ ),
-         map lc $user->CanonicalizeEmailAddress( $_->address ),
-         map RT::EmailParser->CleanupAddresses( Email::Address->parse(
-               Encode::decode( "UTF-8", $args{'Head'}->get( $_ ) ) ) ),
-         qw(To Cc);
- }
- 
- 
- 
- =head2 ParseSenderAddressFromHead HEAD
- 
- Takes a MIME::Header object. Returns (user at host, friendly name, errors)
- where the first two values are the From (evaluated in order of
- Reply-To:, From:, Sender).
- 
- A list of error messages may be returned even when a Sender value is
- found, since it could be a parse error for another (checked earlier)
- sender field. In this case, the errors aren't fatal, but may be useful
- to investigate the parse failure.
- 
- =cut
- 
- sub ParseSenderAddressFromHead {
-     my $head = shift;
-     my @sender_headers = ('Reply-To', 'From', 'Sender');
-     my @errors;  # Accumulate any errors
- 
-     #Figure out who's sending this message.
-     foreach my $header ( @sender_headers ) {
-         my $addr_line = Encode::decode( "UTF-8", $head->get($header) ) || next;
-         my ($addr, $name) = ParseAddressFromHeader( $addr_line );
-         # only return if the address is not empty
-         return ($addr, $name, @errors) if $addr;
+     my $TicketObj = $args{'Ticket'};
+     my $TransactionObj = $args{'Transaction'};
  
-         chomp $addr_line;
-         push @errors, "$header: $addr_line";
+     unless ( $args{'Entity'} ) {
+         $RT::Logger->crit( "Could not send mail without 'Entity' object" );
+         return 0;
      }
  
-     return (undef, undef, @errors);
- }
- 
- =head2 ParseErrorsToAddressFromHead HEAD
- 
- Takes a MIME::Header object. Return a single value : user at host
- of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
- From:, Sender)
- 
- =cut
- 
- sub ParseErrorsToAddressFromHead {
-     my $head = shift;
- 
-     #Figure out who's sending this message.
- 
-     foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
- 
-         # If there's a header of that name
-         my $headerobj = Encode::decode( "UTF-8", $head->get($header) );
-         if ($headerobj) {
-             my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
- 
-             # If it's got actual useful content...
-             return ($addr) if ($addr);
-         }
+     my $msgid = Encode::decode( "UTF-8", $args{'Entity'}->head->get('Message-ID') || '' );
+     chomp $msgid;
+     
+     # If we don't have any recipients to send to, don't send a message;
+     unless ( $args{'Entity'}->head->get('To')
+         || $args{'Entity'}->head->get('Cc')
+         || $args{'Entity'}->head->get('Bcc') )
+     {
+         $RT::Logger->info( $msgid . " No recipients found. Not sending." );
+         return -1;
      }
- }
- 
- 
- 
- =head2 ParseAddressFromHeader ADDRESS
- 
- Takes an address from C<$head->get('Line')> and returns a tuple: user at host, friendly name
- 
- =cut
- 
- sub ParseAddressFromHeader {
-     my $Addr = shift;
- 
-     # Some broken mailers send:  ""Vincent, Jesse"" <jesse at fsck.com>. Hate
-     $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
-     my @Addresses = RT::EmailParser->ParseEmailAddress($Addr);
  
-     my ($AddrObj) = grep ref $_, @Addresses;
-     unless ( $AddrObj ) {
-         return ( undef, undef );
+     if ($args{'Entity'}->head->get('X-RT-Squelch')) {
+         $RT::Logger->info( $msgid . " Squelch header found. Not sending." );
+         return -1;
      }
  
-     return ( $AddrObj->address, $AddrObj->phrase );
- }
- 
- =head2 DeleteRecipientsFromHead HEAD RECIPIENTS
- 
- Gets a head object and list of addresses.
- Deletes addresses from To, Cc or Bcc fields.
- 
- =cut
- 
- sub DeleteRecipientsFromHead {
-     my $head = shift;
-     my %skip = map { lc $_ => 1 } @_;
- 
-     foreach my $field ( qw(To Cc Bcc) ) {
-         $head->replace( $field => Encode::encode( "UTF-8",
-             join ', ', map $_->format, grep !$skip{ lc $_->address },
-                 Email::Address->parse( Encode::decode( "UTF-8", $head->get( $field ) ) ) )
-         );
+     if (my $precedence = RT->Config->Get('DefaultMailPrecedence')
+         and !$args{'Entity'}->head->get("Precedence")
+     ) {
+         $args{'Entity'}->head->replace( 'Precedence', Encode::encode("UTF-8",$precedence) );
      }
- }
  
- sub GenMessageId {
-     my %args = (
-         Ticket      => undef,
-         Scrip       => undef,
-         ScripAction => undef,
-         @_
-     );
-     my $org = RT->Config->Get('Organization');
-     my $ticket_id = ( ref $args{'Ticket'}? $args{'Ticket'}->id : $args{'Ticket'} ) || 0;
-     my $scrip_id = ( ref $args{'Scrip'}? $args{'Scrip'}->id : $args{'Scrip'} ) || 0;
-     my $sent = ( ref $args{'ScripAction'}? $args{'ScripAction'}->{'_Message_ID'} : 0 ) || 0;
- 
-     return "<rt-". $RT::VERSION ."-". $$ ."-". CORE::time() ."-". int(rand(2000)) .'.'
-         . $ticket_id ."-". $scrip_id ."-". $sent ."@". $org .">" ;
- }
- 
- sub SetInReplyTo {
-     my %args = (
-         Message   => undef,
-         InReplyTo => undef,
-         Ticket    => undef,
-         @_
-     );
-     return unless $args{'Message'} && $args{'InReplyTo'};
- 
-     my $get_header = sub {
-         my @res;
-         if ( $args{'InReplyTo'}->isa('MIME::Entity') ) {
-             @res = map {Encode::decode("UTF-8", $_)} $args{'InReplyTo'}->head->get( shift );
-         } else {
-             @res = $args{'InReplyTo'}->GetHeader( shift ) || '';
-         }
-         return grep length, map { split /\s+/m, $_ } grep defined, @res;
-     };
+     if ( $TransactionObj && !$TicketObj
+         && $TransactionObj->ObjectType eq 'RT::Ticket' )
+     {
+         $TicketObj = $TransactionObj->Object;
+     }
  
-     my @id = $get_header->('Message-ID');
-     #XXX: custom header should begin with X- otherwise is violation of the standard
-     my @rtid = $get_header->('RT-Message-ID');
-     my @references = $get_header->('References');
-     unless ( @references ) {
-         @references = $get_header->('In-Reply-To');
+     my $head = $args{'Entity'}->head;
+     unless ( $head->get('Date') ) {
+         require RT::Date;
+         my $date = RT::Date->new( RT->SystemUser );
+         $date->SetToNow;
+         $head->replace( 'Date', Encode::encode("UTF-8",$date->RFC2822( Timezone => 'server' ) ) );
      }
-     push @references, @id, @rtid;
-     if ( $args{'Ticket'} ) {
-         my $pseudo_ref = PseudoReference( $args{'Ticket'} );
-         push @references, $pseudo_ref unless grep $_ eq $pseudo_ref, @references;
+     unless ( $head->get('MIME-Version') ) {
+         # We should never have to set the MIME-Version header
+         $head->replace( 'MIME-Version', '1.0' );
+     }
+     unless ( $head->get('Content-Transfer-Encoding') ) {
+         # fsck.com #5959: Since RT sends 8bit mail, we should say so.
+         $head->replace( 'Content-Transfer-Encoding', '8bit' );
      }
-     splice @references, 4, -6
-         if @references > 10;
- 
-     my $mail = $args{'Message'};
-     $mail->head->replace( 'In-Reply-To' => Encode::encode( "UTF-8", join ' ', @rtid? (@rtid) : (@id)) ) if @id || @rtid;
-     $mail->head->replace( 'References' => Encode::encode( "UTF-8", join ' ', @references) );
- }
  
- sub PseudoReference {
-     my $ticket = shift;
-     return '<RT-Ticket-'. $ticket->id .'@'. RT->Config->Get('Organization') .'>';
- }
+     if ( RT->Config->Get('Crypt')->{'Enable'} ) {
+         %args = WillSignEncrypt(
+             %args,
+             Attachment => $TransactionObj ? $TransactionObj->Attachments->First : undef,
+             Ticket     => $TicketObj,
+         );
+         my $res = SignEncrypt( %args );
+         return $res unless $res > 0;
+     }
  
- =head2 ExtractTicketId
+     my $mail_command = RT->Config->Get('MailCommand');
  
- Passed a MIME::Entity.  Returns a ticket id or undef to signal 'new ticket'.
+     # if it is a sub routine, we just return it;
+     return $mail_command->($args{'Entity'}) if UNIVERSAL::isa( $mail_command, 'CODE' );
  
- This is a great entry point if you need to customize how ticket ids are
- handled for your site. RT-Extension-RepliesToResolved demonstrates one
- possible use for this extension.
+     if ( $mail_command eq 'sendmailpipe' ) {
+         my $path = RT->Config->Get('SendmailPath');
+         my @args = shellwords(RT->Config->Get('SendmailArguments'));
+         push @args, "-t" unless grep {$_ eq "-t"} @args;
  
- If the Subject of this ticket is modified, it will be reloaded by the
- mail gateway code before Ticket creation.
+         # SetOutgoingMailFrom and bounces conflict, since they both want -f
+         if ( $args{'Bounce'} ) {
+             push @args, shellwords(RT->Config->Get('SendmailBounceArguments'));
+         } elsif ( my $MailFrom = RT->Config->Get('SetOutgoingMailFrom') ) {
+             my $OutgoingMailAddress = $MailFrom =~ /\@/ ? $MailFrom : undef;
+             my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
  
- =cut
+             if ($TicketObj) {
+                 my $Queue = $TicketObj->QueueObj;
+                 my $QueueAddressOverride = $Overrides->{$Queue->id}
+                     || $Overrides->{$Queue->Name};
  
- sub ExtractTicketId {
-     my $entity = shift;
+                 if ($QueueAddressOverride) {
+                     $OutgoingMailAddress = $QueueAddressOverride;
+                 } else {
+                     $OutgoingMailAddress ||= $Queue->CorrespondAddress
+                         || RT->Config->Get('CorrespondAddress');
+                 }
+             }
+             elsif ($Overrides->{'Default'}) {
+                 $OutgoingMailAddress = $Overrides->{'Default'};
+             }
  
-     my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') || '' );
-     chomp $subject;
-     return ParseTicketId( $subject );
- }
+             push @args, "-f", $OutgoingMailAddress
+                 if $OutgoingMailAddress;
+         }
  
- =head2 ParseTicketId
+         # VERP
+         if ( $TransactionObj and
+              my $prefix = RT->Config->Get('VERPPrefix') and
+              my $domain = RT->Config->Get('VERPDomain') )
+         {
+             my $from = $TransactionObj->CreatorObj->EmailAddress;
+             $from =~ s/@/=/g;
+             $from =~ s/\s//g;
+             push @args, "-f", "$prefix$from\@$domain";
+         }
  
- Takes a string and searches for [subjecttag #id]
+         eval {
+             # don't ignore CHLD signal to get proper exit code
+             local $SIG{'CHLD'} = 'DEFAULT';
  
- Returns the id if a match is found.  Otherwise returns undef.
+             # if something wrong with $mail->print we will get PIPE signal, handle it
+             local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
  
- =cut
+             require IPC::Open2;
+             my ($mail, $stdout);
+             my $pid = IPC::Open2::open2( $stdout, $mail, $path, @args )
+                 or die "couldn't execute program: $!";
  
- sub ParseTicketId {
-     my $Subject = shift;
+             $args{'Entity'}->print($mail);
+             close $mail or die "close pipe failed: $!";
  
-     my $rtname = RT->Config->Get('rtname');
-     my $test_name = RT->Config->Get('EmailSubjectTagRegex') || qr/\Q$rtname\E/i;
+             waitpid($pid, 0);
+             if ($?) {
+                 # sendmail exit statuses mostly errors with data not software
+                 # TODO: status parsing: core dump, exit on signal or EX_*
+                 my $msg = "$msgid: `$path @args` exited with code ". ($?>>8);
+                 $msg = ", interrupted by signal ". ($?&127) if $?&127;
+                 $RT::Logger->error( $msg );
+                 die $msg;
+             }
+         };
+         if ( $@ ) {
+             $RT::Logger->crit( "$msgid: Could not send mail with command `$path @args`: " . $@ );
+             if ( $TicketObj ) {
+                 _RecordSendEmailFailure( $TicketObj );
+             }
+             return 0;
+         }
 -    }
 -    else {
++    } elsif ( $mail_command eq 'mbox' ) {
++        my $now = RT::Date->new(RT->SystemUser);
++        $now->SetToNow;
 +
-     # We use @captures and pull out the last capture value to guard against
-     # someone using (...) instead of (?:...) in $EmailSubjectTagRegex.
-     my $id;
-     if ( my @captures = $Subject =~ /\[$test_name\s+\#(\d+)\s*\]/i ) {
-         $id = $captures[-1];
++        state $logfile;
++        unless ($logfile) {
++            my $when = $now->ISO( Timezone => "server" );
++            $when =~ s/\s+/-/g;
++            $logfile = "$RT::VarPath/$when.mbox";
++            $RT::Logger->info("Storing outgoing emails in $logfile");
++        }
++        my $fh;
++        unless (open($fh, ">>", $logfile)) {
++            $RT::Logger->crit( "Can't open mbox file $logfile: $!" );
++            return 0;
++        }
++        my $content = $args{Entity}->stringify;
++        $content =~ s/^(>*From )/>$1/mg;
++        print $fh "From $ENV{USER}\@localhost  ".localtime()."\n";
++        print $fh $content, "\n";
++        close $fh;
 +    } else {
-         foreach my $tag ( RT->System->SubjectTag ) {
-             next unless my @captures = $Subject =~ /\[\Q$tag\E\s+\#(\d+)\s*\]/i;
-             $id = $captures[-1];
-             last;
+         local ($ENV{'MAILADDRESS'}, $ENV{'PERL_MAILERS'});
+ 
+         my @mailer_args = ($mail_command);
+         if ( $mail_command eq 'sendmail' ) {
+             $ENV{'PERL_MAILERS'} = RT->Config->Get('SendmailPath');
+             push @mailer_args, grep {$_ ne "-t"}
+                 split(/\s+/, RT->Config->Get('SendmailArguments'));
+         } elsif ( $mail_command eq 'testfile' ) {
+             unless ($Mail::Mailer::testfile::config{outfile}) {
+                 $Mail::Mailer::testfile::config{outfile} = File::Temp->new;
+                 $RT::Logger->info("Storing outgoing emails in $Mail::Mailer::testfile::config{outfile}");
+             }
+         } else {
+             push @mailer_args, RT->Config->Get('MailParams');
          }
-     }
-     return undef unless $id;
  
-     $RT::Logger->debug("Found a ticket ID. It's $id");
-     return $id;
+         unless ( $args{'Entity'}->send( @mailer_args ) ) {
+             $RT::Logger->crit( "$msgid: Could not send mail." );
+             if ( $TicketObj ) {
+                 _RecordSendEmailFailure( $TicketObj );
+             }
+             return 0;
+         }
+     }
+     return 1;
  }
  
- sub AddSubjectTag {
-     my $subject = shift;
-     my $ticket  = shift;
-     unless ( ref $ticket ) {
-         my $tmp = RT::Ticket->new( RT->SystemUser );
-         $tmp->Load( $ticket );
-         $ticket = $tmp;
-     }
-     my $id = $ticket->id;
-     my $queue_tag = $ticket->QueueObj->SubjectTag;
+ =head3 PrepareEmailUsingTemplate Template => '', Arguments => {}
  
-     my $tag_re = RT->Config->Get('EmailSubjectTagRegex');
-     unless ( $tag_re ) {
-         my $tag = $queue_tag || RT->Config->Get('rtname');
-         $tag_re = qr/\Q$tag\E/;
-     } elsif ( $queue_tag ) {
-         $tag_re = qr/$tag_re|\Q$queue_tag\E/;
-     }
-     return $subject if $subject =~ /\[$tag_re\s+#$id\]/;
+ Loads a template. Parses it using arguments if it's not empty.
+ Returns a tuple (L<RT::Template> object, error message).
  
-     $subject =~ s/(\r\n|\n|\s)/ /g;
-     chomp $subject;
-     return "[". ($queue_tag || RT->Config->Get('rtname')) ." #$id] $subject";
- }
+ Note that even if a template object is returned MIMEObj method
+ may return undef for empty templates.
  
+ =cut
  
- =head2 Gateway ARGSREF
+ sub PrepareEmailUsingTemplate {
+     my %args = (
+         Template => '',
+         Arguments => {},
+         @_
+     );
  
+     my $template = RT::Template->new( RT->SystemUser );
+     $template->LoadGlobalTemplate( $args{'Template'} );
+     unless ( $template->id ) {
+         return (undef, "Couldn't load template '". $args{'Template'} ."'");
+     }
+     return $template if $template->IsEmpty;
  
- Takes parameters:
+     my ($status, $msg) = $template->Parse( %{ $args{'Arguments'} } );
+     return (undef, $msg) unless $status;
  
-     action
-     queue
-     message
+     return $template;
+ }
  
+ =head3 SendEmailUsingTemplate Template => '', Arguments => {}, From => CorrespondAddress, To => '', Cc => '', Bcc => ''
  
- This performs all the "guts" of the mail rt-mailgate program, and is
- designed to be called from the web interface with a message, user
- object, and so on.
+ Sends email using a template, takes name of template, arguments for it and recipients.
  
- Can also take an optional 'ticket' parameter; this ticket id overrides
- any ticket id found in the subject.
+ =cut
  
- Returns:
+ sub SendEmailUsingTemplate {
+     my %args = (
+         Template => '',
+         Arguments => {},
+         To => undef,
+         Cc => undef,
+         Bcc => undef,
+         From => RT->Config->Get('CorrespondAddress'),
+         InReplyTo => undef,
+         ExtraHeaders => {},
+         @_
+     );
  
-     An array of:
+     my ($template, $msg) = PrepareEmailUsingTemplate( %args );
+     return (0, $msg) unless $template;
  
-     (status code, message, optional ticket object)
+     my $mail = $template->MIMEObj;
+     unless ( $mail ) {
+         $RT::Logger->info("Message is not sent as template #". $template->id ." is empty");
+         return -1;
+     }
  
-     status code is a numeric value.
+     $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ $_ } ) )
+         foreach grep defined $args{$_}, qw(To Cc Bcc From);
  
-       for temporary failures, the status code should be -75
+     $mail->head->replace( $_ => Encode::encode( "UTF-8", $args{ExtraHeaders}{$_} ) )
+         foreach keys %{ $args{ExtraHeaders} };
  
-       for permanent failures which are handled by RT, the status code
-       should be 0
+     SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
  
-       for succces, the status code should be 1
+     return SendEmail( Entity => $mail );
+ }
  
+ =head3 GetForwardFrom Ticket => undef, Transaction => undef
  
+ Resolve the From field to use in forward mail
  
  =cut
  

-----------------------------------------------------------------------


More information about the rt-commit mailing list