summaryrefslogtreecommitdiffstats
path: root/xbmc/network/NetworkServices.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/network/NetworkServices.cpp')
-rw-r--r--xbmc/network/NetworkServices.cpp1267
1 files changed, 1267 insertions, 0 deletions
diff --git a/xbmc/network/NetworkServices.cpp b/xbmc/network/NetworkServices.cpp
new file mode 100644
index 0000000..4009433
--- /dev/null
+++ b/xbmc/network/NetworkServices.cpp
@@ -0,0 +1,1267 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "NetworkServices.h"
+
+#include "ServiceBroker.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "interfaces/json-rpc/JSONRPC.h"
+#include "messaging/ApplicationMessenger.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "network/EventServer.h"
+#include "network/Network.h"
+#include "network/TCPServer.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/RssManager.h"
+#include "utils/SystemInfo.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <utility>
+
+#ifdef TARGET_LINUX
+#include "Util.h"
+#endif
+#ifdef HAS_AIRPLAY
+#include "network/AirPlayServer.h"
+#endif // HAS_AIRPLAY
+
+#ifdef HAS_AIRTUNES
+#include "network/AirTunesServer.h"
+#endif // HAS_AIRTUNES
+
+#ifdef HAS_ZEROCONF
+#include "network/Zeroconf.h"
+#endif // HAS_ZEROCONF
+
+#ifdef HAS_UPNP
+#include "network/upnp/UPnP.h"
+#endif // HAS_UPNP
+
+#ifdef HAS_WEB_SERVER
+#include "network/WebServer.h"
+#include "network/httprequesthandler/HTTPImageHandler.h"
+#include "network/httprequesthandler/HTTPImageTransformationHandler.h"
+#include "network/httprequesthandler/HTTPVfsHandler.h"
+#include "network/httprequesthandler/HTTPJsonRpcHandler.h"
+#ifdef HAS_WEB_INTERFACE
+#ifdef HAS_PYTHON
+#include "network/httprequesthandler/HTTPPythonHandler.h"
+#endif
+#include "network/httprequesthandler/HTTPWebinterfaceHandler.h"
+#include "network/httprequesthandler/HTTPWebinterfaceAddonsHandler.h"
+#endif // HAS_WEB_INTERFACE
+#endif // HAS_WEB_SERVER
+
+#if defined(HAS_FILESYSTEM_SMB)
+#if defined(TARGET_WINDOWS)
+#include "platform/win32/network/WSDiscoveryWin32.h"
+#else // defined(TARGET_POSIX)
+#include "platform/posix/filesystem/SMBWSDiscovery.h"
+#endif
+#endif
+
+#if defined(TARGET_DARWIN_OSX)
+#include "platform/darwin/osx/XBMCHelper.h"
+#endif
+
+using namespace KODI::MESSAGING;
+using namespace JSONRPC;
+using namespace EVENTSERVER;
+#ifdef HAS_UPNP
+using namespace UPNP;
+#endif // HAS_UPNP
+
+using KODI::MESSAGING::HELPERS::DialogResponse;
+
+CNetworkServices::CNetworkServices()
+#ifdef HAS_WEB_SERVER
+ : m_webserver(*new CWebServer),
+ m_httpImageHandler(*new CHTTPImageHandler),
+ m_httpImageTransformationHandler(*new CHTTPImageTransformationHandler),
+ m_httpVfsHandler(*new CHTTPVfsHandler),
+ m_httpJsonRpcHandler(*new CHTTPJsonRpcHandler)
+#ifdef HAS_WEB_INTERFACE
+#ifdef HAS_PYTHON
+ , m_httpPythonHandler(*new CHTTPPythonHandler)
+#endif
+ , m_httpWebinterfaceHandler(*new CHTTPWebinterfaceHandler)
+ , m_httpWebinterfaceAddonsHandler(*new CHTTPWebinterfaceAddonsHandler)
+#endif // HAS_WEB_INTERFACE
+#endif // HAS_WEB_SERVER
+{
+#ifdef HAS_WEB_SERVER
+ m_webserver.RegisterRequestHandler(&m_httpImageHandler);
+ m_webserver.RegisterRequestHandler(&m_httpImageTransformationHandler);
+ m_webserver.RegisterRequestHandler(&m_httpVfsHandler);
+ m_webserver.RegisterRequestHandler(&m_httpJsonRpcHandler);
+#ifdef HAS_WEB_INTERFACE
+#ifdef HAS_PYTHON
+ m_webserver.RegisterRequestHandler(&m_httpPythonHandler);
+#endif
+ m_webserver.RegisterRequestHandler(&m_httpWebinterfaceAddonsHandler);
+ m_webserver.RegisterRequestHandler(&m_httpWebinterfaceHandler);
+#endif // HAS_WEB_INTERFACE
+#endif // HAS_WEB_SERVER
+ std::set<std::string> settingSet{
+ CSettings::SETTING_SERVICES_WEBSERVER,
+ CSettings::SETTING_SERVICES_WEBSERVERPORT,
+ CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION,
+ CSettings::SETTING_SERVICES_WEBSERVERUSERNAME,
+ CSettings::SETTING_SERVICES_WEBSERVERPASSWORD,
+ CSettings::SETTING_SERVICES_WEBSERVERSSL,
+ CSettings::SETTING_SERVICES_ZEROCONF,
+ CSettings::SETTING_SERVICES_AIRPLAY,
+ CSettings::SETTING_SERVICES_AIRPLAYVOLUMECONTROL,
+ CSettings::SETTING_SERVICES_AIRPLAYVIDEOSUPPORT,
+ CSettings::SETTING_SERVICES_USEAIRPLAYPASSWORD,
+ CSettings::SETTING_SERVICES_AIRPLAYPASSWORD,
+ CSettings::SETTING_SERVICES_UPNP,
+ CSettings::SETTING_SERVICES_UPNPSERVER,
+ CSettings::SETTING_SERVICES_UPNPRENDERER,
+ CSettings::SETTING_SERVICES_UPNPCONTROLLER,
+ CSettings::SETTING_SERVICES_ESENABLED,
+ CSettings::SETTING_SERVICES_ESPORT,
+ CSettings::SETTING_SERVICES_ESALLINTERFACES,
+ CSettings::SETTING_SERVICES_ESINITIALDELAY,
+ CSettings::SETTING_SERVICES_ESCONTINUOUSDELAY,
+ CSettings::SETTING_SMB_WINSSERVER,
+ CSettings::SETTING_SMB_WORKGROUP,
+ CSettings::SETTING_SMB_MINPROTOCOL,
+ CSettings::SETTING_SMB_MAXPROTOCOL,
+ CSettings::SETTING_SMB_LEGACYSECURITY,
+ CSettings::SETTING_SERVICES_WSDISCOVERY,
+ };
+ m_settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ m_settings->GetSettingsManager()->RegisterCallback(this, settingSet);
+}
+
+CNetworkServices::~CNetworkServices()
+{
+ m_settings->GetSettingsManager()->UnregisterCallback(this);
+#ifdef HAS_WEB_SERVER
+ m_webserver.UnregisterRequestHandler(&m_httpImageHandler);
+ delete &m_httpImageHandler;
+ m_webserver.UnregisterRequestHandler(&m_httpImageTransformationHandler);
+ delete &m_httpImageTransformationHandler;
+ m_webserver.UnregisterRequestHandler(&m_httpVfsHandler);
+ delete &m_httpVfsHandler;
+ m_webserver.UnregisterRequestHandler(&m_httpJsonRpcHandler);
+ delete &m_httpJsonRpcHandler;
+ CJSONRPC::Cleanup();
+#ifdef HAS_WEB_INTERFACE
+#ifdef HAS_PYTHON
+ m_webserver.UnregisterRequestHandler(&m_httpPythonHandler);
+ delete &m_httpPythonHandler;
+#endif
+ m_webserver.UnregisterRequestHandler(&m_httpWebinterfaceAddonsHandler);
+ delete &m_httpWebinterfaceAddonsHandler;
+ m_webserver.UnregisterRequestHandler(&m_httpWebinterfaceHandler);
+ delete &m_httpWebinterfaceHandler;
+#endif // HAS_WEB_INTERFACE
+ delete &m_webserver;
+#endif // HAS_WEB_SERVER
+}
+
+bool CNetworkServices::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return false;
+
+ const std::string &settingId = setting->GetId();
+#ifdef HAS_WEB_SERVER
+ // Ask user to confirm disabling the authentication requirement, but not when the configuration
+ // would be invalid when authentication was enabled (meaning that the change was triggered
+ // automatically)
+ if (settingId == CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION &&
+ !std::static_pointer_cast<const CSettingBool>(setting)->GetValue() &&
+ (!m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVER) ||
+ (m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVER) &&
+ !m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERPASSWORD).empty())) &&
+ HELPERS::ShowYesNoDialogText(19098, 36634) != DialogResponse::CHOICE_YES)
+ {
+ // Leave it as-is
+ return false;
+ }
+
+ if (settingId == CSettings::SETTING_SERVICES_WEBSERVER ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERPORT ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERSSL ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERUSERNAME ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERPASSWORD)
+ {
+ if (IsWebserverRunning() && !StopWebserver())
+ return false;
+
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVER))
+ {
+ // Prevent changing to an invalid configuration
+ if ((settingId == CSettings::SETTING_SERVICES_WEBSERVER ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERPASSWORD) &&
+ m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION) &&
+ m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERPASSWORD).empty())
+ {
+ if (settingId == CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION)
+ {
+ HELPERS::ShowOKDialogText(CVariant{257}, CVariant{36636});
+ }
+ else
+ {
+ HELPERS::ShowOKDialogText(CVariant{257}, CVariant{36635});
+ }
+ return false;
+ }
+
+ // Ask for confirmation when enabling the web server
+ if (settingId == CSettings::SETTING_SERVICES_WEBSERVER &&
+ HELPERS::ShowYesNoDialogText(19098, 36632) != DialogResponse::CHOICE_YES)
+ {
+ // Revert change, do not start server
+ return false;
+ }
+
+ if (!StartWebserver())
+ {
+ HELPERS::ShowOKDialogText(CVariant{33101}, CVariant{33100});
+ return false;
+ }
+ }
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_ESPORT ||
+ settingId == CSettings::SETTING_SERVICES_WEBSERVERPORT)
+ return ValidatePort(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ else
+#endif // HAS_WEB_SERVER
+
+#ifdef HAS_ZEROCONF
+ if (settingId == CSettings::SETTING_SERVICES_ZEROCONF)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ return StartZeroconf();
+#ifdef HAS_AIRPLAY
+ else
+ {
+ // cannot disable
+ if (IsAirPlayServerRunning() || IsAirTunesServerRunning())
+ {
+ HELPERS::ShowOKDialogText(CVariant{1259}, CVariant{34303});
+ return false;
+ }
+
+ return StopZeroconf();
+ }
+#endif // HAS_AIRPLAY
+ }
+ else
+#endif // HAS_ZEROCONF
+
+#ifdef HAS_AIRPLAY
+ if (settingId == CSettings::SETTING_SERVICES_AIRPLAY)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ {
+#ifdef HAS_ZEROCONF
+ // AirPlay needs zeroconf
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_ZEROCONF))
+ {
+ HELPERS::ShowOKDialogText(CVariant{1273}, CVariant{34302});
+ return false;
+ }
+#endif //HAS_ZEROCONF
+
+ // note - airtunesserver has to start before airplay server (ios7 client detection bug)
+#ifdef HAS_AIRTUNES
+ if (!StartAirTunesServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{1274}, CVariant{33100});
+ return false;
+ }
+#endif //HAS_AIRTUNES
+
+ if (!StartAirPlayServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{1273}, CVariant{33100});
+ return false;
+ }
+ }
+ else
+ {
+ bool ret = true;
+#ifdef HAS_AIRTUNES
+ if (!StopAirTunesServer(true))
+ ret = false;
+#endif //HAS_AIRTUNES
+
+ if (!StopAirPlayServer(true))
+ ret = false;
+
+ if (!ret)
+ return false;
+ }
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_AIRPLAYVIDEOSUPPORT)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ {
+ if (!StartAirPlayServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{1273}, CVariant{33100});
+ return false;
+ }
+ }
+ else
+ {
+ if (!StopAirPlayServer(true))
+ return false;
+ }
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_AIRPLAYPASSWORD ||
+ settingId == CSettings::SETTING_SERVICES_USEAIRPLAYPASSWORD)
+ {
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_AIRPLAY))
+ return false;
+
+ if (!CAirPlayServer::SetCredentials(m_settings->GetBool(CSettings::SETTING_SERVICES_USEAIRPLAYPASSWORD),
+ m_settings->GetString(CSettings::SETTING_SERVICES_AIRPLAYPASSWORD)))
+ return false;
+ }
+ else
+#endif //HAS_AIRPLAY
+
+#ifdef HAS_UPNP
+ if (settingId == CSettings::SETTING_SERVICES_UPNP)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ {
+ StartUPnPClient();
+ StartUPnPController();
+ StartUPnPServer();
+ StartUPnPRenderer();
+ }
+ else
+ {
+ StopUPnPRenderer();
+ StopUPnPServer();
+ StopUPnPController();
+ StopUPnPClient();
+ }
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_UPNPSERVER)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ {
+ if (!StartUPnPServer())
+ return false;
+
+ // always stop and restart the client and controller if necessary
+ StopUPnPClient();
+ StopUPnPController();
+ StartUPnPClient();
+ StartUPnPController();
+ }
+ else
+ return StopUPnPServer();
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_UPNPRENDERER)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ return StartUPnPRenderer();
+ else
+ return StopUPnPRenderer();
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_UPNPCONTROLLER)
+ {
+ // always stop and restart
+ StopUPnPController();
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ return StartUPnPController();
+ }
+ else
+#endif // HAS_UPNP
+
+ if (settingId == CSettings::SETTING_SERVICES_ESENABLED)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ {
+ bool result = true;
+ if (!StartEventServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{33102}, CVariant{33100});
+ result = false;
+ }
+
+ if (!StartJSONRPCServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{33103}, CVariant{33100});
+ result = false;
+ }
+ return result;
+ }
+ else
+ {
+ bool result = true;
+ result = StopEventServer(true, true);
+ result &= StopJSONRPCServer(false);
+ return result;
+ }
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_ESPORT)
+ {
+ // restart eventserver without asking user
+ if (!StopEventServer(true, false))
+ return false;
+
+ if (!StartEventServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{33102}, CVariant{33100});
+ return false;
+ }
+
+#if defined(TARGET_DARWIN_OSX)
+ // reconfigure XBMCHelper for port changes
+ XBMCHelper::GetInstance().Configure();
+#endif // TARGET_DARWIN_OSX
+ }
+ else if (settingId == CSettings::SETTING_SERVICES_ESALLINTERFACES)
+ {
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_ESALLINTERFACES) &&
+ HELPERS::ShowYesNoDialogText(19098, 36633) != DialogResponse::CHOICE_YES)
+ {
+ // Revert change, do not start server
+ return false;
+ }
+
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED))
+ {
+ if (!StopEventServer(true, true))
+ return false;
+
+ if (!StartEventServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{33102}, CVariant{33100});
+ return false;
+ }
+ }
+
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED))
+ {
+ if (!StopJSONRPCServer(true))
+ return false;
+
+ if (!StartJSONRPCServer())
+ {
+ HELPERS::ShowOKDialogText(CVariant{33103}, CVariant{33100});
+ return false;
+ }
+ }
+ }
+
+ else if (settingId == CSettings::SETTING_SERVICES_ESINITIALDELAY ||
+ settingId == CSettings::SETTING_SERVICES_ESCONTINUOUSDELAY)
+ {
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED))
+ return RefreshEventServer();
+ }
+
+#if defined(HAS_FILESYSTEM_SMB)
+ else if (settingId == CSettings::SETTING_SERVICES_WSDISCOVERY)
+ {
+ if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue())
+ {
+ if (!StartWSDiscovery())
+ return false;
+ }
+ else
+ return StopWSDiscovery();
+ }
+#endif // HAS_FILESYSTEM_SMB
+
+ return true;
+}
+
+void CNetworkServices::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string& settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_SMB_WINSSERVER ||
+ settingId == CSettings::SETTING_SMB_WORKGROUP ||
+ settingId == CSettings::SETTING_SMB_MINPROTOCOL ||
+ settingId == CSettings::SETTING_SMB_MAXPROTOCOL ||
+ settingId == CSettings::SETTING_SMB_LEGACYSECURITY)
+ {
+ // okey we really don't need to restart, only deinit samba, but that could be damn hard if something is playing
+ //! @todo - General way of handling setting changes that require restart
+ if (HELPERS::ShowYesNoDialogText(CVariant{14038}, CVariant{14039}) ==
+ DialogResponse::CHOICE_YES)
+ {
+ m_settings->Save();
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_RESTARTAPP);
+ }
+ }
+}
+
+bool CNetworkServices::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+{
+ if (setting == NULL)
+ return false;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_SERVICES_WEBSERVERUSERNAME)
+ {
+ // if webserverusername is xbmc and pw is not empty we treat it as altered
+ // and don't change the username to kodi - part of rebrand
+ if (m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERUSERNAME) == "xbmc" &&
+ !m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERPASSWORD).empty())
+ return true;
+ }
+ if (settingId == CSettings::SETTING_SERVICES_WEBSERVERPORT)
+ {
+ // if webserverport is default but webserver is activated then treat it as altered
+ // and don't change the port to new value
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVER))
+ return true;
+ }
+ return false;
+}
+
+void CNetworkServices::Start()
+{
+ StartZeroconf();
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_UPNP))
+ StartUPnP();
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED) && !StartEventServer())
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(33102), g_localizeStrings.Get(33100));
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED) && !StartJSONRPCServer())
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(33103), g_localizeStrings.Get(33100));
+
+#ifdef HAS_WEB_SERVER
+ // Start web server after eventserver and JSON-RPC server, so users can use these interfaces
+ // to confirm the warning message below if it is shown
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVER))
+ {
+ // services.webserverauthentication setting was added in Kodi v18 and requires a valid password
+ // to be set, but on upgrade the setting will be activated automatically regardless of whether
+ // a password was set before -> this can lead to an invalid configuration
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION) &&
+ m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERPASSWORD).empty())
+ {
+ // Alert user to new default security settings in new Kodi version
+ HELPERS::ShowOKDialogText(33101, 33104);
+ // Fix settings: Disable web server
+ m_settings->SetBool(CSettings::SETTING_SERVICES_WEBSERVER, false);
+ // Bring user to settings screen where authentication can be configured properly
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(
+ WINDOW_SETTINGS_SERVICE, std::vector<std::string>{"services.webserverauthentication"});
+ }
+ // Only try to start server if configuration is OK
+ else if (!StartWebserver())
+ CGUIDialogKaiToast::QueueNotification(
+ CGUIDialogKaiToast::Warning, g_localizeStrings.Get(33101), g_localizeStrings.Get(33100));
+ }
+#endif // HAS_WEB_SERVER
+
+ // note - airtunesserver has to start before airplay server (ios7 client detection bug)
+ StartAirTunesServer();
+ StartAirPlayServer();
+ StartRss();
+ StartWSDiscovery();
+}
+
+void CNetworkServices::Stop(bool bWait)
+{
+ if (bWait)
+ {
+ StopUPnP(bWait);
+ StopZeroconf();
+ StopWebserver();
+ StopRss();
+ }
+
+ StopEventServer(bWait, false);
+ StopJSONRPCServer(bWait);
+ StopAirPlayServer(bWait);
+ StopAirTunesServer(bWait);
+ StopWSDiscovery();
+}
+
+bool CNetworkServices::StartServer(enum ESERVERS server, bool start)
+{
+ auto settingsComponent = CServiceBroker::GetSettingsComponent();
+ if (!settingsComponent)
+ return false;
+
+ auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (!settings)
+ return false;
+
+ bool ret = false;
+ switch (server)
+ {
+ case ES_WEBSERVER:
+ // the callback will take care of starting/stopping webserver
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_WEBSERVER, start);
+ break;
+
+ case ES_AIRPLAYSERVER:
+ // the callback will take care of starting/stopping airplay
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_AIRPLAY, start);
+ break;
+
+ case ES_JSONRPCSERVER:
+ // the callback will take care of starting/stopping jsonrpc server
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_ESENABLED, start);
+ break;
+
+ case ES_UPNPSERVER:
+ // the callback will take care of starting/stopping upnp server
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_UPNPSERVER, start);
+ break;
+
+ case ES_UPNPRENDERER:
+ // the callback will take care of starting/stopping upnp renderer
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_UPNPRENDERER, start);
+ break;
+
+ case ES_EVENTSERVER:
+ // the callback will take care of starting/stopping event server
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_ESENABLED, start);
+ break;
+
+ case ES_ZEROCONF:
+ // the callback will take care of starting/stopping zeroconf
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_ZEROCONF, start);
+ break;
+
+ case ES_WSDISCOVERY:
+ // the callback will take care of starting/stopping WS-Discovery
+ ret = settings->SetBool(CSettings::SETTING_SERVICES_WSDISCOVERY, start);
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+ settings->Save();
+
+ return ret;
+}
+
+bool CNetworkServices::StartWebserver()
+{
+#ifdef HAS_WEB_SERVER
+ if (!CServiceBroker::GetNetwork().IsAvailable())
+ return false;
+
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVER))
+ return false;
+
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION) &&
+ m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERPASSWORD).empty())
+ {
+ CLog::Log(LOGERROR, "Tried to start webserver with invalid configuration (authentication "
+ "enabled, but no password set");
+ return false;
+ }
+
+ int webPort = m_settings->GetInt(CSettings::SETTING_SERVICES_WEBSERVERPORT);
+ if (!ValidatePort(webPort))
+ {
+ CLog::Log(LOGERROR, "Cannot start Web Server on port {}", webPort);
+ return false;
+ }
+
+ if (IsWebserverRunning())
+ return true;
+
+ std::string username;
+ std::string password;
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION))
+ {
+ username = m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERUSERNAME);
+ password = m_settings->GetString(CSettings::SETTING_SERVICES_WEBSERVERPASSWORD);
+ }
+
+ if (!m_webserver.Start(webPort, username, password))
+ return false;
+
+#ifdef HAS_ZEROCONF
+ std::vector<std::pair<std::string, std::string> > txt;
+ txt.emplace_back("txtvers", "1");
+ txt.emplace_back("uuid", CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_SERVICES_DEVICEUUID));
+
+ // publish web frontend and API services
+#ifdef HAS_WEB_INTERFACE
+ CZeroconf::GetInstance()->PublishService("servers.webserver", "_http._tcp", CSysInfo::GetDeviceName(), webPort, txt);
+#endif // HAS_WEB_INTERFACE
+ CZeroconf::GetInstance()->PublishService("servers.jsonrpc-http", "_xbmc-jsonrpc-h._tcp", CSysInfo::GetDeviceName(), webPort, txt);
+#endif // HAS_ZEROCONF
+
+ return true;
+#endif // HAS_WEB_SERVER
+ return false;
+}
+
+bool CNetworkServices::IsWebserverRunning()
+{
+#ifdef HAS_WEB_SERVER
+ return m_webserver.IsStarted();
+#endif // HAS_WEB_SERVER
+ return false;
+}
+
+bool CNetworkServices::StopWebserver()
+{
+#ifdef HAS_WEB_SERVER
+ if (!IsWebserverRunning())
+ return true;
+
+ if (!m_webserver.Stop() || m_webserver.IsStarted())
+ {
+ CLog::Log(LOGWARNING, "Webserver: Failed to stop.");
+ return false;
+ }
+
+#ifdef HAS_ZEROCONF
+#ifdef HAS_WEB_INTERFACE
+ CZeroconf::GetInstance()->RemoveService("servers.webserver");
+#endif // HAS_WEB_INTERFACE
+ CZeroconf::GetInstance()->RemoveService("servers.jsonrpc-http");
+#endif // HAS_ZEROCONF
+
+ return true;
+#endif // HAS_WEB_SERVER
+ return false;
+}
+
+bool CNetworkServices::StartAirPlayServer()
+{
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_AIRPLAYVIDEOSUPPORT))
+ return true;
+
+#ifdef HAS_AIRPLAY
+ if (!CServiceBroker::GetNetwork().IsAvailable() || !m_settings->GetBool(CSettings::SETTING_SERVICES_AIRPLAY))
+ return false;
+
+ if (IsAirPlayServerRunning())
+ return true;
+
+ if (!CAirPlayServer::StartServer(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_airPlayPort, true))
+ return false;
+
+ if (!CAirPlayServer::SetCredentials(m_settings->GetBool(CSettings::SETTING_SERVICES_USEAIRPLAYPASSWORD),
+ m_settings->GetString(CSettings::SETTING_SERVICES_AIRPLAYPASSWORD)))
+ return false;
+
+#ifdef HAS_ZEROCONF
+ std::vector<std::pair<std::string, std::string> > txt;
+ CNetworkInterface* iface = CServiceBroker::GetNetwork().GetFirstConnectedInterface();
+ txt.emplace_back("deviceid", iface != nullptr ? iface->GetMacAddress() : "FF:FF:FF:FF:FF:F2");
+ txt.emplace_back("model", "Xbmc,1");
+ txt.emplace_back("srcvers", AIRPLAY_SERVER_VERSION_STR);
+
+ // for ios8 clients we need to announce mirroring support
+ // else we won't get video urls anymore.
+ // We also announce photo caching support (as it seems faster and
+ // we have implemented it anyways).
+ txt.emplace_back("features", "0x20F7");
+
+ CZeroconf::GetInstance()->PublishService("servers.airplay", "_airplay._tcp", CSysInfo::GetDeviceName(), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_airPlayPort, txt);
+#endif // HAS_ZEROCONF
+
+ return true;
+#endif // HAS_AIRPLAY
+ return false;
+}
+
+bool CNetworkServices::IsAirPlayServerRunning()
+{
+#ifdef HAS_AIRPLAY
+ return CAirPlayServer::IsRunning();
+#endif // HAS_AIRPLAY
+ return false;
+}
+
+bool CNetworkServices::StopAirPlayServer(bool bWait)
+{
+#ifdef HAS_AIRPLAY
+ if (!IsAirPlayServerRunning())
+ return true;
+
+ CAirPlayServer::StopServer(bWait);
+
+#ifdef HAS_ZEROCONF
+ CZeroconf::GetInstance()->RemoveService("servers.airplay");
+#endif // HAS_ZEROCONF
+
+ return true;
+#endif // HAS_AIRPLAY
+ return false;
+}
+
+bool CNetworkServices::StartAirTunesServer()
+{
+#ifdef HAS_AIRTUNES
+ if (!CServiceBroker::GetNetwork().IsAvailable() || !m_settings->GetBool(CSettings::SETTING_SERVICES_AIRPLAY))
+ return false;
+
+ if (IsAirTunesServerRunning())
+ return true;
+
+ if (!CAirTunesServer::StartServer(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_airTunesPort, true,
+ m_settings->GetBool(CSettings::SETTING_SERVICES_USEAIRPLAYPASSWORD),
+ m_settings->GetString(CSettings::SETTING_SERVICES_AIRPLAYPASSWORD)))
+ {
+ CLog::Log(LOGERROR, "Failed to start AirTunes Server");
+ return false;
+ }
+
+ return true;
+#endif // HAS_AIRTUNES
+ return false;
+}
+
+bool CNetworkServices::IsAirTunesServerRunning()
+{
+#ifdef HAS_AIRTUNES
+ return CAirTunesServer::IsRunning();
+#endif // HAS_AIRTUNES
+ return false;
+}
+
+bool CNetworkServices::StopAirTunesServer(bool bWait)
+{
+#ifdef HAS_AIRTUNES
+ if (!IsAirTunesServerRunning())
+ return true;
+
+ CAirTunesServer::StopServer(bWait);
+ return true;
+#endif // HAS_AIRTUNES
+ return false;
+}
+
+bool CNetworkServices::StartJSONRPCServer()
+{
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED))
+ return false;
+
+ if (IsJSONRPCServerRunning())
+ return true;
+
+ if (!CTCPServer::StartServer(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_jsonTcpPort, m_settings->GetBool(CSettings::SETTING_SERVICES_ESALLINTERFACES)))
+ return false;
+
+#ifdef HAS_ZEROCONF
+ std::vector<std::pair<std::string, std::string> > txt;
+ txt.emplace_back("txtvers", "1");
+ txt.emplace_back("uuid", CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_SERVICES_DEVICEUUID));
+
+ CZeroconf::GetInstance()->PublishService("servers.jsonrpc-tpc", "_xbmc-jsonrpc._tcp", CSysInfo::GetDeviceName(), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_jsonTcpPort, txt);
+#endif // HAS_ZEROCONF
+
+ return true;
+}
+
+bool CNetworkServices::IsJSONRPCServerRunning()
+{
+ return CTCPServer::IsRunning();
+}
+
+bool CNetworkServices::StopJSONRPCServer(bool bWait)
+{
+ if (!IsJSONRPCServerRunning())
+ return true;
+
+ CTCPServer::StopServer(bWait);
+
+#ifdef HAS_ZEROCONF
+ CZeroconf::GetInstance()->RemoveService("servers.jsonrpc-tcp");
+#endif // HAS_ZEROCONF
+
+ return true;
+}
+
+bool CNetworkServices::StartEventServer()
+{
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED))
+ return false;
+
+ if (IsEventServerRunning())
+ return true;
+
+ CEventServer* server = CEventServer::GetInstance();
+ if (!server)
+ {
+ CLog::Log(LOGERROR, "ES: Out of memory");
+ return false;
+ }
+
+ server->StartServer();
+
+ return true;
+}
+
+bool CNetworkServices::IsEventServerRunning()
+{
+ return CEventServer::GetInstance()->Running();
+}
+
+bool CNetworkServices::StopEventServer(bool bWait, bool promptuser)
+{
+ if (!IsEventServerRunning())
+ return true;
+
+ CEventServer* server = CEventServer::GetInstance();
+ if (!server)
+ {
+ CLog::Log(LOGERROR, "ES: Out of memory");
+ return false;
+ }
+
+ if (promptuser)
+ {
+ if (server->GetNumberOfClients() > 0)
+ {
+ if (HELPERS::ShowYesNoDialogText(CVariant{13140}, CVariant{13141}, CVariant{""}, CVariant{""},
+ 10000) != DialogResponse::CHOICE_YES)
+ {
+ CLog::Log(LOGINFO, "ES: Not stopping event server");
+ return false;
+ }
+ }
+ CLog::Log(LOGINFO, "ES: Stopping event server with confirmation");
+
+ CEventServer::GetInstance()->StopServer(true);
+ }
+ else
+ {
+ if (!bWait)
+ CLog::Log(LOGINFO, "ES: Stopping event server");
+
+ CEventServer::GetInstance()->StopServer(bWait);
+ }
+
+ return true;
+}
+
+bool CNetworkServices::RefreshEventServer()
+{
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_ESENABLED))
+ return false;
+
+ if (!IsEventServerRunning())
+ return false;
+
+ CEventServer::GetInstance()->RefreshSettings();
+ return true;
+}
+
+bool CNetworkServices::StartUPnP()
+{
+ bool ret = false;
+#ifdef HAS_UPNP
+ ret |= StartUPnPClient();
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_UPNPSERVER))
+ {
+ ret |= StartUPnPServer();
+ }
+
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_UPNPCONTROLLER))
+ {
+ ret |= StartUPnPController();
+ }
+
+ if (m_settings->GetBool(CSettings::SETTING_SERVICES_UPNPRENDERER))
+ {
+ ret |= StartUPnPRenderer();
+ }
+#endif // HAS_UPNP
+ return ret;
+}
+
+bool CNetworkServices::StopUPnP(bool bWait)
+{
+#ifdef HAS_UPNP
+ if (!CUPnP::IsInstantiated())
+ return true;
+
+ CLog::Log(LOGINFO, "stopping upnp");
+ CUPnP::ReleaseInstance(bWait);
+
+ return true;
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StartUPnPClient()
+{
+#ifdef HAS_UPNP
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_UPNP))
+ return false;
+
+ CLog::Log(LOGINFO, "starting upnp client");
+ CUPnP::GetInstance()->StartClient();
+ return IsUPnPClientRunning();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::IsUPnPClientRunning()
+{
+#ifdef HAS_UPNP
+ return CUPnP::GetInstance()->IsClientStarted();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StopUPnPClient()
+{
+#ifdef HAS_UPNP
+ if (!IsUPnPClientRunning())
+ return true;
+
+ CLog::Log(LOGINFO, "stopping upnp client");
+ CUPnP::GetInstance()->StopClient();
+
+ return true;
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StartUPnPController()
+{
+#ifdef HAS_UPNP
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_UPNPCONTROLLER) ||
+ !m_settings->GetBool(CSettings::SETTING_SERVICES_UPNPSERVER) ||
+ !m_settings->GetBool(CSettings::SETTING_SERVICES_UPNP))
+ return false;
+
+ CLog::Log(LOGINFO, "starting upnp controller");
+ CUPnP::GetInstance()->StartController();
+ return IsUPnPControllerRunning();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::IsUPnPControllerRunning()
+{
+#ifdef HAS_UPNP
+ return CUPnP::GetInstance()->IsControllerStarted();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StopUPnPController()
+{
+#ifdef HAS_UPNP
+ if (!IsUPnPControllerRunning())
+ return true;
+
+ CLog::Log(LOGINFO, "stopping upnp controller");
+ CUPnP::GetInstance()->StopController();
+
+ return true;
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StartUPnPRenderer()
+{
+#ifdef HAS_UPNP
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_UPNPRENDERER) ||
+ !m_settings->GetBool(CSettings::SETTING_SERVICES_UPNP))
+ return false;
+
+ CLog::Log(LOGINFO, "starting upnp renderer");
+ return CUPnP::GetInstance()->StartRenderer();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::IsUPnPRendererRunning()
+{
+#ifdef HAS_UPNP
+ return CUPnP::GetInstance()->IsInstantiated();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StopUPnPRenderer()
+{
+#ifdef HAS_UPNP
+ if (!IsUPnPRendererRunning())
+ return true;
+
+ CLog::Log(LOGINFO, "stopping upnp renderer");
+ CUPnP::GetInstance()->StopRenderer();
+
+ return true;
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StartUPnPServer()
+{
+#ifdef HAS_UPNP
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_UPNPSERVER) ||
+ !m_settings->GetBool(CSettings::SETTING_SERVICES_UPNP))
+ return false;
+
+ CLog::Log(LOGINFO, "starting upnp server");
+ return CUPnP::GetInstance()->StartServer();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::IsUPnPServerRunning()
+{
+#ifdef HAS_UPNP
+ return CUPnP::GetInstance()->IsInstantiated();
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StopUPnPServer()
+{
+#ifdef HAS_UPNP
+ if (!IsUPnPServerRunning())
+ return true;
+
+ StopUPnPController();
+
+ CLog::Log(LOGINFO, "stopping upnp server");
+ CUPnP::GetInstance()->StopServer();
+
+ return true;
+#endif // HAS_UPNP
+ return false;
+}
+
+bool CNetworkServices::StartRss()
+{
+ if (IsRssRunning())
+ return true;
+
+ CRssManager::GetInstance().Start();
+ return true;
+}
+
+bool CNetworkServices::IsRssRunning()
+{
+ return CRssManager::GetInstance().IsActive();
+}
+
+bool CNetworkServices::StopRss()
+{
+ if (!IsRssRunning())
+ return true;
+
+ CRssManager::GetInstance().Stop();
+ return true;
+}
+
+bool CNetworkServices::StartZeroconf()
+{
+#ifdef HAS_ZEROCONF
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_ZEROCONF))
+ return false;
+
+ if (IsZeroconfRunning())
+ return true;
+
+ CLog::Log(LOGINFO, "starting zeroconf publishing");
+ return CZeroconf::GetInstance()->Start();
+#endif // HAS_ZEROCONF
+ return false;
+}
+
+bool CNetworkServices::IsZeroconfRunning()
+{
+#ifdef HAS_ZEROCONF
+ return CZeroconf::GetInstance()->IsStarted();
+#endif // HAS_ZEROCONF
+ return false;
+}
+
+bool CNetworkServices::StopZeroconf()
+{
+#ifdef HAS_ZEROCONF
+ if (!IsZeroconfRunning())
+ return true;
+
+ CLog::Log(LOGINFO, "stopping zeroconf publishing");
+ CZeroconf::GetInstance()->Stop();
+
+ return true;
+#endif // HAS_ZEROCONF
+ return false;
+}
+
+bool CNetworkServices::StartWSDiscovery()
+{
+#if defined(HAS_FILESYSTEM_SMB)
+ if (!m_settings->GetBool(CSettings::SETTING_SERVICES_WSDISCOVERY))
+ return false;
+
+ if (IsWSDiscoveryRunning())
+ return true;
+
+ return CServiceBroker::GetWSDiscovery().StartServices();
+#endif // HAS_FILESYSTEM_SMB
+ return false;
+}
+
+bool CNetworkServices::IsWSDiscoveryRunning()
+{
+#if defined(HAS_FILESYSTEM_SMB)
+ return CServiceBroker::GetWSDiscovery().IsRunning();
+#endif // HAS_FILESYSTEM_SMB
+ return false;
+}
+
+bool CNetworkServices::StopWSDiscovery()
+{
+#if defined(HAS_FILESYSTEM_SMB)
+ if (!IsWSDiscoveryRunning())
+ return true;
+
+ CServiceBroker::GetWSDiscovery().StopServices();
+
+ return true;
+#endif // HAS_FILESYSTEM_SMB
+ return false;
+}
+
+bool CNetworkServices::ValidatePort(int port)
+{
+ if (port <= 0 || port > 65535)
+ return false;
+
+#ifdef TARGET_LINUX
+ if (!CUtil::CanBindPrivileged() && (port < 1024))
+ return false;
+#endif
+
+ return true;
+}