Skip to content

Commit b710333

Browse files
committed
WIP
1 parent f562db2 commit b710333

File tree

7 files changed

+1359
-102
lines changed

7 files changed

+1359
-102
lines changed

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ find_package(Qt6 QUIET COMPONENTS Core NO_SYSTEM_ENVIRONMENT_PATH)
3434

3535
if (Qt6_FOUND)
3636
find_package(Qt6 REQUIRED COMPONENTS Network Test)
37-
find_package(Qt6 QUIET COMPONENTS Zlib)
37+
find_package(Qt6 QUIET COMPONENTS Widgets Zlib)
3838
set(Qt_DIR ${QT6_INSTALL_PREFIX})
3939
set(Qt_VERSION ${Qt6_VERSION})
4040
else()
4141
find_package(Qt5 5.15 REQUIRED COMPONENTS Network Test)
42-
find_package(Qt5 QUIET COMPONENTS Zlib)
42+
find_package(Qt5 QUIET COMPONENTS Widgets Zlib)
4343
set(Qt_VERSION ${Qt5_VERSION})
4444
set(Qt_DIR ${Qt5_DIR})
4545
endif()

upnp/CMakeLists.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ qnc_add_library(
22
QncUpnp STATIC
33
upnpresolver.cpp
44
upnpresolver.h
5+
upnptreemodel.cpp
6+
upnptreemodel.h
57
)
68

79
target_link_libraries(QncUpnp PUBLIC QncSsdp QncXml)
810

911
if (NOT IOS) # FIXME Figure out code signing on Github
10-
qnc_add_executable(UPNPResolverDemo TYPE tool upnpresolverdemo.cpp)
11-
target_link_libraries(UPNPResolverDemo PRIVATE QncUpnp)
12+
qnc_add_executable(upnpresolverdemo TYPE tool upnpresolverdemo.cpp)
13+
target_link_libraries(upnpresolverdemo PRIVATE QncUpnp)
14+
15+
if (TARGET Qt::Widgets)
16+
qnc_add_executable(upnpviewer TYPE tool upnpviewer.cpp)
17+
target_link_libraries(upnpviewer PRIVATE QncUpnp Qt::Widgets)
18+
endif()
1219
endif()

upnp/upnpresolver.cpp

