%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2023 Best Practical Solutions, LLC
%#                                          <sales@bestpractical.com>
%#
%# (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
%# you are the copyright holder for those contributions and you grant
%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
%# 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 }}}
<%PERL>
# Find all the attachments which have parent $Parent
# For each of these attachments
foreach my $message ( @{ $Attachments->{ $Parent || 0 } || [] } ) {
    $m->comp( 'ShowMessageHeaders',
              WarnUnsigned   => $WarnUnsigned,
              Message        => $message,
              DisplayHeaders => \@DisplayHeaders,
            );

    my $name = defined $message->Filename && length $message->Filename ?  $message->Filename : '';
    # calendar files are already rendered.
    my $should_render_download = ($message->ContentLength || $name) && lc($message->ContentType) ne 'text/calendar';

    $m->callback(CallbackName => 'BeforeAttachment', ARGSRef => \%ARGS, Object => $Object, Transaction => $Transaction, Attachment => $message, Name => $name, ShouldRenderDownload => \$should_render_download);

    if ($should_render_download) {
</%PERL>
<div class="downloadattachment">
% if (my $url = RT->System->ExternalStorageURLFor($message)) {
<a href="<% $url %>"
% } else {
<a href="<% $AttachmentPath %>/<% $Transaction->Id %>/<% $message->Id %>/<% $name | un %>" target="_blank"
% }
% if ( length $name ) {  # download link with filename
% my $download_alt = loc( 'Download [_1] [_2]', $message->ContentType, $message->FriendlyContentLength );
alt="<% $download_alt %>" data-toggle="tooltip" data-placement="bottom" data-original-title="<% $download_alt %>">
  <span class="fas fa-paperclip fa-2x"></span>
  <span class="downloadfilename"><% $name %></span>
</a>
% }
% else {  # view source and view source headers, without filename or size
% my $view_source_alt = loc( 'View source' );
>
  <span class="far fa-file fa-2x" alt="<% $view_source_alt %>" data-toggle="tooltip" data-placement="bottom" data-original-title="<% $view_source_alt %>"></span>
</a>
% if ( $DownloadableHeaders && ! length $name && $message->ContentType =~ /text/  ) {
% my $download_with_headers_alt = loc('View source with headers');
<a href="<% $AttachmentPath %>/WithHeaders/<% $message->Id %>" target="_blank">
  <span class="fa-stack" alt="<% $download_with_headers_alt %>" data-toggle="tooltip" data-placement="bottom" data-original-title="<% $download_with_headers_alt %>">
    <i class="far fa-file fa-stack-2x"></i>
    <i class="fas fa-plus fa-stack-1x"></i>
  </span>
</a>
% }
% }
% $m->callback(CallbackName => 'AfterDownloadLinks', ARGSRef => \%ARGS, Object => $Object, Transaction => $Transaction, Attachment => $message);
<br />
</div>
%   }
%# If there is sub-messages, open a dedicated div
% if ( $Attachments->{ $message->id } ) {
<div class="messageattachments">
% } else {
<div class="messagebody">
% }
<%PERL>

$render_attachment->( $message );

$m->comp(
    $m->current_comp,
    %ARGS,
    Parent    => $message->id,
    ParentObj => $message,

    displayed_inline => $displayed_inline,
);

</%PERL>
</div>
% }
<%ARGS>
$Transaction
$Object => $Transaction->Object
$ShowHeaders => 0
$DownloadableHeaders => 1
$AttachmentPath => undef
$Attachments => {}
$AttachmentContent => {}
$Parent => 0
$ParentObj => undef
$WarnUnsigned => 0

# Keep track of CID images we display inline
$displayed_inline => {}
</%ARGS>
<%INIT>
my @DisplayHeaders=qw(_all);
if ( $Transaction->Type =~ /EmailRecord$/ ) {
    @DisplayHeaders = qw(To Cc Bcc);
}

