26 #include <QCoreApplication> 47 QReadWriteLock*
gHandlersLock =
new QReadWriteLock(QReadWriteLock::Recursive);
76 foreach (
const QString &signature, Handler->
Signature().split(
','))
80 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Handler '%1' for '%2' already registered - ignoring").arg(Handler->
Name(), signature));
82 else if (!signature.isEmpty())
84 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Added handler '%1' for %2").arg(Handler->
Name(), signature));
100 bool changed =
false;
108 foreach (
const QString &signature, Handler->
Signature().split(
','))
110 QMap<QString,TorcHTTPHandler*>::iterator it =
gHandlers.find(signature);
113 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Removing handler '%1'").arg(it.key()));
136 QString path = Request.
GetPath();
138 QMap<QString,TorcHTTPHandler*>::const_iterator it =
gHandlers.constFind(path);
142 (*it)->ProcessHTTPRequest(PeerAddress, PeerPort, LocalAddress, LocalPort, Request);
148 for ( ; it !=
gHandlers.constEnd(); ++it)
150 if ((*it)->GetRecursive() && path.startsWith(it.key()))
152 (*it)->ProcessHTTPRequest(PeerAddress, PeerPort, LocalAddress, LocalPort, Request);
176 QString path = QStringLiteral(
"/");
177 int index = Method.lastIndexOf(path);
179 path = Method.left(index + 1).trimmed();
185 QMap<QString,TorcHTTPHandler*>::const_iterator it =
gHandlers.constFind(path);
187 return (*it)->ProcessRequest(Method, Parameters, Connection, Authenticated);
190 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Method '%1' not found in services").arg(Method));
194 error.insert(QStringLiteral(
"code"), -32601);
195 error.insert(QStringLiteral(
"message"), QStringLiteral(
"Method not found"));
196 result.insert(QStringLiteral(
"error"), error);
206 QMap<QString,TorcHTTPHandler*>::const_iterator it =
gHandlers.constBegin();
207 for ( ; it !=
gHandlers.constEnd(); ++it)
213 map.insert(QStringLiteral(
"path"), it.key());
214 map.insert(QStringLiteral(
"name"), service->
GetUIName());
215 result.insert(it.value()->Name(), map);
225 QMap<QString,TorcHTTPHandler*>::const_iterator it =
gHandlers.constBegin();
226 for ( ; it !=
gHandlers.constEnd(); ++it)
228 if (it.value()->Name() == Service)
236 return QVariantMap();
275 m_serverSettings(nullptr),
279 m_upnpSearch(nullptr),
280 m_upnpAdvertise(nullptr),
282 m_bonjourSearch(nullptr),
283 m_bonjourAdvert(nullptr),
287 m_defaultHandler(QStringLiteral(
""),
TORC_TORC),
288 m_servicesHandler(this),
292 m_ssdpThread(nullptr),
293 m_bonjourBrowserReference(0),
294 m_httpBonjourReference(0),
295 m_torcBonjourReference(0),
302 bool root = !geteuid();
305 m_port->
SetRange(root ? 1 : 1024, 65535, 1);
308 m_port->
SetHelpText(tr(
"The port the server will listen on for incoming connections"));
312 m_secure->
SetHelpText(tr(
"Use encrypted (SSL/TLS) connections to the server"));
323 m_upnpSearch->
SetHelpText(tr(
"Use UPnP to search for other devices"));
330 m_upnpAdvertise->
SetHelpText(tr(
"Use UPnP to advertise this device"));
349 m_bonjourSearch->
SetHelpText(tr(
"Use Bonjour to search for other devices"));
359 m_bonjourAdvert->
SetHelpText(tr(
"Use Bonjour to advertise this device"));
376 static bool initialised =
false;
380 gPlatform = QStringLiteral(
"%1, Version: %2 ").arg(
TORC_TORC, QStringLiteral(GIT_VERSION));
382 gPlatform += QStringLiteral(
"(Windows %1.%2)").arg(LOBYTE(LOWORD(GetVersion()))).arg(HIBYTE(LOWORD(GetVersion())));
384 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) 385 gPlatform += QStringLiteral(
"(%1 %2)").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion());
387 gPlatform += QStringLiteral(
"(OldTorc OldVersion)");
409 m_bonjourAdvert->
Remove();
411 m_bonjourAdvert =
nullptr;
416 m_bonjourSearch->
Remove();
418 m_bonjourSearch =
nullptr;
437 m_upnpAdvertise->
Remove();
439 m_upnpAdvertise =
nullptr;
446 m_upnpSearch =
nullptr;
470 if (m_serverSettings)
472 m_serverSettings->
Remove();
474 m_serverSettings =
nullptr;
519 if (Request.
Queries().contains(QStringLiteral(
"accesstoken")) && Request.
GetMethod().isEmpty())
521 QString token = Request.
Queries().value(QStringLiteral(
"accesstoken"));
537 if (!authenticate && !ForceCheck)
572 bool origin =
gOriginWhitelist.contains(Request.
Headers().value(QStringLiteral(
"Origin")), Qt::CaseInsensitive);
575 if (Request.
Headers().contains(QStringLiteral(
"Origin")) && (origin || Request.
GetAllowCORS()))
577 Request.
SetResponseHeader(QStringLiteral(
"Access-Control-Allow-Origin"), Request.
Headers().value(QStringLiteral(
"Origin")));
578 if (Request.
Headers().contains(QStringLiteral(
"Access-Control-Allow-Credentials")))
579 Request.
SetResponseHeader(QStringLiteral(
"Access-Control-Allow-Credentials"), QStringLiteral(
"true"));
580 if (Request.
Headers().contains(QStringLiteral(
"Access-Control-Request-Headers")))
581 Request.
SetResponseHeader(QStringLiteral(
"Access-Control-Request-Headers"), QStringLiteral(
"Origin, X-Requested-With, Content-Type, Accept, Range"));
582 if (Request.
Headers().contains(QStringLiteral(
"Access-Control-Request-Method")))
584 Request.
SetResponseHeader(QStringLiteral(
"Access-Control-Max-Age"), QString::number(86400));
590 if (!Request.
Headers().contains(QStringLiteral(
"Authorization")))
593 QString header = Request.
Headers().value(QStringLiteral(
"Authorization"));
596 if (header.startsWith(QStringLiteral(
"Digest"), Qt::CaseInsensitive))
628 QString protocol = Status.
secure ? QStringLiteral(
"https://") : QStringLiteral(
"http://");
634 QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
635 for (
int i = 0; i < addresses.size(); ++i)
640 foreach (
const QString &host, hosts)
642 QString newhost = QStringLiteral(
"%1%2:%3 ").arg(protocol, host).arg(Status.
port);
650 void TorcHTTPServer::StartBonjour(
void)
652 if (!m_bonjour->
GetValue().toBool())
657 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Not using Bonjour while IPv6 is disabled"));
662 if (!m_bonjourBrowserReference && m_bonjourSearch->
GetValue().toBool())
665 if ((!m_httpBonjourReference || !m_torcBonjourReference) && m_bonjourAdvert->
GetValue().toBool())
667 int port = m_port->
GetValue().toInt();
668 QMap<QByteArray,QByteArray> map;
678 if (!m_httpBonjourReference)
679 m_httpBonjourReference =
TorcBonjour::Instance()->
Register(port, m_secure->
GetValue().toBool() ? QByteArrayLiteral(
"_https._tcp") : QByteArrayLiteral(
"_http._tcp"), name.toLocal8Bit().constData(), map);
681 if (!m_torcBonjourReference)
686 void TorcHTTPServer::StopBonjour(
void)
692 void TorcHTTPServer::StopBonjourBrowse(
void)
694 if (m_bonjourBrowserReference)
697 m_bonjourBrowserReference = 0;
701 void TorcHTTPServer::StopBonjourAdvert(
void)
703 if (m_httpBonjourReference)
706 m_httpBonjourReference = 0;
709 if (m_torcBonjourReference)
712 m_torcBonjourReference = 0;
716 #define en QStringLiteral("en") 717 #define dis QStringLiteral("dis") 721 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Bonjour %1abled").arg(Bonjour ?
en :
dis));
731 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Bonjour advertisement %1abled").arg(Advert ?
en :
dis));
740 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Bonjour search %1abled").arg(Search ?
en :
dis));
754 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"IPv6 %1abled - restarting").arg(IPv6 ?
en :
dis));
762 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"HTTP server alreay listening - closing"));
766 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSL is %1abled").arg(m_secure->
GetValue().toBool() ?
en :
dis));
767 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"IPv6 is %1abled").arg(m_ipv6->
GetValue().toBool() ?
en :
dis));
768 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Bonjour is %1abled").arg(m_bonjour->
GetValue().toBool() ?
en :
dis));
769 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Bonjour search is %1abled").arg(m_bonjourSearch->
GetValue().toBool() ?
en :
dis));
770 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Bonjour advertisement is %1abled").arg(m_bonjourAdvert->
GetValue().toBool() ?
en :
dis));
771 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP is %1abled").arg(m_upnp->
GetValue().toBool() ?
en :
dis));
772 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP search is %1abled").arg(m_upnpSearch->
GetValue().toBool() ?
en :
dis));
773 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP advertisement is %1abled").arg(m_upnpAdvertise->
GetValue().toBool() ?
en :
dis));
774 int port = m_port->
GetValue().toInt();
775 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Attempting to listen on port %1").arg(port));
778 if (!m_listener->isListening() && !m_listener->
Listen(m_ipv6->
GetValue().toBool() ? QHostAddress::Any : QHostAddress::AnyIPv4))
780 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to open web server port"));
786 if (port != m_listener->serverPort())
789 port = m_listener->serverPort();
790 m_port->
SetValue(QVariant((
int)port));
798 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Web server listening for %1secure connections on port %2").arg(m_secure->
GetValue().toBool() ? QStringLiteral(
"") : QStringLiteral(
"in")).arg(port));
805 QString host = QHostInfo::localHostName();
807 host = tr(
"Unknown");
808 return QStringLiteral(
"%1@%2").arg(QCoreApplication::applicationName(), host);
823 m_listener =
nullptr;
825 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Webserver closed"));
835 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Port changed to %1 - restarting").arg(Port));
846 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Secure %1abled - restarting").arg(Secure ?
en :
dis));
850 void TorcHTTPServer::StartUPnP(
void)
854 bool search = m_upnpSearch->
GetValue().toBool();
855 bool advert = m_upnpAdvertise->
GetValue().toBool();
857 if (!m_ssdpThread && (search || advert))
860 m_ssdpThread->start();
870 void TorcHTTPServer::StopUPnP(
void)
877 m_ssdpThread->quit();
878 m_ssdpThread->wait();
880 m_ssdpThread =
nullptr;
886 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP %1abled").arg(UPnP ?
en :
dis));
896 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP advertisement %1abled").arg(Advert ?
en :
dis));
903 if (m_upnpSearch->
GetValue().toBool())
912 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"SSDP search %1abled").arg(Search ?
en :
dis));
919 if (m_upnpAdvertise->
GetValue().toBool())
951 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"User name/credentials changed - restarting webserver"));
961 return QObject::event(Event);
980 qRegisterMetaType<TorcHTTPRequest*>();
981 qRegisterMetaType<TorcHTTPService*>();
982 qRegisterMetaType<QTcpSocket*>();
983 qRegisterMetaType<QHostAddress>();
988 Strings.insert(QStringLiteral(
"ServerApplication"), QCoreApplication::applicationName());
989 Strings.insert(QStringLiteral(
"SocketNotConnected"), QCoreApplication::translate(
"TorcHTTPServer",
"Not connected"));
990 Strings.insert(QStringLiteral(
"SocketConnecting"), QCoreApplication::translate(
"TorcHTTPServer",
"Connecting"));
991 Strings.insert(QStringLiteral(
"SocketConnected"), QCoreApplication::translate(
"TorcHTTPServer",
"Connected"));
992 Strings.insert(QStringLiteral(
"SocketReady"), QCoreApplication::translate(
"TorcHTTPServer",
"Ready"));
993 Strings.insert(QStringLiteral(
"SocketDisconnecting"), QCoreApplication::translate(
"TorcHTTPServer",
"Disconnecting"));
994 Strings.insert(QStringLiteral(
"ConnectedTo"), QCoreApplication::translate(
"TorcHTTPServer",
"Connected to"));
995 Strings.insert(QStringLiteral(
"ConnectedSecureTo"), QCoreApplication::translate(
"TorcHTTPServer",
"Connected securely to"));
996 Strings.insert(QStringLiteral(
"ConnectTo"), QCoreApplication::translate(
"TorcHTTPServer",
"Connect to"));
998 Strings.insert(QStringLiteral(
"SocketReconnectAfterMs"), 10000);
1015 if (TorcHTTPServer::gWebServer)
1018 TorcHTTPServer::gWebServer =
nullptr;
bool Listen(const QHostAddress &Address, int Port=0)
bool operator==(Status Other) const
bool GetAllowCORS(void) const
A class to encapsulate an incoming HTTP request.
void IncomingConnection(qintptr SocketDescriptor, bool Secure)
HTTPRequestType GetHTTPRequestType(void) const
void SetHelpText(const QString &HelpText)
QReadWriteLock * gHandlersLock
void UPnPAdvertChanged(bool Advert)
TorcLocalContext * gLocalContext
High level group of related settings.
Base HTTP response handler class.
static void Authorise(const QString &Host, TorcHTTPRequest &Request, bool ForceCheck)
Ensures remote user is authorised to access this request.
void Deregister(quint32 Reference)
Cancel a Bonjour service registration or browse request.
QString Signature(void) const
#define TORC_PORT_SERVICE
A wrapper around a database setting.
#define TORC_APIVERSION_B
static QMutex gWebServerLock
A factory class for automatically running objects outside of the main loop.
int GetEvent(void)
Return the Torc action associated with this event.
static void ProcessDigestAuth(TorcHTTPRequest &Request, bool Check=false)
A server nonce for Digest Access Authentication.
static void AddAuthenticationHeader(TorcHTTPRequest &Request)
#define TORC_SERVICES_DIR
TorcWebSocketThread * TakeSocketPriv(TorcWebSocketThread *Socket)
bool event(QEvent *Event) overridefinal
QMap< QString, TorcHTTPHandler * > gHandlers
static QStringList GetHostNames(void)
Retrieve the list of currently identified host names.
static void CancelAnnounce(void)
static void HandleRequest(const QString &PeerAddress, int PeerPort, const QString &LocalAddress, int LocalPort, TorcHTTPRequest &Request)
QString GetUuid(void) const
quint32 Browse(const QByteArray &Type, quint32 Reference=0)
Search for a service advertised via Bonjour.
static QString IPAddressToLiteral(const QHostAddress &Address, int Port, bool UseLocalhost=true)
Convert an IP address to a string literal.
void BonjourSearchChanged(bool Search)
void GetStrings(QVariantMap &Strings)
static void ValidateOrigin(TorcHTTPRequest &Request)
Check the Origin header for validity and respond appropriately.
void BonjourAdvertChanged(bool Advert)
virtual bool DownRef(void)
void HandlersChanged(void)
HTTPAuthorisation IsAuthorised(void) const
virtual QString GetUIName(void)
#define TORC_ADMIN_HIGH_PRIORITY
void BonjourChanged(bool Bonjour)
void UPnPSearchChanged(bool Search)
static QVariantMap GetServiceHandlers(void)
static void RegisterHandler(TorcHTTPHandler *Handler)
void PortChanged(int Port)
QString GetPath(void) const
const QMap< QString, QString > & Headers(void) const
static void CancelSearch(void)
Stop searching for a UPnP device type.
static void Announce(TorcHTTPServer::Status Options)
Publish the device.
QString GetMethod(void) const
static Type TorcEventType
Register TorcEventType with QEvent.
static Status gWebServerStatus
static void Search(TorcHTTPServer::Status Options)
Search for Torc UPnP device type.
static Status GetStatus(void)
static void AuthenticateUser(TorcHTTPRequest &Request)
static QString gOriginWhitelist
static void TearDown(void)
Destroys the global TorcBonjour singleton.
quint32 Register(quint16 Port, const QByteArray &Type, const QByteArray &Name, const QMap< QByteArray, QByteArray > &TxtRecords, quint32 Reference=0)
void SetResponseHeader(const QString &Header, const QString &Value)
static void DeregisterHandler(TorcHTTPHandler *Handler)
static TorcBonjour * Instance(void)
Returns the global TorcBonjour singleton.
A general purpose event object.
QString gServicesDirectory(TORC_SERVICES_DIR)
bool SetValue(const QVariant &Value)
static QString GetVersion(void)
void SetStatus(HTTPStatus Status)
#define LOG(_MASK_, _LEVEL_, _STRING_)
static QString PlatformName(void)
void AddObserver(QObject *Observer)
brief Register the given object to receive events.
A factory class to register translatable strings for use with external interfaces/applications.
void SetActiveThreshold(int Threshold)
void SetRange(int Begin, int End, int Step)
void SetResponseType(HTTPResponseType Type)
static TorcHTTPServer * gWebServer
TorcSetting * GetRootSetting(void)
void IPv6Changed(bool IPv6)
void ValueChanged(int Value)
Wraps a TorcQThread around a TorcWebsocket.
QVariantMap GetServiceDetails(void)
Return a QVariantMap describing the services methods and properties.
qint64 GetStartTime(void)
void RemoveObserver(QObject *Observer)
brief Deregister the given object.
friend class TorcHTTPServerObject
void NewConnection(qintptr SocketDescriptor)
static QString ServerDescription(void)
TorcWebSocketThread * TakeSocket(TorcWebSocketThread *Socket)
void UPnPChanged(bool UPnP)
void SecureChanged(bool Secure)
static TorcWebSocketThread * TakeSocket(TorcWebSocketThread *Socket)
const QMap< QString, QString > & Queries(void) const
void Authorise(HTTPAuthorisation Authorisation)
static QVariantMap GetServiceDescription(const QString &Service)
static QReadWriteLock gOriginWhitelistLock
static QString AllowedToString(int Allowed)
virtual ~TorcHTTPServer()
static QString GetWebSocketToken(const QString &Host, const QString &Current=QString())
Retrieve an authentication token for the given request or validate a current token.
void SetActive(bool Value)