File indexing completed on 2025-07-06 12:43:32

0001 /***************************************************************************
0002     The core class that mounts the shares.
0003                              -------------------
0004     begin                : Die Jun 10 2003
0005     copyright            : (C) 2003-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 // Application specific includes
0027 #include "smb4kmounter.h"
0028 #include "smb4kmounter_p.h"
0029 #include "smb4kauthinfo.h"
0030 #include "smb4kshare.h"
0031 #include "smb4ksettings.h"
0032 #include "smb4khomesshareshandler.h"
0033 #include "smb4kwalletmanager.h"
0034 #include "smb4knotification.h"
0035 #include "smb4kbookmarkhandler.h"
0036 #include "smb4kcustomoptionsmanager.h"
0037 #include "smb4kcustomoptions.h"
0038 #include "smb4kbookmark.h"
0039 #include "smb4kprofilemanager.h"
0040 #include "smb4khardwareinterface.h"
0041 
0042 #if defined(Q_OS_LINUX)
0043 #include "smb4kmountsettings_linux.h"
0044 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0045 #include "smb4kmountsettings_bsd.h"
0046 #endif
0047 
0048 // Qt includes
0049 #include <QDir>
0050 #include <QTextStream>
0051 #include <QTextCodec>
0052 #include <QTimer>
0053 #include <QFileInfo>
0054 #include <QDebug>
0055 #include <QApplication>
0056 #include <QTest>
0057 #include <QUdpSocket>
0058 
0059 // KDE includes
0060 #define TRANSLATION_DOMAIN "smb4k-core"
0061 #include <KCoreAddons/KShell>
0062 #include <KCoreAddons/KUser>
0063 #include <KIOCore/KIO/Global>
0064 #include <KIOCore/KIO/StatJob>
0065 #include <KIOCore/KMountPoint>
0066 #include <KIOCore/KDiskFreeSpaceInfo>
0067 #include <KI18n/KLocalizedString>
0068 #include <KWidgetsAddons/KMessageBox>
0069 #include <KAuth/KAuthExecuteJob>
0070 
0071 using namespace Smb4KGlobal;
0072 
0073 #define TIMEOUT 50
0074 
0075 Q_GLOBAL_STATIC(Smb4KMounterStatic, p);
0076 
0077 
0078 
0079 Smb4KMounter::Smb4KMounter(QObject *parent)
0080 : KCompositeJob(parent), d(new Smb4KMounterPrivate)
0081 {
0082   setAutoDelete(false);
0083 
0084   d->timerId = -1;
0085   d->remountTimeout = 0;
0086   d->remountAttempts = 0;
0087   d->checkTimeout = 0;
0088   d->newlyMounted = 0;
0089   d->newlyUnmounted = 0;
0090   d->dialog = 0;
0091   d->firstImportDone = false;
0092   d->longActionRunning = false;
0093   d->activeProfile = Smb4KProfileManager::self()->activeProfile();
0094   d->detectAllShares = Smb4KMountSettings::detectAllShares();
0095 
0096   // 
0097   // Connections
0098   // 
0099   connect(Smb4KHardwareInterface::self(), SIGNAL(onlineStateChanged(bool)), this, SLOT(slotOnlineStateChanged(bool)));
0100   connect(Smb4KHardwareInterface::self(), SIGNAL(networkShareAdded()), this, SLOT(slotTriggerImport()));
0101   connect(Smb4KHardwareInterface::self(), SIGNAL(networkShareRemoved()), this, SLOT(slotTriggerImport()));
0102   
0103   connect(Smb4KProfileManager::self(), SIGNAL(migratedProfile(QString,QString)), this, SLOT(slotProfileMigrated(QString,QString)));
0104   connect(Smb4KProfileManager::self(), SIGNAL(aboutToChangeProfile()), this, SLOT(slotAboutToChangeProfile()));
0105   connect(Smb4KProfileManager::self(), SIGNAL(activeProfileChanged(QString)), this, SLOT(slotActiveProfileChanged(QString)));
0106   
0107   connect(Smb4KMountSettings::self(), SIGNAL(configChanged()), this, SLOT(slotConfigChanged()));
0108   
0109   connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),this, SLOT(slotAboutToQuit()));
0110 }
0111 
0112 
0113 Smb4KMounter::~Smb4KMounter()
0114 {
0115   while (!d->importedShares.isEmpty())
0116   {
0117     d->importedShares.takeFirst().clear();
0118   }
0119 
0120   while (!d->retries.isEmpty())
0121   {
0122     d->retries.takeFirst().clear();
0123   }
0124 }
0125 
0126 
0127 Smb4KMounter *Smb4KMounter::self()
0128 {
0129   return &p->instance;
0130 }
0131 
0132 
0133 void Smb4KMounter::abort()
0134 {
0135   if (!QCoreApplication::closingDown())
0136   {
0137     QListIterator<KJob *> it(subjobs());
0138     
0139     while (it.hasNext())
0140     {
0141       it.next()->kill(KJob::EmitResult);
0142     }
0143   }
0144 }
0145 
0146 
0147 bool Smb4KMounter::isRunning()
0148 {
0149   return (hasSubjobs() || d->longActionRunning);
0150 }
0151 
0152 
0153 void Smb4KMounter::triggerRemounts(bool fillList)
0154 {
0155   if (fillList)
0156   {
0157     //
0158     // Get the list of shares that are to be remounted
0159     // 
0160     QList<OptionsPtr> options = Smb4KCustomOptionsManager::self()->sharesToRemount();
0161     
0162     //
0163     // Process the list and honor the settings the user chose
0164     //
0165     for (const OptionsPtr &option : options)
0166     {
0167       //
0168       // Skip one time remount shares, if needed
0169       // 
0170       if (option->remount() == Smb4KCustomOptions::RemountOnce && !Smb4KMountSettings::remountShares())
0171       {
0172         continue;
0173       }
0174       
0175       //
0176       // Check which share has to be remounted
0177       // 
0178       QList<SharePtr> mountedShares = findShareByUrl(option->url());
0179       bool remountShare = true;
0180       
0181       for (const SharePtr &share : mountedShares)
0182       {
0183         if (!share->isForeign())
0184         {
0185           remountShare = false;
0186           break;
0187         }          
0188       }
0189       
0190       //
0191       // Insert the share to the list of remounts
0192       // 
0193       if (remountShare)
0194       {
0195         bool insertShare = true;
0196         
0197         for (const SharePtr &share : d->remounts)
0198         {
0199           if (QString::compare(share->url().toString(QUrl::RemoveUserInfo|QUrl::RemovePort), option->url().toString(QUrl::RemoveUserInfo|QUrl::RemovePort)) == 0)
0200           {
0201             insertShare = false;
0202             break;
0203           }
0204         }
0205         
0206         if (insertShare)
0207         {
0208           SharePtr share = SharePtr(new Smb4KShare());
0209           share->setUrl(option->url());
0210           share->setWorkgroupName(option->workgroupName());
0211           share->setHostIpAddress(option->ipAddress());
0212           
0213           if (share->url().isValid() && !share->url().isEmpty())
0214           {
0215             d->remounts << share;
0216           }
0217         }
0218       }
0219     }
0220   }
0221   
0222   //
0223   // Remount the shares
0224   // 
0225   mountShares(d->remounts);
0226   
0227   //
0228   // Count the remount attempts
0229   // 
0230   d->remountAttempts++;
0231 }
0232 
0233 
0234 void Smb4KMounter::import(bool checkInaccessible)
0235 {
0236   //
0237   // Immediately return here if we are still processing imported shares
0238   //
0239   if (!d->importedShares.isEmpty())
0240   {
0241     return;
0242   }
0243   
0244   //
0245   // Get the mountpoints that are present on the system
0246   //
0247   KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded|KMountPoint::NeedMountOptions);
0248   
0249   // 
0250   // Now determine all mountpoints that have the SMBFS or the CIFS filesystem.
0251   // 
0252   for (const QExplicitlySharedDataPointer<KMountPoint> &mountPoint : mountPoints)
0253   {
0254     if (QString::compare(mountPoint->mountType(), "cifs") == 0 || QString::compare(mountPoint->mountType(), "smbfs") == 0)
0255     {
0256       // Create new share and set the mountpoint and the filesystem
0257       SharePtr share = SharePtr(new Smb4KShare());
0258       share->setUrl(mountPoint->mountedFrom());
0259       share->setPath(mountPoint->mountPoint());
0260       share->setMounted(true);
0261       
0262       // Get all mount options
0263       for (const QString &option : mountPoint->mountOptions())
0264       {
0265         if (option.startsWith(QLatin1String("domain=")) || option.startsWith(QLatin1String("workgroup=")))
0266         {
0267           share->setWorkgroupName(option.section('=', 1, 1).trimmed());
0268         }
0269         else if (option.startsWith(QLatin1String("addr=")))
0270         {
0271           share->setHostIpAddress(option.section('=', 1, 1).trimmed());
0272         }
0273         else if (option.startsWith(QLatin1String("username=")) || option.startsWith(QLatin1String("user=")))
0274         {
0275           share->setLogin(option.section('=', 1, 1).trimmed());
0276         }
0277       }
0278       
0279       // Work around empty usernames
0280       if (share->login().isEmpty())
0281       {
0282         share->setLogin("guest");
0283       }
0284       
0285       d->importedShares << share;
0286     }
0287   }
0288   
0289   //
0290   // Check which shares were unmounted. Remove all obsolete mountpoints, emit
0291   // the unmounted() signal on each of the unmounted shares and remove them
0292   // from the global list.
0293   // NOTE: The unmount() signal is emitted *BEFORE* the share is removed
0294   // from the global list! You need to account for that in your application.
0295   //
0296   QList<SharePtr> unmountedShares;
0297   
0298   if (!d->importedShares.isEmpty())
0299   {
0300     bool found = false;
0301     
0302     for (const SharePtr &mountedShare : mountedSharesList())
0303     {
0304       for (const SharePtr &importedShare : d->importedShares)
0305       {
0306         // Check the mountpoint, since that one is unique. We will only use
0307         // Smb4KShare::path(), so that we do not run into trouble if a share 
0308         // is inaccessible.
0309         if (QString::compare(mountedShare->path(), importedShare->path()) == 0)
0310         {
0311           found = true;
0312           break;
0313         }
0314       }
0315       
0316       if (!found)
0317       {
0318         unmountedShares << mountedShare;
0319       }
0320       
0321       found = false;
0322     }
0323   }
0324   else
0325   {
0326     unmountedShares << mountedSharesList();
0327   }
0328   
0329   //
0330   // Process the unmounted shares
0331   // 
0332   if (!unmountedShares.isEmpty())
0333   {
0334     d->newlyUnmounted += unmountedShares.size();
0335     
0336     for (const SharePtr &share : unmountedShares)
0337     {
0338       // 
0339       // Remove the mountpoint if the share is not a foreign one
0340       // 
0341       if (!share->isForeign())
0342       {
0343         QDir dir;
0344         dir.cd(share->canonicalPath());
0345         dir.rmdir(dir.canonicalPath());
0346           
0347         if (dir.cdUp())
0348         {
0349           dir.rmdir(dir.canonicalPath());
0350         }
0351       }
0352       
0353       //
0354       // Mark it as unmounted
0355       // 
0356       share->setMounted(false);
0357       
0358       // 
0359       // Copy the share
0360       // 
0361       SharePtr unmountedShare = share;
0362         
0363       // 
0364       // Remove the share from the global list and notify the program
0365       // 
0366       removeMountedShare(share);
0367       emit unmounted(unmountedShare);
0368       
0369       //
0370       // Report the unmounted share to the user if it is a single one
0371       // 
0372       if (!isRunning() && d->newlyUnmounted == 1)
0373       {
0374         Smb4KNotification::shareUnmounted(unmountedShare);
0375       }
0376       
0377       unmountedShare.clear();
0378     }
0379     
0380     //
0381     // Do last things
0382     // 
0383     QTimer::singleShot(250, [this]() {
0384       if (!isRunning())
0385       {
0386         //
0387         // Report the number of unmounted shares to the user if it were 
0388         // several ones
0389         // 
0390         if (d->newlyUnmounted > 1)
0391         {
0392           Smb4KNotification::sharesUnmounted(d->newlyUnmounted);
0393         }
0394       
0395         //
0396         // Reset the number of newly unmounted shares
0397         // 
0398         d->newlyUnmounted = 0;
0399       }
0400     });
0401 
0402     //
0403     // Tell the program the list of mounted shares changed
0404     // 
0405     emit mountedSharesListChanged();
0406   }
0407   else
0408   {
0409     //
0410     // Reset the number of newly unmounted shares
0411     // 
0412     d->newlyUnmounted = 0;
0413   }
0414   
0415   //
0416   // Now stat the imported shares to get information about them.
0417   // Do not use Smb4KShare::canonicalPath() here, otherwise we might
0418   // get lock-ups with inaccessible shares.
0419   //
0420   if (Smb4KHardwareInterface::self()->isOnline())
0421   {
0422     QMutableListIterator<SharePtr> it(d->importedShares);
0423     
0424     while (it.hasNext())
0425     {
0426       SharePtr share = it.next();
0427       SharePtr mountedShare = findShareByPath(share->path());
0428       
0429       if (mountedShare)
0430       {
0431         if (mountedShare->isInaccessible() && !checkInaccessible)
0432         {
0433           it.remove();
0434           continue;
0435         }
0436       }
0437       
0438       QUrl url = QUrl::fromLocalFile(share->path());
0439       KIO::StatJob *job = KIO::stat(url, KIO::HideProgressInfo);
0440       job->setDetails(0);
0441       connect(job, SIGNAL(result(KJob*)), this, SLOT(slotStatResult(KJob*)));
0442        
0443       // Do not use addSubJob(), because that would confuse isRunning(), etc.
0444       
0445       job->start();
0446     }
0447 
0448     //
0449     // Set d->firstImportDone here only for the case that no mounted shares
0450     // could be found. In all other cases d->firstImportDone will be set in 
0451     // slotStatResult().
0452     // 
0453     if (!d->firstImportDone && d->importedShares.isEmpty())
0454     {
0455       d->firstImportDone = true;
0456     }
0457   }
0458   else
0459   {
0460     //
0461     // When the system is offline, no mounted shares are processed, so
0462     // empty the list of imported shares here.
0463     // 
0464     while (!d->importedShares.isEmpty())
0465     {
0466       SharePtr share = d->importedShares.takeFirst();
0467       share.clear();
0468     }
0469   }
0470 }
0471 
0472 
0473 void Smb4KMounter::mountShare(const SharePtr &share)
0474 {
0475   if (share)
0476   {
0477     //
0478     // Check that the URL is valid
0479     //
0480     if (!share->url().isValid())
0481     {
0482       Smb4KNotification::invalidURLPassed();
0483       return;
0484     }
0485     
0486     //
0487     // Check if the share has already been mounted. If it is already present,
0488     // do not process it and return.
0489     //
0490     QUrl url;
0491     
0492     if (share->isHomesShare())
0493     {
0494       if (!Smb4KHomesSharesHandler::self()->specifyUser(share, true))
0495       {
0496         return;
0497       }
0498 
0499       url = share->homeUrl();     
0500     }
0501     else
0502     {
0503       url = share->url();
0504     }
0505     
0506     QList<SharePtr> mountedShares = findShareByUrl(url);
0507     bool isMounted = false;
0508       
0509     for (const SharePtr &s : mountedShares)
0510     {
0511       if (!s->isForeign())
0512       {
0513         isMounted = true;
0514         break;
0515       }
0516     }
0517     
0518     if (isMounted)
0519     {
0520       return;
0521     }
0522 
0523     //
0524     // Wake-On-LAN: Wake up the host before mounting 
0525     //
0526     if (Smb4KSettings::enableWakeOnLAN())
0527     {
0528       OptionsPtr options = Smb4KCustomOptionsManager::self()->findOptions(KIO::upUrl(share->url()));
0529 
0530       if (options && options->wolSendBeforeMount())
0531       {
0532         emit aboutToStart(WakeUp);
0533 
0534         QUdpSocket *socket = new QUdpSocket(this);
0535         QHostAddress addr;
0536 
0537         // Use the host's IP address directly from the share object.
0538         if (share->hasHostIpAddress())
0539         {
0540           addr.setAddress(share->hostIpAddress());
0541         }
0542         else
0543         {
0544           addr.setAddress("255.255.255.255");
0545         }
0546 
0547         // Construct magic sequence
0548         QByteArray sequence;
0549 
0550         // 6 times 0xFF
0551         for (int j = 0; j < 6; ++j)
0552         {
0553           sequence.append(QChar(0xFF).toLatin1());
0554         }
0555 
0556         // 16 times the MAC address
0557         QStringList parts = options->macAddress().split(':', QString::SkipEmptyParts);
0558 
0559         for (int j = 0; j < 16; ++j)
0560         {
0561           for (int k = 0; k < parts.size(); ++k)
0562           {
0563             sequence.append(QChar(QString("0x%1").arg(parts.at(k)).toInt(0, 16)).toLatin1());
0564           }
0565         }
0566             
0567         socket->writeDatagram(sequence, addr, 9);
0568         
0569         delete socket;
0570         
0571         // Wait the defined time
0572         int stop = 1000 * Smb4KSettings::wakeOnLANWaitingTime() / 250;
0573         int i = 0;
0574         
0575         while (i++ < stop)
0576         {
0577           QTest::qWait(250);
0578         }
0579         
0580         emit finished(WakeUp);
0581       }
0582     }
0583 
0584     // 
0585     // Create the mountpoint
0586     //
0587     QString mountpoint;
0588     mountpoint += Smb4KMountSettings::mountPrefix().path();
0589     mountpoint += QDir::separator();
0590     mountpoint += (Smb4KMountSettings::forceLowerCaseSubdirs() ? share->hostName().toLower() : share->hostName());
0591     mountpoint += QDir::separator();
0592 
0593     if (!share->isHomesShare())
0594     {
0595       mountpoint += (Smb4KMountSettings::forceLowerCaseSubdirs() ? share->shareName().toLower() : share->shareName());
0596     }
0597     else
0598     {
0599       mountpoint += (Smb4KMountSettings::forceLowerCaseSubdirs() ? share->login().toLower() : share->login());
0600     }
0601     
0602     // Get the permissions that should be used for creating the
0603     // mount prefix and all its subdirectories. 
0604     // Please note that the actual permissions of the mount points
0605     // are determined by the mount utility.
0606     QFile::Permissions permissions;
0607     QUrl parentDirectory;
0608       
0609     if (QFile::exists(Smb4KMountSettings::mountPrefix().path()))
0610     {
0611       parentDirectory = Smb4KMountSettings::mountPrefix();
0612     }
0613     else
0614     {
0615       QUrl u = Smb4KMountSettings::mountPrefix();
0616       parentDirectory = KIO::upUrl(u);
0617     }
0618       
0619     QFile f(parentDirectory.path());
0620     permissions = f.permissions();
0621       
0622     QDir dir(mountpoint);
0623 
0624     if (!dir.mkpath(dir.path()))
0625     {
0626       share->setPath("");
0627       Smb4KNotification::mkdirFailed(dir);
0628       return;
0629     }
0630     else
0631     {
0632       QUrl u = QUrl::fromLocalFile(dir.path());
0633       
0634       while (!parentDirectory.matches(u, QUrl::StripTrailingSlash))
0635       {
0636         QFile(u.path()).setPermissions(permissions);
0637         u = KIO::upUrl(u);
0638       }
0639     }
0640     
0641     share->setPath(QDir::cleanPath(mountpoint));
0642     
0643     // 
0644     // Get the authentication information
0645     //
0646     Smb4KWalletManager::self()->readAuthInfo(share);
0647 
0648     //
0649     // Mount arguments
0650     //
0651     QVariantMap args;
0652     
0653     if (!fillMountActionArgs(share, args))
0654     {
0655       return;
0656     }
0657     
0658     //
0659     // Emit the aboutToStart() signal
0660     //
0661     emit aboutToStart(MountShare);
0662 
0663     //
0664     // Create the mount action
0665     //
0666     KAuth::Action mountAction("org.kde.smb4k.mounthelper.mount");
0667     mountAction.setHelperId("org.kde.smb4k.mounthelper");
0668     mountAction.setArguments(args);
0669     
0670     KAuth::ExecuteJob *job = mountAction.execute();
0671       
0672     //
0673     // Modify the cursor, if necessary.
0674     //
0675     if (!hasSubjobs() && modifyCursor())
0676     {
0677       QApplication::setOverrideCursor(Qt::BusyCursor);
0678     }
0679       
0680     //
0681     // Add the job
0682     //
0683     addSubjob(job);
0684 
0685     //
0686     // Start the job and process the returned result.
0687     //
0688     bool success = job->exec();
0689     
0690     if (success)
0691     {
0692       int errorCode = job->error();
0693       
0694       if (errorCode == 0)
0695       {
0696         // Get the error message
0697         QString errorMsg = job->data()["mh_error_message"].toString();
0698         
0699         if (!errorMsg.isEmpty())
0700         {
0701 #if defined(Q_OS_LINUX)
0702           if (errorMsg.contains("mount error 13") || errorMsg.contains("mount error(13)") /* authentication error */)
0703           {
0704             if (Smb4KWalletManager::self()->showPasswordDialog(share))
0705             {
0706               d->retries << share;
0707             }
0708           }
0709           else if (errorMsg.contains("Unable to find suitable address."))
0710           {
0711             // Swallow this
0712           }
0713           else
0714           {
0715             Smb4KNotification::mountingFailed(share, errorMsg);
0716           }
0717 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0718           if (errorMsg.contains("Authentication error") || errorMsg.contains("Permission denied"))
0719           {
0720             if (Smb4KWalletManager::self()->showPasswordDialog(share))
0721             {
0722               d->retries << share;
0723             }
0724           }
0725           else
0726           {
0727             Smb4KNotification::mountingFailed(share, errorMsg);
0728           }
0729 #else
0730           qWarning() << "Smb4KMounter::slotMountJobFinished(): Error handling not implemented!";
0731           Smb4KNotification::mountingFailed(share.data(), errorMsg);
0732 #endif
0733         }
0734       }
0735       else
0736       {
0737         Smb4KNotification::actionFailed(errorCode);
0738       }
0739     }
0740     else
0741     {
0742       // FIXME: Report that the action could not be started
0743     }
0744     
0745     //
0746     // Remove the job from the job list
0747     //
0748     removeSubjob(job);
0749       
0750     //
0751     // Reset the busy cursor
0752     //
0753     if (!hasSubjobs() && modifyCursor())
0754     {
0755       QApplication::restoreOverrideCursor();
0756     }
0757 
0758     //
0759     // Emit the finished() signal
0760     //
0761     emit finished(MountShare);
0762   }
0763 }
0764 
0765 
0766 void Smb4KMounter::mountShares(const QList<SharePtr> &shares)
0767 {
0768   //
0769   // This action takes longer
0770   // 
0771   d->longActionRunning = true;
0772   
0773   //
0774   // Unmount the shares
0775   // 
0776   for (const SharePtr &share : shares)
0777   {
0778     //
0779     // Mount the share
0780     // 
0781     mountShare(share);
0782     
0783     // Wait for 50 ms so we can act on the networkShareAdded() 
0784     // signal and we do not trigger a busy error from mount
0785     QTest::qWait(TIMEOUT);
0786   }
0787   
0788   //
0789   // This action is over
0790   // 
0791   d->longActionRunning = false;
0792 }
0793 
0794 
0795 void Smb4KMounter::unmountShare(const SharePtr &share, bool silent)
0796 {
0797   Q_ASSERT(share);
0798   
0799   if (share)
0800   {
0801     //
0802     // Check that the URL is valid.
0803     //
0804     if (!share->url().isValid())
0805     {
0806       Smb4KNotification::invalidURLPassed();
0807       return;
0808     }
0809     
0810     //
0811     // Handle foreign shares according to the settings
0812     //
0813     if (share->isForeign())
0814     {
0815       if (!Smb4KMountSettings::unmountForeignShares())
0816       {
0817         if (!silent)
0818         {
0819           Smb4KNotification::unmountingNotAllowed(share);
0820         }
0821 
0822         return;
0823       }
0824       else
0825       {
0826         if (!silent)
0827         {
0828           if (KMessageBox::warningYesNo(QApplication::activeWindow(),
0829               i18n("<qt><p>The share <b>%1</b> is mounted to <br><b>%2</b> and owned by user <b>%3</b>.</p>"
0830                   "<p>Do you really want to unmount it?</p></qt>",
0831                   share->displayString(), share->path(), share->user().loginName()),
0832               i18n("Foreign Share")) == KMessageBox::No)
0833           {
0834             return;
0835           }
0836         }
0837         else
0838         {
0839           // Without the confirmation of the user, we are not
0840           // unmounting a foreign share!
0841           return;
0842         }
0843       }
0844     }
0845     
0846     //
0847     // Force the unmounting of the share either if the system went offline
0848     // or if the user chose to forcibly unmount inaccessible shares (Linux only).
0849     //
0850     bool force = false;
0851     
0852     if (Smb4KHardwareInterface::self()->isOnline())
0853     {
0854 #if defined(Q_OS_LINUX)
0855       if (share->isInaccessible())
0856       {
0857         force = Smb4KMountSettings::forceUnmountInaccessible();
0858       }
0859 #endif
0860     }
0861     else
0862     {
0863       force = true;
0864     }
0865     
0866     //
0867     // Unmount arguments
0868     //
0869     QVariantMap args;
0870 
0871     if (!fillUnmountActionArgs(share, force, silent, args))
0872     {
0873       return;
0874     }
0875     
0876     //
0877     // Emit the aboutToStart() signal
0878     //
0879     emit aboutToStart(UnmountShare);
0880 
0881     //
0882     // Create the unmount action
0883     //
0884     KAuth::Action unmountAction("org.kde.smb4k.mounthelper.unmount");
0885     unmountAction.setHelperId("org.kde.smb4k.mounthelper");
0886     unmountAction.setArguments(args);
0887     
0888     KAuth::ExecuteJob *job = unmountAction.execute();
0889     
0890     //
0891     // Modify the cursor, if necessary.
0892     //
0893     if (!hasSubjobs() && modifyCursor())
0894     {
0895       QApplication::setOverrideCursor(Qt::BusyCursor);
0896     }
0897       
0898     //
0899     // Add the job
0900     //
0901     addSubjob(job);
0902 
0903     //
0904     // Start the job and process the returned result.
0905     //
0906     bool success = job->exec();
0907     
0908     if (success)
0909     {
0910       int errorCode = job->error();
0911       
0912       if (errorCode == 0)
0913       {
0914         // Get the error message
0915         QString errorMsg = job->data()["mh_error_message"].toString();
0916         
0917         if (!errorMsg.isEmpty())
0918         {
0919           // No error handling needed, just report the error message.
0920           Smb4KNotification::unmountingFailed(share, errorMsg);
0921         }
0922       }
0923       else
0924       {
0925         Smb4KNotification::actionFailed(errorCode);
0926       }
0927     }
0928     else
0929     {
0930       // FIXME: Report that the action could not be started
0931     }
0932     
0933     //
0934     // Remove the job from the job list
0935     //
0936     removeSubjob(job);
0937       
0938     //
0939     // Reset the busy cursor
0940     //
0941     if (!hasSubjobs() && modifyCursor())
0942     {
0943       QApplication::restoreOverrideCursor();
0944     }
0945     
0946     //
0947     // Emit the finished() signal
0948     //
0949     emit finished(UnmountShare);
0950   }
0951 }
0952 
0953 
0954 void Smb4KMounter::unmountShares(const QList<SharePtr> &shares, bool silent)
0955 {
0956   //
0957   // This action takes longer
0958   // 
0959   d->longActionRunning = true;
0960   
0961   //
0962   // Inhibit shutdown and sleep
0963   // 
0964   Smb4KHardwareInterface::self()->inhibit();
0965 
0966   //
0967   // Unmount the list of shares
0968   // 
0969   for (const SharePtr &share : shares)
0970   {
0971     // Unmount the share
0972     unmountShare(share, silent);
0973     
0974     // Wait for 50 ms so we can act on the networkShareRemoved() 
0975     // signal and we do not trigger a busy error from umount
0976     QTest::qWait(TIMEOUT);
0977   }
0978 
0979   //
0980   // Uninhibit shutdown and sleep
0981   // 
0982   Smb4KHardwareInterface::self()->uninhibit();
0983   
0984   //
0985   // This action is over
0986   // 
0987   d->longActionRunning = false;
0988 }
0989 
0990 
0991 void Smb4KMounter::unmountAllShares(bool silent)
0992 {
0993   unmountShares(mountedSharesList(), silent);
0994 }
0995 
0996 
0997 void Smb4KMounter::openMountDialog()
0998 {
0999   if (!d->dialog)
1000   {
1001     SharePtr share = SharePtr(new Smb4KShare());
1002     
1003     d->dialog = new Smb4KMountDialog(share, QApplication::activeWindow());
1004 
1005     if (d->dialog->exec() == QDialog::Accepted && d->dialog->validUserInput())
1006     {
1007       // Pass the share to mountShare().
1008       mountShare(share);
1009       
1010       // Bookmark the share if the user wants this.
1011       if (d->dialog->bookmarkShare())
1012       {
1013         Smb4KBookmarkHandler::self()->addBookmark(share);
1014       }
1015     }
1016 
1017     delete d->dialog;
1018     d->dialog = 0;
1019 
1020     share.clear();
1021   }
1022 }
1023 
1024 
1025 void Smb4KMounter::start()
1026 {
1027   connect(Smb4KHardwareInterface::self(), SIGNAL(networkSessionInitialized()), this, SLOT(slotStartJobs()));
1028 }
1029 
1030 
1031 void Smb4KMounter::saveSharesForRemount()
1032 {
1033   //
1034   // Save the shares for remount
1035   //
1036   for (const SharePtr &share : mountedSharesList())
1037   {
1038     if (!share->isForeign())
1039     {
1040       Smb4KCustomOptionsManager::self()->addRemount(share, false);
1041     }
1042     else
1043     {
1044       Smb4KCustomOptionsManager::self()->removeRemount(share, false);
1045     }
1046   }
1047     
1048   //
1049   // Also save each failed remount and remove it from the list
1050   //
1051   while (!d->remounts.isEmpty())
1052   {
1053     SharePtr share = d->remounts.takeFirst();
1054     Smb4KCustomOptionsManager::self()->addRemount(share, false);
1055     share.clear();
1056   }
1057 }
1058 
1059 
1060 void Smb4KMounter::timerEvent(QTimerEvent *)
1061 {
1062   if (!isRunning() && Smb4KHardwareInterface::self()->isOnline())
1063   {
1064     //
1065     // Try to remount shares
1066     // 
1067     if (d->remountAttempts < Smb4KMountSettings::remountAttempts() && d->firstImportDone)
1068     {
1069       if (d->remountAttempts == 0)
1070       {
1071         triggerRemounts(true);
1072       }
1073       
1074       if ((60000 * Smb4KMountSettings::remountInterval()) < d->remountTimeout)
1075       {
1076         triggerRemounts(false);
1077         d->remountTimeout = -TIMEOUT;
1078       }
1079       
1080       d->remountTimeout += TIMEOUT;
1081     }
1082     
1083     //
1084     // Retry to mount those shares that initially failed
1085     // 
1086     while (!d->retries.isEmpty())
1087     {
1088       SharePtr share = d->retries.takeFirst();
1089       mountShare(share);
1090       share.clear();
1091     }
1092     
1093     //
1094     // Check the size, accessibility, etc. of the shares
1095     // 
1096     // FIXME: Hopefully we can replace this with a recursive QFileSystemWatcher 
1097     // approach in the future. However, using the existing QFileSystemWatcher
1098     // and a QDirIterator to add all the subdirectories of a share to the watcher
1099     // seems to be too resource consuming...
1100     //
1101     if (d->checkTimeout >= 2500 && d->importedShares.isEmpty())
1102     {
1103       for (const SharePtr &share : mountedSharesList())
1104       {
1105         check(share);
1106         emit updated(share);
1107       }
1108 
1109       d->checkTimeout = 0;
1110     }
1111     else
1112     {
1113       d->checkTimeout += TIMEOUT;
1114     }    
1115   }
1116 }
1117 
1118 
1119 #if defined(Q_OS_LINUX)
1120 //
1121 // Linux arguments
1122 //
1123 bool Smb4KMounter::fillMountActionArgs(const SharePtr &share, QVariantMap& map)
1124 {
1125   //
1126   // Find the mount executable
1127   // 
1128   const QString mount = findMountExecutable();
1129   
1130   if (!mount.isEmpty())
1131   {
1132     map.insert("mh_command", mount);
1133   }
1134   else
1135   {
1136     Smb4KNotification::commandNotFound("mount.cifs");
1137     return false;
1138   }
1139   
1140   //
1141   // Global and custom options
1142   // 
1143   QMap<QString, QString> globalOptions = globalSambaOptions();
1144   OptionsPtr options = Smb4KCustomOptionsManager::self()->findOptions(share);
1145   
1146   //
1147   // Pass the remote file system port to the URL
1148   // 
1149   if (options)
1150   {
1151     if (options->useFileSystemPort())
1152     {
1153       share->setPort(options->fileSystemPort());
1154     }
1155   }
1156   else
1157   {
1158     if (Smb4KMountSettings::useRemoteFileSystemPort())
1159     {
1160       share->setPort(Smb4KMountSettings::remoteFileSystemPort());
1161     }
1162   }
1163 
1164   //
1165   // List of arguments passed via "-o ..." to the mount command
1166   // 
1167   QStringList argumentsList;
1168 
1169   //
1170   // Workgroup or domain
1171   // 
1172   if (!share->workgroupName().trimmed().isEmpty())
1173   {
1174     argumentsList << QString("domain=%1").arg(KShell::quoteArg(share->workgroupName()));
1175   }
1176   
1177   //
1178   // Host IP address
1179   // 
1180   if (share->hasHostIpAddress())
1181   {
1182     argumentsList << QString("ip=%1").arg(share->hostIpAddress());
1183   }
1184   
1185   //
1186   // User name (login)
1187   // 
1188   if (!share->login().isEmpty())
1189   {
1190     argumentsList << QString("username=%1").arg(share->login());
1191   }
1192   else
1193   {
1194     argumentsList << "guest";
1195   }
1196   
1197   //
1198   // Client's and server's NetBIOS name
1199   // 
1200   // According to the manual page, this is only needed when port 139
1201   // is used. So, we only pass the NetBIOS name in that case.
1202   // 
1203   if (options)
1204   {
1205     if (options->useFileSystemPort() && options->fileSystemPort() == 139)
1206     {
1207       // The client's NetBIOS name
1208       if (!Smb4KSettings::netBIOSName().isEmpty())
1209       {
1210         argumentsList << QString("netbiosname=%1").arg(KShell::quoteArg(Smb4KSettings::netBIOSName()));
1211       }
1212       else if (!globalOptions["netbios name"].isEmpty())
1213       {
1214         argumentsList << QString("netbiosname=%1").arg(KShell::quoteArg(globalOptions["netbios name"]));
1215       }
1216       
1217       // The server's NetBIOS name
1218       argumentsList << QString("servernetbiosname=%1").arg(KShell::quoteArg(share->hostName()));
1219     }
1220   }
1221   else
1222   {
1223     if (Smb4KMountSettings::useRemoteFileSystemPort() && Smb4KMountSettings::remoteFileSystemPort() == 139)
1224     {
1225       // The client's NetBIOS name
1226       if (!Smb4KSettings::netBIOSName().isEmpty())
1227       {
1228         argumentsList << QString("netbiosname=%1").arg(KShell::quoteArg(Smb4KSettings::netBIOSName()));
1229       }
1230       else if (!globalOptions["netbios name"].isEmpty())
1231       {
1232         argumentsList << QString("netbiosname=%1").arg(KShell::quoteArg(globalOptions["netbios name"]));
1233       }
1234       
1235       // The server's NetBIOS name
1236       argumentsList << QString("servernetbiosname=%1").arg(KShell::quoteArg(share->hostName()));
1237     }
1238   }
1239   
1240   //
1241   // CIFS Unix extensions support
1242   // 
1243   // This sets the uid, gid, file_mode and dir_mode arguments, if necessary.
1244   // 
1245   if (options)
1246   {
1247     if (!options->cifsUnixExtensionsSupport())
1248     {
1249       // User id
1250       if (options->useUser())
1251       {
1252         argumentsList << QString("uid=%1").arg(options->user().userId().nativeId());
1253       }
1254       
1255       // Group id
1256       if (options->useGroup())
1257       {
1258         argumentsList << QString("gid=%1").arg(options->group().groupId().nativeId());
1259       }
1260       
1261       // File mode
1262       if (options->useFileMode())
1263       {
1264         argumentsList << QString("file_mode=%1").arg(options->fileMode());
1265       }
1266       
1267       // Directory mode
1268       if (options->useDirectoryMode())
1269       {
1270         argumentsList << QString("dir_mode=%1").arg(options->directoryMode());
1271       }
1272     }
1273   }
1274   else
1275   {
1276     if (!Smb4KMountSettings::cifsUnixExtensionsSupport())
1277     {
1278       // User id
1279       if (Smb4KMountSettings::useUserId())
1280       {
1281         argumentsList << QString("uid=%1").arg((K_UID)Smb4KMountSettings::userId().toInt());
1282       }
1283       
1284       // Group id
1285       if (Smb4KMountSettings::useGroupId())
1286       {
1287         argumentsList << QString("gid=%1").arg((K_GID)Smb4KMountSettings::groupId().toInt());
1288       }
1289       
1290       // File mode
1291       if (Smb4KMountSettings::useFileMode())
1292       {
1293         argumentsList << QString("file_mode=%1").arg(Smb4KMountSettings::fileMode());
1294       }
1295       
1296       // Directory mode
1297       if (Smb4KMountSettings::useDirectoryMode())
1298       {
1299         argumentsList << QString("dir_mode=%1").arg(Smb4KMountSettings::directoryMode());
1300       }
1301     }
1302   }
1303   
1304   //
1305   // Force user id
1306   // 
1307   // FIXME: The manual page is not clear about this: Is this option only useful when the uid=...
1308   // argument is given? If so, this should be moved into the 'User id' code block above.
1309   // 
1310   if (Smb4KMountSettings::forceUID())
1311   {
1312     argumentsList << "forceuid";
1313   }
1314   
1315   //
1316   // Force group id
1317   // 
1318   // FIXME: The manual page is not clear about this: Is this option only useful when the gid=...
1319   // argument is given? If so, this should be moved into the 'Group id' code block above.
1320   //   
1321   if (Smb4KMountSettings::forceGID())
1322   {
1323     argumentsList << "forcegid";
1324   }
1325   
1326   //
1327   // Client character set
1328   // 
1329   if (Smb4KMountSettings::useClientCharset())
1330   {
1331     switch (Smb4KMountSettings::clientCharset())
1332     {
1333       case Smb4KMountSettings::EnumClientCharset::default_charset:
1334       {
1335         if (!globalOptions["unix charset"].isEmpty())
1336         {
1337           argumentsList << QString("iocharset=%1").arg(globalOptions["unix charset"].toLower());
1338         }
1339 
1340         break;
1341       }
1342       default:
1343       {
1344         argumentsList << QString("iocharset=%1").arg(Smb4KMountSettings::self()->clientCharsetItem()->choices().value(Smb4KMountSettings::clientCharset()).label);
1345         break;
1346       }
1347     }
1348   }
1349   
1350   //
1351   // File system port
1352   // 
1353   if (options)
1354   {
1355     if (options->useFileSystemPort())
1356     {
1357       argumentsList << QString("port=%1").arg(options->fileSystemPort());
1358     }
1359   }
1360   else
1361   {
1362     if (Smb4KMountSettings::useRemoteFileSystemPort())
1363     {
1364       argumentsList << QString("port=%1").arg(Smb4KMountSettings::remoteFileSystemPort());
1365     }
1366   }
1367   
1368   //
1369   // Write access
1370   // 
1371   if (options)
1372   {
1373     if (options->useWriteAccess())
1374     {
1375       switch (options->writeAccess())
1376       {
1377         case Smb4KMountSettings::EnumWriteAccess::ReadWrite:
1378         {
1379           argumentsList << "rw";
1380           break;
1381         }
1382         case Smb4KMountSettings::EnumWriteAccess::ReadOnly:
1383         {
1384           argumentsList << "ro";
1385           break;
1386         }
1387         default:
1388         {
1389           break;
1390         }
1391       }
1392     }
1393   }
1394   else
1395   {
1396     if (Smb4KMountSettings::useWriteAccess())
1397     {
1398       switch (Smb4KMountSettings::writeAccess())
1399       {
1400         case Smb4KMountSettings::EnumWriteAccess::ReadWrite:
1401         {
1402           argumentsList << "rw";
1403           break;
1404         }
1405         case Smb4KMountSettings::EnumWriteAccess::ReadOnly:
1406         {
1407           argumentsList << "ro";
1408           break;
1409         }
1410         default:
1411         {
1412           break;
1413         }
1414       }
1415     }
1416   }
1417   
1418   //
1419   // Permission checks
1420   // 
1421   if (Smb4KMountSettings::permissionChecks())
1422   {
1423     argumentsList << "perm";
1424   }
1425   else
1426   {
1427     argumentsList << "noperm";
1428   }
1429   
1430   //
1431   // Client controls ids
1432   // 
1433   if (Smb4KMountSettings::clientControlsIDs())
1434   {
1435     argumentsList << "setuids";
1436   }
1437   else
1438   {
1439     argumentsList << "nosetuids";
1440   }
1441   
1442   //   
1443   // Server inode numbers
1444   // 
1445   if (Smb4KMountSettings::serverInodeNumbers())
1446   {
1447     argumentsList << "serverino";
1448   }
1449   else
1450   {
1451     argumentsList << "noserverino";
1452   }
1453   
1454   //
1455   // Cache mode
1456   //
1457   if (Smb4KMountSettings::useCacheMode())
1458   {
1459     switch (Smb4KMountSettings::cacheMode())
1460     {
1461       case Smb4KMountSettings::EnumCacheMode::None:
1462       {
1463         argumentsList << "cache=none";
1464         break;
1465       }
1466       case Smb4KMountSettings::EnumCacheMode::Strict:
1467       {
1468         argumentsList << "cache=strict";
1469         break;
1470       }
1471       case Smb4KMountSettings::EnumCacheMode::Loose:
1472       {
1473         argumentsList << "cache=loose";
1474         break;
1475       }
1476       default:
1477       {
1478         break;
1479       }
1480     }
1481   }
1482   
1483   //  
1484   // Translate reserved characters
1485   //
1486   if (Smb4KMountSettings::translateReservedChars())
1487   {
1488     argumentsList << "mapchars";
1489   }
1490   else
1491   {
1492     argumentsList << "nomapchars";
1493   }
1494 
1495   //   
1496   // Locking
1497   //
1498   if (Smb4KMountSettings::noLocking())
1499   {
1500     argumentsList << "nolock";
1501   }
1502   
1503   // 
1504   // Security mode
1505   // 
1506   if (options)
1507   {
1508     if (options->useSecurityMode())
1509     {
1510       switch (options->securityMode())
1511       {
1512         case Smb4KMountSettings::EnumSecurityMode::None:
1513         {
1514           argumentsList << "sec=none";
1515           break;
1516         }
1517         case Smb4KMountSettings::EnumSecurityMode::Krb5:
1518         {
1519           argumentsList << "sec=krb5";
1520           argumentsList << QString("cruid=%1").arg(KUser(KUser::UseRealUserID).userId().nativeId());
1521           break;
1522         }
1523         case Smb4KMountSettings::EnumSecurityMode::Krb5i:
1524         {
1525           argumentsList << "sec=krb5i";
1526           argumentsList << QString("cruid=%1").arg(KUser(KUser::UseRealUserID).userId().nativeId());
1527           break;
1528         }
1529         case Smb4KMountSettings::EnumSecurityMode::Ntlm:
1530         {
1531           argumentsList << "sec=ntlm";
1532           break;
1533         }
1534         case Smb4KMountSettings::EnumSecurityMode::Ntlmi:
1535         {
1536           argumentsList << "sec=ntlmi";
1537           break;
1538         }
1539         case Smb4KMountSettings::EnumSecurityMode::Ntlmv2:
1540         {
1541           argumentsList << "sec=ntlmv2";
1542           break;
1543         }
1544         case Smb4KMountSettings::EnumSecurityMode::Ntlmv2i:
1545         {
1546           argumentsList << "sec=ntlmv2i";
1547           break;
1548         }
1549         case Smb4KMountSettings::EnumSecurityMode::Ntlmssp:
1550         {
1551           argumentsList << "sec=ntlmssp";
1552           break;
1553         }
1554         case Smb4KMountSettings::EnumSecurityMode::Ntlmsspi:
1555         {
1556           argumentsList << "sec=ntlmsspi";
1557           break;
1558         }
1559         default:
1560         {
1561           // Smb4KSettings::EnumSecurityMode::Default,
1562           break;
1563         }
1564       }
1565     }
1566   }
1567   else
1568   {
1569     if (Smb4KMountSettings::useSecurityMode())
1570     {
1571       switch (Smb4KMountSettings::securityMode())
1572       {
1573         case Smb4KMountSettings::EnumSecurityMode::None:
1574         {
1575           argumentsList << "sec=none";
1576           break;
1577         }
1578         case Smb4KMountSettings::EnumSecurityMode::Krb5:
1579         {
1580           argumentsList << "sec=krb5";
1581           argumentsList << QString("cruid=%1").arg(KUser(KUser::UseRealUserID).userId().nativeId());
1582           break;
1583         }
1584         case Smb4KMountSettings::EnumSecurityMode::Krb5i:
1585         {
1586           argumentsList << "sec=krb5i";
1587           argumentsList << QString("cruid=%1").arg(KUser(KUser::UseRealUserID).userId().nativeId());
1588           break;
1589         }
1590         case Smb4KMountSettings::EnumSecurityMode::Ntlm:
1591         {
1592           argumentsList << "sec=ntlm";
1593           break;
1594         }
1595         case Smb4KMountSettings::EnumSecurityMode::Ntlmi:
1596         {
1597           argumentsList << "sec=ntlmi";
1598           break;
1599         }
1600         case Smb4KMountSettings::EnumSecurityMode::Ntlmv2:
1601         {
1602           argumentsList << "sec=ntlmv2";
1603           break;
1604         }
1605         case Smb4KMountSettings::EnumSecurityMode::Ntlmv2i:
1606         {
1607           argumentsList << "sec=ntlmv2i";
1608           break;
1609         }
1610         case Smb4KMountSettings::EnumSecurityMode::Ntlmssp:
1611         {
1612           argumentsList << "sec=ntlmssp";
1613           break;
1614         }
1615         case Smb4KMountSettings::EnumSecurityMode::Ntlmsspi:
1616         {
1617           argumentsList << "sec=ntlmsspi";
1618           break;
1619         }
1620         default:
1621         {
1622           // Smb4KSettings::EnumSecurityMode::Default,
1623           break;
1624         }
1625       }
1626     }
1627   }
1628   
1629   //
1630   // SMB protocol version
1631   // 
1632   if (Smb4KMountSettings::useSmbProtocolVersion())
1633   {
1634     switch (Smb4KMountSettings::smbProtocolVersion())
1635     {
1636       case Smb4KMountSettings::EnumSmbProtocolVersion::OnePointZero:
1637       {
1638         argumentsList << "vers=1.0";
1639         break;
1640       }
1641       case Smb4KMountSettings::EnumSmbProtocolVersion::TwoPointZero:
1642       {
1643         argumentsList << "vers=2.0";
1644         break;
1645       }
1646       case Smb4KMountSettings::EnumSmbProtocolVersion::TwoPointOne:
1647       {
1648         argumentsList << "vers=2.1";
1649         break;
1650       }
1651       case Smb4KMountSettings::EnumSmbProtocolVersion::ThreePointZero:
1652       {
1653         argumentsList << "vers=3.0";
1654         break;
1655       }
1656       case Smb4KMountSettings::EnumSmbProtocolVersion::ThreePointOnePointOne:
1657       {
1658         argumentsList << "vers=3.1.1";
1659         break;
1660       }
1661       default:
1662       {
1663         break;
1664       }
1665     }
1666   }
1667 
1668   //
1669   // Mount options provided by the user
1670   // 
1671   if (!Smb4KMountSettings::customCIFSOptions().isEmpty())
1672   {
1673     // SECURITY: Only pass those arguments to mount.cifs that do not pose
1674     // a potential security risk and that have not already been defined.
1675     //
1676     // This is, among others, the proper fix to the security issue reported
1677     // by Heiner Markert (aka CVE-2014-2581).
1678     QStringList whitelist = whitelistedMountArguments();
1679     QStringList list = Smb4KMountSettings::customCIFSOptions().split(',', QString::SkipEmptyParts);
1680     QMutableStringListIterator it(list);
1681     
1682     while (it.hasNext())
1683     {
1684       QString arg = it.next().section("=", 0, 0);
1685       
1686       if (!whitelist.contains(arg))
1687       {
1688         it.remove();
1689       }
1690       
1691       argumentsList += list;
1692     }
1693   }
1694   
1695   //
1696   // Insert the mount options into the map
1697   // 
1698   QStringList mh_options;
1699   mh_options << "-o";
1700   mh_options << argumentsList.join(",");
1701   map.insert("mh_options", mh_options);
1702   
1703   //
1704   // Insert the mountpoint into the map
1705   // 
1706   map.insert("mh_mountpoint", share->canonicalPath());
1707   
1708   //
1709   // Insert information about the share and its URL into the map
1710   // 
1711   if (!share->isHomesShare())
1712   {
1713     map.insert("mh_url", share->url());
1714   }
1715   else
1716   {
1717     map.insert("mh_url", share->homeUrl());
1718     map.insert("mh_homes_url", share->url());
1719   }  
1720 
1721   map.insert("mh_workgroup", share->workgroupName());
1722   map.insert("mh_ip", share->hostIpAddress());
1723   
1724   //
1725   // Location of the Kerberos ticket
1726   // 
1727   // The path to the Kerberos ticket is stored - if it exists - in the
1728   // KRB5CCNAME environment variable. By default, the ticket is located
1729   // at /tmp/krb5cc_[uid]. So, if the environment variable does not exist,
1730   // but the cache file is there, try to use it.
1731   // 
1732   if (QProcessEnvironment::systemEnvironment().contains("KRB5CCNAME"))
1733   {
1734     map.insert("mh_krb5ticket", QProcessEnvironment::systemEnvironment().value("KRB5CCNAME", ""));
1735   }
1736   else
1737   {
1738     QString ticket = QString("/tmp/krb5cc_%1").arg(KUser(KUser::UseRealUserID).userId().nativeId());
1739     
1740     if (QFile::exists(ticket))
1741     {
1742       map.insert("mh_krb5ticket", "FILE:"+ticket);
1743     }
1744   }
1745   
1746   return true;
1747 }
1748 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
1749 //
1750 // FreeBSD and NetBSD arguments
1751 //
1752 bool Smb4KMounter::fillMountActionArgs(const SharePtr &share, QVariantMap& map)
1753 {
1754   //
1755   // Find the mount executable
1756   // 
1757   const QString mount = findMountExecutable();
1758   
1759   if (!mount.isEmpty())
1760   {
1761     map.insert("mh_command", mount);
1762   }
1763   else
1764   {
1765     Smb4KNotification::commandNotFound("mount_smbfs");
1766     return false;
1767   }
1768   
1769   //
1770   // Global and custom options
1771   // 
1772   QMap<QString, QString> globalOptions = globalSambaOptions();
1773   OptionsPtr options = Smb4KCustomOptionsManager::self()->findOptions(share);
1774   
1775   //
1776   // List of arguments 
1777   // 
1778   QStringList argumentsList;
1779   
1780   //
1781   // Workgroup
1782   // 
1783   if (!share->workgroupName().isEmpty())
1784   {
1785     argumentsList << "-W";
1786     argumentsList << KShell::quoteArg(share->workgroupName());
1787   }
1788   
1789   //
1790   // IP address
1791   // 
1792   if (!share->hostIpAddress().isEmpty())
1793   {
1794     argumentsList << "-I";
1795     argumentsList << share->hostIpAddress();
1796   }
1797 
1798   //
1799   // User Id
1800   // 
1801   if (options)
1802   {
1803     if (options->useUser())
1804     {
1805       argumentsList << "-u";
1806       argumentsList << QString("%1").arg(options->user().userId().nativeId());
1807     }
1808   }
1809   else
1810   {
1811     if (Smb4KMountSettings::useUserId())
1812     {
1813       argumentsList << "-u";
1814       argumentsList << QString("%1").arg((K_UID)Smb4KMountSettings::userId().toInt());
1815     }
1816   }
1817   
1818   //
1819   // Group Id
1820   // 
1821   if (options)
1822   {
1823     if (options->useGroup())
1824     {
1825       argumentsList << "-g";
1826       argumentsList << QString("%1").arg(options->group().groupId().nativeId());
1827     }
1828   }
1829   else
1830   {
1831     if (Smb4KMountSettings::useGroupId())
1832     {
1833       argumentsList << "-g";
1834       argumentsList << QString("%1").arg((K_GID)Smb4KMountSettings::groupId().toInt());
1835     }
1836   }
1837   
1838   if (Smb4KMountSettings::useCharacterSets())
1839   {
1840     // Client character set
1841     QString clientCharset, serverCharset;
1842     
1843     switch (Smb4KMountSettings::clientCharset())
1844     {
1845       case Smb4KMountSettings::EnumClientCharset::default_charset:
1846       {
1847         clientCharset = globalOptions["unix charset"].toLower(); // maybe empty
1848         break;
1849       }
1850       default:
1851       {
1852         clientCharset = Smb4KMountSettings::self()->clientCharsetItem()->choices().value(Smb4KMountSettings::clientCharset()).label;
1853         break;
1854       }
1855     }
1856     
1857     // Server character set
1858     switch (Smb4KMountSettings::serverCodepage())
1859     {
1860       case Smb4KMountSettings::EnumServerCodepage::default_codepage:
1861       {
1862         serverCharset = globalOptions["dos charset"].toLower(); // maybe empty
1863         break;
1864       }
1865       default:
1866       {
1867         serverCharset = Smb4KMountSettings::self()->serverCodepageItem()->choices().value(Smb4KMountSettings::serverCodepage()).label;
1868         break;
1869       }
1870     }
1871     
1872     if (!clientCharset.isEmpty() && !serverCharset.isEmpty())
1873     {
1874       argumentsList << "-E";
1875       argumentsList << QString("%1:%2").arg(clientCharset, serverCharset);
1876     }
1877   }
1878 
1879   //
1880   // File mode
1881   // 
1882   if (options)
1883   {
1884     if (options->useFileMode())
1885     {
1886       argumentsList << "-f";
1887       argumentsList << options->fileMode();
1888     }
1889   }
1890   else
1891   {
1892     if (Smb4KMountSettings::useFileMode())
1893     {
1894       argumentsList << "-f";
1895       argumentsList << Smb4KMountSettings::fileMode();
1896     }
1897   }
1898 
1899   //
1900   // Directory mode
1901   // 
1902   if (options)
1903   {
1904     if (options->useDirectoryMode())
1905     {
1906       argumentsList << "-d";
1907       argumentsList << options->directoryMode();
1908     }
1909   }
1910   else
1911   {
1912     if (Smb4KMountSettings::useDirectoryMode())
1913     {
1914       argumentsList << "-d";
1915       argumentsList << Smb4KMountSettings::directoryMode();
1916     }
1917   }
1918 
1919   //
1920   // User name (login)
1921   // 
1922   if (!share->login().isEmpty())
1923   {
1924     argumentsList << "-U";
1925     argumentsList << share->login();
1926   }
1927   else
1928   {
1929     argumentsList << "-N";
1930   }
1931   
1932   //
1933   // Insert the mount options into the map
1934   // 
1935   map.insert("mh_options", argumentsList);
1936 
1937   //
1938   // Insert the mountpoint into the map
1939   // 
1940   map.insert("mh_mountpoint", share->canonicalPath());
1941   
1942   //
1943   // Insert information about the share and its URL into the map
1944   // 
1945   if (!share->isHomesShare())
1946   {
1947     map.insert("mh_url", share->url());
1948   }
1949   else
1950   {
1951     map.insert("mh_url", share->homeUrl());
1952     map.insert("mh_homes_url", share->url());
1953   }  
1954 
1955   map.insert("mh_workgroup", share->workgroupName());
1956   map.insert("mh_ip", share->hostIpAddress());
1957   
1958   return true;
1959 }
1960 #else
1961 //
1962 // Dummy 
1963 //
1964 bool Smb4KMounter::fillMountActionArgs(const SharePtr &, QVariantMap&)
1965 {
1966   qWarning() << "Smb4KMounter::fillMountActionArgs() is not implemented!";
1967   qWarning() << "Mounting under this operating system is not supported...";
1968   return false;
1969 }
1970 #endif
1971 
1972 
1973 #if defined(Q_OS_LINUX)
1974 //
1975 // Linux arguments
1976 //
1977 bool Smb4KMounter::fillUnmountActionArgs(const SharePtr &share, bool force, bool silent, QVariantMap &map)
1978 {
1979   //
1980   // The umount program
1981   //
1982   const QString umount = findUmountExecutable();
1983 
1984   if (umount.isEmpty() && !silent)
1985   {
1986     Smb4KNotification::commandNotFound("umount");
1987     return false;
1988   }
1989   
1990   //
1991   // The options
1992   //
1993   QStringList options;
1994 
1995   if (force)
1996   {
1997     options << "-l"; // lazy unmount
1998   }
1999 
2000   //
2001   // Insert data into the map
2002   //
2003   map.insert("mh_command", umount);
2004   map.insert("mh_url", share->url());
2005   
2006   if (!share->isInaccessible() && Smb4KHardwareInterface::self()->isOnline())
2007   {
2008     map.insert("mh_mountpoint", share->canonicalPath());
2009   }
2010   else
2011   {
2012     map.insert("mh_mountpoint", share->path());
2013   }
2014   
2015   map.insert("mh_options", options);
2016   
2017   return true;
2018 }
2019 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
2020 //
2021 // FreeBSD and NetBSD arguments
2022 //
2023 bool Smb4KMounter::fillUnmountActionArgs(const SharePtr &share, bool force, bool silent, QVariantMap &map)
2024 {
2025   //
2026   // The umount program
2027   //
2028   const QString umount = findUmountExecutable();
2029 
2030   if (umount.isEmpty() && !silent)
2031   {
2032     Smb4KNotification::commandNotFound("umount");
2033     return false;
2034   }
2035 
2036   //
2037   // The options
2038   //
2039   QStringList options;
2040   
2041   if (force)
2042   {
2043     options << "-f";
2044   }
2045 
2046   //
2047   // Insert data into the map
2048   //
2049   map.insert("mh_command", umount);
2050   map.insert("mh_url", share->url());
2051   
2052   if (!share->isInaccessible() && Smb4KHardwareInterface::self()->isOnline())
2053   {
2054     map.insert("mh_mountpoint", share->canonicalPath());
2055   }
2056   else
2057   {
2058     map.insert("mh_mountpoint", share->path());
2059   }
2060   
2061   map.insert("mh_options", options);
2062   
2063   return true;
2064 }
2065 #else
2066 //
2067 // Dummy
2068 //
2069 bool Smb4KMounter::fillUnmountActionArgs(const SharePtr &, bool, bool, QVariantMap &)
2070 {
2071   qWarning() << "Smb4KMounter::fillUnmountActionArgs() is not implemented!";
2072   qWarning() << "Unmounting under this operating system is not supported...";
2073   return false;
2074 }
2075 #endif
2076 
2077 
2078 void Smb4KMounter::check(const SharePtr &share)
2079 {
2080   // Get the info about the usage, etc.
2081   KDiskFreeSpaceInfo spaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(share->path());
2082     
2083   if (spaceInfo.isValid())
2084   {
2085     // Accessibility
2086     share->setInaccessible(false);
2087        
2088     // Size information
2089     share->setFreeDiskSpace(spaceInfo.available());
2090     share->setTotalDiskSpace(spaceInfo.size());
2091     share->setUsedDiskSpace(spaceInfo.used());
2092       
2093     // Get the owner an group, if possible.
2094     QFileInfo fileInfo(share->path());
2095     fileInfo.setCaching(false);
2096 
2097     if (fileInfo.exists())
2098     {
2099       share->setUser(KUser(static_cast<K_UID>(fileInfo.ownerId())));
2100       share->setGroup(KUserGroup(static_cast<K_GID>(fileInfo.groupId())));
2101       share->setInaccessible(!(fileInfo.isDir() && fileInfo.isExecutable()));
2102     }
2103     else
2104     {
2105       share->setInaccessible(true);
2106       share->setFreeDiskSpace(0);
2107       share->setTotalDiskSpace(0);
2108       share->setUsedDiskSpace(0);
2109       share->setUser(KUser(KUser::UseRealUserID));
2110       share->setGroup(KUserGroup(KUser::UseRealUserID));
2111     }
2112   }
2113   else
2114   {
2115     share->setInaccessible(true);
2116     share->setFreeDiskSpace(0);
2117     share->setTotalDiskSpace(0);
2118     share->setUsedDiskSpace(0);
2119     share->setUser(KUser(KUser::UseRealUserID));
2120     share->setGroup(KUserGroup(KUser::UseRealUserID));
2121   } 
2122 }
2123 
2124 
2125 
2126 /////////////////////////////////////////////////////////////////////////////
2127 // SLOT IMPLEMENTATIONS
2128 /////////////////////////////////////////////////////////////////////////////
2129 
2130 
2131 void Smb4KMounter::slotStartJobs()
2132 {
2133   //
2134   // Disconnect from Smb4KHardwareInterface.
2135   //
2136   disconnect(Smb4KHardwareInterface::self(), SIGNAL(networkSessionInitialized()), this, SLOT(slotStartJobs()));
2137   
2138   //
2139   // Start the import of shares
2140   // 
2141   if (Smb4KHardwareInterface::self()->isOnline())
2142   {
2143     //
2144     // Import the mounted shares
2145     //
2146     if (!d->firstImportDone)
2147     {
2148       import(true);
2149     }
2150   }
2151   
2152   //
2153   // Start the timer
2154   //
2155   if (d->timerId == -1)
2156   {
2157     d->timerId = startTimer(TIMEOUT);
2158   }
2159 }
2160 
2161 
2162 void Smb4KMounter::slotAboutToQuit()
2163 {
2164   //
2165   // Abort any actions
2166   //
2167   abort();
2168 
2169   //
2170   // Check if the user wants to remount shares and save the
2171   // shares for remount if so.
2172   //
2173   if (Smb4KMountSettings::remountShares())
2174   {
2175     saveSharesForRemount();
2176   }
2177 
2178   //
2179   // Unmount the shares if the user chose to do so.
2180   //
2181   if (Smb4KMountSettings::unmountSharesOnExit())
2182   {
2183     unmountAllShares(true);
2184   }
2185 
2186   //
2187   // Clean up the mount prefix.
2188   //
2189   KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded|KMountPoint::NeedMountOptions);
2190   
2191   QDir dir;
2192   dir.cd(Smb4KMountSettings::mountPrefix().path());
2193   QStringList hostDirs = dir.entryList(QDir::Dirs|QDir::NoDotAndDotDot, QDir::NoSort);
2194   QStringList mountpoints;
2195   
2196   for (const QString &hostDir : hostDirs)
2197   {
2198     dir.cd(hostDir);
2199     
2200     QStringList shareDirs = dir.entryList(QDir::Dirs|QDir::NoDotAndDotDot, QDir::NoSort);
2201     
2202     for (const QString &shareDir : shareDirs)
2203     {
2204       dir.cd(shareDir);
2205       mountpoints << dir.absolutePath();
2206       dir.cdUp();
2207     }
2208     
2209     dir.cdUp();
2210   }
2211   
2212   // Remove those mountpoints where a share is actually mounted.
2213   for (const QExplicitlySharedDataPointer<KMountPoint> &mountPoint : mountPoints)
2214   {
2215     mountpoints.removeOne(mountPoint->mountPoint());
2216   }
2217   
2218   // Remove the empty mountpoints.
2219   for (const QString &mp : mountpoints)
2220   {
2221     dir.cd(mp);
2222     dir.rmdir(dir.canonicalPath());
2223     
2224     if (dir.cdUp())
2225     {
2226       dir.rmdir(dir.canonicalPath());
2227     }
2228   }
2229 }
2230 
2231 
2232 void Smb4KMounter::slotOnlineStateChanged(bool online)
2233 {
2234   if (online)
2235   {
2236     //
2237     // Trigger the remounts, but only when the first import has been done 
2238     // already. Otherwise we would get errors. In this case the remounting 
2239     // is done by the code in timerEvent().
2240     // 
2241     if (d->firstImportDone)
2242     {
2243       triggerRemounts(true);
2244     }
2245   }
2246   else
2247   {
2248     //
2249     // Abort all running jobs if the computer goes offline
2250     // 
2251     abort();
2252     
2253     //
2254     // Save the list of shares for later remount
2255     // 
2256     saveSharesForRemount();
2257     
2258     //
2259     // Mark all mounted shares as inaccessible and send the updated() signal
2260     // 
2261     for (const SharePtr &share : mountedSharesList())
2262     {
2263       // Only mark the shares inaccessible and DO NOT emit
2264       // the updated() signal here, because that would freeze
2265       // the application.
2266       share->setInaccessible(true);
2267     }
2268     
2269     //
2270     // Now unmount all shares
2271     // 
2272     unmountAllShares(true);
2273   }
2274 }
2275 
2276 
2277 void Smb4KMounter::slotStatResult(KJob *job)
2278 {
2279   Q_ASSERT(job);
2280   
2281   //
2282   // Stat job
2283   //
2284   KIO::StatJob *statJob = static_cast<KIO::StatJob *>(job);
2285   
2286   //
2287   // Get the mountpoint
2288   //
2289   QString mountpoint = statJob->url().toDisplayString(QUrl::PreferLocalFile);
2290   
2291   //
2292   // Find the imported share
2293   //
2294   SharePtr importedShare;
2295   
2296   for (int i = 0; i < d->importedShares.size(); ++i)
2297   {
2298     if (QString::compare(d->importedShares.at(i)->path(), mountpoint) == 0)
2299     {
2300       importedShare = d->importedShares.takeAt(i);
2301       break;
2302     }
2303     else
2304     {
2305       continue;
2306     }
2307   }
2308   
2309   //
2310   // If the share should have vanished in the meantime, return here.
2311   //
2312   if (!importedShare)
2313   {
2314     return;
2315   }
2316   
2317   //
2318   // Add the size, user and group information
2319   //
2320   if (statJob->error() == 0 /* no error */)
2321   {
2322     check(importedShare);
2323   }
2324   else
2325   {
2326     importedShare->setInaccessible(true);
2327     importedShare->setFreeDiskSpace(0);
2328     importedShare->setTotalDiskSpace(0);
2329     importedShare->setUsedDiskSpace(0);
2330     importedShare->setUser(KUser(KUser::UseRealUserID));
2331     importedShare->setGroup(KUserGroup(KUser::UseRealUserID));    
2332   }
2333   
2334   //
2335   // Decide whether this is a share mounted by the user or by someone else.
2336   // 
2337   QString canonicalMountPrefix = QDir(Smb4KMountSettings::mountPrefix().path()).canonicalPath();
2338   QString canonicalHomePath = QDir::home().canonicalPath();
2339   
2340   if (importedShare->path().startsWith(Smb4KMountSettings::mountPrefix().path()) || importedShare->canonicalPath().startsWith(canonicalMountPrefix))
2341   {
2342     //
2343     // The path is below the mount prefix
2344     // 
2345     importedShare->setForeign(false);
2346   }
2347   else if (importedShare->path().startsWith(QDir::homePath()) || importedShare->canonicalPath().startsWith(canonicalHomePath))
2348   {
2349     //
2350     // The path is below the home directory
2351     // 
2352     importedShare->setForeign(false);
2353   }
2354   else if (importedShare->user().userId() == KUser(KUser::UseRealUserID).userId() && importedShare->group().groupId() == KUserGroup(KUser::UseRealUserID).groupId())
2355   {
2356     //
2357     // The IDs are the same
2358     // 
2359     importedShare->setForeign(false);
2360   }
2361   else
2362   {
2363     //
2364     // The path is elsewhere. This is most certainly a foreign share.
2365     //
2366     importedShare->setForeign(true);
2367   }
2368   
2369   //
2370   // Search for a previously added mounted share and try to update it. If this fails,
2371   // add the share to the global list.
2372   //
2373   if (!importedShare->isForeign() || Smb4KMountSettings::detectAllShares())
2374   {
2375     if (updateMountedShare(importedShare))
2376     {
2377       SharePtr updatedShare = findShareByPath(importedShare->path());
2378       
2379       if (updatedShare)
2380       {
2381         emit updated(updatedShare);
2382       }
2383       
2384       importedShare.clear();
2385     }
2386     else
2387     {
2388       if (addMountedShare(importedShare))
2389       {
2390         // Remove the share from the list of shares that are to be remounted
2391         QMutableListIterator<SharePtr> s(d->remounts);
2392 
2393         while (s.hasNext())
2394         {
2395           SharePtr remount = s.next();
2396 
2397           if (!importedShare->isForeign() && 
2398               QString::compare(remount->url().toString(QUrl::RemoveUserInfo|QUrl::RemovePort),
2399                                importedShare->url().toString(QUrl::RemoveUserInfo|QUrl::RemovePort),
2400                                Qt::CaseInsensitive) == 0)
2401           {
2402             Smb4KCustomOptionsManager::self()->removeRemount(remount);
2403             s.remove();
2404             break;
2405           }
2406           else
2407           {
2408             continue;
2409           }
2410         }
2411         
2412         // Tell the program and the user that the share was mounted. Also, reset the
2413         // counter of newly mounted shares, if necessary.
2414         d->newlyMounted += 1;
2415         emit mounted(importedShare);
2416         
2417         if (!isRunning() && d->firstImportDone && d->importedShares.isEmpty() && d->newlyMounted == 1)
2418         {
2419           Smb4KNotification::shareMounted(importedShare);
2420         }
2421           
2422         QTimer::singleShot(250, [this]() {
2423           if (!isRunning())
2424           {
2425             if (d->firstImportDone && d->importedShares.isEmpty() && d->newlyMounted > 1)
2426             {
2427               Smb4KNotification::sharesMounted(d->newlyMounted);
2428             }
2429               
2430             d->newlyMounted = 0;
2431           }
2432         });
2433           
2434         emit mountedSharesListChanged();
2435       }
2436       else
2437       {
2438         importedShare.clear();
2439       }
2440     }
2441   }
2442   else
2443   {
2444     importedShare.clear();
2445   }
2446   
2447   if (!d->firstImportDone && d->importedShares.isEmpty())
2448   {
2449     d->firstImportDone = true;
2450   }
2451 }
2452 
2453 
2454 void Smb4KMounter::slotAboutToChangeProfile()
2455 {
2456   //
2457   // Save those shares that are to be remounted
2458   //
2459   if (Smb4KMountSettings::remountShares())
2460   {
2461     saveSharesForRemount();
2462   }
2463 }
2464 
2465 
2466 void Smb4KMounter::slotActiveProfileChanged(const QString &newProfile)
2467 {
2468   if (d->activeProfile != newProfile)
2469   {
2470     // Stop the timer.
2471     killTimer(d->timerId);
2472 
2473     abort();
2474     
2475     // Clear all remounts.
2476     while (!d->remounts.isEmpty())
2477     {
2478       d->remounts.takeFirst().clear();
2479     }
2480     
2481     // Clear all retries.
2482     while (!d->retries.isEmpty())
2483     {
2484       d->retries.takeFirst().clear();
2485     }
2486     
2487     // Unmount all shares
2488     unmountAllShares(true);
2489     
2490     // Reset some variables.
2491     d->remountTimeout = 0;
2492     d->remountAttempts = 0;
2493     d->firstImportDone = false;
2494     d->activeProfile = newProfile;
2495     
2496     // Restart the timer
2497     d->timerId = startTimer(TIMEOUT);
2498   }
2499 }
2500 
2501 
2502 void Smb4KMounter::slotProfileMigrated(const QString& from, const QString& to)
2503 {
2504   if (QString::compare(from, d->activeProfile, Qt::CaseSensitive) == 0)
2505   {
2506     d->activeProfile = to;
2507   }
2508 }
2509 
2510 
2511 void Smb4KMounter::slotTriggerImport()
2512 {
2513   import(true);
2514 }
2515 
2516 
2517 void Smb4KMounter::slotConfigChanged()
2518 {
2519   if (d->detectAllShares != Smb4KMountSettings::detectAllShares())
2520   {
2521     slotTriggerImport();
2522     d->detectAllShares = Smb4KMountSettings::detectAllShares();
2523   }
2524 }
2525