File indexing completed on 2024-05-12 03:54:55

0001 /*
0002     This software is a contribution of the LiMux project of the city of Munich.
0003     SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "knetworkmounts.h"
0009 #include "knetworkmounts_p.h"
0010 
0011 #include <QCoreApplication>
0012 #include <QGlobalStatic>
0013 
0014 #include <QDebug>
0015 #include <QDir>
0016 #include <QStandardPaths>
0017 
0018 KNetworkMountsPrivate::KNetworkMountsPrivate(KNetworkMounts *qq)
0019     : q(qq)
0020 {
0021 }
0022 
0023 KNetworkMounts *KNetworkMounts::self()
0024 {
0025     static KNetworkMounts s_self;
0026     return &s_self;
0027 }
0028 
0029 KNetworkMounts::KNetworkMounts()
0030     : d(new KNetworkMountsPrivate(this))
0031 {
0032     const QString configFileName = QStringLiteral("%1/network_mounts").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
0033     d->m_settings = new QSettings(configFileName, QSettings::Format::IniFormat, this);
0034 
0035     for (const auto type : {KNetworkMounts::NfsPaths, KNetworkMounts::SmbPaths, KNetworkMounts::SymlinkDirectory, KNetworkMounts::SymlinkToNetworkMount}) {
0036         QString typeStr = enumToString(type);
0037         QStringList slowPaths = d->m_settings->value(typeStr, QStringList()).toStringList();
0038 
0039         if (ensureTrailingSlashes(&slowPaths)) {
0040             d->m_settings->setValue(typeStr, slowPaths);
0041         }
0042     }
0043 }
0044 
0045 KNetworkMounts::~KNetworkMounts()
0046 {
0047 }
0048 
0049 bool KNetworkMounts::isSlowPath(const QString &path, KNetworkMountsType type)
0050 {
0051     return !getMatchingPath(path, paths(type)).isEmpty();
0052 }
0053 
0054 bool KNetworkMounts::isOptionEnabledForPath(const QString &path, KNetworkMountOption option)
0055 {
0056     if (!isEnabled()) {
0057         return false;
0058     }
0059 
0060     if (!isSlowPath(path)) {
0061         return false;
0062     }
0063 
0064     return isOptionEnabled(option, true);
0065 }
0066 
0067 bool KNetworkMounts::isEnabled() const
0068 {
0069     return d->m_settings->value(QStringLiteral("EnableOptimizations"), false).toBool();
0070 }
0071 
0072 void KNetworkMounts::setEnabled(const bool value)
0073 {
0074     d->m_settings->setValue(QStringLiteral("EnableOptimizations"), value);
0075 }
0076 
0077 bool KNetworkMounts::isOptionEnabled(const KNetworkMountOption option, const bool defaultValue) const
0078 {
0079     return d->m_settings->value(enumToString(option), defaultValue).toBool();
0080 }
0081 
0082 void KNetworkMounts::setOption(const KNetworkMountOption option, const bool value)
0083 {
0084     d->m_settings->setValue(enumToString(option), value);
0085 }
0086 
0087 QStringList KNetworkMounts::paths(KNetworkMountsType type) const
0088 {
0089     if (type == Any) {
0090         QStringList paths;
0091         paths.reserve(4);
0092         for (const auto networkMountType :
0093              {KNetworkMounts::NfsPaths, KNetworkMounts::SmbPaths, KNetworkMounts::SymlinkDirectory, KNetworkMounts::SymlinkToNetworkMount}) {
0094             paths.append(d->m_settings->value(enumToString(networkMountType), QStringList()).toStringList());
0095         }
0096         return paths;
0097     } else {
0098         return d->m_settings->value(enumToString(type), QStringList()).toStringList();
0099     }
0100 }
0101 
0102 void KNetworkMounts::setPaths(const QStringList &paths, KNetworkMountsType type)
0103 {
0104     QStringList _paths(paths);
0105     ensureTrailingSlashes(&_paths);
0106     d->m_settings->setValue(enumToString(type), _paths);
0107 }
0108 
0109 void KNetworkMounts::addPath(const QString &path, KNetworkMountsType type)
0110 {
0111     QString _path(path);
0112     ensureTrailingSlash(&_path);
0113     QStringList newPaths = paths(type);
0114     newPaths.append(_path);
0115     d->m_settings->setValue(enumToString(type), newPaths);
0116 }
0117 
0118 typedef QHash<QString /*symlink*/, QString /*canonical path*/> symlinkCanonicalPathHash;
0119 Q_GLOBAL_STATIC(symlinkCanonicalPathHash, s_canonicalLinkSpacePaths)
0120 
0121 QString KNetworkMounts::canonicalSymlinkPath(const QString &path)
0122 {
0123     bool useCache = isOptionEnabled(KNetworkMountOption::SymlinkPathsUseCache, true);
0124     if (useCache) {
0125         const QString resolved = s_canonicalLinkSpacePaths->value(path);
0126 
0127         if (!resolved.isEmpty()) {
0128             return resolved;
0129         }
0130     }
0131 
0132     QString symlinkPath = getMatchingPath(path, paths(KNetworkMountsType::SymlinkToNetworkMount));
0133     if (!symlinkPath.isEmpty()) {
0134         // remove trailing slash
0135         symlinkPath.chop(1);
0136 
0137         QFileInfo link(symlinkPath);
0138         QString linkPath(path);
0139         QString target = link.symLinkTarget();
0140 
0141         if (target.isEmpty()) {
0142             // not a symlink
0143             if (useCache) {
0144                 s_canonicalLinkSpacePaths->insert(path, path);
0145             }
0146             return path;
0147         } else {
0148             // symlink
0149             // replace only the first occurence of symlinkPath in linkPath with the link target
0150             // linkPath.startsWith(symlinkPath) because of getMatchingPath
0151             linkPath.replace(0, symlinkPath.size(), target);
0152 
0153             if (useCache) {
0154                 s_canonicalLinkSpacePaths->insert(path, linkPath);
0155             }
0156             return linkPath;
0157         }
0158     }
0159 
0160     QString linkSpacePath = getMatchingPath(path, paths(KNetworkMountsType::SymlinkDirectory));
0161     if (!linkSpacePath.isEmpty()) {
0162         QString _path = path;
0163         if (!_path.endsWith(QLatin1Char('/'))) {
0164             _path.append(QLatin1Char('/'));
0165         }
0166 
0167         if (_path == linkSpacePath) {
0168             if (useCache) {
0169                 s_canonicalLinkSpacePaths->insert(path, path);
0170             }
0171             return path;
0172         }
0173 
0174         // search for symlink, linkSpacePath always ends with '/'
0175         int linkIndex = path.indexOf(QLatin1Char('/'), linkSpacePath.length());
0176         const QString symlink = path.left(linkIndex);
0177 
0178         if (useCache && s_canonicalLinkSpacePaths->contains(symlink)) {
0179             QString linkPath(path);
0180             // replace only the first occurence of symlink in linkPath
0181             linkPath.replace(0, symlink.size(), s_canonicalLinkSpacePaths->value(symlink));
0182             s_canonicalLinkSpacePaths->insert(path, linkPath);
0183             return linkPath;
0184         } else {
0185             QFileInfo link(symlink);
0186 
0187             if (link.isSymLink()) {
0188                 QString linkPath(path);
0189                 // replace only the first occurence of symlink in linkPath
0190                 linkPath.replace(0, symlink.size(), link.symLinkTarget());
0191 
0192                 if (useCache) {
0193                     s_canonicalLinkSpacePaths->insert(path, linkPath);
0194                 }
0195                 return linkPath;
0196             } else {
0197                 if (useCache) {
0198                     s_canonicalLinkSpacePaths->insert(path, path);
0199                 }
0200             }
0201         }
0202     }
0203 
0204     return path;
0205 }
0206 
0207 void KNetworkMounts::clearCache()
0208 {
0209     if (s_canonicalLinkSpacePaths.exists()) {
0210         s_canonicalLinkSpacePaths->clear();
0211     }
0212 }
0213 
0214 void KNetworkMounts::sync()
0215 {
0216     d->m_settings->sync();
0217 }
0218 
0219 #include "moc_knetworkmounts.cpp"