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