File indexing completed on 2024-04-21 05:01:38

0001 /*
0002     This is the global namespace for Smb4K.
0003 
0004     SPDX-FileCopyrightText: 2005-2024 Alexander Reinholdt <alexander.reinholdt@kdemail.net>
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // application specific includes
0009 #include "smb4kglobal.h"
0010 #include "smb4kclient.h"
0011 #include "smb4kglobal_p.h"
0012 #include "smb4kglobalenums.h"
0013 #include "smb4kmounter.h"
0014 #include "smb4knotification.h"
0015 #include "smb4ksynchronizer.h"
0016 
0017 // Qt includes
0018 #include <QDebug>
0019 #include <QDirIterator>
0020 #include <QEventLoop>
0021 #include <QRecursiveMutex>
0022 #include <QStandardPaths>
0023 #include <QTimer>
0024 #include <QUrl>
0025 
0026 // KDE includes
0027 #include <KIO/CommandLauncherJob>
0028 #include <KIO/OpenUrlJob>
0029 
0030 Q_GLOBAL_STATIC(Smb4KGlobalPrivate, p);
0031 QRecursiveMutex mutex;
0032 
0033 const QList<WorkgroupPtr> &Smb4KGlobal::workgroupsList()
0034 {
0035     return p->workgroupsList;
0036 }
0037 
0038 WorkgroupPtr Smb4KGlobal::findWorkgroup(const QString &name)
0039 {
0040     WorkgroupPtr workgroup;
0041 
0042     mutex.lock();
0043 
0044     for (const WorkgroupPtr &w : qAsConst(p->workgroupsList)) {
0045         if (QString::compare(w->workgroupName(), name, Qt::CaseInsensitive) == 0) {
0046             workgroup = w;
0047             break;
0048         }
0049     }
0050 
0051     mutex.unlock();
0052 
0053     return workgroup;
0054 }
0055 
0056 bool Smb4KGlobal::addWorkgroup(WorkgroupPtr workgroup)
0057 {
0058     Q_ASSERT(workgroup);
0059 
0060     bool added = false;
0061 
0062     if (workgroup) {
0063         mutex.lock();
0064 
0065         if (!findWorkgroup(workgroup->workgroupName())) {
0066             p->workgroupsList.append(workgroup);
0067             added = true;
0068         }
0069 
0070         mutex.unlock();
0071     }
0072 
0073     return added;
0074 }
0075 
0076 bool Smb4KGlobal::updateWorkgroup(WorkgroupPtr workgroup)
0077 {
0078     Q_ASSERT(workgroup);
0079 
0080     bool updated = false;
0081 
0082     if (workgroup) {
0083         mutex.lock();
0084 
0085         WorkgroupPtr existingWorkgroup = findWorkgroup(workgroup->workgroupName());
0086 
0087         if (existingWorkgroup) {
0088             existingWorkgroup->update(workgroup.data());
0089             updated = true;
0090         }
0091 
0092         mutex.unlock();
0093     }
0094 
0095     return updated;
0096 }
0097 
0098 bool Smb4KGlobal::removeWorkgroup(WorkgroupPtr workgroup)
0099 {
0100     Q_ASSERT(workgroup);
0101 
0102     bool removed = false;
0103 
0104     if (workgroup) {
0105         mutex.lock();
0106 
0107         int index = p->workgroupsList.indexOf(workgroup);
0108 
0109         if (index != -1) {
0110             // The workgroup was found. Remove it.
0111             p->workgroupsList.takeAt(index).clear();
0112             removed = true;
0113         } else {
0114             // Try harder to find the workgroup.
0115             WorkgroupPtr wg = findWorkgroup(workgroup->workgroupName());
0116 
0117             if (wg) {
0118                 index = p->workgroupsList.indexOf(wg);
0119 
0120                 if (index != -1) {
0121                     p->workgroupsList.takeAt(index).clear();
0122                     removed = true;
0123                 }
0124             }
0125 
0126             workgroup.clear();
0127         }
0128 
0129         mutex.unlock();
0130     }
0131 
0132     return removed;
0133 }
0134 
0135 void Smb4KGlobal::clearWorkgroupsList()
0136 {
0137     mutex.lock();
0138 
0139     while (!p->workgroupsList.isEmpty()) {
0140         p->workgroupsList.takeFirst().clear();
0141     }
0142 
0143     mutex.unlock();
0144 }
0145 
0146 const QList<HostPtr> &Smb4KGlobal::hostsList()
0147 {
0148     return p->hostsList;
0149 }
0150 
0151 HostPtr Smb4KGlobal::findHost(const QString &name, const QString &workgroup)
0152 {
0153     HostPtr host;
0154 
0155     mutex.lock();
0156 
0157     for (const HostPtr &h : qAsConst(p->hostsList)) {
0158         if ((workgroup.isEmpty() || QString::compare(h->workgroupName(), workgroup, Qt::CaseInsensitive) == 0)
0159             && QString::compare(h->hostName(), name, Qt::CaseInsensitive) == 0) {
0160             host = h;
0161             break;
0162         }
0163     }
0164 
0165     mutex.unlock();
0166 
0167     return host;
0168 }
0169 
0170 bool Smb4KGlobal::addHost(HostPtr host)
0171 {
0172     Q_ASSERT(host);
0173 
0174     bool added = false;
0175 
0176     if (host) {
0177         mutex.lock();
0178 
0179         if (!findHost(host->hostName(), host->workgroupName())) {
0180             p->hostsList.append(host);
0181             added = true;
0182         }
0183 
0184         mutex.unlock();
0185     }
0186 
0187     return added;
0188 }
0189 
0190 bool Smb4KGlobal::updateHost(HostPtr host)
0191 {
0192     Q_ASSERT(host);
0193 
0194     bool updated = false;
0195 
0196     if (host) {
0197         mutex.lock();
0198 
0199         HostPtr existingHost = findHost(host->hostName(), host->workgroupName());
0200 
0201         if (existingHost) {
0202             existingHost->update(host.data());
0203             updated = true;
0204         }
0205 
0206         mutex.unlock();
0207     }
0208 
0209     return updated;
0210 }
0211 
0212 bool Smb4KGlobal::removeHost(HostPtr host)
0213 {
0214     Q_ASSERT(host);
0215 
0216     bool removed = false;
0217 
0218     if (host) {
0219         mutex.lock();
0220 
0221         int index = p->hostsList.indexOf(host);
0222 
0223         if (index != -1) {
0224             // The host was found. Remove it.
0225             p->hostsList.takeAt(index).clear();
0226             removed = true;
0227         } else {
0228             // Try harder to find the host.
0229             HostPtr h = findHost(host->hostName(), host->workgroupName());
0230 
0231             if (h) {
0232                 index = p->hostsList.indexOf(h);
0233 
0234                 if (index != -1) {
0235                     p->hostsList.takeAt(index).clear();
0236                     removed = true;
0237                 }
0238             }
0239 
0240             host.clear();
0241         }
0242 
0243         mutex.unlock();
0244     }
0245 
0246     return removed;
0247 }
0248 
0249 void Smb4KGlobal::clearHostsList()
0250 {
0251     mutex.lock();
0252 
0253     while (!p->hostsList.isEmpty()) {
0254         p->hostsList.takeFirst().clear();
0255     }
0256 
0257     mutex.unlock();
0258 }
0259 
0260 QList<HostPtr> Smb4KGlobal::workgroupMembers(WorkgroupPtr workgroup)
0261 {
0262     QList<HostPtr> hosts;
0263 
0264     mutex.lock();
0265 
0266     for (const HostPtr &h : qAsConst(p->hostsList)) {
0267         if (QString::compare(h->workgroupName(), workgroup->workgroupName(), Qt::CaseInsensitive) == 0) {
0268             hosts << h;
0269         }
0270     }
0271 
0272     mutex.unlock();
0273 
0274     return hosts;
0275 }
0276 
0277 const QList<SharePtr> &Smb4KGlobal::sharesList()
0278 {
0279     return p->sharesList;
0280 }
0281 
0282 SharePtr Smb4KGlobal::findShare(const QUrl &url, const QString &workgroup)
0283 {
0284     SharePtr share;
0285 
0286     mutex.lock();
0287 
0288     for (const SharePtr &s : qAsConst(p->sharesList)) {
0289         if (QString::compare(s->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort),
0290                              url.toString(QUrl::RemoveUserInfo | QUrl::RemovePort),
0291                              Qt::CaseInsensitive)
0292                 == 0
0293             && (workgroup.isEmpty() || QString::compare(s->workgroupName(), workgroup, Qt::CaseInsensitive) == 0)) {
0294             share = s;
0295             break;
0296         }
0297     }
0298 
0299     mutex.unlock();
0300 
0301     return share;
0302 }
0303 
0304 bool Smb4KGlobal::addShare(SharePtr share)
0305 {
0306     Q_ASSERT(share);
0307 
0308     bool added = false;
0309 
0310     if (share) {
0311         mutex.lock();
0312 
0313         //
0314         // Add the share
0315         //
0316         if (!findShare(share->url(), share->workgroupName())) {
0317             //
0318             // Set the share mounted
0319             // Only honor shares that are owned by the user
0320             //
0321             QList<SharePtr> mountedShares = findShareByUrl(share->url());
0322 
0323             if (!mountedShares.isEmpty()) {
0324                 for (const SharePtr &s : qAsConst(mountedShares)) {
0325                     if (!s->isForeign()) {
0326                         share->setMountData(s.data());
0327                         break;
0328                     } else {
0329                         continue;
0330                     }
0331                 }
0332             }
0333 
0334             //
0335             // Add it
0336             //
0337             p->sharesList.append(share);
0338             added = true;
0339         }
0340     }
0341 
0342     mutex.unlock();
0343 
0344     return added;
0345 }
0346 
0347 bool Smb4KGlobal::updateShare(SharePtr share)
0348 {
0349     Q_ASSERT(share);
0350 
0351     bool updated = false;
0352 
0353     if (share) {
0354         mutex.lock();
0355 
0356         //
0357         // Updated the share
0358         //
0359         SharePtr existingShare = findShare(share->url(), share->workgroupName());
0360 
0361         if (existingShare) {
0362             //
0363             // Set the share mounted
0364             // Only honor shares that are owned by the user
0365             //
0366             QList<SharePtr> mountedShares = findShareByUrl(share->url());
0367 
0368             if (!mountedShares.isEmpty()) {
0369                 for (const SharePtr &s : qAsConst(mountedShares)) {
0370                     if (!s->isForeign()) {
0371                         share->setMountData(s.data());
0372                         break;
0373                     } else {
0374                         continue;
0375                     }
0376                 }
0377             }
0378 
0379             //
0380             // Update it
0381             //
0382             existingShare->update(share.data());
0383             updated = true;
0384         }
0385 
0386         mutex.unlock();
0387     }
0388 
0389     return updated;
0390 }
0391 
0392 bool Smb4KGlobal::removeShare(SharePtr share)
0393 {
0394     Q_ASSERT(share);
0395 
0396     bool removed = false;
0397 
0398     if (share) {
0399         mutex.lock();
0400 
0401         int index = p->sharesList.indexOf(share);
0402 
0403         if (index != -1) {
0404             // The share was found. Remove it.
0405             p->sharesList.takeAt(index).clear();
0406             removed = true;
0407         } else {
0408             // Try harder to find the share.
0409             SharePtr s = findShare(share->url(), share->workgroupName());
0410 
0411             if (s) {
0412                 index = p->sharesList.indexOf(s);
0413 
0414                 if (index != -1) {
0415                     p->sharesList.takeAt(index).clear();
0416                     removed = true;
0417                 }
0418             }
0419 
0420             share.clear();
0421         }
0422 
0423         mutex.unlock();
0424     }
0425 
0426     return removed;
0427 }
0428 
0429 void Smb4KGlobal::clearSharesList()
0430 {
0431     mutex.lock();
0432 
0433     while (!p->sharesList.isEmpty()) {
0434         p->sharesList.takeFirst().clear();
0435     }
0436 
0437     mutex.unlock();
0438 }
0439 
0440 QList<SharePtr> Smb4KGlobal::sharedResources(HostPtr host)
0441 {
0442     QList<SharePtr> shares;
0443 
0444     mutex.lock();
0445 
0446     for (const SharePtr &s : qAsConst(p->sharesList)) {
0447         if (QString::compare(s->hostName(), host->hostName(), Qt::CaseInsensitive) == 0
0448             && QString::compare(s->workgroupName(), host->workgroupName(), Qt::CaseInsensitive) == 0) {
0449             shares += s;
0450         }
0451     }
0452 
0453     mutex.unlock();
0454 
0455     return shares;
0456 }
0457 
0458 const QList<SharePtr> &Smb4KGlobal::mountedSharesList()
0459 {
0460     return p->mountedSharesList;
0461 }
0462 
0463 SharePtr Smb4KGlobal::findShareByPath(const QString &path)
0464 {
0465     SharePtr share;
0466 
0467     mutex.lock();
0468 
0469     if (!path.isEmpty() && !p->mountedSharesList.isEmpty()) {
0470         for (const SharePtr &s : qAsConst(p->mountedSharesList)) {
0471             if (QString::compare(s->path(), path, Qt::CaseInsensitive) == 0
0472                 || (!s->isInaccessible() && QString::compare(s->canonicalPath(), path, Qt::CaseInsensitive) == 0)) {
0473                 share = s;
0474                 break;
0475             }
0476         }
0477     }
0478 
0479     mutex.unlock();
0480 
0481     return share;
0482 }
0483 
0484 QList<SharePtr> Smb4KGlobal::findShareByUrl(const QUrl &url)
0485 {
0486     QList<SharePtr> shares;
0487 
0488     mutex.lock();
0489 
0490     if (!url.isEmpty() && url.isValid() && !p->mountedSharesList.isEmpty()) {
0491         for (const SharePtr &s : qAsConst(p->mountedSharesList)) {
0492             if (QString::compare(s->url().toString(QUrl::RemoveUserInfo | QUrl::RemovePort),
0493                                  url.toString(QUrl::RemoveUserInfo | QUrl::RemovePort),
0494                                  Qt::CaseInsensitive)
0495                 == 0) {
0496                 shares << s;
0497                 break;
0498             }
0499         }
0500     }
0501 
0502     mutex.unlock();
0503 
0504     return shares;
0505 }
0506 
0507 QList<SharePtr> Smb4KGlobal::findInaccessibleShares()
0508 {
0509     QList<SharePtr> inaccessibleShares;
0510 
0511     mutex.lock();
0512 
0513     for (const SharePtr &s : qAsConst(p->mountedSharesList)) {
0514         if (s->isInaccessible()) {
0515             inaccessibleShares += s;
0516         }
0517     }
0518 
0519     mutex.unlock();
0520 
0521     return inaccessibleShares;
0522 }
0523 
0524 bool Smb4KGlobal::addMountedShare(SharePtr share)
0525 {
0526     Q_ASSERT(share);
0527 
0528     bool added = false;
0529 
0530     if (share) {
0531         mutex.lock();
0532 
0533         //
0534         // Copy the mount data to the network share if available.
0535         // Only honor shares that were mounted by the user.
0536         //
0537         if (!share->isForeign()) {
0538             // Network share
0539             SharePtr networkShare = findShare(share->url(), share->workgroupName());
0540 
0541             if (networkShare) {
0542                 networkShare->setMountData(share.data());
0543             }
0544         }
0545 
0546         if (!findShareByPath(share->path())) {
0547             //
0548             // Check if we have to add a workgroup name and/or IP address
0549             //
0550             HostPtr networkHost = findHost(share->hostName(), share->workgroupName());
0551 
0552             if (networkHost) {
0553                 // Set the IP address
0554                 if (!share->hasHostIpAddress() || networkHost->ipAddress() != share->hostIpAddress()) {
0555                     share->setHostIpAddress(networkHost->ipAddress());
0556                 }
0557 
0558                 // Set the workgroup name
0559                 if (share->workgroupName().isEmpty()) {
0560                     share->setWorkgroupName(networkHost->workgroupName());
0561                 }
0562             }
0563 
0564             p->mountedSharesList.append(share);
0565             added = true;
0566 
0567             p->onlyForeignShares = true;
0568 
0569             for (const SharePtr &s : qAsConst(p->mountedSharesList)) {
0570                 if (!s->isForeign()) {
0571                     p->onlyForeignShares = false;
0572                     break;
0573                 }
0574             }
0575         }
0576 
0577         mutex.unlock();
0578     }
0579 
0580     return added;
0581 }
0582 
0583 bool Smb4KGlobal::updateMountedShare(SharePtr share)
0584 {
0585     Q_ASSERT(share);
0586 
0587     bool updated = false;
0588 
0589     if (share) {
0590         mutex.lock();
0591 
0592         //
0593         // Copy the mount data to the network share (needed for unmounting from the network browser)
0594         // Only honor shares that were mounted by the user.
0595         //
0596         if (!share->isForeign()) {
0597             SharePtr networkShare = findShare(share->url(), share->workgroupName());
0598 
0599             if (networkShare) {
0600                 networkShare->setMountData(share.data());
0601             }
0602         }
0603 
0604         SharePtr mountedShare = findShareByPath(share->path());
0605 
0606         if (mountedShare) {
0607             //
0608             // Check if we have to add a workgroup name and/or IP address
0609             //
0610             HostPtr networkHost = findHost(share->hostName(), share->workgroupName());
0611 
0612             if (networkHost) {
0613                 // Set the IP address
0614                 if (!share->hasHostIpAddress() || networkHost->ipAddress() != share->hostIpAddress()) {
0615                     share->setHostIpAddress(networkHost->ipAddress());
0616                 }
0617 
0618                 // Set the workgroup name
0619                 if (share->workgroupName().isEmpty()) {
0620                     share->setWorkgroupName(networkHost->workgroupName());
0621                 }
0622             }
0623 
0624             //
0625             // Update share
0626             //
0627             mountedShare->setMountData(share.data());
0628             updated = true;
0629         }
0630 
0631         mutex.unlock();
0632     }
0633 
0634     return updated;
0635 }
0636 
0637 bool Smb4KGlobal::removeMountedShare(SharePtr share)
0638 {
0639     Q_ASSERT(share);
0640 
0641     bool removed = false;
0642 
0643     if (share) {
0644         mutex.lock();
0645 
0646         //
0647         // Reset the mount data for the network share and the
0648         // search result
0649         //
0650         if (!share->isForeign()) {
0651             // Network share
0652             SharePtr networkShare = findShare(share->url(), share->workgroupName());
0653 
0654             if (networkShare) {
0655                 networkShare->resetMountData();
0656             }
0657         }
0658 
0659         //
0660         // Remove the mounted share
0661         //
0662         int index = p->mountedSharesList.indexOf(share);
0663 
0664         if (index != -1) {
0665             // The share was found. Remove it.
0666             p->mountedSharesList.takeAt(index).clear();
0667             removed = true;
0668         } else {
0669             // Try harder to find the share.
0670             SharePtr s = findShareByPath(share->isInaccessible() ? share->path() : share->canonicalPath());
0671 
0672             if (s) {
0673                 index = p->mountedSharesList.indexOf(s);
0674 
0675                 if (index != -1) {
0676                     p->mountedSharesList.takeAt(index).clear();
0677                     removed = true;
0678                 }
0679             }
0680 
0681             share.clear();
0682         }
0683 
0684         for (const SharePtr &s : qAsConst(p->mountedSharesList)) {
0685             if (!s->isForeign()) {
0686                 p->onlyForeignShares = false;
0687                 break;
0688             }
0689         }
0690 
0691         mutex.unlock();
0692     }
0693 
0694     return removed;
0695 }
0696 
0697 bool Smb4KGlobal::onlyForeignMountedShares()
0698 {
0699     return p->onlyForeignShares;
0700 }
0701 
0702 void Smb4KGlobal::openShare(SharePtr share, OpenWith openWith)
0703 {
0704     if (!share || share->isInaccessible()) {
0705         return;
0706     }
0707 
0708     switch (openWith) {
0709     case FileManager: {
0710         QUrl url = QUrl::fromLocalFile(share->canonicalPath());
0711 
0712         KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url);
0713         job->setFollowRedirections(false);
0714         job->setAutoDelete(true);
0715         job->start();
0716 
0717         break;
0718     }
0719     case Konsole: {
0720         QString konsole = QStandardPaths::findExecutable(QStringLiteral("konsole"));
0721 
0722         if (!konsole.isEmpty()) {
0723             KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(konsole);
0724             job->setWorkingDirectory(share->canonicalPath());
0725             job->setAutoDelete(true);
0726             job->start();
0727         } else {
0728             Smb4KNotification::commandNotFound(QStringLiteral("konsole"));
0729         }
0730 
0731         break;
0732     }
0733     default: {
0734         break;
0735     }
0736     }
0737 }
0738 
0739 const QString Smb4KGlobal::machineNetbiosName()
0740 {
0741     return p->machineNetbiosName;
0742 }
0743 
0744 const QString Smb4KGlobal::machineWorkgroupName()
0745 {
0746     return p->machineWorkgroupName;
0747 }
0748 
0749 #if defined(Q_OS_LINUX)
0750 QStringList Smb4KGlobal::allowedMountArguments()
0751 {
0752     return p->allowedMountArguments;
0753 }
0754 #endif
0755 
0756 const QString Smb4KGlobal::findMountExecutable()
0757 {
0758     QStringList paths;
0759     paths << QStringLiteral("/bin");
0760     paths << QStringLiteral("/sbin");
0761     paths << QStringLiteral("/usr/bin");
0762     paths << QStringLiteral("/usr/sbin");
0763     paths << QStringLiteral("/usr/local/bin");
0764     paths << QStringLiteral("/usr/local/sbin");
0765 
0766 #if defined(Q_OS_LINUX)
0767     return QStandardPaths::findExecutable(QStringLiteral("mount.cifs"), paths);
0768 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0769     return QStandardPaths::findExecutable(QStringLiteral("mount_smbfs"), paths);
0770 #else
0771     return QString();
0772 #endif
0773 }
0774 
0775 const QString Smb4KGlobal::findUmountExecutable()
0776 {
0777     QStringList paths;
0778     paths << QStringLiteral("/bin");
0779     paths << QStringLiteral("/sbin");
0780     paths << QStringLiteral("/usr/bin");
0781     paths << QStringLiteral("/usr/sbin");
0782     paths << QStringLiteral("/usr/local/bin");
0783     paths << QStringLiteral("/usr/local/sbin");
0784 
0785     return QStandardPaths::findExecutable(QStringLiteral("umount"), paths);
0786 }
0787 
0788 const QString Smb4KGlobal::dataLocation()
0789 {
0790     return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + QStringLiteral("smb4k");
0791 }
0792 
0793 void Smb4KGlobal::wait(int time)
0794 {
0795     QEventLoop loop;
0796     QTimer::singleShot(time, &loop, SLOT(quit()));
0797     loop.exec();
0798 }