File indexing completed on 2024-04-14 04:56:25
0001 /* 0002 The core class that mounts the shares. 0003 0004 SPDX-FileCopyrightText: 2003-2023 Alexander Reinholdt <alexander.reinholdt@kdemail.net> 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 // Application specific includes 0009 #include "smb4kmounter.h" 0010 #include "smb4kauthinfo.h" 0011 #include "smb4kbookmark.h" 0012 #include "smb4kbookmarkhandler.h" 0013 #include "smb4kcustomsettings.h" 0014 #include "smb4kcustomsettingsmanager.h" 0015 #include "smb4khardwareinterface.h" 0016 #include "smb4khomesshareshandler.h" 0017 #include "smb4knotification.h" 0018 #include "smb4kprofilemanager.h" 0019 #include "smb4ksettings.h" 0020 #include "smb4kshare.h" 0021 #include "smb4kwalletmanager.h" 0022 #include "smb4kworkgroup.h" 0023 0024 #if defined(Q_OS_LINUX) 0025 #include "smb4kmountsettings_linux.h" 0026 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0027 #include "smb4kmountsettings_bsd.h" 0028 #endif 0029 0030 // Qt includes 0031 #include <QApplication> 0032 #include <QDebug> 0033 #include <QDir> 0034 #include <QFileInfo> 0035 #include <QHostInfo> 0036 #include <QPointer> 0037 #include <QStorageInfo> 0038 #include <QTimer> 0039 #include <QUdpSocket> 0040 0041 // KDE includes 0042 #include <KAuth/ExecuteJob> 0043 #include <KLocalizedString> 0044 #include <KMessageBox> 0045 #include <KMountPoint> 0046 #include <KShell> 0047 #include <KUser> 0048 #include <kauth_version.h> 0049 0050 using namespace Smb4KGlobal; 0051 0052 #define TIMEOUT 50 0053 0054 class Smb4KMounterPrivate 0055 { 0056 public: 0057 int remountTimeout; 0058 int remountAttempts; 0059 int timerId; 0060 int checkTimeout; 0061 int newlyMounted; 0062 int newlyUnmounted; 0063 QList<SharePtr> importedShares; 0064 QList<SharePtr> retries; 0065 QList<SharePtr> remounts; 0066 bool detectAllShares; 0067 bool firstImportDone; 0068 bool longActionRunning; 0069 QStorageInfo storageInfo; 0070 }; 0071 0072 class Smb4KMounterStatic 0073 { 0074 public: 0075 Smb4KMounter instance; 0076 }; 0077 0078 Q_GLOBAL_STATIC(Smb4KMounterStatic, p); 0079 0080 Smb4KMounter::Smb4KMounter(QObject *parent) 0081 : KCompositeJob(parent) 0082 , d(new Smb4KMounterPrivate) 0083 { 0084 setAutoDelete(false); 0085 0086 d->timerId = -1; 0087 d->remountTimeout = 0; 0088 d->remountAttempts = 0; 0089 d->checkTimeout = 0; 0090 d->newlyMounted = 0; 0091 d->newlyUnmounted = 0; 0092 d->firstImportDone = false; 0093 d->longActionRunning = false; 0094 d->detectAllShares = Smb4KMountSettings::detectAllShares(); 0095 0096 // 0097 // Connections 0098 // 0099 connect(Smb4KProfileManager::self(), &Smb4KProfileManager::aboutToChangeProfile, this, &Smb4KMounter::slotAboutToChangeProfile); 0100 connect(Smb4KProfileManager::self(), &Smb4KProfileManager::activeProfileChanged, this, &Smb4KMounter::slotActiveProfileChanged); 0101 connect(Smb4KWalletManager::self(), &Smb4KWalletManager::credentialsUpdated, this, &Smb4KMounter::slotCredentialsUpdated); 0102 connect(Smb4KMountSettings::self(), &Smb4KMountSettings::configChanged, this, &Smb4KMounter::slotConfigChanged); 0103 0104 connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &Smb4KMounter::slotAboutToQuit); 0105 } 0106 0107 Smb4KMounter::~Smb4KMounter() 0108 { 0109 while (!d->importedShares.isEmpty()) { 0110 d->importedShares.takeFirst().clear(); 0111 } 0112 0113 while (!d->retries.isEmpty()) { 0114 d->retries.takeFirst().clear(); 0115 } 0116 } 0117 0118 Smb4KMounter *Smb4KMounter::self() 0119 { 0120 return &p->instance; 0121 } 0122 0123 void Smb4KMounter::abort() 0124 { 0125 if (!QCoreApplication::closingDown()) { 0126 QListIterator<KJob *> it(subjobs()); 0127 0128 while (it.hasNext()) { 0129 it.next()->kill(KJob::EmitResult); 0130 } 0131 } 0132 } 0133 0134 bool Smb4KMounter::isRunning() 0135 { 0136 return (hasSubjobs() || d->longActionRunning); 0137 } 0138 0139 void Smb4KMounter::triggerRemounts(bool fillList) 0140 { 0141 if (fillList) { 0142 // 0143 // Get the list of shares that are to be remounted 0144 // 0145 QList<CustomSettingsPtr> options = Smb4KCustomSettingsManager::self()->sharesToRemount(); 0146 0147 // 0148 // Process the list and honor the settings the user chose 0149 // 0150 for (const CustomSettingsPtr &option : qAsConst(options)) { 0151 // 0152 // Skip one time remount shares, if needed 0153 // 0154 if (option->remount() == Smb4KCustomSettings::RemountOnce && !Smb4KMountSettings::remountShares()) { 0155 continue; 0156 } 0157 0158 // 0159 // Check which share has to be remounted 0160 // 0161 QList<SharePtr> mountedShares = findShareByUrl(option->url()); 0162 bool remountShare = true; 0163 0164 for (const SharePtr &share : qAsConst(mountedShares)) { 0165 if (!share->isForeign()) { 0166 remountShare = false; 0167 break; 0168 } 0169 } 0170 0171 // 0172 // Insert the share to the list of remounts 0173 // 0174 if (remountShare) { 0175 bool insertShare = true; 0176 0177 for (const SharePtr &share : qAsConst(d->remounts)) { 0178 if (QString::compare(share->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 0179 option->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort)) 0180 == 0) { 0181 insertShare = false; 0182 break; 0183 } 0184 } 0185 0186 if (insertShare) { 0187 SharePtr share = SharePtr(new Smb4KShare()); 0188 share->setUrl(option->url()); 0189 share->setWorkgroupName(option->workgroupName()); 0190 share->setHostIpAddress(option->ipAddress()); 0191 0192 if (share->url().isValid() && !share->url().isEmpty()) { 0193 d->remounts << share; 0194 } 0195 } 0196 } 0197 } 0198 } 0199 0200 // 0201 // Remount the shares 0202 // 0203 mountShares(d->remounts); 0204 0205 // 0206 // Count the remount attempts 0207 // 0208 d->remountAttempts++; 0209 } 0210 0211 void Smb4KMounter::import(bool checkInaccessible) 0212 { 0213 // 0214 // Immediately return here if we are still processing imported shares 0215 // 0216 if (!d->importedShares.isEmpty()) { 0217 return; 0218 } 0219 0220 // 0221 // Boolean to determine at the end if the mounted shares list changed 0222 // 0223 bool changed = false; 0224 0225 // 0226 // Get the mountpoints that are present on the system 0227 // 0228 KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded | KMountPoint::NeedMountOptions); 0229 0230 // 0231 // Now determine all mountpoints that have the appropriate filesystem. 0232 // 0233 for (const QExplicitlySharedDataPointer<KMountPoint> &mountPoint : qAsConst(mountPoints)) { 0234 if (mountPoint->mountType() == QStringLiteral("cifs") || mountPoint->mountType() == QStringLiteral("smb3") 0235 || mountPoint->mountType() == QStringLiteral("smbfs")) { 0236 // Create a new share and set the mountpoint and filesystem 0237 SharePtr share = SharePtr(new Smb4KShare()); 0238 share->setUrl(QUrl(mountPoint->mountedFrom())); 0239 share->setPath(mountPoint->mountPoint()); 0240 share->setMounted(true); 0241 0242 // Get all mount options 0243 QStringList mountOptions = mountPoint->mountOptions(); 0244 0245 for (const QString &option : qAsConst(mountOptions)) { 0246 if (option.startsWith(QStringLiteral("domain=")) || option.startsWith(QStringLiteral("workgroup="))) { 0247 share->setWorkgroupName(option.section(QStringLiteral("="), 1, 1).trimmed()); 0248 } else if (option.startsWith(QStringLiteral("addr="))) { 0249 share->setHostIpAddress(option.section(QStringLiteral("="), 1, 1).trimmed()); 0250 } else if (option.startsWith(QStringLiteral("username=")) || option.startsWith(QStringLiteral("user="))) { 0251 share->setUserName(option.section(QStringLiteral("="), 1, 1).trimmed()); 0252 } 0253 } 0254 0255 // Work around empty usernames 0256 if (share->userName().isEmpty()) { 0257 share->setUserName(QStringLiteral("guest")); 0258 } 0259 0260 d->importedShares << share; 0261 } 0262 } 0263 0264 // 0265 // Check which shares were unmounted. Remove all obsolete mountpoints, emit 0266 // the unmounted() signal on each of the unmounted shares and remove them 0267 // from the global list. 0268 // 0269 QList<SharePtr> unmountedShares; 0270 0271 if (!d->importedShares.isEmpty()) { 0272 bool found = false; 0273 0274 for (const SharePtr &mountedShare : qAsConst(mountedSharesList())) { 0275 for (const SharePtr &importedShare : qAsConst(d->importedShares)) { 0276 // Check the mountpoint, since that one is unique. We will only use 0277 // Smb4KShare::path(), so that we do not run into trouble if a share 0278 // is inaccessible. 0279 if (QString::compare(mountedShare->path(), importedShare->path()) == 0) { 0280 found = true; 0281 break; 0282 } 0283 } 0284 0285 if (!found) { 0286 unmountedShares << mountedShare; 0287 } 0288 0289 found = false; 0290 } 0291 } else { 0292 unmountedShares << mountedSharesList(); 0293 } 0294 0295 // 0296 // Process the unmounted shares 0297 // 0298 if (!unmountedShares.isEmpty()) { 0299 d->newlyUnmounted += unmountedShares.size(); 0300 0301 for (const SharePtr &share : qAsConst(unmountedShares)) { 0302 // 0303 // Remove the mountpoint if the share is not a foreign one 0304 // 0305 if (!share->isForeign()) { 0306 QDir dir; 0307 dir.cd(share->canonicalPath()); 0308 dir.rmdir(dir.canonicalPath()); 0309 0310 if (dir.cdUp()) { 0311 dir.rmdir(dir.canonicalPath()); 0312 } 0313 } 0314 0315 // 0316 // Mark it as unmounted 0317 // 0318 share->setMounted(false); 0319 0320 // 0321 // Copy the share 0322 // 0323 SharePtr unmountedShare = share; 0324 0325 // 0326 // Remove the share from the global list and notify the program 0327 // 0328 if (removeMountedShare(share)) { 0329 changed = true; 0330 Q_EMIT unmounted(unmountedShare); 0331 } 0332 0333 // 0334 // Report the unmounted share to the user if it is a single one 0335 // 0336 if (!isRunning() && d->newlyUnmounted == 1) { 0337 Smb4KNotification::shareUnmounted(unmountedShare); 0338 } 0339 0340 unmountedShare.clear(); 0341 } 0342 0343 // 0344 // Report the number of unmounted shares to the user if it are 0345 // several ones 0346 // 0347 if (d->newlyUnmounted > 1) { 0348 Smb4KNotification::sharesUnmounted(d->newlyUnmounted); 0349 } 0350 0351 // 0352 // Reset the number of newly unmounted shares 0353 // 0354 d->newlyUnmounted = 0; 0355 } else { 0356 // 0357 // Reset the number of newly unmounted shares 0358 // 0359 d->newlyUnmounted = 0; 0360 } 0361 0362 // 0363 // Now process the imported shares 0364 // 0365 if (Smb4KHardwareInterface::self()->isOnline()) { 0366 while (!d->importedShares.isEmpty()) { 0367 SharePtr importedShare = d->importedShares.takeFirst(); 0368 SharePtr mountedShare = findShareByPath(importedShare->path()); 0369 0370 if (mountedShare) { 0371 if (mountedShare->isInaccessible() && !checkInaccessible) { 0372 importedShare.clear(); 0373 continue; 0374 } 0375 } 0376 0377 // 0378 // Check the imported share and retrieve some information about it 0379 // 0380 check(importedShare); 0381 0382 // 0383 // Find out whether this is a share mounted by the user 0384 // 0385 if (importedShare->path().startsWith(Smb4KMountSettings::mountPrefix().path()) 0386 || (!importedShare->isInaccessible() 0387 && importedShare->canonicalPath().startsWith(QDir(Smb4KMountSettings::mountPrefix().path()).canonicalPath()))) { 0388 importedShare->setForeign(false); 0389 } else if (importedShare->path().startsWith(QDir::homePath()) 0390 || (!importedShare->isInaccessible() && importedShare->canonicalPath().startsWith(QDir::home().canonicalPath()))) { 0391 importedShare->setForeign(false); 0392 } else if (importedShare->user().userId() == KUser(KUser::UseRealUserID).userId() 0393 && importedShare->group().groupId() == KUserGroup(KUser::UseRealUserID).groupId()) { 0394 importedShare->setForeign(false); 0395 } else { 0396 importedShare->setForeign(true); 0397 } 0398 0399 // 0400 // Search for a previously added mounted share and try to update it. If this fails, 0401 // add the share to the global list. 0402 // 0403 if (!importedShare->isForeign() || Smb4KMountSettings::detectAllShares()) { 0404 if (updateMountedShare(importedShare)) { 0405 SharePtr updatedShare = findShareByPath(importedShare->path()); 0406 0407 if (updatedShare) { 0408 Q_EMIT updated(updatedShare); 0409 } 0410 0411 importedShare.clear(); 0412 } else { 0413 if (addMountedShare(importedShare)) { 0414 changed = true; 0415 0416 // Remove the share from the list of shares that are to be remounted 0417 QMutableListIterator<SharePtr> s(d->remounts); 0418 0419 while (s.hasNext()) { 0420 SharePtr remount = s.next(); 0421 0422 if (!importedShare->isForeign() 0423 && QString::compare(remount->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 0424 importedShare->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 0425 Qt::CaseInsensitive) 0426 == 0) { 0427 Smb4KCustomSettingsManager::self()->removeRemount(remount); 0428 s.remove(); 0429 break; 0430 } else { 0431 continue; 0432 } 0433 } 0434 0435 Q_EMIT mounted(importedShare); 0436 0437 // Do not notify the user on first import 0438 if (d->firstImportDone) { 0439 d->newlyMounted += 1; 0440 0441 if (!isRunning() && d->importedShares.isEmpty()) { 0442 if (d->newlyMounted > 1) { 0443 Smb4KNotification::sharesMounted(d->newlyMounted); 0444 } else { 0445 Smb4KNotification::shareMounted(importedShare); 0446 } 0447 d->newlyMounted = 0; 0448 } 0449 } 0450 } else { 0451 d->newlyMounted = 0; 0452 importedShare.clear(); 0453 } 0454 } 0455 } else { 0456 importedShare.clear(); 0457 } 0458 } 0459 0460 if (!d->firstImportDone && d->importedShares.isEmpty()) { 0461 d->firstImportDone = true; 0462 } 0463 } else { 0464 // 0465 // When the system is offline, no mounted shares are processed, so 0466 // empty the list of imported shares here. 0467 // 0468 while (!d->importedShares.isEmpty()) { 0469 SharePtr share = d->importedShares.takeFirst(); 0470 share.clear(); 0471 } 0472 } 0473 0474 // 0475 // Tell the program the list of mounted shares changed 0476 // 0477 if (changed) { 0478 Q_EMIT mountedSharesListChanged(); 0479 } 0480 } 0481 0482 void Smb4KMounter::mountShare(const SharePtr &share) 0483 { 0484 if (share) { 0485 // 0486 // Check that the URL is valid 0487 // 0488 if (!share->url().isValid()) { 0489 Smb4KNotification::invalidURLPassed(); 0490 return; 0491 } 0492 0493 // 0494 // Check if the share has already been mounted. If it is already present, 0495 // do not process it and return. 0496 // 0497 QUrl url; 0498 0499 if (share->isHomesShare()) { 0500 url = share->homeUrl(); 0501 } else { 0502 url = share->url(); 0503 } 0504 0505 QList<SharePtr> mountedShares = findShareByUrl(url); 0506 bool isMounted = false; 0507 0508 for (const SharePtr &s : qAsConst(mountedShares)) { 0509 if (!s->isForeign()) { 0510 isMounted = true; 0511 break; 0512 } 0513 } 0514 0515 if (isMounted) { 0516 return; 0517 } 0518 0519 // 0520 // Wake-On-LAN: Wake up the host before mounting 0521 // 0522 if (Smb4KSettings::enableWakeOnLAN()) { 0523 CustomSettingsPtr customSettings = Smb4KCustomSettingsManager::self()->findCustomSettings(share->url().resolved(QUrl(QStringLiteral("..")))); 0524 0525 if (customSettings && customSettings->wakeOnLanSendBeforeMount()) { 0526 Q_EMIT aboutToStart(WakeUp); 0527 0528 QUdpSocket *socket = new QUdpSocket(this); 0529 QHostAddress addr; 0530 0531 // Use the host's IP address directly from the share object. 0532 if (share->hasHostIpAddress()) { 0533 addr.setAddress(share->hostIpAddress()); 0534 } else { 0535 addr.setAddress(QStringLiteral("255.255.255.255")); 0536 } 0537 0538 // Construct magic sequence 0539 QByteArray sequence; 0540 0541 // 6 times 0xFF 0542 for (int j = 0; j < 6; ++j) { 0543 sequence.append(QChar(0xFF).toLatin1()); 0544 } 0545 0546 // 16 times the MAC address 0547 QStringList parts = customSettings->macAddress().split(QStringLiteral(":"), Qt::SkipEmptyParts); 0548 0549 for (int j = 0; j < 16; ++j) { 0550 for (int k = 0; k < parts.size(); ++k) { 0551 QString item = QStringLiteral("0x") + parts.at(k); 0552 sequence.append(QChar(item.toInt(nullptr, 16)).toLatin1()); 0553 } 0554 } 0555 0556 socket->writeDatagram(sequence, addr, 9); 0557 0558 delete socket; 0559 0560 // Wait the defined time 0561 int stop = 1000 * Smb4KSettings::wakeOnLANWaitingTime() / 250; 0562 int i = 0; 0563 0564 while (i++ < stop) { 0565 wait(250); 0566 } 0567 0568 Q_EMIT finished(WakeUp); 0569 } 0570 } 0571 0572 // 0573 // Create the mountpoint 0574 // 0575 QString mountpoint; 0576 mountpoint += Smb4KMountSettings::mountPrefix().path(); 0577 mountpoint += QDir::separator(); 0578 mountpoint += (Smb4KMountSettings::forceLowerCaseSubdirs() ? share->hostName().toLower() : share->hostName()); 0579 mountpoint += QDir::separator(); 0580 0581 if (!share->isHomesShare()) { 0582 mountpoint += (Smb4KMountSettings::forceLowerCaseSubdirs() ? share->shareName().toLower() : share->shareName()); 0583 } else { 0584 mountpoint += (Smb4KMountSettings::forceLowerCaseSubdirs() ? share->userName().toLower() : share->userName()); 0585 } 0586 0587 // Get the permissions that should be used for creating the 0588 // mount prefix and all its subdirectories. 0589 // Please note that the actual permissions of the mount points 0590 // are determined by the mount utility. 0591 QFile::Permissions permissions; 0592 QUrl parentDirectory; 0593 0594 if (QFile::exists(Smb4KMountSettings::mountPrefix().path())) { 0595 parentDirectory = Smb4KMountSettings::mountPrefix(); 0596 } else { 0597 QUrl u = Smb4KMountSettings::mountPrefix(); 0598 parentDirectory = u.resolved(QUrl(QStringLiteral(".."))); 0599 ; 0600 } 0601 0602 QFile f(parentDirectory.path()); 0603 permissions = f.permissions(); 0604 0605 QDir dir(mountpoint); 0606 0607 if (!dir.mkpath(dir.path())) { 0608 share->setPath(QStringLiteral("")); 0609 Smb4KNotification::mkdirFailed(dir); 0610 return; 0611 } else { 0612 QUrl u = QUrl::fromLocalFile(dir.path()); 0613 0614 while (!parentDirectory.matches(u, QUrl::StripTrailingSlash)) { 0615 QFile(u.path()).setPermissions(permissions); 0616 u = u.resolved(QUrl(QStringLiteral(".."))); 0617 } 0618 } 0619 0620 share->setPath(QDir::cleanPath(mountpoint)); 0621 0622 // 0623 // Get the authentication information 0624 // 0625 Smb4KWalletManager::self()->readLoginCredentials(share); 0626 0627 // 0628 // Mount arguments 0629 // 0630 QVariantMap args; 0631 0632 if (!fillMountActionArgs(share, args)) { 0633 return; 0634 } 0635 0636 // 0637 // Emit the aboutToStart() signal 0638 // 0639 Q_EMIT aboutToStart(MountShare); 0640 0641 // 0642 // Create the mount action 0643 // 0644 KAuth::Action mountAction(QStringLiteral("org.kde.smb4k.mounthelper.mount")); 0645 mountAction.setHelperId(QStringLiteral("org.kde.smb4k.mounthelper")); 0646 mountAction.setArguments(args); 0647 0648 KAuth::ExecuteJob *job = mountAction.execute(); 0649 0650 // 0651 // Modify the cursor, if necessary. 0652 // 0653 if (!hasSubjobs()) { 0654 QApplication::setOverrideCursor(Qt::BusyCursor); 0655 } 0656 0657 // 0658 // Add the job 0659 // 0660 addSubjob(job); 0661 0662 // 0663 // Start the job and process the returned result. 0664 // 0665 bool success = job->exec(); 0666 0667 if (success) { 0668 int errorCode = job->error(); 0669 0670 if (errorCode == 0) { 0671 // Get the error message 0672 QString errorMsg = job->data().value(QStringLiteral("mh_error_message")).toString(); 0673 0674 if (!errorMsg.isEmpty()) { 0675 #if defined(Q_OS_LINUX) 0676 if (errorMsg.contains(QStringLiteral("mount error 13")) 0677 || errorMsg.contains(QStringLiteral("mount error(13)")) /* authentication error */) { 0678 d->retries << share; 0679 Q_EMIT requestCredentials(share); 0680 } else if (errorMsg.contains(QStringLiteral("Unable to find suitable address."))) { 0681 // Swallow this 0682 } else { 0683 Smb4KNotification::mountingFailed(share, errorMsg); 0684 } 0685 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 0686 if (errorMsg.contains(QStringLiteral("Authentication error")) || errorMsg.contains(QStringLiteral("Permission denied"))) { 0687 d->retries << share; 0688 Q_EMIT requestCredentials(share); 0689 } else { 0690 Smb4KNotification::mountingFailed(share, errorMsg); 0691 } 0692 #else 0693 qWarning() << "Smb4KMounter::slotMountJobFinished(): Error handling not implemented!"; 0694 Smb4KNotification::mountingFailed(share, errorMsg); 0695 #endif 0696 } 0697 } else { 0698 Smb4KNotification::actionFailed(errorCode); 0699 } 0700 } else { 0701 // FIXME: Report that the action could not be started 0702 } 0703 0704 // 0705 // Remove the job from the job list 0706 // 0707 removeSubjob(job); 0708 0709 // 0710 // Reset the busy cursor 0711 // 0712 if (!hasSubjobs()) { 0713 QApplication::restoreOverrideCursor(); 0714 } 0715 0716 // 0717 // Emit the finished() signal 0718 // 0719 Q_EMIT finished(MountShare); 0720 } 0721 } 0722 0723 void Smb4KMounter::mountShares(const QList<SharePtr> &shares) 0724 { 0725 // 0726 // This action takes longer 0727 // 0728 d->longActionRunning = true; 0729 0730 // 0731 // Mount the shares 0732 // 0733 for (const SharePtr &share : shares) { 0734 mountShare(share); 0735 } 0736 0737 // 0738 // This action is over 0739 // 0740 d->longActionRunning = false; 0741 } 0742 0743 void Smb4KMounter::unmountShare(const SharePtr &share, bool silent) 0744 { 0745 Q_ASSERT(share); 0746 0747 if (share) { 0748 // 0749 // Check that the URL is valid. 0750 // 0751 if (!share->url().isValid()) { 0752 Smb4KNotification::invalidURLPassed(); 0753 return; 0754 } 0755 0756 // 0757 // Handle foreign shares according to the settings 0758 // 0759 if (share->isForeign()) { 0760 if (!Smb4KMountSettings::unmountForeignShares()) { 0761 if (!silent) { 0762 Smb4KNotification::unmountingNotAllowed(share); 0763 } 0764 0765 return; 0766 } else { 0767 if (!silent) { 0768 if (KMessageBox::warningTwoActions(QApplication::activeWindow(), 0769 i18n("<p>The share <b>%1</b> is mounted to <br><b>%2</b> and owned by user <b>%3</b>.</p>" 0770 "<p>Do you really want to unmount it?</p>", 0771 share->displayString(), 0772 share->path(), 0773 share->user().loginName()), 0774 i18n("Foreign Share"), 0775 KStandardGuiItem::ok(), 0776 KStandardGuiItem::cancel()) 0777 == KMessageBox::SecondaryAction) { 0778 return; 0779 } 0780 } else { 0781 // Without the confirmation of the user, we are not 0782 // unmounting a foreign share! 0783 return; 0784 } 0785 } 0786 } 0787 0788 // 0789 // Force the unmounting of the share either if the system went offline 0790 // or if the user chose to forcibly unmount inaccessible shares (Linux only). 0791 // 0792 bool force = false; 0793 0794 if (Smb4KHardwareInterface::self()->isOnline()) { 0795 #if defined(Q_OS_LINUX) 0796 if (share->isInaccessible()) { 0797 force = Smb4KMountSettings::forceUnmountInaccessible(); 0798 } 0799 #endif 0800 } else { 0801 force = true; 0802 } 0803 0804 // 0805 // Unmount arguments 0806 // 0807 QVariantMap args; 0808 0809 if (!fillUnmountActionArgs(share, force, silent, args)) { 0810 return; 0811 } 0812 0813 // 0814 // Emit the aboutToStart() signal 0815 // 0816 Q_EMIT aboutToStart(UnmountShare); 0817 0818 // 0819 // Create the unmount action 0820 // 0821 KAuth::Action unmountAction(QStringLiteral("org.kde.smb4k.mounthelper.unmount")); 0822 unmountAction.setHelperId(QStringLiteral("org.kde.smb4k.mounthelper")); 0823 unmountAction.setArguments(args); 0824 0825 KAuth::ExecuteJob *job = unmountAction.execute(); 0826 0827 // 0828 // Modify the cursor, if necessary. 0829 // 0830 if (!hasSubjobs()) { 0831 QApplication::setOverrideCursor(Qt::BusyCursor); 0832 } 0833 0834 // 0835 // Add the job 0836 // 0837 addSubjob(job); 0838 0839 // 0840 // Start the job and process the returned result. 0841 // 0842 bool success = job->exec(); 0843 0844 if (success) { 0845 int errorCode = job->error(); 0846 0847 if (errorCode == 0) { 0848 // Get the error message 0849 QString errorMsg = job->data().value(QStringLiteral("mh_error_message")).toString(); 0850 0851 if (!errorMsg.isEmpty()) { 0852 // No error handling needed, just report the error message. 0853 Smb4KNotification::unmountingFailed(share, errorMsg); 0854 } 0855 } else { 0856 Smb4KNotification::actionFailed(errorCode); 0857 } 0858 } else { 0859 // FIXME: Report that the action could not be started 0860 } 0861 0862 // 0863 // Remove the job from the job list 0864 // 0865 removeSubjob(job); 0866 0867 // 0868 // Reset the busy cursor 0869 // 0870 if (!hasSubjobs()) { 0871 QApplication::restoreOverrideCursor(); 0872 } 0873 0874 // 0875 // Emit the finished() signal 0876 // 0877 Q_EMIT finished(UnmountShare); 0878 } 0879 } 0880 0881 void Smb4KMounter::unmountShares(const QList<SharePtr> &shares, bool silent) 0882 { 0883 // 0884 // This action takes longer 0885 // 0886 d->longActionRunning = true; 0887 0888 // 0889 // Inhibit shutdown and sleep 0890 // 0891 Smb4KHardwareInterface::self()->inhibit(); 0892 0893 // 0894 // Unmount the list of shares 0895 // 0896 for (const SharePtr &share : shares) { 0897 // Unmount the share 0898 unmountShare(share, silent); 0899 } 0900 0901 // 0902 // Uninhibit shutdown and sleep 0903 // 0904 Smb4KHardwareInterface::self()->uninhibit(); 0905 0906 // 0907 // This action is over 0908 // 0909 d->longActionRunning = false; 0910 } 0911 0912 void Smb4KMounter::unmountAllShares(bool silent) 0913 { 0914 unmountShares(mountedSharesList(), silent); 0915 } 0916 0917 void Smb4KMounter::start() 0918 { 0919 // 0920 // Connect to the relevant signals provided by Smb4KHardwareInterface. 0921 // 0922 connect(Smb4KHardwareInterface::self(), &Smb4KHardwareInterface::onlineStateChanged, this, &Smb4KMounter::slotOnlineStateChanged, Qt::UniqueConnection); 0923 connect(Smb4KHardwareInterface::self(), &Smb4KHardwareInterface::networkShareAdded, this, &Smb4KMounter::slotTriggerImport, Qt::UniqueConnection); 0924 connect(Smb4KHardwareInterface::self(), &Smb4KHardwareInterface::networkShareRemoved, this, &Smb4KMounter::slotTriggerImport, Qt::UniqueConnection); 0925 0926 // 0927 // Start with importing shares 0928 // 0929 if (Smb4KHardwareInterface::self()->isOnline()) { 0930 QTimer::singleShot(50, this, SLOT(slotStartJobs())); 0931 } 0932 } 0933 0934 void Smb4KMounter::saveSharesForRemount() 0935 { 0936 // 0937 // Save the shares for remount 0938 // 0939 for (const SharePtr &share : mountedSharesList()) { 0940 if (!share->isForeign()) { 0941 Smb4KCustomSettingsManager::self()->addRemount(share, false); 0942 } else { 0943 Smb4KCustomSettingsManager::self()->removeRemount(share, false); 0944 } 0945 } 0946 0947 // 0948 // Also save each failed remount and remove it from the list 0949 // 0950 while (!d->remounts.isEmpty()) { 0951 SharePtr share = d->remounts.takeFirst(); 0952 Smb4KCustomSettingsManager::self()->addRemount(share, false); 0953 share.clear(); 0954 } 0955 } 0956 0957 void Smb4KMounter::timerEvent(QTimerEvent *) 0958 { 0959 if (!isRunning() && Smb4KHardwareInterface::self()->isOnline()) { 0960 // 0961 // Try to remount shares 0962 // 0963 if (d->remountAttempts < Smb4KMountSettings::remountAttempts() && d->firstImportDone) { 0964 if (d->remountAttempts == 0) { 0965 triggerRemounts(true); 0966 } 0967 0968 if ((60000 * Smb4KMountSettings::remountInterval()) < d->remountTimeout) { 0969 triggerRemounts(false); 0970 d->remountTimeout = -TIMEOUT; 0971 } 0972 0973 d->remountTimeout += TIMEOUT; 0974 } 0975 0976 // 0977 // Check the size, accessibility, etc. of the shares 0978 // 0979 if (d->checkTimeout >= 2500 && d->importedShares.isEmpty()) { 0980 for (const SharePtr &share : mountedSharesList()) { 0981 check(share); 0982 Q_EMIT updated(share); 0983 } 0984 0985 d->checkTimeout = 0; 0986 } else { 0987 d->checkTimeout += TIMEOUT; 0988 } 0989 } 0990 } 0991 0992 #if defined(Q_OS_LINUX) 0993 // 0994 // Linux arguments 0995 // 0996 bool Smb4KMounter::fillMountActionArgs(const SharePtr &share, QVariantMap &map) 0997 { 0998 // 0999 // Find the mount executable 1000 // 1001 const QString mount = findMountExecutable(); 1002 1003 if (!mount.isEmpty()) { 1004 map.insert(QStringLiteral("mh_command"), mount); 1005 } else { 1006 Smb4KNotification::commandNotFound(QStringLiteral("mount.cifs")); 1007 return false; 1008 } 1009 1010 // 1011 // Global and custom options 1012 // 1013 CustomSettingsPtr options = Smb4KCustomSettingsManager::self()->findCustomSettings(share); 1014 1015 // 1016 // Pass the remote file system port to the URL 1017 // 1018 if (options) { 1019 if (options->useFileSystemPort()) { 1020 share->setPort(options->fileSystemPort()); 1021 } 1022 } else { 1023 if (Smb4KMountSettings::useRemoteFileSystemPort()) { 1024 share->setPort(Smb4KMountSettings::remoteFileSystemPort()); 1025 } 1026 } 1027 1028 // 1029 // List of arguments passed via "-o ..." to the mount command 1030 // 1031 QStringList argumentsList; 1032 1033 // 1034 // Workgroup or domain 1035 // 1036 // Do not use this, if the domain is a DNS domain. 1037 // 1038 WorkgroupPtr workgroup = findWorkgroup(share->workgroupName()); 1039 1040 if ((workgroup && !workgroup->dnsDiscovered()) || (!workgroup && !share->workgroupName().trimmed().isEmpty())) { 1041 argumentsList << QStringLiteral("domain=") + KShell::quoteArg(share->workgroupName()); 1042 } 1043 1044 // 1045 // Host IP address 1046 // 1047 if (share->hasHostIpAddress()) { 1048 argumentsList << QStringLiteral("ip=") + share->hostIpAddress(); 1049 } 1050 1051 // 1052 // User name (login) 1053 // 1054 if (!share->userName().isEmpty()) { 1055 argumentsList << QStringLiteral("username=") + share->userName(); 1056 } else { 1057 argumentsList << QStringLiteral("guest"); 1058 } 1059 1060 // 1061 // Client's and server's NetBIOS name 1062 // 1063 // According to the manual page, this is only needed when port 139 1064 // is used. So, we only pass the NetBIOS name in that case. 1065 // 1066 bool setNetbiosNames = false; 1067 1068 if (options) { 1069 setNetbiosNames = (options->useFileSystemPort() && options->fileSystemPort() == 139); 1070 } else { 1071 setNetbiosNames = (Smb4KMountSettings::useRemoteFileSystemPort() && Smb4KMountSettings::remoteFileSystemPort() == 139); 1072 } 1073 1074 if (setNetbiosNames) { 1075 // The client's NetBIOS name. If that is empty, fall back to the local host name. 1076 if (!machineNetbiosName().isEmpty()) { 1077 argumentsList << QStringLiteral("netbiosname=") + KShell::quoteArg(machineNetbiosName()); 1078 } else { 1079 argumentsList << QStringLiteral("netbiosname=") + KShell::quoteArg(QHostInfo::localHostName()); 1080 } 1081 1082 // The server's NetBIOS name 1083 argumentsList << QStringLiteral("servern=") + KShell::quoteArg(share->hostName()); 1084 } 1085 1086 // 1087 // CIFS Unix extensions support 1088 // 1089 // This sets the uid, gid, file_mode and dir_mode arguments, if necessary. 1090 // 1091 bool useCifsUnixExtensionsSupport = false; 1092 QString userString, groupString, fileModeString, directoryModeString; 1093 1094 if (options) { 1095 useCifsUnixExtensionsSupport = options->cifsUnixExtensionsSupport(); 1096 userString = options->useUser() ? options->user().userId().toString() : QString(); 1097 groupString = options->useGroup() ? options->group().groupId().toString() : QString(); 1098 fileModeString = options->useFileMode() ? options->fileMode() : QString(); 1099 directoryModeString = options->useDirectoryMode() ? options->directoryMode() : QString(); 1100 } else { 1101 useCifsUnixExtensionsSupport = Smb4KMountSettings::cifsUnixExtensionsSupport(); 1102 userString = Smb4KMountSettings::useUserId() ? Smb4KMountSettings::userId() : QString(); 1103 groupString = Smb4KMountSettings::useGroupId() ? Smb4KMountSettings::groupId() : QString(); 1104 fileModeString = Smb4KMountSettings::useFileMode() ? Smb4KMountSettings::fileMode() : QString(); 1105 directoryModeString = Smb4KMountSettings::useDirectoryMode() ? Smb4KMountSettings::directoryMode() : QString(); 1106 } 1107 1108 if (!useCifsUnixExtensionsSupport) { 1109 // User id 1110 if (!userString.isEmpty()) { 1111 argumentsList << QStringLiteral("uid=") + userString; 1112 } 1113 1114 // Group id 1115 if (!groupString.isEmpty()) { 1116 argumentsList << QStringLiteral("gid=") + groupString; 1117 } 1118 1119 // File mode 1120 if (!fileModeString.isEmpty()) { 1121 argumentsList << QStringLiteral("file_mode=") + fileModeString; 1122 } 1123 1124 // Directory mode 1125 if (!directoryModeString.isEmpty()) { 1126 argumentsList << QStringLiteral("dir_mode=") + directoryModeString; 1127 } 1128 } 1129 1130 // 1131 // Force user id 1132 // 1133 // FIXME: The manual page is not clear about this: Is this option only useful when the uid=... 1134 // argument is given? If so, this should be moved into the 'User id' code block above. 1135 // 1136 if (Smb4KMountSettings::forceUID()) { 1137 argumentsList << QStringLiteral("forceuid"); 1138 } 1139 1140 // 1141 // Force group id 1142 // 1143 // FIXME: The manual page is not clear about this: Is this option only useful when the gid=... 1144 // argument is given? If so, this should be moved into the 'Group id' code block above. 1145 // 1146 if (Smb4KMountSettings::forceGID()) { 1147 argumentsList << QStringLiteral("forcegid"); 1148 } 1149 1150 // 1151 // Client character set 1152 // 1153 if (Smb4KMountSettings::useClientCharset()) { 1154 switch (Smb4KMountSettings::clientCharset()) { 1155 case Smb4KMountSettings::EnumClientCharset::default_charset: { 1156 break; 1157 } 1158 default: { 1159 argumentsList << QStringLiteral("iocharset=") 1160 + Smb4KMountSettings::self()->clientCharsetItem()->choices().value(Smb4KMountSettings::clientCharset()).label; 1161 break; 1162 } 1163 } 1164 } 1165 1166 // 1167 // File system port 1168 // 1169 if (options) { 1170 if (options->useFileSystemPort()) { 1171 argumentsList << QStringLiteral("port=") + QString::number(options->fileSystemPort()); 1172 } 1173 } else { 1174 if (Smb4KMountSettings::useRemoteFileSystemPort()) { 1175 argumentsList << QStringLiteral("port=") + QString::number(Smb4KMountSettings::remoteFileSystemPort()); 1176 } 1177 } 1178 1179 // 1180 // Write access 1181 // 1182 bool useWriteAccess = false; 1183 int writeAccess = -1; 1184 1185 if (options) { 1186 useWriteAccess = options->useWriteAccess(); 1187 writeAccess = options->writeAccess(); 1188 } else { 1189 useWriteAccess = Smb4KMountSettings::useWriteAccess(); 1190 writeAccess = Smb4KMountSettings::writeAccess(); 1191 } 1192 1193 if (useWriteAccess) { 1194 switch (writeAccess) { 1195 case Smb4KMountSettings::EnumWriteAccess::ReadWrite: { 1196 argumentsList << QStringLiteral("rw"); 1197 break; 1198 } 1199 case Smb4KMountSettings::EnumWriteAccess::ReadOnly: { 1200 argumentsList << QStringLiteral("ro"); 1201 break; 1202 } 1203 default: { 1204 break; 1205 } 1206 } 1207 } 1208 1209 // 1210 // Permission checks 1211 // 1212 if (Smb4KMountSettings::permissionChecks()) { 1213 argumentsList << QStringLiteral("perm"); 1214 } else { 1215 argumentsList << QStringLiteral("noperm"); 1216 } 1217 1218 // 1219 // Client controls ids 1220 // 1221 if (Smb4KMountSettings::clientControlsIDs()) { 1222 argumentsList << QStringLiteral("setuids"); 1223 } else { 1224 argumentsList << QStringLiteral("nosetuids"); 1225 } 1226 1227 // 1228 // Server inode numbers 1229 // 1230 if (Smb4KMountSettings::serverInodeNumbers()) { 1231 argumentsList << QStringLiteral("serverino"); 1232 } else { 1233 argumentsList << QStringLiteral("noserverino"); 1234 } 1235 1236 // 1237 // Cache mode 1238 // 1239 if (Smb4KMountSettings::useCacheMode()) { 1240 switch (Smb4KMountSettings::cacheMode()) { 1241 case Smb4KMountSettings::EnumCacheMode::None: { 1242 argumentsList << QStringLiteral("cache=none"); 1243 break; 1244 } 1245 case Smb4KMountSettings::EnumCacheMode::Strict: { 1246 argumentsList << QStringLiteral("cache=strict"); 1247 break; 1248 } 1249 case Smb4KMountSettings::EnumCacheMode::Loose: { 1250 argumentsList << QStringLiteral("cache=loose"); 1251 break; 1252 } 1253 default: { 1254 break; 1255 } 1256 } 1257 } 1258 1259 // 1260 // Translate reserved characters 1261 // 1262 if (Smb4KMountSettings::translateReservedChars()) { 1263 argumentsList << QStringLiteral("mapchars"); 1264 } else { 1265 argumentsList << QStringLiteral("nomapchars"); 1266 } 1267 1268 // 1269 // Locking 1270 // 1271 if (Smb4KMountSettings::noLocking()) { 1272 argumentsList << QStringLiteral("nobrl"); 1273 } 1274 1275 // 1276 // Security mode 1277 // 1278 bool useSecurityMode = false; 1279 int securityMode = -1; 1280 1281 if (options) { 1282 useSecurityMode = options->useSecurityMode(); 1283 securityMode = options->securityMode(); 1284 } else { 1285 useSecurityMode = Smb4KMountSettings::useSecurityMode(); 1286 securityMode = Smb4KMountSettings::securityMode(); 1287 } 1288 1289 if (useSecurityMode) { 1290 switch (securityMode) { 1291 case Smb4KMountSettings::EnumSecurityMode::None: { 1292 argumentsList << QStringLiteral("sec=none"); 1293 break; 1294 } 1295 case Smb4KMountSettings::EnumSecurityMode::Krb5: { 1296 argumentsList << QStringLiteral("sec=krb5"); 1297 argumentsList << QStringLiteral("cruid=") + KUser(KUser::UseRealUserID).userId().toString(); 1298 break; 1299 } 1300 case Smb4KMountSettings::EnumSecurityMode::Krb5i: { 1301 argumentsList << QStringLiteral("sec=krb5i"); 1302 argumentsList << QStringLiteral("cruid=") + KUser(KUser::UseRealUserID).userId().toString(); 1303 break; 1304 } 1305 case Smb4KMountSettings::EnumSecurityMode::Ntlm: { 1306 argumentsList << QStringLiteral("sec=ntlm"); 1307 break; 1308 } 1309 case Smb4KMountSettings::EnumSecurityMode::Ntlmi: { 1310 argumentsList << QStringLiteral("sec=ntlmi"); 1311 break; 1312 } 1313 case Smb4KMountSettings::EnumSecurityMode::Ntlmv2: { 1314 argumentsList << QStringLiteral("sec=ntlmv2"); 1315 break; 1316 } 1317 case Smb4KMountSettings::EnumSecurityMode::Ntlmv2i: { 1318 argumentsList << QStringLiteral("sec=ntlmv2i"); 1319 break; 1320 } 1321 case Smb4KMountSettings::EnumSecurityMode::Ntlmssp: { 1322 argumentsList << QStringLiteral("sec=ntlmssp"); 1323 break; 1324 } 1325 case Smb4KMountSettings::EnumSecurityMode::Ntlmsspi: { 1326 argumentsList << QStringLiteral("sec=ntlmsspi"); 1327 break; 1328 } 1329 default: { 1330 // Smb4KSettings::EnumSecurityMode::Default, 1331 break; 1332 } 1333 } 1334 } 1335 1336 // 1337 // SMB protocol version 1338 // 1339 bool useMountProtocolVersion = false; 1340 int mountProtocolVersion = -1; 1341 1342 if (options) { 1343 useMountProtocolVersion = options->useMountProtocolVersion(); 1344 mountProtocolVersion = options->mountProtocolVersion(); 1345 } else { 1346 useMountProtocolVersion = Smb4KMountSettings::useSmbProtocolVersion(); 1347 mountProtocolVersion = Smb4KMountSettings::smbProtocolVersion(); 1348 } 1349 1350 if (useMountProtocolVersion) { 1351 switch (mountProtocolVersion) { 1352 case Smb4KMountSettings::EnumSmbProtocolVersion::OnePointZero: { 1353 argumentsList << QStringLiteral("vers=1.0"); 1354 break; 1355 } 1356 case Smb4KMountSettings::EnumSmbProtocolVersion::TwoPointZero: { 1357 argumentsList << QStringLiteral("vers=2.0"); 1358 break; 1359 } 1360 case Smb4KMountSettings::EnumSmbProtocolVersion::TwoPointOne: { 1361 argumentsList << QStringLiteral("vers=2.1"); 1362 break; 1363 } 1364 case Smb4KMountSettings::EnumSmbProtocolVersion::ThreePointZero: { 1365 argumentsList << QStringLiteral("vers=3.0"); 1366 break; 1367 } 1368 case Smb4KMountSettings::EnumSmbProtocolVersion::ThreePointZeroPointTwo: { 1369 argumentsList << QStringLiteral("vers=3.0.2"); 1370 break; 1371 } 1372 case Smb4KMountSettings::EnumSmbProtocolVersion::ThreePointOnePointOne: { 1373 argumentsList << QStringLiteral("vers=3.1.1"); 1374 break; 1375 } 1376 case Smb4KMountSettings::EnumSmbProtocolVersion::ThreeAndAbove: { 1377 argumentsList << QStringLiteral("vers=3"); 1378 break; 1379 } 1380 case Smb4KMountSettings::EnumSmbProtocolVersion::Default: { 1381 argumentsList << QStringLiteral("vers=default"); 1382 break; 1383 } 1384 default: { 1385 break; 1386 } 1387 } 1388 } 1389 1390 // 1391 // Mount options provided by the user 1392 // 1393 if (Smb4KMountSettings::useCustomCifsOptions()) { 1394 if (!Smb4KMountSettings::customCIFSOptions().isEmpty()) { 1395 // SECURITY: Only pass those arguments to mount.cifs that do not pose 1396 // a potential security risk and that have not already been defined. 1397 // 1398 // This is, among others, the proper fix to the security issue reported 1399 // by Heiner Markert (aka CVE-2014-2581). 1400 QStringList allowedArgs = allowedMountArguments(); 1401 QStringList list = Smb4KMountSettings::customCIFSOptions().split(QStringLiteral(","), Qt::SkipEmptyParts); 1402 QMutableStringListIterator it(list); 1403 1404 while (it.hasNext()) { 1405 QString arg = it.next().section(QStringLiteral("="), 0, 0); 1406 1407 if (!allowedArgs.contains(arg)) { 1408 it.remove(); 1409 } 1410 1411 argumentsList += list; 1412 } 1413 } 1414 } 1415 1416 // 1417 // Insert the mount options into the map 1418 // 1419 QStringList mh_options; 1420 mh_options << QStringLiteral("-o"); 1421 mh_options << argumentsList.join(QStringLiteral(",")); 1422 map.insert(QStringLiteral("mh_options"), mh_options); 1423 1424 // 1425 // Insert the mountpoint into the map 1426 // 1427 map.insert(QStringLiteral("mh_mountpoint"), share->canonicalPath()); 1428 1429 // 1430 // Insert information about the share and its URL into the map 1431 // 1432 if (!share->isHomesShare()) { 1433 map.insert(QStringLiteral("mh_url"), share->url()); 1434 } else { 1435 map.insert(QStringLiteral("mh_url"), share->homeUrl()); 1436 map.insert(QStringLiteral("mh_homes_url"), share->url()); 1437 } 1438 1439 // 1440 // Location of the Kerberos ticket 1441 // 1442 // The path to the Kerberos ticket is stored - if it exists - in the 1443 // KRB5CCNAME environment variable. By default, the ticket is located 1444 // at /tmp/krb5cc_[uid]. So, if the environment variable does not exist, 1445 // but the cache file is there, try to use it. 1446 // 1447 if (QProcessEnvironment::systemEnvironment().contains(QStringLiteral("KRB5CCNAME"))) { 1448 map.insert(QStringLiteral("mh_krb5ticket"), QProcessEnvironment::systemEnvironment().value(QStringLiteral("KRB5CCNAME"), QStringLiteral(""))); 1449 } else { 1450 QString ticket = QStringLiteral("/tmp/krb5cc_") + KUser(KUser::UseRealUserID).userId().toString(); 1451 1452 if (QFile::exists(ticket)) { 1453 QString fileEntry = QStringLiteral("FILE:") + ticket; 1454 map.insert(QStringLiteral("mh_krb5ticket"), fileEntry); 1455 } 1456 } 1457 1458 return true; 1459 } 1460 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 1461 // 1462 // FreeBSD and NetBSD arguments 1463 // 1464 bool Smb4KMounter::fillMountActionArgs(const SharePtr &share, QVariantMap &map) 1465 { 1466 // 1467 // Find the mount executable 1468 // 1469 const QString mount = findMountExecutable(); 1470 1471 if (!mount.isEmpty()) { 1472 map.insert(QStringLiteral("mh_command"), mount); 1473 } else { 1474 Smb4KNotification::commandNotFound(QStringLiteral("mount_smbfs")); 1475 return false; 1476 } 1477 1478 // 1479 // Global and custom options 1480 // 1481 CustomSettingsPtr options = Smb4KCustomSettingsManager::self()->findCustomSettings(share); 1482 1483 // 1484 // List of arguments 1485 // 1486 QStringList argumentsList; 1487 1488 // 1489 // Workgroup or domain 1490 // 1491 // Do not use this, if the domain is a DNS domain. 1492 // 1493 WorkgroupPtr workgroup = findWorkgroup(share->workgroupName()); 1494 1495 if ((workgroup && !workgroup->dnsDiscovered()) || (!workgroup && !share->workgroupName().trimmed().isEmpty())) { 1496 argumentsList << QStringLiteral("-W"); 1497 argumentsList << KShell::quoteArg(share->workgroupName()); 1498 } 1499 1500 // 1501 // IP address 1502 // 1503 if (!share->hostIpAddress().isEmpty()) { 1504 argumentsList << QStringLiteral("-I"); 1505 argumentsList << share->hostIpAddress(); 1506 } 1507 1508 // 1509 // User Id 1510 // 1511 if (options) { 1512 if (options->useUser()) { 1513 argumentsList << QStringLiteral("-u"); 1514 argumentsList << options->user().userId().toString(); 1515 } 1516 } else { 1517 if (Smb4KMountSettings::useUserId()) { 1518 argumentsList << QStringLiteral("-u"); 1519 argumentsList << Smb4KMountSettings::userId(); 1520 } 1521 } 1522 1523 // 1524 // Group Id 1525 // 1526 if (options) { 1527 if (options->useGroup()) { 1528 argumentsList << QStringLiteral("-g"); 1529 argumentsList << options->group().groupId().toString(); 1530 } 1531 } else { 1532 if (Smb4KMountSettings::useGroupId()) { 1533 argumentsList << QStringLiteral("-g"); 1534 argumentsList << Smb4KMountSettings::groupId(); 1535 } 1536 } 1537 1538 if (Smb4KMountSettings::useCharacterSets()) { 1539 // Client character set 1540 QString clientCharset, serverCharset; 1541 1542 switch (Smb4KMountSettings::clientCharset()) { 1543 case Smb4KMountSettings::EnumClientCharset::default_charset: { 1544 break; 1545 } 1546 default: { 1547 clientCharset = Smb4KMountSettings::self()->clientCharsetItem()->choices().value(Smb4KMountSettings::clientCharset()).label; 1548 break; 1549 } 1550 } 1551 1552 // Server character set 1553 switch (Smb4KMountSettings::serverCodepage()) { 1554 case Smb4KMountSettings::EnumServerCodepage::default_codepage: { 1555 break; 1556 } 1557 default: { 1558 serverCharset = Smb4KMountSettings::self()->serverCodepageItem()->choices().value(Smb4KMountSettings::serverCodepage()).label; 1559 break; 1560 } 1561 } 1562 1563 if (!clientCharset.isEmpty() && !serverCharset.isEmpty()) { 1564 argumentsList << QStringLiteral("-E"); 1565 argumentsList << clientCharset + QStringLiteral(":") + serverCharset; 1566 } 1567 } 1568 1569 // 1570 // File mode 1571 // 1572 if (options) { 1573 if (options->useFileMode()) { 1574 argumentsList << QStringLiteral("-f"); 1575 argumentsList << options->fileMode(); 1576 } 1577 } else { 1578 if (Smb4KMountSettings::useFileMode()) { 1579 argumentsList << QStringLiteral("-f"); 1580 argumentsList << Smb4KMountSettings::fileMode(); 1581 } 1582 } 1583 1584 // 1585 // Directory mode 1586 // 1587 if (options) { 1588 if (options->useDirectoryMode()) { 1589 argumentsList << QStringLiteral("-d"); 1590 argumentsList << options->directoryMode(); 1591 } 1592 } else { 1593 if (Smb4KMountSettings::useDirectoryMode()) { 1594 argumentsList << QStringLiteral("-d"); 1595 argumentsList << Smb4KMountSettings::directoryMode(); 1596 } 1597 } 1598 1599 // 1600 // User name (login) 1601 // 1602 if (!share->userName().isEmpty()) { 1603 argumentsList << QStringLiteral("-U"); 1604 argumentsList << share->userName(); 1605 } else { 1606 argumentsList << QStringLiteral("-N"); 1607 } 1608 1609 // 1610 // Insert the mount options into the map 1611 // 1612 map.insert(QStringLiteral("mh_options"), argumentsList); 1613 1614 // 1615 // Insert the mountpoint into the map 1616 // 1617 map.insert(QStringLiteral("mh_mountpoint"), share->canonicalPath()); 1618 1619 // 1620 // Insert information about the share and its URL into the map 1621 // 1622 if (!share->isHomesShare()) { 1623 map.insert(QStringLiteral("mh_url"), share->url()); 1624 } else { 1625 map.insert(QStringLiteral("mh_url"), share->homeUrl()); 1626 map.insert(QStringLiteral("mh_homes_url"), share->url()); 1627 } 1628 1629 return true; 1630 } 1631 #else 1632 // 1633 // Dummy 1634 // 1635 bool Smb4KMounter::fillMountActionArgs(const SharePtr &, QVariantMap &) 1636 { 1637 qWarning() << "Smb4KMounter::fillMountActionArgs() is not implemented!"; 1638 qWarning() << "Mounting under this operating system is not supported..."; 1639 return false; 1640 } 1641 #endif 1642 1643 #if defined(Q_OS_LINUX) 1644 // 1645 // Linux arguments 1646 // 1647 bool Smb4KMounter::fillUnmountActionArgs(const SharePtr &share, bool force, bool silent, QVariantMap &map) 1648 { 1649 // 1650 // The umount program 1651 // 1652 const QString umount = findUmountExecutable(); 1653 1654 if (umount.isEmpty() && !silent) { 1655 Smb4KNotification::commandNotFound(QStringLiteral("umount")); 1656 return false; 1657 } 1658 1659 // 1660 // The options 1661 // 1662 QStringList options; 1663 1664 if (force) { 1665 options << QStringLiteral("-l"); // lazy unmount 1666 } 1667 1668 // 1669 // Insert data into the map 1670 // 1671 map.insert(QStringLiteral("mh_command"), umount); 1672 map.insert(QStringLiteral("mh_url"), share->url()); 1673 1674 if (!share->isInaccessible() && Smb4KHardwareInterface::self()->isOnline()) { 1675 map.insert(QStringLiteral("mh_mountpoint"), share->canonicalPath()); 1676 } else { 1677 map.insert(QStringLiteral("mh_mountpoint"), share->path()); 1678 } 1679 1680 map.insert(QStringLiteral("mh_options"), options); 1681 1682 return true; 1683 } 1684 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) 1685 // 1686 // FreeBSD and NetBSD arguments 1687 // 1688 bool Smb4KMounter::fillUnmountActionArgs(const SharePtr &share, bool force, bool silent, QVariantMap &map) 1689 { 1690 // 1691 // The umount program 1692 // 1693 const QString umount = findUmountExecutable(); 1694 1695 if (umount.isEmpty() && !silent) { 1696 Smb4KNotification::commandNotFound(QStringLiteral("umount")); 1697 return false; 1698 } 1699 1700 // 1701 // The options 1702 // 1703 QStringList options; 1704 1705 if (force) { 1706 options << QStringLiteral("-f"); 1707 } 1708 1709 // 1710 // Insert data into the map 1711 // 1712 map.insert(QStringLiteral("mh_command"), umount); 1713 map.insert(QStringLiteral("mh_url"), share->url()); 1714 1715 if (!share->isInaccessible() && Smb4KHardwareInterface::self()->isOnline()) { 1716 map.insert(QStringLiteral("mh_mountpoint"), share->canonicalPath()); 1717 } else { 1718 map.insert(QStringLiteral("mh_mountpoint"), share->path()); 1719 } 1720 1721 map.insert(QStringLiteral("mh_options"), options); 1722 1723 return true; 1724 } 1725 #else 1726 // 1727 // Dummy 1728 // 1729 bool Smb4KMounter::fillUnmountActionArgs(const SharePtr &, bool, bool, QVariantMap &) 1730 { 1731 qWarning() << "Smb4KMounter::fillUnmountActionArgs() is not implemented!"; 1732 qWarning() << "Unmounting under this operating system is not supported..."; 1733 return false; 1734 } 1735 #endif 1736 1737 void Smb4KMounter::check(const SharePtr &share) 1738 { 1739 d->storageInfo.setPath(share->path()); 1740 1741 if (d->storageInfo.isValid() && d->storageInfo.isReady()) { 1742 // Accessibility 1743 share->setInaccessible(false); 1744 1745 // Size information 1746 share->setFreeDiskSpace(d->storageInfo.bytesAvailable()); // Bytes available to the user, might be less that bytesFree() 1747 share->setTotalDiskSpace(d->storageInfo.bytesTotal()); 1748 1749 // Get the owner an group, if possible. 1750 QFileInfo fileInfo(share->path()); 1751 fileInfo.setCaching(false); 1752 1753 if (fileInfo.exists()) { 1754 share->setUser(KUser(static_cast<K_UID>(fileInfo.ownerId()))); 1755 share->setGroup(KUserGroup(static_cast<K_GID>(fileInfo.groupId()))); 1756 } else { 1757 share->setUser(KUser(KUser::UseRealUserID)); 1758 share->setGroup(KUserGroup(KUser::UseRealUserID)); 1759 } 1760 } else { 1761 share->setInaccessible(true); 1762 share->setFreeDiskSpace(0); 1763 share->setTotalDiskSpace(0); 1764 share->setUser(KUser(KUser::UseRealUserID)); 1765 share->setGroup(KUserGroup(KUser::UseRealUserID)); 1766 } 1767 } 1768 1769 ///////////////////////////////////////////////////////////////////////////// 1770 // SLOT IMPLEMENTATIONS 1771 ///////////////////////////////////////////////////////////////////////////// 1772 1773 void Smb4KMounter::slotStartJobs() 1774 { 1775 // 1776 // Start the import of shares 1777 // 1778 if (Smb4KHardwareInterface::self()->isOnline()) { 1779 import(true); 1780 } 1781 1782 // 1783 // Start the timer 1784 // 1785 if (d->timerId == -1) { 1786 d->timerId = startTimer(TIMEOUT); 1787 } 1788 } 1789 1790 void Smb4KMounter::slotAboutToQuit() 1791 { 1792 // 1793 // Abort any actions 1794 // 1795 abort(); 1796 1797 // 1798 // Check if the user wants to remount shares and save the 1799 // shares for remount if so. 1800 // 1801 if (Smb4KMountSettings::remountShares()) { 1802 saveSharesForRemount(); 1803 } 1804 1805 // 1806 // Unmount the shares if the user chose to do so. 1807 // 1808 if (Smb4KMountSettings::unmountSharesOnExit()) { 1809 unmountAllShares(true); 1810 } 1811 1812 // 1813 // Clean up the mount prefix. 1814 // 1815 KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded | KMountPoint::NeedMountOptions); 1816 1817 QDir dir; 1818 dir.cd(Smb4KMountSettings::mountPrefix().path()); 1819 QStringList hostDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort); 1820 QStringList mountpoints; 1821 1822 for (const QString &hostDir : qAsConst(hostDirs)) { 1823 dir.cd(hostDir); 1824 1825 QStringList shareDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort); 1826 1827 for (const QString &shareDir : qAsConst(shareDirs)) { 1828 dir.cd(shareDir); 1829 mountpoints << dir.absolutePath(); 1830 dir.cdUp(); 1831 } 1832 1833 dir.cdUp(); 1834 } 1835 1836 // Remove those mountpoints where a share is actually mounted. 1837 for (const QExplicitlySharedDataPointer<KMountPoint> &mountPoint : qAsConst(mountPoints)) { 1838 mountpoints.removeOne(mountPoint->mountPoint()); 1839 } 1840 1841 // Remove the empty mountpoints. 1842 for (const QString &mp : qAsConst(mountpoints)) { 1843 dir.cd(mp); 1844 dir.rmdir(dir.canonicalPath()); 1845 1846 if (dir.cdUp()) { 1847 dir.rmdir(dir.canonicalPath()); 1848 } 1849 } 1850 } 1851 1852 void Smb4KMounter::slotOnlineStateChanged(bool online) 1853 { 1854 if (online) { 1855 slotStartJobs(); 1856 } else { 1857 abort(); 1858 saveSharesForRemount(); 1859 1860 // FIXME: Do we need this at all? 1861 for (const SharePtr &share : mountedSharesList()) { 1862 share->setInaccessible(true); 1863 } 1864 1865 unmountAllShares(true); 1866 1867 d->remountAttempts = 0; 1868 d->remountTimeout = 0; 1869 } 1870 } 1871 1872 void Smb4KMounter::slotAboutToChangeProfile() 1873 { 1874 if (Smb4KMountSettings::remountShares()) { 1875 saveSharesForRemount(); 1876 } 1877 } 1878 1879 void Smb4KMounter::slotActiveProfileChanged(const QString &newProfile) 1880 { 1881 Q_UNUSED(newProfile); 1882 1883 // Stop the timer. 1884 killTimer(d->timerId); 1885 1886 abort(); 1887 1888 // Clear all remounts. 1889 while (!d->remounts.isEmpty()) { 1890 d->remounts.takeFirst().clear(); 1891 } 1892 1893 // Clear all retries. 1894 while (!d->retries.isEmpty()) { 1895 d->retries.takeFirst().clear(); 1896 } 1897 1898 // Unmount all shares 1899 unmountAllShares(true); 1900 1901 // Reset some variables. 1902 // Don't touch d->firstImportDone here, because that remains true 1903 d->remountTimeout = 0; 1904 d->remountAttempts = 0; 1905 1906 // Restart the timer 1907 d->timerId = startTimer(TIMEOUT); 1908 } 1909 1910 void Smb4KMounter::slotTriggerImport() 1911 { 1912 // 1913 // Wait a bit so that the mount or unmount process can finish and 1914 // then start importing the shares, if no jobs are running anymore 1915 // 1916 QTimer::singleShot(2 * TIMEOUT, this, [&]() { 1917 if (!isRunning()) { 1918 import(true); 1919 } 1920 }); 1921 } 1922 1923 void Smb4KMounter::slotConfigChanged() 1924 { 1925 if (d->detectAllShares != Smb4KMountSettings::detectAllShares()) { 1926 import(true); 1927 d->detectAllShares = Smb4KMountSettings::detectAllShares(); 1928 } 1929 } 1930 1931 void Smb4KMounter::slotCredentialsUpdated(const QUrl &url) 1932 { 1933 if (!url.isEmpty() && !d->retries.isEmpty()) { 1934 QMutableListIterator<SharePtr> it(d->retries); 1935 1936 while (it.hasNext()) { 1937 SharePtr share = it.next(); 1938 1939 QUrl parentUrl = share->url().resolved(QUrl(QStringLiteral(".."))).adjusted(QUrl::StripTrailingSlash); 1940 1941 if (QString::compare(share->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 1942 url.toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 1943 Qt::CaseInsensitive) 1944 == 0 1945 || QString::compare(parentUrl.toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 1946 url.toString(QUrl::RemoveUserInfo | QUrl::RemovePort), 1947 Qt::CaseInsensitive) 1948 == 0) { 1949 share->setUserName(url.userName()); 1950 share->setPassword(url.password()); 1951 1952 mountShare(share); 1953 1954 it.remove(); 1955 } 1956 } 1957 } 1958 }