parent
840c388ab9
commit
6b6a76d2cc
8 changed files with 98 additions and 141 deletions
|
@ -118,7 +118,7 @@ ServerController::runContainerScript(const ServerCredentials &credentials, Docke
|
|||
|
||||
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
const QString &file, const QString &path,
|
||||
libssh::SftpOverwriteMode overwriteMode)
|
||||
libssh::ScpOverwriteMode overwriteMode)
|
||||
{
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
|
||||
|
@ -139,7 +139,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
|||
if (e)
|
||||
return e;
|
||||
|
||||
if (overwriteMode == libssh::SftpOverwriteMode::SftpOverwriteExisting) {
|
||||
if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
|
||||
genVarsForScript(credentials, container)),
|
||||
|
@ -147,7 +147,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
|
|||
|
||||
if (e)
|
||||
return e;
|
||||
} else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) {
|
||||
} else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
|
||||
e = runScript(credentials,
|
||||
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
|
||||
genVarsForScript(credentials, container)),
|
||||
|
@ -199,7 +199,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
|||
}
|
||||
|
||||
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
|
||||
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode)
|
||||
const QString &remotePath, libssh::ScpOverwriteMode overwriteMode)
|
||||
{
|
||||
auto error = m_sshClient.connectToHost(credentials);
|
||||
if (error != ErrorCode::NoError) {
|
||||
|
@ -211,7 +211,7 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
|||
localFile.write(data);
|
||||
localFile.close();
|
||||
|
||||
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc");
|
||||
error = m_sshClient.scpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc");
|
||||
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
|
||||
ErrorCode uploadTextFileToContainer(
|
||||
DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path,
|
||||
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||
libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
|
||||
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
const QString &path, ErrorCode *errorCode = nullptr);
|
||||
|
||||
|
@ -80,7 +80,7 @@ private:
|
|||
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
|
||||
|
||||
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
|
||||
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
|
||||
libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
|
||||
|
||||
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
|
||||
|
||||
|
|
|
@ -46,25 +46,12 @@ namespace amnezia
|
|||
SshPrivateKeyFormatError = 304,
|
||||
SshTimeoutError = 305,
|
||||
|
||||
// Ssh sftp errors
|
||||
SshSftpEofError = 400,
|
||||
SshSftpNoSuchFileError = 401,
|
||||
SshSftpPermissionDeniedError = 402,
|
||||
SshSftpFailureError = 403,
|
||||
SshSftpBadMessageError = 404,
|
||||
SshSftpNoConnectionError = 405,
|
||||
SshSftpConnectionLostError = 406,
|
||||
SshSftpOpUnsupportedError = 407,
|
||||
SshSftpInvalidHandleError = 408,
|
||||
SshSftpNoSuchPathError = 409,
|
||||
SshSftpFileAlreadyExistsError = 410,
|
||||
SshSftpWriteProtectError = 411,
|
||||
SshSftpNoMediaError = 412,
|
||||
// Ssh scp errors
|
||||
SshScpFailureError = 400,
|
||||
|
||||
// Local errors
|
||||
OpenVpnConfigMissing = 500,
|
||||
OpenVpnManagementServerError = 501,
|
||||
ConfigMissing = 502,
|
||||
|
||||
// Distro errors
|
||||
OpenVpnExecutableMissing = 600,
|
||||
|
@ -92,7 +79,15 @@ namespace amnezia
|
|||
|
||||
// Api errors
|
||||
ApiConfigDownloadError = 1100,
|
||||
ApiConfigAlreadyAdded = 1101
|
||||
ApiConfigAlreadyAdded = 1101,
|
||||
|
||||
// QFile errors
|
||||
OpenError = 1200,
|
||||
ReadError = 1201,
|
||||
PermissionsError = 1202,
|
||||
UnspecifiedError = 1203,
|
||||
FatalError = 1204,
|
||||
AbortError = 1205
|
||||
};
|
||||
|
||||
} // namespace amnezia
|
||||
|
|
|
@ -28,20 +28,8 @@ QString errorString(ErrorCode code) {
|
|||
case(SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break;
|
||||
case(SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break;
|
||||
|
||||
// Libssh sftp errors
|
||||
case(SshSftpEofError): errorMessage = QObject::tr("Sftp error: End-of-file encountered"); break;
|
||||
case(SshSftpNoSuchFileError): errorMessage = QObject::tr("Sftp error: File does not exist"); break;
|
||||
case(SshSftpPermissionDeniedError): errorMessage = QObject::tr("Sftp error: Permission denied"); break;
|
||||
case(SshSftpFailureError): errorMessage = QObject::tr("Sftp error: Generic failure"); break;
|
||||
case(SshSftpBadMessageError): errorMessage = QObject::tr("Sftp error: Garbage received from server"); break;
|
||||
case(SshSftpNoConnectionError): errorMessage = QObject::tr("Sftp error: No connection has been set up"); break;
|
||||
case(SshSftpConnectionLostError): errorMessage = QObject::tr("Sftp error: There was a connection, but we lost it"); break;
|
||||
case(SshSftpOpUnsupportedError): errorMessage = QObject::tr("Sftp error: Operation not supported by libssh yet"); break;
|
||||
case(SshSftpInvalidHandleError): errorMessage = QObject::tr("Sftp error: Invalid file handle"); break;
|
||||
case(SshSftpNoSuchPathError): errorMessage = QObject::tr("Sftp error: No such file or directory path exists"); break;
|
||||
case(SshSftpFileAlreadyExistsError): errorMessage = QObject::tr("Sftp error: An attempt to create an already existing file or directory has been made"); break;
|
||||
case(SshSftpWriteProtectError): errorMessage = QObject::tr("Sftp error: Write-protected filesystem"); break;
|
||||
case(SshSftpNoMediaError): errorMessage = QObject::tr("Sftp error: No media was in remote drive"); break;
|
||||
// Ssh scp errors
|
||||
case(SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break;
|
||||
|
||||
// Local errors
|
||||
case (OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break;
|
||||
|
@ -68,6 +56,14 @@ QString errorString(ErrorCode code) {
|
|||
case (ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
|
||||
case (ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break;
|
||||
|
||||
// QFile errors
|
||||
case(OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
|
||||
case(ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break;
|
||||
case(PermissionsError): errorMessage = QObject::tr("QFile error: The file could not be accessed"); break;
|
||||
case(UnspecifiedError): errorMessage = QObject::tr("QFile error: An unspecified error occurred"); break;
|
||||
case(FatalError): errorMessage = QObject::tr("QFile error: A fatal error occurred"); break;
|
||||
case(AbortError): errorMessage = QObject::tr("QFile error: The operation was aborted"); break;
|
||||
|
||||
case(InternalError):
|
||||
default:
|
||||
errorMessage = QObject::tr("Internal error"); break;
|
||||
|
|
|
@ -10,16 +10,10 @@ const uint32_t S_IRWXU = 0644;
|
|||
#endif
|
||||
|
||||
namespace libssh {
|
||||
const QString libsshTimeoutError = "Timeout connecting to";
|
||||
constexpr auto libsshTimeoutError{"Timeout connecting to"};
|
||||
|
||||
std::function<QString()> Client::m_passphraseCallback;
|
||||
|
||||
Client::Client(QObject *parent) : QObject(parent)
|
||||
{ }
|
||||
|
||||
Client::~Client()
|
||||
{ }
|
||||
|
||||
int Client::callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata)
|
||||
{
|
||||
auto passphrase = m_passphraseCallback();
|
||||
|
@ -171,13 +165,13 @@ namespace libssh {
|
|||
return ErrorCode::NoError;
|
||||
};
|
||||
|
||||
auto error = readOutput(false);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
auto errorCode = readOutput(false);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
error = readOutput(true);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
errorCode = readOutput(true);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
} else {
|
||||
return closeChannel();
|
||||
|
@ -222,100 +216,79 @@ namespace libssh {
|
|||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const QString& localPath, const QString& remotePath, const QString &fileDesc)
|
||||
ErrorCode Client::scpFileCopy(const ScpOverwriteMode overwriteMode, const QString& localPath, const QString& remotePath, const QString &fileDesc)
|
||||
{
|
||||
m_sftpSession = sftp_new(m_session);
|
||||
m_scpSession = ssh_scp_new(m_session, SSH_SCP_WRITE, remotePath.toStdString().c_str());
|
||||
|
||||
if (m_sftpSession == nullptr) {
|
||||
return closeSftpSession();
|
||||
if (m_scpSession == nullptr) {
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
int result = sftp_init(m_sftpSession);
|
||||
|
||||
if (result != SSH_OK) {
|
||||
return closeSftpSession();
|
||||
if (ssh_scp_init(m_scpSession) != SSH_OK) {
|
||||
auto errorCode = fromLibsshErrorCode();
|
||||
closeScpSession();
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QFutureWatcher<ErrorCode> watcher;
|
||||
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Client::sftpFileCopyFinished);
|
||||
|
||||
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Client::scpFileCopyFinished);
|
||||
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];
|
||||
const int accessType = O_WRONLY | O_CREAT | overwriteMode;
|
||||
const int localFileSize = QFileInfo(localPath).size();
|
||||
|
||||
file = sftp_open(m_sftpSession, remotePath.toStdString().c_str(), accessType, S_IRWXU);
|
||||
|
||||
if (file == nullptr) {
|
||||
return closeSftpSession();
|
||||
int result = ssh_scp_push_file(m_scpSession, remotePath.toStdString().c_str(), localFileSize, accessType);
|
||||
if (result != SSH_OK) {
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
int localFileSize = QFileInfo(localPath).size();
|
||||
int chunksCount = localFileSize / (bufferSize);
|
||||
|
||||
QFile fin(localPath);
|
||||
|
||||
if (fin.open(QIODevice::ReadOnly)) {
|
||||
for (int currentChunkId = 0; currentChunkId < chunksCount; currentChunkId++) {
|
||||
QByteArray chunk = fin.read(bufferSize);
|
||||
if (chunk.size() != bufferSize) return ErrorCode::SshSftpEofError;
|
||||
constexpr size_t bufferSize = 16384;
|
||||
int transferred = 0;
|
||||
int currentChunkSize = bufferSize;
|
||||
|
||||
int bytesWritten = sftp_write(file, chunk.data(), chunk.size());
|
||||
while (transferred < localFileSize) {
|
||||
|
||||
if (bytesWritten != chunk.size()) {
|
||||
fin.close();
|
||||
sftp_close(file);
|
||||
return closeSftpSession();
|
||||
// Last Chunk
|
||||
if ((localFileSize - transferred) < bufferSize) {
|
||||
currentChunkSize = localFileSize % bufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
int lastChunkSize = localFileSize % bufferSize;
|
||||
|
||||
if (lastChunkSize != 0) {
|
||||
QByteArray lastChunk = fin.read(lastChunkSize);
|
||||
if (lastChunk.size() != lastChunkSize) return ErrorCode::SshSftpEofError;
|
||||
|
||||
int bytesWritten = sftp_write(file, lastChunk.data(), lastChunkSize);
|
||||
|
||||
if (bytesWritten != lastChunkSize) {
|
||||
fin.close();
|
||||
sftp_close(file);
|
||||
return closeSftpSession();
|
||||
QByteArray chunk = fin.read(currentChunkSize);
|
||||
if (chunk.size() != currentChunkSize) {
|
||||
return fromFileErrorCode(fin.error());
|
||||
}
|
||||
|
||||
result = ssh_scp_write(m_scpSession, chunk.data(), chunk.size());
|
||||
if (result != SSH_OK) {
|
||||
return fromLibsshErrorCode();
|
||||
}
|
||||
|
||||
transferred += currentChunkSize;
|
||||
}
|
||||
} else {
|
||||
sftp_close(file);
|
||||
return closeSftpSession();
|
||||
return fromFileErrorCode(fin.error());
|
||||
}
|
||||
|
||||
fin.close();
|
||||
|
||||
int result = sftp_close(file);
|
||||
if (result != SSH_OK) {
|
||||
return closeSftpSession();
|
||||
}
|
||||
|
||||
return closeSftpSession();
|
||||
return ErrorCode::NoError;
|
||||
});
|
||||
watcher.setFuture(future);
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(this, &Client::sftpFileCopyFinished, &wait, &QEventLoop::quit);
|
||||
QObject::connect(this, &Client::scpFileCopyFinished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
closeScpSession();
|
||||
return watcher.result();
|
||||
}
|
||||
|
||||
ErrorCode Client::closeSftpSession()
|
||||
void Client::closeScpSession()
|
||||
{
|
||||
auto errorCode = fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession));
|
||||
if (m_sftpSession != nullptr) {
|
||||
sftp_free(m_sftpSession);
|
||||
m_sftpSession = nullptr;
|
||||
if (m_scpSession != nullptr) {
|
||||
ssh_scp_free(m_scpSession);
|
||||
m_scpSession = nullptr;
|
||||
}
|
||||
qCritical() << ssh_get_error(m_session);
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
ErrorCode Client::fromLibsshErrorCode()
|
||||
|
@ -337,24 +310,17 @@ namespace libssh {
|
|||
default: return ErrorCode::SshInternalError;
|
||||
}
|
||||
}
|
||||
ErrorCode Client::fromLibsshSftpErrorCode(int errorCode)
|
||||
|
||||
ErrorCode Client::fromFileErrorCode(QFileDevice::FileError fileError)
|
||||
{
|
||||
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;
|
||||
switch (fileError) {
|
||||
case QFileDevice::NoError: return ErrorCode::NoError;
|
||||
case QFileDevice::ReadError: return ErrorCode::ReadError;
|
||||
case QFileDevice::OpenError: return ErrorCode::OpenError;
|
||||
case QFileDevice::PermissionsError: return ErrorCode::PermissionsError;
|
||||
case QFileDevice::FatalError: return ErrorCode::FatalError;
|
||||
case QFileDevice::AbortError: return ErrorCode::AbortError;
|
||||
default: return ErrorCode::UnspecifiedError;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,29 +2,29 @@
|
|||
#define SSHCLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFile>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/sftp.h>
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
namespace libssh {
|
||||
enum SftpOverwriteMode {
|
||||
enum ScpOverwriteMode {
|
||||
/*! Overwrite any existing files */
|
||||
SftpOverwriteExisting = O_TRUNC,
|
||||
ScpOverwriteExisting = O_TRUNC,
|
||||
/*! Append new content if the file already exists */
|
||||
SftpAppendToExisting = O_APPEND
|
||||
ScpAppendToExisting = O_APPEND
|
||||
};
|
||||
class Client : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Client(QObject *parent = nullptr);
|
||||
~Client();
|
||||
Client() = default;
|
||||
~Client() = default;
|
||||
|
||||
ErrorCode connectToHost(const ServerCredentials &credentials);
|
||||
void disconnectFromHost();
|
||||
|
@ -32,26 +32,26 @@ namespace libssh {
|
|||
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,
|
||||
ErrorCode scpFileCopy(const ScpOverwriteMode overwriteMode,
|
||||
const QString &localPath,
|
||||
const QString &remotePath,
|
||||
const QString& fileDesc);
|
||||
const QString &fileDesc);
|
||||
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &passphraseCallback);
|
||||
private:
|
||||
ErrorCode closeChannel();
|
||||
ErrorCode closeSftpSession();
|
||||
void closeScpSession();
|
||||
ErrorCode fromLibsshErrorCode();
|
||||
ErrorCode fromLibsshSftpErrorCode(int errorCode);
|
||||
ErrorCode fromFileErrorCode(QFileDevice::FileError fileError);
|
||||
static int callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata);
|
||||
|
||||
ssh_session m_session = nullptr;
|
||||
ssh_channel m_channel = nullptr;
|
||||
sftp_session m_sftpSession = nullptr;
|
||||
ssh_scp m_scpSession = nullptr;
|
||||
|
||||
static std::function<QString()> m_passphraseCallback;
|
||||
signals:
|
||||
void writeToChannelFinished();
|
||||
void sftpFileCopyFinished();
|
||||
void scpFileCopyFinished();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue