QtBase  v6.3.1
qurlquery.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 Intel Corporation.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qurlquery.h"
41 #include "qurl_p.h"
42 
43 #include <QtCore/qhashfunctions.h>
44 #include <QtCore/qstringlist.h>
45 
46 #include <algorithm>
47 
49 
170 
172 {
173 public:
175  : valueDelimiter(QUrlQuery::defaultQueryValueDelimiter()),
176  pairDelimiter(QUrlQuery::defaultQueryPairDelimiter())
177  { if (!query.isEmpty()) setQuery(query); }
178 
179  QString recodeFromUser(const QString &input) const;
180  QString recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const;
181 
182  void setQuery(const QString &query);
183 
184  void addQueryItem(const QString &key, const QString &value)
186  int findRecodedKey(const QString &key, int from = 0) const
187  {
188  for (int i = from; i < itemList.size(); ++i)
189  if (itemList.at(i).first == key)
190  return i;
191  return itemList.size();
192  }
197 
201 };
202 
204 {
205  if (d && d->ref.loadRelaxed() == 1)
206  return;
207  QUrlQueryPrivate *x = (d ? new QUrlQueryPrivate(*d)
208  : new QUrlQueryPrivate);
209  x->ref.ref();
210  if (d && !d->ref.deref())
211  delete d;
212  d = x;
213 }
214 
215 // Here's how we do the encoding in QUrlQuery
216 // The RFC says these are the delimiters:
217 // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
218 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
219 // / "*" / "+" / "," / ";" / "="
220 // And the definition of query is:
221 // query = *( pchar / "/" / "?" )
222 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
223 //
224 // The strict definition of query says that it can have unencoded any
225 // unreserved, sub-delim, ":", "@", "/" and "?". Or, by exclusion, excluded
226 // delimiters are "#", "[" and "]" -- if those are present, they must be
227 // percent-encoded. The fact that "[" and "]" should be encoded is probably a
228 // mistake in the spec, so we ignore it and leave the decoded.
229 //
230 // The internal storage in the Map is equivalent to PrettyDecoded. That means
231 // the getter methods, when called with the default encoding value, will not
232 // have to recode anything (except for toString()).
233 //
234 // QUrlQuery handling of delimiters is quite simple: we never touch any of
235 // them, except for the "#" character and the pair and value delimiters. Those
236 // are always kept in their decoded forms.
237 //
238 // But when recreating the query string, in toString(), we must take care of
239 // the special delimiters: the pair and value delimiters, as well as the "#"
240 // character if unambiguous decoding is requested.
241 
242 #define decode(x) ushort(x)
243 #define leave(x) ushort(0x100 | (x))
244 #define encode(x) ushort(0x200 | (x))
245 
247 {
248  // note: duplicated in setQuery()
249  QString output;
250  ushort prettyDecodedActions[] = {
253  decode('#'),
254  0
255  };
256  if (qt_urlRecode(output, input,
258  prettyDecodedActions))
259  return output;
260  return input;
261 }
262 
263 inline bool idempotentRecodeToUser(QUrl::ComponentFormattingOptions encoding)
264 {
265  return encoding == QUrl::PrettyDecoded;
266 }
267 
268 inline QString QUrlQueryPrivate::recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const
269 {
270  // our internal formats are stored in "PrettyDecoded" form
271  // and there are no ambiguous characters
272  if (idempotentRecodeToUser(encoding))
273  return input;
274 
275  if (!(encoding & QUrl::EncodeDelimiters)) {
276  QString output;
277  if (qt_urlRecode(output, input,
278  encoding, nullptr))
279  return output;
280  return input;
281  }
282 
283  // re-encode the "#" character and the query delimiter pair
285  encode('#'), 0 };
286  QString output;
287  if (qt_urlRecode(output, input, encoding, actions))
288  return output;
289  return input;
290 }
291 
293 {
294  ushort prettyDecodedActions[] = {
297  decode('#'),
298  0
299  };
300 
301  itemList.clear();
302  const QChar *pos = query.constData();
303  const QChar *const end = pos + query.size();
304  while (pos != end) {
305  const QChar *begin = pos;
306  const QChar *delimiter = nullptr;
307  while (pos != end) {
308  // scan for the component parts of this pair
309  if (!delimiter && *pos == valueDelimiter)
310  delimiter = pos;
311  if (*pos == pairDelimiter)
312  break;
313  ++pos;
314  }
315  if (!delimiter)
316  delimiter = pos;
317 
318  // pos is the end of this pair (the end of the string or the pair delimiter)
319  // delimiter points to the value delimiter or to the end of this pair
320 
321  QString key;
322  if (!qt_urlRecode(key, QStringView{begin, delimiter},
324  prettyDecodedActions))
325  key = QString(begin, delimiter - begin);
326 
327  if (delimiter == pos) {
328  // the value delimiter wasn't found, store a null value
330  } else if (delimiter + 1 == pos) {
331  // if the delimiter was found but the value is empty, store empty-but-not-null
333  } else {
334  QString value;
335  if (!qt_urlRecode(value, QStringView{delimiter + 1, pos},
337  prettyDecodedActions))
338  value = QString(delimiter + 1, pos - delimiter - 1);
340  }
341 
342  if (pos != end)
343  ++pos;
344  }
345 }
346 
347 // allow QUrlQueryPrivate to detach from null
348 template <> inline QUrlQueryPrivate *
350 {
351  return d ? new QUrlQueryPrivate(*d) : new QUrlQueryPrivate;
352 }
353 
361  : d(nullptr)
362 {
363 }
364 
371 QUrlQuery::QUrlQuery(const QString &queryString)
372  : d(queryString.isEmpty() ? nullptr : new QUrlQueryPrivate(queryString))
373 {
374 }
375 
385  : d(nullptr)
386 {
387  // use internals to avoid unnecessary recoding
388  // ### FIXME: actually do it
389  if (url.hasQuery())
390  d = new QUrlQueryPrivate(url.query());
391 }
392 
398  : d(other.d)
399 {
400 }
401 
407 {
408  d = other.d;
409  return *this;
410 }
411 
423 {
424  // d auto-deletes
425 }
426 
432 {
433  if (d == other.d)
434  return true;
435  if (d && other.d)
436  // keep in sync with qHash(QUrlQuery):
437  return d->valueDelimiter == other.d->valueDelimiter &&
438  d->pairDelimiter == other.d->pairDelimiter &&
439  d->itemList == other.d->itemList;
440  return false;
441 }
442 
450 size_t qHash(const QUrlQuery &key, size_t seed) noexcept
451 {
452  if (const QUrlQueryPrivate *d = key.d) {
454  // keep in sync with operator==:
455  seed = hash(seed, d->valueDelimiter);
456  seed = hash(seed, d->pairDelimiter);
457  seed = hash(seed, d->itemList);
458  }
459  return seed;
460 }
461 
468 bool QUrlQuery::isEmpty() const
469 {
470  return d ? d->itemList.isEmpty() : true;
471 }
472 
477 {
478  return d && d->ref.loadRelaxed() == 1;
479 }
480 
489 {
490  if (d.constData())
491  d->itemList.clear();
492 }
493 
500 void QUrlQuery::setQuery(const QString &queryString)
501 {
502  d->setQuery(queryString);
503 }
504 
505 static void recodeAndAppend(QString &to, const QString &input,
506  QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications)
507 {
508  if (!qt_urlRecode(to, input, encoding, tableModifications))
509  to += input;
510 }
511 
527 QString QUrlQuery::query(QUrl::ComponentFormattingOptions encoding) const
528 {
529  if (!d)
530  return QString();
531 
532  // unlike the component encoding, for the whole query we need to modify a little:
533  // - the "#" character is unambiguous, so we encode it in EncodeDelimiters mode
534  // - the query delimiter pair must always be encoded
535 
536  // start with what's always encoded
537  ushort tableActions[] = {
538  encode(d->pairDelimiter.unicode()), // 0
539  encode(d->valueDelimiter.unicode()), // 1
540  0, // 2
541  0
542  };
543  if (encoding & QUrl::EncodeDelimiters) {
544  tableActions[2] = encode('#');
545  }
546 
547  QString result;
550 
551  {
552  int size = 0;
553  for ( ; it != end; ++it)
554  size += it->first.length() + 1 + it->second.length() + 1;
555  result.reserve(size + size / 4);
556  }
557 
558  for (it = d->itemList.constBegin(); it != end; ++it) {
559  if (!result.isEmpty())
560  result += QChar(d->pairDelimiter);
561  recodeAndAppend(result, it->first, encoding, tableActions);
562  if (!it->second.isNull()) {
563  result += QChar(d->valueDelimiter);
564  recodeAndAppend(result, it->second, encoding, tableActions);
565  }
566  }
567  return result;
568 }
569 
598 void QUrlQuery::setQueryDelimiters(QChar valueDelimiter, QChar pairDelimiter)
599 {
600  d->valueDelimiter = valueDelimiter;
601  d->pairDelimiter = pairDelimiter;
602 }
603 
611 {
612  return d ? d->valueDelimiter : defaultQueryValueDelimiter();
613 }
614 
622 {
623  return d ? d->pairDelimiter : defaultQueryPairDelimiter();
624 }
625 
639 {
640  clear();
641  if (query.isEmpty())
642  return;
643 
644  QUrlQueryPrivate *dd = d;
646  end = query.constEnd();
647  for ( ; it != end; ++it)
648  dd->addQueryItem(it->first, it->second);
649 }
650 
659 QList<QPair<QString, QString> > QUrlQuery::queryItems(QUrl::ComponentFormattingOptions encoding) const
660 {
661  if (!d)
663  if (idempotentRecodeToUser(encoding))
664  return d->itemList;
665 
669  result.reserve(d->itemList.count());
670  for ( ; it != end; ++it)
671  result << qMakePair(d->recodeToUser(it->first, encoding),
672  d->recodeToUser(it->second, encoding));
673  return result;
674 }
675 
685 {
686  if (!d)
687  return false;
688  return d->findKey(key) != d->itemList.constEnd();
689 }
690 
705 {
706  d->addQueryItem(key, value);
707 }
708 
724 QString QUrlQuery::queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const
725 {
726  QString result;
727  if (d) {
729  if (it != d->itemList.constEnd())
730  result = d->recodeToUser(it->second, encoding);
731  }
732  return result;
733 }
734 
744 QStringList QUrlQuery::allQueryItemValues(const QString &key, QUrl::ComponentFormattingOptions encoding) const
745 {
747  if (d) {
748  QString encodedKey = d->recodeFromUser(key);
749  int idx = d->findRecodedKey(encodedKey);
750  while (idx < d->itemList.size()) {
751  result << d->recodeToUser(d->itemList.at(idx).second, encoding);
752  idx = d->findRecodedKey(encodedKey, idx + 1);
753  }
754  }
755  return result;
756 }
757 
769 {
770  if (d.constData()) {
771  auto *p = d.data();
772  Map::iterator it = p->findKey(key);
773  if (it != p->itemList.end())
774  p->itemList.erase(it);
775  }
776 }
777 
787 {
788  if (d.constData()) {
789  auto *p = d.data();
790  const QString encodedKey = p->recodeFromUser(key);
791  auto firstEqualsEncodedKey = [&encodedKey](const QPair<QString, QString> &item) {
792  return item.first == encodedKey;
793  };
794  p->itemList.removeIf(firstEqualsEncodedKey);
795  }
796 }
797 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
#define value
[5]
FT_UInt idx
Definition: cffcmap.c:135
bool ref() noexcept
Definition: qbasicatomic.h:101
T loadRelaxed() const noexcept
Definition: qbasicatomic.h:90
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:84
constexpr char16_t unicode() const noexcept
Definition: qchar.h:489
template< typename Enum > size_t qHash(QFlags< Enum > flags, size_t seed=0) noexcept
Definition: qlist.h:108
qsizetype size() const noexcept
Definition: qlist.h:414
bool isEmpty() const noexcept
Definition: qlist.h:418
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
const_iterator constBegin() const noexcept
Definition: qlist.h:630
qsizetype count() const noexcept
Definition: qlist.h:415
iterator begin()
Definition: qlist.h:623
void append(parameter_type t)
Definition: qlist.h:469
const_iterator constEnd() const noexcept
Definition: qlist.h:631
void clear()
Definition: qlist.h:445
The QSharedData class is a base class for shared data objects. \reentrant.
Definition: qshareddata.h:55
QAtomicInt ref
Definition: qshareddata.h:57
const T * constData() const noexcept
Definition: qshareddata.h:87
The QString class provides a Unicode character string.
Definition: qstring.h:388
QString & removeIf(Predicate pred)
Definition: qstring.h:677
The QStringList class provides a list of strings.
The QStringView class provides a unified view on UTF-16 strings with a read-only subset of the QStrin...
Definition: qstringview.h:122
The QUrl class provides a convenient interface for working with URLs.
Definition: qurl.h:130
bool hasQuery() const
Definition: qurl.cpp:2533
QString query(ComponentFormattingOptions=PrettyDecoded) const
Definition: qurl.cpp:2629
@ PrettyDecoded
Definition: qurl.h:157
@ EncodeDelimiters
Definition: qurl.h:160
@ DecodeReserved
Definition: qurl.h:162
The QUrlQuery class provides a way to manipulate a key-value pairs in a URL's query.
Definition: qurlquery.h:56
void setQuery(const QString &queryString)
Definition: qurlquery.cpp:500
void removeAllQueryItems(const QString &key)
Definition: qurlquery.cpp:786
void setQueryItems(const QList< QPair< QString, QString > > &query)
Definition: qurlquery.cpp:638
void clear()
Definition: qurlquery.cpp:488
bool isEmpty() const
Definition: qurlquery.cpp:468
static constexpr char16_t defaultQueryPairDelimiter() noexcept
Definition: qurlquery.h:103
void addQueryItem(const QString &key, const QString &value)
Definition: qurlquery.cpp:704
bool operator==(const QUrlQuery &other) const
Definition: qurlquery.cpp:431
bool hasQueryItem(const QString &key) const
Definition: qurlquery.cpp:684
void removeQueryItem(const QString &key)
Definition: qurlquery.cpp:768
QList< QPair< QString, QString > > queryItems(QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Definition: qurlquery.cpp:659
static constexpr char16_t defaultQueryValueDelimiter() noexcept
Definition: qurlquery.h:102
QString query(QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Definition: qurlquery.cpp:527
void setQueryDelimiters(QChar valueDelimiter, QChar pairDelimiter)
Definition: qurlquery.cpp:598
QUrlQuery & operator=(const QUrlQuery &other)
Definition: qurlquery.cpp:406
QStringList allQueryItemValues(const QString &key, QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Definition: qurlquery.cpp:744
QString queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding=QUrl::PrettyDecoded) const
Definition: qurlquery.cpp:724
QChar queryValueDelimiter() const
Definition: qurlquery.cpp:610
QChar queryPairDelimiter() const
Definition: qurlquery.cpp:621
bool isDetached() const
Definition: qurlquery.cpp:476
QUrlQueryPrivate(const QString &query=QString())
Definition: qurlquery.cpp:174
int findRecodedKey(const QString &key, int from=0) const
Definition: qurlquery.cpp:186
void addQueryItem(const QString &key, const QString &value)
Definition: qurlquery.cpp:184
void setQuery(const QString &query)
Definition: qurlquery.cpp:292
QString recodeFromUser(const QString &input) const
Definition: qurlquery.cpp:246
QString recodeToUser(const QString &input, QUrl::ComponentFormattingOptions encoding) const
Definition: qurlquery.cpp:268
Map::const_iterator findKey(const QString &key) const
Definition: qurlquery.cpp:193
Map::iterator findKey(const QString &key)
Definition: qurlquery.cpp:195
QHash< int, QWidget * > hash
[35multi]
typename C::const_iterator const_iterator
typename C::iterator iterator
constexpr Initialization Uninitialized
Definition: qnamespace.h:1613
#define QString()
Definition: parse-defines.h:51
std::pair< T1, T2 > QPair
Definition: qcontainerfwd.h:56
EGLOutputLayerEXT EGLint EGLAttrib value
unsigned short ushort
Definition: qglobal.h:333
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum query
Definition: qopenglext.h:2738
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
GLenum GLenum GLenum input
Definition: qopenglext.h:10816
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition: qpair.h:55
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
QT_BEGIN_NAMESPACE Q_AUTOTEST_EXPORT qsizetype qt_urlRecode(QString &appendTo, QStringView url, QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications=nullptr)
Definition: qurlrecode.cpp:674
#define decode(x)
Definition: qurlquery.cpp:242
QList< QPair< QString, QString > > Map
Definition: qurlquery.cpp:169
#define encode(x)
Definition: qurlquery.cpp:244
bool idempotentRecodeToUser(QUrl::ComponentFormattingOptions encoding)
Definition: qurlquery.cpp:263
QUrl url("http://www.example.com/List of holidays.xml")
[0]
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item
QStringList::Iterator it