| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2017 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the Qt Network Auth module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:GPL$ | 
| 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 General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
| 19 | ** General Public License version 3 or (at your option) any later version | 
| 20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by | 
| 21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 | 
| 22 | ** included in the packaging of this file. Please review the following | 
| 23 | ** information to ensure the GNU General Public License requirements will | 
| 24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
| 25 | ** | 
| 26 | ** $QT_END_LICENSE$ | 
| 27 | ** | 
| 28 | ****************************************************************************/ | 
| 29 |  | 
| 30 | #include <QtNetwork/qtnetwork-config.h> | 
| 31 |  | 
| 32 | #ifndef QT_NO_HTTP | 
| 33 |  | 
| 34 | #include "qoauth1.h" | 
| 35 | #include "qoauth1_p.h" | 
| 36 | #include "qoauth1signature.h" | 
| 37 | #include "qoauthoobreplyhandler.h" | 
| 38 | #include "qoauthhttpserverreplyhandler.h" | 
| 39 |  | 
| 40 | #include <QtCore/qmap.h> | 
| 41 | #include <QtCore/qlist.h> | 
| 42 | #include <QtCore/qvariant.h> | 
| 43 | #include <QtCore/qurlquery.h> | 
| 44 | #include <QtCore/qdatetime.h> | 
| 45 | #include <QtCore/qbytearray.h> | 
| 46 | #include <QtCore/qmessageauthenticationcode.h> | 
| 47 |  | 
| 48 | #include <QtNetwork/qnetworkreply.h> | 
| 49 | #include <QtNetwork/qnetworkrequest.h> | 
| 50 | #include <QtNetwork/qnetworkaccessmanager.h> | 
| 51 |  | 
| 52 | QT_BEGIN_NAMESPACE | 
| 53 |  | 
| 54 | /*! | 
| 55 |     \class QOAuth1 | 
| 56 |     \inmodule QtNetworkAuth | 
| 57 |     \ingroup oauth | 
| 58 |     \brief The QOAuth1 class provides an implementation of the | 
| 59 |     \l {https://tools.ietf.org/html/rfc5849}{OAuth 1 Protocol}. | 
| 60 |     \since 5.8 | 
| 61 |  | 
| 62 |     QOAuth1 provides a method for clients to access server resources | 
| 63 |     on behalf of a resource owner (such as a different client or an | 
| 64 |     end-user). It also provides a process for end-users to authorize | 
| 65 |     third-party access to their server resources without sharing | 
| 66 |     their credentials (typically, a username and password pair), | 
| 67 |     using user-agent redirections. | 
| 68 |  | 
| 69 |     QOAuth1 uses tokens to represent the authorization granted to the | 
| 70 |     client by the resource owner.  Typically, token credentials are | 
| 71 |     issued by the server at the resource owner's request, after | 
| 72 |     authenticating the resource owner's identity (usually using a | 
| 73 |     username and password). | 
| 74 |  | 
| 75 |     When making the temporary credentials request, the client | 
| 76 |     authenticates using only the client credentials. When making the | 
| 77 |     token request, the client authenticates using the client | 
| 78 |     credentials as well as the temporary credentials. Once the | 
| 79 |     client receives and stores the token credentials, it can | 
| 80 |     proceed to access protected resources on behalf of the resource | 
| 81 |     owner by making authenticated requests using the client | 
| 82 |     credentials together with the token credentials received. | 
| 83 | */ | 
| 84 |  | 
| 85 | /*! | 
| 86 |     \enum QOAuth1::SignatureMethod | 
| 87 |  | 
| 88 |     Indicates the signature method to be used to sign requests. | 
| 89 |  | 
| 90 |     \value Hmac_Sha1 | 
| 91 |     \l {https://tools.ietf.org/html/rfc5849#section-3.4.2} | 
| 92 |     {HMAC-SHA1} signature method. | 
| 93 |  | 
| 94 |     \value Rsa_Sha1 | 
| 95 |     \l {https://tools.ietf.org/html/rfc5849#section-3.4.3} | 
| 96 |     {RSA-SHA1} signature method (not supported). | 
| 97 |  | 
| 98 |     \value PlainText | 
| 99 |     \l {https://tools.ietf.org/html/rfc5849#section-3.4.4} | 
| 100 |     {PLAINTEXT} signature method. | 
| 101 | */ | 
| 102 |  | 
| 103 | using Key = QOAuth1Private::OAuth1KeyString; | 
| 104 | const QString Key::oauthCallback =           QStringLiteral("oauth_callback" ); | 
| 105 | const QString Key::oauthCallbackConfirmed =  QStringLiteral("oauth_callback_confirmed" ); | 
| 106 | const QString Key::oauthConsumerKey =        QStringLiteral("oauth_consumer_key" ); | 
| 107 | const QString Key::oauthNonce =              QStringLiteral("oauth_nonce" ); | 
| 108 | const QString Key::oauthSignature =          QStringLiteral("oauth_signature" ); | 
| 109 | const QString Key::oauthSignatureMethod =    QStringLiteral("oauth_signature_method" ); | 
| 110 | const QString Key::oauthTimestamp =          QStringLiteral("oauth_timestamp" ); | 
| 111 | const QString Key::oauthToken =              QStringLiteral("oauth_token" ); | 
| 112 | const QString Key::oauthTokenSecret =        QStringLiteral("oauth_token_secret" ); | 
| 113 | const QString Key::oauthVerifier =           QStringLiteral("oauth_verifier" ); | 
| 114 | const QString Key::oauthVersion =            QStringLiteral("oauth_version" ); | 
| 115 |  | 
| 116 | QOAuth1Private::QOAuth1Private(const QPair<QString, QString> &clientCredentials, | 
| 117 |                                QNetworkAccessManager *networkAccessManager) : | 
| 118 |     QAbstractOAuthPrivate("qt.networkauth.oauth1" , | 
| 119 |                           QUrl(), | 
| 120 |                           clientCredentials.first, | 
| 121 |                           networkAccessManager), | 
| 122 |     clientIdentifierSharedKey(clientCredentials.second) | 
| 123 | { | 
| 124 |     qRegisterMetaType<QNetworkReply::NetworkError>(typeName: "QNetworkReply::NetworkError" ); | 
| 125 |     qRegisterMetaType<QOAuth1::SignatureMethod>(typeName: "QOAuth1::SignatureMethod" ); | 
| 126 | } | 
| 127 |  | 
| 128 | void QOAuth1Private::(QVariantMap *) | 
| 129 | { | 
| 130 |     const auto currentDateTime = QDateTime::currentDateTimeUtc(); | 
| 131 |  | 
| 132 |     headers->insert(akey: Key::oauthNonce, avalue: QOAuth1::nonce()); | 
| 133 |     headers->insert(akey: Key::oauthConsumerKey, avalue: clientIdentifier); | 
| 134 |     headers->insert(akey: Key::oauthTimestamp, avalue: QString::number(currentDateTime.toSecsSinceEpoch())); | 
| 135 |     headers->insert(akey: Key::oauthVersion, avalue: oauthVersion); | 
| 136 |     headers->insert(akey: Key::oauthSignatureMethod, avalue: signatureMethodString().toUtf8()); | 
| 137 | } | 
| 138 |  | 
| 139 | void QOAuth1Private::appendSignature(QAbstractOAuth::Stage stage, | 
| 140 |                                      QVariantMap *, | 
| 141 |                                      const QUrl &url, | 
| 142 |                                      QNetworkAccessManager::Operation operation, | 
| 143 |                                      const QVariantMap parameters) | 
| 144 | { | 
| 145 |     QByteArray signature; | 
| 146 |     { | 
| 147 |         QMultiMap<QString, QVariant>  = *headers; | 
| 148 |         QVariantMap allParameters = headerCopy.unite(other: parameters); | 
| 149 |         if (modifyParametersFunction) | 
| 150 |             modifyParametersFunction(stage, &allParameters); | 
| 151 |         signature = generateSignature(parameters: allParameters, url, operation); | 
| 152 |     } | 
| 153 |     headers->insert(akey: Key::oauthSignature, avalue: signature); | 
| 154 | } | 
| 155 |  | 
| 156 | QNetworkReply *QOAuth1Private::requestToken(QNetworkAccessManager::Operation operation, | 
| 157 |                                             const QUrl &url, | 
| 158 |                                             const QPair<QString, QString> &token, | 
| 159 |                                             const QVariantMap ¶meters) | 
| 160 | { | 
| 161 |     if (Q_UNLIKELY(!networkAccessManager())) { | 
| 162 |         qCWarning(loggingCategory, "QNetworkAccessManager not available" ); | 
| 163 |         return nullptr; | 
| 164 |     } | 
| 165 |     if (Q_UNLIKELY(url.isEmpty())) { | 
| 166 |         qCWarning(loggingCategory, "Request Url not set" ); | 
| 167 |         return nullptr; | 
| 168 |     } | 
| 169 |     if (Q_UNLIKELY(operation != QNetworkAccessManager::GetOperation && | 
| 170 |                    operation != QNetworkAccessManager::PostOperation)) { | 
| 171 |         qCWarning(loggingCategory, "Operation not supported" ); | 
| 172 |         return nullptr; | 
| 173 |     } | 
| 174 |  | 
| 175 |     QNetworkRequest request(url); | 
| 176 |     request.setAttribute(code: QNetworkRequest::RedirectPolicyAttribute, value: QNetworkRequest::NoLessSafeRedirectPolicy); | 
| 177 |  | 
| 178 |     QAbstractOAuth::Stage stage = QAbstractOAuth::Stage::RequestingTemporaryCredentials; | 
| 179 |     QVariantMap ; | 
| 180 |     QVariantMap remainingParameters; | 
| 181 |     appendCommonHeaders(headers: &headers); | 
| 182 |     for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it) { | 
| 183 |         const auto key = it.key(); | 
| 184 |         const auto value = it.value(); | 
| 185 |         if (key.startsWith(QStringLiteral("oauth_" ))) | 
| 186 |             headers.insert(akey: key, avalue: value); | 
| 187 |         else | 
| 188 |             remainingParameters.insert(akey: key, avalue: value); | 
| 189 |     } | 
| 190 |     if (!token.first.isEmpty()) { | 
| 191 |         headers.insert(akey: Key::oauthToken, avalue: token.first); | 
| 192 |         stage = QAbstractOAuth::Stage::RequestingAccessToken; | 
| 193 |     } | 
| 194 |     appendSignature(stage, headers: &headers, url, operation, parameters: remainingParameters); | 
| 195 |  | 
| 196 |     request.setRawHeader(headerName: "Authorization" , value: QOAuth1::generateAuthorizationHeader(oauthParams: headers)); | 
| 197 |  | 
| 198 |     QNetworkReply *reply = nullptr; | 
| 199 |     if (operation == QNetworkAccessManager::GetOperation) { | 
| 200 |         if (parameters.size() > 0) { | 
| 201 |             QUrl url = request.url(); | 
| 202 |             url.setQuery(QOAuth1Private::createQuery(parameters: remainingParameters)); | 
| 203 |             request.setUrl(url); | 
| 204 |         } | 
| 205 |         reply = networkAccessManager()->get(request); | 
| 206 |     } | 
| 207 |     else if (operation == QNetworkAccessManager::PostOperation) { | 
| 208 |         QUrlQuery query = QOAuth1Private::createQuery(parameters: remainingParameters); | 
| 209 |         const QByteArray data = query.toString(encoding: QUrl::FullyEncoded).toUtf8(); | 
| 210 |         request.setHeader(header: QNetworkRequest::ContentTypeHeader, | 
| 211 |                           QStringLiteral("application/x-www-form-urlencoded" )); | 
| 212 |         reply = networkAccessManager()->post(request, data); | 
| 213 |     } | 
| 214 |  | 
| 215 |     connect(sender: reply, signal: &QNetworkReply::errorOccurred, | 
| 216 |             receiverPrivate: this, slot: &QOAuth1Private::_q_onTokenRequestError); | 
| 217 |  | 
| 218 |     QAbstractOAuthReplyHandler *handler = replyHandler ? replyHandler.data() | 
| 219 |                                                        : defaultReplyHandler.data(); | 
| 220 |     QObject::connect(sender: reply, signal: &QNetworkReply::finished, | 
| 221 |                      slot: [handler, reply]() { handler->networkReplyFinished(reply); }); | 
| 222 |     connect(sender: handler, signal: &QAbstractOAuthReplyHandler::tokensReceived, receiverPrivate: this, | 
| 223 |             slot: &QOAuth1Private::_q_tokensReceived); | 
| 224 |  | 
| 225 |     return reply; | 
| 226 | } | 
| 227 |  | 
| 228 | QString QOAuth1Private::signatureMethodString() const | 
| 229 | { | 
| 230 |     switch (signatureMethod) { // No default: intended | 
| 231 |     case QOAuth1::SignatureMethod::PlainText: | 
| 232 |         return QStringLiteral("PLAINTEXT" ); | 
| 233 |     case QOAuth1::SignatureMethod::Hmac_Sha1: | 
| 234 |         return QStringLiteral("HMAC-SHA1" ); | 
| 235 |     case QOAuth1::SignatureMethod::Rsa_Sha1: | 
| 236 |         qFatal(msg: "RSA-SHA1 signature method not supported" ); | 
| 237 |         return QStringLiteral("RSA-SHA1" ); | 
| 238 |     } | 
| 239 |     qFatal(msg: "Invalid signature method" ); | 
| 240 |     return QString(); | 
| 241 | } | 
| 242 |  | 
| 243 | QByteArray QOAuth1Private::generateSignature(const QVariantMap ¶meters, | 
| 244 |                                              const QUrl &url, | 
| 245 |                                              QNetworkAccessManager::Operation operation) const | 
| 246 | { | 
| 247 |     QOAuth1Signature signature(url, | 
| 248 |                                clientIdentifierSharedKey, | 
| 249 |                                tokenSecret, | 
| 250 |                                static_cast<QOAuth1Signature::HttpRequestMethod>(operation), | 
| 251 |                                parameters); | 
| 252 |     return formatSignature(signature); | 
| 253 | } | 
| 254 |  | 
| 255 | QByteArray QOAuth1Private::generateSignature(const QVariantMap ¶meters, | 
| 256 |                                              const QUrl &url, | 
| 257 |                                              const QByteArray &verb) const | 
| 258 | { | 
| 259 |     QOAuth1Signature signature(url, | 
| 260 |                                clientIdentifierSharedKey, | 
| 261 |                                tokenSecret, | 
| 262 |                                QOAuth1Signature::HttpRequestMethod::Custom, | 
| 263 |                                parameters); | 
| 264 |     signature.setCustomMethodString(verb); | 
| 265 |     return formatSignature(signature); | 
| 266 | } | 
| 267 |  | 
| 268 | QByteArray QOAuth1Private::formatSignature(const QOAuth1Signature &signature) const | 
| 269 | { | 
| 270 |     switch (signatureMethod) { | 
| 271 |     case QOAuth1::SignatureMethod::Hmac_Sha1: | 
| 272 |         return signature.hmacSha1().toBase64(); | 
| 273 |     case QOAuth1::SignatureMethod::PlainText: | 
| 274 |         return signature.plainText(); | 
| 275 |     default: | 
| 276 |         qFatal(msg: "QOAuth1Private::generateSignature: Signature method not supported" ); | 
| 277 |         return QByteArray(); | 
| 278 |     } | 
| 279 | } | 
| 280 |  | 
| 281 | QVariantMap QOAuth1Private::createOAuthBaseParams() const | 
| 282 | { | 
| 283 |     QVariantMap oauthParams; | 
| 284 |  | 
| 285 |     const auto currentDateTime = QDateTime::currentDateTimeUtc(); | 
| 286 |  | 
| 287 |     oauthParams.insert(akey: Key::oauthConsumerKey, avalue: clientIdentifier); | 
| 288 |     oauthParams.insert(akey: Key::oauthVersion, QStringLiteral("1.0" )); | 
| 289 |     oauthParams.insert(akey: Key::oauthToken, avalue: token); | 
| 290 |     oauthParams.insert(akey: Key::oauthSignatureMethod, avalue: signatureMethodString()); | 
| 291 |     oauthParams.insert(akey: Key::oauthNonce, avalue: QOAuth1::nonce()); | 
| 292 |     oauthParams.insert(akey: Key::oauthTimestamp, avalue: QString::number(currentDateTime.toSecsSinceEpoch())); | 
| 293 |  | 
| 294 |     return oauthParams; | 
| 295 | } | 
| 296 |  | 
| 297 | void QOAuth1Private::prepareRequestImpl(QNetworkRequest *request, | 
| 298 |                                         const QByteArray &verb, | 
| 299 |                                         const QByteArray &body) | 
| 300 | { | 
| 301 |     Q_Q(QOAuth1); | 
| 302 |     QVariantMap signingParams; | 
| 303 |     if (verb == "POST"  && | 
| 304 |         request->header(header: QNetworkRequest::ContentTypeHeader).toByteArray() | 
| 305 |             == "application/x-www-form-urlencoded" ) { | 
| 306 |         QUrlQuery query(QString::fromUtf8(str: body)); | 
| 307 |         for (const auto &item : query.queryItems(encoding: QUrl::FullyDecoded)) | 
| 308 |             signingParams.insert(akey: item.first, avalue: item.second); | 
| 309 |     } | 
| 310 |     q->setup(request, signingParameters: signingParams, operationVerb: verb); | 
| 311 | } | 
| 312 |  | 
| 313 | void QOAuth1Private::_q_onTokenRequestError(QNetworkReply::NetworkError error) | 
| 314 | { | 
| 315 |     Q_Q(QOAuth1); | 
| 316 |     Q_UNUSED(error); | 
| 317 |     Q_EMIT q->requestFailed(error: QAbstractOAuth::Error::NetworkError); | 
| 318 | } | 
| 319 |  | 
| 320 | void QOAuth1Private::_q_tokensReceived(const QVariantMap &tokens) | 
| 321 | { | 
| 322 |     Q_Q(QOAuth1); | 
| 323 |  | 
| 324 |     if (!tokenRequested && status == QAbstractOAuth::Status::TemporaryCredentialsReceived) { | 
| 325 |         // We didn't request a token yet, but in the "TemporaryCredentialsReceived" state _any_ | 
| 326 |         // new tokens received will count as a successful authentication and we move to the | 
| 327 |         // 'Granted' state. To avoid this, 'status' will be temporarily set to 'NotAuthenticated'. | 
| 328 |         status = QAbstractOAuth::Status::NotAuthenticated; | 
| 329 |     } | 
| 330 |     if (tokenRequested) // 'Reset' tokenRequested now that we've gotten new tokens | 
| 331 |         tokenRequested = false; | 
| 332 |  | 
| 333 |     QPair<QString, QString> credential(tokens.value(akey: Key::oauthToken).toString(), | 
| 334 |                                        tokens.value(akey: Key::oauthTokenSecret).toString()); | 
| 335 |     switch (status) { | 
| 336 |     case QAbstractOAuth::Status::NotAuthenticated: | 
| 337 |         if (tokens.value(akey: Key::oauthCallbackConfirmed, adefaultValue: true).toBool()) { | 
| 338 |             q->setTokenCredentials(credential); | 
| 339 |             setStatus(QAbstractOAuth::Status::TemporaryCredentialsReceived); | 
| 340 |         } else { | 
| 341 |             Q_EMIT q->requestFailed(error: QAbstractOAuth::Error::OAuthCallbackNotVerified); | 
| 342 |         } | 
| 343 |         break; | 
| 344 |     case QAbstractOAuth::Status::TemporaryCredentialsReceived: | 
| 345 |         q->setTokenCredentials(credential); | 
| 346 |         setStatus(QAbstractOAuth::Status::Granted); | 
| 347 |         break; | 
| 348 |     case QAbstractOAuth::Status::Granted: | 
| 349 |     case QAbstractOAuth::Status::RefreshingToken: | 
| 350 |         break; | 
| 351 |     } | 
| 352 | } | 
| 353 |  | 
| 354 | /*! | 
| 355 |     Constructs a QOAuth1 object with parent object \a parent. | 
| 356 | */ | 
| 357 | QOAuth1::QOAuth1(QObject *parent) : | 
| 358 |     QOAuth1(nullptr, | 
| 359 |             parent) | 
| 360 | {} | 
| 361 |  | 
| 362 | /*! | 
| 363 |     Constructs a QOAuth1 object with parent object \a parent, using | 
| 364 |     \a manager to access the network. | 
| 365 | */ | 
| 366 | QOAuth1::QOAuth1(QNetworkAccessManager *manager, QObject *parent) : | 
| 367 |     QOAuth1(QString(), | 
| 368 |             QString(), | 
| 369 |             manager, | 
| 370 |             parent) | 
| 371 | {} | 
| 372 |  | 
| 373 | /*! | 
| 374 |     Constructs a QOAuth1 object with parent object \a parent, using | 
| 375 |     \a manager to access the network. | 
| 376 |     Also sets \a clientIdentifier and \a clientSharedSecret to sign | 
| 377 |     the calls to the web server and identify the application. | 
| 378 | */ | 
| 379 | QOAuth1::QOAuth1(const QString &clientIdentifier, | 
| 380 |                  const QString &clientSharedSecret, | 
| 381 |                  QNetworkAccessManager *manager, | 
| 382 |                  QObject *parent) | 
| 383 |     : QAbstractOAuth(*new QOAuth1Private(qMakePair(x: clientIdentifier, y: clientSharedSecret), | 
| 384 |                                          manager), | 
| 385 |                      parent) | 
| 386 | {} | 
| 387 |  | 
| 388 | /*! | 
| 389 |     Returns the current shared secret used to sign requests to | 
| 390 |     the web server. | 
| 391 |  | 
| 392 |     \sa setClientSharedSecret(), clientCredentials() | 
| 393 | */ | 
| 394 | QString QOAuth1::clientSharedSecret() const | 
| 395 | { | 
| 396 |     Q_D(const QOAuth1); | 
| 397 |     return d->clientIdentifierSharedKey; | 
| 398 | } | 
| 399 |  | 
| 400 | /*! | 
| 401 |     Sets \a clientSharedSecret as the string used to sign the | 
| 402 |     requests to the web server. | 
| 403 |  | 
| 404 |     \sa clientSharedSecret(), setClientCredentials() | 
| 405 | */ | 
| 406 | void QOAuth1::setClientSharedSecret(const QString &clientSharedSecret) | 
| 407 | { | 
| 408 |     Q_D(QOAuth1); | 
| 409 |     if (d->clientIdentifierSharedKey != clientSharedSecret) { | 
| 410 |         d->clientIdentifierSharedKey = clientSharedSecret; | 
| 411 |         Q_EMIT clientSharedSecretChanged(credential: clientSharedSecret); | 
| 412 |     } | 
| 413 | } | 
| 414 |  | 
| 415 | /*! | 
| 416 |     Returns the pair of QString used to identify the application and | 
| 417 |     sign requests to the web server. | 
| 418 |  | 
| 419 |     \sa setClientCredentials() | 
| 420 | */ | 
| 421 | QPair<QString, QString> QOAuth1::clientCredentials() const | 
| 422 | { | 
| 423 |     Q_D(const QOAuth1); | 
| 424 |     return qMakePair(x: d->clientIdentifier, y: d->clientIdentifierSharedKey); | 
| 425 | } | 
| 426 |  | 
| 427 | /*! | 
| 428 |     Sets \a clientCredentials as the pair of QString used to identify | 
| 429 |     the application and sign requests to the web server. | 
| 430 |  | 
| 431 |     \sa clientCredentials() | 
| 432 | */ | 
| 433 | void QOAuth1::setClientCredentials(const QPair<QString, QString> &clientCredentials) | 
| 434 | { | 
| 435 |     setClientCredentials(clientIdentifier: clientCredentials.first, clientSharedSecret: clientCredentials.second); | 
| 436 | } | 
| 437 |  | 
| 438 | /*! | 
| 439 |     Sets \a clientIdentifier and \a clientSharedSecret as the pair of | 
| 440 |     QString used to identify the application and sign requests to the | 
| 441 |     web server. \a clientIdentifier identifies the application and | 
| 442 |     \a clientSharedSecret is used to sign requests. | 
| 443 |  | 
| 444 |     \sa clientCredentials() | 
| 445 | */ | 
| 446 | void QOAuth1::setClientCredentials(const QString &clientIdentifier, | 
| 447 |                                    const QString &clientSharedSecret) | 
| 448 | { | 
| 449 |     setClientIdentifier(clientIdentifier); | 
| 450 |     setClientSharedSecret(clientSharedSecret); | 
| 451 | } | 
| 452 |  | 
| 453 | /*! | 
| 454 |     Returns the current token secret used to sign authenticated | 
| 455 |     requests to the web server. | 
| 456 |  | 
| 457 |     \sa setTokenSecret(), tokenCredentials() | 
| 458 | */ | 
| 459 | QString QOAuth1::tokenSecret() const | 
| 460 | { | 
| 461 |     Q_D(const QOAuth1); | 
| 462 |     return d->tokenSecret; | 
| 463 | } | 
| 464 | /*! | 
| 465 |     Sets \a tokenSecret as the current token secret used to sign | 
| 466 |     authenticated calls to the web server. | 
| 467 |  | 
| 468 |     \sa tokenSecret(), setTokenCredentials() | 
| 469 | */ | 
| 470 | void QOAuth1::setTokenSecret(const QString &tokenSecret) | 
| 471 | { | 
| 472 |     Q_D(QOAuth1); | 
| 473 |     if (d->tokenSecret != tokenSecret) { | 
| 474 |         d->tokenSecret = tokenSecret; | 
| 475 |         Q_EMIT tokenSecretChanged(token: tokenSecret); | 
| 476 |     } | 
| 477 | } | 
| 478 |  | 
| 479 | /*! | 
| 480 |     Returns the pair of QString used to identify and sign | 
| 481 |     authenticated requests to the web server. | 
| 482 |  | 
| 483 |     \sa setTokenCredentials() | 
| 484 | */ | 
| 485 | QPair<QString, QString> QOAuth1::tokenCredentials() const | 
| 486 | { | 
| 487 |     Q_D(const QOAuth1); | 
| 488 |     return qMakePair(x: d->token, y: d->tokenSecret); | 
| 489 | } | 
| 490 |  | 
| 491 | /*! | 
| 492 |     Sets \a tokenCredentials as the pair of QString used to identify | 
| 493 |     and sign authenticated requests to the web server. | 
| 494 |  | 
| 495 |     \sa tokenCredentials() | 
| 496 | */ | 
| 497 | void QOAuth1::setTokenCredentials(const QPair<QString, QString> &tokenCredentials) | 
| 498 | { | 
| 499 |     setTokenCredentials(token: tokenCredentials.first, tokenSecret: tokenCredentials.second); | 
| 500 | } | 
| 501 |  | 
| 502 | /*! | 
| 503 |     Sets \a token and \a tokenSecret as the pair of QString used to | 
| 504 |     identify and sign authenticated requests to the web server. | 
| 505 |     Once the client receives and stores the token credentials, it can | 
| 506 |     proceed to access protected resources on behalf of the resource | 
| 507 |     owner by making authenticated requests using the client | 
| 508 |     credentials together with the token credentials received. | 
| 509 |  | 
| 510 |     \sa tokenCredentials() | 
| 511 | */ | 
| 512 | void QOAuth1::setTokenCredentials(const QString &token, const QString &tokenSecret) | 
| 513 | { | 
| 514 |     setToken(token); | 
| 515 |     setTokenSecret(tokenSecret); | 
| 516 | } | 
| 517 |  | 
| 518 | /*! | 
| 519 |     Returns the url used to request temporary credentials to | 
| 520 |     start the authentication process. | 
| 521 |  | 
| 522 |     \sa setTemporaryCredentialsUrl() | 
| 523 | */ | 
| 524 | QUrl QOAuth1::temporaryCredentialsUrl() const | 
| 525 | { | 
| 526 |     Q_D(const QOAuth1); | 
| 527 |     return d->temporaryCredentialsUrl; | 
| 528 | } | 
| 529 |  | 
| 530 | /*! | 
| 531 |     Sets \a url as the URL to request temporary credentials to | 
| 532 |     start the authentication process. | 
| 533 |  | 
| 534 |     \sa temporaryCredentialsUrl() | 
| 535 | */ | 
| 536 | void QOAuth1::setTemporaryCredentialsUrl(const QUrl &url) | 
| 537 | { | 
| 538 |     Q_D(QOAuth1); | 
| 539 |     if (d->temporaryCredentialsUrl != url) { | 
| 540 |         d->temporaryCredentialsUrl = url; | 
| 541 |         Q_EMIT temporaryCredentialsUrlChanged(url); | 
| 542 |     } | 
| 543 | } | 
| 544 |  | 
| 545 | /*! | 
| 546 |     Returns the url used to request token credentials to continue | 
| 547 |     the authentication process. | 
| 548 |  | 
| 549 |     \sa setTokenCredentialsUrl() | 
| 550 | */ | 
| 551 | QUrl QOAuth1::tokenCredentialsUrl() const | 
| 552 | { | 
| 553 |     Q_D(const QOAuth1); | 
| 554 |     return d->tokenCredentialsUrl; | 
| 555 | } | 
| 556 |  | 
| 557 | /*! | 
| 558 |     Sets \a url as the URL to request the token credentials to | 
| 559 |     continue the authentication process. | 
| 560 |  | 
| 561 |     \sa tokenCredentialsUrl() | 
| 562 | */ | 
| 563 | void QOAuth1::setTokenCredentialsUrl(const QUrl &url) | 
| 564 | { | 
| 565 |     Q_D(QOAuth1); | 
| 566 |     if (d->tokenCredentialsUrl != url) { | 
| 567 |         d->tokenCredentialsUrl = url; | 
| 568 |         Q_EMIT tokenCredentialsUrlChanged(url); | 
| 569 |     } | 
| 570 | } | 
| 571 |  | 
| 572 | /*! | 
| 573 |     Returns the method used to sign the request to the web server. | 
| 574 |  | 
| 575 |     \sa setSignatureMethod() | 
| 576 | */ | 
| 577 | QOAuth1::SignatureMethod QOAuth1::signatureMethod() const | 
| 578 | { | 
| 579 |     Q_D(const QOAuth1); | 
| 580 |     return d->signatureMethod; | 
| 581 | } | 
| 582 |  | 
| 583 | /*! | 
| 584 |     Sets \a value as the method used to sign requests to the web | 
| 585 |     server. | 
| 586 |  | 
| 587 |     \sa signatureMethod() | 
| 588 | */ | 
| 589 | void QOAuth1::setSignatureMethod(QOAuth1::SignatureMethod value) | 
| 590 | { | 
| 591 |     Q_D(QOAuth1); | 
| 592 |     if (d->signatureMethod != value) { | 
| 593 |         d->signatureMethod = value; | 
| 594 |         Q_EMIT signatureMethodChanged(method: value); | 
| 595 |     } | 
| 596 | } | 
| 597 |  | 
| 598 | /*! | 
| 599 |     Sends an authenticated HEAD request and returns a new | 
| 600 |     QNetworkReply. The \a url and \a parameters are used to create | 
| 601 |     the request. | 
| 602 |  | 
| 603 |     \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.4} | 
| 604 |     {Hypertext Transfer Protocol -- HTTP/1.1: HEAD} | 
| 605 | */ | 
| 606 | QNetworkReply *QOAuth1::(const QUrl &url, const QVariantMap ¶meters) | 
| 607 | { | 
| 608 |     Q_D(QOAuth1); | 
| 609 |     if (!d->networkAccessManager()) { | 
| 610 |         qCWarning(d->loggingCategory, "QNetworkAccessManager not available" ); | 
| 611 |         return nullptr; | 
| 612 |     } | 
| 613 |     QNetworkRequest request(url); | 
| 614 |     setup(request: &request, signingParameters: parameters, operation: QNetworkAccessManager::HeadOperation); | 
| 615 |     return d->networkAccessManager()->head(request); | 
| 616 | } | 
| 617 |  | 
| 618 | /*! | 
| 619 |     Sends an authenticated GET request and returns a new | 
| 620 |     QNetworkReply. The \a url and \a parameters are used to create | 
| 621 |     the request. | 
| 622 |  | 
| 623 |     \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.3} | 
| 624 |     {Hypertext Transfer Protocol -- HTTP/1.1: GET} | 
| 625 | */ | 
| 626 | QNetworkReply *QOAuth1::get(const QUrl &url, const QVariantMap ¶meters) | 
| 627 | { | 
| 628 |     Q_D(QOAuth1); | 
| 629 |     if (!d->networkAccessManager()) { | 
| 630 |         qCWarning(d->loggingCategory, "QNetworkAccessManager not available" ); | 
| 631 |         return nullptr; | 
| 632 |     } | 
| 633 |     QNetworkRequest request(url); | 
| 634 |     setup(request: &request, signingParameters: parameters, operation: QNetworkAccessManager::GetOperation); | 
| 635 |     QNetworkReply *reply = d->networkAccessManager()->get(request); | 
| 636 |     connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); }); | 
| 637 |     return reply; | 
| 638 | } | 
| 639 |  | 
| 640 | /*! | 
| 641 |     Sends an authenticated POST request and returns a new | 
| 642 |     QNetworkReply. The \a url and \a parameters are used to create | 
| 643 |     the request. | 
| 644 |  | 
| 645 |     \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.5} | 
| 646 |     {Hypertext Transfer Protocol -- HTTP/1.1: POST} | 
| 647 | */ | 
| 648 | QNetworkReply *QOAuth1::post(const QUrl &url, const QVariantMap ¶meters) | 
| 649 | { | 
| 650 |     Q_D(QOAuth1); | 
| 651 |     if (!d->networkAccessManager()) { | 
| 652 |         qCWarning(d->loggingCategory, "QNetworkAccessManager not available" ); | 
| 653 |         return nullptr; | 
| 654 |     } | 
| 655 |     QNetworkRequest request(url); | 
| 656 |     setup(request: &request, signingParameters: parameters, operation: QNetworkAccessManager::PostOperation); | 
| 657 |     d->addContentTypeHeaders(request: &request); | 
| 658 |  | 
| 659 |     const QByteArray data = d->convertParameters(parameters); | 
| 660 |     QNetworkReply *reply = d->networkAccessManager()->post(request, data); | 
| 661 |     connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); }); | 
| 662 |     return reply; | 
| 663 | } | 
| 664 |  | 
| 665 | /*! | 
| 666 |     Sends an authenticated PUT request and returns a new | 
| 667 |     QNetworkReply. The \a url and \a parameters are used to create | 
| 668 |     the request. | 
| 669 |  | 
| 670 |     \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.6} | 
| 671 |     {Hypertext Transfer Protocol -- HTTP/1.1: PUT} | 
| 672 | */ | 
| 673 | QNetworkReply *QOAuth1::put(const QUrl &url, const QVariantMap ¶meters) | 
| 674 | { | 
| 675 |     Q_D(QOAuth1); | 
| 676 |     if (!d->networkAccessManager()) { | 
| 677 |         qCWarning(d->loggingCategory, "QNetworkAccessManager not available" ); | 
| 678 |         return nullptr; | 
| 679 |     } | 
| 680 |     QNetworkRequest request(url); | 
| 681 |     setup(request: &request, signingParameters: parameters, operation: QNetworkAccessManager::PutOperation); | 
| 682 |     d->addContentTypeHeaders(request: &request); | 
| 683 |  | 
| 684 |     const QByteArray data = d->convertParameters(parameters); | 
| 685 |     QNetworkReply *reply = d->networkAccessManager()->put(request, data); | 
| 686 |     connect(sender: reply, signal: &QNetworkReply::finished, slot: std::bind(f: &QAbstractOAuth::finished, args: this, args&: reply)); | 
| 687 |     return reply; | 
| 688 | } | 
| 689 |  | 
| 690 | /*! | 
| 691 |     Sends an authenticated DELETE request and returns a new | 
| 692 |     QNetworkReply. The \a url and \a parameters are used to create | 
| 693 |     the request. | 
| 694 |  | 
| 695 |     \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.7} | 
| 696 |     {Hypertext Transfer Protocol -- HTTP/1.1: DELETE} | 
| 697 | */ | 
| 698 | QNetworkReply *QOAuth1::deleteResource(const QUrl &url, const QVariantMap ¶meters) | 
| 699 | { | 
| 700 |     Q_D(QOAuth1); | 
| 701 |     if (!d->networkAccessManager()) { | 
| 702 |         qCWarning(d->loggingCategory, "QNetworkAccessManager not available" ); | 
| 703 |         return nullptr; | 
| 704 |     } | 
| 705 |     QNetworkRequest request(url); | 
| 706 |     setup(request: &request, signingParameters: parameters, operation: QNetworkAccessManager::DeleteOperation); | 
| 707 |     QNetworkReply *reply = d->networkAccessManager()->deleteResource(request); | 
| 708 |     connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); }); | 
| 709 |     return reply; | 
| 710 | } | 
| 711 |  | 
| 712 | /*! | 
| 713 |     Starts the a request for temporary credentials using the request | 
| 714 |     method \a operation. The request URL is \a url and the | 
| 715 |     \a parameters shall encoded and sent during the request. | 
| 716 |  | 
| 717 |     \b {See also}: \l {https://tools.ietf.org/html/rfc5849#section-2.1} | 
| 718 |     {The OAuth 1.0 Protocol: Temporary Credentials} | 
| 719 | */ | 
| 720 | QNetworkReply *QOAuth1::requestTemporaryCredentials(QNetworkAccessManager::Operation operation, | 
| 721 |                                                     const QUrl &url, | 
| 722 |                                                     const QVariantMap ¶meters) | 
| 723 | { | 
| 724 |     Q_D(QOAuth1); | 
| 725 |     d->token.clear(); | 
| 726 |     d->tokenSecret.clear(); | 
| 727 |     QVariantMap allParameters(parameters); | 
| 728 |     allParameters.insert(akey: Key::oauthCallback, avalue: callback()); | 
| 729 |     return d->requestToken(operation, url, token: qMakePair(x: d->token, y: d->tokenSecret), parameters: allParameters); | 
| 730 | } | 
| 731 |  | 
| 732 | /*! | 
| 733 |     Starts a request for token credentials using the request | 
| 734 |     method \a operation. The request URL is \a url and the | 
| 735 |     \a parameters shall be encoded and sent during the | 
| 736 |     request. The \a temporaryToken pair of string is used to identify | 
| 737 |     and sign the request. | 
| 738 |  | 
| 739 |     \b {See also}: \l {https://tools.ietf.org/html/rfc5849#section-2.3} | 
| 740 |     {The OAuth 1.0 Protocol: Token Credentials} | 
| 741 | */ | 
| 742 | QNetworkReply *QOAuth1::requestTokenCredentials(QNetworkAccessManager::Operation operation, | 
| 743 |                                                 const QUrl &url, | 
| 744 |                                                 const QPair<QString, QString> &temporaryToken, | 
| 745 |                                                 const QVariantMap ¶meters) | 
| 746 | { | 
| 747 |     Q_D(QOAuth1); | 
| 748 |     d->tokenRequested = true; | 
| 749 |     return d->requestToken(operation, url, token: temporaryToken, parameters); | 
| 750 | } | 
| 751 |  | 
| 752 | /*! | 
| 753 |     Signs the \a request using \a signingParameters and \a operation. | 
| 754 |  | 
| 755 |     \overload | 
| 756 | */ | 
| 757 | void QOAuth1::setup(QNetworkRequest *request, | 
| 758 |                     const QVariantMap &signingParameters, | 
| 759 |                     QNetworkAccessManager::Operation operation) | 
| 760 | { | 
| 761 |     Q_D(const QOAuth1); | 
| 762 |  | 
| 763 |     auto oauthParams = d->createOAuthBaseParams(); | 
| 764 |  | 
| 765 |     // Add signature parameter | 
| 766 |     { | 
| 767 |         QMultiMap<QString, QVariant> oauthParamsCopy(oauthParams); | 
| 768 |         const auto parameters = oauthParamsCopy.unite(other: signingParameters); | 
| 769 |         const auto signature = d->generateSignature(parameters, url: request->url(), operation); | 
| 770 |         oauthParams.insert(akey: Key::oauthSignature, avalue: signature); | 
| 771 |     } | 
| 772 |  | 
| 773 |     if (operation == QNetworkAccessManager::GetOperation) { | 
| 774 |         if (signingParameters.size()) { | 
| 775 |             QUrl url = request->url(); | 
| 776 |             QUrlQuery query = QUrlQuery(url.query()); | 
| 777 |             for (auto it = signingParameters.begin(), end = signingParameters.end(); it != end; | 
| 778 |                  ++it) | 
| 779 |                 query.addQueryItem(key: it.key(), value: it.value().toString()); | 
| 780 |             url.setQuery(query); | 
| 781 |             request->setUrl(url); | 
| 782 |         } | 
| 783 |     } | 
| 784 |  | 
| 785 |     request->setRawHeader(headerName: "Authorization" , value: generateAuthorizationHeader(oauthParams)); | 
| 786 |  | 
| 787 |     if (operation == QNetworkAccessManager::PostOperation | 
| 788 |             || operation == QNetworkAccessManager::PutOperation) | 
| 789 |         request->setHeader(header: QNetworkRequest::ContentTypeHeader, | 
| 790 |                            QStringLiteral("application/x-www-form-urlencoded" )); | 
| 791 | } | 
| 792 |  | 
| 793 | /*! | 
| 794 |     \since 5.13 | 
| 795 |  | 
| 796 |     Signs the \a request using \a signingParameters and \a operationVerb. | 
| 797 |  | 
| 798 |     \overload | 
| 799 | */ | 
| 800 | void QOAuth1::setup(QNetworkRequest *request, const QVariantMap &signingParameters, const QByteArray &operationVerb) | 
| 801 | { | 
| 802 |     Q_D(const QOAuth1); | 
| 803 |  | 
| 804 |     auto oauthParams = d->createOAuthBaseParams(); | 
| 805 |  | 
| 806 |     // Add signature parameter | 
| 807 |     { | 
| 808 |         QMultiMap<QString, QVariant> oauthParamsCopy(oauthParams); | 
| 809 |         const auto parameters = oauthParamsCopy.unite(other: signingParameters); | 
| 810 |         const auto signature = d->generateSignature(parameters, url: request->url(), verb: operationVerb); | 
| 811 |         oauthParams.insert(akey: Key::oauthSignature, avalue: signature); | 
| 812 |     } | 
| 813 |  | 
| 814 |     request->setRawHeader(headerName: "Authorization" , value: generateAuthorizationHeader(oauthParams)); | 
| 815 | } | 
| 816 |  | 
| 817 | /*! | 
| 818 |     Generates a nonce. | 
| 819 |  | 
| 820 |     \b {See also}: \l {https://tools.ietf.org/html/rfc5849#section-3.3} | 
| 821 |     {The OAuth 1.0 Protocol: Nonce and Timestamp} | 
| 822 | */ | 
| 823 | QByteArray QOAuth1::nonce() | 
| 824 | { | 
| 825 |     return QAbstractOAuth::generateRandomString(length: 8); | 
| 826 | } | 
| 827 |  | 
| 828 | /*! | 
| 829 |     Generates an authorization header using \a oauthParams. | 
| 830 |  | 
| 831 |     \b {See also}: \l {https://tools.ietf.org/html/rfc5849#section-3.5.1} | 
| 832 |     {The OAuth 1.0 Protocol: Authorization Header} | 
| 833 | */ | 
| 834 | QByteArray QOAuth1::(const QVariantMap &oauthParams) | 
| 835 | { | 
| 836 |     // TODO Add realm parameter support | 
| 837 |     bool first = true; | 
| 838 |     QString ret(QStringLiteral("OAuth " )); | 
| 839 |     QVariantMap (oauthParams); | 
| 840 |     for (auto it = headers.begin(), end = headers.end(); it != end; ++it) { | 
| 841 |         if (first) | 
| 842 |             first = false; | 
| 843 |         else | 
| 844 |             ret += QLatin1String("," ); | 
| 845 |         ret += it.key() + | 
| 846 |                QLatin1String("=\"" ) + | 
| 847 |                QString::fromUtf8(str: QUrl::toPercentEncoding(it.value().toString())) + | 
| 848 |                QLatin1Char('\"'); | 
| 849 |     } | 
| 850 |     return ret.toUtf8(); | 
| 851 | } | 
| 852 |  | 
| 853 | /*! | 
| 854 |     Starts the Redirection-Based Authorization flow. | 
| 855 |  | 
| 856 |     \note For an out-of-band reply handler, a verifier string is | 
| 857 |     received after the call to this function; pass that to | 
| 858 |     continueGrantWithVerifier() to continue the grant process. | 
| 859 |  | 
| 860 |     \sa continueGrantWithVerifier() | 
| 861 |  | 
| 862 |     \b {See also}: \l {https://tools.ietf.org/html/rfc5849#section-2} | 
| 863 |     {The OAuth 1.0 Protocol: Redirection-Based Authorization} | 
| 864 | */ | 
| 865 | void QOAuth1::grant() | 
| 866 | { | 
| 867 |     Q_D(QOAuth1); | 
| 868 |     using Key = QOAuth1Private::OAuth1KeyString; | 
| 869 |  | 
| 870 |     if (d->temporaryCredentialsUrl.isEmpty()) { | 
| 871 |         qCWarning(d->loggingCategory, "requestTokenUrl is empty" ); | 
| 872 |         return; | 
| 873 |     } | 
| 874 |     if (d->tokenCredentialsUrl.isEmpty()) { | 
| 875 |         qCWarning(d->loggingCategory, "authorizationGrantUrl is empty" ); | 
| 876 |         return; | 
| 877 |     } | 
| 878 |     if (!d->token.isEmpty() && status() == Status::Granted) { | 
| 879 |         qCWarning(d->loggingCategory, "Already authenticated" ); | 
| 880 |         return; | 
| 881 |     } | 
| 882 |  | 
| 883 |     QMetaObject::Connection connection; | 
| 884 |     connection = connect(sender: this, signal: &QAbstractOAuth::statusChanged, slot: [&](Status status) { | 
| 885 |         Q_D(QOAuth1); | 
| 886 |  | 
| 887 |         if (status == Status::TemporaryCredentialsReceived) { | 
| 888 |             if (d->authorizationUrl.isEmpty()) { | 
| 889 |                 // try upgrading token without verifier | 
| 890 |                 auto reply = requestTokenCredentials(operation: QNetworkAccessManager::PostOperation, | 
| 891 |                                                      url: d->tokenCredentialsUrl, | 
| 892 |                                                      temporaryToken: qMakePair(x: d->token, y: d->tokenSecret)); | 
| 893 |                 connect(sender: reply, signal: &QNetworkReply::finished, receiver: reply, slot: &QNetworkReply::deleteLater); | 
| 894 |             } else { | 
| 895 |                 QVariantMap parameters; | 
| 896 |                 parameters.insert(akey: Key::oauthToken, avalue: d->token); | 
| 897 |                 if (d->modifyParametersFunction) | 
| 898 |                     d->modifyParametersFunction(Stage::RequestingAuthorization, ¶meters); | 
| 899 |  | 
| 900 |                 // https://tools.ietf.org/html/rfc5849#section-2.2 | 
| 901 |                 resourceOwnerAuthorization(url: d->authorizationUrl, parameters); | 
| 902 |             } | 
| 903 |         } else if (status == Status::NotAuthenticated) { | 
| 904 |             // Inherit class called QAbstractOAuth::setStatus(Status::NotAuthenticated); | 
| 905 |             setTokenCredentials(token: QString(), tokenSecret: QString()); | 
| 906 |             disconnect(connection); | 
| 907 |         } | 
| 908 |     }); | 
| 909 |  | 
| 910 |     auto httpReplyHandler = qobject_cast<QOAuthHttpServerReplyHandler*>(object: replyHandler()); | 
| 911 |     if (httpReplyHandler) { | 
| 912 |         connect(sender: httpReplyHandler, signal: &QOAuthHttpServerReplyHandler::callbackReceived, slot: [&]( | 
| 913 |                 const QVariantMap &values) { | 
| 914 |             QString verifier = values.value(akey: Key::oauthVerifier).toString(); | 
| 915 |             if (verifier.isEmpty()) { | 
| 916 |                 qCWarning(d->loggingCategory, "%s not found in the callback" , | 
| 917 |                           qPrintable(Key::oauthVerifier)); | 
| 918 |                 return; | 
| 919 |             } | 
| 920 |             continueGrantWithVerifier(verifier); | 
| 921 |         }); | 
| 922 |     } | 
| 923 |  | 
| 924 |     // requesting temporary credentials | 
| 925 |     auto reply = requestTemporaryCredentials(operation: QNetworkAccessManager::PostOperation, | 
| 926 |                                              url: d->temporaryCredentialsUrl); | 
| 927 |     connect(sender: reply, signal: &QNetworkReply::finished, receiver: reply, slot: &QNetworkReply::deleteLater); | 
| 928 | } | 
| 929 |  | 
| 930 | /*! | 
| 931 |     Continues the Redirection-Based Authorization flow using | 
| 932 |     \a verifier. Call this function when using an Out-of-band reply | 
| 933 |     handler to supply the verifier provided by the web server. | 
| 934 | */ | 
| 935 | void QOAuth1::continueGrantWithVerifier(const QString &verifier) | 
| 936 | { | 
| 937 |     // https://tools.ietf.org/html/rfc5849#section-2.3 | 
| 938 |     Q_D(QOAuth1); | 
| 939 |  | 
| 940 |     QVariantMap parameters; | 
| 941 |     parameters.insert(akey: Key::oauthVerifier, avalue: verifier); | 
| 942 |     auto reply = requestTokenCredentials(operation: QNetworkAccessManager::PostOperation, | 
| 943 |                                          url: d->tokenCredentialsUrl, | 
| 944 |                                          temporaryToken: qMakePair(x: d->token, y: d->tokenSecret), | 
| 945 |                                          parameters); | 
| 946 |     connect(sender: reply, signal: &QNetworkReply::finished, receiver: reply, slot: &QNetworkReply::deleteLater); | 
| 947 | } | 
| 948 |  | 
| 949 | QT_END_NAMESPACE | 
| 950 |  | 
| 951 | #endif // QT_NO_HTTP | 
| 952 |  |