// Win32FtpTests.cpp : Defines the entry point for the console application.
//

#define _CRT_SECURE_NO_WARNINGS 1

#include "stdafx.h"
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <direct.h>
#include "curl\curl.h"
#include "TraceLog.h"

//extern int   errno;
//extern char *sys_errlist[];

/*
	This WIN32 FTPS/SFTP prototype uses the following freeware:

	        curllib 17.7.1 
	        libbsh2 0.18 (note: libssh2 0.17 has severe authentication problems) 
        	OpenSSL 0.9.8e 

	I had to compile curllib and libssh2, using VS 2005, with the following
	preprocessor definitions: 

        curllib - WIN32;_DEBUG;_WINDOWS;_USRDLL;BUILDING_LIBCURL;USE_LIBSSH2;USE_SSLEAY;USE_OPENSSL;HAVE_LIBSSH2_H 

        libssh2 - WIN32;_DEBUG;LIBSSH2_WIN32;LIBSSH2_WIN32;_LIB;LIBSSH2_RSA;LIBSSH2_DSA 

	The only source change that I made was in libssh2's libssh2_config.h where I
	commented out the following line (because my SFTP transfers will not be
	using compression):

        //#define LIBSSH2_HAVE_ZLIB 1 

*/


/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

size_t read_function(void   *bufptr,
					 size_t  size,
					 size_t  nMemb,
					 void   *pUser)
{
	// Assign the File Pointer that we will use...
	FILE *fp = (FILE*)pUser;
	size_t ret = 0;
	int errno_save = -1;

	// Read the file that is being uploaded...
	ret = fread(bufptr,size,nMemb,fp);
	errno_save = errno;

	// Did we have an I/O error...
	if ((ferror(fp)) != 0)
	{
		// Yep...
		LogError("read_function(): fread() failed. ret=%d errno=%d '%s'",ret,errno_save,sys_errlist[errno_save]);
		return 0;
	} // if ((ferror(fp) != 0)

	// We're outta here...
	LogMinor("read_function(): Returning %d.",ret);
	LogVerbose("read_function(): bufptr=%s",bufptr);
	return ret;
} //  read_function()

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

int doFtpsUpLoad(void)
{
	static char url[] = "ftp://10.1.4.177/TEMP/PLU.bat";
	static long port = 16987; // The FTPS default port is 21.
	static char userNamePassword[] = "tciuser:abc123";
	static char fileName[] = "C:\\TEMP\\PLU.bat";

	static CURL *pCurl = NULL;
	static CURLcode ret = CURLE_OK;
	static char CurlErrorMsg[1000] = { '\0' };

	static char sslCertificateName[] = "TciSslCertificate.crt";
	static char sslCertificateType[] = "PEM";
	static char sslKeyName[] = "TciSslCertificate.crt";
	static char sslKeyType[] = "PEM";

	static int  hd = -1;
	static struct stat file_info;
	static FILE *fp = NULL;
	static int errno_save = -1;

	// Initialize the local variables...
	memset((void*)&file_info,0,sizeof(file_info));

	// Open our Trace Log...
	OpenTraceLog(TRACE_LOG_DEFAULT_LOG_PATH);

	// Get the outgoing file size...
	if ((hd = _open(fileName,O_RDONLY,_S_IREAD)) == -1)
	{
		errno_save = errno;
		LogError("doFtpsUpLoad(): _open() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		CloseTraceLog();
		return -1;
	}
	if ((fstat(hd,&file_info)) != 0)
	{
		errno_save = errno;
		LogError("doFtpsUpLoad(): fstat() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		_close(hd);
		CloseTraceLog();
		return -1;
	}
	_close(hd);
	hd = -1;

	// Open the file to be uploaded...
	if ((fp = fopen(fileName,"rb")) == NULL)
	{
		errno_save = errno;
		LogError("doFtpsUpLoad(): fopen() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		CloseTraceLog();
		return -1;
	}

	// Initalize curllib globally...
	if ((ret = curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_global_init() failed. ret=%d",ret);
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Initialize curllib...
	if ((pCurl = curl_easy_init()) == NULL)
	{
		LogError("doFtpsUpLoad(): curl_easy_init() failed.");
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Let's have curllib generate a human readable error message for any possible curllib errors...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_ERRORBUFFER, CurlErrorMsg)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_ERRORBUFFER) failed. ret=%d",ret);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Is the Log Level set to the max...
	if (GetLogLevel() >= LOG_VERBOSE)
	{
		// Yep...
		// Enable curllib's verbose logging...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_VERBOSE, TRUE)) != CURLE_OK)
			LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_VERBOSE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

		// Redirect the verbose logging to our Trace Log File...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_STDERR, g_fpLog)) != CURLE_OK)
			LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_STDERR) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
	} // if (GetLogLevel() >= LOG_VERBOSE)

	// Disable curllib's progress bar (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, TRUE)) != CURLE_OK)
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_NOPROGESS) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// DIsable curllib's use of dignals (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, TRUE)) != CURLE_OK)
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_NOSIGNAL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// Tell curllib we are gonna do an upload...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_UPLOAD, TRUE)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_UPLOAD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the port that we will be using...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_PORT, port)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_PORT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the UserName and Password...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_USERPWD, userNamePassword)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_USERPWD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the url of the FTP Server...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_URL, url)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_URL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// We need to pass the fp to the callback function (read_function())...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_READDATA, fp)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_READDATA) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify which callback function...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_READFUNCTION, read_function)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_READFUNCTION) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the outgoing file size...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_INFILESIZE_LARGE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib to use SSL for all communications...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_USE_SSL, CURLUSESSL_ALL)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_USE_SSL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib to use AUTH SSL (not AUTH TLS)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_SSL)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_FTPSSLAUTH) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Disable the "Clear Command Channel" option in curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_FTP_SSL_CCC, CURLFTPSSL_CCC_NONE)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_FTP_SSL_CCC) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Certificate File Name...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLCERT, sslCertificateName)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLCERT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Certificate Type ("PEM") (don't use "DER")...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLCERTTYPE, sslCertificateType)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLCERTTYPE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Private Key File Name...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLKEY, sslKeyName)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLKEY) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Private Key Type ("PEM") (don't use "DER")...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLKEYTYPE, sslKeyType)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLKEYTYPE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify to use the default crypto engine...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLENGINE_DEFAULT, sslKeyType)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLENGINE_DEFAULT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSLv3 SSL will be used...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLVERSION) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Don't determine the authenticity of the peer's certificate...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, FALSE)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSL_VERIFYPEER) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Don't determine the authenticity of the server's certificate...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, FALSE)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSL_VERIFYHOST) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Now do the FTP...
	if ((ret = curl_easy_perform(pCurl)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_perform() failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Cleanup...
	curl_easy_cleanup(pCurl);
	pCurl = NULL;

	// Cleanup the curllib globals...
	curl_global_cleanup();

	// Close the outgoing file...
	fclose(fp);
	fp = NULL;

	// We're outta here...
	LogMajor("doFtpsUpLoad(): Completed OK.");
	CloseTraceLog();
	return 0;
} // doFtpsUpLoad()

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

static int my_trace(CURL *handle,
					curl_infotype type,
                    unsigned char *data,
					size_t size,
                    void *userp)
{
	LogMajor("my_trace(): type=%d data=%s size=%d",type,data,size);
  
	return 0;
}
  

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

int doSftpUpLoad(void)
{
	static char url[] = "sftp://10.1.4.78/TEMP/PLU.bat";
	static long port = 16987; // The SFTP default port is 22.
	static char userNamePassword[] = "ftpuser:ibm4990";
	static char fileName[] = "C:\\TEMP\\PLU.bat";

	static CURL *pCurl = NULL;
	static CURLcode ret = CURLE_OK;
	static char CurlErrorMsg[1000] = { '\0' };

	static char sshPublicKeyFile[]  = "tcidsa.pbk";
	static char sshPrivateKeyFile[] = "tcidsa.dat";

	static int  hd = -1;
	static struct stat file_info;
	static FILE *fp = NULL;
	static int errno_save = -1;

	struct data { char trace_ascii; }; /* 1 or 0 */
	static data config;
  
	// Initialize the local variables...
	memset((void*)&file_info,0,sizeof(file_info));

	// Open our Trace Log...
	OpenTraceLog(TRACE_LOG_DEFAULT_LOG_PATH);


	// Get the outgoing file size...
	if ((hd = _open(fileName,O_RDONLY,_S_IREAD)) == -1)
	{
		errno_save = errno;
		LogError("doSftpUpLoad(): _open() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		CloseTraceLog();
		return -1;
	}
	if ((fstat(hd,&file_info)) != 0)
	{
		errno_save = errno;
		LogError("doSftpUpLoad(): fstat() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		_close(hd);
		CloseTraceLog();
		return -1;
	}
	_close(hd);
	hd = -1;

	// Open the file to be uploaded...
	if ((fp = fopen(fileName,"rb")) == NULL)
	{
		errno_save = errno;
		LogError("doSftpUpLoad(): fopen() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		CloseTraceLog();
		return -1;
	}

	// Initalize curllib globally...
	if ((ret = curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_global_init() failed. ret=%d",ret);
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Initialize curllib...
	if ((pCurl = curl_easy_init()) == NULL)
	{
		LogError("doSftpUpLoad(): curl_easy_init() failed.");
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

//config.trace_ascii = 1; /* enable ascii tracing */
//curl_easy_setopt(pCurl, CURLOPT_DEBUGFUNCTION, my_trace);
//curl_easy_setopt(pCurl, CURLOPT_DEBUGDATA, &config);
 
	// Let's have curllib generate a human readable error message for any possible curllib errors...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_ERRORBUFFER, CurlErrorMsg)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_ERRORBUFFER) failed. ret=%d",ret);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Is the Log Level set to the max...
	if (GetLogLevel() >= LOG_VERBOSE)
	{
		// Yep...
		// Enable curllib's verbose logging...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_VERBOSE, TRUE)) != CURLE_OK)
			LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_VERBOSE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

		// Redirect the verbose logging to our Trace Log File...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_STDERR, g_fpLog)) != CURLE_OK)
			LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_STDERR) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
	} // if (GetLogLevel() >= LOG_VERBOSE)

	// Disable curllib's progress bar (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, TRUE)) != CURLE_OK)
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_NOPROGESS) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// DIsable curllib's use of dignals (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, TRUE)) != CURLE_OK)
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_NOSIGNAL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// Tell curllib we are gonna do an upload...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_UPLOAD, TRUE)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_UPLOAD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the port that we will be using...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_PORT, port)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_PORT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the UserName and Password...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_USERPWD, userNamePassword)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_USERPWD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the url of the FTP Server...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_URL, url)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_URL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// We need to pass the fp to the callback function (read_function())...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_READDATA, fp)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_READDATA) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify which callback function...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_READFUNCTION, read_function)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_READFUNCTION) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the outgoing file size...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_INFILESIZE_LARGE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the necessary SSH authorizations to curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_AUTH_TYPES,  CURLSSH_AUTH_PUBLICKEY
															  + CURLSSH_AUTH_PASSWORD
															  + CURLSSH_AUTH_HOST)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_SSH_AUTH_TYPES) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSH Public Key File Name to curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_PUBLIC_KEYFILE, sshPublicKeyFile)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_SSH_PUBLIC_KEYFILE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSH Private Key File Name to curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_PRIVATE_KEYFILE, sshPrivateKeyFile)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_SSH_PRIVATE_KEYFILE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the Checksum of the remote host's public key...
// I am commenting this Checksum authentication.
// The SFTP Server (i.e. the IBM 4690 SFTP Server) is returning the checksum for
// an "IBM Only DSA Public Key" (i.e. adxsshdk.pbk).  I cannot explain why the IBM 4690 SFTP
// Server is behaving like this (but I do see that the IBM 4690 SFTP Server is using OpenSSH
// (not libssh2).
// Regardless, the SFTP transfer completes OK so this Checksum test is nuked.
//	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, "c61efe5748dbb19708137dd9b0ec9575")) != CURLE_OK) // adxsshdk.pbk
//	{
//		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
//		curl_easy_cleanup(pCurl);
//		curl_global_cleanup();
//		fclose(fp);
//		CloseTraceLog();
//		return -1;
//	}

	// Specify the SSH Pass Phrase (the IBM 4690 has a problem with a Pass Phrase so our
	// SSH keys do not use a Pass Phrase)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_KEYPASSWD, "")) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_setopt(CURLOPT_KEYPASSWD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Now do the FTP...
	if ((ret = curl_easy_perform(pCurl)) != CURLE_OK)
	{
		LogError("doSftpUpLoad(): curl_easy_perform() failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Cleanup...
	curl_easy_cleanup(pCurl);
	pCurl = NULL;

	// Cleanup the curllib globals...
	curl_global_cleanup();

	// Close the outgoing file...
	fclose(fp);
	fp = NULL;

	// We're outta here...
	LogMajor("doSftpUpLoad(): Completed OK.");
	CloseTraceLog();
	return 0;
} // doSftpUpLoad()

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

size_t write_function(void   *bufptr,
			 		  size_t  size,
					  size_t  nMemb,
					  void   *pUser)
{
	// Assign the File Pointer that we will use...
	FILE *fp = (FILE*)pUser;
	size_t ret = 0;
	int errno_save = -1;

	LogMinor("write_function(): size=%d nMemb=%d.",size,nMemb);
	LogVerbose("write_function(): bufptr=%s",bufptr);

	// If a zero length was passed, then the download file on the FTP server is empty...
	if ((size*nMemb) == 0)
	{
		LogMajor("write_function(): size*nMemb=0. The file to be downloaded is empty on the FTP server.");
		return 0;
	} // if ((size*nMemb) == 0)

	// Write the passed data (from curllib) to the download file...
	ret = fwrite(bufptr,size,nMemb,fp);
	errno_save = errno;

	// Did we have an I/O error...
	if ((ferror(fp)) != 0)
	{
		// Yep...
		LogError("write_function(): fwrite() failed. ret=%d errno=%d '%s'",ret,errno_save,sys_errlist[errno_save]);
		return 0;
	} // if ((ferror(fp) != 0)

	// Was all of data written to the download file...
	if (ret != (size*nMemb))
		LogError("write_function(): fwrite() did not write all of the data to the download file. ret=%d size=%d nMemb=%d",ret,size,nMemb);

	// We're outta here...
	LogMinor("write_function(): Returning %d.",ret);
	return ret;
} //  write_function()

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

int doFtpsDownLoad(void)
{
	static char url[] = "ftp://10.1.4.177/TEMP2/DOWNLOAD.BAT";
	static long port = 16987; // The FTPS default port is 21.
	static char userNamePassword[] = "tciuser:abc123";
	static char fileName[] = "C:\\temp5\\dandownload.bat";

	CURL *pCurl = NULL;
	CURLcode ret = CURLE_OK;
	char CurlErrorMsg[1000] = { '\0' };

	static char sslCertificateName[] = "TciSslCertificate.crt";
	static char sslCertificateType[] = "PEM";
	static char sslKeyName[] = "TciSslCertificate.crt";
	static char sslKeyType[] = "PEM";

	static FILE *fp = NULL;
	static int errno_save = -1;

	// Open our Trace Log...
	OpenTraceLog(TRACE_LOG_DEFAULT_LOG_PATH);

	// Open the file to be downloaded...
	if ((fp = fopen(fileName,"wb")) == NULL)
	{
		errno_save = errno;
		LogError("doFtpsDownLoad(): fopen() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		CloseTraceLog();
		return -1;
	}

	// Initalize curllib globally...
	if ((ret = curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_global_init() failed. ret=%d",ret);
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Initialize curllib...
	if ((pCurl = curl_easy_init()) == NULL)
	{
		LogError("doFtpsDownLoad(): curl_easy_init() failed.");
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Let's have curllib generate a human readable error message for any possible curllib errors...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_ERRORBUFFER, CurlErrorMsg)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_ERRORBUFFER) failed. ret=%d",ret);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Is the Log Level set to the max...
	if (GetLogLevel() >= LOG_VERBOSE)
	{
		// Yep...
		// Enable curllib's verbose logging...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_VERBOSE, TRUE)) != CURLE_OK)
			LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_VERBOSE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

		// Redirect the verbose logging to our Trace Log File...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_STDERR, g_fpLog)) != CURLE_OK)
			LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_STDERR) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
	} // if (GetLogLevel() >= LOG_VERBOSE)

	// Disable curllib's progress bar (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, TRUE)) != CURLE_OK)
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_NOPROGESS) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// DIsable curllib's use of dignals (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, TRUE)) != CURLE_OK)
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_NOSIGNAL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// Tell curllib the url of the FTP Server...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_URL, url)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_URL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the port that we will be using...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_PORT, port)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_PORT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the UserName and Password...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_USERPWD, userNamePassword)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_USERPWD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// NOTE:  SINCE WE ARE DOING A DOWNLOAD, WE DON'T HAVE TO TELL curllib (BECAUSE IT ASSUMES
	//        A DOWNLOAD UNLESS EXPLICITLY TOLD TO PERFORM AN UPLOAD).

	// We need to pass the fp to the callback function (write_function())...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, fp)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_WRITEDATA) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify which callback function...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, write_function)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_easy_setopt(CURLOPT_WRITEFUNCTION) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib to use SSL for all communications...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_USE_SSL, CURLUSESSL_ALL)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_USE_SSL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib to use AUTH SSL (not AUTH TLS)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_SSL)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_FTPSSLAUTH) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Disable the "Clear Command Channel" option in curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_FTP_SSL_CCC, CURLFTPSSL_CCC_NONE)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_FTP_SSL_CCC) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Certificate File Name...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLCERT, sslCertificateName)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLCERT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Certificate Type ("PEM") (don't use "DER")...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLCERTTYPE, sslCertificateType)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLCERTTYPE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Private Key File Name...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLKEY, sslKeyName)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLKEY) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSL Private Key Type ("PEM") (don't use "DER")...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLKEYTYPE, sslKeyType)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLKEYTYPE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify to use the default crypto engine...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLENGINE_DEFAULT, sslKeyType)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLENGINE_DEFAULT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSLv3 SSL will be used...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSLVERSION) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Don't determine the authenticity of the peer's certificate...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, FALSE)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSL_VERIFYPEER) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Don't determine the authenticity of the server's certificate...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, FALSE)) != CURLE_OK)
	{
		LogError("doFtpsUpLoad(): curl_easy_setopt(CURLOPT_SSL_VERIFYHOST) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Now do the FTP...
	if ((ret = curl_easy_perform(pCurl)) != CURLE_OK)
	{
		LogError("doFtpsDownLoad(): curl_easy_perform() failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Cleanup...
	curl_easy_cleanup(pCurl);
	pCurl = NULL;

	// Cleanup the curllib globals...
	curl_global_cleanup();

	// Close the outgoing file...
	fclose(fp);
	fp = NULL;

	// We're outta here...
	LogMajor("doFtpsDownLoad(): Completed OK.");
	CloseTraceLog();
	return 0;
} // doFtpsDownLoad()

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

int doSftpDownLoad(void)
{
	static char url[] = "sftp://10.1.4.78/TEMP2/DOWNLOAD.BAT";
	static long port = 16987; // The SFTP default port is 22.
	static char userNamePassword[] = "ftpuser:ibm4990";
	static char fileName[] = "C:\\temp5\\dandownload.bat";

	CURL *pCurl = NULL;
	CURLcode ret = CURLE_OK;
	char CurlErrorMsg[1000] = { '\0' };

	static char sshPublicKeyFile[]  = "tcidsa.pbk";
	static char sshPrivateKeyFile[] = "tcidsa.dat";

	static FILE *fp = NULL;
	static int errno_save = -1;

	// Open our Trace Log...
	OpenTraceLog(TRACE_LOG_DEFAULT_LOG_PATH);

	// Open the file to be downloaded...
	if ((fp = fopen(fileName,"wb")) == NULL)
	{
		errno_save = errno;
		LogError("doSftpDownLoad(): fopen() failed. errno=%d '%s'",errno_save,sys_errlist[errno_save]);
		CloseTraceLog();
		return -1;
	}

	// Initalize curllib globally...
	if ((ret = curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_global_init() failed. ret=%d",ret);
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Initialize curllib...
	if ((pCurl = curl_easy_init()) == NULL)
	{
		LogError("doSftpDownLoad(): curl_easy_init() failed.");
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Let's have curllib generate a human readable error message for any possible curllib errors...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_ERRORBUFFER, CurlErrorMsg)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_ERRORBUFFER) failed. ret=%d",ret);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Is the Log Level set to the max...
	if (GetLogLevel() >= LOG_VERBOSE)
	{
		// Yep...
		// Enable curllib's verbose logging...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_VERBOSE, TRUE)) != CURLE_OK)
			LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_VERBOSE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

		// Redirect the verbose logging to our Trace Log File...
		if ((ret = curl_easy_setopt(pCurl, CURLOPT_STDERR, g_fpLog)) != CURLE_OK)
			LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_STDERR) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
	} // if (GetLogLevel() >= LOG_VERBOSE)

	// Disable curllib's progress bar (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, TRUE)) != CURLE_OK)
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_NOPROGESS) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// DIsable curllib's use of dignals (this may not be necessary)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, TRUE)) != CURLE_OK)
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_NOSIGNAL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);

	// Tell curllib the url of the FTP Server...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_URL, url)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_URL) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the port that we will be using...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_PORT, port)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_PORT) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Tell curllib the UserName and Password...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_USERPWD, userNamePassword)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_USERPWD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// NOTE:  SINCE WE ARE DOING A DOWNLOAD, WE DON'T HAVE TO TELL curllib (BECAUSE IT ASSUMES
	//        A DOWNLOAD UNLESS EXPLICITLY TOLD TO PERFORM AN UPLOAD).

	// We need to pass the fp to the callback function (write_function())...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, fp)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_WRITEDATA) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify which callback function...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, write_function)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_WRITEFUNCTION) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the necessary SSH authorizations to curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_AUTH_TYPES,  CURLSSH_AUTH_PUBLICKEY
															  + CURLSSH_AUTH_PASSWORD
															  + CURLSSH_AUTH_HOST)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_SSH_AUTH_TYPES) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSH Public Key File Name to curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_PUBLIC_KEYFILE, sshPublicKeyFile)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_SSH_PUBLIC_KEYFILE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the SSH Private Key File Name to curllib...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_PRIVATE_KEYFILE, sshPrivateKeyFile)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_SSH_PRIVATE_KEYFILE) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Specify the Checksum of the remote host's public key...
// I am commenting this Checksum authentication.
// The SFTP Server (i.e. the IBM 4690 SFTP Server) is returning the checksum for
// an "IBM Only DSA Public Key" (i.e. adxsshdk.pbk).  I cannot explain why the IBM 4690 SFTP
// Server is behaving like this (but I do see that the IBM 4690 SFTP Server is using OpenSSH
// (not libssh2).
// Regardless, the SFTP transfer completes OK so this Checksum test is nuked.
//	if ((ret = curl_easy_setopt(pCurl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, "c61efe5748dbb19708137dd9b0ec9575")) != CURLE_OK) // adxsshdk.pbk
//	{
//		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
//		curl_easy_cleanup(pCurl);
//		curl_global_cleanup();
//		fclose(fp);
//		CloseTraceLog();
//		return -1;
//	}

	// Specify the SSH Pass Phrase (the IBM 4690 has a problem with a Pass Phrase so our
	// SSH keys do not use a Pass Phrase)...
	if ((ret = curl_easy_setopt(pCurl, CURLOPT_KEYPASSWD, "")) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_setopt(CURLOPT_KEYPASSWD) failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Now do the FTP...
	if ((ret = curl_easy_perform(pCurl)) != CURLE_OK)
	{
		LogError("doSftpDownLoad(): curl_easy_perform() failed. ret=%d CurlErrorMsg=%s",ret,CurlErrorMsg);
		curl_easy_cleanup(pCurl);
		curl_global_cleanup();
		fclose(fp);
		CloseTraceLog();
		return -1;
	}

	// Cleanup...
	curl_easy_cleanup(pCurl);
	pCurl = NULL;

	// Cleanup the curllib globals...
	curl_global_cleanup();

	// Close the outgoing file...
	fclose(fp);
	fp = NULL;

	// We're outta here...
	LogMajor("doSftpDownLoad(): Completed OK.");
	CloseTraceLog();
	return 0;
} // doSftpDownLoad()

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

int _tmain(int argc, _TCHAR* argv[])
{
	char usage[] = "usage: win32ftptests.exe [1(FTPS upload) 2(FTPS download) 3(SFTP upload) 4(SFTP download)]\n";

	// We gotta have at least 1 command line argument...
	if (argc <= 1)
	{
		printf(usage);
		return 0;
	}

	// Execute the appropo FTP test...
	switch (_wtoi(argv[1]))
	{
		case 1:
			return doFtpsUpLoad();
		case 2:
			return doFtpsDownLoad();
		case 3:
			return doSftpUpLoad();
		case 4:
			return doSftpDownLoad();
		default:
			printf(usage);
			return -1;
	} // switch (_wtoi(argv[1]))
}

