From 3509804da85ab70cc8b1e1fa4c6652024efbe971 Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Fri, 5 Nov 2021 16:05:48 -0300 Subject: [PATCH] Bug 29330: Restore handling of serialized MIME messages in message_queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch changes how multipart MIME messages are handled on the message_queue table. The email, with the required attachments, is now generated using Koha::Email, and serialized using Koha::Email->as_string. This bug also adds Koha::Email->new_from_string which is used to read that data from the DB, and produce a Koha::Email object, that can be further augmented/modified using regular Koha::Email methods. This implementation should be considered a middle ground, with backportability in mind. higher-level methods should encapsulate setting the default headers and addresses, to clean the area a bit further. Preparation: - You need a valid SMTP configuration in koha-conf.xml. If you use Gmail you can generate an 'app password' and set things like this: smtp.gmail.com 587 5 STARTTLS youraddress@gmail.com youpassword 1 - Set KohaAdminAddress to your address. To test: 1. Pick a patron. Make sure it doesn't have any email address (Acevedo?) 2. Set an overdue notice trigger for its category 3. Check something out, with due date in the past to force an overdue 4. Run: $ kshell k$ misc/cronjobs/overdue_notices.pl -v k$ exit $ koha-mysql kohadev > SELECT * FROM message_queue WHERE borrowernumber=the_borrowernumber; => SUCCESS: A notice has been created 5. Run: $ kshell k$ misc/cronjobs/process_message_queue.pl --verbose => SUCCESS: SMTP is ok => Email is sent => FAIL: Your inbox shows an email with weird content 6. Apply this patches 7. Run: $ koha-mysql kohadev > DELETE FROM message_queue; 8. Repeat 4 and 5 => SUCCESS: You got an email with an attachment! => SUCCESS: The attachment contains an email that couldn't be delivered! 9. Try all the things that enqueue messages :-D => SUCCESS: No behavior change 10. Sign off :-D Signed-off-by: Tomas Cohen Arazi Signed-off-by: David Nind Signed-off-by: Victor Grousset/tuxayo «Try all the things that enqueue messages»: I only testes an overdue notice email. --- C4/Letters.pm | 111 +++++++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 52 deletions(-) diff --git a/C4/Letters.pm b/C4/Letters.pm index f3cabc2856..b68a733182 100644 --- a/C4/Letters.pm +++ b/C4/Letters.pm @@ -19,7 +19,6 @@ package C4::Letters; use Modern::Perl; -use MIME::Lite; use Carp qw( carp croak ); use Template; use Module::Load::Conditional qw( can_load ); @@ -954,8 +953,7 @@ sub EnqueueLetter { if ( $params->{'attachments'} ) { $params->{'letter'} = _add_attachments( { letter => $params->{'letter'}, - attachments => $params->{'attachments'}, - message => MIME::Lite->new( Type => 'multipart/mixed' ), + attachments => $params->{'attachments'} } ); } @@ -1219,45 +1217,47 @@ sub ResendMessage { =head2 _add_attachements - named parameters: - letter - the standard letter hashref - attachments - listref of attachments. each attachment is a hashref of: - type - the mime type, like 'text/plain' - content - the actual attachment - filename - the name of the attachment. - message - a MIME::Lite object to attach these to. + _add_attachments({ letter => $letter, attachments => $attachments }); - returns your letter object, with the content updated. +Given a I parameter, this method picks the I of it, +and generates a MIME email with I as the the body. + +It then attaches the passed I using Koha::Email's I +method. + +It returns the original I<$letter> object, with the content replaced by the +string representation of the MIME object, and the content-ype is updated to +B for later handling. =cut sub _add_attachments { my $params = shift; - my $letter = $params->{'letter'}; - my $attachments = $params->{'attachments'}; + my $letter = $params->{letter}; + my $attachments = $params->{attachments}; return $letter unless @$attachments; - my $message = $params->{'message'}; - - # First, we have to put the body in as the first attachment - $message->attach( - Type => $letter->{'content-type'} || 'TEXT', - Data => $letter->{'is_html'} - ? _wrap_html($letter->{'content'}, $letter->{'title'}) - : $letter->{'content'}, - ); + + my $message = Koha::Email->new; + + if ( $letter->{is_html} ) { + $message->html_body( _wrap_html( $letter->{content}, $letter->{title} ) ); + } + else { + $message->text_body( $letter->{content} ); + } foreach my $attachment ( @$attachments ) { $message->attach( - Type => $attachment->{'type'}, - Data => $attachment->{'content'}, - Filename => $attachment->{'filename'}, + Encode::encode( "UTF-8", $attachment->{content} ), + content_type => 'application/octet-stream', + name => $attachment->{filename}, + disposition => 'attachment', ); } - # we're forcing list context here to get the header, not the count back from grep. - ( $letter->{'content-type'} ) = grep( /^Content-Type:/, split( /\n/, $params->{'message'}->header_as_string ) ); - $letter->{'content-type'} =~ s/^Content-Type:\s+//; - $letter->{'content'} = $message->body_as_string; + + $letter->{'content-type'} = 'MIME'; + $letter->{content} = $message->as_string; return $letter; @@ -1389,21 +1389,37 @@ sub _send_message_by_email { ); return; }; - my $email = try { - Koha::Email->create( - { - to => $to_address, - ( - C4::Context->preference('NoticeBcc') - ? ( bcc => C4::Context->preference('NoticeBcc') ) - : () - ), - from => $from_address, - reply_to => $message->{'reply_address'} || $branch_replyto, - sender => $branch_returnpath, - subject => "" . $message->{subject} + my $email; + + try { + + my $params = { + to => $to_address, + ( + C4::Context->preference('NoticeBcc') + ? ( bcc => C4::Context->preference('NoticeBcc') ) + : () + ), + from => $from_address, + reply_to => $message->{'reply_address'} || $branch_replyto, + sender => $branch_returnpath, + subject => "" . $message->{subject} + }; + + if ( $message->{'content_type'} && $message->{'content_type'} eq 'MIME' ) { + + # The message has been previously composed as a valid MIME object + # and serialized as a string on the DB + $email = Koha::Email->new_from_string($content); + $email->create($params); + } else { + $email = Koha::Email->create($params); + if ($is_html) { + $email->html_body( _wrap_html( $content, $subject ) ); + } else { + $email->text_body($content); } - ); + } } catch { if ( ref($_) eq 'Koha::Exceptions::BadParameter' ) { @@ -1427,15 +1443,6 @@ sub _send_message_by_email { }; return unless $email; - if ( $is_html ) { - $email->html_body( - _wrap_html( $content, $subject ) - ); - } - else { - $email->text_body( $content ); - } - my $smtp_server; if ( $library ) { $smtp_server = $library->smtp_server; -- 2.33.1