Lines changed: 192 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace qnc::xml {
1717

1818
using namespace qnc::upnp;
1919

20+
template <> DeviceModel &currentObject(DeviceDescription &device) { return device.model; }
21+
template <> DeviceManufacturer &currentObject(DeviceDescription &device) { return device.manufacturer; }
2022
template <> IconDescription &currentObject(DeviceDescription &device) { return device.icons.last(); }
2123
template <> ServiceDescription &currentObject(DeviceDescription &device) { return device.services.last(); }
2224
template <> ActionDescription &currentObject(ControlPointDescription &service) { return service.actions.last(); }
@@ -160,10 +162,8 @@ class DetailLoader : public QObject
160162
, m_device{std::move(device)}
161163
{}
162164

163-
void fetchIcons();
164-
void parseServiceDescription();
165-
void parseControlDescription();
166-
void parseEventingDescription();
165+
void loadIcons();
166+
void loadServiceDescription();
167167

168168
const DeviceDescription &device() const { return m_device; }
169169
QList<QNetworkReply *> pendingReplies() const { return m_pendingReplies; }
@@ -172,6 +172,9 @@ class DetailLoader : public QObject
172172
void finished();
173173

174174
private:
175+
template <class Container>
176+
void load(const char *topic, Container *container);
177+
175178
void checkIfAllRequestsFinished();
176179

177180
QNetworkAccessManager *const m_networkAccessManager;
@@ -216,16 +219,16 @@ QList<DeviceDescription> DeviceDescriptionParser::read(const QUrl &deviceUrl, St
216219
State::Device, {
217220
{u"deviceType", assign<&DeviceDescription::deviceType>(device)},
218221
{u"friendlyName", assign<&DeviceDescription::displayName>(device)},
219-
{u"manufacturer", assign<&DeviceDescription::manufacturerName>(device)},
220-
{u"manufacturerURL", assign<&DeviceDescription::manufacturerUrl>(device)},
221-
{u"modelDescription", assign<&DeviceDescription::modelDescription>(device)},
222-
{u"modelName", assign<&DeviceDescription::modelName>(device)},
223-
{u"modelNumber", assign<&DeviceDescription::modelNumber>(device)},
224-
{u"modelURL", assign<&DeviceDescription::modelUrl>(device)},
222+
{u"manufacturer", assign<&DeviceManufacturer::name>(device)},
223+
{u"manufacturerURL", assign<&DeviceManufacturer::url>(device)},
224+
{u"modelDescription", assign<&DeviceModel::description>(device)},
225+
{u"modelName", assign<&DeviceModel::name>(device)},
226+
{u"modelNumber", assign<&DeviceModel::number>(device)},
227+
{u"modelURL", assign<&DeviceModel::url>(device)},
225228
{u"presentationURL", assign<&DeviceDescription::presentationUrl>(device)},
226229
{u"serialNumber", assign<&DeviceDescription::serialNumber>(device)},
227230
{u"UDN", assign<&DeviceDescription::uniqueDeviceName>(device)},
228-
{u"UPC", assign<&DeviceDescription::universalProductCode>(device)},
231+
{u"UPC", assign<&DeviceModel::universalProductCode>(device)},
229232

230233
{u"deviceList", transition<State::DeviceList>()},
231234
{u"iconList", transition<State::IconList>()},
@@ -304,6 +307,7 @@ std::optional<ControlPointDescription> ControlPointDescriptionParser::read(State
304307
State::Argument, {
305308
{u"name", assign<&ArgumentDescription::name>(service)},
306309
{u"direction", assign<&ArgumentDescription::direction>(service)},
310+
{u"retval", assign<&ArgumentDescription::flags, ArgumentDescription::Flag::ReturnValue>(service)},
307311
{u"relatedStateVariable", assign<&ArgumentDescription::stateVariable>(service)},
308312
}
309313
}, {
@@ -339,25 +343,67 @@ std::optional<ControlPointDescription> ControlPointDescriptionParser::read(State
339343
return service;
340344
}
341345

342-
void DetailLoader::fetchIcons()
346+
QUrl itemUrl(const IconDescription &icon)
347+
{
348+
return icon.url;
349+
}
350+
351+
QUrl itemUrl(const ServiceDescription &service)
352+
{
353+
return service.scpdUrl;
354+
}
355+
356+
bool itemHasData(const IconDescription &icon)
357+
{
358+
return !icon.data.isEmpty();
359+
}
360+
361+
bool itemHasData(const ServiceDescription &service)
362+
{
363+
return service.scpd.has_value();
364+
}
365+
366+
void updateItem(IconDescription &icon, QNetworkReply *reply)
367+
{
368+
icon.data = reply->readAll();
369+
}
370+
371+
void updateItem(ServiceDescription &service, QNetworkReply *reply)
343372
{
344-
for (auto icon = m_device.icons.begin(); icon != m_device.icons.end(); ++icon) {
373+
service.scpd = ControlPointDescription::parse(reply);
374+
}
375+
376+
template <class Container>
377+
void DetailLoader::load(const char *topic, Container *container)
378+
{
379+
const auto first = container->cbegin();
380+
const auto last = container->cend();
381+
382+
for (auto it = first; it != last; ++it) {
383+
const auto &url = itemUrl(*it);
345384

346-
if (icon->url.isEmpty())
385+
if (url.isEmpty())
347386
continue;
348-
if (!icon->data.isEmpty())
387+
if (itemHasData(*it))
349388
continue;
350389

351-
const auto index = icon - m_device.icons.begin();
352-
const auto reply = m_networkAccessManager->get(QNetworkRequest{icon->url});
390+
const auto request = QNetworkRequest{m_device.url.resolved(url)};
391+
const auto reply = m_networkAccessManager->get(request);
392+
const auto index = it - first;
353393

354-
connect(reply, &QNetworkReply::finished, this, [this, index, reply] {
394+
qCInfo(lcResolver,
395+
"Downloading %s for %ls from <%ls>", topic,
396+
qUtf16Printable(m_device.uniqueDeviceName),
397+
qUtf16Printable(request.url().toDisplayString()));
398+
399+
connect(reply, &QNetworkReply::finished, this, [this, container, index, reply] {
355400
if (reply->error() != QNetworkReply::NoError) {
356-
qCWarning(lcResolver, "Could not download icon from %ls: %ls",
401+
qCWarning(lcResolver, "Could not download detail for %ls from %ls: %ls",
402+
qUtf16Printable(m_device.uniqueDeviceName),
357403
qUtf16Printable(reply->url().toDisplayString()),
358404
qUtf16Printable(reply->errorString()));
359405
} else {
360-
m_device.icons[index].data = reply->readAll();
406+
updateItem((*container)[index], reply);
361407
}
362408

363409
checkIfAllRequestsFinished();
@@ -367,16 +413,14 @@ void DetailLoader::fetchIcons()
367413
}
368414
}
369415

370-
void DetailLoader::parseServiceDescription()
371-
{
372-
}
373-
374-
void DetailLoader::parseControlDescription()
416+
void DetailLoader::loadIcons()
375417
{
418+
load("icons", &m_device.icons);
376419
}
377420

378-
void DetailLoader::parseEventingDescription()
421+
void DetailLoader::loadServiceDescription()
379422
{
423+
load("services", &m_device.services);
380424
}
381425

382426
void DetailLoader::checkIfAllRequestsFinished()
@@ -385,6 +429,10 @@ void DetailLoader::checkIfAllRequestsFinished()
385429
if (reply && reply->isRunning())
386430
return;
387431

432+
qCInfo(lcResolver,
433+
"All details downloaded for %ls",
434+
qUtf16Printable(m_device.uniqueDeviceName));
435+
388436
emit finished();
389437
}
390438

@@ -444,16 +492,33 @@ QNetworkAccessManager *Resolver::networkAccessManager() const
444492

445493
void Resolver::onServiceFound(const ssdp::ServiceDescription &service)
446494
{
447-
if (m_networkAccessManager) {
448-
const auto &locationList = service.locations();
495+
const auto &locationList = service.locations();
496+
497+
for (const auto &url : locationList) {
498+
if (m_networkAccessManager) {
499+
qCInfo(lcResolver,
500+
"Downloading device description for %ls from <%ls>",
501+
qUtf16Printable(service.name()), qUtf16Printable(url.toDisplayString()));
449502

450-
for (const auto &url: locationList) {
451503
const auto reply = m_networkAccessManager->get(QNetworkRequest{url});
504+
452505
connect(reply, &QNetworkReply::finished, this, [this, reply] {
453506
onDeviceDescriptionReceived(reply);
454507
});
455508

456509
m_pendingReplies += reply;
510+
} else {
511+
qCInfo(lcResolver,
512+
"Directly reporting %ls without downloading from <%ls>",
513+
qUtf16Printable(service.name()), qUtf16Printable(url.toDisplayString()));
514+
515+
auto device = DeviceDescription{};
516+
517+
device.url = url;
518+
device.deviceType = service.type();
519+
device.uniqueDeviceName = service.name();
520+
521+
emit deviceFound(device);
457522
}
458523
}
459524
}
@@ -462,22 +527,28 @@ void Resolver::onDeviceDescriptionReceived(QNetworkReply *reply)
462527
{
463528
m_pendingReplies.removeOne(reply);
464529

465-
qInfo() << reply->url() << reply->error() << reply->errorString();
530+
if (reply->error() == QNetworkReply::NoError) {
531+
qCInfo(lcResolver,
532+
"Device description received from <%ls>",
533+
qUtf16Printable(reply->url().toDisplayString()));
534+
} else {
535+
qCWarning(lcResolver,
536+
"Could not download device description received from <%ls>: %ls",
537+
qUtf16Printable(reply->url().toDisplayString()),
538+
qUtf16Printable(reply->errorString()));
539+
}
466540

467541
auto deviceList = DeviceDescription::parse(reply, reply->url());
468542

469543
for (auto &device : deviceList) {
470544
if (m_behaviors && m_networkAccessManager) {
471545
const auto detailLoader = new DetailLoader{std::move(device), m_networkAccessManager, this};
472546

473-
if (m_behaviors.testFlag(Behavior::FetchIcons))
474-
detailLoader->fetchIcons();
475-
if (m_behaviors.testFlag(Behavior::ParseServiceDescription))
476-
detailLoader->parseServiceDescription();
477-
if (m_behaviors.testFlag(Behavior::ParseControlDescription))
478-
detailLoader->parseControlDescription();
479-
if (m_behaviors.testFlag(Behavior::ParseEventingDescription))
480-
detailLoader->parseEventingDescription();
547+
if (m_behaviors.testFlag(Behavior::LoadIcons))
548+
detailLoader->loadIcons();
549+
550+
if (m_behaviors.testFlag(Behavior::LoadServiceDescription))
551+
detailLoader->loadServiceDescription();
481552

482553
m_pendingReplies += detailLoader->pendingReplies();
483554

@@ -514,4 +585,87 @@ std::optional<ControlPointDescription> ControlPointDescription::parse(QIODevice
514585

515586
} // namespace qnc::upnp
516587

588+
QDebug operator<<(QDebug debug, const qnc::upnp::DeviceDescription &device)
589+
{
590+
const auto _ = QDebugStateSaver{debug};
591+
592+
const auto verbose = (debug.verbosity() > QDebug::DefaultVerbosity);
593+
const auto silent = (debug.verbosity() < QDebug::DefaultVerbosity);
594+
595+
if (verbose)
596+
debug.nospace() << device.staticMetaObject.className();
597+
598+
debug << "(udn=" << device.uniqueDeviceName
599+
<< ", type=" << device.deviceType;
600+
601+
if (verbose)
602+
debug << ", spec=" << device.specVersion;
603+
604+
if (!device.displayName.isEmpty())
605+
debug << ", name=" << device.displayName;
606+
607+
debug << ", manufacturer=" << device.manufacturer
608+
<< ", model=" << device.model;
609+
610+
if (!device.presentationUrl.isEmpty())
611+
debug << ", presentationUrl=" << device.presentationUrl;
612+
if (!device.serialNumber.isEmpty())
613+
debug << ", serialNumber=" << device.serialNumber;
614+
if (!device.url.isEmpty())
615+
debug << ", url=" << device.url;
616+
617+
if (!silent) {
618+
// QList<IconDescription> icons = {};
619+
// QList<ServiceDescription> services = {};
620+
}
621+
622+
return debug << ')';
623+
}
624+
625+
QDebug operator<<(QDebug debug, const qnc::upnp::DeviceManufacturer &manufacturer)
626+
{
627+
const auto _ = QDebugStateSaver{debug};
628+
629+
const auto verbose = (debug.verbosity() > QDebug::DefaultVerbosity);
630+
const auto silent = (debug.verbosity() < QDebug::DefaultVerbosity);
631+
632+
if (verbose)
633+
debug.nospace() << manufacturer.staticMetaObject.className();
634+
635+
debug << '(';
636+
637+
if (!manufacturer.name.isEmpty())
638+
debug << ", name=" << manufacturer.name;
639+
if (!silent && !manufacturer.url.isEmpty())
640+
debug << ", url=" << manufacturer.url;
641+
642+
return debug << ')';
643+
}
644+
645+
QDebug operator<<(QDebug debug, const qnc::upnp::DeviceModel &model)
646+
{
647+
const auto _ = QDebugStateSaver{debug};
648+
649+
const auto verbose = (debug.verbosity() > QDebug::DefaultVerbosity);
650+
const auto silent = (debug.verbosity() < QDebug::DefaultVerbosity);
651+
652+
if (verbose)
653+
debug.nospace() << model.staticMetaObject.className();
654+
655+
debug << '(';
656+
657+
if (!model.universalProductCode.isEmpty())
658+
debug << ", universalProductCode=" << model.universalProductCode;
659+
if (!model.description.isEmpty())
660+
debug << ", description=" << model.description;
661+
if (!model.name.isEmpty())
662+
debug << ", name=" << model.name;
663+
if (!model.number.isEmpty())
664+
debug << ", number=" << model.number;
665+
if (!silent && !model.url.isEmpty())
666+
debug << ", url=" << model.url;
667+
668+
return debug << ')';
669+
}
670+
517671
#include "upnpresolver.moc"

0 commit comments

Comments
 (0)