diff --git a/CMakeLists.txt b/CMakeLists.txt index 05aa0ea27..768727640 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,250 +1,252 @@ # # Copyright (c) 2010-2018 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # project(kipi-plugins) message(STATUS "----------------------------------------------------------------------------------") message(STATUS "Starting CMake configuration for: ${PROJECT_NAME}") # ======================================================= # Information to update before to release this package. # kipi-plugins version set(KIPIPLUGINS_MAJOR_VERSION "5") set(KIPIPLUGINS_MINOR_VERSION "9") set(KIPIPLUGINS_PATCH_VERSION "1") # Suffix to add at end of version string. Usual values are: # "-git" : alpha code unstable from git. Do not use in production # "-beta1" : beta1 release. # "-beta2" : beta2 release. # "-beta3" : beta3 release. # "-rc" : release candidate. # "" : final relase. Can be used in production. set(KIPIPLUGINS_SUFFIX_VERSION "") # ======================================================= # Set env. variables accordinly. set(KIPIPLUGINS_VERSION_STRING "${KIPIPLUGINS_MAJOR_VERSION}.${KIPIPLUGINS_MINOR_VERSION}.${KIPIPLUGINS_PATCH_VERSION}${KIPIPLUGINS_SUFFIX_VERSION}" ) # NOTE: This string is used to set libkipiplugins SO version ID set(KIPIPLUGINS_LIB_SO_VERSION_STRING "${KIPIPLUGINS_MAJOR_VERSION}.${KIPIPLUGINS_MINOR_VERSION}.${KIPIPLUGINS_PATCH_VERSION}" ) # ======================================================= set(CMAKE_MIN_VERSION "3.0.0") set(KF5_MIN_VERSION "5.18.0") set(QT_MIN_VERSION "5.6.0") set(KIPI_MIN_VERSION "5.0.0") set(MEDIAWIKI_MIN_VERSION "5.0.0") set(VKONTAKTE_MIN_VERSION "4.70.0") cmake_minimum_required(VERSION ${CMAKE_MIN_VERSION}) ############## ECM setup ###################### find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) # Cmake macros include(GenerateExportHeader) include(CheckFunctionExists) include(FeatureSummary) # ECM macros include(ECMOptionalAddSubdirectory) include(ECMAddTests) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(ECMSetupVersion) include(ECMInstallIcons) include(ECMAddAppIcon) # KDE macros include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings) include(KDECompilerSettings) ########################################################################## option(ENABLE_LEGACY "Build Kipi-plugins with legacy plugins support (default=ON)" ON) ############## Find Packages ################### find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Widgets PrintSupport Gui Xml XmlPatterns Svg Concurrent Network ) if(BUILD_TESTING) find_package(Qt5Test ${QT_MIN_VERSION} REQUIRED NO_MODULE) endif() find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Config WindowSystem XmlGui I18n ) find_package(KF5Archive ${KF5_MIN_VERSION}) set_package_properties(KF5Archive PROPERTIES PURPOSE "Required to build the FlashExport plugin") find_package(KF5KIO ${KF5_MIN_VERSION}) set_package_properties(KF5IO PROPERTIES PURPOSE "Required to build the RemoteStorage plugin") if(KF5KIO_FOUND) # Some tools rely only on KIO core, others need KIOWidgets too.. get_target_property(KIOWidgets_INCLUDE_DIRS KF5::KIOWidgets INTERFACE_INCLUDE_DIRECTORIES) message(STATUS "KF5::KIOWidgets include dirs: ${KIOWidgets_INCLUDE_DIRS}") if(NOT KIOWidgets_INCLUDE_DIRS) message(STATUS "KF5::KIOWidgets not available in shared KIO library. Some tools will not be compiled.") set(KF5KIOWidgets_FOUND FALSE) else() set(KF5KIOWidgets_FOUND TRUE) endif() endif() # Dependencies detection required by all plugins find_package(KF5Kipi ${KIPI_MIN_VERSION} REQUIRED) get_target_property(KF5Kipi_INCLUDE_DIRS KF5::Kipi INTERFACE_INCLUDE_DIRECTORIES) # Detect libkipi so version used to compile kipi tool to identify if plugin can be loaded in memory by libkipi. # This will be used to populate plugin desktop files. foreach(var ${KF5Kipi_INCLUDE_DIRS}) if(EXISTS "${var}/libkipi_config.h") set(KF5KipiConfig_FOUND "${var}/libkipi_config.h") message(STATUS "Libkipi config header: ${KF5KipiConfig_FOUND}") break() endif() endforeach() if(KF5KipiConfig_FOUND) file(READ ${KF5KipiConfig_FOUND} KIPI_CONFIG_H_CONTENT) string(REGEX REPLACE ".*static +const +int +kipi_binary_version += ([^ ;]+).*" "\\1" KIPI_LIB_SO_CUR_VERSION_FOUND "${KIPI_CONFIG_H_CONTENT}" ) set(KIPI_LIB_SO_CUR_VERSION ${KIPI_LIB_SO_CUR_VERSION_FOUND} CACHE STRING "libkipi so version") else() message(FATAL_ERROR "Could not find libkipi SO version") set(KF5Kipi_FOUND FALSE) endif() message(STATUS "libkipi includes : ${KF5Kipi_INCLUDE_DIRS}") message(STATUS "libkipi SO version : ${KIPI_LIB_SO_CUR_VERSION}") # -- Optional dependencies detection required by some plugins ------------------------------------- find_package(KF5MediaWiki ${MEDIAWIKI_MIN_VERSION}) find_package(KF5Vkontakte ${VKONTAKTE_MIN_VERSION}) # ================================================================================================== # Create git version header # We only do this IF we are in a .git dir find_file(GIT_MARKER entries PATHS ${CMAKE_SOURCE_DIR}/.git) if(NOT GIT_MARKER) set (GIT_MARKER ${CMAKE_SOURCE_DIR}/CMakeLists.txt) # Dummy file endif() # Add a custom command to drive the git script whenever the git entries # file changes. configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/gitscript.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/gitscript.cmake" @ONLY) # Add a custom target to drive the custom command. add_custom_target(kipiplugins-gitversion ALL COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/gitscript.cmake") # ================================================================================================== include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/libkipiplugins ${CMAKE_CURRENT_BINARY_DIR}/common/libkipiplugins ${CMAKE_CURRENT_SOURCE_DIR}/common/libkipiplugins/dialogs ${CMAKE_CURRENT_SOURCE_DIR}/common/libkipiplugins/widgets ${CMAKE_CURRENT_SOURCE_DIR}/common/libkipiplugins/tools ${CMAKE_CURRENT_SOURCE_DIR}/common/libkipiplugins/o2/src ${KF5Kipi_INCLUDE_DIRS} ) # To prevent warnings from M$ compiler if(WIN32 AND MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_ATL_SECURE_NO_WARNINGS) add_definitions(-D_AFX_SECURE_NO_WARNINGS) endif() +add_definitions(-DQT_NO_URL_CAST_FROM_STRING) + # Remove pedantic GCC flag which generate a lots of warnings on the console with qCDebug() while(CMAKE_CXX_FLAGS MATCHES "-pedantic") string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endwhile() # Remove Wdate-time GCC flag which generate a lots of compile warnings while(CMAKE_CXX_FLAGS MATCHES "-Wdate-time") string(REPLACE "-Wdate-time" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endwhile() add_subdirectory(common) add_subdirectory(flickr) add_subdirectory(dropbox) add_subdirectory(facebook) add_subdirectory(imgur) add_subdirectory(piwigo) add_subdirectory(rajce) add_subdirectory(smug) add_subdirectory(imageshack) add_subdirectory(yandexfotki) add_subdirectory(googleservices) if(ENABLE_LEGACY) add_subdirectory(printimages) add_subdirectory(kmlexport) add_subdirectory(sendimages) add_subdirectory(jalbum) if(KF5Archive_FOUND) add_subdirectory(flashexport) endif() endif() if(KF5Vkontakte_FOUND) add_subdirectory(vkontakte) endif() if(KF5MediaWiki_FOUND) add_subdirectory(mediawiki) endif() if(KF5KIO_FOUND AND KF5KIOWidgets_FOUND) add_subdirectory(remotestorage) # kioimportwindow.cpp, kioexportwindow.cpp endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/common/libkipiplugins/o2/src/o2.cpp b/common/libkipiplugins/o2/src/o2.cpp index c660e4ee6..d1da496a7 100644 --- a/common/libkipiplugins/o2/src/o2.cpp +++ b/common/libkipiplugins/o2/src/o2.cpp @@ -1,499 +1,499 @@ #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #include #include #else #include #include #endif #include "o2.h" #include "o2replyserver.h" #include "o0globals.h" #include "o0settingsstore.h" /// Parse JSON data into a QVariantMap static QVariantMap parseTokenResponse(const QByteArray &data) { #if QT_VERSION >= 0x050000 QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { qWarning() << "parseTokenResponse: Failed to parse token response due to err:" << err.errorString(); return QVariantMap(); } if (!doc.isObject()) { qWarning() << "parseTokenResponse: Token response is not an object"; return QVariantMap(); } return doc.object().toVariantMap(); #else QScriptEngine engine; QScriptValue value = engine.evaluate("(" + QString(data) + ")"); QScriptValueIterator it(value); QVariantMap map; while (it.hasNext()) { it.next(); map.insert(it.name(), it.value().toVariant()); } return map; #endif } /// Add query parameters to a query static void addQueryParametersToUrl(QUrl &url, QList > parameters) { #if QT_VERSION < 0x050000 url.setQueryItems(parameters); #else QUrlQuery query(url); query.setQueryItems(parameters); url.setQuery(query); #endif } O2::O2(QObject *parent, QNetworkAccessManager *manager, O0AbstractStore *store): O0BaseAuth(parent, store) { manager_ = manager ? manager : new QNetworkAccessManager(this); replyServer_ = new O2ReplyServer(this); grantFlow_ = GrantFlowAuthorizationCode; localhostPolicy_ = QString(O2_CALLBACK_URL); qRegisterMetaType("QNetworkReply::NetworkError"); connect(replyServer_, SIGNAL(verificationReceived(QMap)), this, SLOT(onVerificationReceived(QMap))); connect(replyServer_, SIGNAL(serverClosed(bool)), this, SLOT(serverHasClosed(bool))); } O2::GrantFlow O2::grantFlow() { return grantFlow_; } void O2::setGrantFlow(O2::GrantFlow value) { grantFlow_ = value; Q_EMIT grantFlowChanged(); } QString O2::username() { return username_; } void O2::setUsername(const QString &value) { username_ = value; Q_EMIT usernameChanged(); } QString O2::password() { return password_; } void O2::setPassword(const QString &value) { password_ = value; Q_EMIT passwordChanged(); } QString O2::scope() { return scope_; } void O2::setScope(const QString &value) { scope_ = value; Q_EMIT scopeChanged(); } QString O2::requestUrl() { return requestUrl_.toString(); } void O2::setRequestUrl(const QString &value) { - requestUrl_ = value; + requestUrl_ = QUrl(value); Q_EMIT requestUrlChanged(); } QVariantMap O2::extraRequestParams() { return extraReqParams_; } void O2::setExtraRequestParams(const QVariantMap &value) { extraReqParams_ = value; Q_EMIT extraRequestParamsChanged(); } QString O2::tokenUrl() { return tokenUrl_.toString(); } void O2::setTokenUrl(const QString &value) { - tokenUrl_= value; + tokenUrl_= QUrl(value); Q_EMIT tokenUrlChanged(); } QString O2::refreshTokenUrl() { return refreshTokenUrl_.toString(); } void O2::setRefreshTokenUrl(const QString &value) { - refreshTokenUrl_ = value; + refreshTokenUrl_ = QUrl(value); Q_EMIT refreshTokenUrlChanged(); } void O2::link() { qDebug() << "O2::link"; if (linked()) { qDebug() << "O2::link: Linked already"; Q_EMIT linkingSucceeded(); return; } setLinked(false); setToken(""); setTokenSecret(""); setExtraTokens(QVariantMap()); setRefreshToken(QString()); setExpires(0); if (grantFlow_ == GrantFlowAuthorizationCode || grantFlow_ == GrantFlowImplicit) { // Start listening to authentication replies if (!replyServer_->isListening()) { if (replyServer_->listen(QHostAddress::Any, localPort_)) { qDebug() << "O2::link: Reply server listening on port" << localPort(); } else { qWarning() << "O2::link: Reply server failed to start listening on port" << localPort(); Q_EMIT linkingFailed(); return; } } // Save redirect URI, as we have to reuse it when requesting the access token redirectUri_ = localhostPolicy_.arg(replyServer_->serverPort()); // Assemble initial authentication URL QList > parameters; parameters.append(qMakePair(QString(O2_OAUTH2_RESPONSE_TYPE), (grantFlow_ == GrantFlowAuthorizationCode)? QString(O2_OAUTH2_GRANT_TYPE_CODE): QString(O2_OAUTH2_GRANT_TYPE_TOKEN))); parameters.append(qMakePair(QString(O2_OAUTH2_CLIENT_ID), clientId_)); if ( !redirectUri_.isEmpty() ) parameters.append(qMakePair(QString(O2_OAUTH2_REDIRECT_URI), redirectUri_)); if ( !scope_.isEmpty() ) parameters.append(qMakePair(QString(O2_OAUTH2_SCOPE), scope_.replace( " ", "+" ))); if ( !apiKey_.isEmpty() ) parameters.append(qMakePair(QString(O2_OAUTH2_API_KEY), apiKey_)); foreach (QString key, extraRequestParams().keys()) { parameters.append(qMakePair(key, extraRequestParams().value(key).toString())); } // Show authentication URL with a web browser QUrl url(requestUrl_); addQueryParametersToUrl(url, parameters); qDebug() << "O2::link: Emit openBrowser" << url.toString(); Q_EMIT openBrowser(url); } else if (grantFlow_ == GrantFlowResourceOwnerPasswordCredentials) { QList parameters; parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_ID, clientId_.toUtf8())); if ( !clientSecret_.isEmpty() ) parameters.append(O0RequestParameter(O2_OAUTH2_CLIENT_SECRET, clientSecret_.toUtf8())); parameters.append(O0RequestParameter(O2_OAUTH2_USERNAME, username_.toUtf8())); parameters.append(O0RequestParameter(O2_OAUTH2_PASSWORD, password_.toUtf8())); parameters.append(O0RequestParameter(O2_OAUTH2_GRANT_TYPE, O2_OAUTH2_GRANT_TYPE_PASSWORD)); parameters.append(O0RequestParameter(O2_OAUTH2_SCOPE, scope_.toUtf8())); if ( !apiKey_.isEmpty() ) parameters.append(O0RequestParameter(O2_OAUTH2_API_KEY, apiKey_.toUtf8())); foreach (QString key, extraRequestParams().keys()) { parameters.append(O0RequestParameter(key.toUtf8(), extraRequestParams().value(key).toByteArray())); } QByteArray payload = O0BaseAuth::createQueryParameters(parameters); qDebug() << "O2::link: Sending token request for resource owner flow"; QUrl url(tokenUrl_); QNetworkRequest tokenRequest(url); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QNetworkReply *tokenReply = manager_->post(tokenRequest, payload); connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection); connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); } } void O2::unlink() { qDebug() << "O2::unlink"; setLinked(false); setToken(QString()); setRefreshToken(QString()); setExpires(0); setExtraTokens(QVariantMap()); Q_EMIT linkingSucceeded(); } void O2::onVerificationReceived(const QMap response) { qDebug() << "O2::onVerificationReceived:" << response; qDebug() << "O2::onVerificationReceived: Emitting closeBrowser()"; Q_EMIT closeBrowser(); if (response.contains("error")) { qWarning() << "O2::onVerificationReceived: Verification failed:" << response; Q_EMIT linkingFailed(); return; } if (grantFlow_ == GrantFlowAuthorizationCode) { // Save access code setCode(response.value(QString(O2_OAUTH2_GRANT_TYPE_CODE))); // Exchange access code for access/refresh tokens QString query; if(!apiKey_.isEmpty()) query = QString("?" + QString(O2_OAUTH2_API_KEY) + "=" + apiKey_); QNetworkRequest tokenRequest(QUrl(tokenUrl_.toString() + query)); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); tokenRequest.setRawHeader("Accept", O2_MIME_TYPE_JSON); QMap parameters; parameters.insert(O2_OAUTH2_GRANT_TYPE_CODE, code()); parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_); parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_); parameters.insert(O2_OAUTH2_REDIRECT_URI, redirectUri_); parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_AUTHORIZATION_CODE); QByteArray data = buildRequestBody(parameters); qDebug() << QString("O2::onVerificationReceived: Exchange access code data:\n%1").arg(QString(data)); QNetworkReply *tokenReply = manager_->post(tokenRequest, data); timedReplies_.add(tokenReply); connect(tokenReply, SIGNAL(finished()), this, SLOT(onTokenReplyFinished()), Qt::QueuedConnection); connect(tokenReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onTokenReplyError(QNetworkReply::NetworkError)), Qt::QueuedConnection); } else if (grantFlow_ == GrantFlowImplicit) { // Check for mandatory tokens if (response.contains(O2_OAUTH2_ACCESS_TOKEN)) { qDebug() << "O2::onVerificationReceived: Access token returned for implicit flow"; setToken(response.value(O2_OAUTH2_ACCESS_TOKEN)); if (response.contains(O2_OAUTH2_EXPIRES_IN)) { bool ok = false; int expiresIn = response.value(O2_OAUTH2_EXPIRES_IN).toInt(&ok); if (ok) { qDebug() << "O2::onVerificationReceived: Token expires in" << expiresIn << "seconds"; setExpires(QDateTime::currentMSecsSinceEpoch() / 1000 + expiresIn); } } setLinked(true); Q_EMIT linkingSucceeded(); } else { qWarning() << "O2::onVerificationReceived: Access token missing from response for implicit flow"; Q_EMIT linkingFailed(); } } else { setToken(response.value(O2_OAUTH2_ACCESS_TOKEN)); setRefreshToken(response.value(O2_OAUTH2_REFRESH_TOKEN)); } } QString O2::code() { QString key = QString(O2_KEY_CODE).arg(clientId_); return store_->value(key); } void O2::setCode(const QString &c) { QString key = QString(O2_KEY_CODE).arg(clientId_); store_->setValue(key, c); } void O2::onTokenReplyFinished() { qDebug() << "O2::onTokenReplyFinished"; QNetworkReply *tokenReply = qobject_cast(sender()); if (!tokenReply) { qDebug() << "O2::onTokenReplyFinished: reply is null"; return; } if (tokenReply->error() == QNetworkReply::NoError) { QByteArray replyData = tokenReply->readAll(); // Dump replyData // SENSITIVE DATA in RelWithDebInfo or Debug builds //qDebug() << "O2::onTokenReplyFinished: replyData\n"; //qDebug() << QString( replyData ); QVariantMap tokens = parseTokenResponse(replyData); // Dump tokens qDebug() << "O2::onTokenReplyFinished: Tokens returned:\n"; foreach (QString key, tokens.keys()) { // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first qDebug() << key << ": "<< tokens.value( key ).toString().left( 3 ) << "..."; } // Check for mandatory tokens if (tokens.contains(O2_OAUTH2_ACCESS_TOKEN)) { qDebug() << "O2::onTokenReplyFinished: Access token returned"; setToken(tokens.take(O2_OAUTH2_ACCESS_TOKEN).toString()); bool ok = false; int expiresIn = tokens.take(O2_OAUTH2_EXPIRES_IN).toInt(&ok); if (ok) { qDebug() << "O2::onTokenReplyFinished: Token expires in" << expiresIn << "seconds"; setExpires(QDateTime::currentMSecsSinceEpoch() / 1000 + expiresIn); } setRefreshToken(tokens.take(O2_OAUTH2_REFRESH_TOKEN).toString()); setExtraTokens(tokens); timedReplies_.remove(tokenReply); setLinked(true); Q_EMIT linkingSucceeded(); } else { qWarning() << "O2::onTokenReplyFinished: Access token missing from response"; Q_EMIT linkingFailed(); } } tokenReply->deleteLater(); } void O2::onTokenReplyError(QNetworkReply::NetworkError error) { QNetworkReply *tokenReply = qobject_cast(sender()); qWarning() << "O2::onTokenReplyError: " << error << ": " << tokenReply->errorString(); qDebug() << "O2::onTokenReplyError: " << tokenReply->readAll(); setToken(QString()); setRefreshToken(QString()); timedReplies_.remove(tokenReply); Q_EMIT linkingFailed(); } QByteArray O2::buildRequestBody(const QMap ¶meters) { QByteArray body; bool first = true; foreach (QString key, parameters.keys()) { if (first) { first = false; } else { body.append("&"); } QString value = parameters.value(key); body.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() + QUrl::toPercentEncoding(value)); } return body; } int O2::expires() { QString key = QString(O2_KEY_EXPIRES).arg(clientId_); return store_->value(key).toInt(); } void O2::setExpires(int v) { QString key = QString(O2_KEY_EXPIRES).arg(clientId_); store_->setValue(key, QString::number(v)); } QString O2::refreshToken() { QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_); return store_->value(key); } void O2::setRefreshToken(const QString &v) { qDebug() << "O2::setRefreshToken" << v.left(4) << "..."; QString key = QString(O2_KEY_REFRESH_TOKEN).arg(clientId_); store_->setValue(key, v); } void O2::refresh() { qDebug() << "O2::refresh: Token: ..." << refreshToken().right(7); if (refreshToken().isEmpty()) { qWarning() << "O2::refresh: No refresh token"; onRefreshError(QNetworkReply::AuthenticationRequiredError); return; } if (refreshTokenUrl_.isEmpty()) { qWarning() << "O2::refresh: Refresh token URL not set"; onRefreshError(QNetworkReply::AuthenticationRequiredError); return; } QNetworkRequest refreshRequest(refreshTokenUrl_); refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, O2_MIME_TYPE_XFORM); QMap parameters; parameters.insert(O2_OAUTH2_CLIENT_ID, clientId_); parameters.insert(O2_OAUTH2_CLIENT_SECRET, clientSecret_); parameters.insert(O2_OAUTH2_REFRESH_TOKEN, refreshToken()); parameters.insert(O2_OAUTH2_GRANT_TYPE, O2_OAUTH2_REFRESH_TOKEN); QByteArray data = buildRequestBody(parameters); QNetworkReply *refreshReply = manager_->post(refreshRequest, data); timedReplies_.add(refreshReply); connect(refreshReply, SIGNAL(finished()), this, SLOT(onRefreshFinished()), Qt::QueuedConnection); connect(refreshReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRefreshError(QNetworkReply::NetworkError)), Qt::QueuedConnection); } void O2::onRefreshFinished() { QNetworkReply *refreshReply = qobject_cast(sender()); qDebug() << "O2::onRefreshFinished: Error" << (int)refreshReply->error() << refreshReply->errorString(); if (refreshReply->error() == QNetworkReply::NoError) { QByteArray reply = refreshReply->readAll(); QVariantMap tokens = parseTokenResponse(reply); setToken(tokens.value(O2_OAUTH2_ACCESS_TOKEN).toString()); setExpires(QDateTime::currentMSecsSinceEpoch() / 1000 + tokens.value(O2_OAUTH2_EXPIRES_IN).toInt()); setRefreshToken(tokens.value(O2_OAUTH2_REFRESH_TOKEN).toString()); timedReplies_.remove(refreshReply); setLinked(true); Q_EMIT linkingSucceeded(); Q_EMIT refreshFinished(QNetworkReply::NoError); qDebug() << " New token expires in" << expires() << "seconds"; } refreshReply->deleteLater(); } void O2::onRefreshError(QNetworkReply::NetworkError error) { QNetworkReply *refreshReply = qobject_cast(sender()); qWarning() << "O2::onRefreshError: " << error; unlink(); timedReplies_.remove(refreshReply); Q_EMIT refreshFinished(error); } void O2::serverHasClosed(bool paramsfound) { if ( !paramsfound ) { // server has probably timed out after receiving first response Q_EMIT linkingFailed(); } } QString O2::localhostPolicy() const { return localhostPolicy_; } void O2::setLocalhostPolicy(const QString &value) { localhostPolicy_ = value; } QString O2::apiKey() { return apiKey_; } void O2::setApiKey(const QString &value) { apiKey_ = value; } QByteArray O2::replyContent() { return replyServer_->replyContent(); } void O2::setReplyContent(const QByteArray &value) { replyServer_->setReplyContent(value); } bool O2::ignoreSslErrors() { return timedReplies_.ignoreSslErrors(); } void O2::setIgnoreSslErrors(bool ignoreSslErrors) { timedReplies_.setIgnoreSslErrors(ignoreSslErrors); }