removed linking with botan and qssh

This commit is contained in:
vladimir.kuznetsov 2022-12-23 10:13:06 +03:00
parent 81cf108471
commit f9b2829396
10 changed files with 329 additions and 318 deletions

View file

@ -1,7 +1,7 @@
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..) set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
include(${CLIENT_ROOT_DIR}/3rd/QtSsh/src/ssh/qssh.cmake) #include(${CLIENT_ROOT_DIR}/3rd/QtSsh/src/ssh/qssh.cmake)
include(${CLIENT_ROOT_DIR}/3rd/QtSsh/src/botan/botan.cmake) #include(${CLIENT_ROOT_DIR}/3rd/QtSsh/src/botan/botan.cmake)
if(NOT IOS AND NOT ANDROID) if(NOT IOS AND NOT ANDROID)
include(${CLIENT_ROOT_DIR}/3rd/SingleApplication/singleapplication.cmake) include(${CLIENT_ROOT_DIR}/3rd/SingleApplication/singleapplication.cmake)

View file

@ -14,9 +14,6 @@
#include "core/server_defs.h" #include "core/server_defs.h"
#include "utilities.h" #include "utilities.h"
#include "sftpdefs.h"
using namespace QSsh;
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent): SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent) ConfiguratorBase(settings, serverController, parent)

View file

@ -144,7 +144,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
arg(connData.clientIP); arg(connData.clientIP);
e = m_serverController->uploadTextFileToContainer(container, credentials, configPart, e = m_serverController->uploadTextFileToContainer(container, credentials, configPart,
protocols::wireguard::serverConfigPath, QSsh::SftpOverwriteMode::SftpAppendToExisting); protocols::wireguard::serverConfigPath, libssh::SftpOverwriteMode::SftpAppendToExisting);
if (e) { if (e) {
if (errorCode) *errorCode = e; if (errorCode) *errorCode = e;

View file

@ -22,6 +22,7 @@
#include <thread> #include <thread>
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "debug.h"
#include "server_defs.h" #include "server_defs.h"
#include "settings.h" #include "settings.h"
#include "scripts_registry.h" #include "scripts_registry.h"
@ -43,7 +44,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
const std::function<void(const QString &)> &cbReadStdOut, const std::function<void(const QString &)> &cbReadStdOut,
const std::function<void(const QString &)> &cbReadStdErr) { const std::function<void(const QString &)> &cbReadStdErr) {
std::shared_ptr<SshSession> session = m_sshClient.getSession(); std::shared_ptr<libssh::Session> session = m_sshClient.getSession();
if (!session) { if (!session) {
return ErrorCode::SshInternalError; return ErrorCode::SshInternalError;
} }
@ -117,7 +118,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
const ServerCredentials &credentials, const QString &file, const QString &path, const ServerCredentials &credentials, const QString &file, const QString &path,
QSsh::SftpOverwriteMode overwriteMode) libssh::SftpOverwriteMode overwriteMode)
{ {
ErrorCode e = ErrorCode::NoError; ErrorCode e = ErrorCode::NoError;
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16)); QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
@ -141,14 +142,14 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
if (e) return e; if (e) return e;
if (overwriteMode == QSsh::SftpOverwriteMode::SftpOverwriteExisting) { if (overwriteMode == libssh::SftpOverwriteMode::SftpOverwriteExisting) {
e = runScript(credentials, e = runScript(credentials,
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
genVarsForScript(credentials, container)), cbReadStd, cbReadStd); genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
if (e) return e; if (e) return e;
} }
else if (overwriteMode == QSsh::SftpOverwriteMode::SftpAppendToExisting) { else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) {
e = runScript(credentials, e = runScript(credentials,
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName), replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
genVarsForScript(credentials, container)), cbReadStd, cbReadStd); genVarsForScript(credentials, container)), cbReadStd, cbReadStd);
@ -231,9 +232,9 @@ ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const
} }
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
QSsh::SftpOverwriteMode overwriteMode) libssh::SftpOverwriteMode overwriteMode)
{ {
std::shared_ptr<SshSession> session = m_sshClient.getSession(); std::shared_ptr<libssh::Session> session = m_sshClient.getSession();
if (!session) { if (!session) {
return ErrorCode::SshInternalError; return ErrorCode::SshInternalError;
} }
@ -249,7 +250,7 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
qDebug() << "remotePath" << remotePath; qDebug() << "remotePath" << remotePath;
error = session->sftpFileCopy(localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc"); error = session->sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
return error; return error;
} }

View file

@ -4,12 +4,9 @@
#include <QJsonObject> #include <QJsonObject>
#include <QObject> #include <QObject>
#include "debug.h"
#include "defs.h" #include "defs.h"
#include "containers/containers_defs.h" #include "containers/containers_defs.h"
#include "sftpdefs.h"
#include "sshclient.h" #include "sshclient.h"
class Settings; class Settings;
@ -48,11 +45,11 @@ public:
ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials); ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials);
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
const QString &remotePath, QSsh::SftpOverwriteMode overwriteMode = QSsh::SftpOverwriteMode::SftpOverwriteExisting); const QString &remotePath, libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
ErrorCode uploadTextFileToContainer(DockerContainer container, ErrorCode uploadTextFileToContainer(DockerContainer container,
const ServerCredentials &credentials, const QString &file, const QString &path, const ServerCredentials &credentials, const QString &file, const QString &path,
QSsh::SftpOverwriteMode overwriteMode = QSsh::SftpOverwriteMode::SftpOverwriteExisting); libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
QByteArray getTextFileFromContainer(DockerContainer container, QByteArray getTextFileFromContainer(DockerContainer container,
const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr); const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr);
@ -84,7 +81,7 @@ private:
std::shared_ptr<Settings> m_settings; std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator; std::shared_ptr<VpnConfigurator> m_configurator;
SshClient m_sshClient; libssh::Client m_sshClient;
}; };
#endif // SERVERCONTROLLER_H #endif // SERVERCONTROLLER_H

