1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6*/
7
8#include "knfsshare.h"
9
10#include "../utils_p.h"
11
12#include <QDebug>
13#include <QFile>
14#include <QFileInfo>
15#include <QSet>
16#include <QTextStream>
17
18#include "kiocoredebug.h"
19#include <KConfig>
20#include <KConfigGroup>
21#include <KDirWatch>
22
23class Q_DECL_HIDDEN KNFSShare::KNFSSharePrivate
24{
25public:
26 explicit KNFSSharePrivate(KNFSShare *parent);
27
28 void slotFileChange(const QString &);
29
30 bool readExportsFile();
31 bool findExportsFile();
32
33 KNFSShare *const q;
34 QSet<QString> sharedPaths;
35 QString exportsFile;
36};
37
38KNFSShare::KNFSSharePrivate::KNFSSharePrivate(KNFSShare *parent)
39 : q(parent)
40{
41 if (findExportsFile()) {
42 readExportsFile();
43 }
44}
45
46/**
47 * Try to find the nfs config file path
48 * First tries the kconfig, then checks
49 * several well-known paths
50 * @return whether an 'exports' file was found.
51 **/
52bool KNFSShare::KNFSSharePrivate::findExportsFile()
53{
54 KConfig knfsshare(QStringLiteral("knfsshare"));
55 KConfigGroup config(&knfsshare, QStringLiteral("General"));
56 exportsFile = config.readPathEntry(key: "exportsFile", aDefault: QString());
57
58 if (!exportsFile.isEmpty() && QFileInfo::exists(file: exportsFile)) {
59 return true;
60 }
61
62 if (QFile::exists(QStringLiteral("/etc/exports"))) {
63 exportsFile = QStringLiteral("/etc/exports");
64 } else {
65 // qDebug() << "Could not find exports file! /etc/exports doesn't exist. Configure it in share/config/knfsshare, [General], exportsFile=....";
66 return false;
67 }
68
69 config.writeEntry(key: "exportsFile", value: exportsFile);
70 return true;
71}
72
73/**
74 * Reads all paths from the exports file
75 * and fills the sharedPaths dict with the values
76 */
77bool KNFSShare::KNFSSharePrivate::readExportsFile()
78{
79 QFile f(exportsFile);
80
81 // qDebug() << exportsFile;
82
83 if (!f.open(flags: QIODevice::ReadOnly)) {
84 qCWarning(KIO_CORE) << "KNFSShare: Could not open" << exportsFile;
85 return false;
86 }
87
88 sharedPaths.clear();
89
90 QTextStream s(&f);
91
92 bool continuedLine = false; // is true if the line before ended with a backslash
93 QString completeLine;
94
95 while (!s.atEnd()) {
96 QString currentLine = s.readLine().trimmed();
97
98 if (continuedLine) {
99 completeLine += currentLine;
100 continuedLine = false;
101 } else {
102 completeLine = currentLine;
103 }
104
105 // is the line continued in the next line ?
106 if (completeLine.endsWith(c: QLatin1Char('\\'))) {
107 continuedLine = true;
108 // remove the ending backslash
109 completeLine.chop(n: 1);
110 continue;
111 }
112
113 // comments or empty lines
114 if (completeLine.startsWith(c: QLatin1Char('#')) || completeLine.isEmpty()) {
115 continue;
116 }
117
118 QString path;
119
120 // Handle quotation marks
121 if (completeLine[0] == QLatin1Char('\"')) {
122 int i = completeLine.indexOf(c: QLatin1Char('"'), from: 1);
123 if (i == -1) {
124 qCWarning(KIO_CORE) << "KNFSShare: Parse error: Missing quotation mark:" << completeLine;
125 continue;
126 }
127 path = completeLine.mid(position: 1, n: i - 1);
128
129 } else { // no quotation marks
130 int i = completeLine.indexOf(c: QLatin1Char(' '));
131 if (i == -1) {
132 i = completeLine.indexOf(c: QLatin1Char('\t'));
133 }
134
135 if (i == -1) {
136 path = completeLine;
137 } else {
138 path = completeLine.left(n: i);
139 }
140 }
141
142 // qDebug() << "KNFSShare: Found path: " << path;
143
144 if (!path.isEmpty()) {
145 // Append a '/' to normalize path
146 Utils::appendSlash(path);
147 sharedPaths.insert(value: path);
148 }
149 }
150
151 return true;
152}
153
154KNFSShare::KNFSShare()
155 : d(new KNFSSharePrivate(this))
156{
157 if (!d->exportsFile.isEmpty() && QFileInfo::exists(file: d->exportsFile)) {
158 KDirWatch::self()->addFile(file: d->exportsFile);
159 connect(sender: KDirWatch::self(), signal: &KDirWatch::dirty, context: this, slot: [this](const QString &path) {
160 d->slotFileChange(path);
161 });
162 }
163}
164
165KNFSShare::~KNFSShare() = default;
166
167bool KNFSShare::isDirectoryShared(const QString &path) const
168{
169 if (path.isEmpty()) {
170 return false;
171 }
172
173 return d->sharedPaths.contains(value: Utils::slashAppended(s: path));
174}
175
176QStringList KNFSShare::sharedDirectories() const
177{
178 return d->sharedPaths.values();
179}
180
181QString KNFSShare::exportsPath() const
182{
183 return d->exportsFile;
184}
185
186void KNFSShare::KNFSSharePrivate::slotFileChange(const QString &path)
187{
188 if (path == exportsFile) {
189 readExportsFile();
190 }
191
192 Q_EMIT q->changed();
193}
194
195class KNFSShareSingleton
196{
197public:
198 KNFSShare instance;
199};
200
201Q_GLOBAL_STATIC(KNFSShareSingleton, _instance)
202
203KNFSShare *KNFSShare::instance()
204{
205 return &_instance()->instance;
206}
207
208#include "moc_knfsshare.cpp"
209

source code of kio/src/core/knfsshare.cpp