mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Philips Hue APIv2 support (#1637)
* Support Philips Hue APIv2 and refactoring * Fix MDNSBrower - if timeout during host resolvment occurs * Hue API v2 - Migrate database * Fix macOS build * Handle network timeout before any other error * Address CodeQL findings * Clean-up and Fixes * Only getProperties, if username is available * Option to layout by entertainment area center * Fix Wizard --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com>
This commit is contained in:
@@ -2,11 +2,18 @@
|
||||
#include "ProviderRestApi.h"
|
||||
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QByteArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QFile>
|
||||
|
||||
#include <QSslSocket>
|
||||
|
||||
//std includes
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
@@ -30,12 +37,12 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: _log(Logger::getInstance("LEDDEVICE"))
|
||||
, _networkManager(nullptr)
|
||||
, _requestTimeout(DEFAULT_REST_TIMEOUT)
|
||||
,_isSeflSignedCertificateAccpeted(false)
|
||||
{
|
||||
_networkManager = new QNetworkAccessManager();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
_networkManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
#endif
|
||||
|
||||
_apiUrl.setScheme(scheme);
|
||||
_apiUrl.setHost(host);
|
||||
_apiUrl.setPort(port);
|
||||
@@ -46,7 +53,7 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: ProviderRestApi(scheme, host, port, "") {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath)
|
||||
: ProviderRestApi("http", host, port, basePath) {}
|
||||
: ProviderRestApi((port == 443) ? "https" : "http", host, port, basePath) {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port)
|
||||
: ProviderRestApi(host, port, "") {}
|
||||
@@ -59,18 +66,33 @@ ProviderRestApi::~ProviderRestApi()
|
||||
delete _networkManager;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setScheme(const QString& scheme)
|
||||
{
|
||||
_apiUrl.setScheme(scheme);
|
||||
}
|
||||
|
||||
void ProviderRestApi::setUrl(const QUrl& url)
|
||||
{
|
||||
_apiUrl = url;
|
||||
_basePath = url.path();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QStringList& pathElements)
|
||||
{
|
||||
setBasePath(pathElements.join(ONE_SLASH));
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QString& basePath)
|
||||
{
|
||||
_basePath.clear();
|
||||
appendPath(_basePath, basePath);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearBasePath()
|
||||
{
|
||||
_basePath.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setPath(const QStringList& pathElements)
|
||||
{
|
||||
_path.clear();
|
||||
@@ -83,6 +105,11 @@ void ProviderRestApi::setPath(const QString& path)
|
||||
appendPath(_path, path);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearPath()
|
||||
{
|
||||
_path.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::appendPath(const QString& path)
|
||||
{
|
||||
appendPath(_path, path);
|
||||
@@ -204,6 +231,7 @@ httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation
|
||||
QDateTime start = QDateTime::currentDateTime();
|
||||
QString opCode;
|
||||
QNetworkReply* reply;
|
||||
|
||||
switch (operation) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
opCode = "GET";
|
||||
@@ -255,11 +283,11 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
HttpStatusCode httpStatusCode = static_cast<HttpStatusCode>(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
||||
response.setHttpStatusCode(httpStatusCode);
|
||||
response.setNetworkReplyError(reply->error());
|
||||
response.setHeaders(reply->rawHeaderPairs());
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
QByteArray replyData = reply->readAll();
|
||||
|
||||
if (!replyData.isEmpty())
|
||||
{
|
||||
QJsonParseError error;
|
||||
@@ -284,40 +312,41 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
else
|
||||
{
|
||||
QString errorReason;
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
switch ( httpStatusCode ) {
|
||||
case HttpStatusCode::BadRequest:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case HttpStatusCode::UnAuthorized:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
break;
|
||||
case HttpStatusCode::Forbidden:
|
||||
advise = "No permission to access the given resource";
|
||||
break;
|
||||
case HttpStatusCode::NotFound:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
advise = httpReason;
|
||||
break;
|
||||
}
|
||||
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
|
||||
if (reply->error() == QNetworkReply::OperationCanceledError)
|
||||
{
|
||||
errorReason = "Network request timeout error";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reply->error() == QNetworkReply::OperationCanceledError)
|
||||
{
|
||||
errorReason = "Network request timeout error";
|
||||
qDebug() << "httpStatusCode: "<< httpStatusCode;
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
switch ( httpStatusCode ) {
|
||||
case HttpStatusCode::BadRequest:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case HttpStatusCode::UnAuthorized:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
break;
|
||||
case HttpStatusCode::Forbidden:
|
||||
advise = "No permission to access the given resource";
|
||||
break;
|
||||
case HttpStatusCode::NotFound:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
advise = httpReason;
|
||||
break;
|
||||
}
|
||||
errorReason = QString ("[%3 %4] - %5").arg(httpStatusCode).arg(httpReason, advise);
|
||||
}
|
||||
else
|
||||
{
|
||||
errorReason = reply->errorString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
response.setError(true);
|
||||
response.setErrorReason(errorReason);
|
||||
}
|
||||
@@ -344,3 +373,121 @@ void ProviderRestApi::setHeader(const QByteArray &headerName, const QByteArray &
|
||||
{
|
||||
_networkRequestHeaders.setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
void httpResponse::setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs)
|
||||
{
|
||||
_responseHeaders.clear();
|
||||
for (const auto &item: pairs)
|
||||
{
|
||||
_responseHeaders[item.first] = item.second;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray httpResponse::getHeader(const QByteArray header) const
|
||||
{
|
||||
return _responseHeaders.value(header);
|
||||
}
|
||||
|
||||
bool ProviderRestApi::setCaCertificate(const QString& caFileName)
|
||||
{
|
||||
bool rc {false};
|
||||
/// Add our own CA to the default SSL configuration
|
||||
QSslConfiguration configuration = QSslConfiguration::defaultConfiguration();
|
||||
|
||||
QFile caFile (caFileName);
|
||||
if (!caFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error(_log,"Unable to open CA-Certificate file: %s", QSTRING_CSTR(caFileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
QSslCertificate cert (&caFile);
|
||||
caFile.close();
|
||||
|
||||
QList<QSslCertificate> allowedCAs;
|
||||
allowedCAs << cert;
|
||||
configuration.setCaCertificates(allowedCAs);
|
||||
|
||||
QSslConfiguration::setDefaultConfiguration(configuration);
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
if (QSslSocket::supportsSsl())
|
||||
{
|
||||
QObject::connect( _networkManager, &QNetworkAccessManager::sslErrors, this, &ProviderRestApi::onSslErrors, Qt::UniqueConnection );
|
||||
_networkManager->connectToHostEncrypted(_apiUrl.host(), _apiUrl.port(), configuration);
|
||||
rc = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ProviderRestApi::acceptSelfSignedCertificates(bool isAccepted)
|
||||
{
|
||||
_isSeflSignedCertificateAccpeted = isAccepted;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setAlternateServerIdentity(const QString& serverIdentity)
|
||||
{
|
||||
_serverIdentity = serverIdentity;
|
||||
}
|
||||
|
||||
QString ProviderRestApi::getAlternateServerIdentity() const
|
||||
{
|
||||
return _serverIdentity;
|
||||
}
|
||||
|
||||
bool ProviderRestApi::checkServerIdentity(const QSslConfiguration& sslConfig) const
|
||||
{
|
||||
bool isServerIdentified {false};
|
||||
|
||||
// Perform common name validation
|
||||
QSslCertificate serverCertificate = sslConfig.peerCertificate();
|
||||
QStringList commonName = serverCertificate.subjectInfo(QSslCertificate::CommonName);
|
||||
if ( commonName.contains(getAlternateServerIdentity(), Qt::CaseInsensitive) )
|
||||
{
|
||||
isServerIdentified = true;
|
||||
}
|
||||
|
||||
return isServerIdentified;
|
||||
}
|
||||
|
||||
void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
|
||||
{
|
||||
int ignoredErrorCount {0};
|
||||
for (const QSslError &error : errors)
|
||||
{
|
||||
bool ignoreSslError{false};
|
||||
|
||||
switch (error.error()) {
|
||||
case QSslError::HostNameMismatch :
|
||||
if (checkServerIdentity(reply->sslConfiguration()) )
|
||||
{
|
||||
ignoreSslError = true;
|
||||
}
|
||||
break;
|
||||
case QSslError::SelfSignedCertificate :
|
||||
if (_isSeflSignedCertificateAccpeted)
|
||||
{
|
||||
ignoreSslError = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignoreSslError)
|
||||
{
|
||||
++ignoredErrorCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug (_log,"SSL Error occured: [%d] %s ",error.error(), QSTRING_CSTR(error.errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoredErrorCount == errors.size())
|
||||
{
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user