Downloads a file from the FTP server. This is a two-step process of firstly sending of a PASV command to open the data channel and secondly the download over the data channel. The data channel is closed after the download so this function is called for every file required.
| Return Type | Function name | Arguments |
|---|---|---|
| hzEcode | hzFtpClient::FileDownload | (hzDirent&,) |
Declared in file: hzFtpClient.h
Defined in file : hzFtpClient.cpp
Function Logic:
Function body:
hzEcode hzFtpClient::FileDownload (hzDirent& finfo)
{
// Downloads a file from the FTP server. This is a two-step process of firstly sending of a PASV command to open the data channel
// and secondly the download over the data channel. The data channel is closed after the download so this function is called for
// every file required.
//
// Arguments: 1) finfo The hzDirent for the server-side file. This will have been obtained by a prior directory listing.
//
// Returns: E_HOSTFAIL If there was a communication failure and reconnect failed
// E_NOTFOUND The file on the server does not exist.
// E_OPENFAIL The target file on the local machine could not be opened. Presumed fatal
// E_WRITEFAIL The target file on the local machine could not be written too. Presumed fatal.
// E_OK If the operation successful
_hzfunc("hzFtpClient::FileDownload") ;
struct utimbuf svr_mtime ; // Used to alter date on files to match server version
std::ofstream os ; // Target file stream
hzTcpClient X ; // Data channel
hzString tmpFile ; // Download to a temporary file first, rename upon success
hzString tgtFile ; // Download to a temporary file first, rename upon success
uint32_t epoch ; // Time in seconds since 1970
uint32_t nRecv ; // Incoming message size
uint32_t nTotal = 0; // Total bytes downloaded
uint32_t len ; // Outgoing message length
uint32_t nTry ; // Local limit on reconnects
hzEcode rc ; // Return code
// Elimintate zero size files
if (!finfo.Size())
return E_OK ;
// X.SetDebug(_hzGlobal_Debug & HZ_DEBUG_CLIENT) ;
tmpFile = m_LocalDir + "/" ;
tmpFile += finfo.strName() ;
tmpFile += ".tmp" ;
tgtFile = m_LocalDir + "/" + finfo.strName() ;
/*
** ** Loop round until success
** */
for (nTry = 0; nTry < 2; nTry++)
{
start:
nTotal = 0;
if (nTry == 1)
{
X.Close() ;
m_ConnControl.Close() ;
rc = _reconnect() ;
if (rc != E_OK)
break ;
}
/*
** ** First step is to open a data channel
** */
rc = _openpasv(X) ;
if (rc != E_OK)
{
threadLog("Failed PASV\n") ;
continue ;
}
/*
** ** Send the RETR command and recv response
** */
sprintf(m_c_sbuf, "RETR %s\r\n", finfo.txtName()) ;
len = strlen(m_c_sbuf) ;
if ((rc = m_ConnControl.Send(m_c_sbuf, len)) != E_OK)
{
threadLog("Could not send RETR command to get file %s\n", finfo.txtName()) ;
continue ;
}
if ((rc = _ftprecv(nRecv, *_fn)) != E_OK)
{
threadLog("Could not get RETR respeonse (file=%s)\n", finfo.txtName()) ;
continue ;
}
/*
** ** Check response code
** */
if (m_nRescode >&eq; 400)
{
threadLog("Got bad response to RETR (%d)\n", m_nRescode) ;
rc = E_PROTOCOL ;
break ;
}
/*
** ** Then if all is well, download the file
** */
os.open(*tmpFile) ;
if (os.fail())
{
threadLog("Failed to open download temp file %s\n", *tmpFile) ;
rc = E_OPENFAIL ;
break ;
}
for (nTotal = 0; nTotal < finfo.Size() ;)
{
rc = X.Recv(m_x_rbuf, nRecv, HZ_MAXPACKET) ;
if (rc != E_OK)
{
threadLog("Socket error during download of file %s\n", finfo.txtName()) ;
X.Close() ;
os.close() ;
os.clear() ;
goto start ;
}
if (!nRecv)
break ;
nTotal += nRecv ;
os.write(m_x_rbuf, nRecv) ;
if (os.fail())
{
threadLog("Failed to write data to the target file %s\n", finfo.txtName()) ;
rc = E_WRITEFAIL ;
break ;
}
}
X.Close() ;
os.close() ;
if (rc == E_WRITEFAIL)
{
threadLog("Aborted operation to download %d of %d bytes to the target file %s - No disk space\n", nTotal, finfo.Size(), finfo.txtName()) ;
}
// Initial comms phase over so exit loop
break ;
}
if (rc == E_OK)
{
/*
** ** Rename the file to the target name, set mtime of the local file to match that of the server
** */
rename(*tmpFile, *tgtFile) ;
threadLog("Downloaded %d of %d bytes to the target file %s\n", nTotal, finfo.Size(), finfo.txtName()) ;
epoch = finfo.Mtime() ;
svr_mtime.actime = time(0);
svr_mtime.modtime = epoch ;
utime(finfo.txtName(), &svr_mtime) ;
/*
** ** Get server progress report of download. If broken pipe reconnect but as we have the file already we don't
** ** have to repeat any steps.
** */
if ((rc = _ftprecv(nRecv, *_fn)) != E_OK)
{
threadLog("Could not get response to RETR command (file=%s)\n", finfo.txtName()) ;
rc = _reconnect() ;
}
else
{
if (m_nRescode != 226)
threadLog("Expected code of 226 (Xfer complete), got %d\n", m_nRescode) ;
if (m_nRescode >&eq; 400)
rc = E_RECVFAIL ;
}
}
if (rc != E_OK)
threadLog("Could not download %s\n\n", finfo.txtName()) ;
else
{
threadLog("Downloaded %s\n", finfo.txtName()) ;
m_nTries = 0;
}
return rc ;
}