2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2012-02-02 17:22:34 +01:00
|
|
|
**
|
2014-01-07 13:27:11 +01:00
|
|
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
2012-10-02 09:12:39 +02:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2012-02-02 17:22:34 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2012-02-02 17:22:34 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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 Digia. For licensing terms and
|
2014-10-01 13:21:18 +02:00
|
|
|
** conditions see http://www.qt.io/licensing. For further information
|
|
|
|
|
** use the contact form at http://www.qt.io/contact-us.
|
2012-02-02 17:22:34 +01:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2014-10-01 13:21:18 +02:00
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2012-10-02 09:12:39 +02:00
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2012-02-02 17:22:34 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2012-02-02 17:22:34 +01:00
|
|
|
#include "sftpfilesystemmodel.h"
|
|
|
|
|
|
|
|
|
|
#include "sftpchannel.h"
|
|
|
|
|
#include "sshconnection.h"
|
|
|
|
|
#include "sshconnectionmanager.h"
|
|
|
|
|
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QHash>
|
|
|
|
|
#include <QIcon>
|
|
|
|
|
#include <QList>
|
|
|
|
|
#include <QString>
|
|
|
|
|
|
2012-05-18 10:49:35 +02:00
|
|
|
namespace QSsh {
|
2012-02-02 17:22:34 +01:00
|
|
|
namespace Internal {
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class SftpDirNode;
|
|
|
|
|
class SftpFileNode
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SftpFileNode() : parent(0) { }
|
|
|
|
|
virtual ~SftpFileNode() { }
|
|
|
|
|
|
|
|
|
|
QString path;
|
|
|
|
|
SftpFileInfo fileInfo;
|
|
|
|
|
SftpDirNode *parent;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class SftpDirNode : public SftpFileNode
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SftpDirNode() : lsState(LsNotYetCalled) { }
|
|
|
|
|
~SftpDirNode() { qDeleteAll(children); }
|
|
|
|
|
|
|
|
|
|
enum { LsNotYetCalled, LsRunning, LsFinished } lsState;
|
|
|
|
|
QList<SftpFileNode *> children;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef QHash<SftpJobId, SftpDirNode *> DirNodeHash;
|
|
|
|
|
|
|
|
|
|
SftpFileNode *indexToFileNode(const QModelIndex &index)
|
|
|
|
|
{
|
|
|
|
|
return static_cast<SftpFileNode *>(index.internalPointer());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SftpDirNode *indexToDirNode(const QModelIndex &index)
|
|
|
|
|
{
|
|
|
|
|
SftpFileNode * const fileNode = indexToFileNode(index);
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT(fileNode);
|
2012-02-02 17:22:34 +01:00
|
|
|
return dynamic_cast<SftpDirNode *>(fileNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
class SftpFileSystemModelPrivate
|
|
|
|
|
{
|
|
|
|
|
public:
|
2012-05-29 13:22:33 +02:00
|
|
|
SshConnection *sshConnection;
|
2012-02-02 17:22:34 +01:00
|
|
|
SftpChannel::Ptr sftpChannel;
|
|
|
|
|
QString rootDirectory;
|
|
|
|
|
SftpFileNode *rootNode;
|
|
|
|
|
SftpJobId statJobId;
|
|
|
|
|
DirNodeHash lsOps;
|
2012-02-06 12:03:08 +01:00
|
|
|
QList<SftpJobId> externalJobs;
|
2012-02-02 17:22:34 +01:00
|
|
|
};
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
|
|
|
|
|
using namespace Internal;
|
|
|
|
|
|
|
|
|
|
SftpFileSystemModel::SftpFileSystemModel(QObject *parent)
|
|
|
|
|
: QAbstractItemModel(parent), d(new SftpFileSystemModelPrivate)
|
|
|
|
|
{
|
2012-05-29 13:22:33 +02:00
|
|
|
d->sshConnection = 0;
|
2014-08-29 14:00:18 +02:00
|
|
|
d->rootDirectory = QLatin1Char('/');
|
2012-02-02 17:22:34 +01:00
|
|
|
d->rootNode = 0;
|
|
|
|
|
d->statJobId = SftpInvalidJob;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SftpFileSystemModel::~SftpFileSystemModel()
|
|
|
|
|
{
|
|
|
|
|
shutDown();
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::setSshConnection(const SshConnectionParameters &sshParams)
|
|
|
|
|
{
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN(!d->sshConnection);
|
2013-09-10 18:52:17 +02:00
|
|
|
d->sshConnection = QSsh::acquireConnection(sshParams);
|
2012-05-29 13:22:33 +02:00
|
|
|
connect(d->sshConnection, SIGNAL(error(QSsh::SshError)), SLOT(handleSshConnectionFailure()));
|
2012-04-03 10:06:44 +02:00
|
|
|
if (d->sshConnection->state() == SshConnection::Connected) {
|
2012-02-02 17:22:34 +01:00
|
|
|
handleSshConnectionEstablished();
|
2012-04-03 10:06:44 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2012-05-29 13:22:33 +02:00
|
|
|
connect(d->sshConnection, SIGNAL(connected()), SLOT(handleSshConnectionEstablished()));
|
2012-02-02 17:22:34 +01:00
|
|
|
if (d->sshConnection->state() == SshConnection::Unconnected)
|
|
|
|
|
d->sshConnection->connectToHost();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::setRootDirectory(const QString &path)
|
|
|
|
|
{
|
|
|
|
|
beginResetModel();
|
|
|
|
|
d->rootDirectory = path;
|
|
|
|
|
delete d->rootNode;
|
|
|
|
|
d->rootNode = 0;
|
|
|
|
|
d->lsOps.clear();
|
|
|
|
|
d->statJobId = SftpInvalidJob;
|
|
|
|
|
endResetModel();
|
|
|
|
|
statRootDirectory();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString SftpFileSystemModel::rootDirectory() const
|
|
|
|
|
{
|
|
|
|
|
return d->rootDirectory;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-06 12:03:08 +01:00
|
|
|
SftpJobId SftpFileSystemModel::downloadFile(const QModelIndex &index, const QString &targetFilePath)
|
|
|
|
|
{
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN_VALUE(d->rootNode, SftpInvalidJob);
|
2012-02-06 12:03:08 +01:00
|
|
|
const SftpFileNode * const fileNode = indexToFileNode(index);
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN_VALUE(fileNode, SftpInvalidJob);
|
|
|
|
|
QSSH_ASSERT_AND_RETURN_VALUE(fileNode->fileInfo.type == FileTypeRegular, SftpInvalidJob);
|
2012-02-06 12:03:08 +01:00
|
|
|
const SftpJobId jobId = d->sftpChannel->downloadFile(fileNode->path, targetFilePath,
|
|
|
|
|
SftpOverwriteExisting);
|
|
|
|
|
if (jobId != SftpInvalidJob)
|
|
|
|
|
d->externalJobs << jobId;
|
|
|
|
|
return jobId;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-02 17:22:34 +01:00
|
|
|
int SftpFileSystemModel::columnCount(const QModelIndex &parent) const
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(parent);
|
|
|
|
|
return 2; // type + name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant SftpFileSystemModel::data(const QModelIndex &index, int role) const
|
|
|
|
|
{
|
|
|
|
|
const SftpFileNode * const node = indexToFileNode(index);
|
|
|
|
|
if (index.column() == 0 && role == Qt::DecorationRole) {
|
|
|
|
|
switch (node->fileInfo.type) {
|
|
|
|
|
case FileTypeRegular:
|
|
|
|
|
case FileTypeOther:
|
|
|
|
|
return QIcon(QLatin1String(":/core/images/unknownfile.png"));
|
|
|
|
|
case FileTypeDirectory:
|
|
|
|
|
return QIcon(QLatin1String(":/core/images/dir.png"));
|
|
|
|
|
case FileTypeUnknown:
|
|
|
|
|
return QIcon(QLatin1String(":/core/images/help.png")); // Shows a question mark.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (index.column() == 1) {
|
|
|
|
|
if (role == Qt::DisplayRole)
|
|
|
|
|
return node->fileInfo.name;
|
|
|
|
|
if (role == PathRole)
|
|
|
|
|
return node->path;
|
|
|
|
|
}
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Qt::ItemFlags SftpFileSystemModel::flags(const QModelIndex &index) const
|
|
|
|
|
{
|
|
|
|
|
if (!index.isValid())
|
|
|
|
|
return Qt::NoItemFlags;
|
|
|
|
|
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant SftpFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
|
{
|
|
|
|
|
if (orientation != Qt::Horizontal)
|
|
|
|
|
return QVariant();
|
|
|
|
|
if (role != Qt::DisplayRole)
|
|
|
|
|
return QVariant();
|
|
|
|
|
if (section == 0)
|
|
|
|
|
return tr("File Type");
|
|
|
|
|
if (section == 1)
|
|
|
|
|
return tr("File Name");
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QModelIndex SftpFileSystemModel::index(int row, int column, const QModelIndex &parent) const
|
|
|
|
|
{
|
|
|
|
|
if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
if (!d->rootNode)
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
if (!parent.isValid())
|
|
|
|
|
return createIndex(row, column, d->rootNode);
|
|
|
|
|
const SftpDirNode * const parentNode = indexToDirNode(parent);
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN_VALUE(parentNode, QModelIndex());
|
|
|
|
|
QSSH_ASSERT_AND_RETURN_VALUE(row < parentNode->children.count(), QModelIndex());
|
2012-02-02 17:22:34 +01:00
|
|
|
SftpFileNode * const childNode = parentNode->children.at(row);
|
|
|
|
|
return createIndex(row, column, childNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QModelIndex SftpFileSystemModel::parent(const QModelIndex &child) const
|
|
|
|
|
{
|
|
|
|
|
if (!child.isValid()) // Don't assert on this, since the model tester tries it.
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
|
|
|
|
|
const SftpFileNode * const childNode = indexToFileNode(child);
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN_VALUE(childNode, QModelIndex());
|
2012-02-02 17:22:34 +01:00
|
|
|
if (childNode == d->rootNode)
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
SftpDirNode * const parentNode = childNode->parent;
|
|
|
|
|
if (parentNode == d->rootNode)
|
|
|
|
|
return createIndex(0, 0, d->rootNode);
|
|
|
|
|
const SftpDirNode * const grandParentNode = parentNode->parent;
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN_VALUE(grandParentNode, QModelIndex());
|
2012-02-02 17:22:34 +01:00
|
|
|
return createIndex(grandParentNode->children.indexOf(parentNode), 0, parentNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SftpFileSystemModel::rowCount(const QModelIndex &parent) const
|
|
|
|
|
{
|
|
|
|
|
if (!d->rootNode)
|
|
|
|
|
return 0;
|
|
|
|
|
if (!parent.isValid())
|
|
|
|
|
return 1;
|
|
|
|
|
if (parent.column() != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
SftpDirNode * const dirNode = indexToDirNode(parent);
|
|
|
|
|
if (!dirNode)
|
|
|
|
|
return 0;
|
|
|
|
|
if (dirNode->lsState != SftpDirNode::LsNotYetCalled)
|
|
|
|
|
return dirNode->children.count();
|
|
|
|
|
d->lsOps.insert(d->sftpChannel->listDirectory(dirNode->path), dirNode);
|
|
|
|
|
dirNode->lsState = SftpDirNode::LsRunning;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::statRootDirectory()
|
|
|
|
|
{
|
|
|
|
|
d->statJobId = d->sftpChannel->statFile(d->rootDirectory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::shutDown()
|
|
|
|
|
{
|
|
|
|
|
if (d->sftpChannel) {
|
|
|
|
|
disconnect(d->sftpChannel.data(), 0, this, 0);
|
|
|
|
|
d->sftpChannel->closeChannel();
|
|
|
|
|
d->sftpChannel.clear();
|
|
|
|
|
}
|
|
|
|
|
if (d->sshConnection) {
|
2012-05-29 13:22:33 +02:00
|
|
|
disconnect(d->sshConnection, 0, this, 0);
|
2013-09-10 18:52:17 +02:00
|
|
|
QSsh::releaseConnection(d->sshConnection);
|
2012-05-29 13:22:33 +02:00
|
|
|
d->sshConnection = 0;
|
2012-02-02 17:22:34 +01:00
|
|
|
}
|
|
|
|
|
delete d->rootNode;
|
|
|
|
|
d->rootNode = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::handleSshConnectionFailure()
|
|
|
|
|
{
|
|
|
|
|
emit connectionError(d->sshConnection->errorString());
|
|
|
|
|
beginResetModel();
|
|
|
|
|
shutDown();
|
|
|
|
|
endResetModel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::handleSftpChannelInitialized()
|
|
|
|
|
{
|
|
|
|
|
connect(d->sftpChannel.data(),
|
2012-05-18 10:49:35 +02:00
|
|
|
SIGNAL(fileInfoAvailable(QSsh::SftpJobId,QList<QSsh::SftpFileInfo>)),
|
|
|
|
|
SLOT(handleFileInfo(QSsh::SftpJobId,QList<QSsh::SftpFileInfo>)));
|
|
|
|
|
connect(d->sftpChannel.data(), SIGNAL(finished(QSsh::SftpJobId,QString)),
|
|
|
|
|
SLOT(handleSftpJobFinished(QSsh::SftpJobId,QString)));
|
2012-02-02 17:22:34 +01:00
|
|
|
statRootDirectory();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::handleSshConnectionEstablished()
|
|
|
|
|
{
|
|
|
|
|
d->sftpChannel = d->sshConnection->createSftpChannel();
|
|
|
|
|
connect(d->sftpChannel.data(), SIGNAL(initialized()), SLOT(handleSftpChannelInitialized()));
|
2014-01-15 18:23:08 +01:00
|
|
|
connect(d->sftpChannel.data(), SIGNAL(channelError(QString)),
|
|
|
|
|
SLOT(handleSftpChannelError(QString)));
|
2012-02-02 17:22:34 +01:00
|
|
|
d->sftpChannel->initialize();
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-15 18:23:08 +01:00
|
|
|
void SftpFileSystemModel::handleSftpChannelError(const QString &reason)
|
2012-02-02 17:22:34 +01:00
|
|
|
{
|
|
|
|
|
emit connectionError(reason);
|
|
|
|
|
beginResetModel();
|
|
|
|
|
shutDown();
|
|
|
|
|
endResetModel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::handleFileInfo(SftpJobId jobId, const QList<SftpFileInfo> &fileInfoList)
|
|
|
|
|
{
|
|
|
|
|
if (jobId == d->statJobId) {
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN(!d->rootNode);
|
2012-02-02 17:22:34 +01:00
|
|
|
beginInsertRows(QModelIndex(), 0, 0);
|
|
|
|
|
d->rootNode = new SftpDirNode;
|
|
|
|
|
d->rootNode->path = d->rootDirectory;
|
|
|
|
|
d->rootNode->fileInfo = fileInfoList.first();
|
|
|
|
|
d->rootNode->fileInfo.name = d->rootDirectory == QLatin1String("/")
|
|
|
|
|
? d->rootDirectory : QFileInfo(d->rootDirectory).fileName();
|
|
|
|
|
endInsertRows();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SftpDirNode * const parentNode = d->lsOps.value(jobId);
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN(parentNode);
|
2012-02-02 17:22:34 +01:00
|
|
|
QList<SftpFileInfo> filteredList;
|
|
|
|
|
foreach (const SftpFileInfo &fi, fileInfoList) {
|
|
|
|
|
if (fi.name != QLatin1String(".") && fi.name != QLatin1String(".."))
|
|
|
|
|
filteredList << fi;
|
|
|
|
|
}
|
|
|
|
|
if (filteredList.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// In theory beginInsertRows() should suffice, but that fails to have an effect
|
|
|
|
|
// if rowCount() returned 0 earlier.
|
|
|
|
|
emit layoutAboutToBeChanged();
|
|
|
|
|
|
|
|
|
|
foreach (const SftpFileInfo &fileInfo, filteredList) {
|
|
|
|
|
SftpFileNode *childNode;
|
|
|
|
|
if (fileInfo.type == FileTypeDirectory)
|
|
|
|
|
childNode = new SftpDirNode;
|
|
|
|
|
else
|
|
|
|
|
childNode = new SftpFileNode;
|
|
|
|
|
childNode->path = parentNode->path;
|
|
|
|
|
if (!childNode->path.endsWith(QLatin1Char('/')))
|
|
|
|
|
childNode->path += QLatin1Char('/');
|
|
|
|
|
childNode->path += fileInfo.name;
|
|
|
|
|
childNode->fileInfo = fileInfo;
|
|
|
|
|
childNode->parent = parentNode;
|
|
|
|
|
parentNode->children << childNode;
|
|
|
|
|
}
|
|
|
|
|
emit layoutChanged(); // Should be endInsertRows(), see above.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SftpFileSystemModel::handleSftpJobFinished(SftpJobId jobId, const QString &errorMessage)
|
|
|
|
|
{
|
|
|
|
|
if (jobId == d->statJobId) {
|
|
|
|
|
d->statJobId = SftpInvalidJob;
|
2012-02-06 12:03:08 +01:00
|
|
|
if (!errorMessage.isEmpty())
|
2014-04-17 14:09:47 +02:00
|
|
|
emit sftpOperationFailed(tr("Error getting \"stat\" info about \"%1\": %2")
|
2012-02-06 12:03:08 +01:00
|
|
|
.arg(rootDirectory(), errorMessage));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DirNodeHash::Iterator it = d->lsOps.find(jobId);
|
|
|
|
|
if (it != d->lsOps.end()) {
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT(it.value()->lsState == SftpDirNode::LsRunning);
|
2012-02-02 17:22:34 +01:00
|
|
|
it.value()->lsState = SftpDirNode::LsFinished;
|
2012-02-06 12:03:08 +01:00
|
|
|
if (!errorMessage.isEmpty())
|
2014-04-17 14:09:47 +02:00
|
|
|
emit sftpOperationFailed(tr("Error listing contents of directory \"%1\": %2")
|
2012-02-06 12:03:08 +01:00
|
|
|
.arg(it.value()->path, errorMessage));
|
2012-02-02 17:22:34 +01:00
|
|
|
d->lsOps.erase(it);
|
2012-02-06 12:03:08 +01:00
|
|
|
return;
|
2012-02-02 17:22:34 +01:00
|
|
|
}
|
|
|
|
|
|
2012-02-06 12:03:08 +01:00
|
|
|
const int jobIndex = d->externalJobs.indexOf(jobId);
|
2012-05-18 10:49:35 +02:00
|
|
|
QSSH_ASSERT_AND_RETURN(jobIndex != -1);
|
2012-02-06 12:03:08 +01:00
|
|
|
d->externalJobs.removeAt(jobIndex);
|
|
|
|
|
emit sftpOperationFinished(jobId, errorMessage);
|
2012-02-02 17:22:34 +01:00
|
|
|
}
|
|
|
|
|
|
2012-05-18 10:49:35 +02:00
|
|
|
} // namespace QSsh
|