%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2026 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>

RT::Interface::Web::RedirectBoostedRequest();

use Archive::Zip;
my $zip = Archive::Zip->new();
# $UNICODE only sets the 0x0800 bitFlag (UTF-8 filenames); the bytes still
# need to be UTF-8 encoded by us — see the Encode::encode call below.
local $Archive::Zip::UNICODE = 1;

my $max_count = RT->Config->Get('MaxBulkAttachmentCount');
my $max_total = RT->Config->Get('MaxBulkAttachmentTotalSize');

@ids = List::MoreUtils::uniq grep { /\A\d+\z/ } @ids;

if ( $max_count && @ids > $max_count ) {
    RT->Logger->warning(
        "Bulk attachment download rejected: requested " . scalar(@ids) . " attachments, limit is $max_count"
    );
    Abort(
        loc( "Too many attachments selected (maximum is [_1]).", $max_count ),
        Code => HTTP::Status::HTTP_REQUEST_ENTITY_TOO_LARGE,
    );
}

my $total_size = 0;
for my $id (@ids) {
    my $attachment = RT::Attachment->new( $session{CurrentUser} );
    $attachment->Load($id);
    if ( $attachment->Id && $attachment->Filename && $attachment->CurrentUserCanSee ) {
        if ($max_total) {
            $total_size += $attachment->ContentLength;
            if ( $total_size > $max_total ) {
                RT->Logger->warning(
                    "Bulk attachment download rejected: total size exceeds $max_total bytes"
                );
                Abort(
                    loc( "Selected attachments exceed the maximum total size of [_1] bytes.", $max_total ),
                    Code => HTTP::Status::HTTP_REQUEST_ENTITY_TOO_LARGE,
                );
            }
        }

        # Defense-in-depth: legacy rows may pre-date RT::Util::sanitize_filename being applied on Create.
        my $entry = RT::Util::sanitize_filename( $attachment->Filename ) // 'unnamed';
        $zip->addString( $attachment->OriginalContent, Encode::encode( 'UTF-8', $entry ) );
    }
    else {
        RT->Logger->warning("Invalid attachment #$id");
    }
}

$r->headers_out->{'Content-Disposition'} = "attachment; filename=rt-attachments.zip";
$r->headers_out->{'X-Content-Type-Options'} = 'nosniff' if RT->Config->Get('StrictContentTypes');
$r->content_type('application/zip');
$m->comp('/Elements/HttpResponseHeaders', MaxAgeSeconds => 24 * 3600);

$m->clear_buffer();

my $content = '';
open my $fh, '>', \$content;
$zip->writeToFileHandle($fh);
close $fh;
$m->out($content);
$m->abort;
</%perl>

<%ARGS>
@ids => ()
</%ARGS>

<%attr>
AutoFlush => 0
</%attr>
