File indexing completed on 2024-10-06 04:31:38
0001 /** 0002 * SPDX-FileCopyrightText: 2014 Samoilenko Yuri <kinnalru@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "mounter.h" 0008 0009 #include <QDebug> 0010 #include <QDir> 0011 #include <unistd.h> 0012 0013 #include <KLocalizedString> 0014 0015 #include "config-sftp.h" 0016 #include "kdeconnectconfig.h" 0017 #include "mountloop.h" 0018 #include "plugin_sftp_debug.h" 0019 0020 Mounter::Mounter(SftpPlugin *sftp) 0021 : QObject(sftp) 0022 , m_sftp(sftp) 0023 , m_proc(nullptr) 0024 , m_mountPoint(sftp->mountPoint()) 0025 , m_started(false) 0026 { 0027 connect(&m_connectTimer, &QTimer::timeout, this, &Mounter::onMountTimeout); 0028 0029 connect(this, &Mounter::mounted, &m_connectTimer, &QTimer::stop); 0030 connect(this, &Mounter::failed, &m_connectTimer, &QTimer::stop); 0031 0032 m_connectTimer.setInterval(10000); 0033 m_connectTimer.setSingleShot(true); 0034 0035 QTimer::singleShot(0, this, &Mounter::start); 0036 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Created mounter"; 0037 } 0038 0039 Mounter::~Mounter() 0040 { 0041 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Destroy mounter"; 0042 unmount(false); 0043 } 0044 0045 bool Mounter::wait() 0046 { 0047 if (m_started) { 0048 return true; 0049 } 0050 0051 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting loop to wait for mount"; 0052 0053 MountLoop loop; 0054 connect(this, &Mounter::mounted, &loop, &MountLoop::succeeded); 0055 connect(this, &Mounter::failed, &loop, &MountLoop::failed); 0056 return loop.exec(); 0057 } 0058 0059 void Mounter::onPacketReceived(const NetworkPacket &np) 0060 { 0061 if (np.get<bool>(QStringLiteral("stop"), false)) { 0062 qCDebug(KDECONNECT_PLUGIN_SFTP) << "SFTP server stopped"; 0063 unmount(false); 0064 return; 0065 } 0066 0067 if (np.has(QStringLiteral("errorMessage"))) { 0068 Q_EMIT failed(np.get<QString>(QStringLiteral("errorMessage"))); 0069 return; 0070 } 0071 0072 // This is the previous code, to access sftp server using KIO. Now we are 0073 // using the external binary sshfs, and accessing it as a local filesystem. 0074 /* 0075 * QUrl url; 0076 * url.setScheme("sftp"); 0077 * url.setHost(np.get<QString>("ip")); 0078 * url.setPort(np.get<QString>("port").toInt()); 0079 * url.setUserName(np.get<QString>("user")); 0080 * url.setPassword(np.get<QString>("password")); 0081 * url.setPath(np.get<QString>("path")); 0082 * new KRun(url, 0); 0083 * Q_EMIT mounted(); 0084 */ 0085 0086 unmount(false); 0087 0088 m_proc = new KProcess(); 0089 m_proc->setOutputChannelMode(KProcess::MergedChannels); 0090 0091 connect(m_proc, &QProcess::started, this, &Mounter::onStarted); 0092 connect(m_proc, &QProcess::errorOccurred, this, &Mounter::onError); 0093 connect(m_proc, &QProcess::finished, this, &Mounter::onFinished); 0094 0095 QDir().mkpath(m_mountPoint); 0096 0097 const QString program = QStringLiteral("sshfs"); 0098 0099 QString path; 0100 if (np.has(QStringLiteral("multiPaths"))) 0101 path = QStringLiteral("/"); 0102 else 0103 path = np.get<QString>(QStringLiteral("path")); 0104 0105 QHostAddress addr = m_sftp->device()->getLocalIpAddress(); 0106 if (addr == QHostAddress::Null) { 0107 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Device doesn't have a LanDeviceLink, unable to get IP address"; 0108 return; 0109 } 0110 QString ip = addr.toString(); 0111 if (addr.protocol() == QAbstractSocket::IPv6Protocol) { 0112 ip.prepend(QLatin1Char('[')); 0113 ip.append(QLatin1Char(']')); 0114 } 0115 0116 // clang-format off 0117 const QStringList arguments = 0118 QStringList() << QStringLiteral("%1@%2:%3").arg(np.get<QString>(QStringLiteral("user")), ip, path) 0119 << m_mountPoint << QStringLiteral("-p") << np.get<QString>(QStringLiteral("port")) 0120 << QStringLiteral("-s") // This fixes a bug where file chunks are sent out of order and get corrupted on reception 0121 << QStringLiteral("-f") << QStringLiteral("-F") << QStringLiteral("/dev/null") // Do not use ~/.ssh/config 0122 << QStringLiteral("-o") << QStringLiteral("IdentityFile=") + KdeConnectConfig::instance().privateKeyPath() 0123 << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=no") // Do not ask for confirmation because it is not a known host 0124 << QStringLiteral("-o") << QStringLiteral("UserKnownHostsFile=/dev/null") // Prevent storing as a known host 0125 << QStringLiteral("-o") << QStringLiteral("HostKeyAlgorithms=+ssh-dss\\,ssh-rsa") // https://bugs.kde.org/show_bug.cgi?id=351725 0126 << QStringLiteral("-o") << QStringLiteral("PubkeyAcceptedKeyTypes=+ssh-rsa") // https://bugs.kde.org/show_bug.cgi?id=443155 0127 << QStringLiteral("-o") << QStringLiteral("uid=") + QString::number(getuid()) 0128 << QStringLiteral("-o") << QStringLiteral("gid=") + QString::number(getgid()) 0129 << QStringLiteral("-o") << QStringLiteral("reconnect") 0130 << QStringLiteral("-o") << QStringLiteral("ServerAliveInterval=30") 0131 << QStringLiteral("-o") << QStringLiteral("password_stdin"); 0132 // clang-format on 0133 0134 m_proc->setProgram(program, arguments); 0135 0136 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting process: " << m_proc->program().join(QStringLiteral(" ")); 0137 m_proc->start(); 0138 0139 // qCDebug(KDECONNECT_PLUGIN_SFTP) << "Passing password: " << np.get<QString>("password").toLatin1(); 0140 m_proc->write(np.get<QString>(QStringLiteral("password")).toLatin1()); 0141 m_proc->write("\n"); 0142 } 0143 0144 void Mounter::onStarted() 0145 { 0146 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process started"; 0147 m_started = true; 0148 Q_EMIT mounted(); 0149 0150 // m_proc->setStandardOutputFile("/tmp/kdeconnect-sftp.out"); 0151 // m_proc->setStandardErrorFile("/tmp/kdeconnect-sftp.err"); 0152 0153 auto proc = m_proc; 0154 connect(m_proc, &KProcess::readyReadStandardError, this, [proc]() { 0155 qCDebug(KDECONNECT_PLUGIN_SFTP) << "stderr: " << proc->readAll(); 0156 }); 0157 connect(m_proc, &KProcess::readyReadStandardOutput, this, [proc]() { 0158 qCDebug(KDECONNECT_PLUGIN_SFTP) << "stdout:" << proc->readAll(); 0159 }); 0160 } 0161 0162 void Mounter::onError(QProcess::ProcessError error) 0163 { 0164 if (error == QProcess::FailedToStart) { 0165 qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process failed to start"; 0166 m_started = false; 0167 Q_EMIT failed(i18n("Failed to start sshfs")); 0168 } else if (error == QProcess::ProcessError::Crashed) { 0169 qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process crashed"; 0170 m_started = false; 0171 Q_EMIT failed(i18n("sshfs process crashed")); 0172 } else { 0173 qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process error" << error; 0174 m_started = false; 0175 Q_EMIT failed(i18n("Unknown error in sshfs")); 0176 } 0177 } 0178 0179 void Mounter::onFinished(int exitCode, QProcess::ExitStatus exitStatus) 0180 { 0181 if (exitStatus == QProcess::NormalExit && exitCode == 0) { 0182 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process finished (exit code: " << exitCode << ")"; 0183 Q_EMIT unmounted(); 0184 } else { 0185 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process failed (exit code:" << exitCode << ")"; 0186 Q_EMIT failed(i18n("Error when accessing filesystem. sshfs finished with exit code %0").arg(exitCode)); 0187 } 0188 0189 unmount(true); 0190 } 0191 0192 void Mounter::onMountTimeout() 0193 { 0194 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Timeout: device not responding"; 0195 Q_EMIT failed(i18n("Failed to mount filesystem: device not responding")); 0196 } 0197 0198 void Mounter::start() 0199 { 0200 NetworkPacket np(PACKET_TYPE_SFTP_REQUEST, {{QStringLiteral("startBrowsing"), true}}); 0201 m_sftp->sendPacket(np); 0202 0203 m_connectTimer.start(); 0204 } 0205 0206 void Mounter::unmount(bool finished) 0207 { 0208 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Unmount" << m_proc; 0209 if (m_proc) { 0210 if (!finished) { 0211 // Process is still running, we want to stop it 0212 // But when the finished signal come, we might have already gone. 0213 // Disconnect everything. 0214 m_proc->disconnect(); 0215 m_proc->kill(); 0216 0217 auto proc = m_proc; 0218 m_proc = nullptr; 0219 connect(proc, &QProcess::finished, [proc]() { 0220 qCDebug(KDECONNECT_PLUGIN_SFTP) << "Free" << proc; 0221 proc->deleteLater(); 0222 }); 0223 Q_EMIT unmounted(); 0224 } else 0225 m_proc->deleteLater(); 0226 0227 // Free mount point (won't always succeed if the path is in use) 0228 #if defined(HAVE_FUSERMOUNT) 0229 KProcess::execute(QStringList{QStringLiteral("fusermount"), QStringLiteral("-u"), m_mountPoint}, 10000); 0230 #else 0231 KProcess::execute(QStringList{QStringLiteral("umount"), m_mountPoint}, 10000); 0232 #endif 0233 m_proc = nullptr; 0234 } 0235 m_started = false; 0236 } 0237 0238 #include "moc_mounter.cpp"