Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmljsresourcefilemapper.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant
4
6
7#include <QFileInfo>
8#include <QDir>
9#include <QXmlStreamReader>
10
12
13QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::allQmlJSFilter() {
14 return Filter {
15 QString(),
16 QStringList { QStringLiteral("qml"), QStringLiteral("js"), QStringLiteral("mjs") },
17 Directory | Recurse
18 };
19}
20
21QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::localFileFilter(const QString &file)
22{
23 return Filter {
24 QDir::cleanPath(QFileInfo(file).absoluteFilePath()),
25 QStringList(),
26 File
27 };
28}
29
30QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::resourceFileFilter(const QString &file)
31{
32 return Filter {
33 file,
34 QStringList(),
35 Resource
36 };
37}
38
39QQmlJSResourceFileMapper::Filter QQmlJSResourceFileMapper::resourceQmlDirectoryFilter(
40 const QString &directory)
41{
42 return Filter {
43 directory,
44 QStringList { QStringLiteral("qml") },
45 Directory | Resource
46 };
47}
48
49QQmlJSResourceFileMapper::QQmlJSResourceFileMapper(const QStringList &resourceFiles)
50{
51 for (const QString &fileName: resourceFiles) {
52 QFile f(fileName);
53 if (!f.open(QIODevice::ReadOnly))
54 continue;
55 populateFromQrcFile(f);
56 }
57}
58
59bool QQmlJSResourceFileMapper::isEmpty() const
60{
61 return qrcPathToFileSystemPath.isEmpty();
62}
63
64bool QQmlJSResourceFileMapper::isFile(QStringView resourcePath) const
65{
66 for (const auto &entry : qrcPathToFileSystemPath) {
67 if (entry.resourcePath == resourcePath)
68 return true;
69 }
70 return false;
71}
72
73static bool hasSuffix(const QString &qrcPath, const QStringList &suffixes)
74{
75 if (suffixes.isEmpty())
76 return true;
77 const QString suffix = QFileInfo(qrcPath).suffix();
78 return suffixes.contains(suffix);
79}
80
81template<typename HandleMatch>
82void doFilter(const QList<QQmlJSResourceFileMapper::Entry> &qrcPathToFileSystemPath,
83 const QQmlJSResourceFileMapper::Filter &filter,
84 const HandleMatch &handler)
85{
86 if (filter.flags & QQmlJSResourceFileMapper::Directory) {
87 const QString terminatedDirectory = filter.path.endsWith(u'/')
88 ? filter.path : (filter.path + u'/');
89
90 for (auto it = qrcPathToFileSystemPath.constBegin(),
91 end = qrcPathToFileSystemPath.constEnd(); it != end; ++it) {
92
93 const QString candidate = (filter.flags & QQmlJSResourceFileMapper::Resource)
94 ? it->resourcePath
95 : it->filePath;
96
97 if (!filter.path.isEmpty() && !candidate.startsWith(terminatedDirectory))
98 continue;
99
100 if (!hasSuffix(candidate, filter.suffixes))
101 continue;
102
103 if ((filter.flags & QQmlJSResourceFileMapper::Recurse)
104 // Crude. But shall we really allow slashes in QRC file names?
105 || !candidate.mid(terminatedDirectory.size()).contains(u'/')) {
106 if (handler(*it))
107 return;
108 }
109 }
110 return;
111 }
112
113 if (!hasSuffix(filter.path, filter.suffixes))
114 return;
115
116 for (auto it = qrcPathToFileSystemPath.constBegin(),
117 end = qrcPathToFileSystemPath.constEnd(); it != end; ++it) {
118 if (filter.flags & QQmlJSResourceFileMapper::Resource) {
119 if (it->resourcePath == filter.path && handler(*it))
120 return;
121 } else if (it->filePath == filter.path && handler(*it)) {
122 return;
123 }
124 }
125}
126
127QList<QQmlJSResourceFileMapper::Entry> QQmlJSResourceFileMapper::filter(
128 const QQmlJSResourceFileMapper::Filter &filter) const
129{
130 QList<Entry> result;
131 doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
132 result.append(entry);
133 return false;
134 });
135 return result;
136}
137
138QStringList QQmlJSResourceFileMapper::filePaths(
139 const QQmlJSResourceFileMapper::Filter &filter) const
140{
141 QStringList result;
142 doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
143 result.append(entry.filePath);
144 return false;
145 });
146 return result;
147}
148
149QStringList QQmlJSResourceFileMapper::resourcePaths(
150 const QQmlJSResourceFileMapper::Filter &filter) const
151{
152 QStringList result;
153 doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
154 result.append(entry.resourcePath);
155 return false;
156 });
157 return result;
158}
159
160QQmlJSResourceFileMapper::Entry QQmlJSResourceFileMapper::entry(
161 const QQmlJSResourceFileMapper::Filter &filter) const
162{
163 Entry result;
164 doFilter(qrcPathToFileSystemPath, filter, [&](const Entry &entry) {
165 result = entry;
166 return true;
167 });
168 return result;
169}
170
171void QQmlJSResourceFileMapper::populateFromQrcFile(QFile &file)
172{
173 enum State {
174 InitialState,
175 InRCC,
176 InResource,
177 InFile
178 };
179 State state = InitialState;
180
181 QDir qrcDir = QFileInfo(file).absoluteDir();
182
183 QString prefix;
184 QString currentFileName;
185 QXmlStreamAttributes currentFileAttributes;
186
187 QXmlStreamReader reader(&file);
188 while (!reader.atEnd()) {
189 switch (reader.readNext()) {
190 case QXmlStreamReader::StartElement:
191 if (reader.name() == QStringLiteral("RCC")) {
192 if (state != InitialState)
193 return;
194 state = InRCC;
195 continue;
196 } else if (reader.name() == QStringLiteral("qresource")) {
197 if (state != InRCC)
198 return;
199 state = InResource;
200 QXmlStreamAttributes attributes = reader.attributes();
201 if (attributes.hasAttribute(QStringLiteral("prefix")))
202 prefix = attributes.value(QStringLiteral("prefix")).toString();
203 if (!prefix.startsWith(QLatin1Char('/')))
204 prefix.prepend(QLatin1Char('/'));
205 if (!prefix.endsWith(QLatin1Char('/')))
206 prefix.append(QLatin1Char('/'));
207 continue;
208 } else if (reader.name() == QStringLiteral("file")) {
209 if (state != InResource)
210 return;
211 state = InFile;
212 currentFileAttributes = reader.attributes();
213 continue;
214 }
215 return;
216
217 case QXmlStreamReader::EndElement:
218 if (reader.name() == QStringLiteral("file")) {
219 if (state != InFile)
220 return;
221 state = InResource;
222 continue;
223 } else if (reader.name() == QStringLiteral("qresource")) {
224 if (state != InResource)
225 return;
226 state = InRCC;
227 continue;
228 } else if (reader.name() == QStringLiteral("RCC")) {
229 if (state != InRCC)
230 return;
231 state = InitialState;
232 continue;
233 }
234 return;
235
236 case QXmlStreamReader::Characters: {
237 if (reader.isWhitespace())
238 break;
239 if (state != InFile)
240 return;
241 currentFileName = reader.text().toString();
242 if (currentFileName.isEmpty())
243 continue;
244
245 const QString fsPath = QDir::cleanPath(qrcDir.absoluteFilePath(currentFileName));
246
247 if (currentFileAttributes.hasAttribute(QStringLiteral("alias")))
248 currentFileName = currentFileAttributes.value(QStringLiteral("alias")).toString();
249
250 currentFileName = QDir::cleanPath(currentFileName);
251 while (currentFileName.startsWith(QLatin1String("../")))
252 currentFileName.remove(0, 3);
253
254 const QString qrcPath = prefix + currentFileName;
255 if (QFile::exists(fsPath))
256 qrcPathToFileSystemPath.append({qrcPath, fsPath});
257 continue;
258 }
259
260 default: break;
261 }
262 }
263}
264
265QT_END_NAMESPACE
static bool hasSuffix(const QString &qrcPath, const QStringList &suffixes)
void doFilter(const QList< QQmlJSResourceFileMapper::Entry > &qrcPathToFileSystemPath, const QQmlJSResourceFileMapper::Filter &filter, const HandleMatch &handler)