summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorDenis Loh <denis.loh@gmail.com>2009-10-24 14:24:17 +0200
committerDenis Loh <denis.loh@gmail.com>2009-10-24 14:24:17 +0200
commit1cf955a715830130b7add8c1183d65b0f442fd23 (patch)
treec9d03961e9f83b1100ef6010a4a53063f127aa5d /server
downloadvdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.gz
vdr-plugin-upnp-1cf955a715830130b7add8c1183d65b0f442fd23.tar.bz2
Initial commit
Diffstat (limited to 'server')
-rw-r--r--server/server.cpp366
-rw-r--r--server/server.h172
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