KIO

kshorturifilter.cpp
1/*
2 kshorturifilter.h
3
4 This file is part of the KDE project
5 SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org>
6 SPDX-FileCopyrightText: 2000 Malte Starostik <starosti@zedat.fu-berlin.de>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include "kshorturifilter.h"
12#include "../utils_p.h"
13
14#ifdef WITH_QTDBUS
15#include <QDBusConnection>
16#endif
17
18#include <QDir>
19#include <QLoggingCategory>
20#include <qplatformdefs.h>
21
22#include <KApplicationTrader>
23#include <KConfig>
24#include <KConfigGroup>
25#include <KLocalizedString>
26#include <KPluginFactory>
27#include <KService>
28#include <KUser>
29#include <kprotocolinfo.h>
30#include <kurlauthorized.h>
31
32namespace
33{
34Q_LOGGING_CATEGORY(category, "kf.kio.urifilters.shorturi", QtWarningMsg)
35}
36
37static bool isPotentialShortURL(const QString &cmd)
38{
39 // Host names and IPv4 address...
40 // Exclude ".." and paths starting with "../", these are used to go up in a filesystem
41 // dir hierarchy
42 if (cmd != QLatin1String("..") && !cmd.startsWith(QLatin1String("../")) && cmd.contains(QLatin1Char('.'))) {
43 return true;
44 }
45
46 // IPv6 Address...
47 if (cmd.startsWith(QLatin1Char('[')) && cmd.contains(QLatin1Char(':'))) {
48 return true;
49 }
50
51 return false;
52}
53
54static QString removeArgs(const QString &_cmd)
55{
56 QString cmd(_cmd);
57
58 if (cmd.isEmpty()) {
59 return cmd;
60 }
61
62 if (cmd[0] != QLatin1Char('\'') && cmd[0] != QLatin1Char('"')) {
63 // Remove command-line options (look for first non-escaped space)
64 int spacePos = 0;
65
66 do {
67 spacePos = cmd.indexOf(QLatin1Char(' '), spacePos + 1);
68 } while (spacePos > 1 && cmd[spacePos - 1] == QLatin1Char('\\'));
69
70 if (spacePos > 0) {
71 cmd.truncate(spacePos);
72 qCDebug(category) << "spacePos=" << spacePos << " returning " << cmd;
73 }
74 }
75
76 return cmd;
77}
78
79static bool isKnownProtocol(const QString &protocol)
80{
81 if (KProtocolInfo::isKnownProtocol(protocol, false) || protocol == QLatin1String("mailto")) {
82 return true;
83 }
84 const KService::Ptr service = KApplicationTrader::preferredService(QLatin1String("x-scheme-handler/") + protocol);
85 return service;
86}
87
88KShortUriFilter::KShortUriFilter(QObject *parent, const KPluginMetaData &data)
89 : KUriFilterPlugin(parent, data)
90{
91#ifdef WITH_QTDBUS
93 .connect(QString(), QStringLiteral("/"), QStringLiteral("org.kde.KUriFilterPlugin"), QStringLiteral("configure"), this, SLOT(configure()));
94#endif
95
96 configure();
97}
98
100{
101 /*
102 * Here is a description of how the shortURI deals with the supplied
103 * data. First it expands any environment variable settings and then
104 * deals with special shortURI cases. These special cases are the "smb:"
105 * URL scheme which is very specific to KDE, "#" and "##" which are
106 * shortcuts for man:/ and info:/ protocols respectively. It then handles
107 * local files. Then it checks to see if the URL is valid and one that is
108 * supported by KDE's IO system. If all the above checks fails, it simply
109 * lookups the URL in the user-defined list and returns without filtering
110 * if it is not found. TODO: the user-defined table is currently only manually
111 * hackable and is missing a config dialog.
112 */
113
114 // QUrl url = data.uri();
115 QString cmd = data.typedString();
116
117 int firstNonSlash = 0;
118 while (firstNonSlash < cmd.length() && (cmd.at(firstNonSlash) == QLatin1Char('/'))) {
119 firstNonSlash++;
120 }
121 if (firstNonSlash > 1) {
122 cmd.remove(0, firstNonSlash - 1);
123 }
124
125 // Replicate what KUrl(cmd) did in KDE4. This could later be folded into the checks further down...
126 QUrl url;
127 if (Utils::isAbsoluteLocalPath(cmd)) {
128 url = QUrl::fromLocalFile(cmd);
129 } else {
130 url.setUrl(cmd);
131 }
132
133 // WORKAROUND: Allow the use of '@' in the username component of a URL since
134 // other browsers such as firefox in their infinite wisdom allow such blatant
135 // violations of RFC 3986. BR# 69326/118413.
136 if (cmd.count(QLatin1Char('@')) > 1) {
137 const int lastIndex = cmd.lastIndexOf(QLatin1Char('@'));
138 // Percent encode all but the last '@'.
139 const auto suffix = QStringView(cmd).mid(lastIndex);
140 cmd = QString::fromUtf8(QUrl::toPercentEncoding(cmd.left(lastIndex), QByteArrayLiteral(":/"))) + suffix;
141 url.setUrl(cmd);
142 }
143
144 const bool isMalformed = !url.isValid();
145 QString protocol = url.scheme();
146
147 qCDebug(category) << cmd;
148
149 // Fix misparsing of "foo:80", QUrl thinks "foo" is the protocol and "80" is the path.
150 // However, be careful not to do that for valid hostless URLs, e.g. file:///foo!
151 if (!protocol.isEmpty() && url.host().isEmpty() && !url.path().isEmpty() && cmd.contains(QLatin1Char(':')) && !isKnownProtocol(protocol)) {
152 protocol.clear();
153 }
154
155 qCDebug(category) << "url=" << url << "cmd=" << cmd << "isMalformed=" << isMalformed;
156
157 // TODO: Make this a bit more intelligent for Minicli! There
158 // is no need to make comparisons if the supplied data is a local
159 // executable and only the argument part, if any, changed! (Dawit)
160 // You mean caching the last filtering, to try and reuse it, to save stat()s? (David)
161
162 const QLatin1String starthere_proto("start-here:");
163 if (cmd.startsWith(starthere_proto)) {
164 setFilteredUri(data, QUrl(QStringLiteral("system:/")));
165 setUriType(data, KUriFilterData::LocalDir);
166 return true;
167 }
168
169 // Handle MAN & INFO pages shortcuts...
170 const QLatin1String man_proto("man:");
171 const QLatin1String info_proto("info:");
172 if (cmd.startsWith(QLatin1Char('#')) || cmd.startsWith(man_proto) || cmd.startsWith(info_proto)) {
173 QStringView sview(cmd);
174 if (cmd.startsWith(QLatin1String("##"))) {
175 cmd = QLatin1String("info:/") + sview.mid(2);
176 } else if (cmd.startsWith(QLatin1Char('#'))) {
177 cmd = QLatin1String("man:/") + sview.mid(1);
178 } else if (cmd == info_proto || cmd == man_proto) {
179 cmd += QLatin1Char('/');
180 }
181
182 setFilteredUri(data, QUrl(cmd));
183 setUriType(data, KUriFilterData::Help);
184 return true;
185 }
186
187 // Detect UNC style (aka windows SMB) URLs
188 if (cmd.startsWith(QLatin1String("\\\\"))) {
189 // make sure path is unix style
190 cmd.replace(QLatin1Char('\\'), QLatin1Char('/'));
191 cmd.prepend(QLatin1String("smb:"));
192 setFilteredUri(data, QUrl(cmd));
193 setUriType(data, KUriFilterData::NetProtocol);
194 return true;
195 }
196
197 bool expanded = false;
198
199 // Expanding shortcut to HOME URL...
200 QString path;
201 QString ref;
202 QString query;
203 QString nameFilter;
204
205 if (!Utils::isAbsoluteLocalPath(cmd) && QUrl(cmd).isRelative()) {
206 path = cmd;
207 qCDebug(category) << "path=cmd=" << path;
208 } else {
209 if (url.isLocalFile()) {
210 qCDebug(category) << "hasRef=" << url.hasFragment();
211 // Split path from ref/query
212 // but not for "/tmp/a#b", if "a#b" is an existing file,
213 // or for "/tmp/a?b" (#58990)
214 if ((url.hasFragment() || !url.query().isEmpty()) && !url.path().endsWith(QLatin1Char('/'))) { // /tmp/?foo is a namefilter, not a query
215 path = url.path();
216 ref = url.fragment();
217 qCDebug(category) << "isLocalFile set path to" << path << "and ref to" << ref;
218 query = url.query();
219 if (path.isEmpty() && !url.host().isEmpty()) {
220 path = QStringLiteral("/");
221 }
222 } else {
223 if (cmd.startsWith(QLatin1String("file://"))) {
224 path = cmd.mid(strlen("file://"));
225 } else {
226 path = cmd;
227 }
228 qCDebug(category) << "(2) path=cmd=" << path;
229 }
230 }
231 }
232
233 if (path.startsWith(QLatin1Char('~'))) {
234 int slashPos = path.indexOf(QLatin1Char('/'));
235 if (slashPos == -1) {
236 slashPos = path.length();
237 }
238 if (slashPos == 1) { // ~/
239 path.replace(0, 1, QDir::homePath());
240 } else { // ~username/
241 const QString userName(path.mid(1, slashPos - 1));
242 KUser user(userName);
243 if (user.isValid() && !user.homeDir().isEmpty()) {
244 path.replace(0, slashPos, user.homeDir());
245 } else {
246 if (user.isValid()) {
247 setErrorMsg(data, i18n("<qt><b>%1</b> does not have a home folder.</qt>", userName));
248 } else {
249 setErrorMsg(data, i18n("<qt>There is no user called <b>%1</b>.</qt>", userName));
250 }
251 setUriType(data, KUriFilterData::Error);
252 // Always return true for error conditions so
253 // that other filters will not be invoked !!
254 return true;
255 }
256 }
257 expanded = true;
258 } else if (path.startsWith(QLatin1Char('$'))) {
259 // Environment variable expansion.
260 static const QRegularExpression envVarExp(QStringLiteral("\\$[a-zA-Z_][a-zA-Z0-9_]*"));
261 const auto match = envVarExp.match(path);
262 if (match.hasMatch()) {
263 const QByteArray exp = qgetenv(QStringView(path).mid(1, match.capturedLength(0) - 1).toLocal8Bit().constData());
264 if (!exp.isEmpty()) {
265 path.replace(0, match.capturedLength(0), QFile::decodeName(exp));
266 expanded = true;
267 }
268 }
269 }
270
271 if (expanded || cmd.startsWith(QLatin1Char('/'))) {
272 // Look for #ref again, after $ and ~ expansion (testcase: $QTDIR/doc/html/functions.html#s)
273 // Can't use QUrl here, setPath would escape it...
274 const int pos = path.indexOf(QLatin1Char('#'));
275 if (pos > -1) {
276 const QString newPath = path.left(pos);
277 if (QFile::exists(newPath)) {
278 ref = path.mid(pos + 1);
279 path = newPath;
280 qCDebug(category) << "Extracted ref: path=" << path << " ref=" << ref;
281 }
282 }
283 }
284
285 bool isLocalFullPath = Utils::isAbsoluteLocalPath(path);
286
287 // Checking for local resource match...
288 // Determine if "uri" is an absolute path to a local resource OR
289 // A local resource with a supplied absolute path in KUriFilterData
290 const QString abs_path = data.absolutePath();
291
292 const bool canBeAbsolute = (protocol.isEmpty() && !abs_path.isEmpty());
293 const bool canBeLocalAbsolute = (canBeAbsolute && abs_path.startsWith(QLatin1Char('/')) && !isMalformed);
294 bool exists = false;
295
296 /*qCDebug(category) << "abs_path=" << abs_path
297 << "protocol=" << protocol
298 << "canBeAbsolute=" << canBeAbsolute
299 << "canBeLocalAbsolute=" << canBeLocalAbsolute
300 << "isLocalFullPath=" << isLocalFullPath;*/
301
302 QT_STATBUF buff;
303 if (canBeLocalAbsolute) {
304 QString abs = QDir::cleanPath(abs_path);
305 // combine absolute path (abs_path) and relative path (cmd) into abs_path
306 int len = path.length();
307 if ((len == 1 && path[0] == QLatin1Char('.')) || (len == 2 && path[0] == QLatin1Char('.') && path[1] == QLatin1Char('.'))) {
308 path += QLatin1Char('/');
309 }
310 qCDebug(category) << "adding " << abs << " and " << path;
311 abs = QDir::cleanPath(abs + QLatin1Char('/') + path);
312 qCDebug(category) << "checking whether " << abs << " exists.";
313 // Check if it exists
314 if (QT_STAT(QFile::encodeName(abs).constData(), &buff) == 0) {
315 path = abs; // yes -> store as the new cmd
316 exists = true;
317 isLocalFullPath = true;
318 }
319 }
320
321 if (isLocalFullPath && !exists && !isMalformed) {
322 exists = QT_STAT(QFile::encodeName(path).constData(), &buff) == 0;
323
324 if (!exists) {
325 // Support for name filter (/foo/*.txt), see also KonqMainWindow::detectNameFilter
326 // If the app using this filter doesn't support it, well, it'll simply error out itself
327 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
328 if (lastSlash > -1
329 && path.indexOf(QLatin1Char(' '), lastSlash) == -1) { // no space after last slash, otherwise it's more likely command-line arguments
330 QString fileName = path.mid(lastSlash + 1);
331 QString testPath = path.left(lastSlash);
332 if ((fileName.indexOf(QLatin1Char('*')) != -1 || fileName.indexOf(QLatin1Char('[')) != -1 || fileName.indexOf(QLatin1Char('?')) != -1)
333 && QT_STAT(QFile::encodeName(testPath).constData(), &buff) == 0) {
334 nameFilter = fileName;
335 qCDebug(category) << "Setting nameFilter to" << nameFilter << "and path to" << testPath;
336 path = testPath;
337 exists = true;
338 }
339 }
340 }
341 }
342
343 qCDebug(category) << "path =" << path << " isLocalFullPath=" << isLocalFullPath << " exists=" << exists << " url=" << url;
344 if (exists) {
345 QUrl u = QUrl::fromLocalFile(path);
346 qCDebug(category) << "ref=" << ref << "query=" << query;
347 u.setFragment(ref);
348 u.setQuery(query);
349
350 if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("open"), QUrl(), u)) {
351 // No authorization, we pretend it's a file will get
352 // an access denied error later on.
353 setFilteredUri(data, u);
354 setUriType(data, KUriFilterData::LocalFile);
355 return true;
356 }
357
358 // Can be abs path to file or directory, or to executable with args
359 bool isDir = Utils::isDirMask(buff.st_mode);
360 if (!isDir && access(QFile::encodeName(path).data(), X_OK) == 0) {
361 qCDebug(category) << "Abs path to EXECUTABLE";
362 setFilteredUri(data, u);
363 setUriType(data, KUriFilterData::Executable);
364 return true;
365 }
366
367 // Open "uri" as file:/xxx if it is a non-executable local resource.
368 if (isDir || Utils::isRegFileMask(buff.st_mode)) {
369 qCDebug(category) << "Abs path as local file or directory";
370 if (!nameFilter.isEmpty()) {
371 u.setPath(Utils::concatPaths(u.path(), nameFilter));
372 }
373 setFilteredUri(data, u);
374 setUriType(data, (isDir) ? KUriFilterData::LocalDir : KUriFilterData::LocalFile);
375 return true;
376 }
377
378 // Should we return LOCAL_FILE for non-regular files too?
379 qCDebug(category) << "File found, but not a regular file nor dir... socket?";
380 }
381
382 if (data.checkForExecutables()) {
383 // Let us deal with possible relative URLs to see
384 // if it is executable under the user's $PATH variable.
385 // We try hard to avoid parsing any possible command
386 // line arguments or options that might have been supplied.
387 QString exe = removeArgs(cmd);
388 qCDebug(category) << "findExe with" << exe;
389
390 if (!QStandardPaths::findExecutable(exe).isNull()) {
391 qCDebug(category) << "EXECUTABLE exe=" << exe;
392 setFilteredUri(data, QUrl::fromLocalFile(exe));
393 // check if we have command line arguments
394 if (exe != cmd) {
395 setArguments(data, cmd.right(cmd.length() - exe.length()));
396 }
397 setUriType(data, KUriFilterData::Executable);
398 return true;
399 }
400 }
401
402 // Process URLs of known and supported protocols so we don't have
403 // to resort to the pattern matching scheme below which can possibly
404 // slow things down...
405 if (!isMalformed && !isLocalFullPath && !protocol.isEmpty()) {
406 qCDebug(category) << "looking for protocol" << protocol;
407 if (isKnownProtocol(protocol)) {
408 setFilteredUri(data, url);
409 if (protocol == QLatin1String("man") || protocol == QLatin1String("help")) {
410 setUriType(data, KUriFilterData::Help);
411 } else {
412 setUriType(data, KUriFilterData::NetProtocol);
413 }
414 return true;
415 }
416 }
417
418 // Short url matches
419 if (!cmd.contains(QLatin1Char(' '))) {
420 // Okay this is the code that allows users to supply custom matches for specific
421 // URLs using Qt's QRegularExpression class.
422 for (const URLHint &hint : std::as_const(m_urlHints)) {
423 qCDebug(category) << "testing regexp for" << hint.prepend;
424 if (hint.hintRe.match(cmd).capturedStart() == 0) {
425 const QString cmdStr = hint.prepend + cmd;
426 QUrl cmdUrl(cmdStr);
427 qCDebug(category) << "match - prepending" << hint.prepend << "->" << cmdStr << "->" << cmdUrl;
428 setFilteredUri(data, cmdUrl);
429 setUriType(data, hint.type);
430 return true;
431 }
432 }
433
434 // No protocol and not malformed means a valid short URL such as kde.org or
435 // user@192.168.0.1. However, it might also be valid only because it lacks
436 // the scheme component, e.g. www.kde,org (illegal ',' before 'org'). The
437 // check below properly deciphers the difference between the two and sends
438 // back the proper result.
439 if (protocol.isEmpty() && isPotentialShortURL(cmd)) {
440 QString urlStr = data.defaultUrlScheme();
441 if (urlStr.isEmpty()) {
442 urlStr = m_strDefaultUrlScheme;
443 }
444
445 const int index = urlStr.indexOf(QLatin1Char(':'));
446 if (index == -1 || !isKnownProtocol(urlStr.left(index))) {
447 urlStr += QStringLiteral("://");
448 }
449 urlStr += cmd;
450
451 QUrl fixedUrl(urlStr);
452 if (fixedUrl.isValid()) {
453 setFilteredUri(data, fixedUrl);
454 setUriType(data, KUriFilterData::NetProtocol);
455 } else if (isKnownProtocol(fixedUrl.scheme())) {
456 setFilteredUri(data, data.uri());
457 setUriType(data, KUriFilterData::Error);
458 }
459 return true;
460 }
461 }
462
463 // If we previously determined that the URL might be a file,
464 // and if it doesn't exist... we'll pretend it exists.
465 // This allows to use it for completion purposes.
466 // (If you change this logic again, look at the commit that was testing
467 // for KUrlAuthorized::authorizeUrlAction("open"))
468 if (isLocalFullPath && !exists) {
469 QUrl u = QUrl::fromLocalFile(path);
470 u.setFragment(ref);
471 setFilteredUri(data, u);
472 setUriType(data, KUriFilterData::LocalFile);
473 return true;
474 }
475
476 // If we reach this point, we cannot filter this thing so simply return false
477 // so that other filters, if present, can take a crack at it.
478 return false;
479}
480
481void KShortUriFilter::configure()
482{
483 KConfig config(objectName() + QStringLiteral("rc"), KConfig::NoGlobals);
484 KConfigGroup cg(config.group(QString()));
485
486 m_strDefaultUrlScheme = cg.readEntry("DefaultProtocol", QStringLiteral("https://"));
487 const QMap<QString, QString> patterns = config.entryMap(QStringLiteral("Pattern"));
488 const QMap<QString, QString> protocols = config.entryMap(QStringLiteral("Protocol"));
489 KConfigGroup typeGroup(&config, QStringLiteral("Type"));
490
491 for (auto it = patterns.begin(); it != patterns.end(); ++it) {
492 QString protocol = protocols[it.key()];
493 if (!protocol.isEmpty()) {
494 int type = typeGroup.readEntry(it.key(), -1);
495 if (type > -1 && type <= KUriFilterData::Unknown) {
496 m_urlHints.append(URLHint(it.value(), protocol, static_cast<KUriFilterData::UriTypes>(type)));
497 } else {
498 m_urlHints.append(URLHint(it.value(), protocol));
499 }
500 }
501 }
502}
503
504K_PLUGIN_CLASS_WITH_JSON(KShortUriFilter, "kshorturifilter.json")
505
506#include "kshorturifilter.moc"
507
508#include "moc_kshorturifilter.cpp"
#define K_PLUGIN_CLASS_WITH_JSON(classname, jsonFile)
static bool isKnownProtocol(const QUrl &url)
Returns whether a protocol is installed that is able to handle url.
This is short URL filter class.
bool filterUri(KUriFilterData &data) const override
Converts short URIs into fully qualified valid URIs whenever possible.
This class is a basic messaging class used to exchange filtering information between the filter plugi...
Definition kurifilter.h:153
QString absolutePath() const
Returns the absolute path if one has already been set.
QUrl uri() const
Returns the filtered or the original URL.
bool checkForExecutables() const
QString typedString() const
The string as typed by the user, before any URL processing is done.
UriTypes
Describes the type of the URI that was filtered.
Definition kurifilter.h:158
@ Error
An incorrect URI (ex: "~johndoe" when user johndoe does not exist in that system)
Definition kurifilter.h:166
@ NetProtocol
Any network protocol: http, ftp, nttp, pop3, etc...
Definition kurifilter.h:159
@ Unknown
A URI that is not identified. Default value when a KUriFilterData is first created.
Definition kurifilter.h:167
@ Executable
A local file whose executable flag is set.
Definition kurifilter.h:162
@ LocalFile
A local file whose executable flag is not set.
Definition kurifilter.h:160
@ Help
A man or info page.
Definition kurifilter.h:163
@ LocalDir
A local directory.
Definition kurifilter.h:161
QString defaultUrlScheme() const
Returns the default protocol to use when filtering potentially valid url inputs.
QString homeDir() const
bool isValid() const
QString i18n(const char *text, const TYPE &arg...)
KSERVICE_EXPORT KService::Ptr preferredService(const QString &mimeType)
KGuiItem configure()
bool authorizeUrlAction(const QString &action, const QUrl &baseURL, const QUrl &destURL)
Returns whether a certain URL related action is authorized.
const char * constData() const const
bool isEmpty() const const
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QDBusConnection sessionBus()
QString cleanPath(const QString &path)
QString homePath()
QString decodeName(const QByteArray &localFileName)
QByteArray encodeName(const QString &fileName)
bool exists() const const
void append(QList< T > &&value)
iterator begin()
iterator end()
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
QString findExecutable(const QString &executableName, const QStringList &paths)
qsizetype count() const const
const QChar at(qsizetype position) const const
void clear()
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString & prepend(QChar ch)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
void truncate(qsizetype position)
QStringView mid(qsizetype start, qsizetype length) const const
QByteArray toLocal8Bit() const const
QString fragment(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool hasFragment() const const
QString host(ComponentFormattingOptions options) const const
bool isLocalFile() const const
bool isValid() const const
QString path(ComponentFormattingOptions options) const const
QString query(ComponentFormattingOptions options) const const
QString scheme() const const
void setFragment(const QString &fragment, ParsingMode mode)
void setPath(const QString &path, ParsingMode mode)
void setQuery(const QString &query, ParsingMode mode)
void setUrl(const QString &url, ParsingMode parsingMode)
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:28 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.