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 | |
23 | class Q_DECL_HIDDEN KNFSShare::KNFSSharePrivate |
24 | { |
25 | public: |
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 | |
38 | KNFSShare::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 | **/ |
52 | bool 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 | */ |
77 | bool 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 | |
154 | KNFSShare::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 | |
165 | KNFSShare::~KNFSShare() = default; |
166 | |
167 | bool 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 | |
176 | QStringList KNFSShare::sharedDirectories() const |
177 | { |
178 | return d->sharedPaths.values(); |
179 | } |
180 | |
181 | QString KNFSShare::exportsPath() const |
182 | { |
183 | return d->exportsFile; |
184 | } |
185 | |
186 | void KNFSShare::KNFSSharePrivate::slotFileChange(const QString &path) |
187 | { |
188 | if (path == exportsFile) { |
189 | readExportsFile(); |
190 | } |
191 | |
192 | Q_EMIT q->changed(); |
193 | } |
194 | |
195 | class KNFSShareSingleton |
196 | { |
197 | public: |
198 | KNFSShare instance; |
199 | }; |
200 | |
201 | Q_GLOBAL_STATIC(KNFSShareSingleton, _instance) |
202 | |
203 | KNFSShare *KNFSShare::instance() |
204 | { |
205 | return &_instance()->instance; |
206 | } |
207 | |
208 | #include "moc_knfsshare.cpp" |
209 | |