File indexing completed on 2024-05-05 08:56:16
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 "upnpabstractservice.h" 0008 0009 #include "upnplogging.h" 0010 0011 #include "upnpbasictypes.h" 0012 #include "upnpeventsubscriber.h" 0013 0014 #include "upnpactiondescription.h" 0015 #include "upnpservicedescription.h" 0016 #include "upnpstatevariabledescription.h" 0017 0018 #include <QBuffer> 0019 #include <QIODevice> 0020 #include <QMetaObject> 0021 #include <QMetaProperty> 0022 #include <QPointer> 0023 #include <QTimer> 0024 #include <QXmlStreamWriter> 0025 0026 #include <QLoggingCategory> 0027 0028 class UpnpAbstractServicePrivate 0029 { 0030 public: 0031 UpnpAbstractServicePrivate() 0032 : mService() 0033 , mXmlDescription() 0034 , mSubscribers() 0035 { 0036 } 0037 0038 UpnpServiceDescription mService; 0039 0040 QPointer<QIODevice> mXmlDescription; 0041 0042 QVector<QPointer<UpnpEventSubscriber>> mSubscribers; 0043 }; 0044 0045 UpnpAbstractService::UpnpAbstractService(QObject *parent) 0046 : QObject(parent) 0047 , d(std::make_unique<UpnpAbstractServicePrivate>()) 0048 { 0049 } 0050 0051 UpnpAbstractService::~UpnpAbstractService() = default; 0052 0053 QIODevice *UpnpAbstractService::buildAndGetXmlDescription() 0054 { 0055 if (!d->mXmlDescription) { 0056 QPointer<QBuffer> newDescription(new QBuffer); 0057 0058 newDescription->open(QIODevice::ReadWrite); 0059 0060 QXmlStreamWriter insertStream(newDescription.data()); 0061 insertStream.setAutoFormatting(true); 0062 0063 insertStream.writeStartDocument(); 0064 insertStream.writeStartElement(QStringLiteral("scpd")); 0065 insertStream.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("urn:schemas-upnp-org:service-1-0")); 0066 insertStream.writeStartElement(QStringLiteral("specVersion")); 0067 insertStream.writeTextElement(QStringLiteral("major"), QStringLiteral("1")); 0068 insertStream.writeTextElement(QStringLiteral("minor"), QStringLiteral("0")); 0069 insertStream.writeEndElement(); 0070 0071 insertStream.writeStartElement(QStringLiteral("actionList")); 0072 const auto &allActions = description().actions(); 0073 for (const auto &itAction : allActions) { 0074 insertStream.writeStartElement(QStringLiteral("action")); 0075 insertStream.writeTextElement(QStringLiteral("name"), itAction.mName); 0076 insertStream.writeStartElement(QStringLiteral("argumentList")); 0077 for (const auto &itArgument : itAction.mArguments) { 0078 insertStream.writeStartElement(QStringLiteral("argument")); 0079 insertStream.writeTextElement(QStringLiteral("name"), itArgument.mName); 0080 insertStream.writeTextElement(QStringLiteral("direction"), (itArgument.mDirection == UpnpArgumentDirection::In) ? QStringLiteral("in") : QStringLiteral("out")); 0081 if (itArgument.mIsReturnValue) { 0082 insertStream.writeEmptyElement(QStringLiteral("retval")); 0083 } 0084 insertStream.writeTextElement(QStringLiteral("relatedStateVariable"), itArgument.mRelatedStateVariable); 0085 insertStream.writeEndElement(); 0086 } 0087 insertStream.writeEndElement(); 0088 insertStream.writeEndElement(); 0089 } 0090 insertStream.writeEndElement(); 0091 0092 insertStream.writeStartElement(QStringLiteral("serviceStateTable")); 0093 0094 const auto &allStateVariables = description().stateVariables(); 0095 for (const auto &itStateVariable : allStateVariables) { 0096 insertStream.writeStartElement(QStringLiteral("stateVariable")); 0097 if (itStateVariable.mEvented) { 0098 insertStream.writeAttribute(QStringLiteral("sendEvents"), QStringLiteral("yes")); 0099 } else { 0100 insertStream.writeAttribute(QStringLiteral("sendEvents"), QStringLiteral("no")); 0101 } 0102 insertStream.writeTextElement(QStringLiteral("name"), itStateVariable.mUpnpName); 0103 insertStream.writeTextElement(QStringLiteral("dataType"), itStateVariable.mDataType); 0104 if (itStateVariable.mDefaultValue.isValid()) { 0105 insertStream.writeTextElement(QStringLiteral("defaultValue"), itStateVariable.mDefaultValue.toString()); 0106 } 0107 if (itStateVariable.mMinimumValue.isValid() && itStateVariable.mMaximumValue.isValid() 0108 && itStateVariable.mStep.isValid()) { 0109 insertStream.writeStartElement(QStringLiteral("allowedValueRange")); 0110 if (itStateVariable.mMinimumValue.isValid()) { 0111 insertStream.writeTextElement(QStringLiteral("minimum"), itStateVariable.mMinimumValue.toString()); 0112 } 0113 if (itStateVariable.mMaximumValue.isValid()) { 0114 insertStream.writeTextElement(QStringLiteral("maximum"), itStateVariable.mMaximumValue.toString()); 0115 } 0116 if (itStateVariable.mStep.isValid()) { 0117 insertStream.writeTextElement(QStringLiteral("step"), itStateVariable.mStep.toString()); 0118 } 0119 insertStream.writeEndElement(); 0120 } 0121 if (!itStateVariable.mValueList.isEmpty()) { 0122 insertStream.writeStartElement(QStringLiteral("allowedValueList")); 0123 for (const auto &itValue : itStateVariable.mValueList) { 0124 insertStream.writeTextElement(QStringLiteral("allowedValue"), itValue); 0125 } 0126 insertStream.writeEndElement(); 0127 } 0128 insertStream.writeEndElement(); 0129 } 0130 insertStream.writeEndElement(); 0131 insertStream.writeEndElement(); 0132 insertStream.writeEndDocument(); 0133 0134 d->mXmlDescription = newDescription; 0135 } 0136 0137 d->mXmlDescription->seek(0); 0138 0139 return d->mXmlDescription; 0140 } 0141 0142 QPointer<UpnpEventSubscriber> UpnpAbstractService::subscribeToEvents(const QByteArray &requestData, const QMap<QByteArray, QByteArray> &headers) 0143 { 0144 Q_UNUSED(requestData) 0145 0146 QPointer<UpnpEventSubscriber> newSubscriber(new UpnpEventSubscriber); 0147 0148 const QByteArray &rawCallBackAddress = headers["callback"]; 0149 newSubscriber->setCallback(QUrl(QString::fromLatin1(rawCallBackAddress.mid(1, rawCallBackAddress.length() - 2)))); 0150 0151 const QByteArray &rawTimeout = headers["timeout"]; 0152 bool conversionIsOk = false; 0153 newSubscriber->setSecondTimeout(rawTimeout.right(rawTimeout.length() - 7).toInt(&conversionIsOk)); 0154 if (!conversionIsOk || rawTimeout == "Second-infinite") { 0155 newSubscriber->setSecondTimeout(description().maximumSubscriptionDuration()); 0156 } 0157 0158 int signalIndex = -1; 0159 for (int i = 0; i < newSubscriber->metaObject()->methodCount(); ++i) { 0160 if (newSubscriber->metaObject()->method(i).name() == "notifyPropertyChange") { 0161 signalIndex = i; 0162 break; 0163 } 0164 } 0165 0166 if (signalIndex != -1) { 0167 newSubscriber->setUpnpService(this); 0168 d->mSubscribers.push_back(newSubscriber); 0169 0170 const auto &allStateVariables = description().stateVariables(); 0171 for (const auto ¤tStateVariable : allStateVariables) { 0172 if (currentStateVariable.mEvented) { 0173 connect(this, metaObject()->property(currentStateVariable.mPropertyIndex).notifySignal(), newSubscriber, newSubscriber->metaObject()->method(signalIndex)); 0174 } 0175 } 0176 } 0177 0178 sendEventNotification(newSubscriber); 0179 0180 return newSubscriber; 0181 } 0182 0183 void UpnpAbstractService::unsubscribeToEvents(const QByteArray &requestData, const QMap<QByteArray, QByteArray> &headers) 0184 { 0185 Q_UNUSED(headers); 0186 0187 qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpAbstractService::unsubscribeToEvents" << requestData; 0188 } 0189 0190 void UpnpAbstractService::addAction(const UpnpActionDescription &newAction) 0191 { 0192 description().actions()[newAction.mName] = newAction; 0193 } 0194 0195 const UpnpActionDescription &UpnpAbstractService::action(const QString &name) const 0196 { 0197 return description().action(name); 0198 } 0199 0200 QList<QString> UpnpAbstractService::actions() const 0201 { 0202 return description().actions().keys(); 0203 } 0204 0205 void UpnpAbstractService::addStateVariable(const UpnpStateVariableDescription &newVariable) 0206 { 0207 description().stateVariables()[newVariable.mUpnpName] = newVariable; 0208 } 0209 0210 const UpnpStateVariableDescription &UpnpAbstractService::stateVariable(const QString &name) const 0211 { 0212 return description().stateVariable(name); 0213 } 0214 0215 QList<QString> UpnpAbstractService::stateVariables() const 0216 { 0217 return description().stateVariables().keys(); 0218 } 0219 0220 QVector<QPair<QString, QVariant>> UpnpAbstractService::invokeAction(const QString &actionName, const QVector<QVariant> &arguments, bool &isInError) 0221 { 0222 Q_UNUSED(actionName) 0223 Q_UNUSED(arguments) 0224 Q_UNUSED(isInError) 0225 0226 return {}; 0227 } 0228 0229 void UpnpAbstractService::setDescription(const UpnpServiceDescription &value) 0230 { 0231 d->mService = value; 0232 Q_EMIT descriptionChanged(); 0233 } 0234 0235 UpnpServiceDescription &UpnpAbstractService::description() 0236 { 0237 return d->mService; 0238 } 0239 0240 const UpnpServiceDescription &UpnpAbstractService::description() const 0241 { 0242 return d->mService; 0243 } 0244 void UpnpAbstractService::sendEventNotification(const QPointer<UpnpEventSubscriber> ¤tSubscriber) 0245 { 0246 QTimer::singleShot(0, currentSubscriber.data(), &UpnpEventSubscriber::sendEventNotification); 0247 } 0248 0249 #include "moc_upnpabstractservice.cpp"