diff options
author | Denis Loh <denis.loh@gmail.com> | 2009-10-24 14:24:17 +0200 |
---|---|---|
committer | Denis Loh <denis.loh@gmail.com> | 2009-10-24 14:24:17 +0200 |
commit | 1cf955a715830130b7add8c1183d65b0f442fd23 (patch) | |
tree | c9d03961e9f83b1100ef6010a4a53063f127aa5d /server | |
download | vdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.gz vdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.bz2 |
Initial commit
Diffstat (limited to 'server')
-rw-r--r-- | server/server.cpp | 366 | ||||
-rw-r--r-- | server/server.h | 172 |
2 files changed, 538 insertions, 0 deletions
diff --git a/server/server.cpp b/server/server.cpp new file mode 100644 index 0000000..56afcaa --- /dev/null +++ b/server/server.cpp @@ -0,0 +1,366 @@ +/* + * File: server.cpp + * Author: savop + * + * Created on 19. April 2009, 17:42 + */ + +#include <vdr/plugin.h> +#include <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include <upnp/upnp.h> +#include "server.h" +#include "../misc/util.h" +#include "../misc/config.h" +#include "../common.h" +#include "../upnpcomponents/dlna.h" +#include "../database/object.h" + +/**************************************************** + * + * The UPnP Server + * + * Handles incoming messages, UPnP connections + * and so on. + * + ****************************************************/ + +cConnectionManager* cUPnPServer::mConnectionManager = NULL; +cContentDirectory* cUPnPServer::mContentDirectory = NULL; + +cUPnPServer::cUPnPServer() { + this->mServerAddr = new sockaddr_in; + // Bugfix: this was necessary because there were + // some uninitialised bytes in the structure (Please recheck this!) + memset(this->mServerAddr,0,sizeof(sockaddr_in)); + this->mServerAddr->sin_family = AF_INET; + this->mServerAddr->sin_port = 0; + this->mIsRunning = false; + this->mIsAutoDetectionEnabled = true; + this->mIsEnabled = false; + this->mDeviceHandle = NULL; + this->mMediaDatabase = NULL; +} + +cUPnPServer::~cUPnPServer() { + delete this->mServerAddr; this->mServerAddr = NULL; + delete this->mMediaDatabase; +} + +bool cUPnPServer::init(void){ + + MESSAGE("Loading configuration..."); + cUPnPConfig* config = cUPnPConfig::get(); + this->enable(config->mEnable == 1 ? true : false); + if(!config->mAutoSetup){ + if(config->mInterface) + if(!this->setInterface(config->mInterface)){ + ERROR("Invalid network interface: %s", config->mInterface); + return false; + } + if(config->mAddress) + if(!this->setAddress(config->mAddress)){ + ERROR("Invalid IP address: %s", config->mAddress); + return false; + } + if(!this->setServerPort((short)config->mPort)){ + ERROR("Invalid port: %d", config->mPort); + return false; + } + } + else { + if(!this->setAutoDetection(config->mAutoSetup == 1 ? true : false)){ + ERROR("Invalid auto detection setting: %d", config->mAutoSetup); + return false; + } + if(!this->autoDetectSettings()){ + ERROR("Error while auto detecting settings."); + return false; + } + } + + MESSAGE("Initializing Intel UPnP SDK on %s:%d",inet_ntoa(this->mServerAddr->sin_addr), ntohs(this->mServerAddr->sin_port)); + int ret = 0; + ret = UpnpInit(inet_ntoa(this->mServerAddr->sin_addr), ntohs(this->mServerAddr->sin_port)); + + if (ret != UPNP_E_SUCCESS) { + // test if SDK was allready initiated + if (ret == UPNP_E_INIT) { + WARNING("SDK was allready initiated (no problem) - Errorcode: %d", ret); + } else { + ERROR("Error while init Intel SDK - Errorcode: %d", ret); + return false; + } + } + else { + if(!inet_aton(UpnpGetServerIpAddress(),&this->mServerAddr->sin_addr)){ + ERROR("Unable to set IP address"); + } + this->mServerAddr->sin_port = htons(UpnpGetServerPort()); + MESSAGE("Initializing succesfully at %s:%d", UpnpGetServerIpAddress(), UpnpGetServerPort()); + } + + MESSAGE("Setting maximum packet size for SOAP requests"); + UpnpSetMaxContentLength(UPNP_SOAP_MAX_LEN); + + //set the root directory of the webserver + cString WebserverRootDir = cString::sprintf("%s%s", cPluginUpnp::getConfigDirectory(), UPNP_WEB_SERVER_ROOT_DIR); + + MESSAGE("Set web server root dir: %s", *WebserverRootDir ); + this->mWebServer = cUPnPWebServer::getInstance(WebserverRootDir); + MESSAGE("Initializing web server."); + if (!this->mWebServer->init()) { + ERROR("Error while setting web server root dir - Errorcode: %d", ret); + return false; + } + + //register media server device to SDK + cString URLBase = cString::sprintf("http://%s:%d", UpnpGetServerIpAddress(), UpnpGetServerPort()); + + this->mDeviceDescription = cDlna::getInstance()->getDeviceDescription(URLBase); + + MESSAGE("Register Media Server Device"); + ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, + this->mDeviceDescription, sizeof(this->mDeviceDescription), 1, + &cUPnPServer::upnpActionCallback, + &this->mDeviceHandle, + &this->mDeviceHandle); + if (ret != UPNP_E_SUCCESS) { + ERROR("Error while registering device - Errorcode: %d", ret); + return false; + } + + MESSAGE("Unregister server to cleanup previously started servers"); + ret = UpnpUnRegisterRootDevice(this->mDeviceHandle); + if (ret != UPNP_E_SUCCESS) { + WARNING("Unregistering old devices failed"); + return false; + } + + MESSAGE("Register Media Server Device"); + ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, + this->mDeviceDescription, sizeof(this->mDeviceDescription), 1, + &cUPnPServer::upnpActionCallback, + &this->mDeviceHandle, + &this->mDeviceHandle); + if (ret != UPNP_E_SUCCESS) { + ERROR("Error while registering device - Errorcode: %d", ret); + return false; + } + + MESSAGE("Initializing media database"); + this->mMediaDatabase = new cMediaDatabase; + if(!this->mMediaDatabase->init()){ + ERROR("Error while initializing database"); + return false; + } + + MESSAGE("Initializing connection manager"); + cUPnPServer::mConnectionManager = new cConnectionManager(this->mDeviceHandle); + MESSAGE("Initializing content directory"); + cUPnPServer::mContentDirectory = new cContentDirectory(this->mDeviceHandle, this->mMediaDatabase); + if(!cUPnPServer::mContentDirectory->Start()){ + ERROR("Unable to start content directory thread"); + return false; + } + + //send first advertisments + MESSAGE("Send first advertisements to publish start in network"); + ret = UpnpSendAdvertisement(this->mDeviceHandle, UPNP_ANNOUNCE_MAX_AGE); + if (ret != UPNP_E_SUCCESS) { + ERROR("Error while sending first advertisments - Errorcode: %d", ret); + return false; + } + + return true; +} + +bool cUPnPServer::uninit(void) { + MESSAGE("Shuting down content directory"); + delete cUPnPServer::mContentDirectory; cUPnPServer::mContentDirectory = NULL; + + MESSAGE("Shuting down connection manager"); + delete cUPnPServer::mConnectionManager; cUPnPServer::mConnectionManager = NULL; + + MESSAGE("Closing metadata database"); + delete this->mMediaDatabase; this->mMediaDatabase = NULL; + + MESSAGE("Close Intel SDK"); + // unregiser media server device from UPnP SDK + int ret = UpnpUnRegisterRootDevice(this->mDeviceHandle); + if (ret != UPNP_E_SUCCESS) { + WARNING("No device registered"); + } + // send intel sdk message to shutdown + ret = UpnpFinish(); + + if (ret == UPNP_E_SUCCESS) { + MESSAGE("Close Intel SDK Successfull"); + return true; + } else { + ERROR("Intel SDK unintialized or already closed - Errorcode: %d", ret); + return false; + } +} + +int cUPnPServer::upnpActionCallback(Upnp_EventType eventtype, void *event, void *cookie) { + // only to remove warning while compiling because cookie is unused + cookie = NULL; + Upnp_Subscription_Request* eventRequest = NULL; + Upnp_Action_Request* actionRequest = NULL; + + //check committed event variable + if (event == NULL) { + ERROR("UPnP Callback - NULL request"); + return UPNP_E_BAD_REQUEST; + } + + switch (eventtype) { + case UPNP_CONTROL_ACTION_REQUEST: + actionRequest = (Upnp_Action_Request*) event; + + //check that request is for this device + if (strcmp(actionRequest->DevUDN, UPNP_DEVICE_UDN) != 0) { + ERROR("UPnP Callback - actions request not for this device"); + return UPNP_E_BAD_REQUEST; + } + + //find out which service was called + if (strcmp(actionRequest->ServiceID, UPNP_CMS_SERVICE_ID) == 0) { + // proceed action + return cUPnPServer::mConnectionManager->execute(actionRequest); + + } else if (strcmp(actionRequest->ServiceID, UPNP_CDS_SERVICE_ID) == 0) { + // proceed action + return cUPnPServer::mContentDirectory->execute(actionRequest); + } else { + ERROR("UPnP Callback - unsupported service called for control"); + return UPNP_E_BAD_REQUEST; + } + case UPNP_EVENT_SUBSCRIPTION_REQUEST: + eventRequest = (Upnp_Subscription_Request*) event; + + //check that request is for this device + if (strcmp(eventRequest->UDN, UPNP_DEVICE_UDN) != 0) { + ERROR("UPnP Callback - event request not for this device"); + return UPNP_E_BAD_REQUEST; + } + + if (strcmp(eventRequest->ServiceId, UPNP_CMS_SERVICE_ID) == 0) { + // handle event request + return cUPnPServer::mConnectionManager->subscribe(eventRequest); + + } else if (strcmp(eventRequest->ServiceId, UPNP_CDS_SERVICE_ID) == 0) { + // handle event request + return cUPnPServer::mContentDirectory->subscribe(eventRequest); + } else { + ERROR("UPnP Callback - unsupported service called for eventing"); + return UPNP_E_BAD_REQUEST; + } + + return UPNP_E_BAD_REQUEST; + default: + ERROR("UPnP Action Callback - Unsupported Event"); + return UPNP_E_BAD_REQUEST; + } + + return UPNP_E_BAD_REQUEST; +} + +bool cUPnPServer::autoDetectSettings(void){ + int count; + char** Ifaces = getNetworkInterfaces(&count); + int i=0; + bool ret = false; + MESSAGE("AUTODETECT: Found %d possible interfaces.", sizeof(Ifaces)); + while(Ifaces[i]){ + if(strcmp(Ifaces[i],"lo")!=0){ + // true || false == true + // false || false == false + ret = this->setInterface(strdup(Ifaces[i])) || ret; + } + i++; + } + delete [] Ifaces; + if(!ret){ + MESSAGE("AUTODETECT: No suitable interface. Giving up."); + return false; + } + this->setServerPort(0); + return true; +} + +bool cUPnPServer::start(void){ + if(!this->isRunning()){ + // Put all the stuff which shall be started with the server in here + // if the startup failed due any reason return false! + MESSAGE("Starting UPnP Server on %s:%d",inet_ntoa(this->getServerAddress()->sin_addr), ntohs(this->getServerAddress()->sin_port)); + MESSAGE("Using DLNA version: %s", DLNA_PROTOCOL_VERSION_STR); + this->mIsRunning = true; + // Start Media database thread + this->mMediaDatabase->Start(); + } + return true; +} + +void cUPnPServer::stop(void){ + if(this->isRunning()){ + MESSAGE("Call upnpServer STOP"); + this->uninit(); + this->mIsRunning = false; + } + return; +} + +bool cUPnPServer::restart(void){ + MESSAGE("Call upnpServer RESTART"); + this->stop(); + return this->start(); +} + +void cUPnPServer::enable(bool enabled){ + this->mIsEnabled = enabled; +} + +bool cUPnPServer::setInterface(const char* Interface){ + if(Interface != NULL) this->mInterface = Interface; + + if(*this->mInterface!=NULL){ + MESSAGE("Try to retrieve address for NIC %s",Interface); + const sockaddr_in* ipAddress = getIPFromInterface(Interface); + if(ipAddress!=NULL){ + memcpy(&this->mServerAddr->sin_addr,&ipAddress->sin_addr,sizeof(ipAddress->sin_addr)); + MESSAGE("NIC %s has the following IP: %s", *this->mInterface, inet_ntoa(this->mServerAddr->sin_addr)); + this->stop(); + return true; + } + delete ipAddress; + ERROR("Unable to obtain a valid IP address for NIC %s!",Interface); + } + this->mServerAddr = NULL; + return false; +} + +bool cUPnPServer::setServerPort(unsigned short port){ + // check if the port is in user range or 0 + if(port != 0 && port < SERVER_MIN_PORT) return false; + this->stop(); + this->mServerAddr->sin_port = htons(port); + return true; +} + +bool cUPnPServer::setAddress(const char* Address){ + if(inet_aton(Address, &this->mServerAddr->sin_addr) == 0) return false; + this->stop(); + return true; +} + +bool cUPnPServer::setAutoDetection(bool enable){ + this->mIsAutoDetectionEnabled = enable; + return true; +} + +sockaddr_in* cUPnPServer::getServerAddress() { + return this->mServerAddr; +}
\ No newline at end of file diff --git a/server/server.h b/server/server.h new file mode 100644 index 0000000..72ed2c8 --- /dev/null +++ b/server/server.h @@ -0,0 +1,172 @@ +/* + * File: server.h + * Author: savop + * + * Created on 19. April 2009, 17:42 + */ + +#ifndef _SERVER_H +#define _SERVER_H + +#include <netinet/in.h> +#include <vdr/recording.h> +#include <vdr/thread.h> +#include <upnp/upnp.h> +#include "../misc/util.h" +#include "../common.h" +#include "../upnpcomponents/upnpwebserver.h" +#include "../database/metadata.h" +#include "../upnpcomponents/connectionmanager.h" +#include "../upnpcomponents/contentdirectory.h" +#include "../upnp.h" + +class cUPnPServer { + friend class cPluginUpnp; +public: + /** + * Constructor + * + * This will create a new server and initializes the main functionalities + * The server has to be started manually by invoking cUPnPServer::start(). + */ + cUPnPServer(); + /** + * Destructor + * + * This will destroy the server object. Open ports and connections will be + * closed. + */ + virtual ~cUPnPServer(); + /** + * Enable the server + * + * This switch indicates if the server is startable or not + * + * If it is set to FALSE, any invocation of start() will do nothing. + */ + void enable(bool enabled); + /** + * Start the UPnP server + * + * This will start the UPnP server activities as a background task. + * + * @return 1 when the server started successfully, 0 otherwise + */ + bool start(void); + /** + * Restart the server + * + * When the server is not operating properly it can be restarted. + * It will stop the server functionalities, clear everything and + * start it again. + * + * @return 1 when the server restarted successfully, 0 otherwise + */ + bool restart(void); + /** + * Stop the server + * + * This will stop the server. This means that open connections to + * any clients and open ports will be closed. + */ + void stop(void); + bool autoDetectSettings(void); + /** + * Get the server address + * + * Returns a server address structure including IP address and port + * + * @return The server socket address + */ + sockaddr_in* getServerAddress(void); + /** + * Get the interface the server listens to + * + * Returns the network interface + */ + const char* getInterface(void) const { return this->mInterface; } + /** + * Set the server port + * + * The port must be in the scope of user definied ports (49152 - 65535). If + * the port is 0, it is autoassigned. You can retrieve the actual port by + * calling getServerAddress(), which will give you a structure with the port + * in it. + * + * The server must be restarted if the IP or port changes. + * + * Returns 1 when the port is valid, 0 otherwise + * + * @param port The port of the server + * @return 1 if the new server address is set, 0 otherwise + */ + bool setServerPort(unsigned short port); + /** + * The Interface to listen on + * + * Sets the listener interface, for instance 'eth1' or 'wlan0' + */ + bool setInterface(const char* Interface); + /** + * Set the server address + * + * Specifies the servers IP address. The server needs to restart + * when the IP is changed. However, it's not possible to detect + * changes through the system. + * + * This method should only be used in cases of fixed IP addresses + * for example when no DHCP server is available. + */ + bool setAddress(const char* Address); + /** + * Enables oder Disables auto detection mode + * + * If this is set to true, the setup will get it's information via + * auto detection + */ + bool setAutoDetection(bool enable); + /** + * Checks if the server is enabled + * + * This indicates if the server is currently enabled. + * + * @return 1 if the server is enabled, 0 otherwise + */ + bool isEnabled(void) const { return this->mIsEnabled; } + /** + * Checks if the server is running + * + * If the server is enabled, this indicates if it is running. + * + * @return 1 if the server is running, 0 otherwise + */ + bool isRunning(void) const { return this->mIsRunning; } + /** + * Is auto detection enabled or not + * + * Returns true or false if auto detection is enabled or not + */ + bool isAutoDetectionEnabled() { return this->mIsAutoDetectionEnabled; } +protected: +private: + /** + * Inits the server + * + * This method initializes all member variables with default values + */ + bool init(void); + bool uninit(void); + static int upnpActionCallback(Upnp_EventType eventtype, void *event, void *cookie); + bool mIsRunning; + bool mIsEnabled; + sockaddr_in* mServerAddr; + cString mInterface; + bool mIsAutoDetectionEnabled; + cString mDeviceDescription; + cUPnPWebServer* mWebServer; + cMediaDatabase* mMediaDatabase; + UpnpDevice_Handle mDeviceHandle; + static cConnectionManager* mConnectionManager; + static cContentDirectory* mContentDirectory; +}; +#endif /* _SERVER_H */
\ No newline at end of file |