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
qurl.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6/*!
7 \class QUrl
8 \inmodule QtCore
9
10 \brief The QUrl class provides a convenient interface for working
11 with URLs.
12
13 \reentrant
14 \ingroup io
15 \ingroup network
16 \ingroup shared
17
18 \compares weak
19
20 It can parse and construct URLs in both encoded and unencoded
21 form. QUrl also has support for internationalized domain names
22 (IDNs).
23
24 The most common way to use QUrl is to initialize it via the constructor by
25 passing a QString containing a full URL. QUrl objects can also be created
26 from a QByteArray containing a full URL using QUrl::fromEncoded(), or
27 heuristically from incomplete URLs using QUrl::fromUserInput(). The URL
28 representation can be obtained from a QUrl using either QUrl::toString() or
29 QUrl::toEncoded().
30
31 URLs can be represented in two forms: encoded or unencoded. The
32 unencoded representation is suitable for showing to users, but
33 the encoded representation is typically what you would send to
34 a web server. For example, the unencoded URL
35 "http://bühler.example.com/List of applicants.xml"
36 would be sent to the server as
37 "http://xn--bhler-kva.example.com/List%20of%20applicants.xml".
38
39 A URL can also be constructed piece by piece by calling
40 setScheme(), setUserName(), setPassword(), setHost(), setPort(),
41 setPath(), setQuery() and setFragment(). Some convenience
42 functions are also available: setAuthority() sets the user name,
43 password, host and port. setUserInfo() sets the user name and
44 password at once.
45
46 Call isValid() to check if the URL is valid. This can be done at any point
47 during the constructing of a URL. If isValid() returns \c false, you should
48 clear() the URL before proceeding, or start over by parsing a new URL with
49 setUrl().
50
51 Constructing a query is particularly convenient through the use of the \l
52 QUrlQuery class and its methods QUrlQuery::setQueryItems(),
53 QUrlQuery::addQueryItem() and QUrlQuery::removeQueryItem(). Use
54 QUrlQuery::setQueryDelimiters() to customize the delimiters used for
55 generating the query string.
56
57 For the convenience of generating encoded URL strings or query
58 strings, there are two static functions called
59 fromPercentEncoding() and toPercentEncoding() which deal with
60 percent encoding and decoding of QString objects.
61
62 fromLocalFile() constructs a QUrl by parsing a local
63 file path. toLocalFile() converts a URL to a local file path.
64
65 The human readable representation of the URL is fetched with
66 toString(). This representation is appropriate for displaying a
67 URL to a user in unencoded form. The encoded form however, as
68 returned by toEncoded(), is for internal use, passing to web
69 servers, mail clients and so on. Both forms are technically correct
70 and represent the same URL unambiguously -- in fact, passing either
71 form to QUrl's constructor or to setUrl() will yield the same QUrl
72 object.
73
74 QUrl conforms to the URI specification from
75 \l{RFC 3986} (Uniform Resource Identifier: Generic Syntax), and includes
76 scheme extensions from \l{RFC 1738} (Uniform Resource Locators). Case
77 folding rules in QUrl conform to \l{RFC 3491} (Nameprep: A Stringprep
78 Profile for Internationalized Domain Names (IDN)). It is also compatible with the
79 \l{http://freedesktop.org/wiki/Specifications/file-uri-spec/}{file URI specification}
80 from freedesktop.org, provided that the locale encodes file names using
81 UTF-8 (required by IDN).
82
83 \section2 Relative URLs vs Relative Paths
84
85 Calling isRelative() will return whether or not the URL is relative.
86 A relative URL has no \l {scheme}. For example:
87
88 \snippet code/src_corelib_io_qurl.cpp 8
89
90 Notice that a URL can be absolute while containing a relative path, and
91 vice versa:
92
93 \snippet code/src_corelib_io_qurl.cpp 9
94
95 A relative URL can be resolved by passing it as an argument to resolved(),
96 which returns an absolute URL. isParentOf() is used for determining whether
97 one URL is a parent of another.
98
99 \section2 Error checking
100
101 QUrl is capable of detecting many errors in URLs while parsing it or when
102 components of the URL are set with individual setter methods (like
103 setScheme(), setHost() or setPath()). If the parsing or setter function is
104 successful, any previously recorded error conditions will be discarded.
105
106 By default, QUrl setter methods operate in QUrl::TolerantMode, which means
107 they accept some common mistakes and mis-representation of data. An
108 alternate method of parsing is QUrl::StrictMode, which applies further
109 checks. See QUrl::ParsingMode for a description of the difference of the
110 parsing modes.
111
112 QUrl only checks for conformance with the URL specification. It does not
113 try to verify that high-level protocol URLs are in the format they are
114 expected to be by handlers elsewhere. For example, the following URIs are
115 all considered valid by QUrl, even if they do not make sense when used:
116
117 \list
118 \li "http:/filename.html"
119 \li "mailto://example.com"
120 \endlist
121
122 When the parser encounters an error, it signals the event by making
123 isValid() return false and toString() / toEncoded() return an empty string.
124 If it is necessary to show the user the reason why the URL failed to parse,
125 the error condition can be obtained from QUrl by calling errorString().
126 Note that this message is highly technical and may not make sense to
127 end-users.
128
129 QUrl is capable of recording only one error condition. If more than one
130 error is found, it is undefined which error is reported.
131
132 \section2 Character Conversions
133
134 Follow these rules to avoid erroneous character conversion when
135 dealing with URLs and strings:
136
137 \list
138 \li When creating a QString to contain a URL from a QByteArray or a
139 char*, always use QString::fromUtf8().
140 \endlist
141*/
142
143/*!
144 \enum QUrl::ParsingMode
145
146 The parsing mode controls the way QUrl parses strings.
147
148 \value TolerantMode QUrl will try to correct some common errors in URLs.
149 This mode is useful for parsing URLs coming from sources
150 not known to be strictly standards-conforming.
151
152 \value StrictMode Only valid URLs are accepted. This mode is useful for
153 general URL validation.
154
155 \value DecodedMode QUrl will interpret the URL component in the fully-decoded form,
156 where percent characters stand for themselves, not as the beginning
157 of a percent-encoded sequence. This mode is only valid for the
158 setters setting components of a URL; it is not permitted in
159 the QUrl constructor, in fromEncoded() or in setUrl().
160 For more information on this mode, see the documentation for
161 \l {QUrl::ComponentFormattingOption}{QUrl::FullyDecoded}.
162
163 In TolerantMode, the parser has the following behaviour:
164
165 \list
166
167 \li Spaces and "%20": unencoded space characters will be accepted and will
168 be treated as equivalent to "%20".
169
170 \li Single "%" characters: Any occurrences of a percent character "%" not
171 followed by exactly two hexadecimal characters (e.g., "13% coverage.html")
172 will be replaced by "%25". Note that one lone "%" character will trigger
173 the correction mode for all percent characters.
174
175 \li Reserved and unreserved characters: An encoded URL should only
176 contain a few characters as literals; all other characters should
177 be percent-encoded. In TolerantMode, these characters will be
178 accepted if they are found in the URL:
179 space / double-quote / "<" / ">" / "\" /
180 "^" / "`" / "{" / "|" / "}"
181 Those same characters can be decoded again by passing QUrl::DecodeReserved
182 to toString() or toEncoded(). In the getters of individual components,
183 those characters are often returned in decoded form.
184
185 \endlist
186
187 When in StrictMode, if a parsing error is found, isValid() will return \c
188 false and errorString() will return a message describing the error.
189 If more than one error is detected, it is undefined which error gets
190 reported.
191
192 Note that TolerantMode is not usually enough for parsing user input, which
193 often contains more errors and expectations than the parser can deal with.
194 When dealing with data coming directly from the user -- as opposed to data
195 coming from data-transfer sources, such as other programs -- it is
196 recommended to use fromUserInput().
197
198 \sa fromUserInput(), setUrl(), toString(), toEncoded(), QUrl::FormattingOptions
199*/
200
201/*!
202 \enum QUrl::UrlFormattingOption
203
204 The formatting options define how the URL is formatted when written out
205 as text.
206
207 \value None The format of the URL is unchanged.
208 \value RemoveScheme The scheme is removed from the URL.
209 \value RemovePassword Any password in the URL is removed.
210 \value RemoveUserInfo Any user information in the URL is removed.
211 \value RemovePort Any specified port is removed from the URL.
212 \value RemoveAuthority Remove user name, password, host and port.
213 \value RemovePath The URL's path is removed, leaving only the scheme,
214 host address, and port (if present).
215 \value RemoveQuery The query part of the URL (following a '?' character)
216 is removed.
217 \value RemoveFragment The fragment part of the URL (including the '#' character) is removed.
218 \value RemoveFilename The filename (i.e. everything after the last '/' in the path) is removed.
219 The trailing '/' is kept, unless StripTrailingSlash is set.
220 Only valid if RemovePath is not set.
221 \value PreferLocalFile If the URL is a local file according to isLocalFile()
222 and contains no query or fragment, a local file path is returned.
223 \value StripTrailingSlash The trailing slash is removed from the path, if one is present.
224 \value NormalizePathSegments Modifies the path to remove redundant directory separators,
225 and to resolve "."s and ".."s (as far as possible). For non-local paths, adjacent
226 slashes are preserved.
227
228 Note that the case folding rules in \l{RFC 3491}{Nameprep}, which QUrl
229 conforms to, require host names to always be converted to lower case,
230 regardless of the Qt::FormattingOptions used.
231
232 The options from QUrl::ComponentFormattingOptions are also possible.
233
234 \sa QUrl::ComponentFormattingOptions
235*/
236
237/*!
238 \enum QUrl::ComponentFormattingOption
239 \since 5.0
240
241 The component formatting options define how the components of an URL will
242 be formatted when written out as text. They can be combined with the
243 options from QUrl::FormattingOptions when used in toString() and
244 toEncoded().
245
246 \value PrettyDecoded The component is returned in a "pretty form", with
247 most percent-encoded characters decoded. The exact
248 behavior of PrettyDecoded varies from component to
249 component and may also change from Qt release to Qt
250 release. This is the default.
251
252 \value EncodeSpaces Leave space characters in their encoded form ("%20").
253
254 \value EncodeUnicode Leave non-US-ASCII characters encoded in their UTF-8
255 percent-encoded form (e.g., "%C3%A9" for the U+00E9
256 codepoint, LATIN SMALL LETTER E WITH ACUTE).
257
258 \value EncodeDelimiters Leave certain delimiters in their encoded form, as
259 would appear in the URL when the full URL is
260 represented as text. The delimiters are affected
261 by this option change from component to component.
262 This flag has no effect in toString() or toEncoded().
263
264 \value EncodeReserved Leave US-ASCII characters not permitted in the URL by
265 the specification in their encoded form. This is the
266 default on toString() and toEncoded().
267
268 \value DecodeReserved Decode the US-ASCII characters that the URL specification
269 does not allow to appear in the URL. This is the
270 default on the getters of individual components.
271
272 \value FullyEncoded Leave all characters in their properly-encoded form,
273 as this component would appear as part of a URL. When
274 used with toString(), this produces a fully-compliant
275 URL in QString form, exactly equal to the result of
276 toEncoded()
277
278 \value FullyDecoded Attempt to decode as much as possible. For individual
279 components of the URL, this decodes every percent
280 encoding sequence, including control characters (U+0000
281 to U+001F) and UTF-8 sequences found in percent-encoded form.
282 Use of this mode may cause data loss, see below for more information.
283
284 The values of EncodeReserved and DecodeReserved should not be used together
285 in one call. The behavior is undefined if that happens. They are provided
286 as separate values because the behavior of the "pretty mode" with regards
287 to reserved characters is different on certain components and specially on
288 the full URL.
289
290 \section2 Full decoding
291
292 The FullyDecoded mode is similar to the behavior of the functions returning
293 QString in Qt 4.x, in that every character represents itself and never has
294 any special meaning. This is true even for the percent character ('%'),
295 which should be interpreted to mean a literal percent, not the beginning of
296 a percent-encoded sequence. The same actual character, in all other
297 decoding modes, is represented by the sequence "%25".
298
299 Whenever re-applying data obtained with QUrl::FullyDecoded into a QUrl,
300 care must be taken to use the QUrl::DecodedMode parameter to the setters
301 (like setPath() and setUserName()). Failure to do so may cause
302 re-interpretation of the percent character ('%') as the beginning of a
303 percent-encoded sequence.
304
305 This mode is quite useful when portions of a URL are used in a non-URL
306 context. For example, to extract the username, password or file paths in an
307 FTP client application, the FullyDecoded mode should be used.
308
309 This mode should be used with care, since there are two conditions that
310 cannot be reliably represented in the returned QString. They are:
311
312 \list
313 \li \b{Non-UTF-8 sequences:} URLs may contain sequences of
314 percent-encoded characters that do not form valid UTF-8 sequences. Since
315 URLs need to be decoded using UTF-8, any decoder failure will result in
316 the QString containing one or more replacement characters where the
317 sequence existed.
318
319 \li \b{Encoded delimiters:} URLs are also allowed to make a distinction
320 between a delimiter found in its literal form and its equivalent in
321 percent-encoded form. This is most commonly found in the query, but is
322 permitted in most parts of the URL.
323 \endlist
324
325 The following example illustrates the problem:
326
327 \snippet code/src_corelib_io_qurl.cpp 10
328
329 If the two URLs were used via HTTP GET, the interpretation by the web
330 server would probably be different. In the first case, it would interpret
331 as one parameter, with a key of "q" and value "a+=b&c". In the second
332 case, it would probably interpret as two parameters, one with a key of "q"
333 and value "a =b", and the second with a key "c" and no value.
334
335 \sa QUrl::FormattingOptions
336*/
337
338/*!
339 \enum QUrl::UserInputResolutionOption
340 \since 5.4
341
342 The user input resolution options define how fromUserInput() should
343 interpret strings that could either be a relative path or the short
344 form of a HTTP URL. For instance \c{file.pl} can be either a local file
345 or the URL \c{http://file.pl}.
346
347 \value DefaultResolution The default resolution mechanism is to check
348 whether a local file exists, in the working
349 directory given to fromUserInput, and only
350 return a local path in that case. Otherwise a URL
351 is assumed.
352 \value AssumeLocalFile This option makes fromUserInput() always return
353 a local path unless the input contains a scheme, such as
354 \c{http://file.pl}. This is useful for applications
355 such as text editors, which are able to create
356 the file if it doesn't exist.
357
358 \sa fromUserInput()
359*/
360
361/*!
362 \enum QUrl::AceProcessingOption
363 \since 6.3
364
365 The ACE processing options control the way URLs are transformed to and from
366 ASCII-Compatible Encoding.
367
368 \value IgnoreIDNWhitelist Ignore the IDN whitelist when converting URLs
369 to Unicode.
370 \value AceTransitionalProcessing Use transitional processing described in UTS #46.
371 This allows better compatibility with IDNA 2003
372 specification.
373
374 The default is to use nontransitional processing and to allow non-ASCII
375 characters only inside URLs whose top-level domains are listed in the IDN whitelist.
376
377 \sa toAce(), fromAce(), idnWhitelist()
378*/
379
380/*!
381 \fn QUrl::QUrl(QUrl &&other)
382
383 Move-constructs a QUrl instance, making it point at the same
384 object that \a other was pointing to.
385
386 \since 5.2
387*/
388
389/*!
390 \fn QUrl &QUrl::operator=(QUrl &&other)
391
392 Move-assigns \a other to this QUrl instance.
393
394 \since 5.2
395*/
396
397#include "qurl.h"
398#include "qurl_p.h"
399#include "qplatformdefs.h"
400#include "qstring.h"
401#include "qstringlist.h"
402#include "qdebug.h"
403#include "qhash.h"
404#include "qdatastream.h"
405#include "private/qipaddress_p.h"
406#include "qurlquery.h"
407#include "private/qdir_p.h"
408#include <private/qtools_p.h>
409
410QT_BEGIN_NAMESPACE
411
412using namespace Qt::StringLiterals;
413using namespace QtMiscUtils;
414
415inline static bool isHex(char c)
416{
417 c |= 0x20;
418 return isAsciiDigit(c) || (c >= 'a' && c <= 'f');
419}
420
421static inline QString ftpScheme()
422{
423 return QStringLiteral("ftp");
424}
425
426static inline QString fileScheme()
427{
428 return QStringLiteral("file");
429}
430
431static inline QString webDavScheme()
432{
433 return QStringLiteral("webdavs");
434}
435
436static inline QString webDavSslTag()
437{
438 return QStringLiteral("@SSL");
439}
440
442{
443public:
444 enum Section : uchar {
445 Scheme = 0x01,
446 UserName = 0x02,
447 Password = 0x04,
449 Host = 0x08,
450 Port = 0x10,
452 Path = 0x20,
454 Query = 0x40,
455 Fragment = 0x80,
456 FullUrl = 0xff
457 };
458
459 enum Flags : uchar {
461 };
462
464 // the high byte of the error code matches the Section
465 // the first item in each value must be the generic "Invalid xxx Error"
467
469
471
478
481
483
485
487
488 // the following three cases are only possible in combination with
489 // presence/absence of the path, authority and scheme. See validityError().
493
495 };
496
502
506
507 void parse(const QString &url, QUrl::ParsingMode parsingMode);
508 bool isEmpty() const
509 { return sectionIsPresent == 0 && port == -1 && path.isEmpty(); }
510
513 void setError(ErrorCode errorCode, const QString &source, qsizetype supplement = -1);
514 ErrorCode validityError(QString *source = nullptr, qsizetype *position = nullptr) const;
515 bool validateComponent(Section section, const QString &input, qsizetype begin, qsizetype end);
516 bool validateComponent(Section section, const QString &input)
517 { return validateComponent(section, input, 0, input.size()); }
518
519 // no QString scheme() const;
520 void appendAuthority(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const;
521 void appendUserInfo(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const;
522 void appendUserName(QString &appendTo, QUrl::FormattingOptions options) const;
523 void appendPassword(QString &appendTo, QUrl::FormattingOptions options) const;
524 void appendHost(QString &appendTo, QUrl::FormattingOptions options) const;
525 void appendPath(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const;
526 void appendQuery(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const;
527 void appendFragment(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const;
528
529 // the "end" parameters are like STL iterators: they point to one past the last valid element
530 bool setScheme(const QString &value, qsizetype len, bool doSetError);
531 void setAuthority(const QString &auth, qsizetype from, qsizetype end, QUrl::ParsingMode mode);
532 template <typename String> void setUserInfo(String &&value, QUrl::ParsingMode mode);
533 template <typename String> void setUserName(String &&value, QUrl::ParsingMode mode);
534 template <typename String> void setPassword(String &&value, QUrl::ParsingMode mode);
535 bool setHost(const QString &value, qsizetype from, qsizetype end, QUrl::ParsingMode mode);
536 template <typename String> void setPath(String &&value, QUrl::ParsingMode mode);
537 template <typename String> void setQuery(String &&value, QUrl::ParsingMode mode);
538 template <typename String> void setFragment(String &&value, QUrl::ParsingMode mode);
539
540 uint presentSections() const noexcept
541 {
542 uint s = sectionIsPresent;
543
544 // We have to ignore the host-is-present flag for local files (the
545 // "file" protocol), due to the requirements of the XDG file URI
546 // specification.
547 if (isLocalFile())
548 s &= ~Host;
549
550 // If the password was set, we must have a username too
551 if (s & Password)
552 s |= UserName;
553
554 return s;
555 }
556
557 inline bool hasScheme() const { return sectionIsPresent & Scheme; }
558 inline bool hasAuthority() const { return sectionIsPresent & Authority; }
559 inline bool hasUserInfo() const { return sectionIsPresent & UserInfo; }
560 inline bool hasUserName() const { return sectionIsPresent & UserName; }
561 inline bool hasPassword() const { return sectionIsPresent & Password; }
562 inline bool hasHost() const { return sectionIsPresent & Host; }
563 inline bool hasPort() const { return port != -1; }
564 inline bool hasPath() const { return !path.isEmpty(); }
565 inline bool hasQuery() const { return sectionIsPresent & Query; }
566 inline bool hasFragment() const { return sectionIsPresent & Fragment; }
567
568 inline bool isLocalFile() const { return flags & IsLocalFile; }
569 QString toLocalFile(QUrl::FormattingOptions options) const;
570
571 bool normalizePathSegments(QString *path) const
572 {
573 QDirPrivate::PathNormalizations mode = QDirPrivate::UrlNormalizationMode;
574 if (!isLocalFile())
575 mode |= QDirPrivate::RemotePath;
576 return qt_normalizePathSegments(path, mode);
577 }
578 QString mergePaths(const QString &relativePath) const;
579
580 void clear()
581 {
583 scheme = userName = password = host = path = query = fragment = QString();
584 port = -1;
585 sectionIsPresent = 0;
586 flags = 0;
587 }
588
590 int port;
591
592 QString scheme;
593 QString userName;
594 QString password;
595 QString host;
596 QString path;
597 QString query;
598 QString fragment;
599
601
602 // not used for:
603 // - Port (port == -1 means absence)
604 // - Path (there's no path delimiter, so we optimize its use out of existence)
605 // Schemes are never supposed to be empty, but we keep the flag anyway
608
609 // 32-bit: 2 bytes tail padding available
610 // 64-bit: 6 bytes tail padding available
611};
612
614 : ref(1), port(-1),
616 flags(0)
617{
618}
619
634
635inline QUrlPrivate::~QUrlPrivate()
636 = default;
637
639{
640 return error ? std::make_unique<Error>(*error) : nullptr;
641}
642
644{
645 error.reset();
646}
647
648inline void QUrlPrivate::setError(ErrorCode errorCode, const QString &source, qsizetype supplement)
649{
650 if (error) {
651 // don't overwrite an error set in a previous section during parsing
652 return;
653 }
654 error = std::make_unique<Error>();
655 error->code = errorCode;
656 error->source = source;
657 error->position = supplement;
658}
659
660// From RFC 3986, Appendix A Collected ABNF for URI
661// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
662//[...]
663// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
664//
665// authority = [ userinfo "@" ] host [ ":" port ]
666// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
667// host = IP-literal / IPv4address / reg-name
668// port = *DIGIT
669//[...]
670// reg-name = *( unreserved / pct-encoded / sub-delims )
671//[..]
672// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
673//
674// query = *( pchar / "/" / "?" )
675//
676// fragment = *( pchar / "/" / "?" )
677//
678// pct-encoded = "%" HEXDIG HEXDIG
679//
680// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
681// reserved = gen-delims / sub-delims
682// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
683// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
684// / "*" / "+" / "," / ";" / "="
685// the path component has a complex ABNF that basically boils down to
686// slash-separated segments of "pchar"
687
688// The above is the strict definition of the URL components and we mostly
689// adhere to it, with few exceptions. QUrl obeys the following behavior:
690// - percent-encoding sequences always use uppercase HEXDIG;
691// - unreserved characters are *always* decoded, no exceptions;
692// - the space character and bytes with the high bit set are controlled by
693// the EncodeSpaces and EncodeUnicode bits;
694// - control characters, the percent sign itself, and bytes with the high
695// bit set that don't form valid UTF-8 sequences are always encoded,
696// except in FullyDecoded mode;
697// - sub-delims are always left alone, except in FullyDecoded mode;
698// - gen-delim change behavior depending on which section of the URL (or
699// the entire URL) we're looking at; see below;
700// - characters not mentioned above, like "<", and ">", are usually
701// decoded in individual sections of the URL, but encoded when the full
702// URL is put together (we can change on subjective definition of
703// "pretty").
704//
705// The behavior for the delimiters bears some explanation. The spec says in
706// section 2.2:
707// URIs that differ in the replacement of a reserved character with its
708// corresponding percent-encoded octet are not equivalent.
709// (note: QUrl API mistakenly uses the "reserved" term, so we will refer to
710// them here as "delimiters").
711//
712// For that reason, we cannot encode delimiters found in decoded form and we
713// cannot decode the ones found in encoded form if that would change the
714// interpretation. Conversely, we *can* perform the transformation if it would
715// not change the interpretation. From the last component of a URL to the first,
716// here are the gen-delims we can unambiguously transform when the field is
717// taken in isolation:
718// - fragment: none, since it's the last
719// - query: "#" is unambiguous
720// - path: "#" and "?" are unambiguous
721// - host: completely special but never ambiguous, see setHost() below.
722// - password: the "#", "?", "/", "[", "]" and "@" characters are unambiguous
723// - username: the "#", "?", "/", "[", "]", "@", and ":" characters are unambiguous
724// - scheme: doesn't accept any delimiter, see setScheme() below.
725//
726// Internally, QUrl stores each component in the format that corresponds to the
727// default mode (PrettyDecoded). It deviates from the "strict" FullyEncoded
728// mode in the following way:
729// - spaces are decoded
730// - valid UTF-8 sequences are decoded
731// - gen-delims that can be unambiguously transformed are decoded (exception:
732// square brackets in path, query and fragment are left as they were)
733// - characters controlled by DecodeReserved are often decoded, though this behavior
734// can change depending on the subjective definition of "pretty"
735//
736// Note that the list of gen-delims that we can transform is different for the
737// user info (user name + password) and the authority (user info + host +
738// port).
739
740
741// list the recoding table modifications to be used with the recodeFromUser and
742// appendToUser functions, according to the rules above. Spaces and UTF-8
743// sequences are handled outside the tables.
744
745// the encodedXXX tables are run with the delimiters set to "leave" by default;
746// the decodedXXX tables are run with the delimiters set to "decode" by default
747// (except for the query, which doesn't use these functions)
748
749namespace {
750template <typename T> constexpr ushort decode(T x) noexcept { return ushort(x); }
751template <typename T> constexpr ushort leave(T x) noexcept { return ushort(0x100 | x); }
752template <typename T> constexpr ushort encode(T x) noexcept { return ushort(0x200 | x); }
753}
754
755static const ushort userNameInIsolation[] = {
756 decode(':'), // 0
757 decode('@'), // 1
758 decode(']'), // 2
759 decode('['), // 3
760 decode('/'), // 4
761 decode('?'), // 5
762 decode('#'), // 6
763
764 decode('"'), // 7
765 decode('<'),
766 decode('>'),
767 decode('^'),
768 decode('\\'),
769 decode('|'),
770 decode('{'),
771 decode('}'),
772 0
773};
774static const ushort * const passwordInIsolation = userNameInIsolation + 1;
775static const ushort * const pathInIsolation = userNameInIsolation + 5;
776static const ushort * const queryInIsolation = userNameInIsolation + 6;
777static const ushort * const fragmentInIsolation = userNameInIsolation + 7;
778
779static const ushort userNameInUserInfo[] = {
780 encode(':'), // 0
781 decode('@'), // 1
782 decode(']'), // 2
783 decode('['), // 3
784 decode('/'), // 4
785 decode('?'), // 5
786 decode('#'), // 6
787
788 decode('"'), // 7
789 decode('<'),
790 decode('>'),
791 decode('^'),
792 decode('\\'),
793 decode('|'),
794 decode('{'),
795 decode('}'),
796 0
797};
798static const ushort * const passwordInUserInfo = userNameInUserInfo + 1;
799
800static const ushort userNameInAuthority[] = {
801 encode(':'), // 0
802 encode('@'), // 1
803 encode(']'), // 2
804 encode('['), // 3
805 decode('/'), // 4
806 decode('?'), // 5
807 decode('#'), // 6
808
809 decode('"'), // 7
810 decode('<'),
811 decode('>'),
812 decode('^'),
813 decode('\\'),
814 decode('|'),
815 decode('{'),
816 decode('}'),
817 0
818};
819static const ushort * const passwordInAuthority = userNameInAuthority + 1;
820
821static const ushort userNameInUrl[] = {
822 encode(':'), // 0
823 encode('@'), // 1
824 encode(']'), // 2
825 encode('['), // 3
826 encode('/'), // 4
827 encode('?'), // 5
828 encode('#'), // 6
829
830 // no need to list encode(x) for the other characters
831 0
832};
833static const ushort * const passwordInUrl = userNameInUrl + 1;
834static const ushort * const pathInUrl = userNameInUrl + 5;
835static const ushort * const queryInUrl = userNameInUrl + 6;
836static const ushort * const fragmentInUrl = userNameInUrl + 6;
837
838static void
839recodeFromUser(QString &output, const QString &input, const ushort *actions, QUrl::ParsingMode mode)
840{
841 output.resize(0);
842 qsizetype appended;
843 if (mode == QUrl::DecodedMode)
844 appended = qt_encodeFromUser(output, input, actions);
845 else
846 appended = qt_urlRecode(output, input, {}, actions);
847 if (!appended)
848 output = input;
849}
850
851static void
852recodeFromUser(QString &output, QStringView input, const ushort *actions, QUrl::ParsingMode mode)
853{
854 Q_ASSERT_X(mode != QUrl::DecodedMode, "recodeFromUser",
855 "This function should only be called when parsing encoded components");
856 Q_UNUSED(mode);
857 output.resize(0);
858 if (qt_urlRecode(output, input, {}, actions))
859 return;
860 output.append(input);
861}
862
863// appendXXXX functions: copy from the internal form to the external, user form.
864// the internal value is stored in its PrettyDecoded form, so that case is easy.
865static inline void appendToUser(QString &appendTo, QStringView value, QUrl::FormattingOptions options,
866 const ushort *actions)
867{
868 // The stored value is already QUrl::PrettyDecoded, so there's nothing to
869 // do if that's what the user asked for (test only
870 // ComponentFormattingOptions, ignore FormattingOptions).
871 if ((options & 0xFFFF0000) == QUrl::PrettyDecoded ||
872 !qt_urlRecode(appendTo, value, options, actions))
873 appendTo += value;
874
875 // copy nullness, if necessary, because QString::operator+=(QStringView) doesn't
876 if (appendTo.isNull() && !value.isNull())
877 appendTo.detach();
878}
879
880inline void QUrlPrivate::appendAuthority(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
881{
882 if ((options & QUrl::RemoveUserInfo) != QUrl::RemoveUserInfo) {
883 appendUserInfo(appendTo, options, appendingTo);
884
885 // add '@' only if we added anything
886 if (hasUserName() || (hasPassword() && (options & QUrl::RemovePassword) == 0))
887 appendTo += u'@';
888 }
889 appendHost(appendTo, options);
890 if (!(options & QUrl::RemovePort) && port != -1)
891 appendTo += u':' + QString::number(port);
892}
893
894inline void QUrlPrivate::appendUserInfo(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
895{
896 if (Q_LIKELY(!hasUserInfo()))
897 return;
898
899 const ushort *userNameActions;
900 const ushort *passwordActions;
901 if (options & QUrl::EncodeDelimiters) {
902 userNameActions = userNameInUrl;
903 passwordActions = passwordInUrl;
904 } else {
905 switch (appendingTo) {
906 case UserInfo:
907 userNameActions = userNameInUserInfo;
908 passwordActions = passwordInUserInfo;
909 break;
910
911 case Authority:
912 userNameActions = userNameInAuthority;
913 passwordActions = passwordInAuthority;
914 break;
915
916 case FullUrl:
917 userNameActions = userNameInUrl;
918 passwordActions = passwordInUrl;
919 break;
920
921 default:
922 // can't happen
923 Q_UNREACHABLE();
924 break;
925 }
926 }
927
928 if (!qt_urlRecode(appendTo, userName, options, userNameActions))
929 appendTo += userName;
930 if (options & QUrl::RemovePassword || !hasPassword()) {
931 return;
932 } else {
933 appendTo += u':';
934 if (!qt_urlRecode(appendTo, password, options, passwordActions))
935 appendTo += password;
936 }
937}
938
939inline void QUrlPrivate::appendUserName(QString &appendTo, QUrl::FormattingOptions options) const
940{
941 // only called from QUrl::userName()
942 appendToUser(appendTo, userName, options,
943 options & QUrl::EncodeDelimiters ? userNameInUrl : userNameInIsolation);
944 if (appendTo.isNull() && hasPassword())
945 appendTo.detach(); // the presence of password implies presence of username
946}
947
948inline void QUrlPrivate::appendPassword(QString &appendTo, QUrl::FormattingOptions options) const
949{
950 // only called from QUrl::password()
951 appendToUser(appendTo, password, options,
952 options & QUrl::EncodeDelimiters ? passwordInUrl : passwordInIsolation);
953}
954
955inline void QUrlPrivate::appendPath(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
956{
957 QString thePath = path;
958 if (options & QUrl::NormalizePathSegments)
960
961 QStringView thePathView(thePath);
962 if (options & QUrl::RemoveFilename) {
963 const qsizetype slash = thePathView.lastIndexOf(u'/');
964 if (slash == -1)
965 return;
966 thePathView = thePathView.left(slash + 1);
967 }
968 // check if we need to remove trailing slashes
969 if (options & QUrl::StripTrailingSlash) {
970 while (thePathView.size() > 1 && thePathView.endsWith(u'/'))
971 thePathView.chop(1);
972 }
973
974 appendToUser(appendTo, thePathView, options,
975 appendingTo == FullUrl || options & QUrl::EncodeDelimiters ? pathInUrl : pathInIsolation);
976}
977
978inline void QUrlPrivate::appendFragment(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
979{
980 appendToUser(appendTo, fragment, options,
981 options & QUrl::EncodeDelimiters ? fragmentInUrl :
982 appendingTo == FullUrl ? nullptr : fragmentInIsolation);
983}
984
985inline void QUrlPrivate::appendQuery(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
986{
987 appendToUser(appendTo, query, options,
988 appendingTo == FullUrl || options & QUrl::EncodeDelimiters ? queryInUrl : queryInIsolation);
989}
990
991// setXXX functions
992
993inline bool QUrlPrivate::setScheme(const QString &value, qsizetype len, bool doSetError)
994{
995 // schemes are strictly RFC-compliant:
996 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
997 // we also lowercase the scheme
998
999 // schemes in URLs are not allowed to be empty, but they can be in
1000 // "Relative URIs" which QUrl also supports. QUrl::setScheme does
1001 // not call us with len == 0, so this can only be from parse()
1002 scheme.clear();
1003 if (len == 0)
1004 return false;
1005
1006 sectionIsPresent |= Scheme;
1007
1008 // validate it:
1009 qsizetype needsLowercasing = -1;
1010 const ushort *p = reinterpret_cast<const ushort *>(value.data());
1011 for (qsizetype i = 0; i < len; ++i) {
1012 if (isAsciiLower(p[i]))
1013 continue;
1014 if (isAsciiUpper(p[i])) {
1015 needsLowercasing = i;
1016 continue;
1017 }
1018 if (i) {
1019 if (isAsciiDigit(p[i]))
1020 continue;
1021 if (p[i] == '+' || p[i] == '-' || p[i] == '.')
1022 continue;
1023 }
1024
1025 // found something else
1026 // don't call setError needlessly:
1027 // if we've been called from parse(), it will try to recover
1028 if (doSetError)
1029 setError(InvalidSchemeError, value, i);
1030 return false;
1031 }
1032
1033 scheme = value.left(len);
1034
1035 if (needsLowercasing != -1) {
1036 // schemes are ASCII only, so we don't need the full Unicode toLower
1037 QChar *schemeData = scheme.data(); // force detaching here
1038 for (qsizetype i = needsLowercasing; i >= 0; --i) {
1039 ushort c = schemeData[i].unicode();
1040 if (isAsciiUpper(c))
1041 schemeData[i] = QChar(c + 0x20);
1042 }
1043 }
1044
1045 // did we set to the file protocol?
1046 if (scheme == fileScheme()
1047#ifdef Q_OS_WIN
1048 || scheme == webDavScheme()
1049#endif
1050 ) {
1051 flags |= IsLocalFile;
1052 } else {
1053 flags &= ~IsLocalFile;
1054 }
1055 return true;
1056}
1057
1058inline void QUrlPrivate::setAuthority(const QString &auth, qsizetype from, qsizetype end, QUrl::ParsingMode mode)
1059{
1060 Q_ASSERT_X(mode != QUrl::DecodedMode, "setAuthority",
1061 "This function should only be called when parsing encoded components");
1062 sectionIsPresent &= ~Authority;
1063 port = -1;
1064 if (from == end && !auth.isNull())
1065 sectionIsPresent |= Host; // empty but not null authority implies host
1066
1067 // we never actually _loop_
1068 while (from != end) {
1069 qsizetype userInfoIndex = auth.indexOf(u'@', from);
1070 if (size_t(userInfoIndex) < size_t(end)) {
1071 setUserInfo(QStringView(auth).sliced(from, userInfoIndex - from), mode);
1072 if (mode == QUrl::StrictMode && !validateComponent(UserInfo, auth, from, userInfoIndex))
1073 break;
1074 from = userInfoIndex + 1;
1075 }
1076
1077 qsizetype colonIndex = auth.lastIndexOf(u':', end - 1);
1078 if (colonIndex < from)
1079 colonIndex = -1;
1080
1081 if (size_t(colonIndex) < size_t(end)) {
1082 if (auth.at(from).unicode() == '[') {
1083 // check if colonIndex isn't inside the "[...]" part
1084 qsizetype closingBracket = auth.indexOf(u']', from);
1085 if (size_t(closingBracket) > size_t(colonIndex))
1086 colonIndex = -1;
1087 }
1088 }
1089
1090 if (size_t(colonIndex) < size_t(end) - 1) {
1091 // found a colon with digits after it
1092 unsigned long x = 0;
1093 for (qsizetype i = colonIndex + 1; i < end; ++i) {
1094 ushort c = auth.at(i).unicode();
1095 if (isAsciiDigit(c)) {
1096 x *= 10;
1097 x += c - '0';
1098 } else {
1099 x = ulong(-1); // x != ushort(x)
1100 break;
1101 }
1102 }
1103 if (x == ushort(x)) {
1104 port = ushort(x);
1105 } else {
1106 setError(InvalidPortError, auth, colonIndex + 1);
1107 if (mode == QUrl::StrictMode)
1108 break;
1109 }
1110 }
1111
1112 setHost(auth, from, qMin<size_t>(end, colonIndex), mode);
1113 if (mode == QUrl::StrictMode && !validateComponent(Host, auth, from, qMin<size_t>(end, colonIndex))) {
1114 // clear host too
1115 sectionIsPresent &= ~Authority;
1116 break;
1117 }
1118
1119 // success
1120 return;
1121 }
1122 // clear all sections but host
1123 sectionIsPresent &= ~Authority | Host;
1124 userName.clear();
1125 password.clear();
1126 host.clear();
1127 port = -1;
1128}
1129
1130template <typename String> void QUrlPrivate::setUserInfo(String &&value, QUrl::ParsingMode mode)
1131{
1132 Q_ASSERT_X(mode != QUrl::DecodedMode, "setUserInfo",
1133 "This function should only be called when parsing encoded components");
1134 qsizetype delimIndex = value.indexOf(u':');
1135 if (delimIndex < 0) {
1136 // no password
1137 setUserName(std::move(value), mode);
1138 password.clear();
1139 sectionIsPresent &= ~Password;
1140 } else {
1141 setUserName(value.first(delimIndex), mode);
1142 setPassword(value.sliced(delimIndex + 1), mode);
1143 }
1144}
1145
1146template <typename String> inline void QUrlPrivate::setUserName(String &&value, QUrl::ParsingMode mode)
1147{
1148 sectionIsPresent |= UserName;
1149 recodeFromUser(userName, value, userNameInIsolation, mode);
1150}
1151
1152template <typename String> inline void QUrlPrivate::setPassword(String &&value, QUrl::ParsingMode mode)
1153{
1154 sectionIsPresent |= Password;
1155 recodeFromUser(password, value, passwordInIsolation, mode);
1156}
1157
1158template <typename String> inline void QUrlPrivate::setPath(String &&value, QUrl::ParsingMode mode)
1159{
1160 // sectionIsPresent |= Path; // not used, save some cycles
1161 recodeFromUser(path, value, pathInIsolation, mode);
1162}
1163
1164template <typename String> inline void QUrlPrivate::setFragment(String &&value, QUrl::ParsingMode mode)
1165{
1166 sectionIsPresent |= Fragment;
1167 recodeFromUser(fragment, value, fragmentInIsolation, mode);
1168}
1169
1170template <typename String> inline void QUrlPrivate::setQuery(String &&value, QUrl::ParsingMode mode)
1171{
1172 sectionIsPresent |= Query;
1173 recodeFromUser(query, value, queryInIsolation, mode);
1174}
1175
1176// Host handling
1177// The RFC says the host is:
1178// host = IP-literal / IPv4address / reg-name
1179// IP-literal = "[" ( IPv6address / IPvFuture ) "]"
1180// IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
1181// [a strict definition of IPv6Address and IPv4Address]
1182// reg-name = *( unreserved / pct-encoded / sub-delims )
1183//
1184// We deviate from the standard in all but IPvFuture. For IPvFuture we accept
1185// and store only exactly what the RFC says we should. No percent-encoding is
1186// permitted in this field, so Unicode characters and space aren't either.
1187//
1188// For IPv4 addresses, we accept broken addresses like inet_aton does (that is,
1189// less than three dots). However, we correct the address to the proper form
1190// and store the corrected address. After correction, we comply to the RFC and
1191// it's exclusively composed of unreserved characters.
1192//
1193// For IPv6 addresses, we accept addresses including trailing (embedded) IPv4
1194// addresses, the so-called v4-compat and v4-mapped addresses. We also store
1195// those addresses like that in the hostname field, which violates the spec.
1196// IPv6 hosts are stored with the square brackets in the QString. It also
1197// requires no transformation in any way.
1198//
1199// As for registered names, it's the other way around: we accept only valid
1200// hostnames as specified by STD 3 and IDNA. That means everything we accept is
1201// valid in the RFC definition above, but there are many valid reg-names
1202// according to the RFC that we do not accept in the name of security. Since we
1203// do accept IDNA, reg-names are subject to ACE encoding and decoding, which is
1204// specified by the DecodeUnicode flag. The hostname is stored in its Unicode form.
1205
1206inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions options) const
1207{
1208 if (host.isEmpty()) {
1209 if ((sectionIsPresent & Host) && appendTo.isNull())
1210 appendTo.detach();
1211 return;
1212 }
1213 if (host.at(0).unicode() == '[') {
1214 // IPv6 addresses might contain a zone-id which needs to be recoded
1215 if (options != 0)
1216 if (qt_urlRecode(appendTo, host, options, nullptr))
1217 return;
1218 appendTo += host;
1219 } else {
1220 // this is either an IPv4Address or a reg-name
1221 // if it is a reg-name, it is already stored in Unicode form
1222 if (options & QUrl::EncodeUnicode && !(options & 0x4000000))
1223 appendTo += qt_ACE_do(host, ToAceOnly, AllowLeadingDot, {});
1224 else
1225 appendTo += host;
1226 }
1227}
1228
1229// the whole IPvFuture is passed and parsed here, including brackets;
1230// returns null if the parsing was successful, or the QChar of the first failure
1231static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar *end, QUrl::ParsingMode mode)
1232{
1233 // IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
1234 static const char acceptable[] =
1235 "!$&'()*+,;=" // sub-delims
1236 ":" // ":"
1237 "-._~"; // unreserved
1238
1239 // the brackets and the "v" have been checked
1240 const QChar *const origBegin = begin;
1241 if (begin[3].unicode() != '.')
1242 return &begin[3];
1243 if (isHexDigit(begin[2].unicode())) {
1244 // this is so unlikely that we'll just go down the slow path
1245 // decode the whole string, skipping the "[vH." and "]" which we already know to be there
1246 host += QStringView(begin, 4);
1247
1248 // uppercase the version, if necessary
1249 if (begin[2].unicode() >= 'a')
1250 host[host.size() - 2] = QChar{begin[2].unicode() - 0x20};
1251
1252 begin += 4;
1253 --end;
1254
1255 QString decoded;
1256 if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, QStringView{begin, end}, QUrl::FullyDecoded, nullptr)) {
1257 begin = decoded.constBegin();
1258 end = decoded.constEnd();
1259 }
1260
1261 for ( ; begin != end; ++begin) {
1262 if (isAsciiLetterOrNumber(begin->unicode()))
1263 host += *begin;
1264 else if (begin->unicode() < 0x80 && strchr(acceptable, begin->unicode()) != nullptr)
1265 host += *begin;
1266 else
1267 return decoded.isEmpty() ? begin : &origBegin[2];
1268 }
1269 host += u']';
1270 return nullptr;
1271 }
1272 return &origBegin[2];
1273}
1274
1275// ONLY the IPv6 address is parsed here, WITHOUT the brackets
1276static const QChar *parseIp6(QString &host, const QChar *begin, const QChar *end, QUrl::ParsingMode mode)
1277{
1278 QStringView decoded(begin, end);
1279 QString decodedBuffer;
1280 if (mode == QUrl::TolerantMode) {
1281 // this struct is kept in automatic storage because it's only 4 bytes
1282 const ushort decodeColon[] = { decode(':'), 0 };
1283 if (qt_urlRecode(decodedBuffer, decoded, QUrl::ComponentFormattingOption::PrettyDecoded, decodeColon))
1284 decoded = decodedBuffer;
1285 }
1286
1287 const QStringView zoneIdIdentifier(u"%25");
1288 QIPAddressUtils::IPv6Address address;
1289 QStringView zoneId;
1290
1291 qsizetype zoneIdPosition = decoded.indexOf(zoneIdIdentifier);
1292 if ((zoneIdPosition != -1) && (decoded.lastIndexOf(zoneIdIdentifier) == zoneIdPosition)) {
1293 zoneId = decoded.mid(zoneIdPosition + zoneIdIdentifier.size());
1294 decoded.truncate(zoneIdPosition);
1295
1296 // was there anything after the zone ID separator?
1297 if (zoneId.isEmpty())
1298 return end;
1299 }
1300
1301 // did the address become empty after removing the zone ID?
1302 // (it might have always been empty)
1303 if (decoded.isEmpty())
1304 return end;
1305
1306 const QChar *ret = QIPAddressUtils::parseIp6(address, decoded.constBegin(), decoded.constEnd());
1307 if (ret)
1308 return begin + (ret - decoded.constBegin());
1309
1310 host.reserve(host.size() + (end - begin) + 2); // +2 for the brackets
1311 host += u'[';
1312 QIPAddressUtils::toString(host, address);
1313
1314 if (!zoneId.isEmpty()) {
1315 host += zoneIdIdentifier;
1316 host += zoneId;
1317 }
1318 host += u']';
1319 return nullptr;
1320}
1321
1322inline bool
1323QUrlPrivate::setHost(const QString &value, qsizetype from, qsizetype iend, QUrl::ParsingMode mode)
1324{
1325 Q_ASSERT_X(mode != QUrl::DecodedMode, "setUserInfo",
1326 "This function should only be called when parsing encoded components");
1327 const QChar *begin = value.constData() + from;
1328 const QChar *end = value.constData() + iend;
1329
1330 const qsizetype len = end - begin;
1331 host.clear();
1332 sectionIsPresent &= ~Host;
1333 if (!value.isNull() || (sectionIsPresent & Authority))
1334 sectionIsPresent |= Host;
1335 if (len == 0)
1336 return true;
1337
1338 if (begin[0].unicode() == '[') {
1339 // IPv6Address or IPvFuture
1340 // smallest IPv6 address is "[::]" (len = 4)
1341 // smallest IPvFuture address is "[v7.X]" (len = 6)
1342 if (end[-1].unicode() != ']') {
1343 setError(HostMissingEndBracket, value);
1344 return false;
1345 }
1346
1347 if (len > 5 && begin[1].unicode() == 'v') {
1348 const QChar *c = parseIpFuture(host, begin, end, mode);
1349 if (c)
1350 setError(InvalidIPvFutureError, value, c - value.constData());
1351 return !c;
1352 } else if (begin[1].unicode() == 'v') {
1353 setError(InvalidIPvFutureError, value, from);
1354 }
1355
1356 const QChar *c = parseIp6(host, begin + 1, end - 1, mode);
1357 if (!c)
1358 return true;
1359
1360 if (c == end - 1)
1361 setError(InvalidIPv6AddressError, value, from);
1362 else
1363 setError(InvalidCharacterInIPv6Error, value, c - value.constData());
1364 return false;
1365 }
1366
1367 // check if it's an IPv4 address
1368 QIPAddressUtils::IPv4Address ip4;
1369 if (QIPAddressUtils::parseIp4(ip4, begin, end)) {
1370 // yes, it was
1371 QIPAddressUtils::toString(host, ip4);
1372 return true;
1373 }
1374
1375 // This is probably a reg-name.
1376 // But it can also be an encoded string that, when decoded becomes one
1377 // of the types above.
1378 //
1379 // Two types of encoding are possible:
1380 // percent encoding (e.g., "%31%30%2E%30%2E%30%2E%31" -> "10.0.0.1")
1381 // Unicode encoding (some non-ASCII characters case-fold to digits
1382 // when nameprepping is done)
1383 //
1384 // The qt_ACE_do function below does IDNA normalization and the STD3 check.
1385 // That means a Unicode string may become an IPv4 address, but it cannot
1386 // produce a '[' or a '%'.
1387
1388 // check for percent-encoding first
1389 QString s;
1390 if (mode == QUrl::TolerantMode && qt_urlRecode(s, QStringView{begin, end}, { }, nullptr)) {
1391 // something was decoded
1392 // anything encoded left?
1393 qsizetype pos = s.indexOf(QChar(0x25)); // '%'
1394 if (pos != -1) {
1395 setError(InvalidRegNameError, s, pos);
1396 return false;
1397 }
1398
1399 // recurse
1400 return setHost(s, 0, s.size(), QUrl::StrictMode);
1401 }
1402
1403 s = qt_ACE_do(value.mid(from, iend - from), NormalizeAce, ForbidLeadingDot, {});
1404 if (s.isEmpty()) {
1405 setError(InvalidRegNameError, value);
1406 return false;
1407 }
1408
1409 // check IPv4 again
1410 if (QIPAddressUtils::parseIp4(ip4, s.constBegin(), s.constEnd())) {
1411 QIPAddressUtils::toString(host, ip4);
1412 } else {
1413 host = s;
1414 }
1415 return true;
1416}
1417
1418inline void QUrlPrivate::parse(const QString &url, QUrl::ParsingMode parsingMode)
1419{
1420 // URI-reference = URI / relative-ref
1421 // URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
1422 // relative-ref = relative-part [ "?" query ] [ "#" fragment ]
1423 // hier-part = "//" authority path-abempty
1424 // / other path types
1425 // relative-part = "//" authority path-abempty
1426 // / other path types here
1427
1428 Q_ASSERT_X(parsingMode != QUrl::DecodedMode, "parse",
1429 "This function should only be called when parsing encoded URLs");
1430 Q_ASSERT(sectionIsPresent == 0);
1431 Q_ASSERT(!error);
1432
1433 // find the important delimiters
1434 qsizetype colon = -1;
1435 qsizetype question = -1;
1436 qsizetype hash = -1;
1437 const qsizetype len = url.size();
1438 const QChar *const begin = url.constData();
1439 const ushort *const data = reinterpret_cast<const ushort *>(begin);
1440
1441 for (qsizetype i = 0; i < len; ++i) {
1442 size_t uc = data[i];
1443 if (uc == '#' && hash == -1) {
1444 hash = i;
1445
1446 // nothing more to be found
1447 break;
1448 }
1449
1450 if (question == -1) {
1451 if (uc == ':' && colon == -1)
1452 colon = i;
1453 else if (uc == '?')
1454 question = i;
1455 }
1456 }
1457
1458 // check if we have a scheme
1459 qsizetype hierStart;
1460 if (colon != -1 && setScheme(url, colon, /* don't set error */ false)) {
1461 hierStart = colon + 1;
1462 } else {
1463 // recover from a failed scheme: it might not have been a scheme at all
1464 scheme.clear();
1465 sectionIsPresent = 0;
1466 hierStart = 0;
1467 }
1468
1469 qsizetype pathStart;
1470 qsizetype hierEnd = qMin<size_t>(qMin<size_t>(question, hash), len);
1471 if (hierEnd - hierStart >= 2 && data[hierStart] == '/' && data[hierStart + 1] == '/') {
1472 // we have an authority, it ends at the first slash after these
1473 qsizetype authorityEnd = hierEnd;
1474 for (qsizetype i = hierStart + 2; i < authorityEnd ; ++i) {
1475 if (data[i] == '/') {
1476 authorityEnd = i;
1477 break;
1478 }
1479 }
1480
1481 setAuthority(url, hierStart + 2, authorityEnd, parsingMode);
1482
1483 // even if we failed to set the authority properly, let's try to recover
1484 pathStart = authorityEnd;
1485 setPath(QStringView(url).sliced(pathStart, hierEnd - pathStart), parsingMode);
1486 } else {
1487 Q_ASSERT(userName.isNull());
1488 Q_ASSERT(password.isNull());
1489 Q_ASSERT(host.isNull());
1490 Q_ASSERT(port == -1);
1491 pathStart = hierStart;
1492
1493 if (hierStart < hierEnd)
1494 setPath(QStringView(url).sliced(hierStart, hierEnd - hierStart), parsingMode);
1495 else
1496 path.clear();
1497 }
1498
1499 Q_ASSERT(query.isNull());
1500 if (size_t(question) < size_t(hash))
1501 setQuery(QStringView(url).sliced(question + 1, qMin<size_t>(hash, len) - question - 1),
1502 parsingMode);
1503
1504 Q_ASSERT(fragment.isNull());
1505 if (hash != -1)
1506 setFragment(QStringView(url).sliced(hash + 1, len - hash - 1), parsingMode);
1507
1508 if (error || parsingMode == QUrl::TolerantMode)
1509 return;
1510
1511 // The parsing so far was partially tolerant of errors, except for the
1512 // scheme parser (which is always strict) and the authority (which was
1513 // executed in strict mode).
1514 // If we haven't found any errors so far, continue the strict-mode parsing
1515 // from the path component onwards.
1516
1517 if (!validateComponent(Path, url, pathStart, hierEnd))
1518 return;
1519 if (size_t(question) < size_t(hash) && !validateComponent(Query, url, question + 1, qMin<size_t>(hash, len)))
1520 return;
1521 if (hash != -1)
1522 validateComponent(Fragment, url, hash + 1, len);
1523}
1524
1525QString QUrlPrivate::toLocalFile(QUrl::FormattingOptions options) const
1526{
1527 QString tmp;
1528 QString ourPath;
1529 appendPath(ourPath, options, QUrlPrivate::Path);
1530
1531 // magic for shared drive on windows
1532 if (!host.isEmpty()) {
1533 tmp = "//"_L1 + host;
1534#ifdef Q_OS_WIN // QTBUG-42346, WebDAV is visible as local file on Windows only.
1535 if (scheme == webDavScheme())
1536 tmp += webDavSslTag();
1537#endif
1538 if (!ourPath.isEmpty() && !ourPath.startsWith(u'/'))
1539 tmp += u'/';
1540 tmp += ourPath;
1541 } else {
1542 tmp = ourPath;
1543#ifdef Q_OS_WIN
1544 // magic for drives on windows
1545 if (ourPath.length() > 2 && ourPath.at(0) == u'/' && ourPath.at(2) == u':')
1546 tmp.remove(0, 1);
1547#endif
1548 }
1549 return tmp;
1550}
1551
1552/*
1553 From http://www.ietf.org/rfc/rfc3986.txt, 5.2.3: Merge paths
1554
1555 Returns a merge of the current path with the relative path passed
1556 as argument.
1557
1558 Note: \a relativePath is relative (does not start with '/').
1559*/
1560inline QString QUrlPrivate::mergePaths(const QString &relativePath) const
1561{
1562 // If the base URI has a defined authority component and an empty
1563 // path, then return a string consisting of "/" concatenated with
1564 // the reference's path; otherwise,
1565 if (!host.isEmpty() && path.isEmpty())
1566 return u'/' + relativePath;
1567
1568 // Return a string consisting of the reference's path component
1569 // appended to all but the last segment of the base URI's path
1570 // (i.e., excluding any characters after the right-most "/" in the
1571 // base URI path, or excluding the entire base URI path if it does
1572 // not contain any "/" characters).
1573 QString newPath;
1574 if (!path.contains(u'/'))
1575 newPath = relativePath;
1576 else
1577 newPath = QStringView{path}.left(path.lastIndexOf(u'/') + 1) + relativePath;
1578
1579 return newPath;
1580}
1581
1582// Authority-less URLs cannot have paths starting with double slashes (see
1583// QUrlPrivate::validityError). We refuse to turn a valid URL into invalid by
1584// way of QUrl::resolved().
1585static void fixupNonAuthorityPath(QString *path)
1586{
1587 if (path->isEmpty() || path->at(0) != u'/')
1588 return;
1589
1590 // Find the first non-slash character, because its position is equal to the
1591 // number of slashes. We'll remove all but one of them.
1592 qsizetype i = 0;
1593 while (i + 1 < path->size() && path->at(i + 1) == u'/')
1594 ++i;
1595 if (i)
1596 path->remove(0, i);
1597}
1598
1599inline QUrlPrivate::ErrorCode QUrlPrivate::validityError(QString *source, qsizetype *position) const
1600{
1601 Q_ASSERT(!source == !position);
1602 if (error) {
1603 if (source) {
1604 *source = error->source;
1605 *position = error->position;
1606 }
1607 return error->code;
1608 }
1609
1610 // There are three more cases of invalid URLs that QUrl recognizes and they
1611 // are only possible with constructed URLs (setXXX methods), not with
1612 // parsing. Therefore, they are tested here.
1613 //
1614 // Two cases are a non-empty path that doesn't start with a slash and:
1615 // - with an authority
1616 // - without an authority, without scheme but the path with a colon before
1617 // the first slash
1618 // The third case is an empty authority and a non-empty path that starts
1619 // with "//".
1620 // Those cases are considered invalid because toString() would produce a URL
1621 // that wouldn't be parsed back to the same QUrl.
1622
1623 if (path.isEmpty())
1624 return NoError;
1625 if (path.at(0) == u'/') {
1626 if (hasAuthority() || path.size() == 1 || path.at(1) != u'/')
1627 return NoError;
1628 if (source) {
1629 *source = path;
1630 *position = 0;
1631 }
1633 }
1634
1635 if (sectionIsPresent & QUrlPrivate::Host) {
1636 if (source) {
1637 *source = path;
1638 *position = 0;
1639 }
1641 }
1642 if (sectionIsPresent & QUrlPrivate::Scheme)
1643 return NoError;
1644
1645 // check for a path of "text:text/"
1646 for (qsizetype i = 0; i < path.size(); ++i) {
1647 ushort c = path.at(i).unicode();
1648 if (c == '/') {
1649 // found the slash before the colon
1650 return NoError;
1651 }
1652 if (c == ':') {
1653 // found the colon before the slash, it's invalid
1654 if (source) {
1655 *source = path;
1656 *position = i;
1657 }
1659 }
1660 }
1661 return NoError;
1662}
1663
1664bool QUrlPrivate::validateComponent(QUrlPrivate::Section section, const QString &input,
1665 qsizetype begin, qsizetype end)
1666{
1667 // What we need to look out for, that the regular parser tolerates:
1668 // - percent signs not followed by two hex digits
1669 // - forbidden characters, which should always appear encoded
1670 // '"' / '<' / '>' / '\' / '^' / '`' / '{' / '|' / '}' / BKSP
1671 // control characters
1672 // - delimiters not allowed in certain positions
1673 // . scheme: parser is already strict
1674 // . user info: gen-delims except ":" disallowed ("/" / "?" / "#" / "[" / "]" / "@")
1675 // . host: parser is stricter than the standard
1676 // . port: parser is stricter than the standard
1677 // . path: all delimiters allowed
1678 // . fragment: all delimiters allowed
1679 // . query: all delimiters allowed
1680 static const char forbidden[] = "\"<>\\^`{|}\x7F";
1681 static const char forbiddenUserInfo[] = ":/?#[]@";
1682
1683 Q_ASSERT(section != Authority && section != Hierarchy && section != FullUrl);
1684
1685 const ushort *const data = reinterpret_cast<const ushort *>(input.constData());
1686 for (size_t i = size_t(begin); i < size_t(end); ++i) {
1687 uint uc = data[i];
1688 if (uc >= 0x80)
1689 continue;
1690
1691 bool error = false;
1692 if ((uc == '%' && (size_t(end) < i + 2 || !isHex(data[i + 1]) || !isHex(data[i + 2])))
1693 || uc <= 0x20 || strchr(forbidden, uc)) {
1694 // found an error
1695 error = true;
1696 } else if (section & UserInfo) {
1697 if (section == UserInfo && strchr(forbiddenUserInfo + 1, uc))
1698 error = true;
1699 else if (section != UserInfo && strchr(forbiddenUserInfo, uc))
1700 error = true;
1701 }
1702
1703 if (!error)
1704 continue;
1705
1706 ErrorCode errorCode = ErrorCode(int(section) << 8);
1707 if (section == UserInfo) {
1708 // is it the user name or the password?
1709 errorCode = InvalidUserNameError;
1710 for (size_t j = size_t(begin); j < i; ++j)
1711 if (data[j] == ':') {
1712 errorCode = InvalidPasswordError;
1713 break;
1714 }
1715 }
1716
1717 setError(errorCode, input, i);
1718 return false;
1719 }
1720
1721 // no errors
1722 return true;
1723}
1724
1725#if 0
1726inline void QUrlPrivate::validate() const
1727{
1728 QUrlPrivate *that = (QUrlPrivate *)this;
1729 that->encodedOriginal = that->toEncoded(); // may detach
1730 parse(ParseOnly);
1731
1732 QURL_SETFLAG(that->stateFlags, Validated);
1733
1734 if (!isValid)
1735 return;
1736
1737 QString auth = authority(); // causes the non-encoded forms to be valid
1738
1739 // authority() calls canonicalHost() which sets this
1740 if (!isHostValid)
1741 return;
1742
1743 if (scheme == "mailto"_L1) {
1744 if (!host.isEmpty() || port != -1 || !userName.isEmpty() || !password.isEmpty()) {
1745 that->isValid = false;
1746 that->errorInfo.setParams(0, QT_TRANSLATE_NOOP(QUrl, "expected empty host, username,"
1747 "port and password"),
1748 0, 0);
1749 }
1750 } else if (scheme == ftpScheme() || scheme == httpScheme()) {
1751 if (host.isEmpty() && !(path.isEmpty() && encodedPath.isEmpty())) {
1752 that->isValid = false;
1753 that->errorInfo.setParams(0, QT_TRANSLATE_NOOP(QUrl, "the host is empty, but not the path"),
1754 0, 0);
1755 }
1756 }
1757}
1758#endif
1759
1760/*!
1761 \macro QT_NO_URL_CAST_FROM_STRING
1762 \relates QUrl
1763
1764 Disables automatic conversions from QString (or char *) to QUrl.
1765
1766 Compiling your code with this define is useful when you have a lot of
1767 code that uses QString for file names and you wish to convert it to
1768 use QUrl for network transparency. In any code that uses QUrl, it can
1769 help avoid missing QUrl::resolved() calls, and other misuses of
1770 QString to QUrl conversions.
1771
1772 For example, if you have code like
1773
1774 \code
1775 url = filename; // probably not what you want
1776 \endcode
1777
1778 you can rewrite it as
1779
1780 \code
1781 url = QUrl::fromLocalFile(filename);
1782 url = baseurl.resolved(QUrl(filename));
1783 \endcode
1784
1785 \sa QT_NO_CAST_FROM_ASCII
1786*/
1787
1788
1789/*!
1790 Constructs a URL by parsing \a url. Note this constructor expects a proper
1791 URL or URL-Reference and will not attempt to guess intent. For example, the
1792 following declaration:
1793
1794 \snippet code/src_corelib_io_qurl.cpp constructor-url-reference
1795
1796 Will construct a valid URL but it may not be what one expects, as the
1797 scheme() part of the input is missing. For a string like the above,
1798 applications may want to use fromUserInput(). For this constructor or
1799 setUrl(), the following is probably what was intended:
1800
1801 \snippet code/src_corelib_io_qurl.cpp constructor-url
1802
1803 QUrl will automatically percent encode
1804 all characters that are not allowed in a URL and decode the percent-encoded
1805 sequences that represent an unreserved character (letters, digits, hyphens,
1806 underscores, dots and tildes). All other characters are left in their
1807 original forms.
1808
1809 Parses the \a url using the parser mode \a parsingMode. In TolerantMode
1810 (the default), QUrl will correct certain mistakes, notably the presence of
1811 a percent character ('%') not followed by two hexadecimal digits, and it
1812 will accept any character in any position. In StrictMode, encoding mistakes
1813 will not be tolerated and QUrl will also check that certain forbidden
1814 characters are not present in unencoded form. If an error is detected in
1815 StrictMode, isValid() will return false. The parsing mode DecodedMode is not
1816 permitted in this context.
1817
1818 Example:
1819
1820 \snippet code/src_corelib_io_qurl.cpp 0
1821
1822 To construct a URL from an encoded string, you can also use fromEncoded():
1823
1824 \snippet code/src_corelib_io_qurl.cpp 1
1825
1826 Both functions are equivalent and, in Qt 5, both functions accept encoded
1827 data. Usually, the choice of the QUrl constructor or setUrl() versus
1828 fromEncoded() will depend on the source data: the constructor and setUrl()
1829 take a QString, whereas fromEncoded takes a QByteArray.
1830
1831 \sa setUrl(), fromEncoded(), TolerantMode
1832*/
1833QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(nullptr)
1834{
1835 setUrl(url, parsingMode);
1836}
1837
1838/*!
1839 Constructs an empty QUrl object.
1840*/
1841QUrl::QUrl() : d(nullptr)
1842{
1843}
1844
1845/*!
1846 Constructs a copy of \a other.
1847*/
1848QUrl::QUrl(const QUrl &other) noexcept : d(other.d)
1849{
1850 if (d)
1851 d->ref.ref();
1852}
1853
1854/*!
1855 Destructor; called immediately before the object is deleted.
1856*/
1857QUrl::~QUrl()
1858{
1859 if (d && !d->ref.deref())
1860 delete d;
1861}
1862
1863/*!
1864 Returns \c true if the URL is non-empty and valid; otherwise returns \c false.
1865
1866 The URL is run through a conformance test. Every part of the URL
1867 must conform to the standard encoding rules of the URI standard
1868 for the URL to be reported as valid.
1869
1870 \snippet code/src_corelib_io_qurl.cpp 2
1871*/
1872bool QUrl::isValid() const
1873{
1874 if (isEmpty()) {
1875 // also catches d == nullptr
1876 return false;
1877 }
1878 return d->validityError() == QUrlPrivate::NoError;
1879}
1880
1881/*!
1882 Returns \c true if the URL has no data; otherwise returns \c false.
1883
1884 \sa clear()
1885*/
1886bool QUrl::isEmpty() const
1887{
1888 if (!d) return true;
1889 return d->isEmpty();
1890}
1891
1892/*!
1893 Resets the content of the QUrl. After calling this function, the
1894 QUrl is equal to one that has been constructed with the default
1895 empty constructor.
1896
1897 \sa isEmpty()
1898*/
1899void QUrl::clear()
1900{
1901 if (d && !d->ref.deref())
1902 delete d;
1903 d = nullptr;
1904}
1905
1906/*!
1907 Parses \a url and sets this object to that value. QUrl will automatically
1908 percent encode all characters that are not allowed in a URL and decode the
1909 percent-encoded sequences that represent an unreserved character (letters,
1910 digits, hyphens, underscores, dots and tildes). All other characters are
1911 left in their original forms.
1912
1913 Parses the \a url using the parser mode \a parsingMode. In TolerantMode
1914 (the default), QUrl will correct certain mistakes, notably the presence of
1915 a percent character ('%') not followed by two hexadecimal digits, and it
1916 will accept any character in any position. In StrictMode, encoding mistakes
1917 will not be tolerated and QUrl will also check that certain forbidden
1918 characters are not present in unencoded form. If an error is detected in
1919 StrictMode, isValid() will return false. The parsing mode DecodedMode is
1920 not permitted in this context and will produce a run-time warning.
1921
1922 \sa url(), toString()
1923*/
1924void QUrl::setUrl(const QString &url, ParsingMode parsingMode)
1925{
1926 if (parsingMode == DecodedMode) {
1927 qWarning("QUrl: QUrl::DecodedMode is not permitted when parsing a full URL");
1928 } else {
1929 detachToClear();
1930 d->parse(url, parsingMode);
1931 }
1932}
1933
1934/*!
1935 Sets the scheme of the URL to \a scheme. As a scheme can only
1936 contain ASCII characters, no conversion or decoding is done on the
1937 input. It must also start with an ASCII letter.
1938
1939 The scheme describes the type (or protocol) of the URL. It's
1940 represented by one or more ASCII characters at the start the URL.
1941
1942 A scheme is strictly \l {RFC 3986}-compliant:
1943 \tt {scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )}
1944
1945 The following example shows a URL where the scheme is "ftp":
1946
1947 \image qurl-authority2.png {Illustration highlighting 'ftp' as scheme
1948 of an example URL starting with 'ftp://'.}
1949
1950 To set the scheme, the following call is used:
1951 \snippet code/src_corelib_io_qurl.cpp 11
1952
1953 The scheme can also be empty, in which case the URL is interpreted
1954 as relative.
1955
1956 \sa scheme(), isRelative()
1957*/
1958void QUrl::setScheme(const QString &scheme)
1959{
1960 detach();
1961 d->clearError();
1962 if (scheme.isEmpty()) {
1963 // schemes are not allowed to be empty
1964 d->sectionIsPresent &= ~QUrlPrivate::Scheme;
1965 d->flags &= ~QUrlPrivate::IsLocalFile;
1966 d->scheme.clear();
1967 } else {
1968 d->setScheme(scheme, scheme.size(), /* do set error */ true);
1969 }
1970}
1971
1972/*!
1973 Returns the scheme of the URL. If an empty string is returned,
1974 this means the scheme is undefined and the URL is then relative.
1975
1976 The scheme can only contain US-ASCII letters or digits, which means it
1977 cannot contain any character that would otherwise require encoding.
1978 Additionally, schemes are always returned in lowercase form.
1979
1980 \sa setScheme(), isRelative()
1981*/
1982QString QUrl::scheme() const
1983{
1984 if (!d) return QString();
1985
1986 return d->scheme;
1987}
1988
1989/*!
1990 Sets the authority of the URL to \a authority.
1991
1992 The authority of a URL is the combination of user info, a host
1993 name and a port. All of these elements are optional; an empty
1994 authority is therefore valid.
1995
1996 The user info and host are separated by a '@', and the host and
1997 port are separated by a ':'. If the user info is empty, the '@'
1998 must be omitted; although a stray ':' is permitted if the port is
1999 empty.
2000
2001 The following example shows a valid authority string:
2002
2003 \image qurl-authority.png {Screenshot of a URL with parts labeled: scheme,
2004 authority, user info (user and password), host, and port.}
2005
2006 The \a authority data is interpreted according to \a mode: in StrictMode,
2007 any '%' characters must be followed by exactly two hexadecimal characters
2008 and some characters (including space) are not allowed in undecoded form. In
2009 TolerantMode (the default), all characters are accepted in undecoded form
2010 and the tolerant parser will correct stray '%' not followed by two hex
2011 characters.
2012
2013 This function does not allow \a mode to be QUrl::DecodedMode. To set fully
2014 decoded data, call setUserName(), setPassword(), setHost() and setPort()
2015 individually.
2016
2017 \sa setUserInfo(), setHost(), setPort()
2018*/
2019void QUrl::setAuthority(const QString &authority, ParsingMode mode)
2020{
2021 detach();
2022 d->clearError();
2023
2024 if (mode == DecodedMode) {
2025 qWarning("QUrl::setAuthority(): QUrl::DecodedMode is not permitted in this function");
2026 return;
2027 }
2028
2029 d->setAuthority(authority, 0, authority.size(), mode);
2030}
2031
2032/*!
2033 Returns the authority of the URL if it is defined; otherwise
2034 an empty string is returned.
2035
2036 This function returns an unambiguous value, which may contain that
2037 characters still percent-encoded, plus some control sequences not
2038 representable in decoded form in QString.
2039
2040 The \a options argument controls how to format the user info component. The
2041 value of QUrl::FullyDecoded is not permitted in this function. If you need
2042 to obtain fully decoded data, call userName(), password(), host() and
2043 port() individually.
2044
2045 \sa setAuthority(), userInfo(), userName(), password(), host(), port()
2046*/
2047QString QUrl::authority(ComponentFormattingOptions options) const
2048{
2049 QString result;
2050 if (!d)
2051 return result;
2052
2053 if (options == QUrl::FullyDecoded) {
2054 qWarning("QUrl::authority(): QUrl::FullyDecoded is not permitted in this function");
2055 return result;
2056 }
2057
2058 d->appendAuthority(result, options, QUrlPrivate::Authority);
2059 return result;
2060}
2061
2062/*!
2063 Sets the user info of the URL to \a userInfo. The user info is an
2064 optional part of the authority of the URL, as described in
2065 setAuthority().
2066
2067 The user info consists of a user name and optionally a password,
2068 separated by a ':'. If the password is empty, the colon must be
2069 omitted. The following example shows a valid user info string:
2070
2071 \image qurl-authority3.png {Screenshot of a URL with user info highlighted}
2072
2073 The \a userInfo data is interpreted according to \a mode: in StrictMode,
2074 any '%' characters must be followed by exactly two hexadecimal characters
2075 and some characters (including space) are not allowed in undecoded form. In
2076 TolerantMode (the default), all characters are accepted in undecoded form
2077 and the tolerant parser will correct stray '%' not followed by two hex
2078 characters.
2079
2080 This function does not allow \a mode to be QUrl::DecodedMode. To set fully
2081 decoded data, call setUserName() and setPassword() individually.
2082
2083 \sa userInfo(), setUserName(), setPassword(), setAuthority()
2084*/
2085void QUrl::setUserInfo(const QString &userInfo, ParsingMode mode)
2086{
2087 detach();
2088 d->clearError();
2089 QString trimmed = userInfo.trimmed();
2090 if (mode == DecodedMode) {
2091 qWarning("QUrl::setUserInfo(): QUrl::DecodedMode is not permitted in this function");
2092 return;
2093 }
2094
2095 d->setUserInfo(std::move(trimmed), mode);
2096 if (userInfo.isNull()) {
2097 // QUrlPrivate::setUserInfo cleared almost everything
2098 // but it leaves the UserName bit set
2099 d->sectionIsPresent &= ~QUrlPrivate::UserInfo;
2100 } else if (mode == StrictMode && !d->validateComponent(QUrlPrivate::UserInfo, userInfo)) {
2101 d->sectionIsPresent &= ~QUrlPrivate::UserInfo;
2102 d->userName.clear();
2103 d->password.clear();
2104 }
2105}
2106
2107/*!
2108 Returns the user info of the URL, or an empty string if the user
2109 info is undefined.
2110
2111 This function returns an unambiguous value, which may contain that
2112 characters still percent-encoded, plus some control sequences not
2113 representable in decoded form in QString.
2114
2115 The \a options argument controls how to format the user info component. The
2116 value of QUrl::FullyDecoded is not permitted in this function. If you need
2117 to obtain fully decoded data, call userName() and password() individually.
2118
2119 \sa setUserInfo(), userName(), password(), authority()
2120*/
2121QString QUrl::userInfo(ComponentFormattingOptions options) const
2122{
2123 QString result;
2124 if (!d)
2125 return result;
2126
2127 if (options == QUrl::FullyDecoded) {
2128 qWarning("QUrl::userInfo(): QUrl::FullyDecoded is not permitted in this function");
2129 return result;
2130 }
2131
2132 d->appendUserInfo(result, options, QUrlPrivate::UserInfo);
2133 return result;
2134}
2135
2136/*!
2137 Sets the URL's user name to \a userName. The \a userName is part
2138 of the user info element in the authority of the URL, as described
2139 in setUserInfo().
2140
2141 The \a userName data is interpreted according to \a mode: in StrictMode,
2142 any '%' characters must be followed by exactly two hexadecimal characters
2143 and some characters (including space) are not allowed in undecoded form. In
2144 TolerantMode (the default), all characters are accepted in undecoded form
2145 and the tolerant parser will correct stray '%' not followed by two hex
2146 characters. In DecodedMode, '%' stand for themselves and encoded characters
2147 are not possible.
2148
2149 QUrl::DecodedMode should be used when setting the user name from a data
2150 source which is not a URL, such as a password dialog shown to the user or
2151 with a user name obtained by calling userName() with the QUrl::FullyDecoded
2152 formatting option.
2153
2154 \sa userName(), setUserInfo()
2155*/
2156void QUrl::setUserName(const QString &userName, ParsingMode mode)
2157{
2158 detach();
2159 d->clearError();
2160
2161 d->setUserName(userName, mode);
2162 if (userName.isNull())
2163 d->sectionIsPresent &= ~QUrlPrivate::UserName;
2164 else if (mode == StrictMode && !d->validateComponent(QUrlPrivate::UserName, userName))
2165 d->userName.clear();
2166}
2167
2168/*!
2169 Returns the user name of the URL if it is defined; otherwise
2170 an empty string is returned.
2171
2172 The \a options argument controls how to format the user name component. All
2173 values produce an unambiguous result. With QUrl::FullyDecoded, all
2174 percent-encoded sequences are decoded; otherwise, the returned value may
2175 contain some percent-encoded sequences for some control sequences not
2176 representable in decoded form in QString.
2177
2178 Note that QUrl::FullyDecoded may cause data loss if those non-representable
2179 sequences are present. It is recommended to use that value when the result
2180 will be used in a non-URL context, such as setting in QAuthenticator or
2181 negotiating a login.
2182
2183 \sa setUserName(), userInfo()
2184*/
2185QString QUrl::userName(ComponentFormattingOptions options) const
2186{
2187 QString result;
2188 if (d)
2189 d->appendUserName(result, options);
2190 return result;
2191}
2192
2193/*!
2194 Sets the URL's password to \a password. The \a password is part of
2195 the user info element in the authority of the URL, as described in
2196 setUserInfo().
2197
2198 The \a password data is interpreted according to \a mode: in StrictMode,
2199 any '%' characters must be followed by exactly two hexadecimal characters
2200 and some characters (including space) are not allowed in undecoded form. In
2201 TolerantMode, all characters are accepted in undecoded form and the
2202 tolerant parser will correct stray '%' not followed by two hex characters.
2203 In DecodedMode, '%' stand for themselves and encoded characters are not
2204 possible.
2205
2206 QUrl::DecodedMode should be used when setting the password from a data
2207 source which is not a URL, such as a password dialog shown to the user or
2208 with a password obtained by calling password() with the QUrl::FullyDecoded
2209 formatting option.
2210
2211 \sa password(), setUserInfo()
2212*/
2213void QUrl::setPassword(const QString &password, ParsingMode mode)
2214{
2215 detach();
2216 d->clearError();
2217
2218 d->setPassword(password, mode);
2219 if (password.isNull())
2220 d->sectionIsPresent &= ~QUrlPrivate::Password;
2221 else if (mode == StrictMode && !d->validateComponent(QUrlPrivate::Password, password))
2222 d->password.clear();
2223}
2224
2225/*!
2226 Returns the password of the URL if it is defined; otherwise
2227 an empty string is returned.
2228
2229 The \a options argument controls how to format the user name component. All
2230 values produce an unambiguous result. With QUrl::FullyDecoded, all
2231 percent-encoded sequences are decoded; otherwise, the returned value may
2232 contain some percent-encoded sequences for some control sequences not
2233 representable in decoded form in QString.
2234
2235 Note that QUrl::FullyDecoded may cause data loss if those non-representable
2236 sequences are present. It is recommended to use that value when the result
2237 will be used in a non-URL context, such as setting in QAuthenticator or
2238 negotiating a login.
2239
2240 \sa setPassword()
2241*/
2242QString QUrl::password(ComponentFormattingOptions options) const
2243{
2244 QString result;
2245 if (d)
2246 d->appendPassword(result, options);
2247 return result;
2248}
2249
2250/*!
2251 Sets the host of the URL to \a host. The host is part of the
2252 authority.
2253
2254 The \a host data is interpreted according to \a mode: in StrictMode,
2255 any '%' characters must be followed by exactly two hexadecimal characters
2256 and some characters (including space) are not allowed in undecoded form. In
2257 TolerantMode, all characters are accepted in undecoded form and the
2258 tolerant parser will correct stray '%' not followed by two hex characters.
2259 In DecodedMode, '%' stand for themselves and encoded characters are not
2260 possible.
2261
2262 Note that, in all cases, the result of the parsing must be a valid hostname
2263 according to STD 3 rules, as modified by the Internationalized Resource
2264 Identifiers specification (RFC 3987). Invalid hostnames are not permitted
2265 and will cause isValid() to become false.
2266
2267 \sa host(), setAuthority()
2268*/
2269void QUrl::setHost(const QString &host, ParsingMode mode)
2270{
2271 detach();
2272 d->clearError();
2273
2274 QString data = host;
2275 if (mode == DecodedMode) {
2276 data.replace(u'%', "%25"_L1);
2277 mode = TolerantMode;
2278 }
2279
2280 if (d->setHost(data, 0, data.size(), mode)) {
2281 return;
2282 } else if (!data.startsWith(u'[')) {
2283 // setHost failed, it might be IPv6 or IPvFuture in need of bracketing
2284 Q_ASSERT(d->error);
2285
2286 data.prepend(u'[');
2287 data.append(u']');
2288 if (!d->setHost(data, 0, data.size(), mode)) {
2289 // failed again
2290 if (data.contains(u':')) {
2291 // source data contains ':', so it's an IPv6 error
2292 d->error->code = QUrlPrivate::InvalidIPv6AddressError;
2293 }
2294 d->sectionIsPresent &= ~QUrlPrivate::Host;
2295 } else {
2296 // succeeded
2297 d->clearError();
2298 }
2299 }
2300}
2301
2302/*!
2303 Returns the host of the URL if it is defined; otherwise
2304 an empty string is returned.
2305
2306 The \a options argument controls how the hostname will be formatted. The
2307 QUrl::EncodeUnicode option will cause this function to return the hostname
2308 in the ASCII-Compatible Encoding (ACE) form, which is suitable for use in
2309 channels that are not 8-bit clean or that require the legacy hostname (such
2310 as DNS requests or in HTTP request headers). If that flag is not present,
2311 this function returns the International Domain Name (IDN) in Unicode form,
2312 according to the list of permissible top-level domains (see
2313 idnWhitelist()).
2314
2315 All other flags are ignored. Host names cannot contain control or percent
2316 characters, so the returned value can be considered fully decoded.
2317
2318 \sa setHost(), idnWhitelist(), setIdnWhitelist(), authority()
2319*/
2320QString QUrl::host(ComponentFormattingOptions options) const
2321{
2322 QString result;
2323 if (d) {
2324 d->appendHost(result, options);
2325 if (result.startsWith(u'['))
2326 result = result.mid(1, result.size() - 2);
2327 }
2328 return result;
2329}
2330
2331/*!
2332 Sets the port of the URL to \a port. The port is part of the
2333 authority of the URL, as described in setAuthority().
2334
2335 \a port must be between 0 and 65535 inclusive. Setting the
2336 port to -1 indicates that the port is unspecified.
2337*/
2338void QUrl::setPort(int port)
2339{
2340 detach();
2341 d->clearError();
2342
2343 if (port < -1 || port > 65535) {
2344 d->setError(QUrlPrivate::InvalidPortError, QString::number(port), 0);
2345 port = -1;
2346 }
2347
2348 d->port = port;
2349 if (port != -1)
2350 d->sectionIsPresent |= QUrlPrivate::Host;
2351}
2352
2353/*!
2354 \since 4.1
2355
2356 Returns the port of the URL, or \a defaultPort if the port is
2357 unspecified.
2358
2359 Example:
2360
2361 \snippet code/src_corelib_io_qurl.cpp 3
2362*/
2363int QUrl::port(int defaultPort) const
2364{
2365 if (!d) return defaultPort;
2366 return d->port == -1 ? defaultPort : d->port;
2367}
2368
2369/*!
2370 Sets the path of the URL to \a path. The path is the part of the
2371 URL that comes after the authority but before the query string.
2372
2373 \image qurl-ftppath.png {Screenshot showing a URL with the path highlighted}
2374
2375 For non-hierarchical schemes, the path will be everything
2376 following the scheme declaration, as in the following example:
2377
2378 \image qurl-mailtopath.png {Screenshot of a URL with the mail path
2379 highlighted}
2380
2381 The \a path data is interpreted according to \a mode: in StrictMode,
2382 any '%' characters must be followed by exactly two hexadecimal characters
2383 and some characters (including space) are not allowed in undecoded form. In
2384 TolerantMode, all characters are accepted in undecoded form and the
2385 tolerant parser will correct stray '%' not followed by two hex characters.
2386 In DecodedMode, '%' stand for themselves and encoded characters are not
2387 possible.
2388
2389 QUrl::DecodedMode should be used when setting the path from a data source
2390 which is not a URL, such as a dialog shown to the user or with a path
2391 obtained by calling path() with the QUrl::FullyDecoded formatting option.
2392
2393 \sa path()
2394*/
2395void QUrl::setPath(const QString &path, ParsingMode mode)
2396{
2397 detach();
2398 d->clearError();
2399
2400 d->setPath(path, mode);
2401
2402 // optimized out, since there is no path delimiter
2403// if (path.isNull())
2404// d->sectionIsPresent &= ~QUrlPrivate::Path;
2405// else
2406 if (mode == StrictMode && !d->validateComponent(QUrlPrivate::Path, path))
2407 d->path.clear();
2408}
2409
2410/*!
2411 Returns the path of the URL.
2412
2413 \snippet code/src_corelib_io_qurl.cpp 12
2414
2415 The \a options argument controls how to format the path component. All
2416 values produce an unambiguous result. With QUrl::FullyDecoded, all
2417 percent-encoded sequences are decoded; otherwise, the returned value may
2418 contain some percent-encoded sequences for some control sequences not
2419 representable in decoded form in QString.
2420
2421 Note that QUrl::FullyDecoded may cause data loss if those non-representable
2422 sequences are present. It is recommended to use that value when the result
2423 will be used in a non-URL context, such as sending to an FTP server.
2424
2425 An example of data loss is when you have non-Unicode percent-encoded sequences
2426 and use FullyDecoded (the default):
2427
2428 \snippet code/src_corelib_io_qurl.cpp 13
2429
2430 In this example, there will be some level of data loss because the \c %FF cannot
2431 be converted.
2432
2433 Data loss can also occur when the path contains sub-delimiters (such as \c +):
2434
2435 \snippet code/src_corelib_io_qurl.cpp 14
2436
2437 Other decoding examples:
2438
2439 \snippet code/src_corelib_io_qurl.cpp 15
2440
2441 \sa setPath()
2442*/
2443QString QUrl::path(ComponentFormattingOptions options) const
2444{
2445 QString result;
2446 if (d)
2447 d->appendPath(result, options, QUrlPrivate::Path);
2448 return result;
2449}
2450
2451/*!
2452 \since 5.2
2453
2454 Returns the name of the file, excluding the directory path.
2455
2456 Note that, if this QUrl object is given a path ending in a slash, the name of the file is considered empty.
2457
2458 If the path doesn't contain any slash, it is fully returned as the fileName.
2459
2460 Example:
2461
2462 \snippet code/src_corelib_io_qurl.cpp 7
2463
2464 The \a options argument controls how to format the file name component. All
2465 values produce an unambiguous result. With QUrl::FullyDecoded, all
2466 percent-encoded sequences are decoded; otherwise, the returned value may
2467 contain some percent-encoded sequences for some control sequences not
2468 representable in decoded form in QString.
2469
2470 \sa path()
2471*/
2472QString QUrl::fileName(ComponentFormattingOptions options) const
2473{
2474 const QString ourPath = path(options);
2475 const qsizetype slash = ourPath.lastIndexOf(u'/');
2476 if (slash == -1)
2477 return ourPath;
2478 return ourPath.mid(slash + 1);
2479}
2480
2481/*!
2482 \since 4.2
2483
2484 Returns \c true if this URL contains a Query (i.e., if ? was seen on it).
2485
2486 \sa setQuery(), query(), hasFragment()
2487*/
2488bool QUrl::hasQuery() const
2489{
2490 if (!d) return false;
2491 return d->hasQuery();
2492}
2493
2494/*!
2495 Sets the query string of the URL to \a query.
2496
2497 This function is useful if you need to pass a query string that
2498 does not fit into the key-value pattern, or that uses a different
2499 scheme for encoding special characters than what is suggested by
2500 QUrl.
2501
2502 Passing a value of QString() to \a query (a null QString) unsets
2503 the query completely. However, passing a value of QString("")
2504 will set the query to an empty value, as if the original URL
2505 had a lone "?".
2506
2507 The \a query data is interpreted according to \a mode: in StrictMode,
2508 any '%' characters must be followed by exactly two hexadecimal characters
2509 and some characters (including space) are not allowed in undecoded form. In
2510 TolerantMode, all characters are accepted in undecoded form and the
2511 tolerant parser will correct stray '%' not followed by two hex characters.
2512 In DecodedMode, '%' stand for themselves and encoded characters are not
2513 possible.
2514
2515 Query strings often contain percent-encoded sequences, so use of
2516 DecodedMode is discouraged. One special sequence to be aware of is that of
2517 the plus character ('+'). QUrl does not convert spaces to plus characters,
2518 even though HTML forms posted by web browsers do. In order to represent an
2519 actual plus character in a query, the sequence "%2B" is usually used. This
2520 function will leave "%2B" sequences untouched in TolerantMode or
2521 StrictMode.
2522
2523 \sa query(), hasQuery()
2524*/
2525void QUrl::setQuery(const QString &query, ParsingMode mode)
2526{
2527 detach();
2528 d->clearError();
2529
2530 d->setQuery(query, mode);
2531 if (query.isNull())
2532 d->sectionIsPresent &= ~QUrlPrivate::Query;
2533 else if (mode == StrictMode && !d->validateComponent(QUrlPrivate::Query, query))
2534 d->query.clear();
2535}
2536
2537/*!
2538 \overload
2539 \since 5.0
2540 Sets the query string of the URL to \a query.
2541
2542 This function reconstructs the query string from the QUrlQuery object and
2543 sets on this QUrl object. This function does not have parsing parameters
2544 because the QUrlQuery contains data that is already parsed.
2545
2546 \sa query(), hasQuery()
2547*/
2548void QUrl::setQuery(const QUrlQuery &query)
2549{
2550 detach();
2551 d->clearError();
2552
2553 // we know the data is in the right format
2554 d->query = query.toString();
2555 if (query.isEmpty())
2556 d->sectionIsPresent &= ~QUrlPrivate::Query;
2557 else
2558 d->sectionIsPresent |= QUrlPrivate::Query;
2559}
2560
2561/*!
2562 Returns the query string of the URL if there's a query string, or an empty
2563 result if not. To determine if the parsed URL contained a query string, use
2564 hasQuery().
2565
2566 The \a options argument controls how to format the query component. All
2567 values produce an unambiguous result. With QUrl::FullyDecoded, all
2568 percent-encoded sequences are decoded; otherwise, the returned value may
2569 contain some percent-encoded sequences for some control sequences not
2570 representable in decoded form in QString.
2571
2572 Note that use of QUrl::FullyDecoded in queries is discouraged, as queries
2573 often contain data that is supposed to remain percent-encoded, including
2574 the use of the "%2B" sequence to represent a plus character ('+').
2575
2576 \sa setQuery(), hasQuery()
2577*/
2578QString QUrl::query(ComponentFormattingOptions options) const
2579{
2580 QString result;
2581 if (d) {
2582 d->appendQuery(result, options, QUrlPrivate::Query);
2583 if (d->hasQuery() && result.isNull())
2584 result.detach();
2585 }
2586 return result;
2587}
2588
2589/*!
2590 Sets the fragment of the URL to \a fragment. The fragment is the
2591 last part of the URL, represented by a '#' followed by a string of
2592 characters. It is typically used in HTTP for referring to a
2593 certain link or point on a page:
2594
2595 \image qurl-fragment.png {Screenshot of a URL with the fragment highlighted}
2596
2597 The fragment is sometimes also referred to as the URL "reference".
2598
2599 Passing an argument of QString() (a null QString) will unset the fragment.
2600 Passing an argument of QString("") (an empty but not null QString) will set the
2601 fragment to an empty string (as if the original URL had a lone "#").
2602
2603 The \a fragment data is interpreted according to \a mode: in StrictMode,
2604 any '%' characters must be followed by exactly two hexadecimal characters
2605 and some characters (including space) are not allowed in undecoded form. In
2606 TolerantMode, all characters are accepted in undecoded form and the
2607 tolerant parser will correct stray '%' not followed by two hex characters.
2608 In DecodedMode, '%' stand for themselves and encoded characters are not
2609 possible.
2610
2611 QUrl::DecodedMode should be used when setting the fragment from a data
2612 source which is not a URL or with a fragment obtained by calling
2613 fragment() with the QUrl::FullyDecoded formatting option.
2614
2615 \sa fragment(), hasFragment()
2616*/
2617void QUrl::setFragment(const QString &fragment, ParsingMode mode)
2618{
2619 detach();
2620 d->clearError();
2621
2622 d->setFragment(fragment, mode);
2623 if (fragment.isNull())
2624 d->sectionIsPresent &= ~QUrlPrivate::Fragment;
2625 else if (mode == StrictMode && !d->validateComponent(QUrlPrivate::Fragment, fragment))
2626 d->fragment.clear();
2627}
2628
2629/*!
2630 Returns the fragment of the URL. To determine if the parsed URL contained a
2631 fragment, use hasFragment().
2632
2633 The \a options argument controls how to format the fragment component. All
2634 values produce an unambiguous result. With QUrl::FullyDecoded, all
2635 percent-encoded sequences are decoded; otherwise, the returned value may
2636 contain some percent-encoded sequences for some control sequences not
2637 representable in decoded form in QString.
2638
2639 Note that QUrl::FullyDecoded may cause data loss if those non-representable
2640 sequences are present. It is recommended to use that value when the result
2641 will be used in a non-URL context.
2642
2643 \sa setFragment(), hasFragment()
2644*/
2645QString QUrl::fragment(ComponentFormattingOptions options) const
2646{
2647 QString result;
2648 if (d) {
2649 d->appendFragment(result, options, QUrlPrivate::Fragment);
2650 if (d->hasFragment() && result.isNull())
2651 result.detach();
2652 }
2653 return result;
2654}
2655
2656/*!
2657 \since 4.2
2658
2659 Returns \c true if this URL contains a fragment (i.e., if # was seen on it).
2660
2661 \sa fragment(), setFragment()
2662*/
2663bool QUrl::hasFragment() const
2664{
2665 if (!d) return false;
2666 return d->hasFragment();
2667}
2668
2669/*!
2670 Returns the result of the merge of this URL with \a relative. This
2671 URL is used as a base to convert \a relative to an absolute URL.
2672
2673 If \a relative is not a relative URL, this function will return \a
2674 relative directly. Otherwise, the paths of the two URLs are
2675 merged, and the new URL returned has the scheme and authority of
2676 the base URL, but with the merged path, as in the following
2677 example:
2678
2679 \snippet code/src_corelib_io_qurl.cpp 5
2680
2681 Calling resolved() with ".." returns a QUrl whose directory is
2682 one level higher than the original. Similarly, calling resolved()
2683 with "../.." removes two levels from the path. If \a relative is
2684 "/", the path becomes "/".
2685
2686 \sa isRelative()
2687*/
2688QUrl QUrl::resolved(const QUrl &relative) const
2689{
2690 if (!d) return relative;
2691 if (!relative.d) return *this;
2692
2693 QUrl t;
2694 if (!relative.d->scheme.isEmpty()) {
2695 t = relative;
2696 t.detach();
2697 } else {
2698 if (relative.d->hasAuthority()) {
2699 t = relative;
2700 t.detach();
2701 } else {
2702 t.d = new QUrlPrivate;
2703
2704 // copy the authority
2705 t.d->userName = d->userName;
2706 t.d->password = d->password;
2707 t.d->host = d->host;
2708 t.d->port = d->port;
2709 t.d->sectionIsPresent = d->sectionIsPresent & QUrlPrivate::Authority;
2710
2711 if (relative.d->path.isEmpty()) {
2712 t.d->path = d->path;
2713 if (relative.d->hasQuery()) {
2714 t.d->query = relative.d->query;
2715 t.d->sectionIsPresent |= QUrlPrivate::Query;
2716 } else if (d->hasQuery()) {
2717 t.d->query = d->query;
2718 t.d->sectionIsPresent |= QUrlPrivate::Query;
2719 }
2720 } else {
2721 t.d->path = relative.d->path.startsWith(u'/')
2722 ? relative.d->path
2723 : d->mergePaths(relative.d->path);
2724 if (relative.d->hasQuery()) {
2725 t.d->query = relative.d->query;
2726 t.d->sectionIsPresent |= QUrlPrivate::Query;
2727 }
2728 }
2729 }
2730 t.d->scheme = d->scheme;
2731 if (d->hasScheme())
2732 t.d->sectionIsPresent |= QUrlPrivate::Scheme;
2733 else
2734 t.d->sectionIsPresent &= ~QUrlPrivate::Scheme;
2735 t.d->flags |= d->flags & QUrlPrivate::IsLocalFile;
2736 }
2737 t.d->fragment = relative.d->fragment;
2738 if (relative.d->hasFragment())
2739 t.d->sectionIsPresent |= QUrlPrivate::Fragment;
2740 else
2741 t.d->sectionIsPresent &= ~QUrlPrivate::Fragment;
2742
2743 t.d->normalizePathSegments(&t.d->path);
2744 if (!t.d->hasAuthority()) {
2745 if (t.d->isLocalFile() && t.d->path.startsWith(u'/'))
2746 t.d->sectionIsPresent |= QUrlPrivate::Host;
2747 else
2748 fixupNonAuthorityPath(&t.d->path);
2749 }
2750
2751#if defined(QURL_DEBUG)
2752 qDebug("QUrl(\"%ls\").resolved(\"%ls\") = \"%ls\"",
2753 qUtf16Printable(url()),
2754 qUtf16Printable(relative.url()),
2755 qUtf16Printable(t.url()));
2756#endif
2757 return t;
2758}
2759
2760/*!
2761 Returns \c true if the URL is relative; otherwise returns \c false. A URL is
2762 relative reference if its scheme is undefined; this function is therefore
2763 equivalent to calling scheme().isEmpty().
2764
2765 Relative references are defined in RFC 3986 section 4.2.
2766
2767 \sa {Relative URLs vs Relative Paths}
2768*/
2769bool QUrl::isRelative() const
2770{
2771 if (!d) return true;
2772 return !d->hasScheme();
2773}
2774
2775/*!
2776 Returns a string representation of the URL. The output can be customized by
2777 passing flags with \a options. The option QUrl::FullyDecoded is not
2778 permitted in this function since it would generate ambiguous data.
2779
2780 The resulting QString can be passed back to a QUrl later on.
2781
2782 Synonym for toString(options).
2783
2784 \sa FormattingOptions, toEncoded(), toString()
2785*/
2786QString QUrl::url(FormattingOptions options) const
2787{
2788 return toString(options);
2789}
2790
2791/*!
2792 Returns a string representation of the URL. The output can be customized by
2793 passing flags with \a options. The option QUrl::FullyDecoded is not
2794 permitted in this function since it would generate ambiguous data.
2795
2796 The default formatting option is \l{QUrl::FormattingOptions}{PrettyDecoded}.
2797
2798 \sa FormattingOptions, url(), setUrl()
2799*/
2800QString QUrl::toString(FormattingOptions options) const
2801{
2802 QString url;
2803 if (!isValid()) {
2804 // also catches isEmpty()
2805 return url;
2806 }
2807 if ((options & QUrl::FullyDecoded) == QUrl::FullyDecoded) {
2808 qWarning("QUrl: QUrl::FullyDecoded is not permitted when reconstructing the full URL");
2809 options &= ~QUrl::FullyDecoded;
2810 //options |= QUrl::PrettyDecoded; // no-op, value is 0
2811 }
2812
2813 // return just the path if:
2814 // - QUrl::PreferLocalFile is passed
2815 // - QUrl::RemovePath isn't passed (rather stupid if the user did...)
2816 // - there's no query or fragment to return
2817 // that is, either they aren't present, or we're removing them
2818 // - it's a local file
2819 if (options.testFlag(QUrl::PreferLocalFile) && !options.testFlag(QUrl::RemovePath)
2820 && (!d->hasQuery() || options.testFlag(QUrl::RemoveQuery))
2821 && (!d->hasFragment() || options.testFlag(QUrl::RemoveFragment))
2822 && isLocalFile()) {
2823 url = d->toLocalFile(options | QUrl::FullyDecoded);
2824 return url;
2825 }
2826
2827 // for the full URL, we consider that the reserved characters are prettier if encoded
2828 if (options & DecodeReserved)
2829 options &= ~EncodeReserved;
2830 else
2831 options |= EncodeReserved;
2832
2833 if (!(options & QUrl::RemoveScheme) && d->hasScheme())
2834 url += d->scheme + u':';
2835
2836 bool pathIsAbsolute = d->path.startsWith(u'/');
2837 if (!((options & QUrl::RemoveAuthority) == QUrl::RemoveAuthority) && d->hasAuthority()) {
2838 url += "//"_L1;
2839 d->appendAuthority(url, options, QUrlPrivate::FullUrl);
2840 } else if (isLocalFile() && pathIsAbsolute) {
2841 // Comply with the XDG file URI spec, which requires triple slashes.
2842 url += "//"_L1;
2843 }
2844
2845 if (!(options & QUrl::RemovePath))
2846 d->appendPath(url, options, QUrlPrivate::FullUrl);
2847
2848 if (!(options & QUrl::RemoveQuery) && d->hasQuery()) {
2849 url += u'?';
2850 d->appendQuery(url, options, QUrlPrivate::FullUrl);
2851 }
2852 if (!(options & QUrl::RemoveFragment) && d->hasFragment()) {
2853 url += u'#';
2854 d->appendFragment(url, options, QUrlPrivate::FullUrl);
2855 }
2856
2857 return url;
2858}
2859
2860/*!
2861 \since 5.0
2862
2863 Returns a human-displayable string representation of the URL.
2864 The output can be customized by passing flags with \a options.
2865 The option RemovePassword is always enabled, since passwords
2866 should never be shown back to users.
2867
2868 With the default options, the resulting QString can be passed back
2869 to a QUrl later on, but any password that was present initially will
2870 be lost.
2871
2872 \sa FormattingOptions, toEncoded(), toString()
2873*/
2874
2875QString QUrl::toDisplayString(FormattingOptions options) const
2876{
2877 return toString(options | RemovePassword);
2878}
2879
2880/*!
2881 \since 5.2
2882
2883 Returns an adjusted version of the URL.
2884 The output can be customized by passing flags with \a options.
2885
2886 The encoding options from QUrl::ComponentFormattingOption don't make
2887 much sense for this method, nor does QUrl::PreferLocalFile.
2888
2889 This is always equivalent to QUrl(url.toString(options)).
2890
2891 \sa FormattingOptions, toEncoded(), toString()
2892*/
2893QUrl QUrl::adjusted(QUrl::FormattingOptions options) const
2894{
2895 if (!isValid()) {
2896 // also catches isEmpty()
2897 return QUrl();
2898 }
2899 QUrl that = *this;
2900 if (options & RemoveScheme)
2901 that.setScheme(QString());
2902 if ((options & RemoveAuthority) == RemoveAuthority) {
2903 that.setAuthority(QString());
2904 } else {
2905 if ((options & RemoveUserInfo) == RemoveUserInfo)
2906 that.setUserInfo(QString());
2907 else if (options & RemovePassword)
2908 that.setPassword(QString());
2909 if (options & RemovePort)
2910 that.setPort(-1);
2911 }
2912 if (options & RemoveQuery)
2913 that.setQuery(QString());
2914 if (options & RemoveFragment)
2915 that.setFragment(QString());
2916 if (options & RemovePath) {
2917 that.setPath(QString());
2918 } else if (auto pathOpts = options & (StripTrailingSlash | RemoveFilename | NormalizePathSegments)) {
2919 that.detach();
2920 that.d->path.resize(0);
2921 d->appendPath(that.d->path, pathOpts, QUrlPrivate::Path);
2922 }
2923 if (that.d->isLocalFile() && that.d->path.startsWith(u'/')) {
2924 // ensure absolute file URLs have an empty authority to comply with the
2925 // XDG file spec (note this may undo a RemoveAuthority)
2926 that.d->sectionIsPresent |= QUrlPrivate::Host;
2927 }
2928 return that;
2929}
2930
2931/*!
2932 Returns the encoded representation of the URL if it's valid;
2933 otherwise an empty QByteArray is returned. The output can be
2934 customized by passing flags with \a options.
2935
2936 The user info, path and fragment are all converted to UTF-8, and
2937 all non-ASCII characters are then percent encoded. The host name
2938 is encoded using Punycode.
2939*/
2940QByteArray QUrl::toEncoded(FormattingOptions options) const
2941{
2942 options &= ~(FullyDecoded | FullyEncoded);
2943 return toString(options | FullyEncoded).toLatin1();
2944}
2945
2946/*!
2947 Parses \a input and returns the corresponding QUrl. \a input is
2948 assumed to be in encoded form, containing only ASCII characters.
2949
2950 Parses the URL using \a mode. See setUrl() for more information on
2951 this parameter. QUrl::DecodedMode is not permitted in this context.
2952
2953 \note In Qt versions prior to 6.7, this function took a QByteArray, not
2954 QByteArrayView. If you experience compile errors, it's because your code
2955 is passing objects that are implicitly convertible to QByteArray, but not
2956 QByteArrayView. Wrap the corresponding argument in \c{QByteArray{~~~}} to
2957 make the cast explicit. This is backwards-compatible with old Qt versions.
2958
2959 \sa toEncoded(), setUrl()
2960*/
2961QUrl QUrl::fromEncoded(QByteArrayView input, ParsingMode mode)
2962{
2963 return QUrl(QString::fromUtf8(input), mode);
2964}
2965
2966/*!
2967 Returns a decoded copy of \a input. \a input is first decoded from
2968 percent encoding, then converted from UTF-8 to unicode.
2969
2970 \note Given invalid input (such as a string containing the sequence "%G5",
2971 which is not a valid hexadecimal number) the output will be invalid as
2972 well. As an example: the sequence "%G5" could be decoded to 'W'.
2973*/
2974QString QUrl::fromPercentEncoding(const QByteArray &input)
2975{
2976 QByteArray ba = QByteArray::fromPercentEncoding(input);
2977 return QString::fromUtf8(ba);
2978}
2979
2980/*!
2981 Returns an encoded copy of \a input. \a input is first converted
2982 to UTF-8, and all ASCII-characters that are not in the unreserved group
2983 are percent encoded. To prevent characters from being percent encoded
2984 pass them to \a exclude. To force characters to be percent encoded pass
2985 them to \a include.
2986
2987 Unreserved is defined as:
2988 \tt {ALPHA / DIGIT / "-" / "." / "_" / "~"}
2989
2990 \snippet code/src_corelib_io_qurl.cpp 6
2991*/
2992QByteArray QUrl::toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
2993{
2994 return input.toUtf8().toPercentEncoding(exclude, include);
2995}
2996
2997/*!
2998 \since 6.3
2999
3000 Returns the Unicode form of the given domain name
3001 \a domain, which is encoded in the ASCII Compatible Encoding (ACE).
3002 The output can be customized by passing flags with \a options.
3003 The result of this function is considered equivalent to \a domain.
3004
3005 If the value in \a domain cannot be encoded, it will be converted
3006 to QString and returned.
3007
3008 The ASCII-Compatible Encoding (ACE) is defined by RFC 3490, RFC 3491
3009 and RFC 3492 and updated by the Unicode Technical Standard #46. It is part
3010 of the Internationalizing Domain Names in Applications (IDNA) specification,
3011 which allows for domain names (like \c "example.com") to be written using
3012 non-US-ASCII characters.
3013*/
3014QString QUrl::fromAce(const QByteArray &domain, QUrl::AceProcessingOptions options)
3015{
3016 return qt_ACE_do(QString::fromLatin1(domain), NormalizeAce,
3017 ForbidLeadingDot /*FIXME: make configurable*/, options);
3018}
3019
3020/*!
3021 \since 6.3
3022
3023 Returns the ASCII Compatible Encoding of the given domain name \a domain.
3024 The output can be customized by passing flags with \a options.
3025 The result of this function is considered equivalent to \a domain.
3026
3027 The ASCII-Compatible Encoding (ACE) is defined by RFC 3490, RFC 3491
3028 and RFC 3492 and updated by the Unicode Technical Standard #46. It is part
3029 of the Internationalizing Domain Names in Applications (IDNA) specification,
3030 which allows for domain names (like \c "example.com") to be written using
3031 non-US-ASCII characters.
3032
3033 This function returns an empty QByteArray if \a domain is not a valid
3034 hostname. Note, in particular, that IPv6 literals are not valid domain
3035 names.
3036*/
3037QByteArray QUrl::toAce(const QString &domain, AceProcessingOptions options)
3038{
3039 return qt_ACE_do(domain, ToAceOnly, ForbidLeadingDot /*FIXME: make configurable*/, options)
3040 .toLatin1();
3041}
3042
3043/*!
3044 \internal
3045
3046 \fn bool QUrl::operator<(const QUrl &lhs, const QUrl &rhs)
3047
3048 Returns \c true if URL \a lhs is "less than" URL \a rhs. This
3049 provides a means of ordering URLs.
3050*/
3051
3052Qt::weak_ordering compareThreeWay(const QUrl &lhs, const QUrl &rhs)
3053{
3054 if (!lhs.d || !rhs.d) {
3055 bool thisIsEmpty = !lhs.d || lhs.d->isEmpty();
3056 bool thatIsEmpty = !rhs.d || rhs.d->isEmpty();
3057
3058 // sort an empty URL first
3059 if (thisIsEmpty) {
3060 if (!thatIsEmpty)
3061 return Qt::weak_ordering::less;
3062 else
3063 return Qt::weak_ordering::equivalent;
3064 } else {
3065 return Qt::weak_ordering::greater;
3066 }
3067 }
3068
3069 int cmp;
3070 cmp = lhs.d->scheme.compare(rhs.d->scheme);
3071 if (cmp != 0)
3072 return Qt::compareThreeWay(cmp, 0);
3073
3074 cmp = lhs.d->userName.compare(rhs.d->userName);
3075 if (cmp != 0)
3076 return Qt::compareThreeWay(cmp, 0);
3077
3078 cmp = lhs.d->password.compare(rhs.d->password);
3079 if (cmp != 0)
3080 return Qt::compareThreeWay(cmp, 0);
3081
3082 cmp = lhs.d->host.compare(rhs.d->host);
3083 if (cmp != 0)
3084 return Qt::compareThreeWay(cmp, 0);
3085
3086 if (lhs.d->port != rhs.d->port)
3087 return Qt::compareThreeWay(lhs.d->port, rhs.d->port);
3088
3089 cmp = lhs.d->path.compare(rhs.d->path);
3090 if (cmp != 0)
3091 return Qt::compareThreeWay(cmp, 0);
3092
3093 if (lhs.d->hasQuery() != rhs.d->hasQuery())
3094 return rhs.d->hasQuery() ? Qt::weak_ordering::less : Qt::weak_ordering::greater;
3095
3096 cmp = lhs.d->query.compare(rhs.d->query);
3097 if (cmp != 0)
3098 return Qt::compareThreeWay(cmp, 0);
3099
3100 if (lhs.d->hasFragment() != rhs.d->hasFragment())
3101 return rhs.d->hasFragment() ? Qt::weak_ordering::less : Qt::weak_ordering::greater;
3102
3103 cmp = lhs.d->fragment.compare(rhs.d->fragment);
3104 return Qt::compareThreeWay(cmp, 0);
3105}
3106
3107/*!
3108 \fn bool QUrl::operator==(const QUrl &lhs, const QUrl &rhs)
3109
3110 Returns \c true if \a lhs and \a rhs URLs are equivalent;
3111 otherwise returns \c false.
3112
3113 \sa matches()
3114*/
3115
3116bool comparesEqual(const QUrl &lhs, const QUrl &rhs)
3117{
3118 if (!lhs.d && !rhs.d)
3119 return true;
3120 if (!lhs.d)
3121 return rhs.d->isEmpty();
3122 if (!rhs.d)
3123 return lhs.d->isEmpty();
3124
3125 return (lhs.d->presentSections() == rhs.d->presentSections()) &&
3126 lhs.d->scheme == rhs.d->scheme &&
3127 lhs.d->userName == rhs.d->userName &&
3128 lhs.d->password == rhs.d->password &&
3129 lhs.d->host == rhs.d->host &&
3130 lhs.d->port == rhs.d->port &&
3131 lhs.d->path == rhs.d->path &&
3132 lhs.d->query == rhs.d->query &&
3133 lhs.d->fragment == rhs.d->fragment;
3134}
3135
3136/*!
3137 \since 5.2
3138
3139 Returns \c true if this URL and the given \a url are equal after
3140 applying \a options to both; otherwise returns \c false.
3141
3142 This is equivalent to calling \l{adjusted()}{adjusted}(options) on both URLs
3143 and comparing the resulting urls, but faster.
3144
3145*/
3146bool QUrl::matches(const QUrl &url, FormattingOptions options) const
3147{
3148 if (!d && !url.d)
3149 return true;
3150 if (!d)
3151 return url.d->isEmpty();
3152 if (!url.d)
3153 return d->isEmpty();
3154
3155 uint mask = d->presentSections();
3156
3157 if (options.testFlag(QUrl::RemoveScheme))
3158 mask &= ~QUrlPrivate::Scheme;
3159 else if (d->scheme != url.d->scheme)
3160 return false;
3161
3162 if (options.testFlag(QUrl::RemovePassword))
3163 mask &= ~QUrlPrivate::Password;
3164 else if (d->password != url.d->password)
3165 return false;
3166
3167 if (options.testFlag(QUrl::RemoveUserInfo))
3168 mask &= ~QUrlPrivate::UserName;
3169 else if (d->userName != url.d->userName)
3170 return false;
3171
3172 if (options.testFlag(QUrl::RemovePort))
3173 mask &= ~QUrlPrivate::Port;
3174 else if (d->port != url.d->port)
3175 return false;
3176
3177 if (options.testFlag(QUrl::RemoveAuthority))
3178 mask &= ~QUrlPrivate::Host;
3179 else if (d->host != url.d->host)
3180 return false;
3181
3182 if (options.testFlag(QUrl::RemoveQuery))
3183 mask &= ~QUrlPrivate::Query;
3184 else if (d->query != url.d->query)
3185 return false;
3186
3187 if (options.testFlag(QUrl::RemoveFragment))
3188 mask &= ~QUrlPrivate::Fragment;
3189 else if (d->fragment != url.d->fragment)
3190 return false;
3191
3192 if ((d->sectionIsPresent & mask) != (url.d->sectionIsPresent & mask))
3193 return false;
3194
3195 if (options.testFlag(QUrl::RemovePath))
3196 return true;
3197
3198 // Compare paths, after applying path-related options
3199 QString path1;
3200 d->appendPath(path1, options, QUrlPrivate::Path);
3201 QString path2;
3202 url.d->appendPath(path2, options, QUrlPrivate::Path);
3203 return path1 == path2;
3204}
3205
3206/*!
3207 \fn bool QUrl::operator !=(const QUrl &lhs, const QUrl &rhs)
3208
3209 Returns \c true if \a lhs and \a rhs URLs are not equal;
3210 otherwise returns \c false.
3211
3212 \sa matches()
3213*/
3214
3215/*!
3216 Assigns the specified \a url to this object.
3217*/
3218QUrl &QUrl::operator =(const QUrl &url) noexcept
3219{
3220 if (!d) {
3221 if (url.d) {
3222 url.d->ref.ref();
3223 d = url.d;
3224 }
3225 } else {
3226 if (url.d)
3227 qAtomicAssign(d, url.d);
3228 else
3229 clear();
3230 }
3231 return *this;
3232}
3233
3234/*!
3235 Assigns the specified \a url to this object.
3236*/
3237QUrl &QUrl::operator =(const QString &url)
3238{
3239 detachToClear();
3240 if (!url.isEmpty())
3241 d->parse(url, TolerantMode);
3242 return *this;
3243}
3244
3245/*!
3246 \fn void QUrl::swap(QUrl &other)
3247 \since 4.8
3248 \memberswap{URL}
3249*/
3250
3251/*!
3252 \internal
3253
3254 Forces a detach.
3255*/
3256void QUrl::detach()
3257{
3258 if (!d)
3259 d = new QUrlPrivate;
3260 else
3261 qAtomicDetach(d);
3262}
3263
3264/*!
3265 \internal
3266
3267 Forces a detach resulting in a clear state.
3268*/
3269void QUrl::detachToClear()
3270{
3271 if (d && (d->ref.loadAcquire() == 1 || !d->ref.deref())) {
3272 // we had the only copy
3273 d->ref.storeRelaxed(1);
3274 d->clear();
3275 } else {
3276 d = new QUrlPrivate;
3277 }
3278}
3279
3280/*!
3281 \internal
3282*/
3283bool QUrl::isDetached() const
3284{
3285 return !d || d->ref.loadRelaxed() == 1;
3286}
3287
3288static QString fromNativeSeparators(const QString &pathName)
3289{
3290#if defined(Q_OS_WIN)
3291 QString result(pathName);
3292 const QChar nativeSeparator = u'\\';
3293 auto i = result.indexOf(nativeSeparator);
3294 if (i != -1) {
3295 QChar * const data = result.data();
3296 const auto length = result.length();
3297 for (; i < length; ++i) {
3298 if (data[i] == nativeSeparator)
3299 data[i] = u'/';
3300 }
3301 }
3302 return result;
3303#else
3304 return pathName;
3305#endif
3306}
3307
3308/*!
3309 Returns a QUrl representation of \a localFile, interpreted as a local
3310 file. This function accepts paths separated by slashes as well as the
3311 native separator for this platform.
3312
3313 This function also accepts paths with a doubled leading slash (or
3314 backslash) to indicate a remote file, as in
3315 "//servername/path/to/file.txt". Note that only certain platforms can
3316 actually open this file using QFile::open().
3317
3318 An empty \a localFile leads to an empty URL (since Qt 5.4).
3319
3320 \snippet code/src_corelib_io_qurl.cpp 16
3321
3322 In the first line in snippet above, a file URL is constructed from a
3323 local, relative path. A file URL with a relative path only makes sense
3324 if there is a base URL to resolve it against. For example:
3325
3326 \snippet code/src_corelib_io_qurl.cpp 17
3327
3328 To resolve such a URL, it's necessary to remove the scheme beforehand:
3329
3330 \snippet code/src_corelib_io_qurl.cpp 18
3331
3332 For this reason, it is better to use a relative URL (that is, no scheme)
3333 for relative file paths:
3334
3335 \snippet code/src_corelib_io_qurl.cpp 19
3336
3337 \sa toLocalFile(), isLocalFile(), QDir::toNativeSeparators()
3338*/
3339QUrl QUrl::fromLocalFile(const QString &localFile)
3340{
3341 QUrl url;
3342 QString deslashified = fromNativeSeparators(localFile);
3343 if (deslashified.isEmpty())
3344 return url;
3345 QString scheme = fileScheme();
3346 char16_t firstChar = deslashified.at(0).unicode();
3347 char16_t secondChar = deslashified.size() > 1 ? deslashified.at(1).unicode() : u'\0';
3348
3349 // magic for drives on windows
3350 if (firstChar != u'/' && secondChar == u':') {
3351 deslashified.prepend(u'/');
3352 firstChar = u'/';
3353 } else if (firstChar == u'/' && secondChar == u'/') {
3354 // magic for shared drive on windows
3355 qsizetype indexOfPath = deslashified.indexOf(u'/', 2);
3356 QStringView hostSpec = QStringView{deslashified}.mid(2, indexOfPath - 2);
3357 // Check for Windows-specific WebDAV specification: "//host@SSL/path".
3358 if (hostSpec.endsWith(webDavSslTag(), Qt::CaseInsensitive)) {
3359 hostSpec.truncate(hostSpec.size() - 4);
3360 scheme = webDavScheme();
3361 }
3362
3363 // hosts can't be IPv6 addresses without [], so we can use QUrlPrivate::setHost
3364 url.detach();
3365 if (!url.d->setHost(hostSpec.toString(), 0, hostSpec.size(), StrictMode)) {
3366 if (url.d->error->code != QUrlPrivate::InvalidRegNameError)
3367 return url;
3368
3369 // Path hostname is not a valid URL host, so set it entirely in the path
3370 // (by leaving deslashified unchanged)
3371 } else if (indexOfPath > 2) {
3372 deslashified = deslashified.right(deslashified.size() - indexOfPath);
3373 } else {
3374 deslashified.clear();
3375 }
3376 }
3377 if (firstChar == u'/') {
3378 // ensure absolute file URLs have an empty authority to comply with the XDG file spec
3379 url.detach();
3380 url.d->sectionIsPresent |= QUrlPrivate::Host;
3381 }
3382
3383 url.setScheme(scheme);
3384 url.setPath(deslashified, DecodedMode);
3385
3386 return url;
3387}
3388
3389/*!
3390 Returns the path of this URL formatted as a local file path. The path
3391 returned will use forward slashes, even if it was originally created
3392 from one with backslashes.
3393
3394 If this URL contains a non-empty hostname, it will be encoded in the
3395 returned value in the form found on SMB networks (for example,
3396 "//servername/path/to/file.txt").
3397
3398 \snippet code/src_corelib_io_qurl.cpp 20
3399
3400 Note: if the path component of this URL contains a non-UTF-8 binary
3401 sequence (such as %80), the behaviour of this function is undefined.
3402
3403 \sa fromLocalFile(), isLocalFile()
3404*/
3405QString QUrl::toLocalFile() const
3406{
3407 // the call to isLocalFile() also ensures that we're parsed
3408 if (!isLocalFile())
3409 return QString();
3410
3411 return d->toLocalFile(QUrl::FullyDecoded);
3412}
3413
3414/*!
3415 \since 4.8
3416 Returns \c true if this URL is pointing to a local file path. A URL is a
3417 local file path if the scheme is "file".
3418
3419 Note that this function considers URLs with hostnames to be local file
3420 paths, even if the eventual file path cannot be opened with
3421 QFile::open().
3422
3423 \sa fromLocalFile(), toLocalFile()
3424*/
3425bool QUrl::isLocalFile() const
3426{
3427 return d && d->isLocalFile();
3428}
3429
3430/*!
3431 Returns \c true if this URL is a parent of \a childUrl. \a childUrl is a child
3432 of this URL if the two URLs share the same scheme and authority,
3433 and this URL's path is a parent of the path of \a childUrl.
3434*/
3435bool QUrl::isParentOf(const QUrl &childUrl) const
3436{
3437 QString childPath = childUrl.path();
3438
3439 if (!d)
3440 return ((childUrl.scheme().isEmpty())
3441 && (childUrl.authority().isEmpty())
3442 && childPath.size() > 0 && childPath.at(0) == u'/');
3443
3444 QString ourPath = path();
3445
3446 return ((childUrl.scheme().isEmpty() || d->scheme == childUrl.scheme())
3447 && (childUrl.authority().isEmpty() || authority() == childUrl.authority())
3448 && childPath.startsWith(ourPath)
3449 && ((ourPath.endsWith(u'/') && childPath.size() > ourPath.size())
3450 || (!ourPath.endsWith(u'/') && childPath.size() > ourPath.size()
3451 && childPath.at(ourPath.size()) == u'/')));
3452}
3453
3454
3455#ifndef QT_NO_DATASTREAM
3456/*! \relates QUrl
3457
3458 Writes url \a url to the stream \a out and returns a reference
3459 to the stream.
3460
3461 \sa{Serializing Qt Data Types}{Format of the QDataStream operators}
3462*/
3463QDataStream &operator<<(QDataStream &out, const QUrl &url)
3464{
3465 QByteArray u;
3466 if (url.isValid())
3467 u = url.toEncoded();
3468 out << u;
3469 return out;
3470}
3471
3472/*! \relates QUrl
3473
3474 Reads a url into \a url from the stream \a in and returns a
3475 reference to the stream.
3476
3477 \sa{Serializing Qt Data Types}{Format of the QDataStream operators}
3478*/
3479QDataStream &operator>>(QDataStream &in, QUrl &url)
3480{
3481 QByteArray u;
3482 in >> u;
3483 url.setUrl(QString::fromLatin1(u));
3484 return in;
3485}
3486#endif // QT_NO_DATASTREAM
3487
3488#ifndef QT_NO_DEBUG_STREAM
3489QDebug operator<<(QDebug d, const QUrl &url)
3490{
3491 QDebugStateSaver saver(d);
3492 d.nospace() << "QUrl(" << url.toDisplayString() << ')';
3493 return d;
3494}
3495#endif
3496
3497static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
3498{
3499 QChar c = size_t(errorPosition) < size_t(errorSource.size()) ?
3500 errorSource.at(errorPosition) : QChar(QChar::Null);
3501
3502 switch (errorCode) {
3504 Q_UNREACHABLE_RETURN(QString()); // QUrl::errorString should have treated this condition
3505
3507 auto msg = "Invalid scheme (character '%1' not permitted)"_L1;
3508 return msg.arg(c);
3509 }
3510
3511 case QUrlPrivate::InvalidUserNameError:
3512 return "Invalid user name (character '%1' not permitted)"_L1
3513 .arg(c);
3514
3515 case QUrlPrivate::InvalidPasswordError:
3516 return "Invalid password (character '%1' not permitted)"_L1
3517 .arg(c);
3518
3519 case QUrlPrivate::InvalidRegNameError:
3520 if (errorPosition >= 0)
3521 return "Invalid hostname (character '%1' not permitted)"_L1
3522 .arg(c);
3523 else
3524 return QStringLiteral("Invalid hostname (contains invalid characters)");
3526 return QString(); // doesn't happen yet
3527 case QUrlPrivate::InvalidIPv6AddressError:
3528 return QStringLiteral("Invalid IPv6 address");
3529 case QUrlPrivate::InvalidCharacterInIPv6Error:
3530 return "Invalid IPv6 address (character '%1' not permitted)"_L1.arg(c);
3531 case QUrlPrivate::InvalidIPvFutureError:
3532 return "Invalid IPvFuture address (character '%1' not permitted)"_L1.arg(c);
3533 case QUrlPrivate::HostMissingEndBracket:
3534 return QStringLiteral("Expected ']' to match '[' in hostname");
3535
3536 case QUrlPrivate::InvalidPortError:
3537 return QStringLiteral("Invalid port or port number out of range");
3538 case QUrlPrivate::PortEmptyError:
3539 return QStringLiteral("Port field was empty");
3540
3541 case QUrlPrivate::InvalidPathError:
3542 return "Invalid path (character '%1' not permitted)"_L1
3543 .arg(c);
3544
3545 case QUrlPrivate::InvalidQueryError:
3546 return "Invalid query (character '%1' not permitted)"_L1
3547 .arg(c);
3548
3549 case QUrlPrivate::InvalidFragmentError:
3550 return "Invalid fragment (character '%1' not permitted)"_L1
3551 .arg(c);
3552
3553 case QUrlPrivate::AuthorityPresentAndPathIsRelative:
3554 return QStringLiteral("Path component is relative and authority is present");
3555 case QUrlPrivate::AuthorityAbsentAndPathIsDoubleSlash:
3556 return QStringLiteral("Path component starts with '//' and authority is absent");
3557 case QUrlPrivate::RelativeUrlPathContainsColonBeforeSlash:
3558 return QStringLiteral("Relative URL's path component contains ':' before any '/'");
3559 }
3560
3561 Q_UNREACHABLE_RETURN(QString());
3562}
3563
3564static inline void appendComponentIfPresent(QString &msg, bool present, const char *componentName,
3565 const QString &component)
3566{
3567 if (present)
3568 msg += QLatin1StringView(componentName) % u'"' % component % "\","_L1;
3569}
3570
3571/*!
3572 \since 4.2
3573
3574 Returns an error message if the last operation that modified this QUrl
3575 object ran into a parsing error. If no error was detected, this function
3576 returns an empty string and isValid() returns \c true.
3577
3578 The error message returned by this function is technical in nature and may
3579 not be understood by end users. It is mostly useful to developers trying to
3580 understand why QUrl will not accept some input.
3581
3582 \sa QUrl::ParsingMode
3583*/
3584QString QUrl::errorString() const
3585{
3586 QString msg;
3587 if (!d)
3588 return msg;
3589
3590 QString errorSource;
3591 qsizetype errorPosition = 0;
3592 QUrlPrivate::ErrorCode errorCode = d->validityError(&errorSource, &errorPosition);
3593 if (errorCode == QUrlPrivate::NoError)
3594 return msg;
3595
3596 msg += errorMessage(errorCode, errorSource, errorPosition);
3597 msg += "; source was \""_L1;
3598 msg += errorSource;
3599 msg += "\";"_L1;
3600 appendComponentIfPresent(msg, d->sectionIsPresent & QUrlPrivate::Scheme,
3601 " scheme = ", d->scheme);
3602 appendComponentIfPresent(msg, d->sectionIsPresent & QUrlPrivate::UserInfo,
3603 " userinfo = ", userInfo());
3604 appendComponentIfPresent(msg, d->sectionIsPresent & QUrlPrivate::Host,
3605 " host = ", d->host);
3606 appendComponentIfPresent(msg, d->port != -1,
3607 " port = ", QString::number(d->port));
3608 appendComponentIfPresent(msg, !d->path.isEmpty(),
3609 " path = ", d->path);
3610 appendComponentIfPresent(msg, d->sectionIsPresent & QUrlPrivate::Query,
3611 " query = ", d->query);
3612 appendComponentIfPresent(msg, d->sectionIsPresent & QUrlPrivate::Fragment,
3613 " fragment = ", d->fragment);
3614 if (msg.endsWith(u','))
3615 msg.chop(1);
3616 return msg;
3617}
3618
3619/*!
3620 \since 5.1
3621
3622 Converts a list of \a urls into a list of QString objects, using toString(\a options).
3623*/
3624QStringList QUrl::toStringList(const QList<QUrl> &urls, FormattingOptions options)
3625{
3626 QStringList lst;
3627 lst.reserve(urls.size());
3628 for (const QUrl &url : urls)
3629 lst.append(url.toString(options));
3630 return lst;
3631
3632}
3633
3634/*!
3635 \since 5.1
3636
3637 Converts a list of strings representing \a urls into a list of urls, using QUrl(str, \a mode).
3638 Note that this means all strings must be urls, not for instance local paths.
3639*/
3640QList<QUrl> QUrl::fromStringList(const QStringList &urls, ParsingMode mode)
3641{
3642 QList<QUrl> lst;
3643 lst.reserve(urls.size());
3644 for (const QString &str : urls)
3645 lst.append(QUrl(str, mode));
3646 return lst;
3647}
3648
3649/*!
3650 \typedef QUrl::DataPtr
3651 \internal
3652*/
3653
3654/*!
3655 \fn DataPtr &QUrl::data_ptr()
3656 \internal
3657*/
3658
3659/*!
3660 \fn size_t qHash(const QUrl &key, size_t seed)
3661 \qhashold{QHash}
3662 \since 5.0
3663*/
3664size_t qHash(const QUrl &url, size_t seed) noexcept
3665{
3666 QtPrivate::QHashCombineWithSeed hasher(seed);
3667
3668 // non-commutative, we must hash the port first
3669 if (!url.d)
3670 return hasher(0, -1);
3671 size_t state = hasher(0, url.d->port);
3672
3673 if (url.d->hasScheme())
3674 state = hasher(state, url.d->scheme);
3675 if (url.d->hasUserInfo()) {
3676 // see presentSections(), appendUserName(), etc.
3677 state = hasher(state, url.d->userName);
3678 state = hasher(state, url.d->password);
3679 }
3680 if (url.d->hasHost() || url.d->isLocalFile()) // for XDG compatibility
3681 state = hasher(state, url.d->host);
3682 if (url.d->hasPath())
3683 state = hasher(state, url.d->path);
3684 if (url.d->hasQuery())
3685 state = hasher(state, url.d->query);
3686 if (url.d->hasFragment())
3687 state = hasher(state, url.d->fragment);
3688 return state;
3689}
3690
3691static QUrl adjustFtpPath(QUrl url)
3692{
3693 if (url.scheme() == ftpScheme()) {
3694 QString path = url.path(QUrl::PrettyDecoded);
3695 if (path.startsWith("//"_L1))
3696 url.setPath("/%2F"_L1 + QStringView{path}.mid(2), QUrl::TolerantMode);
3697 }
3698 return url;
3699}
3700
3701static bool isIp6(const QString &text)
3702{
3703 QIPAddressUtils::IPv6Address address;
3704 return !text.isEmpty() && QIPAddressUtils::parseIp6(address, text.begin(), text.end()) == nullptr;
3705}
3706
3707/*!
3708 Returns a valid URL from a user supplied \a userInput string if one can be
3709 deduced. In the case that is not possible, an invalid QUrl() is returned.
3710
3711 This allows the user to input a URL or a local file path in the form of a plain
3712 string. This string can be manually typed into a location bar, obtained from
3713 the clipboard, or passed in via command line arguments.
3714
3715 When the string is not already a valid URL, a best guess is performed,
3716 making various assumptions.
3717
3718 In the case the string corresponds to a valid file path on the system,
3719 a file:// URL is constructed, using QUrl::fromLocalFile().
3720
3721 If that is not the case, an attempt is made to turn the string into a
3722 http:// or ftp:// URL. The latter in the case the string starts with
3723 'ftp'. The result is then passed through QUrl's tolerant parser, and
3724 in the case or success, a valid QUrl is returned, or else a QUrl().
3725
3726 \section1 Examples:
3727
3728 \list
3729 \li qt-project.org becomes http://qt-project.org
3730 \li ftp.qt-project.org becomes ftp://ftp.qt-project.org
3731 \li hostname becomes http://hostname
3732 \li /home/user/test.html becomes file:///home/user/test.html
3733 \endlist
3734
3735 In order to be able to handle relative paths, this method takes an optional
3736 \a workingDirectory path. This is especially useful when handling command
3737 line arguments.
3738 If \a workingDirectory is empty, no handling of relative paths will be done.
3739
3740 By default, an input string that looks like a relative path will only be treated
3741 as such if the file actually exists in the given working directory.
3742 If the application can handle files that don't exist yet, it should pass the
3743 flag AssumeLocalFile in \a options.
3744
3745 \since 5.4
3746*/
3747QUrl QUrl::fromUserInput(const QString &userInput, const QString &workingDirectory,
3748 UserInputResolutionOptions options)
3749{
3750 QString trimmedString = userInput.trimmed();
3751
3752 if (trimmedString.isEmpty())
3753 return QUrl();
3754
3755 // Check for IPv6 addresses, since a path starting with ":" is absolute (a resource)
3756 // and IPv6 addresses can start with "c:" too
3757 if (isIp6(trimmedString)) {
3758 QUrl url;
3759 url.setHost(trimmedString);
3760 url.setScheme(QStringLiteral("http"));
3761 return url;
3762 }
3763
3764 const QUrl url = QUrl(trimmedString, QUrl::TolerantMode);
3765
3766 // Check for a relative path
3767 if (!workingDirectory.isEmpty()) {
3768 const QFileInfo fileInfo(QDir(workingDirectory), userInput);
3769 if (fileInfo.exists())
3770 return QUrl::fromLocalFile(fileInfo.absoluteFilePath());
3771
3772 // Check both QUrl::isRelative (to detect full URLs) and QDir::isAbsolutePath (since on Windows drive letters can be interpreted as schemes)
3773 if ((options & AssumeLocalFile) && url.isRelative() && !QDir::isAbsolutePath(userInput))
3774 return QUrl::fromLocalFile(fileInfo.absoluteFilePath());
3775 }
3776
3777 // Check first for files, since on Windows drive letters can be interpreted as schemes
3778 if (QDir::isAbsolutePath(trimmedString))
3779 return QUrl::fromLocalFile(trimmedString);
3780
3781 QUrl urlPrepended = QUrl("http://"_L1 + trimmedString, QUrl::TolerantMode);
3782
3783 // Check the most common case of a valid url with a scheme
3784 // We check if the port would be valid by adding the scheme to handle the case host:port
3785 // where the host would be interpreted as the scheme
3786 if (url.isValid()
3787 && !url.scheme().isEmpty()
3788 && urlPrepended.port() == -1)
3789 return adjustFtpPath(url);
3790
3791 // Else, try the prepended one and adjust the scheme from the host name
3792 if (urlPrepended.isValid() && (!urlPrepended.host().isEmpty() || !urlPrepended.path().isEmpty())) {
3793 qsizetype dotIndex = trimmedString.indexOf(u'.');
3794 const QStringView hostscheme = QStringView{trimmedString}.left(dotIndex);
3795 if (hostscheme.compare(ftpScheme(), Qt::CaseInsensitive) == 0)
3796 urlPrepended.setScheme(ftpScheme());
3797 return adjustFtpPath(urlPrepended);
3798 }
3799
3800 return QUrl();
3801}
3802
3803QT_END_NAMESPACE
bool isEmpty() const
Definition qurl.cpp:508
uint presentSections() const noexcept
Definition qurl.cpp:540
QAtomicInt ref
Definition qurl.cpp:589
void appendPassword(QString &appendTo, QUrl::FormattingOptions options) const
Definition qurl.cpp:948
bool hasScheme() const
Definition qurl.cpp:557
void appendHost(QString &appendTo, QUrl::FormattingOptions options) const
Definition qurl.cpp:1206
bool validateComponent(Section section, const QString &input, qsizetype begin, qsizetype end)
QString host
Definition qurl.cpp:595
uchar sectionIsPresent
Definition qurl.cpp:606
std::unique_ptr< Error > cloneError() const
Definition qurl.cpp:638
void setAuthority(const QString &auth, qsizetype from, qsizetype end, QUrl::ParsingMode mode)
Definition qurl.cpp:1058
bool hasPort() const
Definition qurl.cpp:563
QString toLocalFile(QUrl::FormattingOptions options) const
Definition qurl.cpp:1525
ErrorCode validityError(QString *source=nullptr, qsizetype *position=nullptr) const
Definition qurl.cpp:1599
bool hasQuery() const
Definition qurl.cpp:565
void setFragment(String &&value, QUrl::ParsingMode mode)
Definition qurl.cpp:1164
bool hasUserInfo() const
Definition qurl.cpp:559
QUrlPrivate(const QUrlPrivate &copy)
Definition qurl.cpp:620
QString mergePaths(const QString &relativePath) const
Definition qurl.cpp:1560
bool isLocalFile() const
Definition qurl.cpp:568
std::unique_ptr< Error > error
Definition qurl.cpp:600
@ AuthorityPresentAndPathIsRelative
Definition qurl.cpp:490
@ PortEmptyError
Definition qurl.cpp:480
@ RelativeUrlPathContainsColonBeforeSlash
Definition qurl.cpp:492
@ AuthorityAbsentAndPathIsDoubleSlash
Definition qurl.cpp:491
@ InvalidIPv6AddressError
Definition qurl.cpp:474
@ InvalidIPv4AddressError
Definition qurl.cpp:473
@ InvalidCharacterInIPv6Error
Definition qurl.cpp:475
@ InvalidPasswordError
Definition qurl.cpp:470
@ InvalidRegNameError
Definition qurl.cpp:472
@ InvalidFragmentError
Definition qurl.cpp:486
@ HostMissingEndBracket
Definition qurl.cpp:477
@ InvalidPortError
Definition qurl.cpp:479
@ InvalidQueryError
Definition qurl.cpp:484
@ InvalidPathError
Definition qurl.cpp:482
@ InvalidSchemeError
Definition qurl.cpp:466
@ InvalidUserNameError
Definition qurl.cpp:468
@ InvalidIPvFutureError
Definition qurl.cpp:476
QString path
Definition qurl.cpp:596
bool hasPath() const
Definition qurl.cpp:564
QUrlPrivate()
Definition qurl.cpp:613
void clearError()
Definition qurl.cpp:643
bool setScheme(const QString &value, qsizetype len, bool doSetError)
Definition qurl.cpp:993
void appendAuthority(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
Definition qurl.cpp:880
uchar flags
Definition qurl.cpp:607
@ IsLocalFile
Definition qurl.cpp:460
QString scheme
Definition qurl.cpp:592
void appendUserName(QString &appendTo, QUrl::FormattingOptions options) const
Definition qurl.cpp:939
void clear()
Definition qurl.cpp:580
void appendUserInfo(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
Definition qurl.cpp:894
bool hasFragment() const
Definition qurl.cpp:566
void appendPath(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
Definition qurl.cpp:955
bool validateComponent(Section section, const QString &input)
Definition qurl.cpp:516
bool normalizePathSegments(QString *path) const
Definition qurl.cpp:571
void setError(ErrorCode errorCode, const QString &source, qsizetype supplement=-1)
Definition qurl.cpp:648
bool hasUserName() const
Definition qurl.cpp:560
QString fragment
Definition qurl.cpp:598
void setUserInfo(String &&value, QUrl::ParsingMode mode)
Definition qurl.cpp:1130
QString query
Definition qurl.cpp:597
void setPath(String &&value, QUrl::ParsingMode mode)
Definition qurl.cpp:1158
void setQuery(String &&value, QUrl::ParsingMode mode)
Definition qurl.cpp:1170
QString userName
Definition qurl.cpp:593
bool hasAuthority() const
Definition qurl.cpp:558
bool hasHost() const
Definition qurl.cpp:562
void appendQuery(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
Definition qurl.cpp:985
void setPassword(String &&value, QUrl::ParsingMode mode)
Definition qurl.cpp:1152
void setUserName(String &&value, QUrl::ParsingMode mode)
Definition qurl.cpp:1146
QString password
Definition qurl.cpp:594
void appendFragment(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
Definition qurl.cpp:978
bool setHost(const QString &value, qsizetype from, qsizetype end, QUrl::ParsingMode mode)
Definition qurl.cpp:1323
bool hasPassword() const
Definition qurl.cpp:561
void parse(const QString &url, QUrl::ParsingMode parsingMode)
Definition qurl.cpp:1418
QDataStream & operator>>(QDataStream &in, QUrl &url)
Reads a url into url from the stream in and returns a reference to the stream.
Definition qurl.cpp:3479
#define QStringLiteral(str)
Definition qstring.h:1826
static const ushort userNameInAuthority[]
Definition qurl.cpp:800
size_t qHash(const QUrl &url, size_t seed) noexcept
\qhashold{QHash}
Definition qurl.cpp:3664
QDebug operator<<(QDebug d, const QUrl &url)
Definition qurl.cpp:3489
static QString fileScheme()
Definition qurl.cpp:426
static const QChar * parseIpFuture(QString &host, const QChar *begin, const QChar *end, QUrl::ParsingMode mode)
Definition qurl.cpp:1231
static const ushort *const passwordInAuthority
Definition qurl.cpp:819
static void appendComponentIfPresent(QString &msg, bool present, const char *componentName, const QString &component)
Definition qurl.cpp:3564
static const ushort userNameInUrl[]
Definition qurl.cpp:821
static QString webDavSslTag()
Definition qurl.cpp:436
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3497
static const ushort *const fragmentInIsolation
Definition qurl.cpp:777
static void appendToUser(QString &appendTo, QStringView value, QUrl::FormattingOptions options, const ushort *actions)
Definition qurl.cpp:865
static bool isHex(char c)
Definition qurl.cpp:415
static void fixupNonAuthorityPath(QString *path)
Definition qurl.cpp:1585
static const QChar * parseIp6(QString &host, const QChar *begin, const QChar *end, QUrl::ParsingMode mode)
Definition qurl.cpp:1276
static const ushort *const pathInUrl
Definition qurl.cpp:834
static const ushort *const pathInIsolation
Definition qurl.cpp:775
static const ushort *const passwordInUserInfo
Definition qurl.cpp:798
static void recodeFromUser(QString &output, QStringView input, const ushort *actions, QUrl::ParsingMode mode)
Definition qurl.cpp:852
Qt::weak_ordering compareThreeWay(const QUrl &lhs, const QUrl &rhs)
Definition qurl.cpp:3052
static const ushort userNameInUserInfo[]
Definition qurl.cpp:779
static const ushort userNameInIsolation[]
Definition qurl.cpp:755
static const ushort *const queryInIsolation
Definition qurl.cpp:776
static QUrl adjustFtpPath(QUrl url)
Definition qurl.cpp:3691
static const ushort *const fragmentInUrl
Definition qurl.cpp:836
static QString ftpScheme()
Definition qurl.cpp:421
static bool isIp6(const QString &text)
Definition qurl.cpp:3701
static void recodeFromUser(QString &output, const QString &input, const ushort *actions, QUrl::ParsingMode mode)
Definition qurl.cpp:839
static const ushort *const passwordInUrl
Definition qurl.cpp:833
static const ushort *const queryInUrl
Definition qurl.cpp:835
static const ushort *const passwordInIsolation
Definition qurl.cpp:774
bool comparesEqual(const QUrl &lhs, const QUrl &rhs)
Definition qurl.cpp:3116
static QString webDavScheme()
Definition qurl.cpp:431
static QString fromNativeSeparators(const QString &pathName)
Definition qurl.cpp:3288
@ ForbidLeadingDot
Definition qurl_p.h:33
@ NormalizeAce
Definition qurl_p.h:34
qsizetype position
Definition qurl.cpp:499
ErrorCode code
Definition qurl.cpp:500