From 3277717a7fed877d792ed437a9bb5027a170c4ae Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Fri, 23 Dec 2022 14:50:48 +0300 Subject: [PATCH] Added error handling Added write to channel in callbacks --- client/configurators/openvpn_configurator.cpp | 2 +- .../configurators/wireguard_configurator.cpp | 2 +- client/core/defs.h | 14 +-- client/core/errorstrings.cpp | 29 +++-- client/core/servercontroller.cpp | 82 +++----------- client/core/servercontroller.h | 8 +- client/core/sshsession.cpp | 103 +++++++++++++----- client/core/sshsession.h | 7 +- 8 files changed, 125 insertions(+), 122 deletions(-) diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index 441c15f6..0ef6da0a 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -64,7 +64,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co connData.taKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, &e); if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) { - if (errorCode) *errorCode = ErrorCode::RemoteProcessCrashError; + if (errorCode) *errorCode = ErrorCode::SshSftpFailureError; } return connData; diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 6dd2ab49..85f5cee5 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -76,7 +76,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon { QString script = QString("cat %1 | grep AllowedIPs").arg(amnezia::protocols::wireguard::serverConfigPath); QString stdOut; - auto cbReadStdOut = [&](const QString &data) { + auto cbReadStdOut = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; diff --git a/client/core/defs.h b/client/core/defs.h index 5845fd3c..07b1566e 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -33,14 +33,14 @@ enum ErrorCode ServerDockerFailedError, // Ssh connection errors - SshSocketError, SshTimeoutError, SshProtocolError, - SshHostKeyError, SshKeyFileError, SshAuthenticationError, - SshClosedByServerError, SshInternalError, + SshRequsetDeniedError, SshInterruptedError, SshInternalError, - // Ssh remote process errors - SshRemoteProcessCreationError, - FailedToStartRemoteProcessError, RemoteProcessCrashError, - SshSftpError, + // Ssh sftp errors + SshSftpEofError, SshSftpNoSuchFileError, SshSftpPermissionDeniedError, + SshSftpFailureError, SshSftpBadMessageError, SshSftpNoConnectionError, + SshSftpConnectionLostError, SshSftpOpUnsupportedError, SshSftpInvalidHandleError, + SshSftpNoSuchPathError, SshSftpFileAlreadyExistsError, SshSftpWriteProtectError, + SshSftpNoMediaError, // Local errors FailedToSaveConfigData, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index 722dd4b4..a6ac9a6a 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -16,20 +16,25 @@ QString errorString(ErrorCode code){ case(ServerContainerMissingError): return QObject::tr("Server error: Docker container missing"); case(ServerDockerFailedError): return QObject::tr("Server error: Docker failed"); - // Ssh connection errors - case(SshSocketError): return QObject::tr("Ssh connection error"); - case(SshTimeoutError): return QObject::tr("Ssh connection timeout"); - case(SshProtocolError): return QObject::tr("Ssh protocol error"); - case(SshHostKeyError): return QObject::tr("Ssh server ket check failed"); - case(SshKeyFileError): return QObject::tr("Ssh key file error"); - case(SshAuthenticationError): return QObject::tr("Ssh authentication error"); - case(SshClosedByServerError): return QObject::tr("Ssh session closed"); + // Libssh errors + case(SshRequsetDeniedError): return QObject::tr("Ssh request was denied"); + case(SshInterruptedError): return QObject::tr("Ssh request was interrupted"); case(SshInternalError): return QObject::tr("Ssh internal error"); - // Ssh remote process errors - case(SshRemoteProcessCreationError): return QObject::tr("Failed to create remote process on server"); - case(FailedToStartRemoteProcessError): return QObject::tr("Failed to start remote process on server"); - case(RemoteProcessCrashError): return QObject::tr("Remote process on server crashed"); + // Libssh sftp errors + case(SshSftpEofError): return QObject::tr("Sftp error: End-of-file encountered"); + case(SshSftpNoSuchFileError): return QObject::tr("Sftp error: File does not exist"); + case(SshSftpPermissionDeniedError): return QObject::tr("Sftp error: Permission denied"); + case(SshSftpFailureError): return QObject::tr("Sftp error: Generic failure"); + case(SshSftpBadMessageError): return QObject::tr("Sftp error: Garbage received from server"); + case(SshSftpNoConnectionError): return QObject::tr("Sftp error: No connection has been set up"); + case(SshSftpConnectionLostError): return QObject::tr("Sftp error: There was a connection, but we lost it"); + case(SshSftpOpUnsupportedError): return QObject::tr("Sftp error: Operation not supported by libssh yet"); + case(SshSftpInvalidHandleError): return QObject::tr("Sftp error: Invalid file handle"); + case(SshSftpNoSuchPathError): return QObject::tr("Sftp error: No such file or directory path exists"); + case(SshSftpFileAlreadyExistsError): return QObject::tr("Sftp error: An attempt to create an already existing file or directory has been made"); + case(SshSftpWriteProtectError): return QObject::tr("Sftp error: Write-protected filesystem"); + case(SshSftpNoMediaError): return QObject::tr("Sftp error: No media was in remote drive"); // Local errors case (FailedToSaveConfigData): return QObject::tr("Failed to save config to disk"); diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index b9ee2e76..e054309a 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -41,8 +41,8 @@ ServerController::~ServerController() ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script, - const std::function &cbReadStdOut, - const std::function &cbReadStdErr) { + const std::function &cbReadStdOut, + const std::function &cbReadStdErr) { std::shared_ptr session = m_sshClient.getSession(); if (!session) { @@ -96,8 +96,8 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script, - const std::function &cbReadStdOut, - const std::function &cbReadStdErr) + const std::function &cbReadStdOut, + const std::function &cbReadStdErr) { QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh"; Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + QStringLiteral(":\n") + script); @@ -129,7 +129,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, if (e) return e; QString stdOut; - auto cbReadStd = [&](const QString &data) { + auto cbReadStd = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; @@ -191,7 +191,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container, QString stdOut; - auto cbReadStdOut = [&](const QString &data) { + auto cbReadStdOut = [&](const QString &data, libssh::Session &) { stdOut += data; }; @@ -257,54 +257,6 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential return ErrorCode::NoError; } -//ErrorCode ServerController::fromSshConnectionErrorCode(SshError error) -//{ -// switch (error) { -// case(SshNoError): return ErrorCode::NoError; -// case(QSsh::SshSocketError): return ErrorCode::SshSocketError; -// case(QSsh::SshTimeoutError): return ErrorCode::SshTimeoutError; -// case(QSsh::SshProtocolError): return ErrorCode::SshProtocolError; -// case(QSsh::SshHostKeyError): return ErrorCode::SshHostKeyError; -// case(QSsh::SshKeyFileError): return ErrorCode::SshKeyFileError; -// case(QSsh::SshAuthenticationError): return ErrorCode::SshAuthenticationError; -// case(QSsh::SshClosedByServerError): return ErrorCode::SshClosedByServerError; -// case(QSsh::SshInternalError): return ErrorCode::SshInternalError; -// default: return ErrorCode::SshInternalError; -// } -//} - -//ErrorCode ServerController::fromSshProcessExitStatus(int exitStatus) -//{ -// qDebug() << exitStatus; -// switch (SshRemoteProcess::ExitStatus(exitStatus)) { -// case(SshRemoteProcess::ExitStatus::NormalExit): return ErrorCode::NoError; -// case(SshRemoteProcess::ExitStatus::FailedToStart): return ErrorCode::FailedToStartRemoteProcessError; -// case(SshRemoteProcess::ExitStatus::CrashExit): return ErrorCode::RemoteProcessCrashError; -// default: return ErrorCode::SshInternalError; -// } -//} - -//SshConnectionParameters ServerController::sshParams(const ServerCredentials &credentials) -//{ -// QSsh::SshConnectionParameters sshParams; -// if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) { -// sshParams.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePublicKey; -// sshParams.privateKeyFile = credentials.password; -// } -// else { -// sshParams.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePassword; -// sshParams.setPassword(credentials.password); -// } -// sshParams.setHost(credentials.hostName); -// sshParams.setUserName(credentials.userName); -// sshParams.timeout = 10; -// sshParams.setPort(credentials.port); -// sshParams.hostKeyCheckingMode = QSsh::SshHostKeyCheckingMode::SshHostKeyCheckingNone; -// sshParams.options = SshIgnoreDefaultProxy; - -// return sshParams; -//} - ErrorCode ServerController::removeAllContainers(const ServerCredentials &credentials) { return runScript(credentials, @@ -429,14 +381,14 @@ bool ServerController::isReinstallContainerRequred(DockerContainer container, co ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container) { QString stdOut; - auto cbReadStdOut = [&](const QString &data) { + auto cbReadStdOut = [&](const QString &data, libssh::Session &session) { stdOut += data + "\n"; - // if (data.contains("Automatically restart Docker daemon?")) { - // proc->write("yes\n"); - // } + if (data.contains("Automatically restart Docker daemon?")) { + session.writeToChannel("yes"); + } }; - auto cbReadStdErr = [&](const QString &data) { + auto cbReadStdErr = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; @@ -466,7 +418,7 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden if (e) return e; QString stdOut; - auto cbReadStdOut = [&](const QString &data) { + auto cbReadStdOut = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; // auto cbReadStdErr = [&](const QString &data, QSharedPointer proc) { @@ -484,7 +436,7 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config) { QString stdOut; - auto cbReadStdOut = [&](const QString &data) { + auto cbReadStdOut = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; // auto cbReadStdErr = [&](const QString &data, QSharedPointer proc) { @@ -510,10 +462,10 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config) { QString stdOut; - auto cbReadStdOut = [&](const QString &data) { + auto cbReadStdOut = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; - auto cbReadStdErr = [&](const QString &data) { + auto cbReadStdErr = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; @@ -646,10 +598,10 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode) { QString stdOut; - auto cbReadStdOut = [&](const QString &data) { + auto cbReadStdOut = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; - auto cbReadStdErr = [&](const QString &data) { + auto cbReadStdErr = [&](const QString &data, libssh::Session &) { stdOut += data + "\n"; }; diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index 2892a928..8b5f7c8d 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -59,12 +59,12 @@ public: QString replaceVars(const QString &script, const Vars &vars); ErrorCode runScript(const ServerCredentials &credentials, QString script, - const std::function &cbReadStdOut = nullptr, - const std::function &cbReadStdErr = nullptr); + const std::function &cbReadStdOut = nullptr, + const std::function &cbReadStdErr = nullptr); ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script, - const std::function &cbReadStdOut = nullptr, - const std::function &cbReadStdErr = nullptr); + const std::function &cbReadStdOut = nullptr, + const std::function &cbReadStdErr = nullptr); Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject()); diff --git a/client/core/sshsession.cpp b/client/core/sshsession.cpp index 85f11a55..401a4ec8 100644 --- a/client/core/sshsession.cpp +++ b/client/core/sshsession.cpp @@ -52,7 +52,7 @@ namespace libssh { if (connectionResult != SSH_OK) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshTimeoutError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } m_isSessionConnected = true; @@ -71,10 +71,10 @@ namespace libssh { if (authResult != SSH_OK) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshAuthenticationError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } - return ErrorCode::NoError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } ErrorCode Session::initChannel(const ServerCredentials &credentials) @@ -89,7 +89,7 @@ namespace libssh { if (m_channel == NULL) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshAuthenticationError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } int result = ssh_channel_open_session(m_channel); @@ -99,37 +99,37 @@ namespace libssh { m_isChannelOpened = true; } else { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshAuthenticationError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } result = ssh_channel_request_pty(m_channel); if (result != SSH_OK) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshInternalError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } result = ssh_channel_change_pty_size(m_channel, 80, 1024); if (result != SSH_OK) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshInternalError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } result = ssh_channel_request_shell(m_channel); if (result != SSH_OK) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshInternalError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } - return ErrorCode::NoError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } ErrorCode Session::writeToChannel(const QString &data, - const std::function &cbReadStdOut, - const std::function &cbReadStdErr) + const std::function &cbReadStdOut, + const std::function &cbReadStdErr) { if (m_channel == NULL) { qDebug() << "ssh channel not initialized"; - return ErrorCode::SshAuthenticationError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } QFutureWatcher watcher; @@ -142,12 +142,11 @@ namespace libssh { char buffer[bufferSize]; int bytesWritten = ssh_channel_write(m_channel, data.toUtf8(), (uint32_t)data.size()); - ssh_channel_write(m_channel, "\n", 1); - if (bytesWritten == data.size()) { + if (bytesWritten == data.size() && ssh_channel_write(m_channel, "\n", 1)) { auto readOutput = [&](bool isStdErr) { std::string output; if (ssh_channel_is_open(m_channel) && !ssh_channel_is_eof(m_channel)) { - bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 50); + bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 200); while (bytesRead > 0) { output = std::string(buffer, bytesRead); @@ -155,13 +154,13 @@ namespace libssh { qDebug().noquote() << (isStdErr ? "stdErr" : "stdOut") << QString(output.c_str()); if (cbReadStdOut && !isStdErr){ - cbReadStdOut(output.c_str()); + cbReadStdOut(output.c_str(), *this); } if (cbReadStdErr && isStdErr){ - cbReadStdErr(output.c_str()); + cbReadStdErr(output.c_str(), *this); } } - bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 5000); + bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 2000); } } return output; @@ -171,21 +170,35 @@ namespace libssh { readOutput(true); } else { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshInternalError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } m_isNeedSendChannelEof = true; - return ErrorCode::NoError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); }); watcher.setFuture(future); QEventLoop wait; - QObject::connect(this, &Session::writeToChannelFinished, &wait, &QEventLoop::quit); wait.exec(); return watcher.result(); } + ErrorCode Session::writeToChannel(const QString &data) + { + if (m_channel == NULL) { + qDebug() << "ssh channel not initialized"; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); + } + + int bytesWritten = ssh_channel_write(m_channel, data.toUtf8(), (uint32_t)data.size()); + if (bytesWritten == data.size() && ssh_channel_write(m_channel, "\n", 1)) { + return fromLibsshErrorCode(ssh_get_error_code(m_session)); + } + qDebug() << ssh_get_error(m_session); + return fromLibsshErrorCode(ssh_get_error_code(m_session)); + } + ErrorCode Session::initSftp(const ServerCredentials &credentials) { m_session = ssh_new(); @@ -199,14 +212,14 @@ namespace libssh { if (m_sftpSession == NULL) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshSftpError; + return fromLibsshErrorCode(ssh_get_error_code(m_session)); } int result = sftp_init(m_sftpSession); if (result != SSH_OK) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshSftpError; + return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); } return ErrorCode::NoError; @@ -216,7 +229,7 @@ namespace libssh { { if (m_sftpSession == NULL) { qDebug() << "ssh sftp session not initialized"; - return ErrorCode::SshSftpError; + return ErrorCode::SshInternalError; } QFutureWatcher watcher; @@ -232,7 +245,7 @@ namespace libssh { if (file == NULL) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshSftpError; + return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); } int localFileSize = std::filesystem::file_size(localPath); @@ -253,7 +266,7 @@ namespace libssh { fin.close(); sftp_close(file); qDebug() << ssh_get_error(m_session); - return ErrorCode::SshSftpError; + return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); } } @@ -271,13 +284,13 @@ namespace libssh { fin.close(); sftp_close(file); qDebug() << ssh_get_error(m_session); - return ErrorCode::SshSftpError; + return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); } } } else { sftp_close(file); qDebug() << ssh_get_error(m_session); - return ErrorCode::SshSftpError; + return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); } fin.close(); @@ -285,7 +298,7 @@ namespace libssh { int result = sftp_close(file); if (result != SSH_OK) { qDebug() << ssh_get_error(m_session); - return ErrorCode::SshSftpError; + return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); } return ErrorCode::NoError; @@ -293,10 +306,40 @@ namespace libssh { watcher.setFuture(future); QEventLoop wait; - QObject::connect(this, &Session::sftpFileCopyFinished, &wait, &QEventLoop::quit); wait.exec(); return watcher.result(); } + + ErrorCode Session::fromLibsshErrorCode(int errorCode) + { + switch (errorCode) { + case(SSH_NO_ERROR): return ErrorCode::NoError; + case(SSH_REQUEST_DENIED): return ErrorCode::SshRequsetDeniedError; + case(SSH_EINTR): return ErrorCode::SshInterruptedError; + case(SSH_FATAL): return ErrorCode::SshInternalError; + default: return ErrorCode::SshInternalError; + } + } + ErrorCode Session::fromLibsshSftpErrorCode(int errorCode) + { + switch (errorCode) { + case(SSH_FX_OK): return ErrorCode::NoError; + case(SSH_FX_EOF): return ErrorCode::SshSftpEofError; + case(SSH_FX_NO_SUCH_FILE): return ErrorCode::SshSftpNoSuchFileError; + case(SSH_FX_PERMISSION_DENIED): return ErrorCode::SshSftpPermissionDeniedError; + case(SSH_FX_FAILURE): return ErrorCode::SshSftpFailureError; + case(SSH_FX_BAD_MESSAGE): return ErrorCode::SshSftpBadMessageError; + case(SSH_FX_NO_CONNECTION): return ErrorCode::SshSftpNoConnectionError; + case(SSH_FX_CONNECTION_LOST): return ErrorCode::SshSftpConnectionLostError; + case(SSH_FX_OP_UNSUPPORTED): return ErrorCode::SshSftpOpUnsupportedError; + case(SSH_FX_INVALID_HANDLE): return ErrorCode::SshSftpInvalidHandleError; + case(SSH_FX_NO_SUCH_PATH): return ErrorCode::SshSftpNoSuchPathError; + case(SSH_FX_FILE_ALREADY_EXISTS): return ErrorCode::SshSftpFileAlreadyExistsError; + case(SSH_FX_WRITE_PROTECT): return ErrorCode::SshSftpWriteProtectError; + case(SSH_FX_NO_MEDIA): return ErrorCode::SshSftpNoMediaError; + default: return ErrorCode::SshSftpFailureError; + } + } } diff --git a/client/core/sshsession.h b/client/core/sshsession.h index 8c89dba4..253d1ac4 100644 --- a/client/core/sshsession.h +++ b/client/core/sshsession.h @@ -30,11 +30,14 @@ namespace libssh { ErrorCode initChannel(const ServerCredentials &credentials); ErrorCode initSftp(const ServerCredentials &credentials); ErrorCode writeToChannel(const QString &data, - const std::function &cbReadStdOut, - const std::function &cbReadStdErr); + const std::function &cbReadStdOut, + const std::function &cbReadStdErr); + ErrorCode writeToChannel(const QString &data); ErrorCode sftpFileCopy(const SftpOverwriteMode overwriteMode, const std::string& localPath, const std::string& remotePath, const std::string& fileDesc); private: ErrorCode connectToHost(const ServerCredentials &credentials); + ErrorCode fromLibsshErrorCode(int errorCode); + ErrorCode fromLibsshSftpErrorCode(int errorCode); ssh_session m_session; ssh_channel m_channel;