View file

@ -1,19 +1,18 @@
#include "sshclient.h" #include "sshclient.h"
#include <libssh/libssh.h> namespace libssh {
#include <libssh/sftp.h> Client::Client(QObject *parent) : QObject(parent)
{
ssh_init();
}
SshClient::SshClient(QObject *parent) : QObject(parent) Client::~Client()
{ {
ssh_init(); ssh_finalize();
} }
SshClient::~SshClient() std::shared_ptr<Session> Client::getSession()
{ {
ssh_finalize(); return std::make_shared<Session>();
} }
std::shared_ptr<SshSession> SshClient::getSession()
{
return std::make_shared<SshSession>();
} }

View file

@ -7,14 +7,16 @@
using namespace amnezia; using namespace amnezia;
class SshClient : public QObject namespace libssh {
{ class Client : public QObject
Q_OBJECT {
public: Q_OBJECT
SshClient(QObject *parent = nullptr); public:
~SshClient(); Client(QObject *parent = nullptr);
~Client();
std::shared_ptr<SshSession> getSession(); std::shared_ptr<Session> getSession();
}; };
}
#endif // SSHCLIENT_H #endif // SSHCLIENT_H

View file

@ -4,295 +4,299 @@
#include <QtConcurrent> #include <QtConcurrent>
#include <fstream> #include <fstream>
#include <fcntl.h>
SshSession::SshSession(QObject *parent) : QObject(parent) #ifdef Q_OS_WINDOWS
{ #define S_IRWXU 0
#endif
} namespace libssh {
Session::Session(QObject *parent) : QObject(parent)
{
SshSession::~SshSession()
{
if (m_isNeedSendChannelEof) {
ssh_channel_send_eof(m_channel);
}
if (m_isChannelOpened) {
ssh_channel_free(m_channel);
}
if (m_isSftpInitialized) {
sftp_free(m_sftpSession);
}
if (m_isSessionConnected) {
ssh_disconnect(m_session);
}
ssh_free(m_session);
}
ErrorCode SshSession::connectToHost(const ServerCredentials &credentials)
{
if (m_session == NULL) {
return ErrorCode::InternalError;
} }
int port = credentials.port; Session::~Session()
int logVerbosity = SSH_LOG_NOLOG; {
std::string hostIp = credentials.hostName.toStdString(); if (m_isNeedSendChannelEof) {
std::string hostUsername = credentials.userName.toStdString() + "@" + hostIp; ssh_channel_send_eof(m_channel);
}
ssh_options_set(m_session, SSH_OPTIONS_HOST, hostIp.c_str()); if (m_isChannelOpened) {
ssh_options_set(m_session, SSH_OPTIONS_PORT, &port); ssh_channel_free(m_channel);
ssh_options_set(m_session, SSH_OPTIONS_USER, hostUsername.c_str()); }
ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity); if (m_isSftpInitialized) {
sftp_free(m_sftpSession);
int connectionResult = ssh_connect(m_session); }
if (m_isSessionConnected) {
if (connectionResult != SSH_OK) { ssh_disconnect(m_session);
qDebug() << ssh_get_error(m_session); }
return ErrorCode::SshTimeoutError; ssh_free(m_session);
} }
m_isSessionConnected = true; ErrorCode Session::connectToHost(const ServerCredentials &credentials)
{
if (m_session == NULL) {
return ErrorCode::InternalError;
}
std::string authUsername = credentials.userName.toStdString(); int port = credentials.port;
int logVerbosity = SSH_LOG_NOLOG;
std::string hostIp = credentials.hostName.toStdString();
std::string hostUsername = credentials.userName.toStdString() + "@" + hostIp;
int authResult = SSH_ERROR; ssh_options_set(m_session, SSH_OPTIONS_HOST, hostIp.c_str());
if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) { ssh_options_set(m_session, SSH_OPTIONS_PORT, &port);
ssh_key privateKey; ssh_options_set(m_session, SSH_OPTIONS_USER, hostUsername.c_str());
ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, nullptr, nullptr, &privateKey); ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity);
authResult = ssh_userauth_publickey(m_session, authUsername.c_str(), privateKey);
} int connectionResult = ssh_connect(m_session);
else {
authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.password.toStdString().c_str()); if (connectionResult != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshTimeoutError;
}
m_isSessionConnected = true;
std::string authUsername = credentials.userName.toStdString();
int authResult = SSH_ERROR;
if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) {
ssh_key privateKey;
ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, nullptr, nullptr, &privateKey);
authResult = ssh_userauth_publickey(m_session, authUsername.c_str(), privateKey);
}
else {
authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.password.toStdString().c_str());
}
if (authResult != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshAuthenticationError;
}
return ErrorCode::NoError;
} }
if (authResult != SSH_OK) { ErrorCode Session::initChannel(const ServerCredentials &credentials)
qDebug() << ssh_get_error(m_session); {
return ErrorCode::SshAuthenticationError; m_session = ssh_new();
}
return ErrorCode::NoError; ErrorCode error = connectToHost(credentials);
} if (error) {
return error;
}
m_channel = ssh_channel_new(m_session);
ErrorCode SshSession::initChannel(const ServerCredentials &credentials) if (m_channel == NULL) {
{ qDebug() << ssh_get_error(m_session);
m_session = ssh_new(); return ErrorCode::SshAuthenticationError;
}
ErrorCode error = connectToHost(credentials); int result = ssh_channel_open_session(m_channel);
if (error) {
return error;
}
m_channel = ssh_channel_new(m_session);
if (m_channel == NULL) { if (result == SSH_OK && ssh_channel_is_open(m_channel)) {
qDebug() << ssh_get_error(m_session); qDebug() << "SSH chanel opened";
return ErrorCode::SshAuthenticationError; m_isChannelOpened = true;
}
int result = ssh_channel_open_session(m_channel);
if (result == SSH_OK && ssh_channel_is_open(m_channel)) {
qDebug() << "SSH chanel opened";
m_isChannelOpened = true;
} else {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshAuthenticationError;
}
result = ssh_channel_request_pty(m_channel);
if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshInternalError;
}
result = ssh_channel_change_pty_size(m_channel, 80, 1024);
if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshInternalError;
}
result = ssh_channel_request_shell(m_channel);
if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshInternalError;
}
return ErrorCode::NoError;
}
ErrorCode SshSession::writeToChannel(const QString &data,
const std::function<void(const QString &)> &cbReadStdOut,
const std::function<void(const QString &)> &cbReadStdErr)
{
if (m_channel == NULL) {
qDebug() << "ssh channel not initialized";
return ErrorCode::SshAuthenticationError;
}
QFutureWatcher<ErrorCode> watcher;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &SshSession::writeToChannelFinished);
QFuture<ErrorCode> future = QtConcurrent::run([this, &data, &cbReadStdOut, &cbReadStdErr]() {
const size_t bufferSize = 2048;
int bytesRead = 0;
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()) {
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);
while (bytesRead > 0)
{
output = std::string(buffer, bytesRead);
if (!output.empty()) {
qDebug().noquote() << (isStdErr ? "stdErr" : "stdOut") << QString(output.c_str());
if (cbReadStdOut && !isStdErr){
cbReadStdOut(output.c_str());
}
if (cbReadStdErr && isStdErr){
cbReadStdErr(output.c_str());
}
}
bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 500);
}
}
return output;
};
readOutput(false);
readOutput(true);
} else { } else {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshAuthenticationError;
}
result = ssh_channel_request_pty(m_channel);
if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session); qDebug() << ssh_get_error(m_session);
return ErrorCode::SshInternalError; return ErrorCode::SshInternalError;
} }
m_isNeedSendChannelEof = true;
result = ssh_channel_change_pty_size(m_channel, 80, 1024);
if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshInternalError;
}
result = ssh_channel_request_shell(m_channel);
if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshInternalError;
}
return ErrorCode::NoError; return ErrorCode::NoError;
});
watcher.setFuture(future);
QEventLoop wait;
QObject::connect(this, &SshSession::writeToChannelFinished, &wait, &QEventLoop::quit);
wait.exec();
return watcher.result();
}
ErrorCode SshSession::initSftp(const ServerCredentials &credentials)
{
m_session = ssh_new();
ErrorCode error = connectToHost(credentials);
if (error) {
return error;
} }
m_sftpSession = sftp_new(m_session); ErrorCode Session::writeToChannel(const QString &data,
const std::function<void(const QString &)> &cbReadStdOut,
const std::function<void(const QString &)> &cbReadStdErr)
{
if (m_channel == NULL) {
qDebug() << "ssh channel not initialized";
return ErrorCode::SshAuthenticationError;
}
if (m_sftpSession == NULL) { QFutureWatcher<ErrorCode> watcher;
qDebug() << ssh_get_error(m_session); connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Session::writeToChannelFinished);
return ErrorCode::SshSftpError;
QFuture<ErrorCode> future = QtConcurrent::run([this, &data, &cbReadStdOut, &cbReadStdErr]() {
const size_t bufferSize = 2048;
int bytesRead = 0;
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()) {
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);
while (bytesRead > 0)
{
output = std::string(buffer, bytesRead);
if (!output.empty()) {
qDebug().noquote() << (isStdErr ? "stdErr" : "stdOut") << QString(output.c_str());
if (cbReadStdOut && !isStdErr){
cbReadStdOut(output.c_str());
}
if (cbReadStdErr && isStdErr){
cbReadStdErr(output.c_str());
}
}
bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 5000);
}
}
return output;
};
readOutput(false);
readOutput(true);
} else {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshInternalError;
}
m_isNeedSendChannelEof = true;
return ErrorCode::NoError;
});
watcher.setFuture(future);
QEventLoop wait;
QObject::connect(this, &Session::writeToChannelFinished, &wait, &QEventLoop::quit);
wait.exec();
return watcher.result();
} }
int result = sftp_init(m_sftpSession); ErrorCode Session::initSftp(const ServerCredentials &credentials)
{
m_session = ssh_new();
if (result != SSH_OK) { ErrorCode error = connectToHost(credentials);
qDebug() << ssh_get_error(m_session); if (error) {
return ErrorCode::SshSftpError; return error;
} }
return ErrorCode::NoError; m_sftpSession = sftp_new(m_session);
}
ErrorCode SshSession::sftpFileCopy(const std::string& localPath, const std::string& remotePath, const std::string& fileDesc) if (m_sftpSession == NULL) {
{
if (m_sftpSession == NULL) {
qDebug() << "ssh sftp session not initialized";
return ErrorCode::SshSftpError;
}
QFutureWatcher<ErrorCode> watcher;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &SshSession::sftpFileCopyFinished);
QFuture<ErrorCode> future = QtConcurrent::run([this, &localPath, &remotePath, &fileDesc]() {
int accessType = O_WRONLY | O_CREAT | O_TRUNC;
sftp_file file;
const size_t bufferSize = 16384;
char buffer[bufferSize];
file = sftp_open(m_sftpSession, remotePath.c_str(), accessType, 0);//S_IRWXU);
if (file == NULL) {
qDebug() << ssh_get_error(m_session); qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError; return ErrorCode::SshSftpError;
} }
int localFileSize = std::filesystem::file_size(localPath); int result = sftp_init(m_sftpSession);
int chunksCount = localFileSize / (bufferSize);
std::ifstream fin(localPath, std::ios::binary | std::ios::in);
if (fin.is_open()) {
for (int currentChunkId = 0; currentChunkId < chunksCount; currentChunkId++) {
fin.read(buffer, bufferSize);
int bytesWritten = sftp_write(file, buffer, bufferSize);
std::string chunk(buffer, bufferSize);
qDebug() << "write -> " << QString(chunk.c_str());
if (bytesWritten != bufferSize) {
fin.close();
sftp_close(file);
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
}
int lastChunkSize = localFileSize % (bufferSize);
if (lastChunkSize != 0) {
fin.read(buffer, lastChunkSize);
std::string chunk(buffer, lastChunkSize);
qDebug() << "write -> " << QString(chunk.c_str());
int bytesWritten = sftp_write(file, buffer, lastChunkSize);
if (bytesWritten != lastChunkSize) {
fin.close();
sftp_close(file);
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
}
} else {
sftp_close(file);
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
fin.close();
int result = sftp_close(file);
if (result != SSH_OK) { if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session); qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError; return ErrorCode::SshSftpError;
} }
return ErrorCode::NoError; return ErrorCode::NoError;
}); }
watcher.setFuture(future);
QEventLoop wait; ErrorCode Session::sftpFileCopy(const SftpOverwriteMode overwriteMode, const std::string& localPath, const std::string& remotePath, const std::string& fileDesc)
{
if (m_sftpSession == NULL) {
qDebug() << "ssh sftp session not initialized";
return ErrorCode::SshSftpError;
}
QObject::connect(this, &SshSession::sftpFileCopyFinished, &wait, &QEventLoop::quit); QFutureWatcher<ErrorCode> watcher;
wait.exec(); connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Session::sftpFileCopyFinished);
return watcher.result(); QFuture<ErrorCode> future = QtConcurrent::run([this, overwriteMode, &localPath, &remotePath, &fileDesc]() {
int accessType = O_WRONLY | O_CREAT | overwriteMode;
sftp_file file;
const size_t bufferSize = 16384;
char buffer[bufferSize];
file = sftp_open(m_sftpSession, remotePath.c_str(), accessType, S_IRWXU);
if (file == NULL) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
int localFileSize = std::filesystem::file_size(localPath);
int chunksCount = localFileSize / (bufferSize);
std::ifstream fin(localPath, std::ios::binary | std::ios::in);
if (fin.is_open()) {
for (int currentChunkId = 0; currentChunkId < chunksCount; currentChunkId++) {
fin.read(buffer, bufferSize);
int bytesWritten = sftp_write(file, buffer, bufferSize);
std::string chunk(buffer, bufferSize);
qDebug() << "sftp write: " << QString(chunk.c_str());
if (bytesWritten != bufferSize) {
fin.close();
sftp_close(file);
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
}
int lastChunkSize = localFileSize % (bufferSize);
if (lastChunkSize != 0) {
fin.read(buffer, lastChunkSize);
std::string chunk(buffer, lastChunkSize);
qDebug() << "sftp write: " << QString(chunk.c_str());
int bytesWritten = sftp_write(file, buffer, lastChunkSize);
if (bytesWritten != lastChunkSize) {
fin.close();
sftp_close(file);
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
}
} else {
sftp_close(file);
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
fin.close();
int result = sftp_close(file);
if (result != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return ErrorCode::SshSftpError;
}
return ErrorCode::NoError;
});
watcher.setFuture(future);
QEventLoop wait;
QObject::connect(this, &Session::sftpFileCopyFinished, &wait, &QEventLoop::quit);
wait.exec();
return watcher.result();
}
} }