# If the transaction has anything attached to it at all
elsif (!$ShowHeaders)  {
    @DisplayHeaders = qw(To From RT-Send-Cc Cc Bcc RT-Attach Date Subject);
    push @DisplayHeaders, 'RT-Send-Bcc' if RT->Config->Get('ShowBccHeader');
}

$m->callback(CallbackName => 'MassageDisplayHeaders', DisplayHeaders => \@DisplayHeaders, Transaction => $Transaction, ShowHeaders => $ShowHeaders);

my $render_attachment = sub {
    my $message = shift;
    my $name = defined $message->Filename && length $message->Filename ?  $message->Filename : '';

    my $content_type = lc $message->ContentType;

    # if it has a content-disposition: attachment, don't show inline
    my $disposition = $message->GetHeader('Content-Disposition');

    if ( $disposition && $disposition =~ /^\s*attachment/i ) {
        $disposition = 'attachment';
    } else {
        $disposition = 'inline';
    }

    # If it's text
    if ( $content_type =~ m{^(text|message)/} ) {
        my $max_size = RT->Config->Get( 'MaxInlineBody', $session{'CurrentUser'} );

        # provide a clear download link for meeting invitations
        if ( $content_type eq 'text/calendar' ) {
            # A named attachment will already have a download button
            $m->out('<div class="downloadattachment">');
            if (my $url = RT->System->ExternalStorageURLFor($message)) {
                $m->out('<a href="' . $url . '"');
            }
            else {
                $m->out('<a href="' . $AttachmentPath . '/' . $Transaction->Id . '/' . $message->Id . '/meeting.ics' . '" target="_blank"');
            }
            my $download_alt = loc( 'Download Meeting Invitation' );
            $m->out('alt="' . $download_alt . '" data-toggle="tooltip" data-placement="bottom" data-original-title="' . $download_alt . '">');
            $m->out('<span class="far fa-calendar-alt fa-2x"></span> ');
            $m->out('<span class="downloadfilename">' . ( $name || 'meeting.ics' ) . '</span>');
            $m->out('</a>');
            $m->out('</div>');
        }

        if ( $disposition ne 'inline' ) {
            $m->out('<p>'. loc( 'Message body is not shown because sender requested not to inline it.' ) .'</p>');
            return;
        }
        elsif ( length $name && RT->Config->Get('SuppressInlineTextFiles', $session{'CurrentUser'} ) ) {
            $m->out('<p>'. loc( 'Text file is not shown because it is disabled in preferences.' ) .'</p>');
            return;
        }
        elsif ( $max_size && $message->ContentLength > $max_size ) {
            $m->out('<p>'. loc( 'Message body is not shown because it is too large.' ) .'</p>');
            return;
        }

        my $content;
        # If we've cached the content, use it from there
        if (my $x = $AttachmentContent->{ $Transaction->id }->{$message->id}) {
            $content = $x->Content;
        }
        else {
            $content = $message->Content;
        }

        if ( $content_type eq 'text/calendar' ) {
            # iCalendar is supposed to be UTF-8 encoded.
            my $calendar_info = ParseCalendarData( RawData => Encode::decode('UTF-8', $content, Encode::FB_PERLQQ) );

            if ( $calendar_info ) {
                $m->comp( '/Elements/ShowCalendarInvitation', invitation_info => $calendar_info );
            }

            return;
        }
        elsif (

            # it's a toplevel object
            !$ParentObj

            # or its parent isn't a multipart alternative
            || ( $ParentObj->ContentType !~ m{^multipart/(?:alternative|related)$}i )

            # or it's of our prefered alterative type
            || (
                (
                    RT->Config->Get('PreferRichText', $session{CurrentUser})
                    && ( $content_type =~ m{^text/(?:html|enriched)$} )
                )
                || ( !RT->Config->Get('PreferRichText', $session{CurrentUser})
                    && ( $content_type !~ m{^text/(?:html|enriched)$} )
                )
            )
        ) {

            $RT::Logger->debug(
                "Rendering attachment #". $message->id
                ." of '$content_type' type"
            );

            my $skip_quote_folding;
            $m->callback(
                CallbackName     => 'ModifyContent',
                ARGSRef          => \%ARGS,
                Object           => $Object,
                Transaction      => $Transaction,
                Content          => \$content,
                SkipQuoteFolding => \$skip_quote_folding,
            );

            # if it's a text/html clean the body and show it
            if ( $content_type eq 'text/html' ) {
                $content = $m->comp( '/Elements/ScrubHTML', Content => $content );

                if (RT->Config->Get('ShowTransactionImages')) {
                    my @rewritten = RT::Interface::Web::RewriteInlineImages(
                        Content         => \$content,
                        Attachment      => $message,
                        # Not technically correct to search all parts of the
                        # MIME structure, but it saves having to go to the
                        # database again and is unlikely to break display.
                        Related         => [ map { @$_ } values %$Attachments ],
                        AttachmentPath  => $AttachmentPath,
                    );
                    $displayed_inline->{$_}++ for @rewritten;
                }

                $m->comp(
                    '/Elements/MakeClicky',
                    content => \$content,
                    html    => 1,
                    object  => $Object,
                );

                if ( !$skip_quote_folding && !length $name && RT->Config->Get( 'QuoteFolding', $session{CurrentUser} ) ) {

                    eval {
                        require HTML::Quoted;
                        $content = HTML::Quoted->extract($content)
                    };
                    if ($@) {
                        RT->Logger->error(
                            "HTML::Quoted couldn't process attachment #@{[$message->id]}: $@."
                          . "  This is a bug, please report it to rt-bugs\@bestpractical.com.");
                    }
                }

                $m->comp(
                    'ShowMessageStanza',
                    Message     => $content,
                    Transaction => $Transaction,
                    ContentType => 'text/html',
                );
            }

            elsif ( $content_type eq 'text/enriched' ) {
                $content = $m->comp( '/Elements/ScrubHTML', Content => $content );
                $m->out( $content );
            }

            # It's a text type we don't have special handling for
            else {
                if ( !$skip_quote_folding && !length $name && RT->Config->Get( 'QuoteFolding', $session{CurrentUser} ) ) {
                    eval {
                        require Text::Quoted;
                        Text::Quoted::set_quote_characters(undef);
                        $content = Text::Quoted::extract($content);
                    };
                    if ($@) {
                        RT->Logger->error(
                            "Text::Quoted couldn't process attachment #@{[$message->id]}: $@."
                          . "  This is a bug, please report it to rt-bugs\@bestpractical.com.");
                    }
                }

                $m->comp(
                    'ShowMessageStanza',
                    Message     => $content,
                    Transaction => $Transaction,
                    ContentType => 'text/plain',
                );
            }
        }
    }

    # if it's an image, show it as an image
    elsif ( $content_type =~ m{^image/} ) {
        if (not RT->Config->Get('ShowTransactionImages')) {
            $m->out('<p><i>'. loc( 'Image not shown because display is disabled in system configuration.' ) .'</i></p>');
            return;
        }
        elsif ( $displayed_inline->{$message->Id} ) {
            $m->out('<p><i>'. loc( 'Image displayed inline above' ) .'</i></p>');
            return;
        }
        elsif ( $disposition ne 'inline' ) {
            $m->out('<p>'. loc( 'Image not shown because sender requested not to inline it.' ) .'</p>');
            return;
        }

        my $filename = length $name ? $name : loc('(untitled)');
        my $efilename = $m->interp->apply_escapes( $filename, 'h' );

        my $url = RT->System->ExternalStorageURLFor($message)
               || $AttachmentPath .'/'. $Transaction->Id .'/'. $message->Id .'/'
                . $m->interp->apply_escapes( $filename, 'u', 'h' );

        $m->out(
            qq{<img alt="$efilename" title="$efilename" src="$url" />}
        );
    }
    elsif ( $message->ContentLength && $message->ContentLength > 0 ) {
        $m->out( '<p>' .
            loc( 'Message body not shown because it is not plain text.' ) .
            '</p>'
        );
    }
};

</%INIT>
