File indexing completed on 2024-04-14 03:52:57

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "knfsshare.h"
0009 
0010 #include "../utils_p.h"
0011 
0012 #include <QDebug>
0013 #include <QFile>
0014 #include <QFileInfo>
0015 #include <QSet>
0016 #include <QTextStream>
0017 
0018 #include "kiocoredebug.h"
0019 #include <KConfig>
0020 #include <KConfigGroup>
0021 #include <KDirWatch>
0022 
0023 class Q_DECL_HIDDEN KNFSShare::KNFSSharePrivate
0024 {
0025 public:
0026     explicit KNFSSharePrivate(KNFSShare *parent);
0027 
0028     void slotFileChange(const QString &);
0029 
0030     bool readExportsFile();
0031     bool findExportsFile();
0032 
0033     KNFSShare *const q;
0034     QSet<QString> sharedPaths;
0035     QString exportsFile;
0036 };
0037 
0038 KNFSShare::KNFSSharePrivate::KNFSSharePrivate(KNFSShare *parent)
0039     : q(parent)
0040 {
0041     if (findExportsFile()) {
0042         readExportsFile();
0043     }
0044 }
0045 
0046 /**
0047  * Try to find the nfs config file path
0048  * First tries the kconfig, then checks
0049  * several well-known paths
0050  * @return whether an 'exports' file was found.
0051  **/
0052 bool KNFSShare::KNFSSharePrivate::findExportsFile()
0053 {
0054     KConfig knfsshare(QStringLiteral("knfsshare"));
0055     KConfigGroup config(&knfsshare, QStringLiteral("General"));
0056     exportsFile = config.readPathEntry("exportsFile", QString());
0057 
0058     if (!exportsFile.isEmpty() && QFileInfo::exists(exportsFile)) {
0059         return true;
0060     }
0061 
0062     if (QFile::exists(QStringLiteral("/etc/exports"))) {
0063         exportsFile = QStringLiteral("/etc/exports");
0064     } else {
0065         // qDebug() << "Could not find exports file! /etc/exports doesn't exist. Configure it in share/config/knfsshare, [General], exportsFile=....";
0066         return false;
0067     }
0068 
0069     config.writeEntry("exportsFile", exportsFile);
0070     return true;
0071 }
0072 
0073 /**
0074  * Reads all paths from the exports file
0075  * and fills the sharedPaths dict with the values
0076  */
0077 bool KNFSShare::KNFSSharePrivate::readExportsFile()
0078 {
0079     QFile f(exportsFile);
0080 
0081     // qDebug() << exportsFile;
0082 
0083     if (!f.open(QIODevice::ReadOnly)) {
0084         qCWarning(KIO_CORE) << "KNFSShare: Could not open" << exportsFile;
0085         return false;
0086     }
0087 
0088     sharedPaths.clear();
0089 
0090     QTextStream s(&f);
0091 
0092     bool continuedLine = false; // is true if the line before ended with a backslash
0093     QString completeLine;
0094 
0095     while (!s.atEnd()) {
0096         QString currentLine = s.readLine().trimmed();
0097 
0098         if (continuedLine) {
0099             completeLine += currentLine;
0100             continuedLine = false;
0101         } else {
0102             completeLine = currentLine;
0103         }
0104 
0105         // is the line continued in the next line ?
0106         if (completeLine.endsWith(QLatin1Char('\\'))) {
0107             continuedLine = true;
0108             // remove the ending backslash
0109             completeLine.chop(1);
0110             continue;
0111         }
0112 
0113         // comments or empty lines
0114         if (completeLine.startsWith(QLatin1Char('#')) || completeLine.isEmpty()) {
0115             continue;
0116         }
0117 
0118         QString path;
0119 
0120         // Handle quotation marks
0121         if (completeLine[0] == QLatin1Char('\"')) {
0122             int i = completeLine.indexOf(QLatin1Char('"'), 1);
0123             if (i == -1) {
0124                 qCWarning(KIO_CORE) << "KNFSShare: Parse error: Missing quotation mark:" << completeLine;
0125                 continue;
0126             }
0127             path = completeLine.mid(1, i - 1);
0128 
0129         } else { // no quotation marks
0130             int i = completeLine.indexOf(QLatin1Char(' '));
0131             if (i == -1) {
0132                 i = completeLine.indexOf(QLatin1Char('\t'));
0133             }
0134 
0135             if (i == -1) {
0136                 path = completeLine;
0137             } else {
0138                 path = completeLine.left(i);
0139             }
0140         }
0141 
0142         // qDebug() << "KNFSShare: Found path: " << path;
0143 
0144         if (!path.isEmpty()) {
0145             // Append a '/' to normalize path
0146             Utils::appendSlash(path);
0147             sharedPaths.insert(path);
0148         }
0149     }
0150 
0151     return true;
0152 }
0153 
0154 KNFSShare::KNFSShare()
0155     : d(new KNFSSharePrivate(this))
0156 {
0157     if (!d->exportsFile.isEmpty() && QFileInfo::exists(d->exportsFile)) {
0158         KDirWatch::self()->addFile(d->exportsFile);
0159         connect(KDirWatch::self(), &KDirWatch::dirty, this, [this](const QString &path) {
0160             d->slotFileChange(path);
0161         });
0162     }
0163 }
0164 
0165 KNFSShare::~KNFSShare() = default;
0166 
0167 bool KNFSShare::isDirectoryShared(const QString &path) const
0168 {
0169     if (path.isEmpty()) {
0170         return false;
0171     }
0172 
0173     return d->sharedPaths.contains(Utils::slashAppended(path));
0174 }
0175 
0176 QStringList KNFSShare::sharedDirectories() const
0177 {
0178     return d->sharedPaths.values();
0179 }
0180 
0181 QString KNFSShare::exportsPath() const
0182 {
0183     return d->exportsFile;
0184 }
0185 
0186 void KNFSShare::KNFSSharePrivate::slotFileChange(const QString &path)
0187 {
0188     if (path == exportsFile) {
0189         readExportsFile();
0190     }
0191 
0192     Q_EMIT q->changed();
0193 }
0194 
0195 class KNFSShareSingleton
0196 {
0197 public:
0198     KNFSShare instance;
0199 };
0200 
0201 Q_GLOBAL_STATIC(KNFSShareSingleton, _instance)
0202 
0203 KNFSShare *KNFSShare::instance()
0204 {
0205     return &_instance()->instance;
0206 }
0207 
0208 #include "moc_knfsshare.cpp"