Add sfSftp as a secure replacement for sfFtp

This commit is contained in:
Lukas Dürrenberger committed 2026-10-04 22:12:01 +02:00
1 parent 63dc503d20
commit fe8c6adb12
8 files changed
+1519

No files matched your search

+1
View File
@@ -33,6 +33,7 @@
#include <CSFML/Network/Http.h>
#include <CSFML/Network/IpAddress.h>
#include <CSFML/Network/Packet.h>
#include <CSFML/Network/Sftp.h>
#include <CSFML/Network/SocketSelector.h>
#include <CSFML/Network/SocketStatus.h>
#include <CSFML/Network/TcpListener.h>
+778
View File
@@ -0,0 +1,778 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <CSFML/Network/Export.h>
#include <CSFML/Network/IpAddress.h>
#include <CSFML/Network/Types.h>
#include <CSFML/System/TimeoutWithPredicate.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
////////////////////////////////////////////////////////////
/// \brief Values of a SFTP result
///
////////////////////////////////////////////////////////////
typedef enum
{
// General result values
sfSftpSuccess, ///< Operation completed successfully
sfSftpDisconnected, ///< The TCP socket has been disconnected
sfSftpTimeout, ///< Operation timed out
sfSftpRefused, ///< Connection refused
sfSftpError, ///< Generic error
// SSH result values
sfSftpBannerReceive, ///< Error during banner receive
sfSftpBannerSend, ///< Error during banner send
sfSftpInvalidMac, ///< Invalid message authentication code
sfSftpAllocationFailure, ///< Allocation failure
sfSftpSocketSend, ///< Error sending on socket
sfSftpKeyExchangeFailure, ///< Key exchange failed
sfSftpHostKeyInitialization, ///< Host key initialization failed
sfSftpHostKeySign, ///< Host key signing failed
sfSftpDecryptError, ///< Decryption failed
sfSftpProtocolError, ///< SSH protocol error
sfSftpPasswordExpired, ///< Password expired
sfSftpFileError, ///< File error
sfSftpMethodNone, ///< No method found
sfSftpAuthenticationFailed, ///< Authentication failed
sfSftpPublicKeyUnverified, ///< Public key unverified
sfSftpChannelOutOfOrder, ///< Channel out of order
sfSftpChannelFailure, ///< Channel failure
sfSftpChannelRequestDenied, ///< Channel request denied
sfSftpChannelUnknown, ///< Channel unknown
sfSftpChannelWindowExceeded, ///< Channel window exceeded
sfSftpChannelPacketExceeded, ///< Channel packet exceeded
sfSftpChannelClosed, ///< Channel closed
sfSftpChannelEofSent, ///< Channel EOF sent
sfSftpScpProtocol, ///< SCP protocol error
sfSftpZlibError, ///< Zlib error
sfSftpRequestDenied, ///< Request denied
sfSftpMethodNotSupported, ///< Method not supported
sfSftpInvalidData, ///< Invalid data
sfSftpPublicKeyProtocol, ///< Public key protocol error
sfSftpBufferTooSmall, ///< Buffer too small
sfSftpBadUse, ///< Bad usage
sfSftpCompressError, ///< Compression error
sfSftpOutOfBoundary, ///< Out of boundary
sfSftpAgentProtocol, ///< Agent protocol error
sfSftpSocketRecv, ///< Socket receive error
sfSftpEncryptError, ///< Encryption failed
sfSftpBadSocket, ///< Bad socket
sfSftpKnownHosts, ///< Known hosts error
sfSftpChannelWindowFull, ///< Channel window full
sfSftpKeyFileAuthenticationFailed, ///< Key file authentication failed
// SFTP result values
sfSftpEndOfFile, ///< End of file
sfSftpNoSuchFile, ///< No such file
sfSftpPermissionDenied, ///< Permission denied
sfSftpFailure, ///< Failure
sfSftpBadMessage, ///< Bad message
sfSftpNoConnection, ///< No connection
sfSftpConnectionLost, ///< Connection lost
sfSftpOperationUnsupported, ///< Operation unsupported
sfSftpInvalidHandle, ///< Invalid handle
sfSftpNoSuchPath, ///< No such path
sfSftpFileAlreadyExists, ///< File already exists
sfSftpWriteProtect, ///< Write protect
sfSftpNoMedia, ///< No media
sfSftpNoSpaceOnFileSystem, ///< No space on filesystem
sfSftpQuotaExceeded, ///< Quota exceeded
sfSftpUnknownPrincipal, ///< Unknown principal
sfSftpLockConflict, ///< Lock conflict
sfSftpDirectoryNotEmpty, ///< Directory not empty
sfSftpNotADirectory, ///< Not a directory
sfSftpInvalidFilename, ///< Invalid filename
sfSftpLinkLoop, ///< Link loop
sfSftpSftpError ///< Generic SFTP error
} sfSftpResultValue;
////////////////////////////////////////////////////////////
/// \brief Type of a remote file system entry
///
////////////////////////////////////////////////////////////
typedef enum
{
sfSftpFileNone, ///< No type
sfSftpFileNotFound, ///< The file does not exist
sfSftpFileRegular, ///< Regular file
sfSftpFileDirectory, ///< Directory
sfSftpFileSymlink, ///< Symbolic link
sfSftpFileBlock, ///< Block special file
sfSftpFileCharacter, ///< Character special file
sfSftpFileFifo, ///< FIFO or pipe
sfSftpFileSocket, ///< Socket
sfSftpFileUnknown ///< Unknown type
} sfSftpFileType;
////////////////////////////////////////////////////////////
/// \brief Attributes of a remote file system entry
///
/// Each attribute is only valid if the corresponding
/// has-flag is set.
///
/// Permissions use the POSIX permission bits, e.g. 0755.
/// Times are given in seconds since the Unix epoch.
///
////////////////////////////////////////////////////////////
typedef struct
{
const char* path; ///< Path to the entry, encoded in UTF-8
uint64_t size; ///< Size of the entry
uint64_t userId; ///< Owner user ID
uint64_t groupId; ///< Group ID
int64_t accessTime; ///< Last access time
int64_t modificationTime; ///< Last modification time
sfSftpFileType type; ///< Type of the entry
uint32_t permissions; ///< Permissions
bool hasType; ///< Whether the type is available
bool hasSize; ///< Whether the size is available
bool hasPermissions; ///< Whether the permissions are available
bool hasUserId; ///< Whether the owner user ID is available
bool hasGroupId; ///< Whether the group ID is available
bool hasAccessTime; ///< Whether the last access time is available
bool hasModificationTime; ///< Whether the last modification time is available
} sfSftpAttributes;
////////////////////////////////////////////////////////////
/// \brief Type of a SSH host key
///
////////////////////////////////////////////////////////////
typedef enum
{
sfSftpHostKeyUnknown, ///< Unknown key type
sfSftpHostKeyRsa, ///< RSA
sfSftpHostKeyDsa, ///< DSA
sfSftpHostKeyEcdsa256, ///< NIST P-256 ECDSA
sfSftpHostKeyEcdsa384, ///< NIST P-384 ECDSA
sfSftpHostKeyEcdsa521, ///< NIST P-521 ECDSA
sfSftpHostKeyEd25519 ///< ED25519
} sfSftpHostKeyType;
////////////////////////////////////////////////////////////
/// \brief SSH session information
///
/// The algorithm identifiers follow the RFC 4253 specification.
///
////////////////////////////////////////////////////////////
typedef struct
{
sfSftpHostKeyType hostKeyType; ///< Host key type
const uint8_t* hostKeyData; ///< Host key data
size_t hostKeySize; ///< Size of the host key data
uint8_t hostKeySha1[20]; ///< Host key SHA1 hash
uint8_t hostKeySha256[32]; ///< Host key SHA256 hash
const char* keyExchangeAlgorithm; ///< Key exchange algorithm used in the session
const char* hostKeyAlgorithm; ///< Host key algorithm used in the session
const char* clientToServerEncryptionAlgorithm; ///< Client to server encryption algorithm used in the session
const char* serverToClientEncryptionAlgorithm; ///< Server to client encryption algorithm used in the session
const char* clientToServerMacAlgorithm; ///< Client to server message authentication code algorithm used in the session
const char* serverToClientMacAlgorithm; ///< Server to client message authentication code algorithm used in the session
const char* clientToServerCompressionAlgorithm; ///< Client to server compression algorithm used in the session
const char* serverToClientCompressionAlgorithm; ///< Server to client compression algorithm used in the session
} sfSftpSessionInfo;
////////////////////////////////////////////////////////////
/// \brief Callback receiving the data of a downloaded file
///
/// The data is transferred in sequential blocks. The size of
/// the blocks can change over time.
///
/// \param data Pointer to the received data block
/// \param size Size of the received data block
/// \param userData User data passed to sfSftp_download
///
/// \return True to continue the transfer, false to abort it
///
////////////////////////////////////////////////////////////
typedef bool (*sfSftpDownloadCallback)(const void* data, size_t size, void* userData);
////////////////////////////////////////////////////////////
/// \brief Callback providing the data of a file to upload
///
/// Data to be sent should be copied into the data block and
/// \a size set to the actual number of bytes copied. When
/// called, \a size contains the size of the data block, which
/// can change over time.
///
/// \param data Pointer to the data block to fill
/// \param size Size of the data block, to be set to the number of bytes copied
/// \param userData User data passed to sfSftp_upload
///
/// \return True to continue the transfer, false to stop it e.g. because there is no more data to send
///
////////////////////////////////////////////////////////////
typedef bool (*sfSftpUploadCallback)(void* data, size_t* size, void* userData);
////////////////////////////////////////////////////////////
/// \brief Destroy a SFTP result
///
/// \param result SFTP result to destroy
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSftpResult_destroy(const sfSftpResult* result);
////////////////////////////////////////////////////////////
/// \brief Check if a SFTP result is a success
///
/// \param result SFTP result
///
/// \return True if the result is sfSftpSuccess
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API bool sfSftpResult_isOk(const sfSftpResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the value of a SFTP result
///
/// \param result SFTP result
///
/// \return Result value
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResultValue sfSftpResult_getValue(const sfSftpResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the message of a SFTP result
///
/// \param result SFTP result
///
/// \return The result message
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API const char* sfSftpResult_getMessage(const sfSftpResult* result);
////////////////////////////////////////////////////////////
/// \brief Destroy a SFTP path result
///
/// \param result SFTP path result to destroy
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSftpPathResult_destroy(const sfSftpPathResult* result);
////////////////////////////////////////////////////////////
/// \brief Check if a SFTP path result is a success
///
/// \param result SFTP path result
///
/// \return True if the result is sfSftpSuccess
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API bool sfSftpPathResult_isOk(const sfSftpPathResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the value of a SFTP path result
///
/// \param result SFTP path result
///
/// \return Result value
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResultValue sfSftpPathResult_getValue(const sfSftpPathResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the message of a SFTP path result
///
/// \param result SFTP path result
///
/// \return The result message
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API const char* sfSftpPathResult_getMessage(const sfSftpPathResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the path of a SFTP path result
///
/// \param result SFTP path result
///
/// \return The path, encoded in UTF-8
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API const char* sfSftpPathResult_getPath(const sfSftpPathResult* result);
////////////////////////////////////////////////////////////
/// \brief Destroy a SFTP attributes result
///
/// \param result SFTP attributes result to destroy
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSftpAttributesResult_destroy(const sfSftpAttributesResult* result);
////////////////////////////////////////////////////////////
/// \brief Check if a SFTP attributes result is a success
///
/// \param result SFTP attributes result
///
/// \return True if the result is sfSftpSuccess
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API bool sfSftpAttributesResult_isOk(const sfSftpAttributesResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the value of a SFTP attributes result
///
/// \param result SFTP attributes result
///
/// \return Result value
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResultValue sfSftpAttributesResult_getValue(const sfSftpAttributesResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the message of a SFTP attributes result
///
/// \param result SFTP attributes result
///
/// \return The result message
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API const char* sfSftpAttributesResult_getMessage(const sfSftpAttributesResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the attributes of a SFTP attributes result
///
/// The strings of the attributes stay valid until the
/// result is destroyed.
///
/// \param result SFTP attributes result
///
/// \return The attributes
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpAttributes sfSftpAttributesResult_getAttributes(const sfSftpAttributesResult* result);
////////////////////////////////////////////////////////////
/// \brief Destroy a SFTP listing result
///
/// \param result SFTP listing result to destroy
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSftpListingResult_destroy(const sfSftpListingResult* result);
////////////////////////////////////////////////////////////
/// \brief Check if a SFTP listing result is a success
///
/// \param result SFTP listing result
///
/// \return True if the result is sfSftpSuccess
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API bool sfSftpListingResult_isOk(const sfSftpListingResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the value of a SFTP listing result
///
/// \param result SFTP listing result
///
/// \return Result value
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResultValue sfSftpListingResult_getValue(const sfSftpListingResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the message of a SFTP listing result
///
/// \param result SFTP listing result
///
/// \return The result message
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API const char* sfSftpListingResult_getMessage(const sfSftpListingResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the number of entries in a SFTP listing result
///
/// \param result SFTP listing result
///
/// \return Number of directory entries
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API size_t sfSftpListingResult_getCount(const sfSftpListingResult* result);
////////////////////////////////////////////////////////////
/// \brief Get the attributes of an entry in a SFTP listing result
///
/// The strings of the attributes stay valid until the
/// result is destroyed.
///
/// \param result SFTP listing result
/// \param index Index of the entry to get
///
/// \return The attributes of the entry
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpAttributes sfSftpListingResult_getAttributes(const sfSftpListingResult* result, size_t index);
////////////////////////////////////////////////////////////
/// \brief Create a new SFTP object
///
/// \return A new sfSftp object
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftp* sfSftp_create(void);
////////////////////////////////////////////////////////////
/// \brief Destroy an existing SFTP object
///
/// \param sftp SFTP object to destroy
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSftp_destroy(const sfSftp* sftp);
////////////////////////////////////////////////////////////
/// \brief Connect to the specified SFTP server
///
/// \param sftp SFTP object
/// \param server Address of the server to connect to
/// \param port Port used for the connection, the default SFTP port is 22
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of the connection attempt
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_connect(sfSftp* sftp, sfIpAddress server, unsigned short port, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Disconnect the connection with the server
///
/// \param sftp SFTP object
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of disconnecting the connection with the server
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_disconnect(sfSftp* sftp, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Get SSH session information
///
/// After connecting to the server and before actually
/// logging in the SSH session information of the underlying
/// connection will be available.
///
/// The session information contains among other things the
/// public key identifying the remote host and the connection
/// parameters such as encryption and compression used.
///
/// Relying on the user to check the authenticity of the host
/// key is the typical method used to verify the connection
/// to the legitimate host. If connection security is a high
/// priority, examining the parameters and aborting the
/// connection if any weak algorithms are used is also possible.
///
/// The pointers in the session information stay valid until
/// the next call to this function or until the SFTP object
/// is destroyed.
///
/// \param sftp SFTP object
/// \param info Session information to fill
///
/// \return True if the session information is available, false otherwise
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API bool sfSftp_getSessionInfo(const sfSftp* sftp, sfSftpSessionInfo* info);
////////////////////////////////////////////////////////////
/// \brief Log in using a username and a password
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// \param sftp SFTP object
/// \param name User name
/// \param password Password
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of attempting to log in to the server
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_login(sfSftp* sftp, const char* name, const char* password, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Log in using a public/private key pair
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// The public and private key data should be provided in PEM
/// format. Even though it is technically possible to derive
/// the public key from the private key, due to backend
/// limitations, providing a pre-generated public key as well
/// is necessary for this function to be able to succeed.
///
/// If the private key is not protected by a passphrase the
/// passphrase should be set to the empty string or NULL.
///
/// \param sftp SFTP object
/// \param name User name
/// \param publicKeyData Public key data
/// \param publicKeyLength Public key data length
/// \param privateKeyData Private key data
/// \param privateKeyLength Private key data length
/// \param privateKeyPassphrase Private key passphrase, NULL terminated
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of attempting to log in to the server
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_loginWithKey(
sfSftp* sftp,
const char* name,
const char* publicKeyData,
size_t publicKeyLength,
const char* privateKeyData,
size_t privateKeyLength,
const char* privateKeyPassphrase,
sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Resolve a remote path into an absolute remote path
///
/// Paths can contain links and other reserved path identifiers
/// such as . and .. referring to the current directory and
/// parent directory respectively. This function determines
/// the absolute path, which does not contain links or . or ..
///
/// Resolving "." will return the absolute path to the current
/// working directory of the user after logging in to the SFTP
/// server.
///
/// \param sftp SFTP object
/// \param path Path to convert into an absolute path, encoded in UTF-8
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of converting the path into an absolute path
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpPathResult* sfSftp_resolvePath(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Get the current working directory on the server
///
/// This is an alias for calling sfSftp_resolvePath with ".".
///
/// \param sftp SFTP object
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of getting the current working directory
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpPathResult* sfSftp_getWorkingDirectory(sfSftp* sftp, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Get the attributes of a remote file or directory
///
/// Depending on whether \a path refers to a file or directory,
/// the attributes can contain e.g. the type of file, the file
/// owner, group, file size, modification and access times.
///
/// If links are not to be followed, \a followLinks can be set
/// to false. In this case the attributes of the link itself
/// will be returned.
///
/// \param sftp SFTP object
/// \param path Path to the remote file or directory whose attributes to get, encoded in UTF-8
/// \param followLinks True to follow links, false to return attributes of the link itself
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of getting the attributes
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpAttributesResult* sfSftp_getAttributes(
sfSftp* sftp,
const char* path,
bool followLinks,
sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Get the contents of the given directory
///
/// This function retrieves the sub-directories and files
/// contained in the given directory. It is not recursive.
///
/// \param sftp SFTP object
/// \param path Path of the directory whose contents to list, encoded in UTF-8
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of getting the contents of the given directory
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpListingResult* sfSftp_getDirectoryListing(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Create a new directory
///
/// The new directory is created as a child of the current
/// working directory.
///
/// A common permissions value is 0755 (rwxr-xr-x).
///
/// \param sftp SFTP object
/// \param path Path of the directory to create, encoded in UTF-8
/// \param permissions POSIX permissions of the directory to create
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of creating the directory
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_createDirectory(sfSftp* sftp,
const char* path,
uint32_t permissions,
sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Remove an existing directory
///
/// Use this function with caution, the directory will
/// be removed permanently!
///
/// \param sftp SFTP object
/// \param path Path of the directory to remove, encoded in UTF-8
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of removing the directory
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_deleteDirectory(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Rename an existing file or directory
///
/// In POSIX renaming and moving are synonymous. If you want
/// to move a file or directory from one place to another
/// you rename it from an old to a new path.
///
/// If a file exists at the specified new path, depending
/// on whether \a overwrite is set to true, the rename operation
/// will overwrite it or not. Non-empty directories cannot be
/// overwritten by this operation.
///
/// \param sftp SFTP object
/// \param oldPath Old path to the file or directory, encoded in UTF-8
/// \param newPath New path to the file or directory, encoded in UTF-8
/// \param overwrite True to allow overwriting a file that exists at \a newPath
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of the operation
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_rename(
sfSftp* sftp,
const char* oldPath,
const char* newPath,
bool overwrite,
sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Remove an existing file
///
/// Use this function with caution, the file will be
/// removed permanently!
///
/// \param sftp SFTP object
/// \param path Path to the file to remove, encoded in UTF-8
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of removing the file
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_deleteFile(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Download a file from the server
///
/// The file data is transferred in sequential blocks. For
/// every block of data transferred, the provided callback
/// is called.
///
/// The function returns once all the data in the remote file
/// has been transferred or an error occurs or the function
/// times out.
///
/// \param sftp SFTP object
/// \param remotePath Path of the remote file whose data to download, encoded in UTF-8
/// \param callback Callback to be called for every available data block
/// \param userData User data that will be passed to the callback
/// \param offset Byte offset into the remote file at which reading should start
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of downloading the file
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_download(
sfSftp* sftp,
const char* remotePath,
sfSftpDownloadCallback callback,
void* userData,
uint64_t offset,
sfTimeoutWithPredicate timeout);
////////////////////////////////////////////////////////////
/// \brief Upload a file to the server
///
/// The file data is transferred in sequential blocks. Every
/// time the function wants to send a new block of data the
/// provided callback is called.
///
/// The function returns once all the data has been sent or
/// an error occurs or the function times out.
///
/// If a file does not exist at the remote path yet, it will
/// be created with the provided permissions. A common
/// permissions value is 0644 (rw-r--r--).
///
/// \param sftp SFTP object
/// \param remotePath Path of the remote file in which to upload the data, encoded in UTF-8
/// \param callback Callback to be called for every data block to send
/// \param userData User data that will be passed to the callback
/// \param permissions POSIX permissions of the remote file if it has to be created
/// \param truncate True to truncate the remote file if it already exists
/// \param append True to append to the remote file if it already exists
/// \param offset Byte offset into the remote file at which writing should start
/// \param timeout Maximum time to wait, optionally a predicate can be provided for more fine-grained control
///
/// \return Result of uploading the file
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API sfSftpResult* sfSftp_upload(
sfSftp* sftp,
const char* remotePath,
sfSftpUploadCallback callback,
void* userData,
uint32_t permissions,
bool truncate,
bool append,
uint64_t offset,
sfTimeoutWithPredicate timeout);
+5
View File
@@ -37,6 +37,11 @@ typedef struct sfHttpRequest sfHttpRequest;
typedef struct sfHttpResponse sfHttpResponse;
typedef struct sfHttp sfHttp;
typedef struct sfPacket sfPacket;
typedef struct sfSftpAttributesResult sfSftpAttributesResult;
typedef struct sfSftpListingResult sfSftpListingResult;
typedef struct sfSftpPathResult sfSftpPathResult;
typedef struct sfSftpResult sfSftpResult;
typedef struct sfSftp sfSftp;
typedef struct sfSocketSelector sfSocketSelector;
typedef struct sfTcpListener sfTcpListener;
typedef struct sfTcpSocket sfTcpSocket;
+3
View File
@@ -19,6 +19,9 @@ set(SRC
${SRCROOT}/Packet.cpp
${SRCROOT}/PacketStruct.hpp
${INCROOT}/Packet.h
${SRCROOT}/Sftp.cpp
${SRCROOT}/SftpStruct.hpp
${INCROOT}/Sftp.h
${SRCROOT}/SocketSelector.cpp
${SRCROOT}/SocketSelectorStruct.hpp
${INCROOT}/SocketSelector.h
+547
View File
@@ -0,0 +1,547 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <CSFML/Network/ConvertIpAddress.hpp>
#include <CSFML/Network/Sftp.h>
#include <CSFML/Network/SftpStruct.hpp>
#include <CSFML/System/ConvertTimeoutWithPredicate.hpp>
#include <algorithm>
#include <cassert>
#include <chrono>
#include <filesystem>
namespace
{
////////////////////////////////////////////////////////////
[[nodiscard]] std::filesystem::path toPath(const char* path)
{
return std::filesystem::u8path(path ? path : "");
}
////////////////////////////////////////////////////////////
[[nodiscard]] std::string fromPath(const std::filesystem::path& path)
{
const auto string = path.u8string();
return {string.begin(), string.end()};
}
////////////////////////////////////////////////////////////
[[nodiscard]] sfSftpFileType convertFileType(std::filesystem::file_type type)
{
switch (type)
{
case std::filesystem::file_type::none:
return sfSftpFileNone;
case std::filesystem::file_type::not_found:
return sfSftpFileNotFound;
case std::filesystem::file_type::regular:
return sfSftpFileRegular;
case std::filesystem::file_type::directory:
return sfSftpFileDirectory;
case std::filesystem::file_type::symlink:
return sfSftpFileSymlink;
case std::filesystem::file_type::block:
return sfSftpFileBlock;
case std::filesystem::file_type::character:
return sfSftpFileCharacter;
case std::filesystem::file_type::fifo:
return sfSftpFileFifo;
case std::filesystem::file_type::socket:
return sfSftpFileSocket;
default:
return sfSftpFileUnknown;
}
}
////////////////////////////////////////////////////////////
// SFML stores the seconds since the Unix epoch in the file time
[[nodiscard]] std::int64_t convertFileTime(std::filesystem::file_time_type time)
{
return std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count();
}
////////////////////////////////////////////////////////////
[[nodiscard]] sfSftpAttributes convertAttributes(const sf::Sftp::Attributes& attributes, const std::string& path)
{
sfSftpAttributes result{};
result.path = path.c_str();
if (attributes.type)
{
result.hasType = true;
result.type = convertFileType(*attributes.type);
}
if (attributes.size)
{
result.hasSize = true;
result.size = *attributes.size;
}
if (attributes.permissions)
{
result.hasPermissions = true;
result.permissions = static_cast<std::uint32_t>(*attributes.permissions);
}
if (attributes.userId)
{
result.hasUserId = true;
result.userId = *attributes.userId;
}
if (attributes.groupId)
{
result.hasGroupId = true;
result.groupId = *attributes.groupId;
}
if (attributes.accessTime)
{
result.hasAccessTime = true;
result.accessTime = convertFileTime(*attributes.accessTime);
}
if (attributes.modificationTime)
{
result.hasModificationTime = true;
result.modificationTime = convertFileTime(*attributes.modificationTime);
}
return result;
}
////////////////////////////////////////////////////////////
[[nodiscard]] sfSftpResult* createResult(const sf::Sftp::Result& result)
{
return new sfSftpResult{result};
}
////////////////////////////////////////////////////////////
[[nodiscard]] sfSftpPathResult* createResult(const sf::Sftp::PathResult& result)
{
return new sfSftpPathResult{result, fromPath(result.getPath())};
}
////////////////////////////////////////////////////////////
[[nodiscard]] sfSftpAttributesResult* createResult(const sf::Sftp::AttributesResult& result)
{
return new sfSftpAttributesResult{result, fromPath(result.getAttributes().path)};
}
////////////////////////////////////////////////////////////
[[nodiscard]] sfSftpListingResult* createResult(const sf::Sftp::ListingResult& result)
{
std::vector<std::string> paths;
paths.reserve(result.getListing().size());
for (const auto& attributes : result.getListing())
paths.push_back(fromPath(attributes.path));
return new sfSftpListingResult{result, std::move(paths)};
}
} // namespace
////////////////////////////////////////////////////////////
void sfSftpResult_destroy(const sfSftpResult* result)
{
delete result;
}
////////////////////////////////////////////////////////////
bool sfSftpResult_isOk(const sfSftpResult* result)
{
assert(result);
return result->isOk();
}
////////////////////////////////////////////////////////////
sfSftpResultValue sfSftpResult_getValue(const sfSftpResult* result)
{
assert(result);
return static_cast<sfSftpResultValue>(result->getValue());
}
////////////////////////////////////////////////////////////
const char* sfSftpResult_getMessage(const sfSftpResult* result)
{
assert(result);
return result->getMessage().c_str();
}
////////////////////////////////////////////////////////////
void sfSftpPathResult_destroy(const sfSftpPathResult* result)
{
delete result;
}
////////////////////////////////////////////////////////////
bool sfSftpPathResult_isOk(const sfSftpPathResult* result)
{
assert(result);
return result->isOk();
}
////////////////////////////////////////////////////////////
sfSftpResultValue sfSftpPathResult_getValue(const sfSftpPathResult* result)
{
assert(result);
return static_cast<sfSftpResultValue>(result->getValue());
}
////////////////////////////////////////////////////////////
const char* sfSftpPathResult_getMessage(const sfSftpPathResult* result)
{
assert(result);
return result->getMessage().c_str();
}
////////////////////////////////////////////////////////////
const char* sfSftpPathResult_getPath(const sfSftpPathResult* result)
{
assert(result);
return result->Path.c_str();
}
////////////////////////////////////////////////////////////
void sfSftpAttributesResult_destroy(const sfSftpAttributesResult* result)
{
delete result;
}
////////////////////////////////////////////////////////////
bool sfSftpAttributesResult_isOk(const sfSftpAttributesResult* result)
{
assert(result);
return result->isOk();
}
////////////////////////////////////////////////////////////
sfSftpResultValue sfSftpAttributesResult_getValue(const sfSftpAttributesResult* result)
{
assert(result);
return static_cast<sfSftpResultValue>(result->getValue());
}
////////////////////////////////////////////////////////////
const char* sfSftpAttributesResult_getMessage(const sfSftpAttributesResult* result)
{
assert(result);
return result->getMessage().c_str();
}
////////////////////////////////////////////////////////////
sfSftpAttributes sfSftpAttributesResult_getAttributes(const sfSftpAttributesResult* result)
{
assert(result);
return convertAttributes(result->getAttributes(), result->Path);
}
////////////////////////////////////////////////////////////
void sfSftpListingResult_destroy(const sfSftpListingResult* result)
{
delete result;
}
////////////////////////////////////////////////////////////
bool sfSftpListingResult_isOk(const sfSftpListingResult* result)
{
assert(result);
return result->isOk();
}
////////////////////////////////////////////////////////////
sfSftpResultValue sfSftpListingResult_getValue(const sfSftpListingResult* result)
{
assert(result);
return static_cast<sfSftpResultValue>(result->getValue());
}
////////////////////////////////////////////////////////////
const char* sfSftpListingResult_getMessage(const sfSftpListingResult* result)
{
assert(result);
return result->getMessage().c_str();
}
////////////////////////////////////////////////////////////
size_t sfSftpListingResult_getCount(const sfSftpListingResult* result)
{
assert(result);
return result->getListing().size();
}
////////////////////////////////////////////////////////////
sfSftpAttributes sfSftpListingResult_getAttributes(const sfSftpListingResult* result, size_t index)
{
assert(result);
assert(index < result->getListing().size());
return convertAttributes(result->getListing()[index], result->Paths[index]);
}
////////////////////////////////////////////////////////////
sfSftp* sfSftp_create()
{
return new sfSftp;
}
////////////////////////////////////////////////////////////
void sfSftp_destroy(const sfSftp* sftp)
{
delete sftp;
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_connect(sfSftp* sftp, sfIpAddress server, unsigned short port, sfTimeoutWithPredicate timeout)
{
assert(sftp);
const auto address = convertIpAddress(server);
if (!address)
return createResult(sf::Sftp::Result(sf::Sftp::Result::Value::Error, "Invalid server address"));
return createResult(sftp->connect(*address, port, convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_disconnect(sfSftp* sftp, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->disconnect(convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
bool sfSftp_getSessionInfo(const sfSftp* sftp, sfSftpSessionInfo* info)
{
assert(sftp);
assert(info);
sftp->SessionInfo = sftp->getSessionInfo();
if (!sftp->SessionInfo)
return false;
const auto& sessionInfo = *sftp->SessionInfo;
const auto& hostKey = sessionInfo.hostKey;
*info = {};
info->hostKeyType = static_cast<sfSftpHostKeyType>(hostKey.type);
info->hostKeyData = reinterpret_cast<const std::uint8_t*>(hostKey.data.data());
info->hostKeySize = hostKey.data.size();
std::transform(hostKey.sha1.begin(),
hostKey.sha1.end(),
info->hostKeySha1,
[](std::byte b) { return std::to_integer<std::uint8_t>(b); });
std::transform(hostKey.sha256.begin(),
hostKey.sha256.end(),
info->hostKeySha256,
[](std::byte b) { return std::to_integer<std::uint8_t>(b); });
info->keyExchangeAlgorithm = sessionInfo.keyExchangeAlgorithm.c_str();
info->hostKeyAlgorithm = sessionInfo.hostKeyAlgorithm.c_str();
info->clientToServerEncryptionAlgorithm = sessionInfo.clientToServerEncryptionAlgorithm.c_str();
info->serverToClientEncryptionAlgorithm = sessionInfo.serverToClientEncryptionAlgorithm.c_str();
info->clientToServerMacAlgorithm = sessionInfo.clientToServerMacAlgorithm.c_str();
info->serverToClientMacAlgorithm = sessionInfo.serverToClientMacAlgorithm.c_str();
info->clientToServerCompressionAlgorithm = sessionInfo.clientToServerCompressionAlgorithm.c_str();
info->serverToClientCompressionAlgorithm = sessionInfo.serverToClientCompressionAlgorithm.c_str();
return true;
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_login(sfSftp* sftp, const char* name, const char* password, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->login(name ? name : "", password ? password : "", convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_loginWithKey(
sfSftp* sftp,
const char* name,
const char* publicKeyData,
size_t publicKeyLength,
const char* privateKeyData,
size_t privateKeyLength,
const char* privateKeyPassphrase,
sfTimeoutWithPredicate timeout)
{
assert(sftp);
assert(publicKeyData);
assert(privateKeyData);
return createResult(
sftp->login(name ? name : "",
publicKeyData,
publicKeyLength,
privateKeyData,
privateKeyLength,
privateKeyPassphrase ? privateKeyPassphrase : "",
convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpPathResult* sfSftp_resolvePath(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->resolvePath(toPath(path), convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpPathResult* sfSftp_getWorkingDirectory(sfSftp* sftp, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->getWorkingDirectory(convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpAttributesResult* sfSftp_getAttributes(sfSftp* sftp, const char* path, bool followLinks, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->getAttributes(toPath(path), followLinks, convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpListingResult* sfSftp_getDirectoryListing(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->getDirectoryListing(toPath(path), convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_createDirectory(sfSftp* sftp, const char* path, uint32_t permissions, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->createDirectory(toPath(path),
static_cast<std::filesystem::perms>(permissions),
convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_deleteDirectory(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->deleteDirectory(toPath(path), convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_rename(sfSftp* sftp, const char* oldPath, const char* newPath, bool overwrite, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->rename(toPath(oldPath), toPath(newPath), overwrite, convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_deleteFile(sfSftp* sftp, const char* path, sfTimeoutWithPredicate timeout)
{
assert(sftp);
return createResult(sftp->deleteFile(toPath(path), convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_download(sfSftp* sftp,
const char* remotePath,
sfSftpDownloadCallback callback,
void* userData,
uint64_t offset,
sfTimeoutWithPredicate timeout)
{
assert(sftp);
assert(callback);
return createResult(sftp->download(
toPath(remotePath),
[callback, userData](const void* data, std::size_t size) { return callback(data, size, userData); },
offset,
convertTimeoutWithPredicate(timeout)));
}
////////////////////////////////////////////////////////////
sfSftpResult* sfSftp_upload(
sfSftp* sftp,
const char* remotePath,
sfSftpUploadCallback callback,
void* userData,
uint32_t permissions,
bool truncate,
bool append,
uint64_t offset,
sfTimeoutWithPredicate timeout)
{
assert(sftp);
assert(callback);
return createResult(sftp->upload(
toPath(remotePath),
[callback, userData](void* data, std::size_t& size) { return callback(data, &size, userData); },
static_cast<std::filesystem::perms>(permissions),
truncate,
append,
offset,
convertTimeoutWithPredicate(timeout)));
}
+78
View File
@@ -0,0 +1,78 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2026 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Sftp.hpp>
#include <optional>
#include <string>
#include <vector>
////////////////////////////////////////////////////////////
// Internal structure of sfSftp
////////////////////////////////////////////////////////////
struct sfSftp : sf::Sftp
{
mutable std::optional<sf::Sftp::SessionInfo> SessionInfo;
};
////////////////////////////////////////////////////////////
// Internal structure of sfSftpResult
////////////////////////////////////////////////////////////
struct sfSftpResult : sf::Sftp::Result
{
};
////////////////////////////////////////////////////////////
// Internal structure of sfSftpPathResult
////////////////////////////////////////////////////////////
struct sfSftpPathResult : sf::Sftp::PathResult
{
std::string Path;
};
////////////////////////////////////////////////////////////
// Internal structure of sfSftpAttributesResult
////////////////////////////////////////////////////////////
struct sfSftpAttributesResult : sf::Sftp::AttributesResult
{
std::string Path;
};
////////////////////////////////////////////////////////////
// Internal structure of sfSftpListingResult
////////////////////////////////////////////////////////////
struct sfSftpListingResult : sf::Sftp::ListingResult
{
std::vector<std::string> Paths;
};
+1
View File
@@ -70,6 +70,7 @@ add_executable(test-csfml-network
Network/Ftp.test.cpp
Network/Http.test.cpp
Network/IpAddress.test.cpp
Network/Sftp.test.cpp
Network/SocketSelector.test.cpp
Network/SocketStatus.test.cpp
Network/TcpSocket.test.cpp
+106
View File
@@ -0,0 +1,106 @@
#include <CSFML/Network/Sftp.h>
#include <SFML/Network/Sftp.hpp>
#include <catch2/catch_test_macros.hpp>
TEST_CASE("[Network] sfSftp")
{
SECTION("sfSftpResultValue")
{
STATIC_CHECK(sfSftpSuccess == static_cast<int>(sf::Sftp::Result::Value::Success));
STATIC_CHECK(sfSftpDisconnected == static_cast<int>(sf::Sftp::Result::Value::Disconnected));
STATIC_CHECK(sfSftpTimeout == static_cast<int>(sf::Sftp::Result::Value::Timeout));
STATIC_CHECK(sfSftpRefused == static_cast<int>(sf::Sftp::Result::Value::Refused));
STATIC_CHECK(sfSftpError == static_cast<int>(sf::Sftp::Result::Value::Error));
STATIC_CHECK(sfSftpBannerReceive == static_cast<int>(sf::Sftp::Result::Value::BannerReceive));
STATIC_CHECK(sfSftpBannerSend == static_cast<int>(sf::Sftp::Result::Value::BannerSend));
STATIC_CHECK(sfSftpInvalidMac == static_cast<int>(sf::Sftp::Result::Value::InvalidMac));
STATIC_CHECK(sfSftpAllocationFailure == static_cast<int>(sf::Sftp::Result::Value::AllocationFailure));
STATIC_CHECK(sfSftpSocketSend == static_cast<int>(sf::Sftp::Result::Value::SocketSend));
STATIC_CHECK(sfSftpKeyExchangeFailure == static_cast<int>(sf::Sftp::Result::Value::KeyExchangeFailure));
STATIC_CHECK(sfSftpHostKeyInitialization == static_cast<int>(sf::Sftp::Result::Value::HostKeyInitialization));
STATIC_CHECK(sfSftpHostKeySign == static_cast<int>(sf::Sftp::Result::Value::HostKeySign));
STATIC_CHECK(sfSftpDecryptError == static_cast<int>(sf::Sftp::Result::Value::DecryptError));
STATIC_CHECK(sfSftpProtocolError == static_cast<int>(sf::Sftp::Result::Value::ProtocolError));
STATIC_CHECK(sfSftpPasswordExpired == static_cast<int>(sf::Sftp::Result::Value::PasswordExpired));
STATIC_CHECK(sfSftpFileError == static_cast<int>(sf::Sftp::Result::Value::FileError));
STATIC_CHECK(sfSftpMethodNone == static_cast<int>(sf::Sftp::Result::Value::MethodNone));
STATIC_CHECK(sfSftpAuthenticationFailed == static_cast<int>(sf::Sftp::Result::Value::AuthenticationFailed));
STATIC_CHECK(sfSftpPublicKeyUnverified == static_cast<int>(sf::Sftp::Result::Value::PublicKeyUnverified));
STATIC_CHECK(sfSftpChannelOutOfOrder == static_cast<int>(sf::Sftp::Result::Value::ChannelOutOfOrder));
STATIC_CHECK(sfSftpChannelFailure == static_cast<int>(sf::Sftp::Result::Value::ChannelFailure));
STATIC_CHECK(sfSftpChannelRequestDenied == static_cast<int>(sf::Sftp::Result::Value::ChannelRequestDenied));
STATIC_CHECK(sfSftpChannelUnknown == static_cast<int>(sf::Sftp::Result::Value::ChannelUnknown));
STATIC_CHECK(sfSftpChannelWindowExceeded == static_cast<int>(sf::Sftp::Result::Value::ChannelWindowExceeded));
STATIC_CHECK(sfSftpChannelPacketExceeded == static_cast<int>(sf::Sftp::Result::Value::ChannelPacketExceeded));
STATIC_CHECK(sfSftpChannelClosed == static_cast<int>(sf::Sftp::Result::Value::ChannelClosed));
STATIC_CHECK(sfSftpChannelEofSent == static_cast<int>(sf::Sftp::Result::Value::ChannelEofSent));
STATIC_CHECK(sfSftpScpProtocol == static_cast<int>(sf::Sftp::Result::Value::ScpProtocol));
STATIC_CHECK(sfSftpZlibError == static_cast<int>(sf::Sftp::Result::Value::ZlibError));
STATIC_CHECK(sfSftpRequestDenied == static_cast<int>(sf::Sftp::Result::Value::RequestDenied));
STATIC_CHECK(sfSftpMethodNotSupported == static_cast<int>(sf::Sftp::Result::Value::MethodNotSupported));
STATIC_CHECK(sfSftpInvalidData == static_cast<int>(sf::Sftp::Result::Value::InvalidData));
STATIC_CHECK(sfSftpPublicKeyProtocol == static_cast<int>(sf::Sftp::Result::Value::PublicKeyProtocol));
STATIC_CHECK(sfSftpBufferTooSmall == static_cast<int>(sf::Sftp::Result::Value::BufferTooSmall));
STATIC_CHECK(sfSftpBadUse == static_cast<int>(sf::Sftp::Result::Value::BadUse));
STATIC_CHECK(sfSftpCompressError == static_cast<int>(sf::Sftp::Result::Value::CompressError));
STATIC_CHECK(sfSftpOutOfBoundary == static_cast<int>(sf::Sftp::Result::Value::OutOfBoundary));
STATIC_CHECK(sfSftpAgentProtocol == static_cast<int>(sf::Sftp::Result::Value::AgentProtocol));
STATIC_CHECK(sfSftpSocketRecv == static_cast<int>(sf::Sftp::Result::Value::SocketRecv));
STATIC_CHECK(sfSftpEncryptError == static_cast<int>(sf::Sftp::Result::Value::EncryptError));
STATIC_CHECK(sfSftpBadSocket == static_cast<int>(sf::Sftp::Result::Value::BadSocket));
STATIC_CHECK(sfSftpKnownHosts == static_cast<int>(sf::Sftp::Result::Value::KnownHosts));
STATIC_CHECK(sfSftpChannelWindowFull == static_cast<int>(sf::Sftp::Result::Value::ChannelWindowFull));
STATIC_CHECK(sfSftpKeyFileAuthenticationFailed ==
static_cast<int>(sf::Sftp::Result::Value::KeyFileAuthenticationFailed));
STATIC_CHECK(sfSftpEndOfFile == static_cast<int>(sf::Sftp::Result::Value::EndOfFile));
STATIC_CHECK(sfSftpNoSuchFile == static_cast<int>(sf::Sftp::Result::Value::NoSuchFile));
STATIC_CHECK(sfSftpPermissionDenied == static_cast<int>(sf::Sftp::Result::Value::PermissionDenied));
STATIC_CHECK(sfSftpFailure == static_cast<int>(sf::Sftp::Result::Value::Failure));
STATIC_CHECK(sfSftpBadMessage == static_cast<int>(sf::Sftp::Result::Value::BadMessage));
STATIC_CHECK(sfSftpNoConnection == static_cast<int>(sf::Sftp::Result::Value::NoConnection));
STATIC_CHECK(sfSftpConnectionLost == static_cast<int>(sf::Sftp::Result::Value::ConnectionLost));
STATIC_CHECK(sfSftpOperationUnsupported == static_cast<int>(sf::Sftp::Result::Value::OperationUnsupported));
STATIC_CHECK(sfSftpInvalidHandle == static_cast<int>(sf::Sftp::Result::Value::InvalidHandle));
STATIC_CHECK(sfSftpNoSuchPath == static_cast<int>(sf::Sftp::Result::Value::NoSuchPath));
STATIC_CHECK(sfSftpFileAlreadyExists == static_cast<int>(sf::Sftp::Result::Value::FileAlreadyExists));
STATIC_CHECK(sfSftpWriteProtect == static_cast<int>(sf::Sftp::Result::Value::WriteProtect));
STATIC_CHECK(sfSftpNoMedia == static_cast<int>(sf::Sftp::Result::Value::NoMedia));
STATIC_CHECK(sfSftpNoSpaceOnFileSystem == static_cast<int>(sf::Sftp::Result::Value::NoSpaceOnFileSystem));
STATIC_CHECK(sfSftpQuotaExceeded == static_cast<int>(sf::Sftp::Result::Value::QuotaExceeded));
STATIC_CHECK(sfSftpUnknownPrincipal == static_cast<int>(sf::Sftp::Result::Value::UnknownPrincipal));
STATIC_CHECK(sfSftpLockConflict == static_cast<int>(sf::Sftp::Result::Value::LockConflict));
STATIC_CHECK(sfSftpDirectoryNotEmpty == static_cast<int>(sf::Sftp::Result::Value::DirectoryNotEmpty));
STATIC_CHECK(sfSftpNotADirectory == static_cast<int>(sf::Sftp::Result::Value::NotADirectory));
STATIC_CHECK(sfSftpInvalidFilename == static_cast<int>(sf::Sftp::Result::Value::InvalidFilename));
STATIC_CHECK(sfSftpLinkLoop == static_cast<int>(sf::Sftp::Result::Value::LinkLoop));
STATIC_CHECK(sfSftpSftpError == static_cast<int>(sf::Sftp::Result::Value::SftpError));
}
SECTION("sfSftpHostKeyType")
{
using HostKeyType = sf::Sftp::SessionInfo::HostKey::Type;
STATIC_CHECK(sfSftpHostKeyUnknown == static_cast<int>(HostKeyType::Unknown));
STATIC_CHECK(sfSftpHostKeyRsa == static_cast<int>(HostKeyType::Rsa));
STATIC_CHECK(sfSftpHostKeyDsa == static_cast<int>(HostKeyType::Dsa));
STATIC_CHECK(sfSftpHostKeyEcdsa256 == static_cast<int>(HostKeyType::Ecdsa256));
STATIC_CHECK(sfSftpHostKeyEcdsa384 == static_cast<int>(HostKeyType::Ecdsa384));
STATIC_CHECK(sfSftpHostKeyEcdsa521 == static_cast<int>(HostKeyType::Ecdsa521));
STATIC_CHECK(sfSftpHostKeyEd25519 == static_cast<int>(HostKeyType::Ed25519));
}
SECTION("Unconnected")
{
sfSftp* sftp = sfSftp_create();
sfSftpSessionInfo info{};
CHECK(!sfSftp_getSessionInfo(sftp, &info));
sfSftpResult* result = sfSftp_connect(sftp, sfIpAddress_None, 22, {});
CHECK(!sfSftpResult_isOk(result));
CHECK(sfSftpResult_getValue(result) == sfSftpError);
sfSftpResult_destroy(result);
sfSftp_destroy(sftp);
}
}