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