2015-08-05 14:43:41 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# PROTOCOL COMMON MODULE
|
|
|
|
####################################################################################################################################
|
2016-04-14 15:30:54 +02:00
|
|
|
package pgBackRest::Protocol::Common;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings FATAL => qw(all);
|
|
|
|
use Carp qw(confess);
|
|
|
|
|
2015-12-24 17:32:25 +02:00
|
|
|
use Exporter qw(import);
|
|
|
|
our @EXPORT = qw();
|
2015-08-05 14:43:41 +02:00
|
|
|
use Compress::Raw::Zlib qw(WANT_GZIP Z_OK Z_BUF_ERROR Z_STREAM_END);
|
|
|
|
use File::Basename qw(dirname);
|
2016-12-04 00:34:51 +02:00
|
|
|
use JSON::PP;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-04-14 15:30:54 +02:00
|
|
|
use pgBackRest::Common::Exception;
|
|
|
|
use pgBackRest::Common::Ini;
|
|
|
|
use pgBackRest::Common::Log;
|
2017-01-27 17:06:16 +02:00
|
|
|
use pgBackRest::Protocol::IO::ProcessIO;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-08-24 18:39:27 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# DB/BACKUP Constants
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant DB => 'db';
|
|
|
|
push @EXPORT, qw(DB);
|
|
|
|
use constant BACKUP => 'backup';
|
|
|
|
push @EXPORT, qw(BACKUP);
|
|
|
|
use constant NONE => 'none';
|
|
|
|
push @EXPORT, qw(NONE);
|
|
|
|
|
2016-09-06 15:15:22 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# Operation constants
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant OP_NOOP => 'noop';
|
|
|
|
push @EXPORT, qw(OP_NOOP);
|
|
|
|
use constant OP_EXIT => 'exit';
|
|
|
|
push @EXPORT, qw(OP_EXIT);
|
|
|
|
|
2016-09-06 15:35:02 +02:00
|
|
|
# Backup module
|
|
|
|
use constant OP_BACKUP_FILE => 'backupFile';
|
|
|
|
push @EXPORT, qw(OP_BACKUP_FILE);
|
|
|
|
|
2016-09-06 15:15:22 +02:00
|
|
|
# Archive Module
|
|
|
|
use constant OP_ARCHIVE_GET_ARCHIVE_ID => 'archiveId';
|
|
|
|
push @EXPORT, qw(OP_ARCHIVE_GET_ARCHIVE_ID);
|
|
|
|
use constant OP_ARCHIVE_GET_CHECK => 'archiveCheck';
|
|
|
|
push @EXPORT, qw(OP_ARCHIVE_GET_CHECK);
|
|
|
|
use constant OP_ARCHIVE_PUSH_CHECK => 'archivePushCheck';
|
|
|
|
push @EXPORT, qw(OP_ARCHIVE_PUSH_CHECK);
|
|
|
|
|
2017-01-27 18:02:27 +02:00
|
|
|
# Archive Push Async Module
|
|
|
|
use constant OP_ARCHIVE_PUSH_ASYNC => 'archivePushAsync';
|
|
|
|
push @EXPORT, qw(OP_ARCHIVE_PUSH_ASYNC);
|
|
|
|
|
|
|
|
# Archive File Module
|
|
|
|
use constant OP_ARCHIVE_PUSH_FILE => 'archivePushFile';
|
|
|
|
push @EXPORT, qw(OP_ARCHIVE_PUSH_FILE);
|
|
|
|
|
2017-01-11 02:54:51 +02:00
|
|
|
# Check Module
|
|
|
|
use constant OP_CHECK_BACKUP_INFO_CHECK => 'backupInfoCheck';
|
|
|
|
push @EXPORT, qw(OP_CHECK_BACKUP_INFO_CHECK);
|
|
|
|
|
2016-09-06 15:15:22 +02:00
|
|
|
# Db Module
|
|
|
|
use constant OP_DB_CONNECT => 'dbConnect';
|
|
|
|
push @EXPORT, qw(OP_DB_CONNECT);
|
|
|
|
use constant OP_DB_EXECUTE_SQL => 'dbExecSql';
|
|
|
|
push @EXPORT, qw(OP_DB_EXECUTE_SQL);
|
|
|
|
use constant OP_DB_INFO => 'dbInfo';
|
|
|
|
push @EXPORT, qw(OP_DB_INFO);
|
|
|
|
|
|
|
|
# File Module
|
|
|
|
use constant OP_FILE_COPY => 'fileCopy';
|
|
|
|
push @EXPORT, qw(OP_FILE_COPY);
|
|
|
|
use constant OP_FILE_COPY_IN => 'fileCopyIn';
|
|
|
|
push @EXPORT, qw(OP_FILE_COPY_IN);
|
|
|
|
use constant OP_FILE_COPY_OUT => 'fileCopyOut';
|
|
|
|
push @EXPORT, qw(OP_FILE_COPY_OUT);
|
|
|
|
use constant OP_FILE_EXISTS => 'fileExists';
|
|
|
|
push @EXPORT, qw(OP_FILE_EXISTS);
|
|
|
|
use constant OP_FILE_LIST => 'fileList';
|
|
|
|
push @EXPORT, qw(OP_FILE_LIST);
|
|
|
|
use constant OP_FILE_MANIFEST => 'fileManifest';
|
|
|
|
push @EXPORT, qw(OP_FILE_MANIFEST);
|
|
|
|
use constant OP_FILE_PATH_CREATE => 'pathCreate';
|
|
|
|
push @EXPORT, qw(OP_FILE_PATH_CREATE);
|
|
|
|
use constant OP_FILE_WAIT => 'wait';
|
|
|
|
push @EXPORT, qw(OP_FILE_WAIT);
|
|
|
|
|
|
|
|
# Info module
|
|
|
|
use constant OP_INFO_STANZA_LIST => 'infoStanzList';
|
|
|
|
push @EXPORT, qw(OP_INFO_STANZA_LIST);
|
|
|
|
|
2016-09-06 15:35:02 +02:00
|
|
|
# Restore module
|
|
|
|
use constant OP_RESTORE_FILE => 'restoreFile';
|
|
|
|
push @EXPORT, qw(OP_RESTORE_FILE);
|
|
|
|
|
2016-12-04 00:34:51 +02:00
|
|
|
# To be run after each command
|
|
|
|
use constant OP_POST => 'post';
|
|
|
|
push @EXPORT, qw(OP_POST);
|
2016-09-06 15:35:02 +02:00
|
|
|
|
2015-08-05 14:43:41 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# CONSTRUCTOR
|
|
|
|
####################################################################################################################################
|
|
|
|
sub new
|
|
|
|
{
|
|
|
|
my $class = shift; # Class name
|
|
|
|
|
|
|
|
# Create the class hash
|
|
|
|
my $self = {};
|
|
|
|
bless $self, $class;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
(
|
|
|
|
my $strOperation,
|
2015-10-08 17:43:56 +02:00
|
|
|
$self->{iBufferMax},
|
2015-08-29 20:20:46 +02:00
|
|
|
$self->{iCompressLevel},
|
|
|
|
$self->{iCompressLevelNetwork},
|
2015-10-08 17:43:56 +02:00
|
|
|
$self->{iProtocolTimeout},
|
2015-08-29 20:20:46 +02:00
|
|
|
$self->{strName}
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
2016-08-11 23:32:28 +02:00
|
|
|
__PACKAGE__ . '->new', \@_,
|
2015-10-08 17:43:56 +02:00
|
|
|
{name => 'iBufferMax', trace => true},
|
2015-08-29 20:20:46 +02:00
|
|
|
{name => 'iCompressLevel', trace => true},
|
|
|
|
{name => 'iCompressNetworkLevel', trace => true},
|
2015-10-08 17:43:56 +02:00
|
|
|
{name => 'iProtocolTimeout', trace => true},
|
2015-08-29 20:20:46 +02:00
|
|
|
{name => 'strName', required => false, trace => true}
|
|
|
|
);
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-08-24 18:39:27 +02:00
|
|
|
# By default remote type is NONE
|
|
|
|
$self->{strRemoteType} = NONE;
|
|
|
|
|
2016-12-04 00:34:51 +02:00
|
|
|
# Create JSON object
|
|
|
|
$self->{oJSON} = JSON::PP->new()->allow_nonref();
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'self', value => $self}
|
|
|
|
);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
2015-12-24 17:32:25 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# keepAlive
|
|
|
|
#
|
|
|
|
# Don't do anything for keep alive if there is no remote.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub keepAlive
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-07-30 00:27:35 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# noop
|
|
|
|
#
|
|
|
|
# Don't do anything for noop if there is no remote.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub noOp
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-08-05 14:43:41 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# blockRead
|
|
|
|
#
|
|
|
|
# Read a block from the protocol layer.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub blockRead
|
|
|
|
{
|
|
|
|
my $self = shift;
|
2015-10-08 17:43:56 +02:00
|
|
|
my $oIn = shift;
|
2015-08-05 14:43:41 +02:00
|
|
|
my $strBlockRef = shift;
|
|
|
|
my $bProtocol = shift;
|
|
|
|
|
|
|
|
my $iBlockSize;
|
2016-12-10 16:09:16 +02:00
|
|
|
my $hMessage;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
if ($bProtocol)
|
|
|
|
{
|
|
|
|
# Read the block header and make sure it's valid
|
2015-10-08 17:43:56 +02:00
|
|
|
my $strBlockHeader = $oIn->lineRead();
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
if ($strBlockHeader !~ /^block -{0,1}[0-9]+( .*){0,1}$/)
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
confess &log(ERROR, "invalid block header ${strBlockHeader}", ERROR_FILE_READ);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Get block size from the header
|
|
|
|
my @stryToken = split(/ /, $strBlockHeader);
|
|
|
|
$iBlockSize = $stryToken[1];
|
2016-12-10 16:09:16 +02:00
|
|
|
|
|
|
|
if (defined($stryToken[2]))
|
|
|
|
{
|
|
|
|
$hMessage = $self->{oJSON}->decode($stryToken[2]);
|
|
|
|
}
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# If block size is 0 or an error code then undef the buffer
|
|
|
|
if ($iBlockSize <= 0)
|
|
|
|
{
|
|
|
|
undef($$strBlockRef);
|
|
|
|
}
|
|
|
|
# Else read the block
|
|
|
|
else
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$oIn->bufferRead($strBlockRef, $iBlockSize, undef, true);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$iBlockSize = $oIn->bufferRead($strBlockRef, $self->{iBufferMax}, defined($$strBlockRef) ? length($$strBlockRef) : undef);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Return the block size
|
2016-12-10 16:09:16 +02:00
|
|
|
return $iBlockSize, $hMessage;
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# blockWrite
|
|
|
|
#
|
|
|
|
# Write a block to the protocol layer.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub blockWrite
|
|
|
|
{
|
|
|
|
my $self = shift;
|
2015-10-08 17:43:56 +02:00
|
|
|
my $oOut = shift;
|
2015-08-05 14:43:41 +02:00
|
|
|
my $tBlockRef = shift;
|
|
|
|
my $iBlockSize = shift;
|
|
|
|
my $bProtocol = shift;
|
2016-12-10 16:09:16 +02:00
|
|
|
my $hMessage = shift;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# If block size is not defined, get it from buffer length
|
|
|
|
$iBlockSize = defined($iBlockSize) ? $iBlockSize : length($$tBlockRef);
|
|
|
|
|
|
|
|
# Write block header to the protocol stream
|
|
|
|
if ($bProtocol)
|
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$oOut->lineWrite("block ${iBlockSize}" . (defined($hMessage) ? " " . $self->{oJSON}->encode($hMessage) : ''));
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Write block if size > 0
|
|
|
|
if ($iBlockSize > 0)
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$oOut->bufferWrite($tBlockRef, $iBlockSize);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# binaryXfer
|
|
|
|
#
|
|
|
|
# Copies data from one file handle to another, optionally compressing or decompressing the data in stream. If $strRemote != none
|
|
|
|
# then one side is a protocol stream, though this can be controlled with the bProtocol param.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub binaryXfer
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $hIn = shift;
|
|
|
|
my $hOut = shift;
|
|
|
|
my $strRemote = shift;
|
|
|
|
my $bSourceCompressed = shift;
|
|
|
|
my $bDestinationCompress = shift;
|
|
|
|
my $bProtocol = shift;
|
2016-12-13 01:47:17 +02:00
|
|
|
my $fnExtra = shift;
|
2017-01-30 20:59:00 +02:00
|
|
|
my $rExtraParam = shift;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# The input stream must be defined
|
2015-10-08 17:43:56 +02:00
|
|
|
my $oIn;
|
|
|
|
|
2015-08-05 14:43:41 +02:00
|
|
|
if (!defined($hIn))
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$oIn = $self->{io};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-01-27 17:06:16 +02:00
|
|
|
$oIn = new pgBackRest::Protocol::IO::ProcessIO(
|
2016-07-26 22:57:38 +02:00
|
|
|
$hIn, undef, $self->{io}->{hErr}, $self->{io}->{pid}, $self->{io}->{strId}, $self->{iProtocolTimeout},
|
|
|
|
$self->{iBufferMax});
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# The output stream must be defined unless 'none' is passed
|
2015-10-08 17:43:56 +02:00
|
|
|
my $oOut;
|
|
|
|
|
2015-08-05 14:43:41 +02:00
|
|
|
if (!defined($hOut))
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$oOut = $self->{io};
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
2015-10-08 17:43:56 +02:00
|
|
|
elsif ($hOut ne 'none')
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
2017-01-27 17:06:16 +02:00
|
|
|
$oOut = new pgBackRest::Protocol::IO::ProcessIO(
|
2016-07-26 22:57:38 +02:00
|
|
|
undef, $hOut, $self->{io}->{hErr}, $self->{io}->{pid}, $self->{io}->{strId}, $self->{iProtocolTimeout},
|
|
|
|
$self->{iBufferMax});
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# If no remote is defined then set to none
|
|
|
|
if (!defined($strRemote))
|
|
|
|
{
|
|
|
|
$strRemote = 'none';
|
|
|
|
}
|
|
|
|
# Only set compression defaults when remote is defined
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$bSourceCompressed = defined($bSourceCompressed) ? $bSourceCompressed : false;
|
|
|
|
$bDestinationCompress = defined($bDestinationCompress) ? $bDestinationCompress : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Default protocol to true
|
|
|
|
$bProtocol = defined($bProtocol) ? $bProtocol : true;
|
2016-12-10 16:09:16 +02:00
|
|
|
my $hMessage = undef;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-12-13 01:47:17 +02:00
|
|
|
# Checksum, size, and extra
|
2016-12-10 16:09:16 +02:00
|
|
|
my $strChecksum = undef;
|
|
|
|
my $iFileSize = undef;
|
2016-12-13 01:47:17 +02:00
|
|
|
my $rExtra = undef;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# Read from the protocol stream
|
|
|
|
if ($strRemote eq 'in')
|
|
|
|
{
|
|
|
|
# If the destination should not be compressed then decompress
|
|
|
|
if (!$bDestinationCompress)
|
|
|
|
{
|
|
|
|
my $iBlockSize;
|
|
|
|
my $tCompressedBuffer;
|
|
|
|
my $tUncompressedBuffer;
|
|
|
|
my $iUncompressedBufferSize;
|
|
|
|
|
|
|
|
# Initialize SHA
|
|
|
|
my $oSHA;
|
|
|
|
|
|
|
|
if (!$bProtocol)
|
|
|
|
{
|
|
|
|
$oSHA = Digest::SHA->new('sha1');
|
|
|
|
}
|
|
|
|
|
|
|
|
# Initialize inflate object and check for errors
|
|
|
|
my ($oZLib, $iZLibStatus) =
|
|
|
|
new Compress::Raw::Zlib::Inflate(WindowBits => 15 & $bSourceCompressed ? WANT_GZIP : 0,
|
2015-10-08 17:43:56 +02:00
|
|
|
Bufsize => $self->{iBufferMax}, LimitOutput => 1);
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
if ($iZLibStatus != Z_OK)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable create a inflate object: ${iZLibStatus}");
|
|
|
|
}
|
|
|
|
|
|
|
|
# Read all input
|
|
|
|
do
|
|
|
|
{
|
|
|
|
# Read a block from the input stream
|
2016-12-10 16:09:16 +02:00
|
|
|
($iBlockSize, $hMessage) = $self->blockRead($oIn, \$tCompressedBuffer, $bProtocol);
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# Process protocol messages
|
2016-12-10 16:09:16 +02:00
|
|
|
if (defined($hMessage) && defined($hMessage->{bChecksum}) && !$hMessage->{bChecksum})
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
|
|
|
$oSHA = Digest::SHA->new('sha1');
|
2016-12-10 16:09:16 +02:00
|
|
|
undef($hMessage);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# If the block contains data, decompress it
|
|
|
|
if ($iBlockSize > 0)
|
|
|
|
{
|
|
|
|
# Keep looping while there is more to decompress
|
|
|
|
do
|
|
|
|
{
|
|
|
|
# Decompress data
|
|
|
|
$iZLibStatus = $oZLib->inflate($tCompressedBuffer, $tUncompressedBuffer);
|
|
|
|
$iUncompressedBufferSize = length($tUncompressedBuffer);
|
|
|
|
|
|
|
|
# If status is ok, write the data
|
|
|
|
if ($iZLibStatus == Z_OK || $iZLibStatus == Z_BUF_ERROR || $iZLibStatus == Z_STREAM_END)
|
|
|
|
{
|
|
|
|
if ($iUncompressedBufferSize > 0)
|
|
|
|
{
|
|
|
|
# Add data to checksum
|
|
|
|
if (defined($oSHA))
|
|
|
|
{
|
|
|
|
$oSHA->add($tUncompressedBuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write data if hOut is defined
|
2015-10-08 17:43:56 +02:00
|
|
|
if (defined($oOut))
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$oOut->bufferWrite(\$tUncompressedBuffer, $iUncompressedBufferSize);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# Else error, exit so it can be handled
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$iBlockSize = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while ($iZLibStatus != Z_STREAM_END && $iUncompressedBufferSize > 0 && $iBlockSize > 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while ($iBlockSize > 0);
|
|
|
|
|
|
|
|
# Make sure the decompression succeeded (iBlockSize < 0 indicates remote error, handled later)
|
|
|
|
if ($iBlockSize == 0 && $iZLibStatus != Z_STREAM_END)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable to inflate stream: ${iZLibStatus}");
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get checksum and total uncompressed bytes written
|
|
|
|
if (defined($oSHA))
|
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$strChecksum = $oSHA->hexdigest();
|
|
|
|
$iFileSize = $oZLib->total_out();
|
2015-08-05 14:43:41 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
# If the destination should be compressed then just write out the already compressed stream
|
|
|
|
else
|
|
|
|
{
|
|
|
|
my $iBlockSize;
|
|
|
|
my $tBuffer;
|
|
|
|
|
|
|
|
# Initialize checksum and size
|
|
|
|
my $oSHA;
|
|
|
|
|
|
|
|
if (!$bProtocol)
|
|
|
|
{
|
|
|
|
$oSHA = Digest::SHA->new('sha1');
|
2016-12-10 16:09:16 +02:00
|
|
|
$iFileSize = 0;
|
2016-12-13 01:47:17 +02:00
|
|
|
$rExtra = defined($fnExtra) ? {} : undef;
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
# Read a block from the protocol stream
|
2016-12-10 16:09:16 +02:00
|
|
|
($iBlockSize, $hMessage) = $self->blockRead($oIn, \$tBuffer, $bProtocol);
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-12-13 01:47:17 +02:00
|
|
|
# Add data to checksum and size
|
|
|
|
if ($iBlockSize > 0 && !$bProtocol)
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
2016-12-13 01:47:17 +02:00
|
|
|
$oSHA->add($tBuffer);
|
|
|
|
$iFileSize += $iBlockSize;
|
|
|
|
}
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-12-13 01:47:17 +02:00
|
|
|
# Do extra processing on the buffer if requested
|
|
|
|
if (!$bProtocol && defined($fnExtra))
|
|
|
|
{
|
2017-01-30 20:59:00 +02:00
|
|
|
$fnExtra->($rExtraParam, \$tBuffer, $iBlockSize, $iFileSize - $iBlockSize, $rExtra);
|
2016-12-13 01:47:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Write buffer
|
|
|
|
if ($iBlockSize > 0)
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$oOut->bufferWrite(\$tBuffer, $iBlockSize);
|
2015-08-05 14:43:41 +02:00
|
|
|
undef($tBuffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while ($iBlockSize > 0);
|
|
|
|
|
|
|
|
# Get checksum
|
|
|
|
if (!$bProtocol)
|
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$strChecksum = $oSHA->hexdigest();
|
2015-08-05 14:43:41 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# Read from file input stream
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# If source is not already compressed then compress it
|
|
|
|
if ($strRemote eq 'out' && !$bSourceCompressed)
|
|
|
|
{
|
|
|
|
my $iBlockSize;
|
|
|
|
my $tCompressedBuffer;
|
|
|
|
my $iCompressedBufferSize;
|
|
|
|
my $tUncompressedBuffer;
|
|
|
|
|
|
|
|
# Initialize message to indicate that a checksum will be sent
|
2015-10-08 17:43:56 +02:00
|
|
|
if ($bProtocol && defined($oOut))
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$hMessage->{bChecksum} = true;
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Initialize checksum
|
|
|
|
my $oSHA = Digest::SHA->new('sha1');
|
2016-12-13 01:47:17 +02:00
|
|
|
$rExtra = defined($fnExtra) ? {} : undef;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# Initialize inflate object and check for errors
|
|
|
|
my ($oZLib, $iZLibStatus) =
|
|
|
|
new Compress::Raw::Zlib::Deflate(WindowBits => 15 & $bDestinationCompress ? WANT_GZIP : 0,
|
|
|
|
Level => $bDestinationCompress ? $self->{iCompressLevel} :
|
|
|
|
$self->{iCompressLevelNetwork},
|
2015-10-08 17:43:56 +02:00
|
|
|
Bufsize => $self->{iBufferMax}, AppendOutput => 1);
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
if ($iZLibStatus != Z_OK)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable create a deflate object: ${iZLibStatus}");
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
# Read a block from the stream
|
2015-10-08 17:43:56 +02:00
|
|
|
$iBlockSize = $oIn->bufferRead(\$tUncompressedBuffer, $self->{iBufferMax});
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-12-13 01:47:17 +02:00
|
|
|
# If block size > 0 then update checksum and size
|
2015-08-05 14:43:41 +02:00
|
|
|
if ($iBlockSize > 0)
|
|
|
|
{
|
|
|
|
# Update checksum and filesize
|
|
|
|
$oSHA->add($tUncompressedBuffer);
|
2016-12-13 01:47:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Do extra processing on the buffer if requested
|
|
|
|
if (defined($fnExtra))
|
|
|
|
{
|
2017-01-30 20:59:00 +02:00
|
|
|
$fnExtra->($rExtraParam, \$tUncompressedBuffer, $iBlockSize, $oZLib->total_in(), $rExtra);
|
2016-12-13 01:47:17 +02:00
|
|
|
}
|
2015-08-05 14:43:41 +02:00
|
|
|
|
2016-12-13 01:47:17 +02:00
|
|
|
# If block size > 0 then compress
|
|
|
|
if ($iBlockSize > 0)
|
|
|
|
{
|
2015-08-05 14:43:41 +02:00
|
|
|
# Compress the data
|
|
|
|
$iZLibStatus = $oZLib->deflate($tUncompressedBuffer, $tCompressedBuffer);
|
|
|
|
$iCompressedBufferSize = length($tCompressedBuffer);
|
|
|
|
|
|
|
|
# If compression was successful
|
|
|
|
if ($iZLibStatus == Z_OK)
|
|
|
|
{
|
|
|
|
# The compressed data is larger than block size, then write
|
2015-10-08 17:43:56 +02:00
|
|
|
if ($iCompressedBufferSize > $self->{iBufferMax})
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$self->blockWrite($oOut, \$tCompressedBuffer, $iCompressedBufferSize, $bProtocol, $hMessage);
|
2015-08-05 14:43:41 +02:00
|
|
|
undef($tCompressedBuffer);
|
2016-12-10 16:09:16 +02:00
|
|
|
undef($hMessage);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# Else if error
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$iBlockSize = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while ($iBlockSize > 0);
|
|
|
|
|
|
|
|
# If good so far flush out the last bytes
|
|
|
|
if ($iZLibStatus == Z_OK)
|
|
|
|
{
|
|
|
|
$iZLibStatus = $oZLib->flush($tCompressedBuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Make sure the compression succeeded
|
|
|
|
if ($iZLibStatus != Z_OK)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable to deflate stream: ${iZLibStatus}");
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get checksum and total uncompressed bytes written
|
2016-12-10 16:09:16 +02:00
|
|
|
$strChecksum = $oSHA->hexdigest();
|
|
|
|
$iFileSize = $oZLib->total_in();
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# Write out the last block
|
2015-10-08 17:43:56 +02:00
|
|
|
if (defined($oOut))
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
|
|
|
$iCompressedBufferSize = length($tCompressedBuffer);
|
|
|
|
|
|
|
|
if ($iCompressedBufferSize > 0)
|
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$self->blockWrite($oOut, \$tCompressedBuffer, $iCompressedBufferSize, $bProtocol, $hMessage);
|
|
|
|
undef($hMessage);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
2016-12-10 16:09:16 +02:00
|
|
|
$self->blockWrite(
|
2016-12-13 01:47:17 +02:00
|
|
|
$oOut, undef, 0, $bProtocol, {strChecksum => $strChecksum, iFileSize => $iFileSize, rExtra => $rExtra});
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# If source is already compressed or transfer is not compressed then just read the stream
|
|
|
|
else
|
|
|
|
{
|
|
|
|
my $iBlockSize;
|
|
|
|
my $tBuffer;
|
|
|
|
my $tCompressedBuffer;
|
|
|
|
my $tUncompressedBuffer;
|
|
|
|
my $iUncompressedBufferSize;
|
|
|
|
my $oSHA;
|
|
|
|
my $oZLib;
|
|
|
|
my $iZLibStatus;
|
|
|
|
|
|
|
|
# If the destination will be compressed setup deflate
|
|
|
|
if ($bDestinationCompress)
|
|
|
|
{
|
|
|
|
if ($bProtocol)
|
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$hMessage->{bChecksum} = true;
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Initialize checksum and size
|
|
|
|
$oSHA = Digest::SHA->new('sha1');
|
2016-12-10 16:09:16 +02:00
|
|
|
$iFileSize = 0;
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# Initialize inflate object and check for errors
|
|
|
|
($oZLib, $iZLibStatus) =
|
2015-10-08 17:43:56 +02:00
|
|
|
new Compress::Raw::Zlib::Inflate(WindowBits => WANT_GZIP, Bufsize => $self->{iBufferMax}, LimitOutput => 1);
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
if ($iZLibStatus != Z_OK)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable create a inflate object: ${iZLibStatus}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# Initialize message to indicate that a checksum will not be sent
|
|
|
|
elsif ($bProtocol)
|
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$hMessage->{bChecksum} = false;
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Read input
|
|
|
|
do
|
|
|
|
{
|
2015-10-08 17:43:56 +02:00
|
|
|
$iBlockSize = $oIn->bufferRead(\$tBuffer, $self->{iBufferMax});
|
2015-08-05 14:43:41 +02:00
|
|
|
|
|
|
|
# Write a block if size > 0
|
|
|
|
if ($iBlockSize > 0)
|
|
|
|
{
|
2016-12-10 16:09:16 +02:00
|
|
|
$self->blockWrite($oOut, \$tBuffer, $iBlockSize, $bProtocol, $hMessage);
|
|
|
|
undef($hMessage);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Decompress the buffer to calculate checksum/size
|
|
|
|
if ($bDestinationCompress)
|
|
|
|
{
|
|
|
|
# If the block contains data, decompress it
|
|
|
|
if ($iBlockSize > 0)
|
|
|
|
{
|
|
|
|
# Copy file buffer to compressed buffer
|
|
|
|
if (defined($tCompressedBuffer))
|
|
|
|
{
|
|
|
|
$tCompressedBuffer .= $tBuffer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$tCompressedBuffer = $tBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Keep looping while there is more to decompress
|
|
|
|
do
|
|
|
|
{
|
|
|
|
# Decompress data
|
|
|
|
$iZLibStatus = $oZLib->inflate($tCompressedBuffer, $tUncompressedBuffer);
|
|
|
|
$iUncompressedBufferSize = length($tUncompressedBuffer);
|
|
|
|
|
|
|
|
# If status is ok, write the data
|
|
|
|
if ($iZLibStatus == Z_OK || $iZLibStatus == Z_BUF_ERROR || $iZLibStatus == Z_STREAM_END)
|
|
|
|
{
|
|
|
|
if ($iUncompressedBufferSize > 0)
|
|
|
|
{
|
|
|
|
$oSHA->add($tUncompressedBuffer);
|
2016-12-10 16:09:16 +02:00
|
|
|
$iFileSize += $iUncompressedBufferSize;
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# Else error, exit so it can be handled
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$iBlockSize = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while ($iZLibStatus != Z_STREAM_END && $iUncompressedBufferSize > 0 && $iBlockSize > 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while ($iBlockSize > 0);
|
|
|
|
|
2016-12-04 00:34:51 +02:00
|
|
|
# Check decompression, get checksum
|
2015-08-05 14:43:41 +02:00
|
|
|
if ($bDestinationCompress)
|
|
|
|
{
|
|
|
|
# Make sure the decompression succeeded (iBlockSize < 0 indicates remote error, handled later)
|
|
|
|
if ($iBlockSize == 0 && $iZLibStatus != Z_STREAM_END)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable to inflate stream: ${iZLibStatus}");
|
|
|
|
}
|
|
|
|
|
2016-12-10 16:09:16 +02:00
|
|
|
# If protocol then create the message
|
2015-08-05 14:43:41 +02:00
|
|
|
if ($bProtocol)
|
|
|
|
{
|
2016-12-13 01:47:17 +02:00
|
|
|
$hMessage = {strChecksum => $oSHA->hexdigest(), iFileSize => $iFileSize, rExtra => $rExtra};
|
2016-12-10 16:09:16 +02:00
|
|
|
}
|
|
|
|
# Otherwise just set checksum
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$strChecksum = $oSHA->hexdigest();
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# If protocol write
|
|
|
|
if ($bProtocol)
|
|
|
|
{
|
|
|
|
# Write 0 block to indicate end of stream
|
2016-12-10 16:09:16 +02:00
|
|
|
$self->blockWrite($oOut, undef, 0, $bProtocol, $hMessage);
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-04 00:34:51 +02:00
|
|
|
# If message is defined then the checksum, size, and extra should be in it
|
2016-12-10 16:09:16 +02:00
|
|
|
if (defined($hMessage))
|
2015-08-05 14:43:41 +02:00
|
|
|
{
|
2016-12-13 01:47:17 +02:00
|
|
|
return $hMessage->{strChecksum}, $hMessage->{iFileSize}, $hMessage->{rExtra};
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# Return the checksum and size if they are available
|
2016-12-13 01:47:17 +02:00
|
|
|
return $strChecksum, $iFileSize, $rExtra;
|
2015-08-05 14:43:41 +02:00
|
|
|
}
|
|
|
|
|
2016-08-24 18:39:27 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# remoteType
|
|
|
|
#
|
|
|
|
# Return the remote type.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub remoteType
|
|
|
|
{
|
|
|
|
return shift->{strRemoteType};
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# remoteTypeTest
|
|
|
|
#
|
|
|
|
# Determine if the remote matches the specified remote.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub remoteTypeTest
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strRemoteType = shift;
|
|
|
|
|
2017-01-10 03:49:04 +02:00
|
|
|
return $self->remoteType() eq $strRemoteType ? true : false;
|
2016-08-24 18:39:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# isRemote
|
|
|
|
#
|
|
|
|
# Determine if the protocol object is communicating with a remote.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub isRemote
|
|
|
|
{
|
|
|
|
return shift->{strRemoteType} ne NONE ? true : false;
|
|
|
|
}
|
|
|
|
|
2015-08-05 14:43:41 +02:00
|
|
|
1;
|