24 #include <QHostAddress> 44 : m_responseAddress(Address),
45 m_responseTypes(Types),
69 m_searchDebugged(false),
70 m_firstSearchTimer(0),
71 m_secondSearchTimer(0),
72 m_firstAnnounceTimer(0),
73 m_secondAnnounceTimer(0),
80 m_ipv4MulticastSocket(nullptr),
82 m_ipv6LinkMulticastSocket(nullptr),
83 m_discoveredDevices(),
84 m_ipv4UnicastSocket(nullptr),
85 m_ipv6UnicastSocket(nullptr),
89 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) 90 m_serverString = QStringLiteral(
"%1/%2 UPnP/1.0 Torc/0.1").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion());
92 m_serverString = QStringLiteral(
"OldTorc/OldVersion UPnP/1.0 Torc/0.1");
101 m_refreshTimer = startTimer(5 * 60 * 1000, Qt::VeryCoarseTimer);
106 m_responseTimer.start(5000);
114 killTimer(m_refreshTimer);
119 void TorcSSDP::Start(
void)
124 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Starting SSDP as %1").arg(m_serverString));
127 QNetworkInterface interface;
128 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
129 foreach (
const QNetworkInterface &i, interfaces)
131 QNetworkInterface::InterfaceFlags flags = i.flags();
132 if (flags & QNetworkInterface::IsUp &&
133 flags & QNetworkInterface::IsRunning &&
134 flags & QNetworkInterface::CanMulticast &&
135 !(flags & QNetworkInterface::IsLoopBack))
137 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Using interface '%1' for multicast").arg(i.name()));
144 m_ipv4Address = QString();
145 m_ipv6Address = QString();
146 m_addressess = QNetworkInterface::allAddresses();
150 QString ipv6linklocal;
151 foreach (QHostAddress address, m_addressess)
159 if (address.protocol() == QAbstractSocket::IPv4Protocol)
160 m_ipv4Address = address.toString();
162 m_ipv6Address = address.toString();
166 ipv6linklocal = address.toString();
171 if (m_ipv6Address.isEmpty() && !ipv6linklocal.isEmpty())
172 m_ipv6Address = ipv6linklocal;
173 if (!m_ipv6Address.isEmpty() && !m_ipv6Address.startsWith(
'['))
174 m_ipv6Address = QStringLiteral(
"[%1]").arg(m_ipv6Address);
176 if (m_ipv4Address.isEmpty())
177 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Disabling SSDP IPv4 - no valid address detected"));
179 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP IPv4 host url: %1").arg(m_ipv4Address));
180 if (m_ipv6Address.isEmpty())
181 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Disabling SSDP IPv6 - no valid address detected"));
183 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP IPv6 host url: %1 (if enabled)").arg(m_ipv6Address));
186 if (interface.isValid())
188 m_ipv4MulticastSocket = CreateMulticastSocket(m_ipv4GroupAddress,
this, interface);
189 m_ipv6LinkMulticastSocket = CreateMulticastSocket(m_ipv6LinkGroupBaseAddress,
this, interface);
190 m_ipv4UnicastSocket = CreateSearchSocket(QHostAddress(QHostAddress::AnyIPv4),
this);
191 m_ipv6UnicastSocket = CreateSearchSocket(QHostAddress(QHostAddress::AnyIPv6),
this);
195 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Failed to create SSDP sockets - no suitable network interface"));
216 void TorcSSDP::Stop(
void)
223 m_responseQueue.clear();
226 QHash<QString,TorcUPNPDescription>::const_iterator it2 = m_discoveredDevices.constBegin();
227 for ( ; it2 != m_discoveredDevices.constEnd(); ++it2)
230 data.insert(
TORC_USN, it2.value().GetUSN());
234 m_discoveredDevices.clear();
236 m_addressess.clear();
237 m_ipv4Address = QString();
238 m_ipv6Address = QString();
241 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Stopping SSDP"));
244 if (m_ipv4MulticastSocket)
245 m_ipv4MulticastSocket->close();
246 if (m_ipv6LinkMulticastSocket)
247 m_ipv6LinkMulticastSocket->close();
248 if (m_ipv4UnicastSocket)
249 m_ipv4UnicastSocket->close();
250 if (m_ipv6UnicastSocket)
251 m_ipv6UnicastSocket->close();
253 delete m_ipv4MulticastSocket;
254 delete m_ipv6LinkMulticastSocket;
255 delete m_ipv4UnicastSocket;
256 delete m_ipv6UnicastSocket;
258 m_ipv4MulticastSocket =
nullptr;
259 m_ipv6LinkMulticastSocket =
nullptr;
260 m_ipv4UnicastSocket =
nullptr;
261 m_ipv6UnicastSocket =
nullptr;
275 QMetaObject::invokeMethod(
gSSDP,
"SearchPriv", Qt::AutoConnection);
288 QMetaObject::invokeMethod(
gSSDP,
"CancelSearchPriv", Qt::AutoConnection);
300 QMetaObject::invokeMethod(
gSSDP,
"AnnouncePriv", Qt::AutoConnection);
311 QMetaObject::invokeMethod(
gSSDP,
"CancelAnnouncePriv", Qt::AutoConnection);
330 #define IPv4 QStringLiteral("IPv4") 331 #define IPv6 QStringLiteral("IPv6") 332 QUdpSocket* TorcSSDP::CreateSearchSocket(
const QHostAddress &HostAddress,
TorcSSDP *Owner)
334 QUdpSocket *socket =
new QUdpSocket();
335 if (socket->bind(HostAddress, 1900, QUdpSocket::ShareAddress))
337 socket->setSocketOption(QAbstractSocket::MulticastTtlOption, 4);
338 socket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, 1);
339 QObject::connect(socket, &QUdpSocket::readyRead, Owner, &
TorcSSDP::Read);
340 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"%1 SSDP unicast socket %2:%3")
341 .arg(HostAddress.protocol() == QAbstractSocket::IPv6Protocol ?
IPv6 :
IPv4,
342 socket->localAddress().toString(), QString::number(socket->localPort())));
346 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to bind %1 SSDP search socket with address %3 (%2)")
347 .arg(HostAddress.protocol() == QAbstractSocket::IPv6Protocol ?
IPv6 :
IPv4, socket->errorString(), HostAddress.toString()));
352 QUdpSocket* TorcSSDP::CreateMulticastSocket(
const QHostAddress &HostAddress,
TorcSSDP *Owner,
const QNetworkInterface &Interface)
354 QUdpSocket *socket =
new QUdpSocket();
357 if (socket->joinMulticastGroup(HostAddress, Interface))
359 QObject::connect(socket, &QUdpSocket::readyRead, Owner, &
TorcSSDP::Read);
360 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"%1 SSDP multicast socket %2:%3")
361 .arg(HostAddress.protocol() == QAbstractSocket::IPv6Protocol ?
IPv6 :
IPv4,
362 socket->localAddress().toString(),
363 QString::number(socket->localPort())));
367 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to join %1 multicast group (%2)")
368 .arg(HostAddress.protocol() == QAbstractSocket::IPv6Protocol ?
IPv6 :
IPv4, socket->errorString()));
372 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to bind %1 SSDP multicast socket with address %3 (%2)")
373 .arg(HostAddress.protocol() == QAbstractSocket::IPv6Protocol ?
IPv6 :
IPv4, socket->errorString(), HostAddress.toString()));
386 static const QString search(QStringLiteral(
387 "M-SEARCH * HTTP/1.1\r\n" 389 "MAN: \"ssdp:discover\"\r\n" 393 if (!m_ipv4Address.isEmpty() && m_ipv4MulticastSocket && m_ipv4MulticastSocket->isValid() && m_ipv4MulticastSocket->state() == QAbstractSocket::BoundState)
395 m_ipv4MulticastSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 2);
398 if (sent != ipv4search.size())
400 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Error sending IPv4 search request (%1)").arg(m_ipv4MulticastSocket->errorString()));
402 else if (!m_searchDebugged)
404 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Sent IPv4 SSDP global search request"));
405 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Will send further IPv4 requests every 5 minutes"));
409 if (m_searchOptions.
ipv6 && !m_ipv6Address.isEmpty() && m_ipv6LinkMulticastSocket && m_ipv6LinkMulticastSocket->isValid() && m_ipv6LinkMulticastSocket->state() == QAbstractSocket::BoundState)
411 m_ipv6LinkMulticastSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 2);
414 if (sent != ipv6search.size())
416 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Error sending IPv6 search request (%1)").arg(m_ipv6LinkMulticastSocket->errorString()));
418 else if (!m_searchDebugged)
420 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Sent IPv6 SSDP global search request"));
421 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Will send further IPv6 requests every 5 minutes"));
424 m_searchDebugged =
true;
451 void TorcSSDP::StartAnnounce(
void)
457 if (m_announceOptions == newoptions)
459 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP announce options unchanged - not restarting"));
465 m_announceOptions = newoptions;
468 m_firstAnnounceTimer = startTimer(100, Qt::CoarseTimer);
470 m_secondAnnounceTimer = startTimer(500, Qt::CoarseTimer);
474 void TorcSSDP::StopAnnounce(
void)
476 if (m_announceOptions.
port)
479 if (m_firstAnnounceTimer)
481 killTimer(m_firstAnnounceTimer);
482 m_firstAnnounceTimer = 0;
485 if (m_secondAnnounceTimer)
487 killTimer(m_secondAnnounceTimer);
488 m_secondAnnounceTimer = 0;
492 if (m_announceOptions.
port)
495 if (m_announceOptions.
ipv6)
499 m_announceOptions.
port = 0;
504 if (!m_announceOptions.
port)
506 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Cannot send announce - no port set"));
510 QUdpSocket *socket = IsIPv6 ? m_ipv6LinkMulticastSocket : m_ipv4MulticastSocket;
512 if ((IsIPv6 && m_ipv6Address.isEmpty()) || (!IsIPv6 && m_ipv4Address.isEmpty()))
515 if (!socket || !socket->isValid() || socket->state() != QAbstractSocket::BoundState)
524 static const QString notify(QStringLiteral(
525 "NOTIFY * HTTP/1.1\r\n" 527 "CACHE-CONTROL: max-age=1800\r\n" 528 "LOCATION: http%2://%3:%4/upnp/description\r\n" 534 "APIVERSION: %10\r\n" 536 "PRIORITY: %12\r\n%13\r\n"));
539 QHostAddress address = IsIPv6 ? m_ipv6LinkGroupBaseAddress : m_ipv4GroupAddress;
541 QString alive = Alive ? QStringLiteral(
"alive") :QStringLiteral(
"byebye");
542 QString url = IsIPv6 ? m_ipv6Address : m_ipv4Address;
543 QString secure = m_announceOptions.
secure ? QStringLiteral(
"s") : QStringLiteral(
"");
544 QString secure2 = m_announceOptions.
secure ? QStringLiteral(
"SECURE: yes\r\n") : QStringLiteral(
"");
549 QByteArray packet1 = QString(notify).arg(ip, secure, url).arg(m_announceOptions.
port)
550 .arg(QStringLiteral(
"upnp:rootdevice"), alive, m_serverString, QStringLiteral(
"%1::upnp::rootdevice").arg(uuid), name, apiversion, starttime, priority, secure2).toLocal8Bit();
551 QByteArray packet2 = QString(notify).arg(ip, secure, url).arg(m_announceOptions.
port)
552 .arg(uuid, alive, m_serverString, uuid, name, apiversion, starttime, priority, secure2).toLocal8Bit();
553 QByteArray packet3 = QString(notify).arg(ip, secure, url).arg(m_announceOptions.
port)
556 socket->setSocketOption(QAbstractSocket::MulticastTtlOption, 4);
560 if (sent != (packet1.size() + packet2.size() + packet3.size()))
561 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Error sending 3 %1 SSDP NOTIFYs (%2)").arg(IsIPv6 ?
IPv6 :
IPv4, m_ipv4MulticastSocket->errorString()));
563 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Sent 3 %1 SSDP NOTIFYs ").arg(IsIPv6 ?
IPv6 : IPv4));
583 else if (Event->type() == QEvent::Timer)
585 QTimerEvent*
event =
dynamic_cast<QTimerEvent*
>(Event);
593 if (
event->timerId() == m_firstSearchTimer)
595 killTimer(m_firstSearchTimer);
597 m_firstSearchTimer = startTimer(5 * 60 * 1000);
599 else if (
event->timerId() == m_secondSearchTimer)
601 killTimer(m_secondSearchTimer);
603 m_secondSearchTimer = startTimer(5 * 60 * 1000);
607 else if (
event->timerId() == m_firstAnnounceTimer)
609 killTimer(m_firstAnnounceTimer);
611 if (m_announceOptions.
ipv6)
613 m_firstAnnounceTimer = startTimer(10 * 60 * 1000);
615 else if (
event->timerId() == m_secondAnnounceTimer)
617 killTimer(m_secondAnnounceTimer);
619 if (m_announceOptions.
ipv6)
621 m_secondAnnounceTimer = startTimer(10 * 60 * 1000);
624 else if (
event->timerId() == m_refreshTimer)
631 return QObject::event(Event);
636 QUdpSocket *socket =
dynamic_cast<QUdpSocket*
>(sender());
637 while (socket && socket->hasPendingDatagrams())
639 QHostAddress address;
642 datagram.resize(socket->pendingDatagramSize());
643 socket->readDatagram(datagram.data(), datagram.size(), &address, &port);
647 if ((m_ipv4MulticastSocket && (port == m_ipv4MulticastSocket->localPort())) || (m_ipv6LinkMulticastSocket && (port == m_ipv6LinkMulticastSocket->localPort())))
648 if (m_addressess.contains(address))
652 QString data(datagram);
653 LOG(VB_NETWORK, LOG_DEBUG, QStringLiteral(
"Raw datagram:\r\n%1").arg(data));
656 QStringList lines = data.split(QStringLiteral(
"\r\n"), QString::SkipEmptyParts);
660 QMap<QString,QString> headers;
663 QString type = lines.takeFirst().trimmed();
666 while (!lines.isEmpty())
668 QString header = lines.takeFirst();
669 int index = header.indexOf(
':');
671 headers.insert(header.left(index).trimmed().toLower(), header.mid(index + 1).trimmed());
674 if (type.startsWith(QStringLiteral(
"HTTP"), Qt::CaseInsensitive))
677 if (headers.contains(QStringLiteral(
"cache-control")))
679 qint64 expires = GetExpiryTime(headers.value(QStringLiteral(
"cache-control")));
681 ProcessDevice(headers, expires,
true);
684 else if (type.startsWith(QStringLiteral(
"NOTIFY"), Qt::CaseInsensitive))
687 if (headers.contains(QStringLiteral(
"nts")))
689 bool add = headers.value(QStringLiteral(
"nts")) != QStringLiteral(
"ssdp:byebye");
693 if (headers.contains(QStringLiteral(
"cache-control")))
695 qint64 expires = GetExpiryTime(headers.value(QStringLiteral(
"cache-control")));
697 ProcessDevice(headers, expires,
true);
702 ProcessDevice(headers, 1,
false);
706 else if (type.startsWith(QStringLiteral(
"M-SEARCH"), Qt::CaseInsensitive))
709 if (headers.value(QStringLiteral(
"man")).contains(QStringLiteral(
"ssdp:discover"), Qt::CaseInsensitive))
714 int mx = qBound(1, headers.value(QStringLiteral(
"mx")).toInt(), 5);
718 QString st = headers.value(QStringLiteral(
"st"));
719 if (st == QStringLiteral(
"ssdp:all"))
725 else if (st == QStringLiteral(
"upnp:rootdevice"))
729 else if (st.startsWith(QStringLiteral(
"uuid:")))
741 qint64 firstrandom = qrand() % ((mx * 1000) >> 1);
742 qint64 secondrandom = qrand() % (mx * 1000);
743 m_responseQueue.insertMulti(QDateTime::currentMSecsSinceEpoch() + firstrandom,
TorcSSDPSearchResponse(address, types, port));
744 m_responseQueue.insertMulti(QDateTime::currentMSecsSinceEpoch() + secondrandom,
TorcSSDPSearchResponse(address, types, port));
747 firstrandom = qMin(firstrandom, secondrandom);
748 if (firstrandom < m_responseTimer.remainingTime())
749 m_responseTimer.start(firstrandom + 1);
754 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Unknown MAN value in search request"));
762 qint64 timenow = QDateTime::currentMSecsSinceEpoch();
764 bool finished =
false;
765 while (!finished && !m_responseQueue.isEmpty())
767 qint64 time = m_responseQueue.firstKey();
771 ProcessResponse(response);
779 qint64 nextcheck = 5000;
780 if (!m_responseQueue.isEmpty())
781 nextcheck = m_responseQueue.firstKey() - timenow;
782 m_responseTimer.start(nextcheck);
787 if (!m_announceOptions.
port)
789 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Cannot send response to %1 - no port set, not advertising!")
801 static const QString dateformat(QStringLiteral(
"ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
802 static const QString reply(QStringLiteral(
803 "HTTP/1.1 200 OK\r\n" 804 "CACHE-CONTROL: max-age=1800\r\n" 807 "LOCATION: http%2://%3:%4/upnp/description\r\n" 814 "PRIORITY: %11\r\n%12\r\n"));
816 QString date = QDateTime::currentDateTime().toString(dateformat);
818 bool ipv6 = Response.
m_responseAddress.protocol() == QAbstractSocket::IPv6Protocol;
819 if ((ipv6 && m_ipv6Address.isEmpty()) || (!ipv6 && m_ipv4Address.isEmpty()))
821 QUdpSocket *socket = ipv6 ? m_ipv6UnicastSocket : m_ipv4UnicastSocket;
823 QString secure = m_announceOptions.
secure ? QStringLiteral(
"s") : QStringLiteral(
"");
824 QString secure2 = m_announceOptions.
secure ? QStringLiteral(
"SECURE: yes\r\n") : QStringLiteral(
"");
833 packet = QString(reply).arg(date, secure, ipv6 ? m_ipv6Address : m_ipv4Address).arg(m_announceOptions.
port)
834 .arg(m_serverString, QStringLiteral(
"upnp:rootdevice"), QStringLiteral(
"%1::upnp:rootdevice").arg(uuid), name, apiversion, starttime, priority, secure2).toLocal8Bit();
836 if (sent != packet.size())
837 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Error sending SSDP root search response to %1 (%2)").arg(raddress, socket->errorString()));
839 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Sent SSDP root search response to %1").arg(raddress));
844 packet = QString(reply).arg(date, secure, ipv6 ? m_ipv6Address : m_ipv4Address).arg(m_announceOptions.
port)
845 .arg(m_serverString, uuid, uuid, name, apiversion, starttime, priority, secure2).toLocal8Bit();
847 if (sent != packet.size())
848 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Error sending SSDP UUID search response to %1 (%2)").arg(raddress, socket->errorString()));
850 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Sent SSDP UUID search response to %1").arg(raddress));
855 packet = QString(reply).arg(date, secure, ipv6 ? m_ipv6Address : m_ipv4Address).arg(m_announceOptions.
port)
858 if (sent != packet.size())
859 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Error sending SSDP Device search response to %1 (%2)").arg(raddress, socket->errorString()));
861 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Sent SSDP UUID Device response to %1").arg(raddress));
865 void TorcSSDP::Refresh(
void)
870 qint64 now = QDateTime::currentMSecsSinceEpoch();
874 QVector<TorcUPNPDescription> removed;
875 QMutableHashIterator<QString,TorcUPNPDescription> it(m_discoveredDevices);
879 if (it.value().GetExpiry() < now)
881 removed << it.value();
887 QVector<TorcUPNPDescription>::const_iterator it2 = removed.constBegin();
888 for ( ; it2 != removed.constEnd(); ++it2)
891 data.insert(
TORC_USN, (*it2).GetUSN());
897 LOG(VB_NETWORK, LOG_INFO, QStringLiteral(
"Removed %1 stale cache entries").arg(removed.size()));
900 void TorcSSDP::ProcessDevice(
const QMap<QString,QString> &Headers, qint64 Expires,
bool Add)
902 if (!Headers.contains(
TORC_USN) || !Headers.contains(QStringLiteral(
"location")) || Expires < 1)
905 QString USN = Headers.value(
TORC_USN);
906 QString location = Headers.value(QStringLiteral(
"location"));
913 if (m_discoveredDevices.contains(USN))
919 (void)m_discoveredDevices.remove(USN);
924 (void)m_discoveredDevices.remove(USN);
949 void TorcSSDP::StartSearch(
void)
955 if (m_searching && newoptions == m_searchOptions)
957 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP search options unchanged - not restarting"));
964 m_searchOptions = newoptions;
967 m_firstSearchTimer = startTimer(1, Qt::CoarseTimer);
969 m_secondSearchTimer = startTimer(5000, Qt::CoarseTimer);
971 m_searchDebugged =
false;
975 void TorcSSDP::StopSearch(
void)
980 if (m_firstSearchTimer)
982 killTimer(m_firstSearchTimer);
983 m_firstSearchTimer = 0;
986 if (m_secondSearchTimer)
988 killTimer(m_secondSearchTimer);
989 m_secondSearchTimer = 0;
995 qint64 TorcSSDP::GetExpiryTime(
const QString &Expires)
998 int index = Expires.indexOf(QStringLiteral(
"max-age"), 0, Qt::CaseInsensitive);
1001 int index2 = Expires.indexOf(
'=', index);
1004 int seconds = Expires.midRef(index2 + 1).toInt();
1005 return QDateTime::currentMSecsSinceEpoch() + 1000 * seconds;
1018 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP thread starting"));
1025 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP thread stopping"));
void SendAnnounce(bool IsIPv6, bool Alive)
#define TORC_IPV4_UDP_MULTICAST_ADDR
TorcLocalContext * gLocalContext
static QMutex * gSSDPLock
QHostAddress m_responseAddress
#define TORC_IPV6_UDP_MULTICAST_URL
void CancelAnnouncePriv(void)
static void CancelAnnounce(void)
static bool IsGlobal(const QHostAddress &Address)
QString GetUuid(void) const
static bool IsAvailable(void)
#define TORC_IPV4_UDP_MULTICAST_URL
static TorcSSDP * Create(bool Destroy=false)
ResponseTypes m_responseTypes
static void CancelSearch(void)
Stop searching for a UPnP device type.
static void Announce(TorcHTTPServer::Status Options)
Publish the device.
The public class for handling Simple Service Discovery Protocol searches and announcements.
static bool IsLinkLocal(const QHostAddress &Address)
static TorcHTTPServer::Status gAnnounceOptions
static Type TorcEventType
Register TorcEventType with QEvent.
static bool gAnnounceEnabled
static void Search(TorcHTTPServer::Status Options)
Search for Torc UPnP device type.
void Finish(void) override
static bool IsExternal(const QHostAddress &Address)
Returns true if the address is accessible from other devices.
#define TORC_ROOT_UPNP_DEVICE
static bool gSearchEnabled
A general purpose event object.
#define TORC_IPV6_UDP_MULTICAST_ADDR2
static QString GetVersion(void)
#define LOG(_MASK_, _LEVEL_, _STRING_)
static TorcHTTPServer::Status gSearchOptions
void AddObserver(QObject *Observer)
brief Register the given object to receive events.
void Notify(const TorcEvent &Event)
Brief Send the given event to each registered listener/observer.
bool event(QEvent *Event) override
void Start(void) override
qint64 GetStartTime(void)
void ProcessResponses(void)
void RemoveObserver(QObject *Observer)
brief Deregister the given object.
static QString ServerDescription(void)
A Torc specific wrapper around QThread.
TorcSSDPSearchResponse(void)
#define TORC_SSDP_UDP_MULTICAST_PORT
static bool IsLocal(const QHostAddress &Address)
void CancelSearchPriv(void)