Moved ssh lib wrapper from SshSession to SshClient
This commit is contained in:
parent
be39b3be8c
commit
4a75f2ebca
8 changed files with 377 additions and 414 deletions
|
@ -72,7 +72,6 @@ set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
|
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
|
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
|
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/sshsession.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS)
|
||||||
|
@ -96,7 +95,6 @@ set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
|
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/sshsession.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS)
|
||||||
|
|
|
@ -76,8 +76,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
||||||
{
|
{
|
||||||
QString script = QString("cat %1 | grep AllowedIPs").arg(amnezia::protocols::wireguard::serverConfigPath);
|
QString script = QString("cat %1 | grep AllowedIPs").arg(amnezia::protocols::wireguard::serverConfigPath);
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
e = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
e = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
|
||||||
|
|
|
@ -41,14 +41,10 @@ ServerController::~ServerController()
|
||||||
|
|
||||||
|
|
||||||
ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script,
|
ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script,
|
||||||
const std::function<void(const QString &, libssh::Session &)> &cbReadStdOut,
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut,
|
||||||
const std::function<void(const QString &, libssh::Session &)> &cbReadStdErr) {
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr) {
|
||||||
|
|
||||||
std::shared_ptr<libssh::Session> session = m_sshClient.getSession();
|
auto error = m_sshClient.connectToHost(credentials);
|
||||||
if (!session) {
|
|
||||||
return ErrorCode::SshInternalError;
|
|
||||||
}
|
|
||||||
auto error = session->initChannel(credentials);
|
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +79,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
qDebug().noquote() << "EXEC" << lineToExec;
|
qDebug().noquote() << "EXEC" << lineToExec;
|
||||||
Debug::appendSshLog("Run command:" + lineToExec);
|
Debug::appendSshLog("Run command:" + lineToExec);
|
||||||
|
|
||||||
error = session->writeToChannel(lineToExec, cbReadStdOut, cbReadStdErr);
|
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -96,8 +92,8 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
|
|
||||||
ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials,
|
ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials,
|
||||||
DockerContainer container, QString script,
|
DockerContainer container, QString script,
|
||||||
const std::function<void (const QString &, libssh::Session &)> &cbReadStdOut,
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut,
|
||||||
const std::function<void (const QString &, libssh::Session &)> &cbReadStdErr)
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr)
|
||||||
{
|
{
|
||||||
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
||||||
Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + QStringLiteral(":\n") + script);
|
Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + QStringLiteral(":\n") + script);
|
||||||
|
@ -126,8 +122,9 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
||||||
if (e) return e;
|
if (e) return e;
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStd = [&](const QString &data, libssh::Session &) {
|
auto cbReadStd = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
// mkdir
|
// mkdir
|
||||||
|
@ -188,8 +185,9 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
||||||
|
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data;
|
stdOut += data;
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
*errorCode = runScript(credentials, script, cbReadStdOut);
|
*errorCode = runScript(credentials, script, cbReadStdOut);
|
||||||
|
@ -220,11 +218,7 @@ 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,
|
||||||
libssh::SftpOverwriteMode overwriteMode)
|
libssh::SftpOverwriteMode overwriteMode)
|
||||||
{
|
{
|
||||||
std::shared_ptr<libssh::Session> session = m_sshClient.getSession();
|
auto error = m_sshClient.connectToHost(credentials);
|
||||||
if (!session) {
|
|
||||||
return ErrorCode::SshInternalError;
|
|
||||||
}
|
|
||||||
auto error = session->initSftp(credentials);
|
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -236,7 +230,7 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
||||||
|
|
||||||
qDebug() << "remotePath" << remotePath;
|
qDebug() << "remotePath" << remotePath;
|
||||||
|
|
||||||
error = session->sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
|
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
|
||||||
if (error != ErrorCode::NoError) {
|
if (error != ErrorCode::NoError) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -367,15 +361,17 @@ bool ServerController::isReinstallContainerRequred(DockerContainer container, co
|
||||||
ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container)
|
ErrorCode ServerController::installDockerWorker(const ServerCredentials &credentials, DockerContainer container)
|
||||||
{
|
{
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Session &session) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &client) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
|
||||||
if (data.contains("Automatically restart Docker daemon?")) {
|
if (data.contains("Automatically restart Docker daemon?")) {
|
||||||
session.writeToChannel("yes");
|
return client.writeResponse("yes");
|
||||||
}
|
}
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorCode e = runScript(credentials,
|
ErrorCode e = runScript(credentials,
|
||||||
|
@ -404,8 +400,9 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||||
if (e) return e;
|
if (e) return e;
|
||||||
|
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||||
// stdOut += data + "\n";
|
// stdOut += data + "\n";
|
||||||
|
@ -422,8 +419,9 @@ ErrorCode ServerController::buildContainerWorker(const ServerCredentials &creden
|
||||||
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
ErrorCode ServerController::runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||||
{
|
{
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
// auto cbReadStdErr = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
|
||||||
// stdOut += data + "\n";
|
// stdOut += data + "\n";
|
||||||
|
@ -448,11 +446,13 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
|
||||||
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
ErrorCode ServerController::configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config)
|
||||||
{
|
{
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -584,11 +584,13 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
||||||
QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode)
|
QString ServerController::checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode)
|
||||||
{
|
{
|
||||||
QString stdOut;
|
QString stdOut;
|
||||||
auto cbReadStdOut = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
auto cbReadStdErr = [&](const QString &data, libssh::Session &) {
|
auto cbReadStdErr = [&](const QString &data, libssh::Client &) {
|
||||||
stdOut += data + "\n";
|
stdOut += data + "\n";
|
||||||
|
return ErrorCode::NoError;
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorCode e = runScript(credentials,
|
ErrorCode e = runScript(credentials,
|
||||||
|
@ -601,8 +603,7 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential
|
||||||
|
|
||||||
void ServerController::disconnectFromHost(const ServerCredentials &credentials)
|
void ServerController::disconnectFromHost(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
// SshConnection *client = acquireConnection(sshParams(credentials));
|
m_sshClient.disconnectFromHost();
|
||||||
// if (client) client->disconnectFromHost();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
|
||||||
|
|
|
@ -59,12 +59,12 @@ public:
|
||||||
QString replaceVars(const QString &script, const Vars &vars);
|
QString replaceVars(const QString &script, const Vars &vars);
|
||||||
|
|
||||||
ErrorCode runScript(const ServerCredentials &credentials, QString script,
|
ErrorCode runScript(const ServerCredentials &credentials, QString script,
|
||||||
const std::function<void(const QString &, libssh::Session &)> &cbReadStdOut = nullptr,
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||||
const std::function<void(const QString &, libssh::Session &)> &cbReadStdErr = nullptr);
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||||
|
|
||||||
ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
ErrorCode runContainerScript(const ServerCredentials &credentials, DockerContainer container, QString script,
|
||||||
const std::function<void(const QString &, libssh::Session &)> &cbReadStdOut = nullptr,
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
|
||||||
const std::function<void(const QString &, libssh::Session &)> &cbReadStdErr = nullptr);
|
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
|
||||||
|
|
||||||
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
|
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
#include "sshclient.h"
|
#include "sshclient.h"
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
#define S_IRWXU 0
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace libssh {
|
namespace libssh {
|
||||||
Client::Client(QObject *parent) : QObject(parent)
|
Client::Client(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
@ -11,8 +20,304 @@ namespace libssh {
|
||||||
ssh_finalize();
|
ssh_finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Session> Client::getSession()
|
ErrorCode Client::connectToHost(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
return std::make_shared<Session>();
|
// if (is_ssh_initialized()) {
|
||||||
|
// qDebug() << "Failed to initialize ssh";
|
||||||
|
// return ErrorCode::InternalError;
|
||||||
|
// }
|
||||||
|
if (m_session != NULL) {
|
||||||
|
m_session = ssh_new();
|
||||||
|
|
||||||
|
if (m_session == NULL) {
|
||||||
|
qDebug() << "Failed to create ssh session";
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = credentials.port;
|
||||||
|
int logVerbosity = SSH_LOG_NOLOG;
|
||||||
|
std::string hostIp = credentials.hostName.toStdString();
|
||||||
|
std::string hostUsername = credentials.userName.toStdString() + "@" + hostIp;
|
||||||
|
|
||||||
|
ssh_options_set(m_session, SSH_OPTIONS_HOST, hostIp.c_str());
|
||||||
|
ssh_options_set(m_session, SSH_OPTIONS_PORT, &port);
|
||||||
|
ssh_options_set(m_session, SSH_OPTIONS_USER, hostUsername.c_str());
|
||||||
|
ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity);
|
||||||
|
|
||||||
|
int connectionResult = ssh_connect(m_session);
|
||||||
|
|
||||||
|
if (connectionResult != SSH_OK) {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||||
|
}
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::disconnectFromHost()
|
||||||
|
{
|
||||||
|
if (m_session != NULL) {
|
||||||
|
if (ssh_is_connected(m_session)) {
|
||||||
|
ssh_disconnect(m_session);
|
||||||
|
}
|
||||||
|
ssh_free(m_session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode Client::executeCommand(const QString &data,
|
||||||
|
const std::function<ErrorCode (const QString &, Client &)> &cbReadStdOut,
|
||||||
|
const std::function<ErrorCode (const QString &, Client &)> &cbReadStdErr)
|
||||||
|
{
|
||||||
|
m_channel = ssh_channel_new(m_session);
|
||||||
|
|
||||||
|
if (m_channel == NULL) {
|
||||||
|
return closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = ssh_channel_open_session(m_channel);
|
||||||
|
|
||||||
|
if (result == SSH_OK && ssh_channel_is_open(m_channel)) {
|
||||||
|
qDebug() << "SSH chanel opened";
|
||||||
|
} else {
|
||||||
|
return closeChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QFutureWatcher<ErrorCode> watcher;
|
||||||
|
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Client::writeToChannelFinished);
|
||||||
|
|
||||||
|
QFuture<ErrorCode> future = QtConcurrent::run([this, &data, &cbReadStdOut, &cbReadStdErr]() {
|
||||||
|
const size_t bufferSize = 2048;
|
||||||
|
|
||||||
|
int bytesRead = 0;
|
||||||
|
char buffer[bufferSize];
|
||||||
|
|
||||||
|
int result = ssh_channel_request_exec(m_channel, data.toUtf8());
|
||||||
|
if (result == SSH_OK) {
|
||||||
|
std::string output;
|
||||||
|
auto readOutput = [&](bool isStdErr) {
|
||||||
|
bytesRead = ssh_channel_read(m_channel, buffer, sizeof(buffer), isStdErr);
|
||||||
|
while (bytesRead > 0)
|
||||||
|
{
|
||||||
|
output = std::string(buffer, bytesRead);
|
||||||
|
if (!output.empty()) {
|
||||||
|
qDebug().noquote() << (isStdErr ? "stdErr" : "stdOut") << QString(output.c_str());
|
||||||
|
|
||||||
|
if (cbReadStdOut && !isStdErr){
|
||||||
|
auto error = cbReadStdOut(output.c_str(), *this);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cbReadStdErr && isStdErr){
|
||||||
|
auto error = cbReadStdErr(output.c_str(), *this);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytesRead = ssh_channel_read(m_channel, buffer, sizeof(buffer), isStdErr);
|
||||||
|
}
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto error = readOutput(false);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
error = readOutput(true);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return closeChannel();
|
||||||
|
}
|
||||||
|
return closeChannel();
|
||||||
|
});
|
||||||
|
watcher.setFuture(future);
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
QObject::connect(this, &Client::writeToChannelFinished, &wait, &QEventLoop::quit);
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
return watcher.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode Client::writeResponse(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 Client::closeChannel()
|
||||||
|
{
|
||||||
|
if (m_channel != NULL) {
|
||||||
|
if (ssh_channel_is_eof(m_channel)) {
|
||||||
|
ssh_channel_send_eof(m_channel);
|
||||||
|
}
|
||||||
|
if (ssh_channel_is_open(m_channel)) {
|
||||||
|
ssh_channel_close(m_channel);
|
||||||
|
}
|
||||||
|
ssh_channel_free(m_channel);
|
||||||
|
}
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const std::string& localPath, const std::string& remotePath, const std::string& fileDesc)
|
||||||
|
{
|
||||||
|
m_sftpSession = sftp_new(m_session);
|
||||||
|
|
||||||
|
if (m_sftpSession == NULL) {
|
||||||
|
return closeSftpSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = sftp_init(m_sftpSession);
|
||||||
|
|
||||||
|
if (result != SSH_OK) {
|
||||||
|
return closeSftpSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
QFutureWatcher<ErrorCode> watcher;
|
||||||
|
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Client::sftpFileCopyFinished);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return closeSftpSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return closeSftpSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return closeSftpSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sftp_close(file);
|
||||||
|
return closeSftpSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
fin.close();
|
||||||
|
|
||||||
|
int result = sftp_close(file);
|
||||||
|
if (result != SSH_OK) {
|
||||||
|
return closeSftpSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
return closeSftpSession();
|
||||||
|
});
|
||||||
|
watcher.setFuture(future);
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
QObject::connect(this, &Client::sftpFileCopyFinished, &wait, &QEventLoop::quit);
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
return watcher.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode Client::closeSftpSession()
|
||||||
|
{
|
||||||
|
auto errorCode = fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
||||||
|
if (m_sftpSession != NULL) {
|
||||||
|
sftp_free(m_sftpSession);
|
||||||
|
}
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode Client::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 Client::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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,22 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "sshsession.h"
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <libssh/libssh.h>
|
||||||
|
#include <libssh/sftp.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
namespace libssh {
|
namespace libssh {
|
||||||
|
enum SftpOverwriteMode {
|
||||||
|
/*! Overwrite any existing files */
|
||||||
|
SftpOverwriteExisting = O_TRUNC,
|
||||||
|
/*! Append new content if the file already exists */
|
||||||
|
SftpAppendToExisting = O_APPEND
|
||||||
|
};
|
||||||
class Client : public QObject
|
class Client : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -15,7 +26,28 @@ namespace libssh {
|
||||||
Client(QObject *parent = nullptr);
|
Client(QObject *parent = nullptr);
|
||||||
~Client();
|
~Client();
|
||||||
|
|
||||||
std::shared_ptr<Session> getSession();
|
ErrorCode connectToHost(const ServerCredentials &credentials);
|
||||||
|
void disconnectFromHost();
|
||||||
|
ErrorCode executeCommand(const QString &data,
|
||||||
|
const std::function<ErrorCode (const QString &, Client &)> &cbReadStdOut,
|
||||||
|
const std::function<ErrorCode (const QString &, Client &)> &cbReadStdErr);
|
||||||
|
ErrorCode writeResponse(const QString &data);
|
||||||
|
ErrorCode sftpFileCopy(const SftpOverwriteMode overwriteMode,
|
||||||
|
const std::string& localPath,
|
||||||
|
const std::string& remotePath,
|
||||||
|
const std::string& fileDesc);
|
||||||
|
private:
|
||||||
|
ErrorCode closeChannel();
|
||||||
|
ErrorCode closeSftpSession();
|
||||||
|
ErrorCode fromLibsshErrorCode(int errorCode);
|
||||||
|
ErrorCode fromLibsshSftpErrorCode(int errorCode);
|
||||||
|
|
||||||
|
ssh_session m_session;
|
||||||
|
ssh_channel m_channel;
|
||||||
|
sftp_session m_sftpSession;
|
||||||
|
signals:
|
||||||
|
void writeToChannelFinished();
|
||||||
|
void sftpFileCopyFinished();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,317 +0,0 @@
|
||||||
#include "sshsession.h"
|
|
||||||
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QtConcurrent>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
#define S_IRWXU 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace libssh {
|
|
||||||
Session::Session(QObject *parent) : QObject(parent)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Session::~Session()
|
|
||||||
{
|
|
||||||
if (m_isSftpInitialized) {
|
|
||||||
sftp_free(m_sftpSession);
|
|
||||||
}
|
|
||||||
if (m_isSessionConnected) {
|
|
||||||
ssh_disconnect(m_session);
|
|
||||||
}
|
|
||||||
ssh_free(m_session);
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode Session::connectToHost(const ServerCredentials &credentials)
|
|
||||||
{
|
|
||||||
if (m_session == NULL) {
|
|
||||||
return ErrorCode::InternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
int port = credentials.port;
|
|
||||||
int logVerbosity = SSH_LOG_NOLOG;
|
|
||||||
std::string hostIp = credentials.hostName.toStdString();
|
|
||||||
std::string hostUsername = credentials.userName.toStdString() + "@" + hostIp;
|
|
||||||
|
|
||||||
ssh_options_set(m_session, SSH_OPTIONS_HOST, hostIp.c_str());
|
|
||||||
ssh_options_set(m_session, SSH_OPTIONS_PORT, &port);
|
|
||||||
ssh_options_set(m_session, SSH_OPTIONS_USER, hostUsername.c_str());
|
|
||||||
ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity);
|
|
||||||
|
|
||||||
int connectionResult = ssh_connect(m_session);
|
|
||||||
|
|
||||||
if (connectionResult != SSH_OK) {
|
|
||||||
qDebug() << ssh_get_error(m_session);
|
|
||||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
}
|
|
||||||
|
|
||||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode Session::initChannel(const ServerCredentials &credentials)
|
|
||||||
{
|
|
||||||
m_session = ssh_new();
|
|
||||||
|
|
||||||
ErrorCode error = connectToHost(credentials);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode Session::writeToChannel(const QString &data,
|
|
||||||
const std::function<void(const QString &, Session &)> &cbReadStdOut,
|
|
||||||
const std::function<void(const QString &, Session &)> &cbReadStdErr)
|
|
||||||
{
|
|
||||||
m_channel = ssh_channel_new(m_session);
|
|
||||||
|
|
||||||
if (m_channel == NULL) {
|
|
||||||
qDebug() << ssh_get_error(m_session);
|
|
||||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = ssh_channel_open_session(m_channel);
|
|
||||||
|
|
||||||
if (result == SSH_OK && ssh_channel_is_open(m_channel)) {
|
|
||||||
qDebug() << "SSH chanel opened";
|
|
||||||
} else {
|
|
||||||
qDebug() << ssh_get_error(m_session);
|
|
||||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
}
|
|
||||||
|
|
||||||
QFutureWatcher<ErrorCode> watcher;
|
|
||||||
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Session::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_request_exec(m_channel, data.toUtf8());
|
|
||||||
if (bytesWritten == SSH_OK) {
|
|
||||||
auto readOutput = [&](bool isStdErr) {
|
|
||||||
std::string output;
|
|
||||||
bytesRead = ssh_channel_read(m_channel, buffer, sizeof(buffer), isStdErr);
|
|
||||||
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(), *this);
|
|
||||||
}
|
|
||||||
if (cbReadStdErr && isStdErr){
|
|
||||||
cbReadStdErr(output.c_str(), *this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bytesRead = ssh_channel_read(m_channel, buffer, sizeof(buffer), isStdErr);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
readOutput(false);
|
|
||||||
readOutput(true);
|
|
||||||
} else {
|
|
||||||
qDebug() << ssh_get_error(m_session);
|
|
||||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
}
|
|
||||||
return fromLibsshErrorCode(ssh_get_error_code(m_session));
|
|
||||||
|
|
||||||
ssh_channel_send_eof(m_channel);
|
|
||||||
ssh_channel_close(m_channel);
|
|
||||||
ssh_channel_free(m_channel);
|
|
||||||
});
|
|
||||||
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();
|
|
||||||
|
|
||||||
ErrorCode error = connectToHost(credentials);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_sftpSession = sftp_new(m_session);
|
|
||||||
|
|
||||||
if (m_sftpSession == NULL) {
|
|
||||||
qDebug() << ssh_get_error(m_session);
|
|
||||||
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 fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
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::SshInternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFutureWatcher<ErrorCode> watcher;
|
|
||||||
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Session::sftpFileCopyFinished);
|
|
||||||
|
|
||||||
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 fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sftp_close(file);
|
|
||||||
qDebug() << ssh_get_error(m_session);
|
|
||||||
return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
|
||||||
}
|
|
||||||
|
|
||||||
fin.close();
|
|
||||||
|
|
||||||
int result = sftp_close(file);
|
|
||||||
if (result != SSH_OK) {
|
|
||||||
qDebug() << ssh_get_error(m_session);
|
|
||||||
return fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
|
||||||
});
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
#ifndef SSHSESSION_H
|
|
||||||
#define SSHSESSION_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
#include <libssh/libssh.h>
|
|
||||||
#include <libssh/sftp.h>
|
|
||||||
|
|
||||||
#include "defs.h"
|
|
||||||
|
|
||||||
using namespace amnezia;
|
|
||||||
|
|
||||||
namespace libssh {
|
|
||||||
enum SftpOverwriteMode {
|
|
||||||
/*! Overwrite any existing files */
|
|
||||||
SftpOverwriteExisting = O_TRUNC,
|
|
||||||
/*! Append new content if the file already exists */
|
|
||||||
SftpAppendToExisting = O_APPEND
|
|
||||||
};
|
|
||||||
|
|
||||||
class Session : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Session(QObject *parent = nullptr);
|
|
||||||
~Session();
|
|
||||||
|
|
||||||
ErrorCode initChannel(const ServerCredentials &credentials);
|
|
||||||
ErrorCode initSftp(const ServerCredentials &credentials);
|
|
||||||
ErrorCode writeToChannel(const QString &data,
|
|
||||||
const std::function<void(const QString &, Session &)> &cbReadStdOut,
|
|
||||||
const std::function<void(const QString &, Session &)> &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;
|
|
||||||
sftp_session m_sftpSession;
|
|
||||||
|
|
||||||
bool m_isChannelCreated = false;
|
|
||||||
bool m_isChannelOpened = false;
|
|
||||||
bool m_isSessionConnected = false;
|
|
||||||
bool m_isNeedSendChannelEof = false;
|
|
||||||
bool m_isSftpInitialized = false;
|
|
||||||
signals:
|
|
||||||
void writeToChannelFinished();
|
|
||||||
void sftpFileCopyFinished();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SSHSESSION_H
|
|
Loading…
Add table
Add a link
Reference in a new issue