1// Copyright (C) 2021 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qurlquery.h"
6#include "qurl_p.h"
7
8#include <QtCore/qhashfunctions.h>
9#include <QtCore/qstringlist.h>
10
11#include <algorithm>
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \class QUrlQuery
17 \inmodule QtCore
18 \since 5.0
19
20 \brief The QUrlQuery class provides a way to manipulate a key-value pairs in
21 a URL's query.
22
23 \reentrant
24 \ingroup io
25 \ingroup network
26 \ingroup shared
27
28 \compares equality
29
30 It is used to parse the query strings found in URLs like the following:
31
32 \image qurl-querystring.png
33
34 Query strings like the above are used to transmit options in the URL and are
35 usually decoded into multiple key-value pairs. The one above would contain
36 two entries in its list, with keys "type" and "color". QUrlQuery can also be
37 used to create a query string suitable for use in QUrl::setQuery() from the
38 individual components of the query.
39
40 The most common way of parsing a query string is to initialize it in the
41 constructor by passing it the query string. Otherwise, the setQuery() method
42 can be used to set the query to be parsed. That method can also be used to
43 parse a query with non-standard delimiters, after having set them using the
44 setQueryDelimiters() function.
45
46 The encoded query string can be obtained again using query(). This will take
47 all the internally-stored items and encode the string using the delimiters.
48
49 \section1 Encoding
50
51 All of the getter methods in QUrlQuery support an optional parameter of type
52 QUrl::ComponentFormattingOptions, including query(), which dictate how to
53 encode the data in question. Except for QUrl::FullyDecoded, the returned value must
54 still be considered a percent-encoded string, as there are certain values
55 which cannot be expressed in decoded form (like control characters, byte
56 sequences not decodable to UTF-8). For that reason, the percent character is
57 always represented by the string "%25".
58
59 All of the setter methods and the query methods like hasQueryItem() in
60 QUrlQuery take encoded forms only. Unlike in QUrl, there's no optional
61 parameter to specify that the strings being passed are decoded. If
62 improperly-encoded strings are passed to the setter or query methods,
63 QUrlQuery will attempt to recover instead of failing. That is to say, all
64 functions in this class parse their string arguments as if the
65 QUrl::TolerantMode decoding mode was specified.
66
67 Application code should strive to always ensure proper encoding and not rely
68 on TolerantMode parsing fixing the strings. Notably, all user input must be
69 first percent-encoded using QUrl::toPercentEncoding() or similar functions
70 before being passed to the functions in this class.
71
72 \section2 Handling of spaces and plus ("+")
73
74 Web browsers usually encode spaces found in HTML FORM elements to a plus sign
75 ("+") and plus signs to its percent-encoded form (%2B). However, the Internet
76 specifications governing URLs do not consider spaces and the plus character
77 equivalent.
78
79 For that reason, QUrlQuery never encodes the space character to "+" and will
80 never decode "+" to a space character. Instead, space characters will be
81 rendered "%20" in encoded form.
82
83 To support encoding like that of HTML forms, QUrlQuery also never decodes the
84 "%2B" sequence to a plus sign nor encode a plus sign. In fact, any "%2B" or
85 "+" sequences found in the keys, values, or query string are left exactly
86 like written (except for the uppercasing of "%2b" to "%2B").
87
88 \section2 Full decoding
89
90 With QUrl::FullyDecoded formatting, all percent-encoded sequences will be
91 decoded fully and the '%' character is used to represent itself.
92 QUrl::FullyDecoded should be used with care, since it may cause data loss.
93 See the documentation of QUrl::FullyDecoded for information on what data may
94 be lost.
95
96 This formatting mode should be used only when dealing with text presented to
97 the user in contexts where percent-encoding is not desired. Note that
98 QUrlQuery setters and query methods do not support the counterpart
99 QUrl::DecodedMode parsing, so using QUrl::FullyDecoded to obtain a listing of
100 keys may result in keys not found in the object.
101
102 \section1 Non-standard delimiters
103
104 By default, QUrlQuery uses an equal sign ("=") to separate a key from its
105 value, and an ampersand ("&") to separate key-value pairs from each other. It
106 is possible to change the delimiters that QUrlQuery uses for parsing and for
107 reconstructing the query by calling setQueryDelimiters().
108
109 Non-standard delimiters should be chosen from among what RFC 3986 calls
110 "sub-delimiters". They are:
111
112 \snippet code/src_corelib_io_qurlquery.cpp 0
113
114 Use of other characters is not supported and may result in unexpected
115 behaviour. QUrlQuery does not verify that you passed a valid delimiter.
116
117 \sa QUrl
118*/
119
120/*!
121 \fn QUrlQuery &QUrlQuery::operator=(QUrlQuery &&other)
122
123 Move-assigns \a other to this QUrlQuery instance.
124
125 \since 5.2
126*/
127
128/*!
129 \fn QUrlQuery::QUrlQuery(std::initializer_list<std::pair<QString, QString>> list)
130
131 \since 5.13
132
133 Constructs a QUrlQuery object from the \a list of key/value pair.
134*/
135
136typedef QList<std::pair<QString, QString> > Map;
137
138class QUrlQueryPrivate : public QSharedData
139{
140public:
141 QUrlQueryPrivate(const QString &query = QString())
142 : valueDelimiter(QUrlQuery::defaultQueryValueDelimiter()),
143 pairDelimiter(QUrlQuery::defaultQueryPairDelimiter())
144 { if (!query.isEmpty()) setQuery(query); }
145
146 QString recodeFromUser(const QString &input) const;
147 QString recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const;
148
149 void setQuery(const QString &query);
150
151 void addQueryItem(const QString &key, const QString &value)
152 { itemList.append(t: std::make_pair(x: recodeFromUser(input: key), y: recodeFromUser(input: value))); }
153 int findRecodedKey(const QString &key, int from = 0) const
154 {
155 for (int i = from; i < itemList.size(); ++i)
156 if (itemList.at(i).first == key)
157 return i;
158 return itemList.size();
159 }
160 Map::const_iterator findKey(const QString &key) const
161 { return itemList.constBegin() + findRecodedKey(key: recodeFromUser(input: key)); }
162 Map::iterator findKey(const QString &key)
163 { return itemList.begin() + findRecodedKey(key: recodeFromUser(input: key)); }
164
165 Map itemList;
166 QChar valueDelimiter;
167 QChar pairDelimiter;
168};
169
170template<> void QSharedDataPointer<QUrlQueryPrivate>::detach()
171{
172 if (d && d->ref.loadRelaxed() == 1)
173 return;
174 QUrlQueryPrivate *x = (d ? new QUrlQueryPrivate(*d)
175 : new QUrlQueryPrivate);
176 x->ref.ref();
177 if (d && !d->ref.deref())
178 delete d.get();
179 d.reset(p: x);
180}
181
182// Here's how we do the encoding in QUrlQuery
183// The RFC says these are the delimiters:
184// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
185// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
186// / "*" / "+" / "," / ";" / "="
187// And the definition of query is:
188// query = *( pchar / "/" / "?" )
189// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
190//
191// The strict definition of query says that it can have unencoded any
192// unreserved, sub-delim, ":", "@", "/" and "?". Or, by exclusion, excluded
193// delimiters are "#", "[" and "]" -- if those are present, they must be
194// percent-encoded. The fact that "[" and "]" should be encoded is probably a
195// mistake in the spec, so we ignore it and leave the decoded.
196//
197// The internal storage in the Map is equivalent to PrettyDecoded. That means
198// the getter methods, when called with the default encoding value, will not
199// have to recode anything (except for toString()).
200//
201// QUrlQuery handling of delimiters is quite simple: we never touch any of
202// them, except for the "#" character and the pair and value delimiters. Those
203// are always kept in their decoded forms.
204//
205// But when recreating the query string, in toString(), we must take care of
206// the special delimiters: the pair and value delimiters, as well as the "#"
207// character if unambiguous decoding is requested.
208
209#define decode(x) ushort(x)
210#define leave(x) ushort(0x100 | (x))
211#define encode(x) ushort(0x200 | (x))
212
213inline QString QUrlQueryPrivate::recodeFromUser(const QString &input) const
214{
215 // note: duplicated in setQuery()
216 QString output;
217 ushort prettyDecodedActions[] = {
218 decode(pairDelimiter.unicode()),
219 decode(valueDelimiter.unicode()),
220 decode('#'),
221 0
222 };
223 if (qt_urlRecode(appendTo&: output, url: input,
224 encoding: QUrl::DecodeReserved,
225 tableModifications: prettyDecodedActions))
226 return output;
227 return input;
228}
229
230inline bool idempotentRecodeToUser(QUrl::ComponentFormattingOptions encoding)
231{
232 return encoding == QUrl::PrettyDecoded;
233}
234
235inline QString QUrlQueryPrivate::recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const
236{
237 // our internal formats are stored in "PrettyDecoded" form
238 // and there are no ambiguous characters
239 if (idempotentRecodeToUser(encoding))
240 return input;
241
242 if (!(encoding & QUrl::EncodeDelimiters)) {
243 QString output;
244 if (qt_urlRecode(appendTo&: output, url: input,
245 encoding, tableModifications: nullptr))
246 return output;
247 return input;
248 }
249
250 // re-encode the "#" character and the query delimiter pair
251 ushort actions[] = { encode(pairDelimiter.unicode()), encode(valueDelimiter.unicode()),
252 encode('#'), 0 };
253 QString output;
254 if (qt_urlRecode(appendTo&: output, url: input, encoding, tableModifications: actions))
255 return output;
256 return input;
257}
258
259void QUrlQueryPrivate::setQuery(const QString &query)
260{
261 ushort prettyDecodedActions[] = {
262 decode(pairDelimiter.unicode()),
263 decode(valueDelimiter.unicode()),
264 decode('#'),
265 0
266 };
267
268 itemList.clear();
269 const QChar *pos = query.constData();
270 const QChar *const end = pos + query.size();
271 while (pos != end) {
272 const QChar *begin = pos;
273 const QChar *delimiter = nullptr;
274 while (pos != end) {
275 // scan for the component parts of this pair
276 if (!delimiter && *pos == valueDelimiter)
277 delimiter = pos;
278 if (*pos == pairDelimiter)
279 break;
280 ++pos;
281 }
282 if (!delimiter)
283 delimiter = pos;
284
285 // pos is the end of this pair (the end of the string or the pair delimiter)
286 // delimiter points to the value delimiter or to the end of this pair
287
288 QString key;
289 if (!qt_urlRecode(appendTo&: key, url: QStringView{begin, delimiter},
290 encoding: QUrl::DecodeReserved,
291 tableModifications: prettyDecodedActions))
292 key = QString(begin, delimiter - begin);
293
294 if (delimiter == pos) {
295 // the value delimiter wasn't found, store a null value
296 itemList.append(t: std::make_pair(x&: key, y: QString()));
297 } else if (delimiter + 1 == pos) {
298 // if the delimiter was found but the value is empty, store empty-but-not-null
299 itemList.append(t: std::make_pair(x&: key, y: QString(0, Qt::Uninitialized)));
300 } else {
301 QString value;
302 if (!qt_urlRecode(appendTo&: value, url: QStringView{delimiter + 1, pos},
303 encoding: QUrl::DecodeReserved,
304 tableModifications: prettyDecodedActions))
305 value = QString(delimiter + 1, pos - delimiter - 1);
306 itemList.append(t: std::make_pair(x&: key, y&: value));
307 }
308
309 if (pos != end)
310 ++pos;
311 }
312}
313
314// allow QUrlQueryPrivate to detach from null
315template <> inline QUrlQueryPrivate *
316QSharedDataPointer<QUrlQueryPrivate>::clone()
317{
318 return d ? new QUrlQueryPrivate(*d) : new QUrlQueryPrivate;
319}
320
321/*!
322 Constructs an empty QUrlQuery object. A query can be set afterwards by
323 calling setQuery() or items can be added by using addQueryItem().
324
325 \sa setQuery(), addQueryItem()
326*/
327QUrlQuery::QUrlQuery()
328 : d(nullptr)
329{
330}
331
332/*!
333 Constructs a QUrlQuery object and parses the \a queryString query string,
334 using the default query delimiters. To parse a query string using other
335 delimiters, you should first set them using setQueryDelimiters() and then
336 set the query with setQuery().
337*/
338QUrlQuery::QUrlQuery(const QString &queryString)
339 : d(queryString.isEmpty() ? nullptr : new QUrlQueryPrivate(queryString))
340{
341}
342
343/*!
344 Constructs a QUrlQuery object and parses the query string found in the \a
345 url URL, using the default query delimiters. To parse a query string using
346 other delimiters, you should first set them using setQueryDelimiters() and
347 then set the query with setQuery().
348
349 \sa QUrl::query()
350*/
351QUrlQuery::QUrlQuery(const QUrl &url)
352 : d(nullptr)
353{
354 // use internals to avoid unnecessary recoding
355 // ### FIXME: actually do it
356 if (url.hasQuery())
357 d = new QUrlQueryPrivate(url.query());
358}
359
360/*!
361 Copies the contents of the \a other QUrlQuery object, including the query
362 delimiters.
363*/
364QUrlQuery::QUrlQuery(const QUrlQuery &other)
365 : d(other.d)
366{
367}
368
369/*!
370 \since 6.5
371 Moves the contents of the \a other QUrlQuery object, including the query
372 delimiters.
373*/
374QUrlQuery::QUrlQuery(QUrlQuery &&other) noexcept
375 : d(std::move(other.d))
376{
377}
378
379/*!
380 Copies the contents of the \a other QUrlQuery object, including the query
381 delimiters.
382*/
383QUrlQuery &QUrlQuery::operator =(const QUrlQuery &other)
384{
385 d = other.d;
386 return *this;
387}
388
389/*!
390 \fn void QUrlQuery::swap(QUrlQuery &other)
391 \memberswap{URL query instance}
392*/
393
394/*!
395 Destroys this QUrlQuery object.
396*/
397QUrlQuery::~QUrlQuery()
398{
399 // d auto-deletes
400}
401
402/*!
403 \fn bool QUrlQuery::operator==(const QUrlQuery &lhs, const QUrlQuery &rhs)
404
405 Returns \c true if QUrlQuery objects \a lhs and \a rhs contain the same
406 contents, in the same order, and use the same query delimiters.
407*/
408
409bool comparesEqual(const QUrlQuery &lhs, const QUrlQuery &rhs)
410{
411 if (lhs.d == rhs.d)
412 return true;
413 if (lhs.d && rhs.d)
414 // keep in sync with qHash(QUrlQuery):
415 return lhs.d->valueDelimiter == rhs.d->valueDelimiter &&
416 lhs.d->pairDelimiter == rhs.d->pairDelimiter &&
417 lhs.d->itemList == rhs.d->itemList;
418
419 const QUrlQueryPrivate *x = lhs.d ? lhs.d.data() : rhs.d.data();
420 return x->valueDelimiter == QUrlQuery::defaultQueryValueDelimiter() &&
421 x->pairDelimiter == QUrlQuery::defaultQueryPairDelimiter() &&
422 x->itemList.isEmpty();
423}
424
425/*!
426 \since 5.6
427 \qhashold{QUrlQuery}
428*/
429size_t qHash(const QUrlQuery &key, size_t seed) noexcept
430{
431 if (const QUrlQueryPrivate *d = key.d) {
432 // keep in sync with operator==:
433 return qHashMulti(seed, args: d->valueDelimiter, args: d->pairDelimiter, args: d->itemList);
434 }
435 return seed;
436}
437
438/*!
439 Returns \c true if this QUrlQuery object contains no key-value pairs, such as
440 after being default-constructed or after parsing an empty query string.
441
442 \sa setQuery(), clear()
443*/
444bool QUrlQuery::isEmpty() const
445{
446 return d ? d->itemList.isEmpty() : true;
447}
448
449/*!
450 \internal
451*/
452bool QUrlQuery::isDetached() const
453{
454 return d && d->ref.loadRelaxed() == 1;
455}
456
457/*!
458 Clears this QUrlQuery object by removing all of the key-value pairs
459 currently stored. If the query delimiters have been changed, this function
460 will leave them with their changed values.
461
462 \sa isEmpty(), setQueryDelimiters()
463*/
464void QUrlQuery::clear()
465{
466 if (d.constData())
467 d->itemList.clear();
468}
469
470/*!
471 Parses the query string in \a queryString and sets the internal items to
472 the values found there. If any delimiters have been specified with
473 setQueryDelimiters(), this function will use them instead of the default
474 delimiters to parse the string.
475*/
476void QUrlQuery::setQuery(const QString &queryString)
477{
478 d->setQuery(queryString);
479}
480
481static void recodeAndAppend(QString &to, const QString &input,
482 QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications)
483{
484 if (!qt_urlRecode(appendTo&: to, url: input, encoding, tableModifications))
485 to += input;
486}
487
488/*!
489 Returns the reconstructed query string, formed from the key-value pairs
490 currently stored in this QUrlQuery object and separated by the query
491 delimiters chosen for this object. The keys and values are encoded using
492 the options given by the \a encoding parameter.
493
494 For this function, the only ambiguous delimiter is the hash ("#"), as in
495 URLs it is used to separate the query string from the fragment that may
496 follow.
497
498 The order of the key-value pairs in the returned string is exactly the same
499 as in the original query.
500
501 \sa setQuery(), QUrl::setQuery(), QUrl::fragment(), {encoding}{Encoding}
502*/
503QString QUrlQuery::query(QUrl::ComponentFormattingOptions encoding) const
504{
505 if (!d)
506 return QString();
507
508 // unlike the component encoding, for the whole query we need to modify a little:
509 // - the "#" character is unambiguous, so we encode it in EncodeDelimiters mode
510 // - the query delimiter pair must always be encoded
511
512 // start with what's always encoded
513 ushort tableActions[] = {
514 encode(d->pairDelimiter.unicode()), // 0
515 encode(d->valueDelimiter.unicode()), // 1
516 0, // 2
517 0
518 };
519 if (encoding & QUrl::EncodeDelimiters) {
520 tableActions[2] = encode('#');
521 }
522
523 QString result;
524 Map::const_iterator it = d->itemList.constBegin();
525 Map::const_iterator end = d->itemList.constEnd();
526
527 {
528 int size = 0;
529 for ( ; it != end; ++it)
530 size += it->first.size() + 1 + it->second.size() + 1;
531 result.reserve(asize: size + size / 4);
532 }
533
534 for (it = d->itemList.constBegin(); it != end; ++it) {
535 if (!result.isEmpty())
536 result += QChar(d->pairDelimiter);
537 recodeAndAppend(to&: result, input: it->first, encoding, tableModifications: tableActions);
538 if (!it->second.isNull()) {
539 result += QChar(d->valueDelimiter);
540 recodeAndAppend(to&: result, input: it->second, encoding, tableModifications: tableActions);
541 }
542 }
543 return result;
544}
545
546/*!
547 Sets the characters used for delimiting between keys and values,
548 and between key-value pairs in the URL's query string. The default
549 value delimiter is '=' and the default pair delimiter is '&'.
550
551 \image qurl-querystring.png
552
553 \a valueDelimiter will be used for separating keys from values,
554 and \a pairDelimiter will be used to separate key-value pairs.
555 Any occurrences of these delimiting characters in the encoded
556 representation of the keys and values of the query string are
557 percent encoded when returned in query().
558
559 If \a valueDelimiter is set to ',' and \a pairDelimiter is ';',
560 the above query string would instead be represented like this:
561
562 \snippet code/src_corelib_io_qurl.cpp 4
563
564 \note Non-standard delimiters should be chosen from among what RFC 3986 calls
565 "sub-delimiters". They are:
566
567 \snippet code/src_corelib_io_qurlquery.cpp 0
568
569 Use of other characters is not supported and may result in unexpected
570 behavior. This method does not verify that you passed a valid delimiter.
571
572 \sa queryValueDelimiter(), queryPairDelimiter()
573*/
574void QUrlQuery::setQueryDelimiters(QChar valueDelimiter, QChar pairDelimiter)
575{
576 d->valueDelimiter = valueDelimiter;
577 d->pairDelimiter = pairDelimiter;
578}
579
580/*!
581 Returns the character used to delimit between keys and values when
582 reconstructing the query string in query() or when parsing in setQuery().
583
584 \sa setQueryDelimiters(), queryPairDelimiter()
585*/
586QChar QUrlQuery::queryValueDelimiter() const
587{
588 return d ? d->valueDelimiter : defaultQueryValueDelimiter();
589}
590
591/*!
592 Returns the character used to delimit between keys-value pairs when
593 reconstructing the query string in query() or when parsing in setQuery().
594
595 \sa setQueryDelimiters(), queryValueDelimiter()
596*/
597QChar QUrlQuery::queryPairDelimiter() const
598{
599 return d ? d->pairDelimiter : defaultQueryPairDelimiter();
600}
601
602/*!
603 Sets the items in this QUrlQuery object to \a query. The order of the
604 elements in \a query is preserved.
605
606 \note This method does not treat spaces (ASCII 0x20) and plus ("+") signs
607 as the same, like HTML forms do. If you need spaces to be represented as
608 plus signs, use actual plus signs.
609
610 \note The keys and values are expected to be in percent-encoded form.
611
612 \sa queryItems(), isEmpty()
613*/
614void QUrlQuery::setQueryItems(const QList<std::pair<QString, QString> > &query)
615{
616 clear();
617 if (query.isEmpty())
618 return;
619
620 QUrlQueryPrivate *dd = d;
621 QList<std::pair<QString, QString> >::const_iterator it = query.constBegin(),
622 end = query.constEnd();
623 for ( ; it != end; ++it)
624 dd->addQueryItem(key: it->first, value: it->second);
625}
626
627/*!
628 Returns the query string of the URL, as a map of keys and values, using the
629 options specified in \a encoding to encode the items. The order of the
630 elements is the same as the one found in the query string or set with
631 setQueryItems().
632
633 \sa setQueryItems(), {encoding}{Encoding}
634*/
635QList<std::pair<QString, QString> > QUrlQuery::queryItems(QUrl::ComponentFormattingOptions encoding) const
636{
637 if (!d)
638 return QList<std::pair<QString, QString> >();
639 if (idempotentRecodeToUser(encoding))
640 return d->itemList;
641
642 QList<std::pair<QString, QString> > result;
643 Map::const_iterator it = d->itemList.constBegin();
644 Map::const_iterator end = d->itemList.constEnd();
645 result.reserve(asize: d->itemList.size());
646 for ( ; it != end; ++it)
647 result << std::make_pair(x: d->recodeToUser(input: it->first, encoding),
648 y: d->recodeToUser(input: it->second, encoding));
649 return result;
650}
651
652/*!
653 Returns \c true if there is a query string pair whose key is equal
654 to \a key from the URL.
655
656 \note The key expected to be in percent-encoded form.
657
658 \sa addQueryItem(), queryItemValue()
659*/
660bool QUrlQuery::hasQueryItem(const QString &key) const
661{
662 if (!d)
663 return false;
664 return d->findKey(key) != d->itemList.constEnd();
665}
666
667/*!
668 Appends the pair \a key = \a value to the end of the query string of the
669 URL. This method does not overwrite existing items that might exist with
670 the same key.
671
672 \note This method does not treat spaces (ASCII 0x20) and plus ("+") signs
673 as the same, like HTML forms do. If you need spaces to be represented as
674 plus signs, use actual plus signs.
675
676 \note The key and value strings are expected to be in percent-encoded form.
677
678 \sa hasQueryItem(), queryItemValue()
679*/
680void QUrlQuery::addQueryItem(const QString &key, const QString &value)
681{
682 d->addQueryItem(key, value);
683}
684
685/*!
686 Returns the query value associated with key \a key from the URL, using the
687 options specified in \a encoding to encode the return value. If the key \a
688 key is not found, this function returns an empty string. If you need to
689 distinguish between an empty value and a non-existent key, you should check
690 for the key's presence first using hasQueryItem().
691
692 If the key \a key is multiply defined, this function will return the first
693 one found, in the order they were present in the query string or added
694 using addQueryItem().
695
696 \note The key is expected to be in percent-encoded form.
697
698 \sa addQueryItem(), allQueryItemValues(), {encoding}{Encoding}
699*/
700QString QUrlQuery::queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const
701{
702 QString result;
703 if (d) {
704 Map::const_iterator it = d->findKey(key);
705 if (it != d->itemList.constEnd())
706 result = d->recodeToUser(input: it->second, encoding);
707 }
708 return result;
709}
710
711/*!
712 Returns the a list of query string values whose key is equal to \a key from
713 the URL, using the options specified in \a encoding to encode the return
714 value. If the key \a key is not found, this function returns an empty list.
715
716 \note The key is expected to be in percent-encoded form.
717
718 \sa queryItemValue(), addQueryItem()
719*/
720QStringList QUrlQuery::allQueryItemValues(const QString &key, QUrl::ComponentFormattingOptions encoding) const
721{
722 QStringList result;
723 if (d) {
724 QString encodedKey = d->recodeFromUser(input: key);
725 int idx = d->findRecodedKey(key: encodedKey);
726 while (idx < d->itemList.size()) {
727 result << d->recodeToUser(input: d->itemList.at(i: idx).second, encoding);
728 idx = d->findRecodedKey(key: encodedKey, from: idx + 1);
729 }
730 }
731 return result;
732}
733
734/*!
735 Removes the query string pair whose key is equal to \a key from the URL. If
736 there are multiple items with a key equal to \a key, it removes the first
737 item in the order they were present in the query string or added with
738 addQueryItem().
739
740 \note The key is expected to be in percent-encoded form.
741
742 \sa removeAllQueryItems()
743*/
744void QUrlQuery::removeQueryItem(const QString &key)
745{
746 if (d.constData()) {
747 auto *p = d.data();
748 Map::iterator it = p->findKey(key);
749 if (it != p->itemList.end())
750 p->itemList.erase(pos: it);
751 }
752}
753
754/*!
755 Removes all the query string pairs whose key is equal to \a key
756 from the URL.
757
758 \note The key is expected to be in percent-encoded form.
759
760 \sa removeQueryItem()
761*/
762void QUrlQuery::removeAllQueryItems(const QString &key)
763{
764 if (d.constData()) {
765 auto *p = d.data();
766 const QString encodedKey = p->recodeFromUser(input: key);
767 auto firstEqualsEncodedKey = [&encodedKey](const std::pair<QString, QString> &item) {
768 return item.first == encodedKey;
769 };
770 p->itemList.removeIf(pred: firstEqualsEncodedKey);
771 }
772}
773
774/*!
775 \fn QUrlQuery::defaultQueryValueDelimiter()
776 Returns the default character for separating keys from values in the query,
777 an equal sign ("=").
778
779 \note Prior to Qt 6, this function returned QChar.
780
781 \sa setQueryDelimiters(), queryValueDelimiter(), defaultQueryPairDelimiter()
782*/
783
784/*!
785 \fn QUrlQuery::defaultQueryPairDelimiter()
786 Returns the default character for separating keys-value pairs from each
787 other, an ampersand ("&").
788
789 \note Prior to Qt 6, this function returned QChar.
790
791 \sa setQueryDelimiters(), queryPairDelimiter(), defaultQueryValueDelimiter()
792*/
793
794/*!
795 \typedef QUrlQuery::DataPtr
796 \internal
797*/
798
799/*!
800 \fn DataPtr &QUrlQuery::data_ptr()
801 \internal
802*/
803
804/*!
805 \fn QString QUrlQuery::toString(QUrl::ComponentFormattingOptions encoding = QUrl::PrettyDecoded) const
806
807 Returns this QUrlQuery as a QString. \a encoding can be used to specify the URL string encoding of the return value.
808*/
809
810/*!
811 \fn bool QUrlQuery::operator!=(const QUrlQuery &lhs, const QUrlQuery &rhs)
812
813 Returns \c true if the QUrlQuery object \a rhs is not equal to \a lhs.
814 Otherwise, returns \c false.
815
816 \sa operator==()
817*/
818QT_END_NAMESPACE
819
820#undef decode
821#undef leave
822#undef encode
823

source code of qtbase/src/corelib/io/qurlquery.cpp