kdeconnect-kde/plugins/sftp/mounter.cpp

238 lines
8.5 KiB
C++
Raw Normal View History

/**
* SPDX-FileCopyrightText: 2014 Samoilenko Yuri <kinnalru@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
2015-06-21 20:32:18 +01:00
#include "mounter.h"
#include <QDebug>
#include <QDir>
#include <unistd.h>
#include <KLocalizedString>
#include "config-sftp.h"
2016-07-05 13:27:53 +01:00
#include "kdeconnectconfig.h"
#include "mountloop.h"
#include "plugin_sftp_debug.h"
Mounter::Mounter(SftpPlugin *sftp)
: QObject(sftp)
, m_sftp(sftp)
, m_proc(nullptr)
2015-06-21 20:32:18 +01:00
, m_mountPoint(sftp->mountPoint())
, m_started(false)
2015-06-21 20:32:18 +01:00
{
connect(m_sftp, &SftpPlugin::packetReceived, this, &Mounter::onPackageReceived);
2016-11-26 14:12:38 +00:00
connect(&m_connectTimer, &QTimer::timeout, this, &Mounter::onMountTimeout);
2016-11-26 14:12:38 +00:00
connect(this, &Mounter::mounted, &m_connectTimer, &QTimer::stop);
connect(this, &Mounter::failed, &m_connectTimer, &QTimer::stop);
m_connectTimer.setInterval(10000);
m_connectTimer.setSingleShot(true);
2016-11-26 14:12:38 +00:00
QTimer::singleShot(0, this, &Mounter::start);
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Created mounter";
}
Mounter::~Mounter()
{
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Destroy mounter";
2017-03-15 10:34:12 +00:00
unmount(false);
}
bool Mounter::wait()
{
if (m_started) {
return true;
}
2017-03-15 10:34:12 +00:00
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting loop to wait for mount";
2017-03-15 10:34:12 +00:00
MountLoop loop;
2020-08-06 12:51:31 +01:00
connect(this, &Mounter::mounted, &loop, &MountLoop::succeeded);
2016-11-26 14:12:38 +00:00
connect(this, &Mounter::failed, &loop, &MountLoop::failed);
return loop.exec();
}
void Mounter::onPackageReceived(const NetworkPacket &np)
{
if (np.get<bool>(QStringLiteral("stop"), false)) {
qCDebug(KDECONNECT_PLUGIN_SFTP) << "SFTP server stopped";
2017-03-15 10:34:12 +00:00
unmount(false);
return;
}
if (np.has(QStringLiteral("errorMessage"))) {
Q_EMIT failed(np.get<QString>(QStringLiteral("errorMessage")));
return;
}
2014-02-14 19:12:40 +00:00
// This is the previous code, to access sftp server using KIO. Now we are
// using the external binary sshfs, and accessing it as a local filesystem.
/*
* QUrl url;
* url.setScheme("sftp");
* url.setHost(np.get<QString>("ip"));
* url.setPort(np.get<QString>("port").toInt());
* url.setUserName(np.get<QString>("user"));
* url.setPassword(np.get<QString>("password"));
* url.setPath(np.get<QString>("path"));
* new KRun(url, 0);
* Q_EMIT mounted();
*/
2014-02-14 19:12:40 +00:00
2017-03-15 10:34:12 +00:00
unmount(false);
2017-03-15 10:34:12 +00:00
m_proc = new KProcess();
m_proc->setOutputChannelMode(KProcess::MergedChannels);
2016-11-26 14:12:38 +00:00
connect(m_proc, &QProcess::started, this, &Mounter::onStarted);
2019-04-28 15:35:44 +01:00
connect(m_proc, &QProcess::errorOccurred, this, &Mounter::onError);
connect(m_proc, &QProcess::finished, this, &Mounter::onFinished);
2015-06-21 20:32:18 +01:00
QDir().mkpath(m_mountPoint);
2014-02-14 19:12:40 +00:00
const QString program = QStringLiteral("sshfs");
QString path;
if (np.has(QStringLiteral("multiPaths")))
path = QStringLiteral("/");
else
path = np.get<QString>(QStringLiteral("path"));
QHostAddress addr = m_sftp->device()->getLocalIpAddress();
if (addr == QHostAddress::Null) {
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Device doesn't have a LanDeviceLink, unable to get IP address";
return;
}
QString ip = addr.toString();
2021-03-31 03:13:24 +01:00
if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
ip.prepend(QLatin1Char('['));
ip.append(QLatin1Char(']'));
}
const QStringList arguments =
QStringList() << QStringLiteral("%1@%2:%3")
.arg(np.get<QString>(QStringLiteral("user")),
ip,
path)
<< m_mountPoint << QStringLiteral("-p") << np.get<QString>(QStringLiteral("port"))
<< QStringLiteral("-s") // This fixes a bug where file chunks are sent out of order and get corrupted on reception
<< QStringLiteral("-f") << QStringLiteral("-F") << QStringLiteral("/dev/null") // Do not use ~/.ssh/config
<< QStringLiteral("-o") << QStringLiteral("IdentityFile=") + KdeConnectConfig::instance().privateKeyPath() << QStringLiteral("-o")
<< QStringLiteral("StrictHostKeyChecking=no") // Do not ask for confirmation because it is not a known host
<< QStringLiteral("-o") << QStringLiteral("UserKnownHostsFile=/dev/null") // Prevent storing as a known host
<< QStringLiteral("-o") << QStringLiteral("HostKeyAlgorithms=+ssh-dss\\,ssh-rsa") // https://bugs.kde.org/show_bug.cgi?id=351725
<< QStringLiteral("-o") << QStringLiteral("PubkeyAcceptedKeyTypes=+ssh-rsa") // https://bugs.kde.org/show_bug.cgi?id=443155
<< QStringLiteral("-o") << QStringLiteral("uid=") + QString::number(getuid()) << QStringLiteral("-o")
<< QStringLiteral("gid=") + QString::number(getgid()) << QStringLiteral("-o") << QStringLiteral("reconnect") << QStringLiteral("-o")
<< QStringLiteral("ServerAliveInterval=30") << QStringLiteral("-o") << QStringLiteral("password_stdin");
m_proc->setProgram(program, arguments);
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting process: " << m_proc->program().join(QStringLiteral(" "));
m_proc->start();
// qCDebug(KDECONNECT_PLUGIN_SFTP) << "Passing password: " << np.get<QString>("password").toLatin1();
m_proc->write(np.get<QString>(QStringLiteral("password")).toLatin1());
m_proc->write("\n");
}
void Mounter::onStarted()
{
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process started";
m_started = true;
Q_EMIT mounted();
// m_proc->setStandardOutputFile("/tmp/kdeconnect-sftp.out");
// m_proc->setStandardErrorFile("/tmp/kdeconnect-sftp.err");
2017-03-15 10:34:12 +00:00
auto proc = m_proc;
2019-02-28 13:18:47 +00:00
connect(m_proc, &KProcess::readyReadStandardError, this, [proc]() {
2017-03-15 10:34:12 +00:00
qCDebug(KDECONNECT_PLUGIN_SFTP) << "stderr: " << proc->readAll();
2015-06-21 20:32:18 +01:00
});
2019-02-28 13:18:47 +00:00
connect(m_proc, &KProcess::readyReadStandardOutput, this, [proc]() {
2017-03-15 10:34:12 +00:00
qCDebug(KDECONNECT_PLUGIN_SFTP) << "stdout:" << proc->readAll();
2015-06-21 20:32:18 +01:00
});
}
void Mounter::onError(QProcess::ProcessError error)
{
if (error == QProcess::FailedToStart) {
2019-04-25 19:47:50 +01:00
qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process failed to start";
m_started = false;
Q_EMIT failed(i18n("Failed to start sshfs"));
} else if (error == QProcess::ProcessError::Crashed) {
2019-04-25 19:47:50 +01:00
qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process crashed";
m_started = false;
Q_EMIT failed(i18n("sshfs process crashed"));
} else {
qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process error" << error;
m_started = false;
Q_EMIT failed(i18n("Unknown error in sshfs"));
}
}
void Mounter::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process finished (exit code: " << exitCode << ")";
Q_EMIT unmounted();
} else {
2015-06-21 20:32:18 +01:00
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process failed (exit code:" << exitCode << ")";
2019-04-28 20:28:06 +01:00
Q_EMIT failed(i18n("Error when accessing filesystem. sshfs finished with exit code %0").arg(exitCode));
}
2017-03-15 10:34:12 +00:00
unmount(true);
}
void Mounter::onMountTimeout()
{
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Timeout: device not responding";
Q_EMIT failed(i18n("Failed to mount filesystem: device not responding"));
}
void Mounter::start()
{
NetworkPacket np(PACKET_TYPE_SFTP_REQUEST, {{QStringLiteral("startBrowsing"), true}});
m_sftp->sendPacket(np);
2017-03-15 10:34:12 +00:00
m_connectTimer.start();
}
2017-03-15 10:34:12 +00:00
void Mounter::unmount(bool finished)
{
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Unmount" << m_proc;
if (m_proc) {
if (!finished) {
// Process is still running, we want to stop it
// But when the finished signal come, we might have already gone.
// Disconnect everything.
2017-03-15 10:34:12 +00:00
m_proc->disconnect();
m_proc->kill();
auto proc = m_proc;
m_proc = nullptr;
connect(proc, &QProcess::finished, [proc]() {
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Free" << proc;
proc->deleteLater();
2017-03-15 10:34:12 +00:00
});
Q_EMIT unmounted();
} else
2017-03-15 10:34:12 +00:00
m_proc->deleteLater();
// Free mount point (won't always succeed if the path is in use)
#if defined(HAVE_FUSERMOUNT)
KProcess::execute(QStringList{QStringLiteral("fusermount"), QStringLiteral("-u"), m_mountPoint}, 10000);
#else
KProcess::execute(QStringList{QStringLiteral("umount"), m_mountPoint}, 10000);
#endif
2017-03-15 10:34:12 +00:00
m_proc = nullptr;
}
m_started = false;
}