X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FAction%2FSendEmail.pm;h=2a7a2e3c0768cf0f9086cddef313b2863391e8ce;hb=a72a10f754f7465121d6137bb3dcee0a21ea6443;hp=8b682c118d144b16bc5dad5d15f04f5fedd1ce21;hpb=e70abd21bab68b23488f7ef1ee2e693a3b365691;p=freeside.git diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm index 8b682c118..2a7a2e3c0 100755 --- a/rt/lib/RT/Action/SendEmail.pm +++ b/rt/lib/RT/Action/SendEmail.pm @@ -1,40 +1,40 @@ # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC -# -# +# +# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC +# +# # (Except where explicitly superseded by other copyright notices) -# -# +# +# # LICENSE: -# +# # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. -# +# # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# +# +# # CONTRIBUTION SUBMISSION POLICY: -# +# # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) -# +# # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that @@ -43,7 +43,7 @@ # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. -# +# # END BPS TAGGED BLOCK }}} # Portions Copyright 2000 Tobias Brox @@ -80,13 +80,12 @@ RT::Action::SendEmail. =head2 CleanSlate -Cleans class-wide options, like L or L. +Cleans class-wide options, like L. =cut sub CleanSlate { my $self = shift; - $self->SquelchMailTo(undef); $self->AttachTickets(undef); } @@ -100,47 +99,31 @@ activated in the config. sub Commit { my $self = shift; - $self->DeferDigestRecipients() if RT->Config->Get('RecordOutgoingEmail'); + return abs $self->SendMessage( $self->TemplateObj->MIMEObj ) + unless RT->Config->Get('RecordOutgoingEmail'); + + $self->DeferDigestRecipients(); my $message = $self->TemplateObj->MIMEObj; my $orig_message; - if ( RT->Config->Get('RecordOutgoingEmail') - && RT->Config->Get('GnuPG')->{'Enable'} ) - { - - # it's hacky, but we should know if we're going to crypt things - my $attachment = $self->TransactionObj->Attachments->First; - - my %crypt; - foreach my $argument (qw(Sign Encrypt)) { - if ( $attachment - && defined $attachment->GetHeader("X-RT-$argument") ) - { - $crypt{$argument} = $attachment->GetHeader("X-RT-$argument"); - } else { - $crypt{$argument} = $self->TicketObj->QueueObj->$argument(); - } - } - if ( $crypt{'Sign'} || $crypt{'Encrypt'} ) { - $orig_message = $message->dup; - } - } + $orig_message = $message->dup if RT::Interface::Email::WillSignEncrypt( + Attachment => $self->TransactionObj->Attachments->First, + Ticket => $self->TicketObj, + ); my ($ret) = $self->SendMessage($message); - if ( $ret > 0 && RT->Config->Get('RecordOutgoingEmail') ) { - if ($orig_message) { - $message->attach( - Type => 'application/x-rt-original-message', - Disposition => 'inline', - Data => $orig_message->as_string, - ); - } - $self->RecordOutgoingMailTransaction($message); - $self->RecordDeferredRecipients(); + return abs( $ret ) if $ret <= 0; + + if ($orig_message) { + $message->attach( + Type => 'application/x-rt-original-message', + Disposition => 'inline', + Data => $orig_message->as_string, + ); } - - - return ( abs $ret ); + $self->RecordOutgoingMailTransaction($message); + $self->RecordDeferredRecipients(); + return 1; } =head2 Prepare @@ -166,8 +149,6 @@ sub Prepare { # Header $self->SetRTSpecialHeaders(); - $self->RemoveInappropriateRecipients(); - my %seen; foreach my $type (@EMAIL_RECIPIENT_HEADERS) { @{ $self->{$type} } @@ -175,6 +156,8 @@ sub Prepare { @{ $self->{$type} }; } + $self->RemoveInappropriateRecipients(); + # Go add all the Tos, Ccs and Bccs that we need to to the message to # make it happy, but only if we actually have values in those arrays. @@ -182,11 +165,11 @@ sub Prepare { for my $header (@EMAIL_RECIPIENT_HEADERS) { - $self->SetHeader( $header, join( ', ', @{ $self->{$header} } ) ) - if ( !$MIMEObj->head->get($header) - && $self->{$header} - && @{ $self->{$header} } ); -} + $self->SetHeader( $header, join( ', ', @{ $self->{$header} } ) ) + if (!$MIMEObj->head->get($header) + && $self->{$header} + && @{ $self->{$header} } ); + } # PseudoTo (fake to headers) shouldn't get matched for message recipients. # If we don't have any 'To' header (but do have other recipients), drop in # the pseudo-to header. @@ -206,7 +189,9 @@ sub Prepare { foreach my $part ( grep !$_->is_multipart, $MIMEObj->parts_DFS ) { my $type = $part->mime_type || 'text/plain'; $type = 'text/plain' unless RT::I18N::IsTextualContentType($type); - $part->head->mime_attr( "Content-Type" => $type ); + $part->head->mime_attr( "Content-Type" => $type ); + # utf-8 here is for _FindOrGuessCharset in I18N.pm + # it's not the final charset/encoding sent $part->head->mime_attr( "Content-Type.charset" => 'utf-8' ); } @@ -347,7 +332,7 @@ sub AddAttachments { $MIMEObj->head->delete('RT-Attach-Message'); - my $attachments = RT::Attachments->new($RT::SystemUser); + my $attachments = RT::Attachments->new( RT->SystemUser ); $attachments->Limit( FIELD => 'TransactionId', VALUE => $self->TransactionObj->Id @@ -407,11 +392,20 @@ sub AddAttachment { my $attach = shift; my $MIMEObj = shift || $self->TemplateObj->MIMEObj; + # $attach->TransactionObj may not always be $self->TransactionObj + return unless $attach->Id + and $attach->TransactionObj->CurrentUserCanSee; + + # ->attach expects just the disposition type; extract it if we have the header + my $disp = ($attach->GetHeader('Content-Disposition') || '') + =~ /^\s*(inline|attachment)/i ? $1 : undef; + $MIMEObj->attach( - Type => $attach->ContentType, - Charset => $attach->OriginalEncoding, - Data => $attach->OriginalContent, - Filename => $self->MIMEEncodeString( $attach->Filename ), + Type => $attach->ContentType, + Charset => $attach->OriginalEncoding, + Data => $attach->OriginalContent, + Disposition => $disp, # a false value defaults to inline in MIME::Entity + Filename => $self->MIMEEncodeString( $attach->Filename ), 'RT-Attachment:' => $self->TicketObj->Id . "/" . $self->TransactionObj->Id . "/" . $attach->id, @@ -465,8 +459,7 @@ sub AddTicket { my $self = shift; my $tid = shift; - # XXX: we need a current user here, but who is current user? - my $attachs = RT::Attachments->new($RT::SystemUser); + my $attachs = RT::Attachments->new( $self->TransactionObj->CreatorObj ); my $txn_alias = $attachs->TransactionAlias; $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Create' ); $attachs->Limit( @@ -658,7 +651,7 @@ sub DeferDigestRecipients { $RT::Logger->debug( $self->TemplateObj->MIMEObj->head->as_string ); foreach my $rcpt ( map { $_->address } $self->AddressesFromHeader($mailfield) ) { next unless $rcpt; - my $user_obj = RT::User->new($RT::SystemUser); + my $user_obj = RT::User->new(RT->SystemUser); $user_obj->LoadByEmail($rcpt); if ( ! $user_obj->id ) { # If there's an email address in here without an associated @@ -727,26 +720,15 @@ sub RecordDeferredRecipients { return ($ret,$msg); } -=head2 SquelchMailTo [@ADDRESSES] - -Mark ADDRESSES to be removed from list of the recipients. Returns list of the addresses. -To empty list pass undefined argument. +=head2 SquelchMailTo -B that this method can be called as class method and works globaly. Don't forget to -clean this list when blocking is not required anymore, pass undef to do this. +Returns list of the addresses to squelch on this transaction. =cut -{ - my $squelch = []; - - sub SquelchMailTo { - my $self = shift; - if (@_) { - $squelch = [ grep defined, @_ ]; - } - return @$squelch; - } +sub SquelchMailTo { + my $self = shift; + return map $_->Content, $self->TransactionObj->SquelchMailTo; } =head2 RemoveInappropriateRecipients @@ -787,9 +769,9 @@ sub RemoveInappropriateRecipients { # Only send to "privileged" watchers. foreach my $type (@EMAIL_RECIPIENT_HEADERS) { foreach my $addr ( @{ $self->{$type} } ) { - my $user = RT::User->new($RT::SystemUser); + my $user = RT::User->new(RT->SystemUser); $user->LoadByEmail($addr); - push @blacklist, $addr if ( !$user->Privileged ); + push @blacklist, $addr unless $user->id && $user->Privileged; } } $RT::Logger->info( $msgid @@ -804,15 +786,15 @@ sub RemoveInappropriateRecipients { } } -# Let's grab the SquelchMailTo attribue and push those entries into the @blacklist - push @blacklist, map $_->Content, $self->TicketObj->SquelchMailTo; - push @blacklist, $self->SquelchMailTo; + # Let's grab the SquelchMailTo attributes and push those entries into the @blacklisted + push @blacklist, map $_->Content, $self->TicketObj->SquelchMailTo, $self->TransactionObj->SquelchMailTo; # Cycle through the people we're sending to and pull out anyone on the # system blacklist # Trim leading and trailing spaces. - @blacklist = map { RT::User->CanonicalizeEmailAddress( $_->address ) } Email::Address->parse(join(', ', grep {defined} @blacklist)); + @blacklist = map { RT::User->CanonicalizeEmailAddress( $_->address ) } + Email::Address->parse( join ', ', grep defined, @blacklist ); foreach my $type (@EMAIL_RECIPIENT_HEADERS) { my @addrs; @@ -824,12 +806,20 @@ sub RemoveInappropriateRecipients { $RT::Logger->info( $msgid . "$addr appears to point to this RT instance. Skipping" ); next; } - if ( grep /^\Q$addr\E$/, @blacklist ) { + if ( grep $addr eq $_, @blacklist ) { $RT::Logger->info( $msgid . "$addr was blacklisted for outbound mail on this transaction. Skipping"); next; } push @addrs, $addr; } + foreach my $addr ( @{ $self->{'NoSquelch'}{$type} || [] } ) { + # never send email to itself + if ( !RT::EmailParser->CullRTAddresses($addr) ) { + $RT::Logger->info( $msgid . "$addr appears to point to this RT instance. Skipping" ); + next; + } + push @addrs, $addr; + } @{ $self->{$type} } = @addrs; } } @@ -862,36 +852,67 @@ sub SetReturnAddress { } unless ( $self->TemplateObj->MIMEObj->head->get('From') ) { - if ( RT->Config->Get('UseFriendlyFromLine') ) { - my $friendly_name = $args{friendly_name}; + $self->SetFrom( %args, From => $replyto ); + } - unless ( $friendly_name ) { - $friendly_name = $self->TransactionObj->CreatorObj->FriendlyName; - if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string - $friendly_name = $1; - } - } + unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) { + $self->SetHeader( 'Reply-To', "$replyto" ); + } - $friendly_name =~ s/"/\\"/g; - $self->SetHeader( - 'From', - sprintf( - RT->Config->Get('FriendlyFromLineFormat'), - $self->MIMEEncodeString( - $friendly_name, RT->Config->Get('EmailOutputEncoding') - ), - $replyto +} + +=head2 SetFrom ( From => emailaddress ) + +Set the From: address for outgoing email + +=cut + +sub SetFrom { + my $self = shift; + my %args = @_; + + if ( RT->Config->Get('UseFriendlyFromLine') ) { + my $friendly_name = $self->GetFriendlyName(%args); + $self->SetHeader( + 'From', + sprintf( + RT->Config->Get('FriendlyFromLineFormat'), + $self->MIMEEncodeString( + $friendly_name, RT->Config->Get('EmailOutputEncoding') ), - ); - } else { - $self->SetHeader( 'From', $replyto ); - } + $args{From} + ), + ); + } else { + $self->SetHeader( 'From', $args{From} ); } +} - unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) { - $self->SetHeader( 'Reply-To', "$replyto" ); +=head2 GetFriendlyName + +Calculate the proper Friendly Name based on the creator of the transaction + +=cut + +sub GetFriendlyName { + my $self = shift; + my %args = ( + is_comment => 0, + friendly_name => '', + @_ + ); + my $friendly_name = $args{friendly_name}; + + unless ( $friendly_name ) { + $friendly_name = $self->TransactionObj->CreatorObj->FriendlyName; + if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string + $friendly_name = $1; + } } + $friendly_name =~ s/"/\\"/g; + return $friendly_name; + } =head2 SetHeader FIELD, VALUE @@ -929,8 +950,14 @@ sub SetSubject { return (); } - my $message = $self->TransactionObj->Attachments; + # don't use Transaction->Attachments because it caches + # and anything which later calls ->Attachments will be hurt + # by our RowsPerPage() call. caching is hard. + my $message = RT::Attachments->new( $self->CurrentUser ); + $message->Limit( FIELD => 'TransactionId', VALUE => $self->TransactionObj->id); + $message->OrderBy( FIELD => 'id', ORDER => 'ASC' ); $message->RowsPerPage(1); + if ( $self->{'Subject'} ) { $subject = $self->{'Subject'}; } elsif ( my $first = $message->First ) { @@ -974,16 +1001,17 @@ Set References and In-Reply-To headers for this message. sub SetReferencesHeaders { my $self = shift; - my ( @in_reply_to, @references, @msgid ); - if ( my $top = $self->TransactionObj->Message->First ) { - @in_reply_to = split( /\s+/m, $top->GetHeader('In-Reply-To') || '' ); - @references = split( /\s+/m, $top->GetHeader('References') || '' ); - @msgid = split( /\s+/m, $top->GetHeader('Message-ID') || '' ); - } else { + my $top = $self->TransactionObj->Message->First; + unless ( $top ) { + $self->SetHeader( References => $self->PseudoReference ); return (undef); } + my @in_reply_to = split( /\s+/m, $top->GetHeader('In-Reply-To') || '' ); + my @references = split( /\s+/m, $top->GetHeader('References') || '' ); + my @msgid = split( /\s+/m, $top->GetHeader('Message-ID') || '' ); + # There are two main cases -- this transaction was created with # the RT Web UI, and hence we want to *not* append its Message-ID # to the References and In-Reply-To. OR it came from an outside @@ -1089,10 +1117,7 @@ sub MIMEEncodeString { return RT::Interface::Email::EncodeToMIME( String => $_[0], Charset => $_[1] ); } -eval "require RT::Action::SendEmail_Vendor"; -die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm} ); -eval "require RT::Action::SendEmail_Local"; -die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm} ); +RT::Base->_ImportOverlays(); 1;