File indexing completed on 2024-12-01 04:37:13
0001 /* 0002 The helper that mounts and unmounts shares. 0003 0004 SPDX-FileCopyrightText: 2010-2022 Alexander Reinholdt <alexander.reinholdt@kdemail.net> 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 // application specific includes 0009 #include "smb4kmounthelper.h" 0010 #include "../core/smb4kglobal.h" 0011 0012 // Qt includes 0013 #include <QDebug> 0014 #include <QNetworkInterface> 0015 #include <QProcessEnvironment> 0016 #include <QUrl> 0017 0018 // KDE includes 0019 #include <KAuth/HelperSupport> 0020 #include <KLocalizedString> 0021 #include <KMountPoint> 0022 #include <KProcess> 0023 0024 using namespace Smb4KGlobal; 0025 0026 KAUTH_HELPER_MAIN("org.kde.smb4k.mounthelper", Smb4KMountHelper); 0027 0028 KAuth::ActionReply Smb4KMountHelper::mount(const QVariantMap &args) 0029 { 0030 // 0031 // The action reply 0032 // 0033 ActionReply reply; 0034 0035 // 0036 // Check if the system is online and return an error if it is not 0037 // 0038 bool online = false; 0039 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); 0040 0041 for (const QNetworkInterface &interface : qAsConst(interfaces)) { 0042 if (interface.isValid() && interface.type() != QNetworkInterface::Loopback && interface.flags() & QNetworkInterface::IsRunning && !online) { 0043 online = true; 0044 break; 0045 } 0046 } 0047 0048 if (!online) { 0049 reply.setType(ActionReply::HelperErrorType); 0050 return reply; 0051 } 0052 0053 // 0054 // Get the mount executable 0055 // 0056 const QString mount = findMountExecutable(); 0057 0058 // 0059 // Check the mount executable 0060 // 0061 if (mount != args[QStringLiteral("mh_command")].toString()) { 0062 // Something weird is going on, bail out. 0063 reply.setType(ActionReply::HelperErrorType); 0064 return reply; 0065 } 0066 0067 // 0068 // Mount command 0069 // 0070 QStringList command; 0071 #if defined(Q_OS_LINUX) 0072 command << mount; 0073 command << args[QStringLiteral("mh_url")].toUrl().toString(QUrl::RemoveScheme | QUrl::RemoveUserInfo | QUrl::RemovePort); 0074 command << args[QStringLiteral("mh_mountpoint")].toString(); 0075 command << args[QStringLiteral("mh_options")].toStringList(); 0076 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0077 command << mount; 0078 command << args[QStringLiteral("mh_options")].toStringList(); 0079 command << args[QStringLiteral("mh_url")].toUrl().toString(QUrl::RemoveScheme | QUrl::RemoveUserInfo | QUrl::RemovePort); 0080 command << args[QStringLiteral("mh_mountpoint")].toString(); 0081 #endif 0082 0083 // 0084 // The process 0085 // 0086 KProcess proc(this); 0087 proc.setOutputChannelMode(KProcess::SeparateChannels); 0088 proc.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); 0089 #if defined(Q_OS_LINUX) 0090 proc.setEnv(QStringLiteral("PASSWD"), args[QStringLiteral("mh_url")].toUrl().password(), true); 0091 #endif 0092 // We need this to avoid a translated password prompt. 0093 proc.setEnv(QStringLiteral("LANG"), QStringLiteral("C")); 0094 // If the location of a Kerberos ticket is passed, it needs to 0095 // be passed to the process environment here. 0096 if (args.contains(QStringLiteral("mh_krb5ticket"))) { 0097 proc.setEnv(QStringLiteral("KRB5CCNAME"), args[QStringLiteral("mh_krb5ticket")].toString()); 0098 } 0099 0100 proc.setProgram(command); 0101 proc.start(); 0102 0103 if (proc.waitForStarted(-1)) { 0104 int timeout = 0; 0105 0106 while (proc.state() != KProcess::NotRunning) { 0107 // We want to be able to terminate the process from outside. 0108 // Thus, we implement a loop that checks periodically, if we 0109 // need to kill the process. 0110 if (HelperSupport::isStopped() || timeout == 30000) { 0111 proc.kill(); 0112 break; 0113 } 0114 0115 // Check if there is a password prompt. If there is one, pass 0116 // the password to it. 0117 QByteArray out = proc.readAllStandardError(); 0118 0119 if (out.startsWith("Password")) { 0120 proc.write(args[QStringLiteral("mh_url")].toUrl().password().toUtf8().data()); 0121 proc.write("\r"); 0122 } 0123 0124 timeout += 10; 0125 0126 wait(10); 0127 } 0128 0129 if (proc.exitStatus() == KProcess::NormalExit) { 0130 QString stdErr = QString::fromUtf8(proc.readAllStandardError()); 0131 reply.addData(QStringLiteral("mh_error_message"), stdErr.trimmed()); 0132 } 0133 } else { 0134 reply.setType(ActionReply::HelperErrorType); 0135 reply.setErrorDescription(i18n("The mount process could not be started.")); 0136 } 0137 0138 return reply; 0139 } 0140 0141 KAuth::ActionReply Smb4KMountHelper::unmount(const QVariantMap &args) 0142 { 0143 ActionReply reply; 0144 0145 // 0146 // Get the umount executable 0147 // 0148 const QString umount = findUmountExecutable(); 0149 0150 // 0151 // Check the mount executable 0152 // 0153 if (umount != args[QStringLiteral("mh_command")].toString()) { 0154 // Something weird is going on, bail out. 0155 reply.setType(ActionReply::HelperErrorType); 0156 return reply; 0157 } 0158 0159 // 0160 // Check if the mountpoint is valid and the filesystem is correct. 0161 // 0162 bool mountPointOk = false; 0163 KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded | KMountPoint::NeedMountOptions); 0164 0165 for (const QExplicitlySharedDataPointer<KMountPoint> &mountPoint : qAsConst(mountPoints)) { 0166 if (args[QStringLiteral("mh_mountpoint")].toString() == mountPoint->mountPoint() 0167 && (mountPoint->mountType() == QStringLiteral("cifs") || mountPoint->mountType() == QStringLiteral("smb3") 0168 || mountPoint->mountType() == QStringLiteral("smbfs"))) { 0169 mountPointOk = true; 0170 break; 0171 } 0172 } 0173 0174 // 0175 // Stop here if the mountpoint is not valid 0176 // 0177 if (!mountPointOk) { 0178 reply.setType(ActionReply::HelperErrorType); 0179 reply.setErrorDescription(i18n("The mountpoint %1 is invalid.", args[QStringLiteral("mh_mountpoint")].toString())); 0180 } 0181 0182 // 0183 // The command 0184 // 0185 QStringList command; 0186 command << umount; 0187 command << args[QStringLiteral("mh_options")].toStringList(); 0188 command << args[QStringLiteral("mh_mountpoint")].toString(); 0189 0190 // 0191 // The process 0192 // 0193 KProcess proc(this); 0194 proc.setOutputChannelMode(KProcess::SeparateChannels); 0195 proc.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); 0196 proc.setProgram(command); 0197 0198 // 0199 // Depending on the online state, use a different behavior for unmounting. 0200 // 0201 // Extensive tests have shown that - when offline - unmounting does not 0202 // work properly when the process is not detached. Thus, detach it when 0203 // the system is offline. 0204 // 0205 bool online = false; 0206 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); 0207 0208 for (const QNetworkInterface &interface : qAsConst(interfaces)) { 0209 if (interface.isValid() && interface.type() != QNetworkInterface::Loopback && interface.flags() & QNetworkInterface::IsRunning && !online) { 0210 online = true; 0211 break; 0212 } 0213 } 0214 0215 if (online) { 0216 proc.start(); 0217 0218 if (proc.waitForStarted(-1)) { 0219 // We want to be able to terminate the process from outside. 0220 // Thus, we implement a loop that checks periodically, if we 0221 // need to kill the process. 0222 int timeout = 0; 0223 0224 while (proc.state() != KProcess::NotRunning) { 0225 if (HelperSupport::isStopped() || timeout == 30000) { 0226 proc.kill(); 0227 break; 0228 } 0229 0230 timeout += 10; 0231 0232 wait(10); 0233 } 0234 0235 if (proc.exitStatus() == KProcess::NormalExit) { 0236 QString stdErr = QString::fromUtf8(proc.readAllStandardError()); 0237 reply.addData(QStringLiteral("mh_error_message"), stdErr.trimmed()); 0238 } 0239 } else { 0240 reply.setType(ActionReply::HelperErrorType); 0241 reply.setErrorDescription(i18n("The unmount process could not be started.")); 0242 } 0243 } else { 0244 proc.startDetached(); 0245 } 0246 0247 return reply; 0248 }