summaryrefslogtreecommitdiff
path: root/upnpcomponents
diff options
context:
space:
mode:
Diffstat (limited to 'upnpcomponents')
-rw-r--r--upnpcomponents/connectionmanager.cpp393
-rw-r--r--upnpcomponents/connectionmanager.h67
-rw-r--r--upnpcomponents/contentdirectory.cpp306
-rw-r--r--upnpcomponents/contentdirectory.h38
-rw-r--r--upnpcomponents/dlna.cpp235
-rw-r--r--upnpcomponents/dlna.h64
-rw-r--r--upnpcomponents/upnpservice.cpp118
-rw-r--r--upnpcomponents/upnpservice.h27
-rw-r--r--upnpcomponents/upnpwebserver.cpp335
-rw-r--r--upnpcomponents/upnpwebserver.h123
10 files changed, 1706 insertions, 0 deletions
diff --git a/upnpcomponents/connectionmanager.cpp b/upnpcomponents/connectionmanager.cpp
new file mode 100644
index 0000000..b2c7149
--- /dev/null
+++ b/upnpcomponents/connectionmanager.cpp
@@ -0,0 +1,393 @@
+/*
+ * File: connectionmanager.cpp
+ * Author: savop
+ *
+ * Created on 21. August 2009, 18:35
+ */
+
+#include "upnpservice.h"
+#include <string.h>
+#include <upnp/ixml.h>
+#include <upnp/upnptools.h>
+#include <vdr/tools.h>
+#include "connectionmanager.h"
+#include "dlna.h"
+
+cVirtualConnection::cVirtualConnection() : mRcsID(-1) {}
+
+cConnectionManager::cConnectionManager(UpnpDevice_Handle DeviceHandle) : cUpnpService(DeviceHandle) {
+ this->mVirtualConnections = new cList<cVirtualConnection>;
+ this->mDefaultConnection = this->createVirtualConnection();
+ this->mSupportedProtocols = cDlna::getInstance()->getSupportedProtocols();
+}
+
+cConnectionManager::~cConnectionManager() {
+ delete this->mDefaultConnection;
+ delete this->mVirtualConnections;
+}
+
+int cConnectionManager::subscribe(Upnp_Subscription_Request* Request){
+ IXML_Document* PropertySet = NULL;
+ /* The protocol infos which this server supports */
+ UpnpAddToPropertySet(&PropertySet, "SourceProtocolInfo", this->mSupportedProtocols);
+ /* Not set, this field is only used by Media Renderers */
+ UpnpAddToPropertySet(&PropertySet, "SinkProtocolInfo", "");
+ /* The current connection IDs of all virtual connections */
+ const char* IDs = this->getConnectionIDsCVS();
+ if(!IDs){
+ return UPNP_E_INTERNAL_ERROR;
+ }
+ UpnpAddToPropertySet(&PropertySet, "CurrentConnectionIDs", IDs);
+ // Accept subscription
+ int ret = UpnpAcceptSubscriptionExt(this->mDeviceHandle, Request->UDN, Request->ServiceId, PropertySet, Request->Sid);
+
+ if(ret != UPNP_E_SUCCESS){
+ ERROR("Subscription failed (Error code: %d)", ret);
+ }
+
+ ixmlDocument_free(PropertySet);
+ return ret;
+}
+
+int cConnectionManager::execute(Upnp_Action_Request* Request){
+ if (Request == NULL) {
+ ERROR("CMS Action Handler - request is null");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ if(!strcmp(Request->ActionName, UPNP_CMS_ACTION_GETPROTOCOLINFO))
+ return this->getProtocolInfo(Request);
+ if(!strcmp(Request->ActionName, UPNP_CMS_ACTION_GETCURRENTCONNECTIONIDS))
+ return this->getCurrentConnectionIDs(Request);
+ if(!strcmp(Request->ActionName, UPNP_CMS_ACTION_GETCURRENTCONNECTIONINFO))
+ return this->getCurrentConnectionInfo(Request);
+ if(!strcmp(Request->ActionName, UPNP_CMS_ACTION_PREPAREFORCONNECTION))
+ return this->prepareForConnection(Request);
+ if(!strcmp(Request->ActionName, UPNP_CMS_ACTION_CONNECTIONCOMPLETE))
+ return this->connectionComplete(Request);
+
+ return UPNP_E_BAD_REQUEST;
+}
+
+int cConnectionManager::getProtocolInfo(Upnp_Action_Request* Request){
+ MESSAGE("Protocol info requested by %s.", inet_ntoa(Request->CtrlPtIPAddr));
+ cString Result = cString::sprintf(
+ "<u:%sResponse xmlns:u=\"%s\"> \
+ <Source>%s</Source> \
+ <Sink></Sink> \
+ </u:%sResponse>",
+ Request->ActionName,
+ UPNP_CMS_SERVICE_TYPE,
+ *this->mSupportedProtocols,
+ Request->ActionName
+ );
+ Request->ActionResult = ixmlParseBuffer(Result);
+ Request->ErrCode = UPNP_E_SUCCESS;
+ return Request->ErrCode;
+}
+
+int cConnectionManager::getCurrentConnectionIDs(Upnp_Action_Request* Request){
+ MESSAGE("Current connection IDs requested by %s.", inet_ntoa(Request->CtrlPtIPAddr));
+ cString Result;
+ const char* IDs = this->getConnectionIDsCVS();
+ if(!IDs){
+ Request->ErrCode = UPNP_E_INTERNAL_ERROR;
+ return Request->ErrCode;
+ }
+ Result = cString::sprintf(
+ "<u:%sResponse xmlns:u=\"%s\"> \
+ <ConnectionIDs>%s</ConnectionIDs> \
+ </u:%sResponse>",
+ Request->ActionName,
+ UPNP_CMS_SERVICE_TYPE,
+ IDs,
+ Request->ActionName
+ );
+ Request->ActionResult = ixmlParseBuffer(Result);
+ Request->ErrCode = UPNP_E_SUCCESS;
+ return Request->ErrCode;
+}
+
+int cConnectionManager::getCurrentConnectionInfo(Upnp_Action_Request* Request){
+ MESSAGE("Current connection info requested by %s.", inet_ntoa(Request->CtrlPtIPAddr));
+ int ConnectionID;
+
+ if(this->parseIntegerValue(Request->ActionRequest, "ConnectionID", &ConnectionID) != 0){
+ ERROR("Invalid arguments. ConnectionID missing or wrong");
+ this->setError(Request, 402);
+ return Request->ErrCode;
+ }
+
+ cVirtualConnection* Connection;
+ for(Connection = this->mVirtualConnections->First(); Connection && Connection->mConnectionID != ConnectionID; Connection = this->mVirtualConnections->Next(Connection)){}
+
+ if(Connection){
+ cString Result = cString::sprintf(
+ "<u:%sResponse xmlns:u=\"%s\">\
+ <ProtocolInfo>%s</ProtocolInfo>\
+ <PeerConnectionManager>%s</PeerConnectionManager>\
+ <PeerConnectionID>%d</PeerConnectionID>\
+ <Direction>%s</Direction>\
+ <RcsID>%d</RcsID>\
+ <AVTransportID>%d</AVTransportID>\
+ <Status>%s</Status>\
+ </u:%sResponse>",
+ Request->ActionName,
+ UPNP_CMS_SERVICE_TYPE,
+ *Connection->mRemoteProtocolInfo,
+ *Connection->mRemoteConnectionManager,
+ -1,
+ cVirtualConnection::getDirectionString(OUTPUT),
+ Connection->mRcsID,
+ Connection->mAVTransportID,
+ cVirtualConnection::getStatusString(Connection->mStatus),
+ Request->ActionName
+ );
+ Request->ActionResult = ixmlParseBuffer(Result);
+ Request->ErrCode = UPNP_E_SUCCESS;
+ }
+ else {
+ ERROR("No valid connection found with given ID=%d!", ConnectionID);
+ this->setError(Request, 706);
+ }
+
+ return Request->ErrCode;
+
+}
+
+int cConnectionManager::prepareForConnection(Upnp_Action_Request* Request){
+ MESSAGE("Request for a new connection by %s.", inet_ntoa(Request->CtrlPtIPAddr));
+ //char* Result = NULL;
+ char* RemoteProtocolInfo = NULL;
+ char* PeerConnectionManager = NULL;
+ int PeerConnectionID = 0;
+ char* DirectionStr = NULL;
+ int Direction;
+
+ if(this->parseStringValue(Request->ActionRequest, "RemoteProtocolInfo", &RemoteProtocolInfo) != 0){
+ ERROR("Invalid argument RemoteProtocolInfo: Missing or wrong");
+ this->setError(Request, 402);
+ return Request->ErrCode;
+ }
+
+ if(this->parseStringValue(Request->ActionRequest, "PeerConnectionManager", &PeerConnectionManager) != 0){
+ ERROR("Invalid argument PeerConnectionManager: Missing or wrong");
+ this->setError(Request, 402);
+ return Request->ErrCode;
+ }
+
+ if(this->parseStringValue(Request->ActionRequest, "Direction", &DirectionStr) != 0 && (Direction = cVirtualConnection::getDirection(DirectionStr)) == -1){
+ ERROR("Invalid argument Direction: Missing or wrong");
+ this->setError(Request, 402);
+ return Request->ErrCode;
+ }
+
+ if(this->parseIntegerValue(Request->ActionRequest, "PeerConnectionID", &PeerConnectionID) != 0){
+ ERROR("Invalid argument PeerConnectionID: Missing or wrong");
+ this->setError(Request, 402);
+ return Request->ErrCode;
+ }
+
+
+ /* TODO:
+ Create Connection
+ Notify AVTransport that a new connection was established
+ Send back the response */
+ this->setError(Request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return Request->ErrCode;
+}
+
+int cConnectionManager::connectionComplete(Upnp_Action_Request* Request){
+ MESSAGE("Request for closing an open connection by %s.", inet_ntoa(Request->CtrlPtIPAddr));
+ //char* Result = NULL;
+ int ConnectionID;
+
+ if(this->parseIntegerValue(Request->ActionRequest, "ConnectionID", &ConnectionID) != 0){
+ ERROR("Invalid argument ConnectionID: Missing or wrong");
+ this->setError(Request, 402);
+ return Request->ErrCode;
+ }
+
+ // TODO:
+ // Close and clear any open resources
+ // Close and delete the connection
+ // Free other resources left
+ this->setError(Request, UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED);
+ return Request->ErrCode;
+}
+
+bool cConnectionManager::setProtocolInfo(const char* ProtocolInfo){
+ if(strcmp(this->mSupportedProtocols, ProtocolInfo)){
+ // ProtocolInfo changed, save and invoke a event notification
+ this->mSupportedProtocols = ProtocolInfo;
+
+ IXML_Document* PropertySet = NULL;
+ UpnpAddToPropertySet(&PropertySet, "SourceProtocolInfo", this->mSupportedProtocols);
+ int ret = UpnpNotifyExt(this->mDeviceHandle, UPNP_DEVICE_UDN, UPNP_CMS_SERVICE_ID, PropertySet);
+ ixmlDocument_free(PropertySet);
+
+ if(ret != UPNP_E_SUCCESS){
+ ERROR("State change notification failed (Error code: %d)",ret);
+ return false;
+ }
+ }
+ return true;
+}
+
+cVirtualConnection* cConnectionManager::createVirtualConnection(const char* RemoteProtocolInfo, const char* RemoteConnectionManager, int RemoteConnectionID, eDirection Direction){
+ static int lastConnectionID = 0;
+ MESSAGE("Create virtual connection");
+ if(lastConnectionID == 2147483647) lastConnectionID = 1;
+ cVirtualConnection* Connection = new cVirtualConnection;
+ // AVT is available
+ Connection->mAVTransportID = 0;
+ // The ProtocolInfo of the remote device (i.e. Media Renderer)
+ Connection->mRemoteProtocolInfo = RemoteProtocolInfo;
+ // The responsible connection manager
+ Connection->mRemoteConnectionManager = RemoteConnectionManager;
+ // The virtual connection direction is output
+ Connection->mDirection = Direction;
+ // The remote connection ID, -1 says ID is unknown
+ Connection->mRemoteConnectionID = RemoteConnectionID;
+ // Connection status, assume that its ok.
+ Connection->mStatus = OK;
+ // new assigned ConnectionID
+ Connection->mConnectionID = lastConnectionID++;
+
+ // Notify the subscribers
+ IXML_Document* PropertySet = NULL;
+ const char* IDs = this->getConnectionIDsCVS();
+ if(!IDs){
+ return NULL;
+ }
+ UpnpAddToPropertySet(&PropertySet, "CurrentConnectionIDs", IDs);
+ int ret = UpnpNotifyExt(this->mDeviceHandle, UPNP_DEVICE_UDN, UPNP_CMS_SERVICE_ID, PropertySet);
+ ixmlDocument_free(PropertySet);
+
+ if(ret != UPNP_E_SUCCESS){
+ ERROR("State change notification failed (Error code: %d)",ret);
+ return NULL;
+ }
+ MESSAGE("Notification of connection creation sent");
+ this->mVirtualConnections->Add(Connection);
+ return Connection;
+}
+
+bool cConnectionManager::destroyVirtualConnection(int ConnectionID){
+ if(ConnectionID == 0){
+ ERROR("Cannot delete default connection with ID 0!");
+ return false;
+ }
+
+ cVirtualConnection* Connection;
+ for(Connection = this->mVirtualConnections->First(); Connection && Connection->mConnectionID != ConnectionID; Connection = this->mVirtualConnections->Next(Connection)){}
+
+ if(Connection){
+ this->mVirtualConnections->Del(Connection);
+ // Notify the subscribers
+ IXML_Document* PropertySet = NULL;
+ const char* IDs = this->getConnectionIDsCVS();
+ if(!IDs){
+ return false;
+ }
+ UpnpAddToPropertySet(&PropertySet, "CurrentConnectionIDs", IDs);
+ int ret = UpnpNotifyExt(this->mDeviceHandle, UPNP_DEVICE_UDN, UPNP_CMS_SERVICE_ID, PropertySet);
+ ixmlDocument_free(PropertySet);
+
+ if(ret != UPNP_E_SUCCESS){
+ ERROR("State change notification failed (Error code: %d)",ret);
+ return false;
+ }
+ return true;
+ }
+ ERROR("No connection with ID=%d found!", ConnectionID);
+ return false;
+}
+
+const char* cConnectionManager::getConnectionIDsCVS(){
+ cString IDs;
+ for(cVirtualConnection* Connection = this->mVirtualConnections->First(); Connection; Connection = this->mVirtualConnections->Next(Connection)){
+ IDs = cString::sprintf("%s,%d", (*IDs)?*IDs:"", Connection->mConnectionID);
+ }
+ return IDs;
+}
+
+void cConnectionManager::setError(Upnp_Action_Request* Request, int Error){
+ Request->ErrCode = Error;
+ switch(Error){
+ case 701:
+ strn0cpy(Request->ErrStr,_("Incompatible protocol info"),LINE_SIZE);
+ break;
+ case 702:
+ strn0cpy(Request->ErrStr,_("Incompatible directions"),LINE_SIZE);
+ break;
+ case 703:
+ strn0cpy(Request->ErrStr,_("Insufficient network resources"),LINE_SIZE);
+ break;
+ case 704:
+ strn0cpy(Request->ErrStr,_("Local restrictions"),LINE_SIZE);
+ break;
+ case 705:
+ strn0cpy(Request->ErrStr,_("Access denied"),LINE_SIZE);
+ break;
+ case 706:
+ strn0cpy(Request->ErrStr,_("Invalid connection reference"),LINE_SIZE);
+ break;
+ case 707:
+ strn0cpy(Request->ErrStr,_("Not in network"),LINE_SIZE);
+ break;
+ default:
+ cUpnpService::setError(Request, Error);
+ break;
+ }
+}
+
+const char* cVirtualConnection::getDirectionString(eDirection Direction){
+ switch(Direction){
+ case INPUT:
+ return "Input";
+ case OUTPUT:
+ return "Output";
+ default:
+ return NULL;
+ }
+}
+
+const char* cVirtualConnection::getStatusString(eConnectionStatus Status){
+ switch(Status){
+ case OK:
+ return "OK";
+ case CONTENT_FORMAT_MISMATCH:
+ return "ContentFormatMismatch";
+ case INSUFFICIENT_BANDWIDTH:
+ return "InsufficientBandwidth";
+ case UNRELIABLE_CHANNEL:
+ return "UnreliableChannel";
+ case UNKNOWN:
+ return "Unknown";
+ default:
+ return NULL;
+ }
+}
+
+int cVirtualConnection::getConnectionStatus(const char* eConnectionStatus){
+ if(!strcasecmp(eConnectionStatus,"OK"))
+ return OK;
+ if(!strcasecmp(eConnectionStatus,"ContentFormatMismatch"))
+ return CONTENT_FORMAT_MISMATCH;
+ if(!strcasecmp(eConnectionStatus,"InsufficientBandwidth"))
+ return INSUFFICIENT_BANDWIDTH;
+ if(!strcasecmp(eConnectionStatus,"UnreliableChannel"))
+ return UNRELIABLE_CHANNEL;
+ if(!strcasecmp(eConnectionStatus,"Unknown"))
+ return UNKNOWN;
+ return -1;
+}
+
+int cVirtualConnection::getDirection(const char* Direction){
+ if(!strcasecmp(Direction, "Output"))
+ return OUTPUT;
+ if(!strcasecmp(Direction, "Input"))
+ return INPUT;
+ return -1;
+} \ No newline at end of file
diff --git a/upnpcomponents/connectionmanager.h b/upnpcomponents/connectionmanager.h
new file mode 100644
index 0000000..202df59
--- /dev/null
+++ b/upnpcomponents/connectionmanager.h
@@ -0,0 +1,67 @@
+/*
+ * File: connectionmanager.h
+ * Author: savop
+ *
+ * Created on 21. August 2009, 18:35
+ */
+
+#ifndef _CONNECTIONMANAGER_H
+#define _CONNECTIONMANAGER_H
+
+#include "upnpservice.h"
+
+enum eConnectionStatus {
+ OK,
+ CONTENT_FORMAT_MISMATCH,
+ INSUFFICIENT_BANDWIDTH,
+ UNRELIABLE_CHANNEL,
+ UNKNOWN
+};
+
+enum eDirection {
+ OUTPUT,
+ INPUT
+};
+
+class cVirtualConnection : public cListObject {
+ friend class cConnectionManager;
+private:
+ cString mRemoteProtocolInfo;
+ cString mRemoteConnectionManager;
+ eDirection mDirection;
+ int mRemoteConnectionID;
+ int mConnectionID;
+ int mAVTransportID;
+ const int mRcsID;
+ eConnectionStatus mStatus;
+ cVirtualConnection();
+ static const char* getStatusString(eConnectionStatus Status);
+ static const char* getDirectionString(eDirection Direction);
+ static int getDirection(const char* Direction);
+ static int getConnectionStatus(const char* ConnectionStatus);
+};
+
+class cConnectionManager : public cUpnpService {
+public:
+ cConnectionManager(UpnpDevice_Handle DeviceHandle);
+ virtual ~cConnectionManager();
+ virtual int execute(Upnp_Action_Request* Request);
+ virtual int subscribe(Upnp_Subscription_Request* Request);
+ bool setProtocolInfo(const char* ProtocolInfo);
+private:
+ virtual void setError(Upnp_Action_Request* Request, int Error);
+ int getProtocolInfo(Upnp_Action_Request* Request);
+ int getCurrentConnectionIDs(Upnp_Action_Request* Request);
+ int getCurrentConnectionInfo(Upnp_Action_Request* Request);
+ int prepareForConnection(Upnp_Action_Request* Request);
+ int connectionComplete(Upnp_Action_Request* Request);
+ cVirtualConnection* createVirtualConnection(const char* RemoteProtocolInfo = NULL, const char* RemoteConnectionManager = NULL, int RemoteConnectionID = -1, eDirection Direction = OUTPUT);
+ bool destroyVirtualConnection(int ConnectionID);
+ const char* getConnectionIDsCVS();
+ cVirtualConnection* mDefaultConnection;
+ cList<cVirtualConnection>* mVirtualConnections;
+ cString mSupportedProtocols;
+};
+
+#endif /* _CONNECTIONMANAGER_H */
+
diff --git a/upnpcomponents/contentdirectory.cpp b/upnpcomponents/contentdirectory.cpp
new file mode 100644
index 0000000..a9afdfa
--- /dev/null
+++ b/upnpcomponents/contentdirectory.cpp
@@ -0,0 +1,306 @@
+/*
+ * File: contentdirectory.cpp
+ * Author: savop
+ *
+ * Created on 21. August 2009, 16:12
+ */
+
+#include <upnp/ixml.h>
+#include <upnp/upnptools.h>
+#include "contentdirectory.h"
+#include "../common.h"
+#include "../misc/util.h"
+
+cContentDirectory::cContentDirectory(UpnpDevice_Handle DeviceHandle, cMediaDatabase* MediaDatabase)
+: cUpnpService(DeviceHandle) {
+ this->mMediaDatabase = MediaDatabase;
+}
+
+cContentDirectory::~cContentDirectory() {}
+
+int cContentDirectory::subscribe(Upnp_Subscription_Request* Request){
+ IXML_Document* PropertySet = NULL;
+
+ /* The system update ID */
+ UpnpAddToPropertySet(&PropertySet, "SystemUpdateID", itoa(this->mMediaDatabase->getSystemUpdateID()));
+ /* The container update IDs as CSV list */
+ UpnpAddToPropertySet(&PropertySet, "ContainerUpdateIDs", this->mMediaDatabase->getContainerUpdateIDs());
+ /* The transfer IDs, which are not supported, i.e. empty */
+ UpnpAddToPropertySet(&PropertySet, "TransferIDs", "");
+ // Accept subscription
+ int ret = UpnpAcceptSubscriptionExt(this->mDeviceHandle, Request->UDN, Request->ServiceId, PropertySet, Request->Sid);
+
+ if(ret != UPNP_E_SUCCESS){
+ ERROR("Subscription failed (Error code: %d)", ret);
+ }
+
+ ixmlDocument_free(PropertySet);
+ return ret;
+}
+
+void cContentDirectory::Action(){
+ static int Retry = 5;
+ MESSAGE("Start Content directory thread");
+ while(this->Running()){
+ IXML_Document* PropertySet = NULL;
+ UpnpAddToPropertySet(&PropertySet, "SystemUpdateID", itoa(this->mMediaDatabase->getSystemUpdateID()));
+ int ret = UpnpNotifyExt(this->mDeviceHandle, UPNP_DEVICE_UDN, UPNP_CMS_SERVICE_ID, PropertySet);
+ ixmlDocument_free(PropertySet);
+
+ if(ret != UPNP_E_SUCCESS){
+ Retry--;
+ ERROR("State change notification failed (Error code: %d)",ret);
+ ERROR("%d of %d notifications failed", (5-Retry), 5);
+ }
+ else {
+ Retry = 5;
+ }
+ if (!Retry){
+ ERROR("Maximum retries of notifications reached. Stopping...");
+ this->Cancel();
+ }
+ // Sleep 2 seconds
+ cCondWait::SleepMs(2000);
+ }
+}
+
+int cContentDirectory::execute(Upnp_Action_Request* Request){
+ if (Request == NULL) {
+ ERROR("CMS Action Handler - request is null");
+ return UPNP_E_BAD_REQUEST;
+ }
+
+ if(!strcmp(Request->ActionName, UPNP_CDS_ACTION_BROWSE))
+ return this->browse(Request);
+ if(!strcmp(Request->ActionName, UPNP_CDS_ACTION_SEARCHCAPABILITIES))
+ return this->getSearchCapabilities(Request);
+ if(!strcmp(Request->ActionName, UPNP_CDS_ACTION_SORTCAPABILITIES))
+ return this->getSortCapabilities(Request);
+ if(!strcmp(Request->ActionName, UPNP_CDS_ACTION_SYSTEMUPDATEID))
+ return this->getSystemUpdateID(Request);
+
+ return UPNP_E_BAD_REQUEST;
+}
+
+
+int cContentDirectory::browse(Upnp_Action_Request* Request){
+ MESSAGE("Browse requested by %s.", inet_ntoa(Request->CtrlPtIPAddr));
+
+ char* ObjectID = NULL;
+ if(this->parseStringValue(Request->ActionRequest, "ObjectID", &ObjectID)){
+ ERROR("Invalid arguments. ObjectID missing or wrong");
+ this->setError(Request, UPNP_SOAP_E_INVALID_ARGS);
+ return Request->ErrCode;
+ }
+
+ char* BrowseFlag = NULL;
+ bool BrowseMetadata = false;
+ if(this->parseStringValue(Request->ActionRequest, "BrowseFlag", &BrowseFlag)){
+ ERROR("Invalid arguments. Browse flag missing or wrong");
+ this->setError(Request, UPNP_SOAP_E_INVALID_ARGS);
+ return Request->ErrCode;
+ }
+ if(!strcasecmp(BrowseFlag, "BrowseMetadata")){
+ BrowseMetadata = true;
+ }
+ else if(!strcasecmp(BrowseFlag, "BrowseDirectChildren")){
+ BrowseMetadata = false;
+ }
+ else {
+ ERROR("Invalid argument. Browse flag invalid");
+ this->setError(Request, UPNP_SOAP_E_INVALID_ARGS);
+ return Request->ErrCode;
+ }
+
+ char* Filter = NULL;
+ if(this->parseStringValue(Request->ActionRequest, "Filter", &Filter)){
+ ERROR("Invalid arguments. Filter missing or wrong");
+ this->setError(Request, UPNP_SOAP_E_INVALID_ARGS);
+ return Request->ErrCode;
+ }
+
+ int StartingIndex = 0;
+ if(this->parseIntegerValue(Request->ActionRequest, "StartingIndex", &StartingIndex)){
+ ERROR("Invalid arguments. Starting index missing or wrong");
+ this->setError(Request, UPNP_SOAP_E_INVALID_ARGS);
+ return Request->ErrCode;
+ }
+
+ int RequestedCount = 0;
+ if(this->parseIntegerValue(Request->ActionRequest, "RequestedCount", &RequestedCount)){
+ ERROR("Invalid arguments. Requested count missing or wrong");
+ this->setError(Request, UPNP_SOAP_E_INVALID_ARGS);
+ return Request->ErrCode;
+ }
+
+ char* SortCriteria = NULL;
+ if(this->parseStringValue(Request->ActionRequest, "SortCriteria", &SortCriteria)){
+ ERROR("Invalid arguments. Sort criteria missing or wrong");
+ this->setError(Request, UPNP_SOAP_E_INVALID_ARGS);
+ return Request->ErrCode;
+ }
+
+ cUPnPResultSet* ResultSet;
+
+ int ret = this->mMediaDatabase->browse(&ResultSet, ObjectID, BrowseMetadata, Filter, StartingIndex, RequestedCount, SortCriteria);
+ if(ret!=UPNP_E_SUCCESS){
+ ERROR("Error while browsing. Code: %d", ret);
+ this->setError(Request, ret);
+ return Request->ErrCode;
+ }
+
+ char* escapedResult = NULL;
+ escapeXMLCharacters(ResultSet->mResult, &escapedResult);
+
+ if(!escapedResult){
+ ERROR("Escaping XML data failed");
+ this->setError(Request, UPNP_SOAP_E_ACTION_FAILED);
+ return Request->ErrCode;
+ }
+
+ cString Result = cString::sprintf(
+ "<u:%sResponse xmlns:u=\"%s\"> \
+ <Result>%s</Result> \
+ <NumberReturned>%d</NumberReturned> \
+ <TotalMatches>%d</TotalMatches> \
+ <UpdateID>%d</UpdateID> \
+ </u:%sResponse>",
+ Request->ActionName,
+ UPNP_CDS_SERVICE_TYPE,
+ escapedResult,
+ ResultSet->mNumberReturned,
+ ResultSet->mTotalMatches,
+ this->mMediaDatabase->getSystemUpdateID(),
+ Request->ActionName
+ );
+
+ Request->ActionResult = ixmlParseBuffer(Result);
+ Request->ErrCode = UPNP_E_SUCCESS;
+
+ free(escapedResult);
+
+ return Request->ErrCode;
+
+}
+
+int cContentDirectory::getSystemUpdateID(Upnp_Action_Request* Request){
+ cString Result = cString::sprintf(
+ "<u:%sResponse xmlns:u=\"%s\"> \
+ <Id>%d</Id> \
+ </u:%sResponse>",
+ Request->ActionName,
+ UPNP_CDS_SERVICE_TYPE,
+ this->mMediaDatabase->getSystemUpdateID(),
+ Request->ActionName
+ );
+
+ Request->ActionResult = ixmlParseBuffer(Result);
+ Request->ErrCode = UPNP_E_SUCCESS;
+
+ return Request->ErrCode;
+}
+
+int cContentDirectory::getSearchCapabilities(Upnp_Action_Request* Request){
+ MESSAGE("Sorry, no search capabilities yet");
+
+ cString Result = cString::sprintf(
+ "<u:%sResponse xmlns:u=\"%s\"> \
+ <SearchCaps>%s</SearchCaps> \
+ </u:%sResponse>",
+ Request->ActionName,
+ UPNP_CDS_SERVICE_TYPE,
+ UPNP_CDS_SEARCH_CAPABILITIES,
+ Request->ActionName
+ );
+
+ Request->ActionResult = ixmlParseBuffer(Result);
+ Request->ErrCode = UPNP_E_SUCCESS;
+
+ return Request->ErrCode;
+}
+
+int cContentDirectory::getSortCapabilities(Upnp_Action_Request* Request){
+ MESSAGE("Sorry, no sort capabilities yet");
+
+ cString Result = cString::sprintf(
+ "<u:%sResponse xmlns:u=\"%s\"> \
+ <SortCaps>%s</SortCaps> \
+ </u:%sResponse>",
+ Request->ActionName,
+ UPNP_CDS_SERVICE_TYPE,
+ UPNP_CDS_SORT_CAPABILITIES,
+ Request->ActionName
+ );
+
+ Request->ActionResult = ixmlParseBuffer(Result);
+ Request->ErrCode = UPNP_E_SUCCESS;
+
+ return Request->ErrCode;
+}
+
+void cContentDirectory::setError(Upnp_Action_Request* Request, int Error){
+ Request->ErrCode = Error;
+ switch(Error){
+ case UPNP_CDS_E_BAD_METADATA:
+ strn0cpy(Request->ErrStr,_("Bad metadata"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_CANT_PROCESS_REQUEST:
+ strn0cpy(Request->ErrStr,_("Cannot process the request"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_DEST_RESOURCE_ACCESS_DENIED:
+ strn0cpy(Request->ErrStr,_("Destination resource access denied"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_CURRENT_TAG:
+ strn0cpy(Request->ErrStr,_("Invalid current tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_NEW_TAG:
+ strn0cpy(Request->ErrStr,_("Invalid new tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_SEARCH_CRITERIA:
+ strn0cpy(Request->ErrStr,_("Invalid or unsupported search criteria"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_INVALID_SORT_CRITERIA:
+ strn0cpy(Request->ErrStr,_("Invalid or unsupported sort criteria"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_CONTAINER:
+ strn0cpy(Request->ErrStr,_("No such container"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_DESTINATION_RESOURCE:
+ strn0cpy(Request->ErrStr,_("No such destination resource"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_FILE_TRANSFER:
+ strn0cpy(Request->ErrStr,_("No such file transfer"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_OBJECT:
+ strn0cpy(Request->ErrStr,_("No such objectID"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_NO_SUCH_SOURCE_RESOURCE:
+ strn0cpy(Request->ErrStr,_("No such source resource"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_PARAMETER_MISMATCH:
+ strn0cpy(Request->ErrStr,_("Parameter mismatch"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_READ_ONLY_TAG:
+ strn0cpy(Request->ErrStr,_("Read only tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_REQUIRED_TAG:
+ strn0cpy(Request->ErrStr,_("Required tag"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_RESOURCE_ACCESS_DENIED:
+ strn0cpy(Request->ErrStr,_("Resource access denied"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_RESTRICTED_OBJECT:
+ strn0cpy(Request->ErrStr,_("Restricted object"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_RESTRICTED_PARENT:
+ strn0cpy(Request->ErrStr,_("Restricted parent"),LINE_SIZE);
+ break;
+ case UPNP_CDS_E_TRANSFER_BUSY:
+ strn0cpy(Request->ErrStr,_("Transfer busy"),LINE_SIZE);
+ break;
+ default:
+ cUpnpService::setError(Request, Error);
+ break;
+ }
+ return;
+} \ No newline at end of file
diff --git a/upnpcomponents/contentdirectory.h b/upnpcomponents/contentdirectory.h
new file mode 100644
index 0000000..a504fdc
--- /dev/null
+++ b/upnpcomponents/contentdirectory.h
@@ -0,0 +1,38 @@
+/*
+ * File: contentdirectory.h
+ * Author: savop
+ *
+ * Created on 21. August 2009, 16:12
+ */
+
+#ifndef _CONTENTDIRECTORY_H
+#define _CONTENTDIRECTORY_H
+
+#include <upnp/upnp.h>
+#include "upnpservice.h"
+#include "../database/metadata.h"
+
+class cContentDirectory : public cUpnpService, public cThread {
+public:
+ cContentDirectory(UpnpDevice_Handle DeviceHandle, cMediaDatabase* MediaDatabase);
+ virtual ~cContentDirectory();
+ virtual int subscribe(Upnp_Subscription_Request* Request);
+ virtual int execute(Upnp_Action_Request* Request);
+ virtual void setError(Upnp_Action_Request* Request, int Error);
+private:
+ cMediaDatabase* mMediaDatabase;
+ void Action();
+ int getSearchCapabilities(Upnp_Action_Request* Request);
+ int getSortCapabilities(Upnp_Action_Request* Request);
+ int getSystemUpdateID(Upnp_Action_Request* Request);
+ int browse(Upnp_Action_Request* Request);
+// int search(Upnp_Action_Request* Request);
+// int createObject(Upnp_Action_Request* Request);
+// int destroyObject(Upnp_Action_Request* Request);
+// int updateObject(Upnp_Action_Request* Request);
+// int deleteResource(Upnp_Action_Request* Request);
+// int createReference(Upnp_Action_Request* Request);
+};
+
+#endif /* _CONTENTDIRECTORY_H */
+
diff --git a/upnpcomponents/dlna.cpp b/upnpcomponents/dlna.cpp
new file mode 100644
index 0000000..a68c6db
--- /dev/null
+++ b/upnpcomponents/dlna.cpp
@@ -0,0 +1,235 @@
+/*
+ * File: dlna.cpp
+ * Author: savop
+ *
+ * Created on 18. April 2009, 23:27
+ */
+
+#include <stdio.h>
+#include <vdr/tools.h>
+#include "dlna.h"
+
+cDlna* cDlna::mInstance = NULL;
+
+cDlna* cDlna::getInstance(void){
+ if(cDlna::mInstance == NULL)
+ cDlna::mInstance = new cDlna;
+
+ if(cDlna::mInstance != NULL)
+ return cDlna::mInstance;
+ else return NULL;
+}
+
+cDlna::cDlna() {
+ this->mRegisteredProfiles = new cRegisteredProfiles;
+ this->init();
+}
+
+cDlna::~cDlna() {
+ delete this->mRegisteredProfiles;
+}
+
+
+void cDlna::init(void){
+ this->registerMainProfiles();
+}
+
+void cDlna::registerProfile(DLNAProfile* Profile, int Op, const char* Ps, int Ci, unsigned int Flags){
+ cRegisteredProfile *RegisteredProfile = new cRegisteredProfile();
+ RegisteredProfile->Profile = Profile;
+ RegisteredProfile->Operation = Op;
+ RegisteredProfile->PlaySpeeds = Ps;
+ RegisteredProfile->Conversion = Ci;
+ RegisteredProfile->PrimaryFlags = Flags;
+ this->mRegisteredProfiles->Add(RegisteredProfile);
+}
+
+void cDlna::registerMainProfiles(){
+ this->registerProfile(&DLNA_PROFILE_MPEG2_TS_SD_EU, -1, NULL, -1, DLNA_FLAGS_PLUGIN_SUPPORT);
+ this->registerProfile(&DLNA_PROFILE_AVC_TS_HD_EU, -1, NULL, -1, DLNA_FLAGS_PLUGIN_SUPPORT);
+}
+
+const char* cDlna::getSupportedProtocols(){
+ cString Protocols;
+ cRegisteredProfile* Profile;
+ for(Profile = this->mRegisteredProfiles->First(); Profile; Profile = this->mRegisteredProfiles->Next(Profile)){
+ Protocols = cString::sprintf("%s%s%s",(*Protocols)?*Protocols:"",(*Protocols)?",":"",this->getRegisteredProtocolInfoString(Profile));
+ }
+ return Protocols;
+}
+
+const char* cDlna::getProtocolInfo(DLNAProfile *Prof){
+ cRegisteredProfile* Profile;
+ for(Profile = this->mRegisteredProfiles->First(); Profile && Profile->Profile != Prof; Profile = this->mRegisteredProfiles->Next(Profile)){}
+ if(Profile){
+ return this->getRegisteredProtocolInfoString(Profile);
+ }
+ return NULL;
+}
+
+DLNAProfile* cDlna::getProfileOfChannel(cChannel* Channel){
+ if(Channel == NULL) return NULL;
+ // Switching the video types of the DVB-Stream
+ switch(Channel->Vtype()){
+ case 0x02:
+ return &DLNA_PROFILE_MPEG2_TS_SD_EU;
+ case 0x1B:
+ return &DLNA_PROFILE_AVC_TS_HD_EU;
+ default:
+ ERROR("Unknown video type %d for channel %s!", Channel->Vtype(), Channel->Name());
+ return NULL;
+ }
+}
+
+DLNAProfile* cDlna::getProfileOfRecording(cRecording* Recording){
+ // Get the data of the first file of the recording
+ cString File = cString::sprintf(VDR_RECORDFILE_PATTERN_TS, Recording->FileName(), 1);
+ return this->getProfileOfFile(File);
+}
+
+DLNAProfile* cDlna::getProfileOfFile(cString){
+ WARNING("Not yet supported");
+ return NULL;
+}
+
+const char* cDlna::getRegisteredProtocolInfoString(cRegisteredProfile *Profile){
+ cString DLNA4thField = NULL;
+ DLNA4thField = cString::sprintf("DLNA.ORG_PN=%s", Profile->Profile->ID);
+ if(Profile->Operation != -1)
+ DLNA4thField = cString::sprintf("%s;DLNA.ORG_OP=%d",*DLNA4thField,Profile->Operation);
+ if(Profile->PlaySpeeds != NULL)
+ DLNA4thField = cString::sprintf("%s;DLNA.ORG_PS=%s",*DLNA4thField,Profile->PlaySpeeds);
+ if(Profile->Conversion != -1)
+ DLNA4thField = cString::sprintf("%s;DLNA.ORG_CI=%d",*DLNA4thField,Profile->Conversion);
+ if(Profile->PrimaryFlags != 0)
+ DLNA4thField = cString::sprintf("%s;DLNA.ORG_FLAGS=%.8x%.24x",*DLNA4thField,Profile->PrimaryFlags,0);
+
+ char* Protocol = strdup(cString::sprintf("http-get:*:%s:%s", Profile->Profile->mime, *DLNA4thField));
+ return Protocol;
+}
+
+const char* cDlna::getDeviceDescription(const char* URLBase){
+ cString description = cString::sprintf(
+ "<?xml version = \"1.0\" encoding = \"utf-8\"?> \
+ <root xmlns=\"%s\" xmlns:%s=\"%s\"> \
+ <specVersion> \
+ <major>1</major> \
+ <minor>0</minor> \
+ </specVersion> \
+ <URLBase>%s</URLBase> \
+ <device> \
+ <deviceType>%s</deviceType> \
+ <friendlyName>%s</friendlyName> \
+ <manufacturer>%s</manufacturer> \
+ <manufacturerURL>%s</manufacturerURL> \
+ <modelDescription>%s</modelDescription> \
+ <modelName>%s</modelName> \
+ <modelNumber>%s</modelNumber> \
+ <modelURL>%s</modelURL> \
+ <serialNumber>%s</serialNumber> \
+ <UDN>%s</UDN> \
+ <iconList> \
+ <icon> \
+ <mimetype>%s</mimetype> \
+ <width>%d</width> \
+ <height>%d</height> \
+ <depth>%d</depth> \
+ <url>%s</url> \
+ </icon> \
+ <icon> \
+ <mimetype>%s</mimetype> \
+ <width>%d</width> \
+ <height>%d</height> \
+ <depth>%d</depth> \
+ <url>%s</url> \
+ </icon> \
+ <icon> \
+ <mimetype>%s</mimetype> \
+ <width>%d</width> \
+ <height>%d</height> \
+ <depth>%d</depth> \
+ <url>%s</url> \
+ </icon> \
+ <icon> \
+ <mimetype>%s</mimetype> \
+ <width>%d</width> \
+ <height>%d</height> \
+ <depth>%d</depth> \
+ <url>%s</url> \
+ </icon> \
+ </iconList> \
+ <presentationURL>%s</presentationURL> \
+ <%s:X_DLNADOC>%s</dlna:X_DLNADOC> \
+ <serviceList> \
+ <service> \
+ <serviceType>%s</serviceType> \
+ <serviceId>%s</serviceId> \
+ <SCPDURL>%s</SCPDURL> \
+ <controlURL>%s</controlURL> \
+ <eventSubURL>%s</eventSubURL> \
+ </service> \
+ <service> \
+ <serviceType>%s</serviceType> \
+ <serviceId>%s</serviceId> \
+ <SCPDURL>%s</SCPDURL> \
+ <controlURL>%s</controlURL> \
+ <eventSubURL>%s</eventSubURL> \
+ </service> \
+ </serviceList> \
+ </device> \
+ </root>",
+ UPNP_XMLNS_UPNP_DEV, // UPnP Device Namespace (2)
+ UPNP_XMLNS_PREFIX_DLNA, // DLNA Namespace prefix (2)
+ UPNP_XMLNS_DLNA_DEV, // DLNA Device Namespace (2)
+ URLBase, // URLBase (IP:PORT) (7)
+ UPNP_DEVICE_TYPE, // UPnP Device Type (MediaServer:1) (9)
+ UPNP_DEVICE_FRIENDLY_NAME, // UPnP Device Friendly Name (10)
+ UPNP_DEVICE_MANUFACTURER, // UPnP Device Manufacturer (11)
+ UPNP_DEVICE_MANUFACTURER_URL, // UPnP Device Manufacturer URL (12)
+ UPNP_DEVICE_MODEL_DESCRIPTION, // UPnP Device Model Description (13)
+ UPNP_DEVICE_MODEL_NAME, // UPnP Device Model Name (14)
+ UPNP_DEVICE_MODEL_NUMBER, // UPnP Device Model Number (15)
+ UPNP_DEVICE_MODEL_URL, // UPnP Device Model URL (16)
+ UPNP_DEVICE_SERIAL_NUMBER, // UPnP Device Serialnumber (17)
+ UPNP_DEVICE_UDN, // UPnP Device UDN (18)
+ DLNA_ICON_JPEG_LRG_24.mime, // UPnP Device Large Icon JPEG Mimetype (21)
+ DLNA_ICON_JPEG_LRG_24.width, // UPnP Device Large Icon Width (22)
+ DLNA_ICON_JPEG_LRG_24.height, // UPnP Device Large Icon Height (23)
+ DLNA_ICON_JPEG_LRG_24.bitDepth, // UPnP Device Large Icon Bit Depth (24)
+ UPNP_DEVICE_ICON_JPEG_LRG, // UPnP Device Large Icon Path (25)
+ DLNA_ICON_JPEG_SM_24.mime, // UPnP Device Small Icon JPEG Mimetype (28)
+ DLNA_ICON_JPEG_SM_24.width, // UPnP Device Small Icon Width (29)
+ DLNA_ICON_JPEG_SM_24.height, // UPnP Device Small Icon Height (30)
+ DLNA_ICON_JPEG_SM_24.bitDepth, // UPnP Device Small Icon Bit Depth (31)
+ UPNP_DEVICE_ICON_JPEG_SM, // UPnP Device Small Icon Path (32)
+ DLNA_ICON_PNG_SM_24A.mime, // UPnP Device Small Icon PNG Mimetype (35)
+ DLNA_ICON_PNG_SM_24A.width, // UPnP Device Small Icon Width (36)
+ DLNA_ICON_PNG_SM_24A.height, // UPnP Device Small Icon Height (37)
+ DLNA_ICON_PNG_SM_24A.bitDepth, // UPnP Device Small Icon Bit Depth (38)
+ UPNP_DEVICE_ICON_PNG_SM, // UPnP Device Small Icon Path (39)
+ DLNA_ICON_PNG_LRG_24A.mime, // UPnP Device Large Icon PNG Mimetype (42)
+ DLNA_ICON_PNG_LRG_24A.width, // UPnP Device Large Icon Width (43)
+ DLNA_ICON_PNG_LRG_24A.height, // UPnP Device Large Icon Height (44)
+ DLNA_ICON_PNG_LRG_24A.bitDepth, // UPnP Device Large Icon Bit Depth (45)
+ UPNP_DEVICE_ICON_PNG_LRG, // UPnP Device Large Icon Path (46)
+ UPNP_WEB_PRESENTATION_URL, // UPnP Presentation URL (49)
+ UPNP_XMLNS_PREFIX_DLNA, // DLNA Namespace prefix (50)
+ DLNA_DEVICE_DMS_1_5, // DLNA Device Type/Version (50)
+ UPNP_CMS_SERVICE_TYPE, // UPnP CMS Service Type
+ UPNP_CMS_SERVICE_ID, // UPnP CMS Service ID
+ UPNP_CMS_SCPD_URL, // UPnP CMS Service Description
+ UPNP_CMS_CONTROL_URL, // UPnP CMS Control URL
+ UPNP_CMS_EVENT_URL, // UPnP CMS Event URL
+ UPNP_CDS_SERVICE_TYPE, // UPnP CDS Service Type
+ UPNP_CDS_SERVICE_ID, // UPnP CDS Service ID
+ UPNP_CDS_SCPD_URL, // UPnP CDS Service Description
+ UPNP_CDS_CONTROL_URL, // UPnP CDS Control URL
+ UPNP_CDS_EVENT_URL // UPnP CDS Event URL
+// UPNP_AVT_SERVICE_TYPE, // UPnP AVT Service Type
+// UPNP_AVT_SERVICE_ID, // UPnP AVT Service ID
+// UPNP_AVT_SCPD_URL, // UPnP AVT Service Description
+// UPNP_AVT_CONTROL_URL, // UPnP AVT Control URL
+// UPNP_AVT_EVENT_URL // UPnP AVT Event URL
+ );
+ return description;
+} \ No newline at end of file
diff --git a/upnpcomponents/dlna.h b/upnpcomponents/dlna.h
new file mode 100644
index 0000000..c05d69a
--- /dev/null
+++ b/upnpcomponents/dlna.h
@@ -0,0 +1,64 @@
+/*
+ * File: dlna.h
+ * Author: savop
+ *
+ * Created on 18. April 2009, 23:27
+ */
+
+#ifndef _DLNA_H
+#define _DLNA_H
+
+#include "../common.h"
+#include <vdr/channels.h>
+#include <vdr/recording.h>
+
+class cDlna;
+
+class cRegisteredProfile : public cListObject {
+ friend class cDlna;
+private:
+ DLNAProfile* Profile;
+ int Operation;
+ const char* PlaySpeeds;
+ int Conversion;
+ int PrimaryFlags;
+public:
+ cRegisteredProfile(){};
+ virtual ~cRegisteredProfile(){};
+};
+
+class cRegisteredProfiles : public cList<cRegisteredProfile> {
+ friend class cDlna;
+};
+
+/**
+ * Enable DLNA compliant media transfer
+ *
+ * This class enables media transmission with DLNA conformity. Its compliant with
+ * version 1.5 of the DLNA guidelines.
+ *
+ */
+class cDlna {
+ friend class cUPnPServer;
+public:
+ static cDlna* getInstance(void);
+ virtual ~cDlna();
+ //const char* getProtocolInfo(UPnPObjectID OID);
+ const char* getDeviceDescription(const char* URLBase);
+ void registerProfile(DLNAProfile* Profile, int Op = -1, const char* Ps = NULL, int Ci = -1, unsigned int Flags = 0);
+ void registerMainProfiles();
+ const char* getSupportedProtocols();
+ const char* getProtocolInfo(DLNAProfile *Prof);
+ DLNAProfile* getProfileOfChannel(cChannel* Channel);
+ DLNAProfile* getProfileOfRecording(cRecording* Recording);
+ DLNAProfile* getProfileOfFile(cString File);
+private:
+ const char* getRegisteredProtocolInfoString(cRegisteredProfile *Profile);
+ cDlna();
+ void init(void);
+ static cDlna* mInstance;
+ cRegisteredProfiles* mRegisteredProfiles;
+};
+
+#endif /* _DLNA_H */
+
diff --git a/upnpcomponents/upnpservice.cpp b/upnpcomponents/upnpservice.cpp
new file mode 100644
index 0000000..a1d6a47
--- /dev/null
+++ b/upnpcomponents/upnpservice.cpp
@@ -0,0 +1,118 @@
+/*
+ * File: upnpservice.cpp
+ * Author: savop
+ *
+ * Created on 21. August 2009, 18:38
+ */
+
+#include "upnpservice.h"
+#include "../common.h"
+#include "../misc/util.h"
+
+cUpnpService::cUpnpService(UpnpDevice_Handle DeviceHandle) {
+ this->mDeviceHandle = DeviceHandle;
+}
+
+int cUpnpService::parseIntegerValue(IXML_Document* Document, const char* Item, int* Value){
+ char* Val = NULL;
+ int Error = 0;
+
+ Val = ixmlGetFirstDocumentItem(Document, Item, &Error);
+
+ if(Error != 0){
+ ERROR("Error while parsing integer value for item=%s", Item);
+ Error = -1;
+ }
+ else if(!Value){
+ WARNING("Value %s empty!", Item);
+ *Value = 0;
+ }
+ else {
+ *Value = atoi(Val);
+ free(Val);
+ }
+ return Error;
+}
+
+int cUpnpService::parseStringValue(IXML_Document* Document, const char* Item, char** Value){
+ char* Val = NULL;
+ int Error = 0;
+
+ Val = ixmlGetFirstDocumentItem(Document, Item, &Error);
+
+ if(Error != 0){
+ ERROR("Error while parsing string value for item=%s", Item);
+ Error = -1;
+ }
+ else if(!Val){
+ WARNING("Value %s empty!", Item);
+ *Value = NULL;
+ }
+ else {
+ *Value = strdup(Val);
+ free(Val);
+ }
+
+ return Error;
+}
+
+void cUpnpService::setError(Upnp_Action_Request* Request, int Error){
+ Request->ErrCode = Error;
+ switch(Error){
+ case UPNP_SOAP_E_INVALID_ACTION:
+ strn0cpy(Request->ErrStr,_("Invalid action"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_ARGS:
+ strn0cpy(Request->ErrStr,_("Invalid args"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_VAR:
+ strn0cpy(Request->ErrStr,_("Invalid var"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ACTION_FAILED:
+ strn0cpy(Request->ErrStr,_("Action failed"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ARGUMENT_INVALID:
+ strn0cpy(Request->ErrStr,_("Argument value invalid"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ARGUMENT_OUT_OF_RANGE:
+ strn0cpy(Request->ErrStr,_("Argument value out of range"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_ACTION_NOT_IMPLEMENTED:
+ strn0cpy(Request->ErrStr,_("Optional action not implemented"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_OUT_OF_MEMORY:
+ strn0cpy(Request->ErrStr,_("Out of memory"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_HUMAN_INTERVENTION:
+ strn0cpy(Request->ErrStr,_("Human intervention required"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_STRING_TO_LONG:
+ strn0cpy(Request->ErrStr,_("String argument to long"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_NOT_AUTHORIZED:
+ strn0cpy(Request->ErrStr,_("Action not authorized"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_SIGNATURE_FAILURE:
+ strn0cpy(Request->ErrStr,_("Signature failure"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_SIGNATURE_MISSING:
+ strn0cpy(Request->ErrStr,_("Signature missing"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_NOT_ENCRYPTED:
+ strn0cpy(Request->ErrStr,_("Not encrypted"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_SEQUENCE:
+ strn0cpy(Request->ErrStr,_("Invalid sequence"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_INVALID_CONTROL_URL:
+ strn0cpy(Request->ErrStr,_("Invalid control URL"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_NO_SUCH_SESSION:
+ strn0cpy(Request->ErrStr,_("No such session"),LINE_SIZE);
+ break;
+ case UPNP_SOAP_E_OUT_OF_SYNC:
+ default:
+ strn0cpy(Request->ErrStr,_("Unknown error code. Contact the device manufacturer"),LINE_SIZE);
+ break;
+ }
+} \ No newline at end of file
diff --git a/upnpcomponents/upnpservice.h b/upnpcomponents/upnpservice.h
new file mode 100644
index 0000000..c8630b5
--- /dev/null
+++ b/upnpcomponents/upnpservice.h
@@ -0,0 +1,27 @@
+/*
+ * File: upnpservice.h
+ * Author: savop
+ *
+ * Created on 21. August 2009, 18:38
+ */
+
+#ifndef _UPNPSERVICE_H
+#define _UPNPSERVICE_H
+
+#include <upnp/upnp.h>
+
+class cUpnpService {
+public:
+ cUpnpService(UpnpDevice_Handle DeviceHandle);
+ virtual ~cUpnpService(){};
+ virtual int subscribe(Upnp_Subscription_Request* Request) = 0;
+ virtual int execute(Upnp_Action_Request* Request) = 0;
+protected:
+ virtual void setError(Upnp_Action_Request* Request, int Error);
+ int parseIntegerValue(IN IXML_Document* Document, IN const char* Item, OUT int* Value);
+ int parseStringValue(IN IXML_Document* Document, IN const char* Item, OUT char** Value);
+ UpnpDevice_Handle mDeviceHandle;
+};
+
+#endif /* _UPNPSERVICE_H */
+
diff --git a/upnpcomponents/upnpwebserver.cpp b/upnpcomponents/upnpwebserver.cpp
new file mode 100644
index 0000000..9be3d6a
--- /dev/null
+++ b/upnpcomponents/upnpwebserver.cpp
@@ -0,0 +1,335 @@
+/*
+ * File: upnpwebserver.cpp
+ * Author: savop
+ *
+ * Created on 30. Mai 2009, 18:13
+ */
+
+#include <time.h>
+#include <vdr/channels.h>
+#include <map>
+#include <upnp/upnp.h>
+#include "upnpwebserver.h"
+#include "../server/server.h"
+#include "../receiver/livereceiver.h"
+#include "../receiver/recplayer.h"
+#include "../misc/search.h"
+
+/* COPIED FROM INTEL UPNP TOOLS */
+/*******************************************************************************
+ *
+ * Copyright (c) 2000-2003 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************/
+struct File_Info_
+{
+ /** The length of the file. A length less than 0 indicates the size
+ * is unknown, and data will be sent until 0 bytes are returned from
+ * a read call. */
+ off64_t file_length;
+
+ /** The time at which the contents of the file was modified;
+ * The time system is always local (not GMT). */
+ time_t last_modified;
+
+ /** If the file is a directory, {\bf is_directory} contains
+ * a non-zero value. For a regular file, it should be 0. */
+ int is_directory;
+
+ /** If the file or directory is readable, this contains
+ * a non-zero value. If unreadable, it should be set to 0. */
+ int is_readable;
+
+ /** The content type of the file. This string needs to be allocated
+ * by the caller using {\bf ixmlCloneDOMString}. When finished
+ * with it, the SDK frees the {\bf DOMString}. */
+
+ DOMString content_type;
+
+};
+
+struct cWebFileHandle {
+ cString Filename;
+ off64_t Size;
+ cFileHandle* FileHandle;
+};
+
+/****************************************************
+ *
+ * The web server
+ *
+ * Handles the virtual directories and the
+ * provision of data
+ *
+ * Interface between the channels/recordings of the
+ * VDR and the outer world
+ *
+ ****************************************************/
+
+cUPnPWebServer::cUPnPWebServer(const char* root) : mRootdir(root) {
+}
+
+cUPnPWebServer::~cUPnPWebServer(){
+ delete this->mRootdir;
+}
+
+cUPnPWebServer* cUPnPWebServer::mInstance = NULL;
+
+UpnpVirtualDirCallbacks cUPnPWebServer::mVirtualDirCallbacks = {
+ cUPnPWebServer::getInfo,
+ cUPnPWebServer::open,
+ cUPnPWebServer::read,
+ cUPnPWebServer::write,
+ cUPnPWebServer::seek,
+ cUPnPWebServer::close
+};
+
+bool cUPnPWebServer::init(){
+ MESSAGE("Initialize callbacks for virtual directories.");
+
+ if(UpnpSetWebServerRootDir(this->mRootdir) == UPNP_E_INVALID_ARGUMENT){
+ ERROR("The root directory of the webserver is invalid.");
+ return false;
+ }
+ MESSAGE("Setting up callbacks");
+
+ if(UpnpSetVirtualDirCallbacks(&cUPnPWebServer::mVirtualDirCallbacks) == UPNP_E_INVALID_ARGUMENT){
+ ERROR("The virtual directory callbacks are invalid.");
+ return false;
+ }
+
+ if(UpnpIsWebserverEnabled() == FALSE){
+ WARNING("The webserver has not been started. For whatever reason...");
+ return false;
+ }
+
+ MESSAGE("Add virtual directories.");
+ if(UpnpAddVirtualDir(UPNP_DIR_SHARES) == UPNP_E_INVALID_ARGUMENT){
+ ERROR("The virtual directory %s is invalid.",UPNP_DIR_SHARES);
+ return false;
+ }
+ return true;
+}
+
+cUPnPWebServer* cUPnPWebServer::getInstance(const char* rootdir){
+ if(cUPnPWebServer::mInstance == NULL)
+ cUPnPWebServer::mInstance = new cUPnPWebServer(rootdir);
+
+ if(cUPnPWebServer::mInstance){
+ return cUPnPWebServer::mInstance;
+ }
+ else return NULL;
+}
+
+void cUPnPWebServer::free(){
+ delete cUPnPWebServer::mInstance;
+}
+
+int cUPnPWebServer::getInfo(const char* filename, File_Info* info){
+ MESSAGE("Getting information of file '%s'", filename);
+
+ propertyMap Properties;
+ int Method;
+ int Section;
+
+ if(cPathParser::parse(filename, &Section, &Method, &Properties)){
+ switch(Section){
+ case 0:
+ switch(Method){
+ case UPNP_WEB_METHOD_STREAM:
+ {
+ MESSAGE("Stream request");
+ propertyMap::iterator It = Properties.find("resId");
+ unsigned int ResourceID = 0;
+ if(It == Properties.end()){
+ ERROR("No resourceID for stream request");
+ return -1;
+ }
+ else {
+ ResourceID = (unsigned)atoi(It->second);
+ cUPnPResource* Resource = cUPnPResources::getInstance()->getResource(ResourceID);
+ if(!Resource){
+ ERROR("No such resource with ID (%d)", ResourceID);
+ return -1;
+ }
+ else {
+ File_Info_ finfo;
+
+ finfo.content_type = ixmlCloneDOMString(Resource->getContentType());
+ finfo.file_length = Resource->getFileSize();
+ finfo.is_directory = 0;
+ finfo.is_readable = 1;
+ finfo.last_modified = Resource->getLastModification();
+ memcpy(info, &finfo, sizeof(File_Info_));
+
+ MESSAGE("==== File info of Resource #%d ====", Resource->getID());
+ MESSAGE("Size: %lld", finfo.file_length);
+ MESSAGE("Dir: %s", finfo.is_directory?"yes":"no");
+ MESSAGE("Read: %s", finfo.is_readable?"allowed":"not allowed");
+ MESSAGE("Last modified: %s", ctime(&(finfo.last_modified)));
+ MESSAGE("Content-type: %s", finfo.content_type);
+ }
+ }
+ }
+ break;
+ case UPNP_WEB_METHOD_BROWSE:
+ // break;
+ case UPNP_WEB_METHOD_SHOW:
+ // break;
+ case UPNP_WEB_METHOD_SEARCH:
+ case UPNP_WEB_METHOD_DOWNLOAD:
+ default:
+ ERROR("Unknown or unsupported method ID (%d)", Method);
+ return -1;
+ }
+ break;
+ default:
+ ERROR("Unknown or unsupported section ID (%d).", Section);
+ return -1;
+ }
+ }
+ else {
+ return -1;
+ }
+
+ return 0;
+}
+
+UpnpWebFileHandle cUPnPWebServer::open(const char* filename, UpnpOpenFileMode mode){
+ MESSAGE("File %s was opened for %s.",filename,mode==UPNP_READ ? "reading" : "writing");
+
+ propertyMap Properties;
+ int Method;
+ int Section;
+ cWebFileHandle* WebFileHandle = NULL;
+
+ if(cPathParser::parse(filename, &Section, &Method, &Properties)){
+ switch(Section){
+ case 0:
+ switch(Method){
+ case UPNP_WEB_METHOD_STREAM:
+ {
+ MESSAGE("Stream request");
+ propertyMap::iterator It = Properties.find("resId");
+ unsigned int ResourceID = 0;
+ if(It == Properties.end()){
+ ERROR("No resourceID for stream request");
+ return NULL;
+ }
+ else {
+ ResourceID = (unsigned)atoi(It->second);
+ cUPnPResource* Resource = cUPnPResources::getInstance()->getResource(ResourceID);
+ if(!Resource){
+ ERROR("No such resource with ID (%d)", ResourceID);
+ return NULL;
+ }
+ else {
+ WebFileHandle = new cWebFileHandle;
+ WebFileHandle->Filename = Resource->getResource();
+ WebFileHandle->Size = Resource->getFileSize();
+ switch(Resource->getResourceType()){
+ case UPNP_RESOURCE_CHANNEL:
+ {
+ char* ChannelID = strtok(strdup(Resource->getResource()),":");
+ int AudioID = atoi(strtok(NULL,":"));
+ MESSAGE("Try to create Receiver for Channel %s with Audio ID %d", ChannelID, AudioID);
+ cChannel* Channel = Channels.GetByChannelID(tChannelID::FromString(ChannelID));
+ if(!Channel){
+ ERROR("No such channel with ID %s", ChannelID);
+ return NULL;
+ }
+ cLiveReceiver* Receiver = cLiveReceiver::newInstance(Channel,0);
+ if(!Receiver){
+ ERROR("Unable to tune channel. No available tuners?");
+ return NULL;
+ }
+ WebFileHandle->FileHandle = Receiver;
+ }
+ break;
+ case UPNP_RESOURCE_RECORDING:
+ // break;
+ case UPNP_RESOURCE_FILE:
+ // break;
+ case UPNP_RESOURCE_URL:
+ default:
+ return NULL;
+ }
+ }
+ }
+ }
+ break;
+ case UPNP_WEB_METHOD_BROWSE:
+ // break;
+ case UPNP_WEB_METHOD_SHOW:
+ // break;
+ case UPNP_WEB_METHOD_SEARCH:
+ case UPNP_WEB_METHOD_DOWNLOAD:
+ default:
+ ERROR("Unknown or unsupported method ID (%d)", Method);
+ return NULL;
+ }
+ break;
+ default:
+ ERROR("Unknown or unsupported section ID (%d).", Section);
+ return NULL;
+ }
+ }
+ else {
+ return NULL;
+ }
+ MESSAGE("Open the file handle");
+ WebFileHandle->FileHandle->open(mode);
+ return (UpnpWebFileHandle)WebFileHandle;
+}
+
+int cUPnPWebServer::write(UpnpWebFileHandle fh, char* buf, size_t buflen){
+ cWebFileHandle* FileHandle = (cWebFileHandle*)fh;
+ MESSAGE("Writing to %s", *FileHandle->Filename);
+ return FileHandle->FileHandle->write(buf, buflen);
+}
+
+int cUPnPWebServer::read(UpnpWebFileHandle fh, char* buf, size_t buflen){
+ cWebFileHandle* FileHandle = (cWebFileHandle*)fh;
+ MESSAGE("Reading from %s", *FileHandle->Filename);
+ return FileHandle->FileHandle->read(buf, buflen);
+}
+
+int cUPnPWebServer::seek(UpnpWebFileHandle fh, off_t offset, int origin){
+ cWebFileHandle* FileHandle = (cWebFileHandle*)fh;
+ MESSAGE("Seeking on %s", *FileHandle->Filename);
+ return FileHandle->FileHandle->seek(offset, origin);
+}
+
+int cUPnPWebServer::close(UpnpWebFileHandle fh){
+ cWebFileHandle *FileHandle = (cWebFileHandle *)fh;
+ MESSAGE("Closing file %s", *FileHandle->Filename);
+ FileHandle->FileHandle->close();
+ delete FileHandle->FileHandle;
+ delete FileHandle;
+ return 0;
+}
diff --git a/upnpcomponents/upnpwebserver.h b/upnpcomponents/upnpwebserver.h
new file mode 100644
index 0000000..55ef260
--- /dev/null
+++ b/upnpcomponents/upnpwebserver.h
@@ -0,0 +1,123 @@
+/*
+ * File: upnpwebserver.h
+ * Author: savop
+ *
+ * Created on 30. Mai 2009, 18:13
+ */
+
+#ifndef _UPNPWEBSERVER_H
+#define _UPNPWEBSERVER_H
+
+#include "../common.h"
+#include <upnp/upnp.h>
+
+class cUPnPWebServer {
+ friend class cUPnPServer;
+private:
+ static cUPnPWebServer *mInstance;
+ static UpnpVirtualDirCallbacks mVirtualDirCallbacks;
+ const char* mRootdir;
+ cUPnPWebServer(const char* root = "/");
+protected:
+ bool enable(bool enable);
+ static void free();
+public:
+ bool init();
+ static cUPnPWebServer* getInstance(const char* rootdir = "/");
+ virtual ~cUPnPWebServer();
+//};
+
+ /****************************************************
+ *
+ * The callback functions for the webserver
+ *
+ ****************************************************/
+ /**
+ * Retrieve file information
+ *
+ * Returns file related information for an virtual directory file
+ *
+ * @return 0 on success, -1 otherwise
+ * @param filename The filename of which the information is gathered
+ * @param info The File_Info structure with the data
+ */
+ static int getInfo(const char* filename, struct File_Info* info);
+ /**
+ * Opens a virtual directory file
+ *
+ * Opens a file in a virtual directory with the specified mode.
+ *
+ * Possible modes are:
+ * - UPNP_READ : Opens the file for reading
+ * - UPNP_WRITE: Opens the file for writing
+ *
+ * It returns a file handle to the opened file, NULL otherwise
+ *
+ * @return FileHandle to the opened file, NULL otherwise
+ * @param filename The file to open
+ * @param mode UPNP_WRITE for writing, UPNP_READ for reading.
+ */
+ static UpnpWebFileHandle open(const char* filename, UpnpOpenFileMode mode);
+ /**
+ * Reads from the opened file
+ *
+ * Reads <code>buflen</code> bytes from the file and stores the content
+ * to the buffer
+ *
+ * Returns 0 no more bytes read (EOF)
+ * >0 bytes read from file
+ *
+ * @return number of bytes read, 0 on EOF
+ * @param fh the file handle of the opened file
+ * @param buf the buffer to write the bytes to
+ * @param buflen the maximum count of bytes to read
+ *
+ */
+ static int read(UpnpWebFileHandle fh, char* buf, size_t buflen);
+ /**
+ * Writes to the opened file
+ *
+ * Writes <code>buflen</code> bytes from the buffer and stores the content
+ * in the file
+ *
+ * Returns >0 bytes wrote to file, maybe less the buflen in case of write
+ * errors
+ *
+ * @return number of bytes read, 0 on EOF
+ * @param fh the file handle of the opened file
+ * @param buf the buffer to read the bytes from
+ * @param buflen the maximum count of bytes to write
+ *
+ */
+ static int write(UpnpWebFileHandle fh, char* buf, size_t buflen);
+ /**
+ * Seek in the file
+ *
+ * Seeks in the opened file and sets the file pointer to the specified offset
+ *
+ * Returns 0 on success, non-zero value otherwise
+ *
+ * @return 0 on success, non-zero value otherwise
+ * @param fh the file handle of the opened file
+ * @param offset a negative oder positive value which moves the pointer
+ * forward or backward
+ * @param origin SEEK_CUR, SEEK_END or SEEK_SET
+ *
+ */
+ static int seek(UpnpWebFileHandle fh, off_t offset, int origin);
+ /**
+ * Closes the file
+ *
+ * closes the opened file
+ *
+ * Returns 0 on success, non-zero value otherwise
+ *
+ * @return 0 on success, non-zero value otherwise
+ * @param fh the file handle of the opened file
+ *
+ */
+ static int close(UpnpWebFileHandle fh);
+};
+
+#endif /* _UPNPWEBSERVER_H */
+