2018-11-23 11:07:57 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2018 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "sftptransfer.h"
|
|
|
|
|
|
2019-09-30 14:32:49 +02:00
|
|
|
#include "sshlogging_p.h"
|
2018-12-19 17:28:59 +01:00
|
|
|
#include "sshprocess.h"
|
2018-11-23 11:07:57 +01:00
|
|
|
#include "sshsettings.h"
|
|
|
|
|
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QStringList>
|
|
|
|
|
#include <QTemporaryFile>
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
2021-05-11 14:34:56 +02:00
|
|
|
#include <utils/commandline.h>
|
2018-11-23 11:07:57 +01:00
|
|
|
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace QSsh {
|
|
|
|
|
|
|
|
|
|
struct SftpTransfer::SftpTransferPrivate
|
|
|
|
|
{
|
2018-12-19 17:28:59 +01:00
|
|
|
SshProcess sftpProc;
|
2018-11-23 11:07:57 +01:00
|
|
|
FilesToTransfer files;
|
|
|
|
|
Internal::FileTransferType transferType;
|
|
|
|
|
FileTransferErrorHandling errorHandlingMode;
|
|
|
|
|
QStringList connectionArgs;
|
2019-09-30 14:32:49 +02:00
|
|
|
QString batchFilePath;
|
2018-11-23 11:07:57 +01:00
|
|
|
|
|
|
|
|
QStringList dirsToCreate() const
|
|
|
|
|
{
|
|
|
|
|
QStringList dirs;
|
|
|
|
|
for (const FileToTransfer &f : qAsConst(files)) {
|
|
|
|
|
QString parentDir = QFileInfo(f.targetFile).path();
|
|
|
|
|
while (true) {
|
|
|
|
|
if (dirs.contains(parentDir) || !parentDir.startsWith('/'))
|
|
|
|
|
break;
|
|
|
|
|
dirs << parentDir;
|
|
|
|
|
parentDir = QFileInfo(parentDir).path();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sort(dirs, [](const QString &d1, const QString &d2) {
|
|
|
|
|
if (d1 == "/" && d2 != "/")
|
|
|
|
|
return true;
|
|
|
|
|
return d1.count('/') < d2.count('/');
|
|
|
|
|
});
|
|
|
|
|
return dirs;
|
|
|
|
|
}
|
|
|
|
|
QByteArray transferCommand(bool link) const
|
|
|
|
|
{
|
|
|
|
|
QByteArray command;
|
|
|
|
|
switch (transferType) {
|
|
|
|
|
case Internal::FileTransferType::Upload:
|
|
|
|
|
command = link ? "ln -s" : "put";
|
|
|
|
|
break;
|
|
|
|
|
case Internal::FileTransferType::Download:
|
|
|
|
|
command = "get";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (errorHandlingMode == FileTransferErrorHandling::Ignore)
|
|
|
|
|
command.prepend('-');
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SftpTransfer::~SftpTransfer()
|
|
|
|
|
{
|
2019-09-30 14:32:49 +02:00
|
|
|
if (!d->batchFilePath.isEmpty() && !QFile::remove(d->batchFilePath))
|
|
|
|
|
qCWarning(Internal::sshLog) << "failed to remove batch file" << d->batchFilePath;
|
2018-11-23 11:07:57 +01:00
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpTransfer::start()
|
|
|
|
|
{
|
|
|
|
|
QTimer::singleShot(0, this, &SftpTransfer::doStart);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpTransfer::stop()
|
|
|
|
|
{
|
|
|
|
|
d->sftpProc.terminate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SftpTransfer::SftpTransfer(const FilesToTransfer &files, Internal::FileTransferType type,
|
|
|
|
|
FileTransferErrorHandling errorHandlingMode,
|
|
|
|
|
const QStringList &connectionArgs)
|
|
|
|
|
: d(new SftpTransferPrivate)
|
|
|
|
|
{
|
|
|
|
|
d->files = files;
|
|
|
|
|
d->transferType = type;
|
|
|
|
|
d->errorHandlingMode = errorHandlingMode;
|
|
|
|
|
d->connectionArgs = connectionArgs;
|
|
|
|
|
connect(&d->sftpProc, &QProcess::errorOccurred, [this](QProcess::ProcessError error) {
|
|
|
|
|
if (error == QProcess::FailedToStart)
|
|
|
|
|
emitError(tr("sftp failed to start: %1").arg(d->sftpProc.errorString()));
|
|
|
|
|
});
|
2019-02-11 10:17:53 +01:00
|
|
|
connect(&d->sftpProc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [this] {
|
2018-11-23 11:07:57 +01:00
|
|
|
if (d->sftpProc.exitStatus() != QProcess::NormalExit) {
|
|
|
|
|
emitError(tr("sftp crashed."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (d->sftpProc.exitCode() != 0) {
|
|
|
|
|
emitError(QString::fromLocal8Bit(d->sftpProc.readAllStandardError()));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
emit done(QString());
|
|
|
|
|
});
|
|
|
|
|
connect(&d->sftpProc, &QProcess::readyReadStandardOutput, [this] {
|
|
|
|
|
emit progress(QString::fromLocal8Bit(d->sftpProc.readAllStandardOutput()));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpTransfer::doStart()
|
|
|
|
|
{
|
2019-05-28 13:49:26 +02:00
|
|
|
const FilePath sftpBinary = SshSettings::sftpFilePath();
|
2018-11-23 11:07:57 +01:00
|
|
|
if (!sftpBinary.exists()) {
|
|
|
|
|
emitError(tr("sftp binary \"%1\" does not exist.").arg(sftpBinary.toUserOutput()));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-09-30 14:32:49 +02:00
|
|
|
QTemporaryFile batchFile;
|
|
|
|
|
batchFile.setAutoRemove(false);
|
|
|
|
|
if (!batchFile.isOpen() && !batchFile.open()) {
|
|
|
|
|
emitError(tr("Could not create temporary file: %1").arg(batchFile.errorString()));
|
2018-11-23 11:07:57 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2019-09-30 14:32:49 +02:00
|
|
|
d->batchFilePath = batchFile.fileName();
|
|
|
|
|
batchFile.resize(0);
|
2018-11-23 11:07:57 +01:00
|
|
|
for (const QString &dir : d->dirsToCreate()) {
|
|
|
|
|
switch (d->transferType) {
|
|
|
|
|
case Internal::FileTransferType::Upload:
|
2021-05-06 13:07:36 +02:00
|
|
|
batchFile.write("-mkdir " + ProcessArgs::quoteArgUnix(dir).toLocal8Bit() + '\n');
|
2018-11-23 11:07:57 +01:00
|
|
|
break;
|
|
|
|
|
case Internal::FileTransferType::Download:
|
2021-01-14 18:00:52 +01:00
|
|
|
if (!QDir::root().mkpath(dir)) {
|
2019-03-01 17:04:47 +01:00
|
|
|
emitError(tr("Failed to create local directory \"%1\".")
|
2018-11-23 11:07:57 +01:00
|
|
|
.arg(QDir::toNativeSeparators(dir)));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-16 15:01:44 +01:00
|
|
|
for (const FileToTransfer &f : qAsConst(d->files)) {
|
2021-01-14 18:00:52 +01:00
|
|
|
QString sourceFileOrLinkTarget = f.sourceFile;
|
2018-11-23 11:07:57 +01:00
|
|
|
bool link = false;
|
|
|
|
|
if (d->transferType == Internal::FileTransferType::Upload) {
|
|
|
|
|
QFileInfo fi(f.sourceFile);
|
|
|
|
|
if (fi.isSymLink()) {
|
|
|
|
|
link = true;
|
2021-05-06 13:07:36 +02:00
|
|
|
batchFile.write("-rm " + ProcessArgs::quoteArgUnix(f.targetFile).toLocal8Bit()
|
2019-09-30 14:32:49 +02:00
|
|
|
+ '\n');
|
2019-04-17 16:31:53 +02:00
|
|
|
sourceFileOrLinkTarget = fi.dir().relativeFilePath(fi.symLinkTarget()); // see QTBUG-5817.
|
2018-11-23 11:07:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-09-30 14:32:49 +02:00
|
|
|
batchFile.write(d->transferCommand(link) + ' '
|
2021-05-06 13:07:36 +02:00
|
|
|
+ ProcessArgs::quoteArgUnix(sourceFileOrLinkTarget).toLocal8Bit() + ' '
|
|
|
|
|
+ ProcessArgs::quoteArgUnix(f.targetFile).toLocal8Bit() + '\n');
|
2018-11-23 11:07:57 +01:00
|
|
|
}
|
2020-11-30 14:42:33 +01:00
|
|
|
d->sftpProc.setStandardInputFile(batchFile.fileName());
|
|
|
|
|
d->sftpProc.start(sftpBinary.toString(), d->connectionArgs);
|
2018-11-23 11:07:57 +01:00
|
|
|
emit started();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpTransfer::emitError(const QString &details)
|
|
|
|
|
{
|
|
|
|
|
emit done(tr("File transfer failed: %1").arg(details));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace QSsh
|