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.
#include "stdafx.h"
#include "Win32.h"
#include <string_view>
#include <memory>
#if 0
// free result with ::CertFreeCertificateContext
[[nodiscard]] PCCERT_CONTEXT GetRemoteCertificate(_In_ sfFTPLib::IFTPConnection* connection)
{
ATL::CComPtr<sfFTPLib::IAsyncSSLSocketLayer> tlsSocketLayer;
ATLENSURE_SUCCEEDED(connection->get_SSLSocketLayer(&tlsSocketLayer));
ATL::CComVariant remoteCertVariant;
ATLENSURE_SUCCEEDED(tlsSocketLayer->get_RemoteCert(&remoteCertVariant));
// Validate that we actually received a Byte Array
ATLENSURE(remoteCertVariant.vt == (VT_ARRAY | VT_UI1));
// Use CComSafeArray to access the raw data
ATL::CComSafeArray<byte> saCert(remoteCertVariant.parray);
const auto pbData{ reinterpret_cast<byte*>(saCert.GetData()) };
const auto cbData{ saCert.GetCount() };
// Create a certificate context
return reinterpret_cast<PCCERT_CONTEXT>(::CertCreateContext(CERT_STORE_CERTIFICATE_CONTEXT, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbData, cbData, 0, NULL));
}
#endif
[[nodiscard]] ATL::CComSafeArray<byte> GetKey(const std::wstring_view password)
{
// TODO: Hash password (e.g. SHA1) and set data
byte data[128 / 8]{};
ATL::CComSafeArray<byte> key;
ATLENSURE_SUCCEEDED(key.Create(sizeof(data)));
const auto begin{ reinterpret_cast<const byte*>(data) };
const auto end{ reinterpret_cast<const byte*>(data) + sizeof(data) };
auto it{ begin };
for (long Index = 0; it != end; Index++, ++it)
{
key.SetAt(Index, *it);
}
return key;
}
// mode: 1 = Read, 2 = Write
[[nodiscard]] ATL::CComPtr<sfFTPLib::IStreamFilter> CreateStreamFilter(const std::wstring_view password, int nBits, int mode)
{
ATL::CComPtr<sfFTPLib::IStreamFilter> stream;
if (mode == 1)
{
ATL::CComPtr<sfFTPLib::IAES128CTRReadStream> readStream;
ATLENSURE_SUCCEEDED(readStream.CoCreateInstance(__uuidof(sfFTPLib::AES128CTRReadStream)));
ATLENSURE_SUCCEEDED(readStream->SetKey(const_cast<byte*>(reinterpret_cast<const byte*>(password.data())), static_cast<ULONG>(password.size())));
stream = readStream;
}
else if (mode == 2)
{
ATL::CComPtr<sfFTPLib::IAES128CTRWriteStream> writeStream;
ATLENSURE_SUCCEEDED(writeStream.CoCreateInstance(__uuidof(sfFTPLib::AES128CTRWriteStream)));
ATLENSURE_SUCCEEDED(writeStream->SetKey(const_cast<byte*>(reinterpret_cast<const byte*>(password.data())), static_cast<ULONG>(password.size())));
stream = writeStream;
}
return stream;
}
void DownloadUsingStream(_In_ sfFTPLib::IFTPConnection* connection)
{
// create a storage
ATL::CComPtr<IStorage> storage;
ATLENSURE_SUCCEEDED(::StgCreateDocfile(L"Storage", STGM_NOSCRATCH | STGM_TRANSACTED | STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &storage));
ATL::CComPtr<IStream> stream;
ATLENSURE_SUCCEEDED(storage->CreateStream(L"CONTENTS", STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream));
ATLENSURE_SUCCEEDED(connection->DownloadFileEx(ATL::CComBSTR(L"This is a DEMO server.txt"), ATL::CComVariant(stream.p), 0, MAXULONGLONG, nullptr));
::wprintf(L"DownloadFileEx() successful.\n");
stream->Commit(STGC_OVERWRITE | STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE);
storage->Commit(STGC_OVERWRITE | STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE);
}
void ReadDirectory(_In_ sfFTPLib::IFTPConnection* connection, _In_z_ PCWSTR directory)
{
ATLENSURE_SUCCEEDED(connection->ChangeDirectory(ATL::CComBSTR(directory)));
// read listing
ATL::CComPtr<sfFTPLib::IFTPItems> directoryItems;
ATLENSURE_SUCCEEDED(connection->ReadDirectory(&directoryItems));
long count;
ATLENSURE_SUCCEEDED(directoryItems->get_Count(&count));
::wprintf(L"Count = %d\n", count);
// Enum
if (count > 0)
{
ATL::CComPtr<IUnknown> unkEnum;
directoryItems->get__NewEnum(&unkEnum);
ATL::CComQIPtr<IEnumVARIANT> pEnum(unkEnum);
const auto pArrVariant{ std::make_unique<VARIANT[]>(count) };
ULONG fetched;
if (SUCCEEDED(pEnum->Next(count, pArrVariant.get(), &fetched)))
{
for (ULONG i = 0; i < fetched; i++)
{
if (pArrVariant[i].vt == VT_DISPATCH)
{
ATL::CComQIPtr<sfFTPLib::IFTPItem> item{ pArrVariant[i].pdispVal };
sfFTPLib::ItemType itemType;
ATLENSURE_SUCCEEDED(item->get_Type(&itemType));
ATL::CComBSTR itemName;
ATLENSURE_SUCCEEDED(item->get_Name(&itemName));
ULONGLONG itemSize;
ATLENSURE_SUCCEEDED(item->get_Size(&itemSize));
::wprintf(L"Type=0x%x; Name=%s; Size=%I64u\n", itemType, (PCWSTR) itemName, itemSize);
if (itemType == sfFTPLib::ftpItemTypeSymbolicLink)
{
ATL::CComBSTR linkPoint;
item->get_LinkPoint(&linkPoint);
::wprintf(L"LinkPoint=%s", (PCWSTR) linkPoint);
}
}
::VariantClear(&pArrVariant[i]);
}
}
}
}
void SetFileLogger(_In_ sfFTPLib::IFTPConnection* connection)
{
ATL::CComPtr<sfFTPLib::IFileLogger> fileLogger;
ATLENSURE_SUCCEEDED(fileLogger.CoCreateInstance(__uuidof(sfFTPLib::FileLogger)));
ATLENSURE_SUCCEEDED(fileLogger->put_File(ATL::CComBSTR(L"Win32Demo.log")));
ATL::CComQIPtr<sfFTPLib::ILogger> logger{ fileLogger };
ATLENSURE_SUCCEEDED(connection->put_Logger(logger));
}
#if 0
void SetProxy(_In_ sfFTPLib::IFTPConnection* connection)
{
ATL::CComPtr<sfFTPLib::IProxySettings> proxy;
ATLENSURE_SUCCEEDED(connection->get_Proxy(&proxy));
ATLENSURE_SUCCEEDED(proxy->put_Type(sfFTPLib::ftpProxyTypeNone));
ATLENSURE_SUCCEEDED(proxy->put_Host(ATL::CComBSTR(L"192.168.1.10"));
ATLENSURE_SUCCEEDED(proxy->put_Port(1080));
ATLENSURE_SUCCEEDED(proxy->put_Authentication(VARIANT_TRUE));
ATLENSURE_SUCCEEDED(proxy->put_Username(ATL::CComBSTR(L"user"));
ATLENSURE_SUCCEEDED(proxy->put_Password(ATL::CComBSTR(L"pass"));
}
#endif
int wmain(int argc, wchar_t* argv[], wchar_t* envp[])
{
ATLENSURE_SUCCEEDED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED));
ATL::CComPtr<sfFTPLib::IGlobal> global;
ATLENSURE_SUCCEEDED(global.CoCreateInstance(__uuidof(sfFTPLib::Global)));
// If LoadLicense is not called, the component reads the product key from the Windows registry.
// The product key can be set using the supplied SetProductKey.cmd script from the installation folder.
// If no product key is found in the registry, e.g. during the trial period, 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: For manual activation, use the provided product key after purchasing a license.
//ATLENSURE_SUCCEEDED(global->LoadLicense(ATL::CComBSTR(L"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")));
ATL::CComPtr<sfFTPLib::IFTPConnection> ftp;
ATLENSURE_SUCCEEDED(ftp.CoCreateInstance(__uuidof(sfFTPLib::FTPConnectionMTA)));
SetFileLogger(ftp);
// AUTH TLS
ATLENSURE_SUCCEEDED(ftp->put_Protocol(sfFTPLib::ftpProtocolPreferTLS));
ATLENSURE_SUCCEEDED(ftp->put_Host(ATL::CComBSTR(L"ftp.smartftp.com")));
ATLENSURE_SUCCEEDED(ftp->put_Port(21));
ATLENSURE_SUCCEEDED(ftp->put_Username(ATL::CComBSTR(L"anonymous")));
ATLENSURE_SUCCEEDED(ftp->put_Password(ATL::CComBSTR(L"bla@bla.com")));
//ATLENSURE_SUCCEEDED(ftp->put_Passive(VARIANT_TRUE));
// if the FTP server (e.g. Microsoft FTP Service for IIS with virtual hosts) requires the HOST command, uncomment this line:
//ATLENSURE_SUCCEEDED(ftp->put_FEAT(sfFTPLib::ftpFEATCommandEnableBeforeAndAfterLogin));
// No Proxy
// SetProxy(ftp);
ATLENSURE_SUCCEEDED(ftp->Connect());
ReadDirectory(ftp, ATL::CComBSTR(L"/"));
// download file
ATLENSURE_SUCCEEDED(ftp->DownloadFileEx(ATL::CComBSTR(L"This is a DEMO server.txt"), ATL::CComVariant(L"This is a DEMO server.txt"), 0, MAXULONGLONG, nullptr));
::wprintf(L"DownloadFile() successful.\n");
ULONGLONG lastTransferBytes;
ATLENSURE_SUCCEEDED(ftp->get_LastTransferBytes(&lastTransferBytes));
::wprintf(L"LastTransferBytes = %I64u B\n", lastTransferBytes);
long lastTransferTime;
ATLENSURE_SUCCEEDED(ftp->get_LastTransferTime(&lastTransferTime));
::wprintf(L"LastTransferTime = %d s\n", lastTransferTime);
long lastTransferSpeed;
ATLENSURE_SUCCEEDED(ftp->get_LastTransferSpeed(&lastTransferSpeed));
::wprintf(L"LastTransferSpeed = %d B/s\n", lastTransferSpeed);
// DownloadFileEx using IStream
//DownloadUsingStream(ftp);
// Upload file
//ATLENSURE_SUCCEEDED(ftp->UploadFileEx(ATL::CComVariant(L"c:\\test.dat"), ATL::CComBSTR(L"test.dat"), 0, nullptr));
::CoUninitialize();
return 0;
}