File indexing completed on 2024-04-21 04:44:13

0001 /*
0002    SPDX-FileCopyrightText: 2015 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003 
0004    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 #include "upnpeventsubscriber.h"
0008 
0009 #include "upnplogging.h"
0010 
0011 #include "upnpabstractservice.h"
0012 #include "upnpbasictypes.h"
0013 
0014 #include "upnpstatevariabledescription.h"
0015 
0016 #include <QBuffer>
0017 #include <QPointer>
0018 #include <QUuid>
0019 #include <QXmlStreamWriter>
0020 
0021 #include <QNetworkReply>
0022 #include <QNetworkRequest>
0023 
0024 #include <QLoggingCategory>
0025 
0026 class UpnpEventSubscriberPrivate
0027 {
0028 public:
0029     int mSecondTimeout = 1800;
0030 
0031     QUrl mCallback;
0032 
0033     QString mUuid;
0034 
0035     quint32 mSequenceCounter = 0;
0036 
0037     QNetworkAccessManager mNetworkAccess;
0038 
0039     UpnpAbstractService *mUpnpService = nullptr;
0040 
0041     QPointer<QBuffer> mSentBuffer;
0042 };
0043 
0044 UpnpEventSubscriber::UpnpEventSubscriber(QObject *parent)
0045     : QObject(parent)
0046     , d(std::make_unique<UpnpEventSubscriberPrivate>())
0047 {
0048     const QString &uuidString(QUuid::createUuid().toString());
0049     d->mUuid = uuidString.mid(1, uuidString.length() - 2);
0050 }
0051 
0052 UpnpEventSubscriber::~UpnpEventSubscriber() = default;
0053 
0054 void UpnpEventSubscriber::setSecondTimeout(int newValue)
0055 {
0056     d->mSecondTimeout = newValue;
0057 }
0058 
0059 int UpnpEventSubscriber::secondTimeout() const
0060 {
0061     return d->mSecondTimeout;
0062 }
0063 
0064 void UpnpEventSubscriber::setCallback(const QUrl &callbackAddress)
0065 {
0066     d->mCallback = callbackAddress;
0067 }
0068 
0069 const QUrl &UpnpEventSubscriber::callback() const
0070 {
0071     return d->mCallback;
0072 }
0073 
0074 const QString &UpnpEventSubscriber::uuid() const
0075 {
0076     return d->mUuid;
0077 }
0078 
0079 void UpnpEventSubscriber::setUpnpService(UpnpAbstractService *service)
0080 {
0081     d->mUpnpService = service;
0082 }
0083 
0084 void UpnpEventSubscriber::sendEventNotification()
0085 {
0086     QNetworkRequest newRequest(d->mCallback);
0087     newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("text/xml"));
0088     newRequest.setRawHeader("NT", "upnp:event");
0089     newRequest.setRawHeader("NTS", "upnp:propchange");
0090     QString sidHeader = QStringLiteral("uuid:") + d->mUuid;
0091     newRequest.setRawHeader("SID", sidHeader.toLatin1());
0092     newRequest.setRawHeader("SEQ", QByteArray::number(d->mSequenceCounter));
0093 
0094     QPointer<QBuffer> requestBody(new QBuffer);
0095     requestBody->open(QIODevice::ReadWrite);
0096 
0097     QXmlStreamWriter insertStream(requestBody.data());
0098     insertStream.setAutoFormatting(true);
0099 
0100     insertStream.writeStartDocument(QStringLiteral("1.0"));
0101     insertStream.writeNamespace(QStringLiteral("urn:schemas-upnp-org:event-1-0"), QStringLiteral("e"));
0102     insertStream.writeStartElement(QStringLiteral("urn:schemas-upnp-org:event-1-0"), QStringLiteral("propertyset"));
0103     const auto &allStateVariables(d->mUpnpService->stateVariables());
0104     for (const QString &itVariable : allStateVariables) {
0105         const UpnpStateVariableDescription &currentStateVariable(d->mUpnpService->stateVariable(itVariable));
0106         if (currentStateVariable.mEvented) {
0107             insertStream.writeStartElement(QStringLiteral("urn:schemas-upnp-org:event-1-0"), QStringLiteral("property"));
0108 
0109             const QVariant &propertyValue(d->mUpnpService->property(currentStateVariable.mPropertyName.constData()));
0110             if (propertyValue.canConvert<bool>()) {
0111                 insertStream.writeTextElement(itVariable, propertyValue.toBool() ? QStringLiteral("1") : QStringLiteral("0"));
0112             } else {
0113                 insertStream.writeTextElement(itVariable, propertyValue.toString());
0114             }
0115 
0116             insertStream.writeEndElement();
0117         }
0118     }
0119     insertStream.writeEndElement();
0120     insertStream.writeEndDocument();
0121     requestBody->seek(0);
0122 
0123     QNetworkReply *replyHandler = d->mNetworkAccess.sendCustomRequest(newRequest, "NOTIFY", requestBody.data());
0124     connect(replyHandler, &QNetworkReply::finished, this, &UpnpEventSubscriber::eventingFinished);
0125     connect(replyHandler, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::errorOccurred), this, &UpnpEventSubscriber::eventingInErrorFinished);
0126 
0127     d->mSentBuffer = requestBody;
0128 }
0129 
0130 void UpnpEventSubscriber::notifyPropertyChange(const QString &serviceId, const QByteArray &propertyName)
0131 {
0132     Q_UNUSED(serviceId)
0133 
0134     QNetworkRequest newRequest(d->mCallback);
0135     newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("text/xml"));
0136     newRequest.setRawHeader("NT", "upnp:event");
0137     newRequest.setRawHeader("NTS", "upnp:propchange");
0138     QString sidHeader = QStringLiteral("uuid:") + d->mUuid;
0139     newRequest.setRawHeader("SID", sidHeader.toLatin1());
0140     newRequest.setRawHeader("SEQ", QByteArray::number(d->mSequenceCounter));
0141 
0142     QPointer<QBuffer> requestBody(new QBuffer);
0143     requestBody->open(QIODevice::ReadWrite);
0144 
0145     QXmlStreamWriter insertStream(requestBody.data());
0146     insertStream.setAutoFormatting(true);
0147 
0148     insertStream.writeStartDocument(QStringLiteral("1.0"));
0149     insertStream.writeNamespace(QStringLiteral("urn:schemas-upnp-org:event-1-0"), QStringLiteral("e"));
0150     insertStream.writeStartElement(QStringLiteral("urn:schemas-upnp-org:event-1-0"), QStringLiteral("propertyset"));
0151     const auto &allStateVariables(d->mUpnpService->stateVariables());
0152     for (const QString &itVariable : allStateVariables) {
0153         const UpnpStateVariableDescription &currentStateVariable(d->mUpnpService->stateVariable(itVariable));
0154         if (currentStateVariable.mEvented && currentStateVariable.mPropertyName == propertyName) {
0155             insertStream.writeStartElement(QStringLiteral("urn:schemas-upnp-org:event-1-0"), QStringLiteral("property"));
0156 
0157             const QVariant &propertyValue(d->mUpnpService->property(currentStateVariable.mPropertyName.constData()));
0158             if (propertyValue.canConvert<bool>()) {
0159                 insertStream.writeTextElement(itVariable, propertyValue.toBool() ? QStringLiteral("1") : QStringLiteral("0"));
0160             } else {
0161                 insertStream.writeTextElement(itVariable, propertyValue.toString());
0162             }
0163 
0164             insertStream.writeEndElement();
0165         }
0166     }
0167     insertStream.writeEndElement();
0168     insertStream.writeEndDocument();
0169     requestBody->seek(0);
0170 
0171     QNetworkReply *replyHandler = d->mNetworkAccess.sendCustomRequest(newRequest, "NOTIFY", requestBody.data());
0172 
0173     connect(replyHandler, &QNetworkReply::finished, this, &UpnpEventSubscriber::eventingFinished);
0174 
0175     d->mSentBuffer = requestBody;
0176     d->mSentBuffer->seek(0);
0177     qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpEventSubscriber::notifyPropertyChange" << d->mSentBuffer->data();
0178 }
0179 
0180 void UpnpEventSubscriber::eventingFinished()
0181 {
0182     d->mSentBuffer.clear();
0183 }
0184 
0185 void UpnpEventSubscriber::eventingInErrorFinished(QNetworkReply::NetworkError code)
0186 {
0187     Q_UNUSED(code)
0188 
0189     d->mSentBuffer.clear();
0190 }
0191 
0192 #include "moc_upnpeventsubscriber.cpp"