File indexing completed on 2024-04-21 15:42:51

0001 /***************************************************************************
0002     The helper that mounts and unmounts shares.
0003                              -------------------
0004     begin                : Sa Okt 16 2010
0005     copyright            : (C) 2010-2020 by Alexander Reinholdt
0006     email                : alexander.reinholdt@kdemail.net
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *   This program is free software; you can redistribute it and/or modify  *
0011  *   it under the terms of the GNU General Public License as published by  *
0012  *   the Free Software Foundation; either version 2 of the License, or     *
0013  *   (at your option) any later version.                                   *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful, but   *
0016  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
0018  *   General Public License for more details.                              *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program; if not, write to the                         *
0022  *   Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston,*
0023  *   MA 02110-1335, USA                                                    *
0024  ***************************************************************************/
0025 
0026 #ifdef HAVE_CONFIG_H
0027 #include <config.h>
0028 #endif
0029 
0030 // application specific includes
0031 #include "smb4kmounthelper.h"
0032 #include "../core/smb4kglobal.h"
0033 
0034 // Qt includes
0035 #include <QProcessEnvironment>
0036 #include <QDebug>
0037 #include <QLatin1String>
0038 #include <QUrl>
0039 #include <QNetworkConfigurationManager>
0040 
0041 // KDE includes
0042 #include <KAuth/KAuthHelperSupport>
0043 #include <KCoreAddons/KProcess>
0044 #include <KI18n/KLocalizedString>
0045 #include <KIOCore/KMountPoint>
0046 
0047 using namespace Smb4KGlobal;
0048 
0049 KAUTH_HELPER_MAIN("org.kde.smb4k.mounthelper", Smb4KMountHelper);
0050 
0051 KAuth::ActionReply Smb4KMountHelper::mount(const QVariantMap& args)
0052 {
0053   //
0054   // The action reply
0055   //
0056   ActionReply reply;
0057   
0058   //
0059   // Check if the system is online and return an error if it is not
0060   // 
0061   QNetworkConfigurationManager networkManager;
0062   
0063   if (!networkManager.isOnline())
0064   {
0065     reply.setType(ActionReply::HelperErrorType);
0066     return reply;
0067   }
0068   
0069   //
0070   // Get the mount executable
0071   //
0072   const QString mount = findMountExecutable();
0073   
0074   //
0075   // Check the mount executable
0076   //
0077   if (mount != args["mh_command"].toString())
0078   {
0079     // Something weird is going on, bail out.
0080     reply.setType(ActionReply::HelperErrorType);
0081     return reply;
0082   }
0083   
0084   //
0085   // Mount command
0086   //
0087   QStringList command;
0088 #if defined(Q_OS_LINUX)
0089   command << mount;
0090   command << args["mh_url"].toUrl().toString(QUrl::RemoveScheme|QUrl::RemoveUserInfo|QUrl::RemovePort);
0091   command << args["mh_mountpoint"].toString();
0092   command << args["mh_options"].toStringList();
0093 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0094   command << mount;
0095   command << args["mh_options"].toStringList();
0096   command << args["mh_url"].toUrl().toString(QUrl::RemoveScheme|QUrl::RemoveUserInfo|QUrl::RemovePort);
0097   command << args["mh_mountpoint"].toString();
0098 #endif
0099   
0100   //
0101   // The process
0102   //
0103   KProcess proc(this);
0104   proc.setOutputChannelMode(KProcess::SeparateChannels);
0105   proc.setProcessEnvironment(QProcessEnvironment::systemEnvironment());
0106 #if defined(Q_OS_LINUX)
0107   proc.setEnv("PASSWD", args["mh_url"].toUrl().password().toUtf8().data(), true);
0108 #endif
0109   // We need this to avoid a translated password prompt.
0110   proc.setEnv("LANG", "C");
0111   // If the location of a Kerberos ticket is passed, it needs to
0112   // be passed to the process environment here.
0113   if (args.contains("mh_krb5ticket"))
0114   {
0115     proc.setEnv("KRB5CCNAME", args["mh_krb5ticket"].toString());
0116   }
0117 
0118   proc.setProgram(command);
0119   proc.start();
0120     
0121   if (proc.waitForStarted(-1))
0122   {
0123     bool userKill = false;
0124 
0125     while (!proc.waitForFinished(10))
0126     {
0127       // Check if there is a password prompt. If there is one, pass
0128       // the password to it.
0129       QByteArray out = proc.readAllStandardError();
0130 
0131       if (out.startsWith("Password"))
0132       {
0133         proc.write(args["mh_url"].toUrl().password().toUtf8().data());
0134         proc.write("\r");
0135       }
0136 
0137       // We want to be able to terminate the process from outside.
0138       // Thus, we implement a loop that checks periodically, if we
0139       // need to kill the process.
0140       if (HelperSupport::isStopped())
0141       {
0142         proc.kill();
0143         userKill = true;
0144       }
0145     }
0146 
0147     if (proc.exitStatus() == KProcess::CrashExit)
0148     {
0149       if (!userKill)
0150       {
0151         reply.setType(ActionReply::HelperErrorType);
0152         reply.setErrorDescription(i18n("The mount process crashed."));
0153       }
0154     }
0155     else
0156     {
0157       // Check if there is output on stderr.
0158       QString stdErr = QString::fromUtf8(proc.readAllStandardError());
0159       reply.addData("mh_error_message", stdErr.trimmed());
0160     }
0161   }
0162   else
0163   {
0164     reply.setType(ActionReply::HelperErrorType);
0165     reply.setErrorDescription(i18n("The mount process could not be started."));
0166   }  
0167   
0168   return reply;
0169 }
0170 
0171 
0172 KAuth::ActionReply Smb4KMountHelper::unmount(const QVariantMap& args)
0173 {
0174   ActionReply reply;
0175   
0176   //
0177   // Get the umount executable
0178   //
0179   const QString umount = findUmountExecutable();
0180   
0181   //
0182   // Check the mount executable
0183   //
0184   if (umount != args["mh_command"].toString())
0185   {
0186     // Something weird is going on, bail out.
0187     reply.setType(ActionReply::HelperErrorType);
0188     return reply;
0189   }
0190   
0191   //
0192   // Check if the mountpoint is valid and the filesystem is correct.
0193   //
0194   bool mountPointOk = false;
0195   KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded|KMountPoint::NeedMountOptions);
0196       
0197   for (const QExplicitlySharedDataPointer<KMountPoint> &mountPoint : mountPoints)
0198   {
0199 #if defined(Q_OS_LINUX)
0200     if (QString::compare(args["mh_mountpoint"].toString(), mountPoint->mountPoint()) == 0 &&
0201         QString::compare(mountPoint->mountType(), "cifs", Qt::CaseInsensitive) == 0)
0202 #else
0203     if (QString::compare(args["mh_mountpoint"].toString(), mountPoint->mountPoint()) == 0 &&
0204         QString::compare(mountPoint->mountType(), "smbfs", Qt::CaseInsensitive) == 0)
0205 #endif
0206     {
0207       mountPointOk = true;
0208       break;
0209     }
0210   }
0211   
0212   //
0213   // Stop here if the mountpoint is not valid
0214   //
0215   if (!mountPointOk)
0216   {
0217     reply.setType(ActionReply::HelperErrorType);
0218     reply.setErrorDescription(i18n("The mountpoint %1 is invalid.", args["mh_mountpoint"].toString()));
0219   }
0220   
0221   //
0222   // The command
0223   //
0224   QStringList command;
0225   command << umount;
0226   command << args["mh_options"].toStringList();
0227   command << args["mh_mountpoint"].toString();
0228   
0229   //
0230   // The process
0231   //
0232   KProcess proc(this);
0233   proc.setOutputChannelMode(KProcess::SeparateChannels);
0234   proc.setProcessEnvironment(QProcessEnvironment::systemEnvironment());
0235   proc.setProgram(command);
0236   
0237   //
0238   // Depending on the online state, use a different behavior for unmounting.
0239   // 
0240   // Extensive tests have shown that - when offline - unmounting does not 
0241   // work properly when the the process is not detached. Thus, detach it 
0242   // when the system is offline.
0243   // 
0244   QNetworkConfigurationManager networkManager;
0245   
0246   if (networkManager.isOnline())
0247   {
0248     proc.start();
0249     
0250     if (proc.waitForStarted(-1))
0251     {
0252       // We want to be able to terminate the process from outside.
0253       // Thus, we implement a loop that checks periodically, if we
0254       // need to kill the process.
0255       bool userKill = false;
0256 
0257       while (!proc.waitForFinished(10))
0258       {
0259         if (HelperSupport::isStopped())
0260         {
0261           proc.kill();
0262           userKill = true;
0263           break;
0264         }
0265       }
0266       
0267       if (proc.exitStatus() == KProcess::CrashExit)
0268       {
0269         if (!userKill)
0270         {
0271           reply.setType(ActionReply::HelperErrorType);
0272           reply.setErrorDescription(i18n("The unmount process crashed."));
0273         }
0274       }
0275       else
0276       {
0277         // Check if there is output on stderr.
0278         QString stdErr = QString::fromUtf8(proc.readAllStandardError());
0279         reply.addData("mh_error_message", stdErr.trimmed());
0280       }
0281     }
0282     else
0283     {
0284       reply.setType(ActionReply::HelperErrorType);
0285       reply.setErrorDescription(i18n("The unmount process could not be started."));
0286     }
0287   }
0288   else
0289   {
0290     proc.startDetached();
0291   }
0292   
0293   return reply;
0294 }
0295 
0296