diff --git a/ChangeLog b/ChangeLog index 45ca322..b8c3a85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -255,6 +255,16 @@ Version 1.8.0 Version 1.6.13 ******************************************************************************* +2011-03-15 Fabrice Fontaine + + New UpnpRegisterRootDevice4 for legacy CPs. + + Add a new UpnpRegisterRootDevice4 which allow user to specify a + description URL to be returned for legacy CPs (for example, CPs + searching for a v1 when the device is v2). Most of those CPs does not + work if they found a v2 in the XML description, so this new function is + only used to solve interoperability issues. + 2011-03-08 Iain Denniston Fix for memory leak. diff --git a/upnp/inc/upnp.h b/upnp/inc/upnp.h index 7e006fc..4f57a30 100644 --- a/upnp/inc/upnp.h +++ b/upnp/inc/upnp.h @@ -128,8 +128,9 @@ #define UPNP_E_BUFFER_TOO_SMALL -106 /*! - * \brief The description document passed to \b UpnpRegisterRootDevice or - * \b UpnpRegisterRootDevice2 is invalid. + * \brief The description document passed to \b UpnpRegisterRootDevice, + * \b UpnpRegisterRootDevice2 \b UpnpRegisterRootDevice3 or + * \b UpnpRegisterRootDevice4 is invalid. */ #define UPNP_E_INVALID_DESC -107 @@ -418,7 +419,8 @@ typedef int UpnpClient_Handle; /*! * \brief Returned when a device application registers with - * \b UpnpRegisterRootDevice or \b UpnpRegisterRootDevice2. + * \b UpnpRegisterRootDevice, \b UpnpRegisterRootDevice2, + * \b UpnpRegisterRootDevice3 or \b UpnpRegisterRootDevice4. * * Device handles can only be used with functions that operate with a device * handle. @@ -840,8 +842,62 @@ EXPORT_SPEC int UpnpRegisterRootDevice3( const int AddressFamily); /*! - * \brief Unregisters a root device registered with \b UpnpRegisterRootDevice or - * \b UpnpRegisterRootDevice2. + * \brief Registers a device application for a specific address family with + * the UPnP library. This function can also be used to specify a dedicated + * description URL to be returned for legacy CPs. + * + * A device application cannot make any other API calls until it registers + * using this function. Device applications can also register as control + * points (see \b UpnpRegisterClient to get a control point handle to perform + * control point functionality). + * + * This is synchronous and does not generate any callbacks. Callbacks can occur + * as soon as this function returns. + * + * \return An integer representing one of the following: + * \li \c UPNP_E_SUCCESS: The operation completed successfully. + * \li \c UPNP_E_FINISH: The SDK is already terminated or + * is not initialized. + * \li \c UPNP_E_INVALID_DESC: The description document was not + * a valid device description. + * \li \c UPNP_E_INVALID_URL: The URL for the description document + * is not valid. + * \li \c UPNP_E_INVALID_PARAM: Either \b Callback or \b Hnd + * is not a valid pointer or \b DescURL is \c NULL. + * \li \c UPNP_E_NETWORK_ERROR: A network error occurred. + * \li \c UPNP_E_SOCKET_WRITE: An error or timeout occurred writing + * to a socket. + * \li \c UPNP_E_SOCKET_READ: An error or timeout occurred reading + * from a socket. + * \li \c UPNP_E_SOCKET_BIND: An error occurred binding a socket. + * \li \c UPNP_E_SOCKET_CONNECT: An error occurred connecting the + * socket. + * \li \c UPNP_E_OUTOF_SOCKET: Too many sockets are currently + * allocated. + * \li \c UPNP_E_OUTOF_MEMORY: There are insufficient resources to + * register this root device. + */ +EXPORT_SPEC int UpnpRegisterRootDevice4( + /*! [in] Pointer to a string containing the description URL for this root + * device instance. */ + const char *DescUrl, + /*! [in] Pointer to the callback function for receiving asynchronous events. */ + Upnp_FunPtr Callback, + /*! [in] Pointer to user data returned with the callback function when invoked. */ + const void *Cookie, + /*! [out] Pointer to a variable to store the new device handle. */ + UpnpDevice_Handle *Hnd, + /*! [in] Address family of this device. Can be AF_INET for an IPv4 device, or + * AF_INET6 for an IPv6 device. Defaults to AF_INET. */ + const int AddressFamily, + /*! [in] Pointer to a string containing the description URL to be returned for + * legacy CPs for this root device instance. */ + const char *LowerDescUrl); + +/*! + * \brief Unregisters a root device registered with \b UpnpRegisterRootDevice, + * \b UpnpRegisterRootDevice2, \b UpnpRegisterRootDevice3 or + * \b UpnpRegisterRootDevice4. * * After this call, the \b UpnpDevice_Handle is no longer valid. For all * advertisements that have not yet expired, the SDK sends a device unavailable diff --git a/upnp/src/api/upnpapi.c b/upnp/src/api/upnpapi.c index 25b86cb..85e844a 100644 --- a/upnp/src/api/upnpapi.c +++ b/upnp/src/api/upnpapi.c @@ -790,6 +790,10 @@ int UpnpRegisterRootDevice( HInfo->aliasInstalled = 0; HInfo->HType = HND_DEVICE; strcpy(HInfo->DescURL, DescUrl); + strcpy(HInfo->LowerDescURL, DescUrl); + UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, + "Following Root Device URL will be used when answering to legacy CPs %s\n", + HInfo->LowerDescURL); HInfo->Callback = Fun; HInfo->Cookie = (void *)Cookie; HInfo->MaxAge = DEFAULT_MAXAGE; @@ -952,6 +956,10 @@ int UpnpRegisterRootDevice2( goto exit_function; } + strcpy(HInfo->LowerDescURL, HInfo->DescURL); + UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, + "Following Root Device URL will be used when answering to legacy CPs %s\n", + HInfo->LowerDescURL); HInfo->aliasInstalled = config_baseURL != 0; HInfo->HType = HND_DEVICE; HInfo->Callback = Fun; @@ -1034,6 +1042,23 @@ int UpnpRegisterRootDevice3( const void *Cookie, UpnpDevice_Handle *Hnd, const int AddressFamily) +{ + UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, + "Inside UpnpRegisterRootDevice3\n"); + return UpnpRegisterRootDevice4(DescUrl, Fun, Cookie, Hnd, + AddressFamily, NULL); +} +#endif /* INCLUDE_DEVICE_APIS */ + + +#ifdef INCLUDE_DEVICE_APIS +int UpnpRegisterRootDevice4( + const char *DescUrl, + Upnp_FunPtr Fun, + const void *Cookie, + UpnpDevice_Handle *Hnd, + const int AddressFamily, + const char *LowerDescUrl) { struct Handle_Info *HInfo; int retVal = 0; @@ -1043,7 +1068,7 @@ int UpnpRegisterRootDevice3( HandleLock(); UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, - "Inside UpnpRegisterRootDevice3\n"); + "Inside UpnpRegisterRootDevice4\n"); if (UpnpSdkInit != 1) { retVal = UPNP_E_FINISH; goto exit_function; @@ -1087,6 +1112,13 @@ int UpnpRegisterRootDevice3( HInfo->aliasInstalled = 0; HInfo->HType = HND_DEVICE; strcpy(HInfo->DescURL, DescUrl); + if (LowerDescUrl == NULL) + strcpy(HInfo->LowerDescURL, DescUrl); + else + strcpy(HInfo->LowerDescURL, LowerDescUrl); + UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, + "Following Root Device URL will be used when answering to legacy CPs %s\n", + HInfo->LowerDescURL); HInfo->Callback = Fun; HInfo->Cookie = (void *)Cookie; HInfo->MaxAge = DEFAULT_MAXAGE; @@ -1109,8 +1141,8 @@ int UpnpRegisterRootDevice3( goto exit_function; } UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, - "UpnpRegisterRootDevice3: Valid Description\n" - "UpnpRegisterRootDevice3: DescURL : %s\n", + "UpnpRegisterRootDevice4: Valid Description\n" + "UpnpRegisterRootDevice4: DescURL : %s\n", HInfo->DescURL); HInfo->DeviceList = ixmlDocument_getElementsByTagName( @@ -1122,7 +1154,7 @@ int UpnpRegisterRootDevice3( ixmlDocument_free(HInfo->DescDocument); FreeHandle(*Hnd); UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__, - "UpnpRegisterRootDevice3: No devices found for RootDevice\n"); + "UpnpRegisterRootDevice4: No devices found for RootDevice\n"); retVal = UPNP_E_INVALID_DESC; goto exit_function; } @@ -1131,26 +1163,26 @@ int UpnpRegisterRootDevice3( HInfo->DescDocument, "serviceList" ); if (!HInfo->ServiceList) { UpnpPrintf(UPNP_CRITICAL, API, __FILE__, __LINE__, - "UpnpRegisterRootDevice3: No services found for RootDevice\n"); + "UpnpRegisterRootDevice4: No services found for RootDevice\n"); } /* * GENA SET UP */ UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, - "UpnpRegisterRootDevice3: Gena Check\n" ); + "UpnpRegisterRootDevice4: Gena Check\n" ); hasServiceTable = getServiceTable( (IXML_Node *)HInfo->DescDocument, &HInfo->ServiceTable, HInfo->DescURL); if (hasServiceTable) { UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, - "UpnpRegisterRootDevice3: GENA Service Table \n" + "UpnpRegisterRootDevice4: GENA Service Table \n" "Here are the known services: \n" ); printServiceTable(&HInfo->ServiceTable, UPNP_ALL, API); } else { UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, - "\nUpnpRegisterRootDevice3: Empty service table\n"); + "\nUpnpRegisterRootDevice4: Empty service table\n"); } if (AddressFamily == AF_INET) { @@ -1163,7 +1195,7 @@ int UpnpRegisterRootDevice3( exit_function: UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, - "Exiting RegisterRootDevice3, return value == %d\n", retVal); + "Exiting RegisterRootDevice4, return value == %d\n", retVal); HandleUnlock(); return retVal; diff --git a/upnp/src/inc/upnpapi.h b/upnp/src/inc/upnpapi.h index 9ae4aae..aa10fcb 100644 --- a/upnp/src/inc/upnpapi.h +++ b/upnp/src/inc/upnpapi.h @@ -80,6 +80,9 @@ struct Handle_Info #ifdef INCLUDE_DEVICE_APIS /*! URL for the use of SSDP. */ char DescURL[LINE_SIZE]; + /*! URL for the use of SSDP when answering to legacy CPs (CP searching + * for a v1 when the device is v2). */ + char LowerDescURL[LINE_SIZE]; /*! XML file path for device description. */ char DescXML[LINE_SIZE]; /* Advertisement timeout */ diff --git a/upnp/src/soap/soap_device.c b/upnp/src/soap/soap_device.c index 422503e..cb64adf 100644 --- a/upnp/src/soap/soap_device.c +++ b/upnp/src/soap/soap_device.c @@ -366,10 +366,13 @@ static int check_soap_body( if (actionNode) { ns = ixmlNode_getNamespaceURI(actionNode); name = ixmlNode_getLocalName(actionNode); + /* Don't check version number, to accept a + * request comming on a v1 service when + * publishing a v2 service */ if (name && ns && !strcmp(actionName, name) && - !strcmp(urn, ns)) + !strncmp(urn, ns, strlen (urn) - 2)) ret_code = UPNP_E_SUCCESS; } } @@ -440,7 +443,9 @@ static int check_soap_action_header( return ret_code; } snprintf(ns_compare, tempSize, "\"%s", urn); - if (strcmp(temp_header_value, ns_compare)) + /* Don't check version number, to accept a request comming on a v1 + * service when publishing a v2 service */ + if (strncmp(temp_header_value, ns_compare, strlen(ns_compare) - 2)) ret_code = UPNP_E_INVALID_ACTION; else { ret_code = UPNP_E_SUCCESS; diff --git a/upnp/src/ssdp/ssdp_server.c b/upnp/src/ssdp/ssdp_server.c index f18b1e3..6d53f55 100644 --- a/upnp/src/ssdp/ssdp_server.c +++ b/upnp/src/ssdp/ssdp_server.c @@ -245,10 +245,18 @@ int AdvertiseAndReply(int AdFlag, UpnpDevice_Handle Hnd, } case SSDP_DEVICETYPE: { if (!strncasecmp(DeviceType, devType, strlen(DeviceType) - 2)) { - if (atoi(&DeviceType[strlen(DeviceType) - - 1]) <= atoi(&devType[strlen(devType) - 1])) { + if (atoi(strrchr(DeviceType, ':') + 1) + < atoi(&devType[strlen(devType) - 1])) { /* the requested version is lower than the device version - * must reply with the lower version number */ + * must reply with the lower version number and the lower + * description URL */ + UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, + "DeviceType=%s and search devType=%s MATCH\n", + devType, DeviceType); + SendReply(DestAddr, DeviceType, 0, UDNstr, SInfo->LowerDescURL, + defaultExp, 1); + } else if (atoi(strrchr(DeviceType, ':') + 1) + == atoi(&devType[strlen(devType) - 1])) { UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "DeviceType=%s and search devType=%s MATCH\n", devType, DeviceType); @@ -347,10 +355,18 @@ int AdvertiseAndReply(int AdFlag, UpnpDevice_Handle Hnd, case SSDP_SERVICE: if (ServiceType) { if (!strncasecmp(ServiceType, servType, strlen(ServiceType) - 2)) { - if (atoi(&ServiceType[strlen(ServiceType) - 1]) <= + if (atoi(strrchr(ServiceType, ':') + 1) < atoi(&servType[strlen(servType) - 1])) { /* the requested version is lower than the service version - * must reply with the lower version number */ + * must reply with the lower version number and the lower + * description URL */ + UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, + "ServiceType=%s and search servType=%s MATCH\n", + ServiceType, servType); + SendReply(DestAddr, ServiceType, 0, UDNstr, SInfo->LowerDescURL, + defaultExp, 1); + } else if (atoi(strrchr (ServiceType, ':') + 1) == + atoi(&servType[strlen(servType) - 1])) { UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "ServiceType=%s and search servType=%s MATCH\n", ServiceType, servType);