This commit is contained in:
2024-10-14 00:08:40 +02:00
parent dbfba56f66
commit 1462d52e13
4572 changed files with 2658864 additions and 0 deletions

View File

@@ -0,0 +1,505 @@
# --
# 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::VirtualFS;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DB',
'Kernel::System::Log',
'Kernel::System::Main',
);
=head1 NAME
Kernel::System::VirtualFS - virtual filesystem lib
=head1 DESCRIPTION
All virtual filesystem functions.
=head1 PUBLIC INTERFACE
=head2 new()
Don't use the constructor directly, use the ObjectManager instead:
my $VirtualFSObject = $Kernel::OM->Get('Kernel::System::VirtualFS');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# load backend
$Self->{BackendDefault} = $Kernel::OM->Get('Kernel::Config')->Get('VirtualFS::Backend')
|| 'Kernel::System::VirtualFS::DB';
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require( $Self->{BackendDefault} ) ) {
return;
}
$Self->{Backend}->{ $Self->{BackendDefault} } = $Self->{BackendDefault}->new();
return $Self;
}
=head2 Read()
read a file from virtual file system
my %File = $VirtualFSObject->Read(
Filename => '/Object/some/name.txt',
Mode => 'utf8',
# optional
DisableWarnings => 1,
);
returns
my %File = (
Content => $ContentSCALAR,
# preferences data
Preferences => {
# generated automatically
FilesizeRaw => 12345,
# optional
ContentType => 'text/plain',
ContentID => '<some_id@example.com>',
ContentAlternative => 1,
SomeCustomParams => 'with our own value',
},
);
=cut
sub Read {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Filename Mode)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# lookup
my ( $FileID, $BackendKey, $Backend ) = $Self->_FileLookup( $Param{Filename} );
if ( !$BackendKey ) {
if ( !$Param{DisableWarnings} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No such file '$Param{Filename}'!",
);
}
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# get preferences
my %Preferences;
return if !$DBObject->Prepare(
SQL => 'SELECT preferences_key, preferences_value FROM '
. 'virtual_fs_preferences WHERE virtual_fs_id = ?',
Bind => [ \$FileID ],
);
while ( my @Row = $DBObject->FetchrowArray() ) {
$Preferences{ $Row[0] } = $Row[1];
}
# load backend (if not default)
if ( !$Self->{Backend}->{$Backend} ) {
return if !$Kernel::OM->Get('Kernel::System::Main')->Require($Backend);
$Self->{Backend}->{$Backend} = $Backend->new();
return if !$Self->{Backend}->{$Backend};
}
# get file
my $Content = $Self->{Backend}->{$Backend}->Read(
%Param,
BackendKey => $BackendKey,
);
return if !$Content;
return (
Preferences => \%Preferences,
Content => $Content,
);
}
=head2 Write()
write a file to virtual file system
my $Success = $VirtualFSObject->Write(
Content => \$Content,
Filename => '/Object/SomeFileName.txt',
Mode => 'binary' # (binary|utf8)
# optional, preferences data
Preferences => {
ContentType => 'text/plain',
ContentID => '<some_id@example.com>',
ContentAlternative => 1,
SomeCustomParams => 'with our own value',
},
);
=cut
sub Write {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Filename Content Mode)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# lookup
my ($FileID) = $Self->_FileLookup( $Param{Filename} );
if ($FileID) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "File already exists '$Param{Filename}'!",
);
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# insert
return if !$DBObject->Do(
SQL => 'INSERT INTO virtual_fs (filename, backend_key, backend, create_time)'
. ' VALUES ( ?, \'TMP\', ?, current_timestamp)',
Bind => [ \$Param{Filename}, \$Self->{BackendDefault} ],
);
($FileID) = $Self->_FileLookup( $Param{Filename} );
if ( !$FileID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Unable to store '$Param{Filename}'!",
);
return;
}
# size calculation
$Param{Preferences}->{FilesizeRaw} = bytes::length( ${ $Param{Content} } );
# insert preferences
for my $Key ( sort keys %{ $Param{Preferences} } ) {
return if !$DBObject->Do(
SQL => 'INSERT INTO virtual_fs_preferences '
. '(virtual_fs_id, preferences_key, preferences_value) VALUES ( ?, ?, ?)',
Bind => [ \$FileID, \$Key, \$Param{Preferences}->{$Key} ],
);
}
# store file
my $BackendKey = $Self->{Backend}->{ $Self->{BackendDefault} }->Write(%Param);
return if !$BackendKey;
# update backend key
return if !$DBObject->Do(
SQL => 'UPDATE virtual_fs SET backend_key = ? WHERE id = ?',
Bind => [ \$BackendKey, \$FileID ],
);
return 1;
}
=head2 Delete()
delete a file from virtual file system
my $Success = $VirtualFSObject->Delete(
Filename => '/Object/SomeFileName.txt',
# optional
DisableWarnings => 1,
);
=cut
sub Delete {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Filename)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# lookup
my ( $FileID, $BackendKey, $Backend ) = $Self->_FileLookup( $Param{Filename} );
if ( !$FileID ) {
if ( !$Param{DisableWarnings} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No such file '$Param{Filename}'!",
);
}
return;
}
# load backend (if not default)
if ( !$Self->{Backend}->{$Backend} ) {
return if !$Kernel::OM->Get('Kernel::System::Main')->Require($Backend);
$Self->{Backend}->{$Backend} = $Backend->new();
return if !$Self->{Backend}->{$Backend};
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# delete preferences
return if !$DBObject->Do(
SQL => 'DELETE FROM virtual_fs_preferences WHERE virtual_fs_id = ?',
Bind => [ \$FileID ],
);
# delete
return if !$DBObject->Do(
SQL => 'DELETE FROM virtual_fs WHERE id = ?',
Bind => [ \$FileID ],
);
# delete file
return $Self->{Backend}->{$Backend}->Delete(
%Param,
BackendKey => $BackendKey,
);
}
=head2 Find()
find files in virtual file system
only for file name
my @List = $VirtualFSObject->Find(
Filename => '/Object/some_what/*.txt',
);
only for preferences
my @List = $VirtualFSObject->Find(
Preferences => {
ContentType => 'text/plain',
},
);
for file name and for preferences
my @List = $VirtualFSObject->Find(
Filename => '/Object/some_what/*.txt',
Preferences => {
ContentType => 'text/plain',
},
);
Returns:
my @List = (
'/Object/some/file.txt',
'/Object/my.pdf',
);
=cut
sub Find {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Filename} && !$Param{Preferences} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Filename or/and Preferences!',
);
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# get like escape string needed for some databases (e.g. oracle)
my $LikeEscapeString = $DBObject->GetDatabaseFunction('LikeEscapeString');
# prepare file name search
my $SQLResult = 'vfs.filename';
my $SQLTable = 'virtual_fs vfs ';
my $SQLWhere = '';
my @SQLBind;
if ( $Param{Filename} ) {
my $Like = $Param{Filename};
$Like =~ s/\*/%/g;
$Like = $DBObject->Quote( $Like, 'Like' );
$SQLWhere .= "vfs.filename LIKE '$Like' $LikeEscapeString";
}
# prepare preferences search
if ( $Param{Preferences} ) {
$SQLResult = 'vfs.filename, vfsp.preferences_key, vfsp.preferences_value';
$SQLTable .= ', virtual_fs_preferences vfsp';
if ($SQLWhere) {
$SQLWhere .= ' AND ';
}
$SQLWhere .= 'vfs.id = vfsp.virtual_fs_id ';
my $SQL = '';
for my $Key ( sort keys %{ $Param{Preferences} } ) {
if ($SQL) {
$SQL .= ' OR ';
}
$SQL .= '(vfsp.preferences_key = ? AND ';
push @SQLBind, \$Key;
my $Value = $Param{Preferences}->{$Key};
if ( $Value =~ /(\*|\%)/ ) {
$Value =~ s/\*/%/g;
$Value = $DBObject->Quote( $Value, 'Like' );
$SQL .= "vfsp.preferences_value LIKE '$Value' $LikeEscapeString";
}
else {
$SQL .= 'vfsp.preferences_value = ?';
push @SQLBind, \$Value;
}
$SQL .= ')';
}
$SQLWhere .= " AND ($SQL)";
}
# search
return if !$DBObject->Prepare(
SQL => "SELECT $SQLResult FROM $SQLTable WHERE $SQLWhere",
Bind => \@SQLBind,
);
my @List;
my %Result;
while ( my @Row = $DBObject->FetchrowArray() ) {
if ( $Param{Preferences} ) {
for my $Key ( sort keys %{ $Param{Preferences} } ) {
$Result{ $Row[0] }->{ $Row[1] } = $Row[2];
}
}
else {
push @List, $Row[0];
}
}
# check preferences search
if ( $Param{Preferences} ) {
FILE:
for my $File ( sort keys %Result ) {
for my $Key ( sort keys %{ $Param{Preferences} } ) {
my $DB = $Result{$File}->{$Key};
my $Given = $Param{Preferences}->{$Key};
next FILE if defined $DB && !defined $Given;
next FILE if !defined $DB && defined $Given;
if ( $Given =~ /\*/ ) {
$Given =~ s/\*/.\*/g;
$Given =~ s/\//\\\//g;
next FILE if $DB !~ /$Given/;
}
else {
next FILE if $DB ne $Given;
}
}
push @List, $File;
}
}
# return result
return @List;
}
=begin Internal:
returns internal meta information, unique file id, where and with what arguments the
file is stored
my ( $FileID, $BackendKey, $Backend ) = $Self->_FileLookup( '/Object/SomeFile.txt' );
=cut
sub _FileLookup {
my ( $Self, $Filename ) = @_;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# lookup
return if !$DBObject->Prepare(
SQL => 'SELECT id, backend_key, backend FROM virtual_fs WHERE filename = ?',
Bind => [ \$Filename ],
);
my $FileID;
my $BackendKey;
my $Backend;
while ( my @Row = $DBObject->FetchrowArray() ) {
$FileID = $Row[0];
$BackendKey = $Row[1];
$Backend = $Row[2];
}
return ( $FileID, $BackendKey, $Backend );
}
=end Internal:
=cut
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
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 L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut