ftp/Win32.cpp

// Win32.cpp : Defines the entry point for the console application.
//
// Summary: sfFTPLib c++ demonstration
//
// Technical support: support@smartftp.com
//
// Copyright (c) SmartSoft Ltd.

// Note for Non-Unicode Build:
// If you get one of the following errors add comsuppw.lib (Release) or comsuppwd.lib (Debug) to the linker's "Additional Dependencies".
// Win32.obj : error LNK2019: unresolved external symbol "wchar_t * __stdcall _com_util::ConvertStringToBSTR(char const *)"
// Win32.obj : error LNK2019: unresolved external symbol "char * __stdcall _com_util::ConvertBSTRToString(wchar_t *)"

#include "stdafx.h"
#include "Win32.h"
#include <atlsafe.h>
#include <atlcom.h>

CComSafeArray<byte> GetKey(const CString& strPassword)
{
	// TODO: Hash strPassword (e.g. MD5) and set data
	byte data[128 / 8];

	CComSafeArray<byte> key;
	ATLENSURE_SUCCEEDED(key.Create(sizeof(data)));
	const byte* begin = reinterpret_cast<const byte*>(data);
	const byte* end = reinterpret_cast<const byte*>(data) + sizeof(data);
	const byte* it = begin;
	for (long Index = 0; it != end; Index++, ++it)
	{
		key.SetAt(Index, *it);
	}
	return key;
}

// nMode: 1 = Read
//        2 = Write
sfFTPLib::IStreamFilterPtr CreateStreamFilter(const CString &strPassword, int nBits, int nMode)
{
	sfFTPLib::IStreamFilterPtr pStream;

	if(nMode == 1)
	{
		sfFTPLib::IAES128CTRReadStreamPtr pRead;
		ATLENSURE_SUCCEEDED(pRead.CreateInstance(__uuidof(sfFTPLib::AES128CTRReadStream)));

		ATLENSURE_SUCCEEDED(pRead->raw_SetKey(CComVariant(GetKey(strPassword))));
		pStream = pRead;
	}
	else if(nMode == 2)
	{
		sfFTPLib::IAES128CTRWriteStreamPtr pWrite;
		ATLENSURE_SUCCEEDED(pWrite.CreateInstance(__uuidof(sfFTPLib::AES128CTRWriteStream)));
		ATLENSURE_SUCCEEDED(pWrite->raw_SetKey(CComVariant(GetKey(strPassword))));
		pStream = pWrite;
	}

	return pStream;
}

void DownloadUsingStream(_In_ sfFTPLib::IFTPConnection* connection)
{
	// create a storage
	CComPtr<IStorage> pStorage;
	ATLENSURE_SUCCEEDED(::StgCreateDocfile(_T("Storage"), STGM_NOSCRATCH | STGM_TRANSACTED | STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pStorage));

	CComPtr<IStream> pStream;
	ATLENSURE_SUCCEEDED(pStorage->CreateStream(L"CONTENTS", STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream));

	connection->DownloadFileEx(_T("History.txt"), CComVariant(pStream.p), 0, 0, nullptr);
	_tprintf(_T("DownloadFileEx() successful.\n"));

	pStream->Commit(STGC_OVERWRITE | STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE);
	pStorage->Commit(STGC_OVERWRITE | STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE);
}

void ReadDirectory(_In_ sfFTPLib::IFTPConnection* connection, _In_z_ PCWSTR directory)
{
	connection->ChangeDirectory(directory);

	// read listing
	CComPtr<sfFTPLib::IFTPItems> pDirectory;
	ATLENSURE_SUCCEEDED(connection->raw_ReadDirectory(&pDirectory));

	int nCount = pDirectory->Count;
	_tprintf(_T("Count = %d\n"), nCount);

	// Enum
	if (nCount > 0)
	{
		IEnumVARIANTPtr pEnum = pDirectory->_NewEnum;

		VARIANT *pArrVariant = new VARIANT[nCount];
		ULONG CeltFetched;
		if (SUCCEEDED(pEnum->Next(nCount, pArrVariant, &CeltFetched)))
		{
			for (ULONG i = 0; i < CeltFetched; i++)
			{
				if (pArrVariant[i].vt == VT_DISPATCH)
				{
					sfFTPLib::IFTPItemPtr pFTPItem = pArrVariant[i].pdispVal;
					if (pFTPItem)
					{
						_tprintf(_T("Type=0x%x; Name=%s; Size=%I64u\n"), pFTPItem->Type, (LPCTSTR) pFTPItem->Name, pFTPItem->Size);

						if (pFTPItem->Type == sfFTPLib::ftpItemTypeSymbolicLink)
						{
							_tprintf(_T("LinkPoint=%s"), (LPCTSTR) pFTPItem->LinkPoint);
						}
					}
				}
				::VariantClear(&pArrVariant[i]);
			}
		}

		delete [] pArrVariant;
	}
}

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	// init COM
	ATLENSURE_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED));

	try
	{
		CComPtr<sfFTPLib::IGlobal> global;
		ATLENSURE_SUCCEEDED(global.CoCreateInstance(__uuidof(sfFTPLib::Global)));

		// If LoadLicense is not called a trial license is automatically obtained from the activation server. The FTP Library uses WinHTTP to access
		// the activation server at www.smartftp.com (TLS, port 443). Ensure that your application is not blocked by any firewall.
		// TODO: insert the provided serial after the purchase of a license
		//global->LoadLicense(_T("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));

		CComPtr<sfFTPLib::IFTPConnection> ftp;
		ATLENSURE_SUCCEEDED(ftp.CoCreateInstance(__uuidof(sfFTPLib::FTPConnectionMTA)));

		CComPtr<sfFTPLib::IFileLogger> fileLogger;
		ATLENSURE_SUCCEEDED(fileLogger.CoCreateInstance(__uuidof(sfFTPLib::FileLogger)));
		fileLogger->Putfile(L"Win32Demo.log");

		CComQIPtr<sfFTPLib::ILogger> logger(fileLogger);
		ftp->Logger = logger;

		// AUTH TLS
		ftp->_Protocol = sfFTPLib::ftpProtocolSSLExplicit;
		ftp->Host = _T("smartftp.com");
		ftp->Port = 21;
		ftp->Username = _T("anonymous");
		ftp->Password = _T("bla@bla.com");
		ftp->Passive = VARIANT_TRUE;

		// No Proxy
		//ftp->Proxy->Type = sfFTPLib::ftpProxyTypeNone;
		//ftp->Proxy->Host =_T("192.168.1.10");
		//ftp->Proxy->Port = 1080;
		//ftp->Proxy->Authentication = VARIANT_TRUE;
		//ftp->Proxy->Username = _T("user");
		//ftp->Proxy->Password = _T("pass");

		ftp->Connect();
		
		ReadDirectory(ftp, L"/");

		// download file
		ftp->DownloadFile(L"History.txt", L"History.txt", 0, 0);
		_tprintf(_T("DownloadFile() successful.\n"));
		_tprintf(_T("LastTransferBytes = %I64u B\n"), ftp->LastTransferBytes);
		_tprintf(_T("LastTransferTime = %d s\n"), ftp->LastTransferTime);
		_tprintf(_T("LastTransferSpeed = %d B/s\n"), ftp->LastTransferSpeed);

		// DownloadFileEx using IStream
		//DownloadUsingStream(ftp);
	}
	catch(_com_error &e)
	{
		_tprintf(_T("Com Error:\n"));
		_tprintf(_T("Code = %08lx\n"), e.Error());
		_tprintf(_T("Code meaning = %s\n"), (LPCTSTR) e.ErrorMessage());
		_tprintf(_T("Source = %s\n"), (LPCTSTR) e.Source());
		_tprintf(_T("Error Description = %s\n"), (LPCTSTR) e.Description());
	}
	catch(...)
	{
		_tprintf(_T("Unknown Error\n"));
	}
	::CoUninitialize();

	return nRetCode;
}