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 "upnpdevicesoapserverobject.h"
0008 
0009 #include "upnplogging.h"
0010 
0011 #include "upnpabstractdevice.h"
0012 #include "upnpabstractservice.h"
0013 #include "upnpbasictypes.h"
0014 #include "upnpeventsubscriber.h"
0015 
0016 #include "upnpactiondescription.h"
0017 #include "upnpdevicedescription.h"
0018 #include "upnpservicedescription.h"
0019 
0020 #include "KDSoapClient/KDSoapValue.h"
0021 
0022 #include <QDateTime>
0023 #include <QList>
0024 #include <QLoggingCategory>
0025 #include <QPair>
0026 #include <QString>
0027 #include <QSysInfo>
0028 #include <QVariant>
0029 
0030 class UpnpDeviceSoapServerObjectPrivate
0031 {
0032 public:
0033     explicit UpnpDeviceSoapServerObjectPrivate(QList<UpnpAbstractDevice *> &devices)
0034         : mDevices(devices)
0035     {
0036     }
0037 
0038     QList<UpnpAbstractDevice *> &mDevices;
0039 };
0040 
0041 UpnpDeviceSoapServerObject::UpnpDeviceSoapServerObject(QList<UpnpAbstractDevice *> &devices, QObject *parent)
0042     : QObject(parent)
0043     , KDSoapServerObjectInterface()
0044     , d(std::make_unique<UpnpDeviceSoapServerObjectPrivate>(devices))
0045 {
0046 }
0047 
0048 UpnpDeviceSoapServerObject::~UpnpDeviceSoapServerObject() = default;
0049 
0050 void UpnpDeviceSoapServerObject::processRequest(const KDSoapMessage &request, KDSoapMessage &response, const QByteArray &soapAction)
0051 {
0052     Q_UNUSED(request)
0053     Q_UNUSED(response)
0054     Q_UNUSED(request)
0055     Q_UNUSED(soapAction)
0056 
0057     qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceSoapServerObject::processRequest" << request.name();
0058 }
0059 
0060 QIODevice *UpnpDeviceSoapServerObject::processFileRequest(const QString &path, QByteArray &contentType)
0061 {
0062     const QList<QString> &pathParts = path.split(QStringLiteral("/"));
0063     if (pathParts.count() == 3 && pathParts.last() == QStringLiteral("device.xml")) {
0064         const int deviceIndex = pathParts[1].toInt();
0065         if (deviceIndex >= 0 && deviceIndex < d->mDevices.count()) {
0066             return downloadDeviceXmlDescription(d->mDevices[deviceIndex], contentType).release();
0067         }
0068     } else if (pathParts.count() == 4 && pathParts.last() == QStringLiteral("service.xml")) {
0069         const int deviceIndex = pathParts[1].toInt();
0070         const int serviceIndex = pathParts[2].toInt();
0071         if (deviceIndex >= 0 && deviceIndex < d->mDevices.count()) {
0072             return downloadServiceXmlDescription(d->mDevices[deviceIndex], serviceIndex, contentType).release();
0073         }
0074     }
0075 
0076     return nullptr;
0077 }
0078 
0079 void UpnpDeviceSoapServerObject::processRequestWithPath(const KDSoapMessage &request, KDSoapMessage &response, const QByteArray &soapAction, const QString &path)
0080 {
0081     Q_UNUSED(request)
0082     Q_UNUSED(response)
0083     Q_UNUSED(soapAction)
0084 
0085     qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceSoapServerObject::processRequestWithPath" << path << request.name();
0086 
0087     const QList<QString> &pathParts = path.split(QStringLiteral("/"));
0088     if (pathParts.count() != 4 || pathParts.last() != QStringLiteral("control")) {
0089         response.setFault(true);
0090         return;
0091     }
0092 
0093     const int deviceIndex = pathParts[1].toInt();
0094     const int serviceIndex = pathParts[2].toInt();
0095 
0096     if (deviceIndex < 0 || deviceIndex >= d->mDevices.count()) {
0097         response.setFault(true);
0098         return;
0099     }
0100     UpnpAbstractDevice *currentDevice = d->mDevices[deviceIndex];
0101 
0102     if (serviceIndex < 0 || serviceIndex >= currentDevice->services().count()) {
0103         response.setFault(true);
0104         return;
0105     }
0106     auto currentService = currentDevice->serviceDescriptionByIndex(serviceIndex);
0107 
0108     const QList<QByteArray> &soapActionParts = soapAction.split('#');
0109     if (soapActionParts.size() != 2 || soapActionParts.first() != currentService.serviceType().toLatin1()) {
0110         response.setFault(true);
0111         return;
0112     }
0113 
0114     const QByteArray &actionName = soapActionParts.last();
0115     const QString &actionNameString = QString::fromLatin1(actionName);
0116 
0117     response = KDSoapValue(actionNameString + QStringLiteral("Response"), QVariant(), currentService.serviceType());
0118 
0119     const KDSoapValueList &allArguments(request.arguments());
0120     const UpnpActionDescription &currentAction = currentService.action(actionNameString);
0121     qCDebug(orgKdeUpnpLibQtUpnp()) << "allArguments" << allArguments << "action arguments" << currentAction.mNumberInArgument;
0122 
0123     QVector<QVariant> checkedArguments;
0124     bool argumentError = false;
0125 
0126     for (int argumentIndexMessage = 0, actionArgumentIndex = 0; !argumentError;) {
0127         if (actionArgumentIndex >= currentAction.mArguments.size()) {
0128             break;
0129         }
0130         if (currentAction.mArguments[actionArgumentIndex].mDirection == UpnpArgumentDirection::Out) {
0131             ++actionArgumentIndex;
0132             continue;
0133         }
0134         if (argumentIndexMessage < allArguments.size() && argumentIndexMessage < currentAction.mNumberInArgument) {
0135             if (allArguments[argumentIndexMessage].name() == currentAction.mArguments[argumentIndexMessage].mName) {
0136                 checkedArguments.push_back(allArguments[argumentIndexMessage].value());
0137             } else {
0138                 qCDebug(orgKdeUpnpLibQtUpnp()) << "invalid argument";
0139                 argumentError = true;
0140             }
0141         } else {
0142             if (argumentIndexMessage == currentAction.mNumberInArgument && argumentIndexMessage == allArguments.size()) {
0143                 break;
0144             }
0145 
0146             if (argumentIndexMessage < currentAction.mNumberInArgument) {
0147                 qCDebug(orgKdeUpnpLibQtUpnp()) << "missing arguments";
0148             }
0149             argumentError = true;
0150         }
0151         ++argumentIndexMessage;
0152         ++actionArgumentIndex;
0153     }
0154 
0155     if (argumentError) {
0156         qCDebug(orgKdeUpnpLibQtUpnp()) << "error about arguments";
0157         response.setFault(true);
0158         return;
0159     } else {
0160         bool actionCallIsInError = false;
0161 
0162         //const QList<QPair<QString, QVariant> > &returnedValues(currentService.invokeAction(actionNameString, checkedArguments, actionCallIsInError));
0163 
0164         if (actionCallIsInError) {
0165             response.setFault(true);
0166             return;
0167         } else {
0168             response.setFault(false);
0169         }
0170 
0171         response.setType(currentService.serviceType(), actionNameString + QStringLiteral("Response"));
0172         /*for (const QPair<QString, QVariant> &oneValue : returnedValues) {
0173             response.addArgument(oneValue.first, oneValue.second);
0174         }*/
0175     }
0176 }
0177 
0178 bool UpnpDeviceSoapServerObject::processCustomVerbRequest(const QByteArray &requestType, const QByteArray &requestData,
0179     const QMap<QByteArray, QByteArray> &httpHeaders, QByteArray &customAnswer)
0180 {
0181     if (requestType == "SUBSCRIBE") {
0182         const QString &path = QString::fromLatin1(httpHeaders.value("_path"));
0183         const QList<QString> &pathParts = path.split(QStringLiteral("/"));
0184         if (pathParts.count() == 4 && pathParts.last() == QStringLiteral("event")) {
0185             const int deviceIndex = pathParts[1].toInt();
0186             const int serviceIndex = pathParts[2].toInt();
0187             if (deviceIndex >= 0 && deviceIndex < d->mDevices.count()) {
0188                 UpnpAbstractDevice *currentDevice = d->mDevices[deviceIndex];
0189                 if (serviceIndex >= 0 && serviceIndex < currentDevice->services().count()) {
0190                     //QPointer<UpnpEventSubscriber> newSubscriber = currentDevice->serviceByIndex(serviceIndex)->subscribeToEvents(requestData, httpHeaders);
0191 
0192                     customAnswer = "HTTP/1.1 200 OK\r\n";
0193                     customAnswer += "DATE: " + QDateTime::currentDateTime().toString(QStringLiteral("ddd, d MMM yyyy HH:mm:ss t")).toLatin1() + "\r\n";
0194                     //customAnswer += "SID: uuid:" + newSubscriber->uuid().toLatin1() + "\r\n";
0195                     customAnswer += "SERVER: " + QSysInfo::kernelType().toLatin1() + " " + QSysInfo::kernelVersion().toLatin1() + " UPnP/1.0 test/1.0\r\n";
0196                     //customAnswer += "TIMEOUT:Second-" + QByteArray::number(newSubscriber->secondTimeout()) + "\r\n";
0197                     customAnswer += "\r\n";
0198 
0199                     return true;
0200                 }
0201             }
0202         }
0203     } else if (requestType == "UNSUBSCRIBE") {
0204         const QString &path = QString::fromLatin1(httpHeaders.value("_path"));
0205         const QList<QString> &pathParts = path.split(QStringLiteral("/"));
0206         if (pathParts.count() == 4 && pathParts.last() == QStringLiteral("event")) {
0207             const int deviceIndex = pathParts[1].toInt();
0208             const int serviceIndex = pathParts[2].toInt();
0209             if (deviceIndex >= 0 && deviceIndex < d->mDevices.count()) {
0210                 UpnpAbstractDevice *currentDevice = d->mDevices[deviceIndex];
0211                 if (serviceIndex >= 0 && serviceIndex < currentDevice->services().count()) {
0212                     //currentDevice->serviceByIndex(serviceIndex)->unsubscribeToEvents(requestData, httpHeaders);
0213 
0214                     customAnswer = "HTTP/1.1 200 OK\r\n";
0215                     customAnswer += "DATE: " + QDateTime::currentDateTime().toString(QStringLiteral("ddd, d MMM yyyy HH:mm:ss t")).toLatin1() + "\r\n";
0216                     //customAnswer += "SID: uuid:" + newSubscriber->uuid().toLatin1() + "\r\n";
0217                     customAnswer += "SERVER: " + QSysInfo::kernelType().toLatin1() + " " + QSysInfo::kernelVersion().toLatin1() + " UPnP/1.0 test/1.0\r\n";
0218                     //customAnswer += "TIMEOUT:Second-" + QByteArray::number(newSubscriber->secondTimeout()) + "\r\n";
0219                     customAnswer += "\r\n";
0220 
0221                     return true;
0222                 }
0223             }
0224         }
0225     } else {
0226         qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceSoapServerObject::processCustomVerbRequest" << requestData << httpHeaders;
0227     }
0228 
0229     return false;
0230 }
0231 
0232 std::unique_ptr<QIODevice> UpnpDeviceSoapServerObject::downloadDeviceXmlDescription(UpnpAbstractDevice *device, QByteArray &contentType)
0233 {
0234     qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceSoapServerObject::downloadDeviceXmlDescription" << device->description().UDN();
0235 
0236     contentType = "text/xml";
0237 
0238     return device->buildAndGetXmlDescription();
0239 }
0240 
0241 std::unique_ptr<QIODevice> UpnpDeviceSoapServerObject::downloadServiceXmlDescription(UpnpAbstractDevice *device, const int serviceIndex, QByteArray &contentType)
0242 {
0243     qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceSoapServerObject::downloadServiceXmlDescription" << device->description().UDN() << serviceIndex;
0244 
0245     contentType = "text/xml";
0246 
0247     return {}/*device->serviceByIndex(serviceIndex)->buildAndGetXmlDescription()*/;
0248 }
0249 
0250 #include "moc_upnpdevicesoapserverobject.cpp"