File indexing completed on 2025-01-19 03:55:39
0001 #include <QList> 0002 #include <QDebug> 0003 #include <QTcpServer> 0004 #include <QMap> 0005 #include <QDateTime> 0006 #include <QCryptographicHash> 0007 #include <QTimer> 0008 #include <QVariantMap> 0009 #include <QUuid> 0010 0011 #if QT_VERSION >= 0x050000 0012 #include <QUrlQuery> 0013 #endif 0014 0015 #if QT_VERSION >= 0x050000 0016 #include <QRegularExpression> 0017 #else 0018 #include <QRegExp> 0019 #endif 0020 0021 #include "o2.h" 0022 #include "o2pollserver.h" 0023 #include "o2replyserver.h" 0024 #include "o0globals.h" 0025 #include "o0jsonresponse.h" 0026 #include "o0settingsstore.h" 0027 0028 /// Add query parameters to a query 0029 static void addQueryParametersToUrl(QUrl &url, QList<QPair<QString, QString> > parameters) { 0030 #if QT_VERSION < 0x050000 0031 url.setQueryItems(parameters); 0032 #else 0033 QUrlQuery query(url); 0034 query.setQueryItems(parameters); 0035 url.setQuery(query); 0036 #endif 0037 } 0038 0039 // ref: https://tools.ietf.org/html/rfc8628#section-3.2 0040 // Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both. 0041 static bool hasMandatoryDeviceAuthParams(const QVariantMap& params) 0042 { 0043 if (!params.contains(O2_OAUTH2_DEVICE_CODE)) 0044 return false; 0045 0046 if (!params.contains(O2_OAUTH2_USER_CODE)) 0047 return false; 0048 0049 if (!(params.contains(O2_OAUTH2_VERIFICATION_URI) || params.contains(O2_OAUTH2_VERIFICATION_URL))) 0050 return false; 0051 0052 if (!params.contains(O2_OAUTH2_EXPIRES_IN)) 0053 return false; 0054 0055 return true; 0056 } 0057 0058 O2::O2(QObject *parent, QNetworkAccessManager *manager, O0AbstractStore *store): O0BaseAuth(parent, store) { 0059 manager_ = manager ? manager : new QNetworkAccessManager(this); 0060 grantFlow_ = GrantFlowAuthorizationCode; 0061 localhostPolicy_ = QString(O2_CALLBACK_URL); 0062 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); 0063 } 0064 0065 O2::GrantFlow O2::grantFlow() { 0066 return grantFlow_; 0067 } 0068 0069 void O2::setGrantFlow(O2::GrantFlow value) { 0070 grantFlow_ = value; 0071 Q_EMIT grantFlowChanged(); 0072 } 0073 0074 QString O2::username() { 0075 return username_; 0076 } 0077 0078 void O2::setUsername(const QString &value) { 0079 username_ = value; 0080 Q_EMIT usernameChanged(); 0081 } 0082 0083 QString O2::password() { 0084 return password_; 0085 } 0086 0087 void O2::setPassword(const QString &value) { 0088 password_ = value; 0089 Q_EMIT passwordChanged(); 0090 } 0091 0092 QString O2::scope() { 0093 return scope_; 0094 } 0095 0096 void O2::setScope(const QString &value) { 0097 scope_ = value; 0098 Q_EMIT scopeChanged(); 0099 } 0100 0101 QString O2::requestUrl() { 0102 return requestUrl_.toString(); 0103 } 0104 0105 void O2::setRequestUrl(const QString &value) { 0106 requestUrl_ = value; 0107 Q_EMIT requestUrlChanged(); 0108 } 0109 0110 QVariantMap O2::extraRequestParams() 0111 { 0112 return extraReqParams_; 0113 } 0114 0115 void O2::setExtraRequestParams(const QVariantMap &value) 0116 { 0117 extraReqParams_ = value; 0118 Q_EMIT extraRequestParamsChanged(); 0119 } 0120 0121 QString O2::tokenUrl() { 0122 return tokenUrl_.toString(); 0123 } 0124 0125 void O2::setTokenUrl(const QString &value) { 0126 tokenUrl_= value; 0127 Q_EMIT tokenUrlChanged(); 0128 } 0129 0130 QString O2::refreshTokenUrl() { 0131 return refreshTokenUrl_.toString(); 0132 } 0133 0134 void O2::setRefreshTokenUrl(const QString &value) { 0135 refreshTokenUrl_ = value; 0136 Q_EMIT refreshTokenUrlChanged(); 0137 } 0138 0139 QString O2::grantType() 0140 { 0141 if (!grantType_.isEmpty()) 0142 return grantType_; 0143 0144 switch (grantFlow_) { 0145 case GrantFlowAuthorizationCode: 0146 return O2_OAUTH2_GRANT_TYPE_CODE; 0147 case GrantFlowImplicit: 0148 return O2_OAUTH2_GRANT_TYPE_TOKEN; 0149 case GrantFlowResourceOwnerPasswordCredentials: 0150 return O2_OAUTH2_GRANT_TYPE_PASSWORD; 0151 case GrantFlowDevice: 0152 return O2_OAUTH2_GRANT_TYPE_DEVICE; 0153 } 0154 0155 return QString(); 0156 } 0157 0158 void O2::setGrantType(const QString &value) 0159 { 0160 grantType_ = value; 0161 } 0162 0163 void O2::link() { 0164 qDebug() << "O2::link"; 0165 0166 // Create the reply server if it doesn't exist 0167 // and we don't use an external web interceptor 0168 if(!useExternalWebInterceptor_) { 0169 if(replyServer() == NULL) { 0170 O2ReplyServer * replyServer = new O2ReplyServer(this); 0171 connect(replyServer, SIGNAL(verificationReceived(QMap<QString,QString>)), this, SLOT(onVerificationReceived(QMap<QString,QString>))); 0172 connect(replyServer, SIGNAL(serverClosed(bool)), this, SLOT(serverHasClosed(bool))); 0173 setReplyServer(replyServer); 0174 } 0175 } 0176 0177 if (linked()) { 0178 qDebug() << "O2::link: Linked already"; 0179 Q_EMIT linkingSucceeded(); 0180 return; 0181 } 0182 0183 setLinked(false); 0184 setToken(""); 0185 setTokenSecret(""); 0186 setExtraTokens(QVariantMap()); 0187 setRefreshToken(QString()); 0188 setExpires(0); 0189 0190 if (grantFlow_ == GrantFlowAuthorizationCode || grantFlow_ == GrantFlowImplicit) { 0191 0192 #if QT_VERSION >= 0x050000 0193 QString uniqueState = QUuid::createUuid().toString().remove(QRegularExpression("([^a-zA-Z0-9]|[-])")); 0194 #else 0195 QString uniqueState = QUuid::createUuid().toString().remove(QRegExp("([^a-zA-Z0-9]|[-])")); 0196 #endif 0197 if (useExternalWebInterceptor_) { 0198 // Save redirect URI, as we have to reuse it when requesting the access token 0199 redirectUri_ = localhostPolicy_.arg(localPort()); 0200 } else { 0201 // Start listening to authentication replies 0202 if (!replyServer()->isListening()) { 0203 if (replyServer()->listen(QHostAddress::Any, localPort_)) { 0204 qDebug() << "O2::link: Reply server listening on port" << localPort(); 0205 } else { 0206 qWarning() << "O2::link: Reply server failed to start listening on port" << localPort(); 0207 Q_EMIT linkingFailed(); 0208 return; 0209 } 0210 } 0211 0212 // Save redirect URI, as we have to reuse it when requesting the access token 0213 redirectUri_ = localhostPolicy_.arg(replyServer()->serverPort()); 0214 replyServer()->setUniqueState(uniqueState); 0215 } 0216 0217 // Assemble intial authentication URL 0218 QList<QPair<QString, QString> > parameters; 0219 parameters.append(qMakePair(QString(O2_OAUTH2_RESPONSE_TYPE), 0220 (grantFlow_ == GrantFlowAuthorizationCode)? QString(O2_OAUTH2_GRANT_TYPE_CODE): QString(O2_OAUTH2_GRANT_TYPE_TOKEN))); 0221 parameters.append(qMakePair(QString(O2_OAUTH2_CLIENT_ID), clientId_)); 0222 if ( !redirectUri_.isEmpty() ) 0223 parameters.append(qMakePair(QString(O2_OAUTH2_REDIRECT_URI), redirectUri_)); 0224 if ( !scope_.isEmpty() ) 0225 parameters.append(qMakePair(QString(O2_OAUTH2_SCOPE), scope_.replace( " ", "+" ))); 0226 parameters.append(qMakePair(QString(O2_OAUTH2_STATE), uniqueState)); 0227 if ( !apiKey_.isEmpty() ) 0228 parameters.append(qMakePair(QString(O2_OAUTH2_API_KEY), apiKey_)); 0229 foreach (QString key, extraRequestParams().keys()) { 0230 parameters.append(qMakePair(key, extraRequestParams().value(key).toString())); 0231 } 0232 // Show authentication URL with a web browser 0233 QUrl url(requestUrl_); 0234 addQueryParametersToUrl(url, parameters); 0235 qDebug() << "O2::link: Emit openBrowser" << url.toString(); 0236 Q_EMIT openBrowser(url); 0237 } else if (grantFlow_ == GrantFlowResourceOwnerPasswordCredentials) { 0238 QList<O0RequestParameter> parameters; 0239 parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_ID, clientId_.toUtf8())); 0240 if ( !clientSecret_.isEmpty() ) 0241 parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_SECRET, clientSecret_.toUtf8())); 0242 parameters.append(O0RequestParameter(O2_OAUTH2_USERNAME, username_.toUtf8())); 0243 parameters.append(O0RequestParameter(O2_OAUTH2_PASSWORD, password_.toUtf8())); 0244 parameters.append(O0RequestParameter(O2_OAUTH2_GRANT_TYPE, O2_OAUTH2_GRANT_TYPE_PASSWORD)); 0245 parameters.append(O0RequestParameter(O2_OAUTH2_SCOPE, scope_.toUtf8())); 0246 if ( !apiKey_.isEmpty() ) 0247 parameters.append(O0RequestParameter(O2_OAUTH2_API_KEY, apiKey_.toUtf8())); 0248 foreach (QString key, extraRequestParams().keys()) { 0249 parameters.append(O0RequestParameter(key.toUtf8(), extraRequestParams().value(key).toByteArray())); 0250 } 0251 QByteArray payload = O0BaseAuth::createQueryParameters(parameters); 0252 0253 qDebug() << "O2::link: Sending token request for resource owner flow"; 0254 QUrl url(tokenUrl_); 0255 QNetworkRequest tokenRequest(url); 0256 tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 0257 QNetworkReply *tokenReply = manager_->post(tokenRequest, payload); 0258 0259 connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection); 0260 #if QT_VERSION < 0x051500 0261 connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0262 #else 0263 connect(tokenReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0264 #endif 0265 } 0266 else if (grantFlow_ == GrantFlowDevice) { 0267 QList<O0RequestParameter> parameters; 0268 parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_ID, clientId_.toUtf8())); 0269 parameters.append(O0RequestParameter(O2_OAUTH2_SCOPE, scope_.toUtf8())); 0270 QByteArray payload = O0BaseAuth::createQueryParameters(parameters); 0271 0272 QUrl url(requestUrl_); 0273 QNetworkRequest deviceRequest(url); 0274 deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 0275 QNetworkReply *tokenReply = manager_->post(deviceRequest, payload); 0276 0277 connect(tokenReply, SIGNAL(finished()), this, SLOT(onDeviceAuthReplyFinished()), Qt::QueuedConnection); 0278 #if QT_VERSION < 0x051500 0279 connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0280 #else 0281 connect(tokenReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0282 #endif 0283 } 0284 } 0285 0286 void O2::unlink() { 0287 qDebug() << "O2::unlink"; 0288 setLinked(false); 0289 setToken(QString()); 0290 setRefreshToken(QString()); 0291 setExpires(0); 0292 setExtraTokens(QVariantMap()); 0293 Q_EMIT linkingSucceeded(); 0294 } 0295 0296 void O2::onVerificationReceived(const QMap<QString, QString> response) { 0297 qDebug() << "O2::onVerificationReceived: Emitting closeBrowser()"; 0298 Q_EMIT closeBrowser(); 0299 0300 if (response.contains("error")) { 0301 qWarning() << "O2::onVerificationReceived: Verification failed:" << response; 0302 Q_EMIT linkingFailed(); 0303 return; 0304 } 0305 0306 if (grantFlow_ == GrantFlowAuthorizationCode) { 0307 // Save access code 0308 setCode(response.value(QString(O2_OAUTH2_GRANT_TYPE_CODE))); 0309 0310 // Exchange access code for access/refresh tokens 0311 QString query; 0312 if(!apiKey_.isEmpty()) 0313 query = QString("?" + QString(O2_OAUTH2_API_KEY) + "=" + apiKey_); 0314 QNetworkRequest tokenRequest(QUrl(tokenUrl_.toString() + query)); 0315 tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); 0316 tokenRequest.setRawHeader("Accept", O2_MIME_TYPE_JSON); 0317 QMap<QString, QString> parameters; 0318 parameters.insert(O2_OAUTH2_GRANT_TYPE_CODE, code()); 0319 parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_); 0320 parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_); 0321 parameters.insert(O2_OAUTH2_REDIRECT_URI, redirectUri_); 0322 parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_AUTHORIZATION_CODE); 0323 QByteArray data = buildRequestBody(parameters); 0324 0325 qDebug() << QString("O2::onVerificationReceived: Exchange access code data:\n%1").arg(QString(data)); 0326 0327 QNetworkReply *tokenReply = manager_->post(tokenRequest, data); 0328 timedReplies_.add(tokenReply); 0329 connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection); 0330 #if QT_VERSION < 0x051500 0331 connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0332 #else 0333 connect(tokenReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0334 #endif 0335 } else if (grantFlow_ == GrantFlowImplicit || grantFlow_ == GrantFlowDevice) { 0336 // Check for mandatory tokens 0337 if (response.contains(O2_OAUTH2_ACCESS_TOKEN)) { 0338 qDebug() << "O2::onVerificationReceived: Access token returned for implicit or device flow"; 0339 setToken(response.value(O2_OAUTH2_ACCESS_TOKEN)); 0340 if (response.contains(O2_OAUTH2_EXPIRES_IN)) { 0341 bool ok = false; 0342 int expiresIn = response.value(O2_OAUTH2_EXPIRES_IN).toInt(&ok); 0343 if (ok) { 0344 qDebug() << "O2::onVerificationReceived: Token expires in" << expiresIn << "seconds"; 0345 setExpires((int)(QDateTime::currentMSecsSinceEpoch() / 1000 + expiresIn)); 0346 } 0347 } 0348 if (response.contains(O2_OAUTH2_REFRESH_TOKEN)) { 0349 setRefreshToken(response.value(O2_OAUTH2_REFRESH_TOKEN)); 0350 } 0351 setLinked(true); 0352 Q_EMIT linkingSucceeded(); 0353 } else { 0354 qWarning() << "O2::onVerificationReceived: Access token missing from response for implicit or device flow"; 0355 Q_EMIT linkingFailed(); 0356 } 0357 } else { 0358 setToken(response.value(O2_OAUTH2_ACCESS_TOKEN)); 0359 setRefreshToken(response.value(O2_OAUTH2_REFRESH_TOKEN)); 0360 } 0361 } 0362 0363 QString O2::code() { 0364 QString key = QString(O2_KEY_CODE).arg(clientId_); 0365 return store_->value(key); 0366 } 0367 0368 void O2::setCode(const QString &c) { 0369 QString key = QString(O2_KEY_CODE).arg(clientId_); 0370 store_->setValue(key, c); 0371 } 0372 0373 void O2::onTokenReplyFinished() { 0374 qDebug() << "O2::onTokenReplyFinished"; 0375 QNetworkReply *tokenReply = qobject_cast<QNetworkReply *>(sender()); 0376 if (!tokenReply) 0377 { 0378 qDebug() << "O2::onTokenReplyFinished: reply is null"; 0379 return; 0380 } 0381 if (tokenReply->error() == QNetworkReply::NoError) { 0382 QByteArray replyData = tokenReply->readAll(); 0383 0384 // Dump replyData 0385 // SENSITIVE DATA in RelWithDebInfo or Debug builds 0386 //qDebug() << "O2::onTokenReplyFinished: replyData\n"; 0387 //qDebug() << QString( replyData ); 0388 0389 QVariantMap tokens = parseJsonResponse(replyData); 0390 0391 // Dump tokens 0392 qDebug() << "O2::onTokenReplyFinished: Tokens returned:\n"; 0393 foreach (QString key, tokens.keys()) { 0394 // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first 0395 qDebug() << key << ": "<< tokens.value( key ).toString().left( 3 ) << "..."; 0396 } 0397 0398 // Check for mandatory tokens 0399 if (tokens.contains(O2_OAUTH2_ACCESS_TOKEN)) { 0400 qDebug() << "O2::onTokenReplyFinished: Access token returned"; 0401 setToken(tokens.take(O2_OAUTH2_ACCESS_TOKEN).toString()); 0402 bool ok = false; 0403 int expiresIn = tokens.take(O2_OAUTH2_EXPIRES_IN).toInt(&ok); 0404 if (ok) { 0405 qDebug() << "O2::onTokenReplyFinished: Token expires in" << expiresIn << "seconds"; 0406 setExpires((int)(QDateTime::currentMSecsSinceEpoch() / 1000 + expiresIn)); 0407 } 0408 setRefreshToken(tokens.take(O2_OAUTH2_REFRESH_TOKEN).toString()); 0409 setExtraTokens(tokens); 0410 timedReplies_.remove(tokenReply); 0411 setLinked(true); 0412 Q_EMIT linkingSucceeded(); 0413 } else { 0414 qWarning() << "O2::onTokenReplyFinished: Access token missing from response"; 0415 Q_EMIT linkingFailed(); 0416 } 0417 } 0418 tokenReply->deleteLater(); 0419 } 0420 0421 void O2::onTokenReplyError(QNetworkReply::NetworkError error) { 0422 QNetworkReply *tokenReply = qobject_cast<QNetworkReply *>(sender()); 0423 if (!tokenReply) 0424 { 0425 qDebug() << "O2::onTokenReplyError: reply is null"; 0426 } else { 0427 qWarning() << "O2::onTokenReplyError: " << error << ": " << tokenReply->errorString(); 0428 qDebug() << "O2::onTokenReplyError: " << tokenReply->readAll(); 0429 timedReplies_.remove(tokenReply); 0430 } 0431 0432 setToken(QString()); 0433 setRefreshToken(QString()); 0434 Q_EMIT linkingFailed(); 0435 } 0436 0437 QByteArray O2::buildRequestBody(const QMap<QString, QString> ¶meters) { 0438 QByteArray body; 0439 bool first = true; 0440 foreach (QString key, parameters.keys()) { 0441 if (first) { 0442 first = false; 0443 } else { 0444 body.append("&"); 0445 } 0446 QString value = parameters.value(key); 0447 body.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() + QUrl::toPercentEncoding(value)); 0448 } 0449 return body; 0450 } 0451 0452 int O2::expires() { 0453 QString key = QString(O2_KEY_EXPIRES).arg(clientId_); 0454 return store_->value(key).toInt(); 0455 } 0456 0457 void O2::setExpires(int v) { 0458 QString key = QString(O2_KEY_EXPIRES).arg(clientId_); 0459 store_->setValue(key, QString::number(v)); 0460 } 0461 0462 void O2::startPollServer(const QVariantMap ¶ms) 0463 { 0464 bool ok = false; 0465 int expiresIn = params[O2_OAUTH2_EXPIRES_IN].toInt(&ok); 0466 if (!ok) { 0467 qWarning() << "O2::startPollServer: No expired_in parameter"; 0468 Q_EMIT linkingFailed(); 0469 return; 0470 } 0471 0472 qDebug() << "O2::startPollServer: device_ and user_code expires in" << expiresIn << "seconds"; 0473 0474 QUrl url(tokenUrl_); 0475 QNetworkRequest authRequest(url); 0476 authRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 0477 0478 const QString deviceCode = params[O2_OAUTH2_DEVICE_CODE].toString(); 0479 const QString grantType = grantType_.isEmpty() ? O2_OAUTH2_GRANT_TYPE_DEVICE : grantType_; 0480 0481 QList<O0RequestParameter> parameters; 0482 parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_ID, clientId_.toUtf8())); 0483 if ( !clientSecret_.isEmpty() ) 0484 parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_SECRET, clientSecret_.toUtf8())); 0485 parameters.append(O0RequestParameter(O2_OAUTH2_CODE, deviceCode.toUtf8())); 0486 parameters.append(O0RequestParameter(O2_OAUTH2_GRANT_TYPE, grantType.toUtf8())); 0487 QByteArray payload = O0BaseAuth::createQueryParameters(parameters); 0488 0489 O2PollServer * pollServer = new O2PollServer(manager_, authRequest, payload, expiresIn, this); 0490 if (params.contains(O2_OAUTH2_INTERVAL)) { 0491 int interval = params[O2_OAUTH2_INTERVAL].toInt(&ok); 0492 if (ok) 0493 pollServer->setInterval(interval); 0494 } 0495 connect(pollServer, SIGNAL(verificationReceived(QMap<QString,QString>)), this, SLOT(onVerificationReceived(QMap<QString,QString>))); 0496 connect(pollServer, SIGNAL(serverClosed(bool)), this, SLOT(serverHasClosed(bool))); 0497 setPollServer(pollServer); 0498 pollServer->startPolling(); 0499 } 0500 0501 QString O2::refreshToken() { 0502 QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_); 0503 return store_->value(key); 0504 } 0505 0506 void O2::setRefreshToken(const QString &v) { 0507 qDebug() << "O2::setRefreshToken" << v.left(4) << "..."; 0508 QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_); 0509 store_->setValue(key, v); 0510 } 0511 0512 void O2::refresh() { 0513 qDebug() << "O2::refresh: Token: ..." << refreshToken().right(7); 0514 0515 if (refreshToken().isEmpty()) { 0516 qWarning() << "O2::refresh: No refresh token"; 0517 onRefreshError(QNetworkReply::AuthenticationRequiredError); 0518 return; 0519 } 0520 if (refreshTokenUrl_.isEmpty()) { 0521 qWarning() << "O2::refresh: Refresh token URL not set"; 0522 onRefreshError(QNetworkReply::AuthenticationRequiredError); 0523 return; 0524 } 0525 0526 QNetworkRequest refreshRequest(refreshTokenUrl_); 0527 refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); 0528 QMap<QString, QString> parameters; 0529 parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_); 0530 parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_); 0531 parameters.insert(O2_OAUTH2_REFRESH_TOKEN, refreshToken()); 0532 parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_OAUTH2_REFRESH_TOKEN); 0533 0534 QByteArray data = buildRequestBody(parameters); 0535 QNetworkReply *refreshReply = manager_->post(refreshRequest, data); 0536 timedReplies_.add(refreshReply); 0537 connect(refreshReply, SIGNAL(finished()), this, SLOT(onRefreshFinished()), Qt::QueuedConnection); 0538 #if QT_VERSION < 0x051500 0539 connect(refreshReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRefreshError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0540 #else 0541 connect(refreshReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onRefreshError(QNetworkReply::NetworkError)), Qt::QueuedConnection); 0542 #endif 0543 } 0544 0545 void O2::onRefreshFinished() { 0546 QNetworkReply *refreshReply = qobject_cast<QNetworkReply *>(sender()); 0547 0548 if (refreshReply->error() == QNetworkReply::NoError) { 0549 QByteArray reply = refreshReply->readAll(); 0550 QVariantMap tokens = parseJsonResponse(reply); 0551 setToken(tokens.value(O2_OAUTH2_ACCESS_TOKEN).toString()); 0552 setExpires((int)(QDateTime::currentMSecsSinceEpoch() / 1000 + tokens.value(O2_OAUTH2_EXPIRES_IN).toInt())); 0553 QString refreshToken = tokens.value(O2_OAUTH2_REFRESH_TOKEN).toString(); 0554 if(!refreshToken.isEmpty()) { 0555 setRefreshToken(refreshToken); 0556 } 0557 else { 0558 qDebug() << "No new refresh token. Keep the old one."; 0559 } 0560 timedReplies_.remove(refreshReply); 0561 setLinked(true); 0562 Q_EMIT linkingSucceeded(); 0563 Q_EMIT refreshFinished(QNetworkReply::NoError); 0564 qDebug() << " New token expires in" << expires() << "seconds"; 0565 } else { 0566 qDebug() << "O2::onRefreshFinished: Error" << (int)refreshReply->error() << refreshReply->errorString(); 0567 } 0568 refreshReply->deleteLater(); 0569 } 0570 0571 void O2::onRefreshError(QNetworkReply::NetworkError error) { 0572 QNetworkReply *refreshReply = qobject_cast<QNetworkReply *>(sender()); 0573 qWarning() << "O2::onRefreshError: " << error; 0574 unlink(); 0575 timedReplies_.remove(refreshReply); 0576 Q_EMIT refreshFinished(error); 0577 } 0578 0579 void O2::onDeviceAuthReplyFinished() 0580 { 0581 qDebug() << "O2::onDeviceAuthReplyFinished"; 0582 QNetworkReply *tokenReply = qobject_cast<QNetworkReply *>(sender()); 0583 if (!tokenReply) 0584 { 0585 qDebug() << "O2::onDeviceAuthReplyFinished: reply is null"; 0586 return; 0587 } 0588 if (tokenReply->error() == QNetworkReply::NoError) { 0589 QByteArray replyData = tokenReply->readAll(); 0590 0591 // Dump replyData 0592 // SENSITIVE DATA in RelWithDebInfo or Debug builds 0593 //qDebug() << "O2::onDeviceAuthReplyFinished: replyData\n"; 0594 //qDebug() << QString( replyData ); 0595 0596 QVariantMap params = parseJsonResponse(replyData); 0597 0598 // Dump tokens 0599 qDebug() << "O2::onDeviceAuthReplyFinished: Tokens returned:\n"; 0600 foreach (QString key, params.keys()) { 0601 // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first 0602 qDebug() << key << ": "<< params.value( key ).toString().left( 3 ) << "..."; 0603 } 0604 0605 // Check for mandatory parameters 0606 if (hasMandatoryDeviceAuthParams(params)) { 0607 qDebug() << "O2::onDeviceAuthReplyFinished: Device auth request response"; 0608 0609 const QString userCode = params.take(O2_OAUTH2_USER_CODE).toString(); 0610 QUrl uri = params.take(O2_OAUTH2_VERIFICATION_URI).toUrl(); 0611 if (uri.isEmpty()) 0612 uri = params.take(O2_OAUTH2_VERIFICATION_URL).toUrl(); 0613 0614 if (params.contains(O2_OAUTH2_VERIFICATION_URI_COMPLETE)) 0615 Q_EMIT openBrowser(params.take(O2_OAUTH2_VERIFICATION_URI_COMPLETE).toUrl()); 0616 0617 Q_EMIT showVerificationUriAndCode(uri, userCode); 0618 0619 startPollServer(params); 0620 } else { 0621 qWarning() << "O2::onDeviceAuthReplyFinished: Mandatory parameters missing from response"; 0622 Q_EMIT linkingFailed(); 0623 } 0624 } 0625 tokenReply->deleteLater(); 0626 } 0627 0628 void O2::serverHasClosed(bool paramsfound) 0629 { 0630 if ( !paramsfound ) { 0631 // server has probably timed out after receiving first response 0632 Q_EMIT linkingFailed(); 0633 } 0634 // poll server is not re-used for later auth requests 0635 setPollServer(NULL); 0636 } 0637 0638 QString O2::localhostPolicy() const { 0639 return localhostPolicy_; 0640 } 0641 0642 void O2::setLocalhostPolicy(const QString &value) { 0643 localhostPolicy_ = value; 0644 } 0645 0646 QString O2::apiKey() { 0647 return apiKey_; 0648 } 0649 0650 void O2::setApiKey(const QString &value) { 0651 apiKey_ = value; 0652 } 0653 0654 bool O2::ignoreSslErrors() { 0655 return timedReplies_.ignoreSslErrors(); 0656 } 0657 0658 void O2::setIgnoreSslErrors(bool ignoreSslErrors) { 0659 timedReplies_.setIgnoreSslErrors(ignoreSslErrors); 0660 } 0661 0662 #include "moc_o2.cpp"