13 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
14 #include <QStringConverter>
29 static constexpr std::chrono::milliseconds
MAX_WAIT { 30s };
31 #define LOC QString("UPnPSub: ")
37 :
m_url(std::move(url)),
m_path(std::move(path)) { }
54 if (addr.protocol() == QAbstractSocket::IPv6Protocol)
55 host =
"[" + addr.toString() +
"]";
57 host = addr.toString();
59 m_callback = QString(
"http://%1:%2/Subscriptions/event?usn=")
60 .arg(host, QString::number(port));
67 while (!usns.isEmpty())
72 LOG(VB_UPNP, LOG_DEBUG,
LOC +
"Finished");
78 LOG(VB_UPNP, LOG_DEBUG,
LOC + QString(
"Subscribe %1 %2 %3")
79 .arg(usn, url.toString(), path));
92 LOG(VB_GENERAL, LOG_WARNING,
LOC +
93 "Re-subscribing with different url and path.");
112 QString uuid = QString();
130 LOG(VB_UPNP, LOG_DEBUG,
LOC + QString(
"Renew: %1").arg(usn));
146 LOG(VB_UPNP, LOG_ERR,
LOC + QString(
"Unrecognised renewal usn: %1")
157 LOG(VB_UPNP, LOG_ERR,
LOC + QString(
"No uuid - not renewing usn: %1")
171 LOG(VB_UPNP, LOG_INFO,
LOC + QString(
"Removing %1").arg(usn));
188 LOG(VB_UPNP, LOG_DEBUG,
LOC + QString(
"%1\n%2")
200 if (nt.isEmpty() || nts.isEmpty() || !no)
207 if (nt !=
"upnp:event" || nts !=
"upnp:propchange")
212 if (usn.isEmpty() || sid.isEmpty())
225 int loc = pRequest->
m_sPayload.lastIndexOf(
"propertyset>");
226 QString payload = (loc > -1) ? pRequest->
m_sPayload.left(loc + 12) :
229 LOG(VB_UPNP, LOG_DEBUG,
LOC + QString(
"Payload:\n%1").arg(payload));
233 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
237 if (!body.setContent(payload,
true, &
error, &errorLine, &errorCol))
239 LOG(VB_GENERAL, LOG_ERR,
LOC +
240 QString(
"Failed to parse event: Line: %1 Col: %2 Error: '%3'")
241 .arg(errorLine).arg(errorCol).arg(
error));
246 body.setContent(payload,
247 QDomDocument::ParseOption::UseNamespaceProcessing);
250 LOG(VB_GENERAL, LOG_ERR,
LOC +
251 QString(
"Failed to parse event: Line: %1 Col: %2 Error: '%3'")
252 .arg(parseResult.errorLine).arg(parseResult.errorColumn)
253 .arg(parseResult.errorMessage));
258 LOG(VB_UPNP, LOG_DEBUG,
LOC +
"/n/n" + body.toString(4) +
"/n/n");
260 QDomNodeList properties = body.elementsByTagName(
"property");
265 for (
int i = 0; i < properties.size(); i++)
267 QDomNodeList arguments = properties.at(i).childNodes();
268 for (
int j = 0; j < arguments.size(); j++)
270 QDomElement e = arguments.at(j).toElement();
271 if (!e.isNull() && !e.text().isEmpty() && !e.tagName().isEmpty())
272 results.insert(e.tagName(), e.text());
296 bool success =
false;
297 QString host = url.host();
298 int port = url.port();
301 QTextStream data(&sub);
302 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
303 data.setCodec(QTextCodec::codecForName(
"UTF-8"));
305 data.setEncoding(QStringConverter::Utf8);
308 data << QString(
"UNSUBSCRIBE %1 HTTP/1.1\r\n").arg(path);
309 data << QString(
"HOST: %1:%2\r\n").arg(host, QString::number(port));
310 data << QString(
"SID: uuid:%1\r\n").arg(uuid);
314 LOG(VB_UPNP, LOG_DEBUG,
LOC +
"\n\n" + sub);
316 auto *sockdev =
new MSocketDevice(MSocketDevice::Stream);
318 sockdev->setBlocking(
true);
320 if (sock->Connect(QHostAddress(host), port))
322 if (sock->WriteBlockDirect(sub.constData(), sub.size()) != -1)
324 QString line = sock->ReadLine(
MAX_WAIT);
325 success = !line.isEmpty();
329 LOG(VB_GENERAL, LOG_ERR,
LOC +
330 QString(
"Socket write error for %1:%2") .arg(host).arg(port));
336 LOG(VB_GENERAL, LOG_ERR,
LOC +
337 QString(
"Failed to open socket for %1:%2") .arg(host).arg(port));
343 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Unsubscribed to %1").arg(usn));
345 LOG(VB_UPNP, LOG_WARNING,
LOC + QString(
"Failed to unsubscribe to %1")
354 const QString &uuidin,
357 QString host = url.host();
358 int port = url.port();
361 QTextStream data(&sub);
362 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
363 data.setCodec(QTextCodec::codecForName(
"UTF-8"));
365 data.setEncoding(QStringConverter::Utf8);
368 data << QString(
"SUBSCRIBE %1 HTTP/1.1\r\n").arg(path);
369 data << QString(
"HOST: %1:%2\r\n").arg(host, QString::number(port));
372 if (uuidin.isEmpty())
374 data << QString(
"CALLBACK: <%1%2>\r\n")
376 data <<
"NT: upnp:event\r\n";
381 data << QString(
"SID: uuid:%1\r\n").arg(uuidin);
388 LOG(VB_UPNP, LOG_DEBUG,
LOC +
"\n\n" + sub);
390 auto *sockdev =
new MSocketDevice(MSocketDevice::Stream);
392 sockdev->setBlocking(
true);
396 std::chrono::seconds result = 0s;
398 if (sock->Connect(QHostAddress(host), port))
400 if (sock->WriteBlockDirect(sub.constData(), sub.size()) != -1)
403 QString line = sock->ReadLine(
MAX_WAIT);
404 while (!line.isEmpty())
406 LOG(VB_UPNP, LOG_DEBUG,
LOC + line);
407 if (line.contains(
"HTTP/1.1 200 OK", Qt::CaseInsensitive))
409 if (line.startsWith(
"SID:", Qt::CaseInsensitive))
410 uuid = line.mid(4).trimmed().mid(5).trimmed();
411 if (line.startsWith(
"TIMEOUT:", Qt::CaseInsensitive))
412 timeout = line.mid(8).trimmed().mid(7).trimmed();
413 if (ok && !uuid.isEmpty() && !
timeout.isEmpty())
418 if (ok && !uuid.isEmpty() && !
timeout.isEmpty())
421 result = std::chrono::seconds(
timeout.toUInt());
425 LOG(VB_GENERAL, LOG_ERR,
LOC +
426 QString(
"Failed to subscribe to %1").arg(usn));
431 LOG(VB_GENERAL, LOG_ERR,
LOC +
432 QString(
"Socket write error for %1:%2") .arg(host).arg(port));
438 LOG(VB_GENERAL, LOG_ERR,
LOC +
439 QString(
"Failed to open socket for %1:%2") .arg(host).arg(port));