Files
scripts/Perl OTRS/Kernel/System/Web/UploadCache/FS.pm
2024-10-14 00:08:40 +02:00

486 lines
12 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::Web::UploadCache::FS;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Main',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{TempDir} = $Kernel::OM->Get('Kernel::Config')->Get('TempDir') . '/upload_cache';
if ( !-d $Self->{TempDir} ) {
mkdir $Self->{TempDir};
}
return $Self;
}
sub FormIDCreate {
my ( $Self, %Param ) = @_;
# return requested form id
return time() . '.' . rand(12341241);
}
sub FormIDRemove {
my ( $Self, %Param ) = @_;
if ( !$Param{FormID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need FormID!'
);
return;
}
return if !$Self->_FormIDValidate( $Param{FormID} );
my $Directory = $Self->{TempDir} . '/' . $Param{FormID};
if ( !-d $Directory ) {
return 1;
}
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my @List = $MainObject->DirectoryRead(
Directory => $Directory,
Filter => "*",
);
my @Data;
for my $File (@List) {
$MainObject->FileDelete(
Location => $File,
);
}
if ( !rmdir($Directory) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't remove: $Directory: $!!",
);
}
return 1;
}
sub FormIDAddFile {
my ( $Self, %Param ) = @_;
for (qw(FormID Filename ContentType)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
return if !$Self->_FormIDValidate( $Param{FormID} );
$Param{Content} = '' if !defined( $Param{Content} );
# create content id
my $ContentID = $Param{ContentID};
my $Disposition = $Param{Disposition} || '';
if ( !$ContentID && lc $Disposition eq 'inline' ) {
my $Random = rand 999999;
my $FQDN = $Kernel::OM->Get('Kernel::Config')->Get('FQDN');
$ContentID = "$Disposition$Random.$Param{FormID}\@$FQDN";
}
# create cache subdirectory if not exist
my $Directory = $Self->{TempDir} . '/' . $Param{FormID};
if ( !-d $Directory ) {
# Create directory. This could fail if another process creates the
# same directory, so don't use the return value.
File::Path::mkpath( $Directory, 0, 0770 ); ## no critic
if ( !-d $Directory ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't create directory '$Directory': $!",
);
return;
}
}
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
# files must readable for creator
return if !$MainObject->FileWrite(
Directory => $Directory,
Filename => "$Param{Filename}",
Content => \$Param{Content},
Mode => 'binmode',
Permission => '640',
NoReplace => 1,
);
return if !$MainObject->FileWrite(
Directory => $Directory,
Filename => "$Param{Filename}.ContentType",
Content => \$Param{ContentType},
Mode => 'binmode',
Permission => '640',
NoReplace => 1,
);
return if !$MainObject->FileWrite(
Directory => $Directory,
Filename => "$Param{Filename}.ContentID",
Content => \$ContentID,
Mode => 'binmode',
Permission => '640',
NoReplace => 1,
);
return if !$MainObject->FileWrite(
Directory => $Directory,
Filename => "$Param{Filename}.Disposition",
Content => \$Disposition,
Mode => 'binmode',
Permission => '644',
NoReplace => 1,
);
return 1;
}
sub FormIDRemoveFile {
my ( $Self, %Param ) = @_;
for (qw(FormID FileID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
return if !$Self->_FormIDValidate( $Param{FormID} );
my @Index = @{ $Self->FormIDGetAllFilesMeta(%Param) };
# finish if files have been already removed by other process
return if !@Index;
my $ID = $Param{FileID} - 1;
my %File = %{ $Index[$ID] };
my $Directory = $Self->{TempDir} . '/' . $Param{FormID};
if ( !-d $Directory ) {
return 1;
}
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
$MainObject->FileDelete(
Directory => $Directory,
Filename => "$File{Filename}",
NoReplace => 1,
);
$MainObject->FileDelete(
Directory => $Directory,
Filename => "$File{Filename}.ContentType",
NoReplace => 1,
);
$MainObject->FileDelete(
Directory => $Directory,
Filename => "$File{Filename}.ContentID",
NoReplace => 1,
);
$MainObject->FileDelete(
Directory => $Directory,
Filename => "$File{Filename}.Disposition",
NoReplace => 1,
);
return 1;
}
sub FormIDGetAllFilesData {
my ( $Self, %Param ) = @_;
if ( !$Param{FormID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need FormID!'
);
return;
}
my @Data;
return \@Data if !$Self->_FormIDValidate( $Param{FormID} );
my $Directory = $Self->{TempDir} . '/' . $Param{FormID};
if ( !-d $Directory ) {
return \@Data;
}
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my @List = $MainObject->DirectoryRead(
Directory => $Directory,
Filter => "*",
);
my $Counter = 0;
FILE:
for my $File (@List) {
# ignore meta files
next FILE if $File =~ /\.ContentType$/;
next FILE if $File =~ /\.ContentID$/;
next FILE if $File =~ /\.Disposition$/;
$Counter++;
my $FileSize = -s $File;
# human readable file size
if ( defined $FileSize ) {
# remove meta data in files
if ( $FileSize > 30 ) {
$FileSize = $FileSize - 30;
}
}
my $Content = $MainObject->FileRead(
Location => $File,
Mode => 'binmode', # optional - binmode|utf8
);
next FILE if !$Content;
my $ContentType = $MainObject->FileRead(
Location => "$File.ContentType",
Mode => 'binmode', # optional - binmode|utf8
);
next FILE if !$ContentType;
my $ContentID = $MainObject->FileRead(
Location => "$File.ContentID",
Mode => 'binmode', # optional - binmode|utf8
);
next FILE if !$ContentID;
# verify if content id is empty, set to undef
if ( !${$ContentID} ) {
${$ContentID} = undef;
}
my $Disposition = $MainObject->FileRead(
Location => "$File.Disposition",
Mode => 'binmode', # optional - binmode|utf8
);
next FILE if !$Disposition;
# strip filename
$File =~ s/^.*\/(.+?)$/$1/;
push(
@Data,
{
Content => ${$Content},
ContentID => ${$ContentID},
ContentType => ${$ContentType},
Filename => $File,
Filesize => $FileSize,
FileID => $Counter,
Disposition => ${$Disposition},
},
);
}
return \@Data;
}
sub FormIDGetAllFilesMeta {
my ( $Self, %Param ) = @_;
if ( !$Param{FormID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need FormID!'
);
return;
}
my @Data;
return \@Data if !$Self->_FormIDValidate( $Param{FormID} );
my $Directory = $Self->{TempDir} . '/' . $Param{FormID};
if ( !-d $Directory ) {
return \@Data;
}
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my @List = $MainObject->DirectoryRead(
Directory => $Directory,
Filter => "*",
);
my $Counter = 0;
FILE:
for my $File (@List) {
# ignore meta files
next FILE if $File =~ /\.ContentType$/;
next FILE if $File =~ /\.ContentID$/;
next FILE if $File =~ /\.Disposition$/;
$Counter++;
my $FileSize = -s $File;
# human readable file size
if ( defined $FileSize ) {
# remove meta data in files
if ( $FileSize > 30 ) {
$FileSize = $FileSize - 30;
}
}
my $ContentType = $MainObject->FileRead(
Location => "$File.ContentType",
Mode => 'binmode', # optional - binmode|utf8
);
next FILE if !$ContentType;
my $ContentID = $MainObject->FileRead(
Location => "$File.ContentID",
Mode => 'binmode', # optional - binmode|utf8
);
next FILE if !$ContentID;
# verify if content id is empty, set to undef
if ( !${$ContentID} ) {
${$ContentID} = undef;
}
my $Disposition = $MainObject->FileRead(
Location => "$File.Disposition",
Mode => 'binmode', # optional - binmode|utf8
);
next FILE if !$Disposition;
# strip filename
$File =~ s/^.*\/(.+?)$/$1/;
push(
@Data,
{
ContentID => ${$ContentID},
ContentType => ${$ContentType},
Filename => $File,
Filesize => $FileSize,
FileID => $Counter,
Disposition => ${$Disposition},
},
);
}
return \@Data;
}
sub FormIDCleanUp {
my ( $Self, %Param ) = @_;
# get main object
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my $RetentionTime = int( time() - 86400 ); # remove subdirs older than 24h
my @List = $MainObject->DirectoryRead(
Directory => $Self->{TempDir},
Filter => '*'
);
SUBDIR:
for my $Subdir (@List) {
my $SubdirTime = $Subdir;
if ( $SubdirTime =~ /^.*\/\d+\..+$/ ) {
$SubdirTime =~ s/^.*\/(\d+?)\..+$/$1/;
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Won't delete upload cache directory $Subdir: timestamp in directory name not found! Please fix it manually.",
);
next SUBDIR;
}
if ( $RetentionTime > $SubdirTime ) {
my @Sublist = $MainObject->DirectoryRead(
Directory => $Subdir,
Filter => "*",
);
for my $File (@Sublist) {
$MainObject->FileDelete(
Location => $File,
);
}
if ( !rmdir($Subdir) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't remove: $Subdir: $!!",
);
next SUBDIR;
}
}
}
return 1;
}
sub _FormIDValidate {
my ( $Self, $FormID ) = @_;
return if !$FormID;
if ( $FormID !~ m{^ \d+ \. \d+ \. \d+ $}xms ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Invalid FormID!',
);
return;
}
return 1;
}
1;