View file

@ -3,6 +3,8 @@
#include <QObject> #include <QObject>
#include <fcntl.h>
#include <libssh/libssh.h> #include <libssh/libssh.h>
#include <libssh/sftp.h> #include <libssh/sftp.h>
@ -10,33 +12,42 @@
using namespace amnezia; using namespace amnezia;
class SshSession : public QObject namespace libssh {
{ enum SftpOverwriteMode {
Q_OBJECT /*! Overwrite any existing files */
public: SftpOverwriteExisting = O_TRUNC,
SshSession(QObject *parent = nullptr); /*! Append new content if the file already exists */
~SshSession(); SftpAppendToExisting = O_APPEND
};
ErrorCode initChannel(const ServerCredentials &credentials); class Session : public QObject
ErrorCode initSftp(const ServerCredentials &credentials); {
ErrorCode writeToChannel(const QString &data, Q_OBJECT
const std::function<void(const QString &)> &cbReadStdOut, public:
const std::function<void(const QString &)> &cbReadStdErr); Session(QObject *parent = nullptr);
ErrorCode sftpFileCopy(const std::string& localPath, const std::string& remotePath, const std::string& fileDesc); ~Session();
private:
ErrorCode connectToHost(const ServerCredentials &credentials);
ssh_session m_session; ErrorCode initChannel(const ServerCredentials &credentials);
ssh_channel m_channel; ErrorCode initSftp(const ServerCredentials &credentials);
sftp_session m_sftpSession; ErrorCode writeToChannel(const QString &data,
const std::function<void(const QString &)> &cbReadStdOut,
const std::function<void(const QString &)> &cbReadStdErr);
ErrorCode sftpFileCopy(const SftpOverwriteMode overwriteMode, const std::string& localPath, const std::string& remotePath, const std::string& fileDesc);
private:
ErrorCode connectToHost(const ServerCredentials &credentials);
bool m_isChannelOpened = false; ssh_session m_session;
bool m_isSessionConnected = false; ssh_channel m_channel;
bool m_isNeedSendChannelEof = false; sftp_session m_sftpSession;
bool m_isSftpInitialized = false;
signals: bool m_isChannelOpened = false;
void writeToChannelFinished(); bool m_isSessionConnected = false;
void sftpFileCopyFinished(); bool m_isNeedSendChannelEof = false;
}; bool m_isSftpInitialized = false;
signals:
void writeToChannelFinished();
void sftpFileCopyFinished();
};
}
#endif // SSHSESSION_H #endif // SSHSESSION_H

View file

@ -49,9 +49,9 @@ void ManagementServer::onNewConnection()
m_socket = QPointer<QTcpSocket>(m_tcpServer->nextPendingConnection()); m_socket = QPointer<QTcpSocket>(m_tcpServer->nextPendingConnection());
if (m_tcpServer) m_tcpServer->close(); if (m_tcpServer) m_tcpServer->close();
QObject::connect(m_socket.data(), SIGNAL(disconnected()), this, SLOT(onSocketDisconnected())); QObject::connect(m_socket.data(), &QTcpSocket::disconnected, this, &ManagementServer::onSocketDisconnected);
QObject::connect(m_socket.data(), SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError))); QObject::connect(m_socket.data(), &QTcpSocket::errorOccurred, this, &ManagementServer::onSocketError);
QObject::connect(m_socket.data(), SIGNAL(readyRead()), this, SLOT(onReadyRead())); QObject::connect(m_socket.data(), &QTcpSocket::readyRead, this, &ManagementServer::onReadyRead);
} }
void ManagementServer::onSocketError(QAbstractSocket::SocketError socketError) void ManagementServer::onSocketError(QAbstractSocket::SocketError socketError)