Changed discovery JSON for spec compliance to contain device ID.
authorMandeep Shetty <mandeep.shetty@intel.com>
Tue, 7 Jul 2015 22:35:12 +0000 (15:35 -0700)
committerErich Keane <erich.keane@intel.com>
Thu, 9 Jul 2015 06:54:28 +0000 (06:54 +0000)
New json schema does not have properties field for resources.
Everything is now a property.
Device ID as string UUID is included in discovery responses once per
response as opposed to being a part of every resource earlier.

Device Ids as part of every resource rep is also accepted and will be
used in resource construction if present.
Refactored filtering and encoding for resource into separate functions.
Removed unnecessary repeated strcmp() to figure out type of virtual
uri.

Change-Id: Ib995e819c8ab63aae6abff956941698d6919f9c4
Signed-off-by: Mandeep Shetty <mandeep.shetty@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/934
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Sakthivel Samidurai <sakthivel.samidurai@intel.com>
Reviewed-by: Erich Keane <erich.keane@intel.com>
resource/csdk/stack/include/internal/ocresourcehandler.h
resource/csdk/stack/include/octypes.h
resource/csdk/stack/src/occollection.c
resource/csdk/stack/src/ocresource.c
resource/examples/simpleclient.cpp
resource/include/OCSerialization.h
resource/include/StringConstants.h

index e068231..b0776b3 100644 (file)
@@ -36,6 +36,7 @@
 #define OC_JSON_SUFFIX_LEN                 (sizeof(OC_JSON_SUFFIX) - 1)
 #define OC_JSON_SEPARATOR                  ','
 #define OC_JSON_SEPARATOR_STR              ","
+#define OC_KEY_VALUE_DELIMITER             "="
 
 /**
  * Static values for various JSON attributes.
@@ -137,8 +138,6 @@ void DeleteDeviceInfo();
  * Prepares a JSON string for response.
  */
 OCStackResult BuildVirtualResourceResponse(const OCResource *resourcePtr,
-                                           uint8_t filterOn,
-                                           const char *filterValue,
                                            char * out,
                                            uint16_t *remaining,
                                            CATransportAdapter_t adapter);
index 48782aa..42c16bc 100644 (file)
@@ -62,6 +62,7 @@ extern "C" {
 #define OC_RSRVD_TTL                    "ttl"
 #define OC_RSRVD_NONCE                  "non"
 #define OC_RSRVD_TRIGGER                "trg"
+#define OC_RSRVD_LINKS                  "links"
 
 #define OC_RSRVD_INTERFACE_DEFAULT      "oic.if.baseline"
 #define OC_RSRVD_INTERFACE_LL           "oic.if.ll"
index d680a0f..83bbfec 100644 (file)
@@ -306,12 +306,11 @@ HandleLinkedListInterface(OCEntityHandlerRequest *ehRequest, uint8_t filterOn, c
             OCResource* temp = collResource->rsrcResources[i];
             if (temp)
             {
-                //TODO : Update needed here to get correct connectivity type
-                //from ServerRequest data structure.
+                //TODO : Add resource type filtering once collections
+                // start supporting queries.
+                ret = BuildVirtualResourceResponse(temp,
+                         (char*)ptr, &remaining, CA_ADAPTER_IP);
 
-                // Function will return error if not enough space in buffer.
-                ret = BuildVirtualResourceResponse(temp, filterOn, filterValue,
-                                         (char*)ptr, &remaining, CA_ADAPTER_IP);
                 if (ret != OC_STACK_OK)
                 {
                     break;
@@ -502,9 +501,12 @@ OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
                 OC_LOG(INFO, TAG, PCF("STACK_IF_BATCH"));
                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
                                                                         HandleAggregateResponse;
+
                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
+
                 return HandleBatchInterface(ehRequest);
+
             case STACK_IF_GROUP:
                 return BuildCollectionGroupActionJSONResponse(OC_REST_GET/*flag*/,
                         (OCResource *) ehRequest->resource, ehRequest);
index 2694ced..063e7da 100644 (file)
@@ -104,6 +104,10 @@ static OCStackResult GetSecurePortInfo(CATransportAdapter_t connType, uint16_t *
     return ret;
 }
 
+/*
+ * Encodes platform info into json and returns a string on the heap.
+ * Caller responsible for freeing the string.
+ */
 static char* GetJSONStringFromPlatformInfo(OCPlatformInfo info)
 {
     cJSON *rootObj = cJSON_CreateObject();
@@ -198,6 +202,10 @@ static char* GetJSONStringFromPlatformInfo(OCPlatformInfo info)
     return jsonEncodedInfo;
 }
 
+/*
+ * Encodes device info into json and returns a string on the heap.
+ * Caller responsible for freeing the string.
+ */
 static char* GetJSONStringFromDeviceInfo(OCDeviceInfo info)
 {
     cJSON *rootObj = cJSON_CreateObject();
@@ -242,86 +250,140 @@ static char* GetJSONStringFromDeviceInfo(OCDeviceInfo info)
     return jsonEncodedInfo;
 }
 
-static OCStackResult ValidateUrlQuery (char *url, char *query,
-                                uint8_t *filterOn, char **filterValue)
+/*
+ * Function will extract 0, 1 or 2 filters from query.
+ * More than 2 filters or unsupported filters will result in error.
+ * If both filters are of the same supported type, the 2nd one will be picked.
+ * Resource and device filters in the SAME query are NOT validated
+ * and resources will likely not clear filters.
+ */
+static OCStackResult ExtractFiltersFromQuery(char *query, char **filterOne, char **filterTwo)
 {
-    if(!filterOn || !filterValue)
+
+    char *key = NULL;
+    char *value = NULL;
+    char *restOfQuery = NULL;
+    int numKeyValuePairsParsed = 0;
+
+    *filterOne = NULL;
+    *filterTwo = NULL;
+
+    OC_LOG_V(INFO, TAG, PCF("Received query %s for param extraction"), query);
+
+    char *keyValuePair = strtok_r (query, OC_QUERY_SEPARATOR, &restOfQuery);
+
+    while(keyValuePair)
     {
-        return OC_STACK_INVALID_PARAM;
+        if (numKeyValuePairsParsed >= 2)
+        {
+            OC_LOG(ERROR, TAG, PCF("More than 2 queries params in URI."));
+            return OC_STACK_INVALID_QUERY;
+        }
+
+        key = strtok_r(keyValuePair, OC_KEY_VALUE_DELIMITER, &value);
+
+        if (!key || !value)
+        {
+            return OC_STACK_INVALID_QUERY;
+        }
+        else if (strcmp (key, OC_RSRVD_INTERFACE) == 0)
+        {
+            *filterOne = value;     // if
+        }
+        else if (strcmp (key, OC_RSRVD_RESOURCE_TYPE) == 0)
+        {
+            *filterTwo = value;     // rt
+        }
+        else
+        {
+            OC_LOG_V(ERROR, TAG, "Unsupported query key: %s", key);
+            return OC_STACK_INVALID_QUERY;
+        }
+        ++numKeyValuePairsParsed;
+
+        keyValuePair = strtok_r(NULL, OC_QUERY_SEPARATOR, &restOfQuery);
     }
 
-    char *filterParam = NULL;
+    OC_LOG_V(INFO, TAG, "Extracted params %s and %s.", *filterOne, *filterTwo);
+    return OC_STACK_OK;
+}
 
-    OC_LOG(INFO, TAG, PCF("Entering ValidateUrlQuery"));
-    if (!url)
+static OCVirtualResources GetTypeOfVirtualURI(char *uriInRequest)
+{
+    if (strcmp (uriInRequest, GetVirtualResourceUri(OC_WELL_KNOWN_URI)) == 0)
     {
-        return OC_STACK_INVALID_URI;
+        return OC_WELL_KNOWN_URI;
+    }
+    else if (strcmp (uriInRequest, GetVirtualResourceUri(OC_DEVICE_URI)) == 0)
+    {
+        return OC_DEVICE_URI;
+    }
+    else if (strcmp (uriInRequest, GetVirtualResourceUri(OC_PLATFORM_URI)) == 0)
+    {
+        return OC_PLATFORM_URI;
     }
 
-    if (strcmp ((char *)url, GetVirtualResourceUri(OC_WELL_KNOWN_URI)) == 0 ||
-                strcmp ((char *)url, GetVirtualResourceUri(OC_DEVICE_URI)) == 0 ||
-                strcmp((char *)url, GetVirtualResourceUri(OC_PLATFORM_URI)) == 0)
+    #ifdef WITH_PRESENCE
+    else
     {
-        *filterOn = STACK_RES_DISCOVERY_NOFILTER;
-        if (query && *query)
-        {
-            char* strTokPtr = NULL;
-            filterParam = strtok_r((char *)query, "=", &strTokPtr);
-            *filterValue = strtok_r(NULL, " ", &strTokPtr);
+        return OC_PRESENCE;
+    }
+    #endif
+}
 
-            if (!(*filterValue) || ! filterParam)
-            {
-                return OC_STACK_INVALID_QUERY;
-            }
-            else if (strcmp (filterParam, OC_RSRVD_INTERFACE) == 0)
-            {
-                // Resource discovery with interface filter
-                *filterOn = STACK_RES_DISCOVERY_IF_FILTER;
-            }
-            else if (strcmp (filterParam, OC_RSRVD_RESOURCE_TYPE) == 0)
-            {
-                // Resource discovery with resource type filter
-                *filterOn = STACK_RES_DISCOVERY_RT_FILTER;
-            }
-            else if (strcmp (filterParam, OC_RSRVD_DEVICE_ID) == 0)
-            {
-                //Device ID filter
-                *filterOn = STACK_DEVICE_DISCOVERY_DI_FILTER;
-            }
-            else if (strcmp (filterParam, OC_RSRVD_DEVICE_NAME) == 0)
-            {
-                //Device Name filter
-                *filterOn = STACK_DEVICE_DISCOVERY_DN_FILTER;
-            }
-            else
-            {
-                // Other filter types not supported
-                return OC_STACK_INVALID_QUERY;
-            }
-        }
+static OCStackResult getQueryParamsForFiltering (OCVirtualResources uri, char *query,
+                                            char **filterOne, char **filterTwo)
+{
+    if(!filterOne || !filterTwo)
+    {
+        return OC_STACK_INVALID_PARAM;
     }
+
+    *filterOne = NULL;
+    *filterTwo = NULL;
+
     #ifdef WITH_PRESENCE
-    else if (strcmp((char *)url, GetVirtualResourceUri(OC_PRESENCE)) == 0)
+    if (uri == OC_PRESENCE)
     {
         //Nothing needs to be done, except for pass a OC_PRESENCE query through as OC_STACK_OK.
-        OC_LOG(INFO, TAG, PCF("OC_PRESENCE Request"));
-        *filterOn = STACK_RES_DISCOVERY_NOFILTER;
+        OC_LOG(INFO, TAG, PCF("OC_PRESENCE Request for virtual resource."));
+        return OC_STACK_OK;
     }
     #endif
-    else
+
+    OCStackResult result = OC_STACK_OK;
+
+    if (query && *query)
     {
-        // Other URIs not yet supported
-        return OC_STACK_INVALID_URI;
+        result = ExtractFiltersFromQuery(query, filterOne, filterTwo);
     }
-    OC_LOG(INFO, TAG, PCF("Exiting ValidateUrlQuery"));
-    return OC_STACK_OK;
+
+    return result;
 }
 
+OCStackResult copyStringIfEnoughSpace(char* dest, const char *src, uint16_t *remaining)
+{
+    if (!dest || !src || !remaining)
+    {
+        return OC_STACK_INVALID_PARAM;
+    }
+
+    size_t srcLen = strlen(src);
 
-OCStackResult
-BuildVirtualResourceResponse(const OCResource *resourcePtr, uint8_t filterOn,
-                       const char *filterValue, char *out, uint16_t *remaining,
-                       CATransportAdapter_t adapter)
+    if (srcLen < *remaining)
+    {
+        OICStrcpy(dest, *remaining, src);
+        *remaining = *remaining - srcLen;
+        return OC_STACK_OK;
+    }
+    else
+    {
+        return OC_STACK_ERROR;
+    }
+}
+
+OCStackResult BuildVirtualResourceResponse(const OCResource *resourcePtr, char *out,
+                        uint16_t *remaining, CATransportAdapter_t adapter )
 {
     if(!resourcePtr || !out  || !remaining)
     {
@@ -331,114 +393,61 @@ BuildVirtualResourceResponse(const OCResource *resourcePtr, uint8_t filterOn,
     OCResourceType *resourceTypePtr = NULL;
     OCResourceInterface *interfacePtr = NULL;
     cJSON *resObj = NULL;
-    cJSON *propObj = NULL;
     cJSON *policyObj = NULL;
     cJSON *rtArray = NULL;
     char *jsonStr = NULL;
-    uint8_t encodeRes = 0;
     OCStackResult ret = OC_STACK_OK;
-    uint16_t jsonLen = 0;
 
-    OC_LOG(INFO, TAG, PCF("Entering BuildVirtualResourceResponse"));
+    OC_LOG_V(INFO, TAG, PCF("Encoding resource %s"), resourcePtr->uri);
+
     resObj = cJSON_CreateObject();
 
     if (resourcePtr)
     {
-        encodeRes = 0;
-        if ((filterOn == STACK_RES_DISCOVERY_RT_FILTER) && filterValue)
-        {
-            resourceTypePtr = resourcePtr->rsrcType;
-            while (resourceTypePtr)
-            {
-                if (strcmp (resourceTypePtr->resourcetypename, filterValue) == 0)
-                {
-                    encodeRes = 1;
-                    break;
-                }
-                resourceTypePtr = resourceTypePtr->next;
-            }
-        }
-        else if ((filterOn == STACK_RES_DISCOVERY_IF_FILTER) && filterValue)
-        {
-            interfacePtr = resourcePtr->rsrcInterface;
-            while (interfacePtr)
-            {
-                if (strcmp (interfacePtr->name, filterValue) == 0)
-                {
-                    encodeRes = 1;
-                    break;
-                }
-                interfacePtr = interfacePtr->next;
-            }
-        }
-        else if (filterOn == STACK_RES_DISCOVERY_NOFILTER)
+        // URI
+        cJSON_AddItemToObject (resObj, OC_RSRVD_HREF, cJSON_CreateString(resourcePtr->uri));
+
+        // resource types
+        cJSON_AddItemToObject (resObj, OC_RSRVD_RESOURCE_TYPE, rtArray = cJSON_CreateArray());
+        resourceTypePtr = resourcePtr->rsrcType;
+        while (resourceTypePtr)
         {
-            encodeRes = 1;
+            cJSON_AddItemToArray (rtArray,
+                                  cJSON_CreateString(resourceTypePtr->resourcetypename));
+            resourceTypePtr = resourceTypePtr->next;
         }
-        else
+        // interfaces
+        cJSON_AddItemToObject (resObj, OC_RSRVD_INTERFACE, rtArray = cJSON_CreateArray());
+        interfacePtr = resourcePtr->rsrcInterface;
+        while (interfacePtr)
         {
-            //TODO: Unsupported query filter
-            return OC_STACK_INVALID_QUERY;
+            cJSON_AddItemToArray (rtArray, cJSON_CreateString(interfacePtr->name));
+            interfacePtr = interfacePtr->next;
         }
 
-        if (encodeRes)
-        {
-            // Add URIs
-            cJSON_AddItemToObject(resObj, OC_RSRVD_HREF, cJSON_CreateString(resourcePtr->uri));
-
-            // Add server instance id
-            cJSON_AddItemToObject(resObj,
-                                   OC_RSRVD_SERVER_INSTANCE_ID,
-                                   cJSON_CreateString(OCGetServerInstanceIDString()));
-
-
-            cJSON_AddItemToObject (resObj, OC_RSRVD_PROPERTY, propObj = cJSON_CreateObject());
-            // Add resource types
-            cJSON_AddItemToObject(propObj, OC_RSRVD_RESOURCE_TYPE, rtArray = cJSON_CreateArray());
-            resourceTypePtr = resourcePtr->rsrcType;
-            while (resourceTypePtr)
-            {
-                cJSON_AddItemToArray(rtArray,
-                                      cJSON_CreateString(resourceTypePtr->resourcetypename));
-                resourceTypePtr = resourceTypePtr->next;
-            }
-            // Add interface types
-            cJSON_AddItemToObject(propObj, OC_RSRVD_INTERFACE, rtArray = cJSON_CreateArray());
-            interfacePtr = resourcePtr->rsrcInterface;
-            while (interfacePtr)
-            {
-                cJSON_AddItemToArray(rtArray, cJSON_CreateString(interfacePtr->name));
-                interfacePtr = interfacePtr->next;
-            }
-
-            //Add Policy
-            cJSON_AddItemToObject(propObj, OC_RSRVD_POLICY, policyObj = cJSON_CreateObject());
+        // Policy
+        cJSON_AddItemToObject (resObj, OC_RSRVD_POLICY, policyObj = cJSON_CreateObject());
 
-            if (policyObj)
+        if (policyObj)
+        {
+            cJSON_AddNumberToObject (policyObj, OC_RSRVD_BITMAP,
+                             resourcePtr->resourceProperties & (OC_OBSERVABLE|OC_DISCOVERABLE));
+            // Set secure flag for secure resources
+            if (resourcePtr->resourceProperties & OC_SECURE)
             {
-                // Policy Property Bitmap
-                // If resource is discoverable, set discoverability flag.
-                // Resources that are not discoverable will not have the flag.
-                cJSON_AddNumberToObject(policyObj, OC_RSRVD_BITMAP,
-                                 resourcePtr->resourceProperties & (OC_OBSERVABLE|OC_DISCOVERABLE));
-
-                // Set secure flag for secure resources
-                if (resourcePtr->resourceProperties & OC_SECURE)
+                cJSON_AddNumberToObject(policyObj, OC_RSRVD_SECURE, OC_RESOURCE_SECURE);
+                //Set the IP port also as secure resources are hosted on a different port
+                uint16_t port = 0;
+                if (GetSecurePortInfo(adapter, &port) == OC_STACK_OK)
                 {
-                    cJSON_AddNumberToObject(policyObj, OC_RSRVD_SECURE, OC_RESOURCE_SECURE);
-                    //Set the IP port also as secure resources are hosted on a different port
-                    uint16_t port = 0;
-                    if (GetSecurePortInfo(adapter, &port) == OC_STACK_OK)
-                    {
-                        cJSON_AddNumberToObject(policyObj, OC_RSRVD_HOSTING_PORT, port);
-                    }
+                    cJSON_AddNumberToObject(policyObj, OC_RSRVD_HOSTING_PORT, port);
                 }
             }
-            else
-            {
-                cJSON_Delete(resObj);
-                return OC_STACK_NO_MEMORY;
-            }
+        }
+        else
+        {
+            cJSON_Delete(resObj);
+            return OC_STACK_NO_MEMORY;
         }
     }
     jsonStr = cJSON_PrintUnformatted (resObj);
@@ -449,25 +458,15 @@ BuildVirtualResourceResponse(const OCResource *resourcePtr, uint8_t filterOn,
         return OC_STACK_NO_MEMORY;
     }
 
-    jsonLen = strlen(jsonStr);
-    if (jsonLen < *remaining)
-    {
-        OICStrcpy(out, *remaining, jsonStr);
-        *remaining = *remaining - jsonLen;
-    }
-    else
-    {
-        ret = OC_STACK_ERROR;
-    }
+    ret = copyStringIfEnoughSpace(out, jsonStr, remaining);
+
     cJSON_Delete (resObj);
     OICFree (jsonStr);
 
-    OC_LOG(INFO, TAG, PCF("Exiting BuildVirtualResourceResponse"));
     return ret;
 }
 
-OCStackResult BuildVirtualResourceResponseForDevice(uint8_t filterOn, char *filterValue,
-                                                    char *out, uint16_t *remaining)
+OCStackResult BuildVirtualResourceResponseForDevice(char *out, uint16_t *remaining)
 {
     if(!out || !remaining)
     {
@@ -476,25 +475,12 @@ OCStackResult BuildVirtualResourceResponseForDevice(uint8_t filterOn, char *filt
 
     OCStackResult ret = OC_STACK_ERROR;
     char *jsonStr = NULL;
-    uint16_t jsonLen = 0;
 
     jsonStr = GetJSONStringFromDeviceInfo(savedDeviceInfo);
 
     if(jsonStr)
     {
-        jsonLen = strlen(jsonStr);
-
-        if (jsonLen < *remaining)
-        {
-            OICStrcpy(out, *remaining, jsonStr);
-            *remaining = *remaining - jsonLen;
-            ret = OC_STACK_OK;
-        }
-        else
-        {
-            ret = OC_STACK_ERROR;
-        }
-
+        ret = copyStringIfEnoughSpace(out, jsonStr, remaining);
         OICFree(jsonStr);
     }
     else
@@ -507,25 +493,19 @@ OCStackResult BuildVirtualResourceResponseForDevice(uint8_t filterOn, char *filt
 
 OCStackResult BuildVirtualResourceResponseForPlatform(char *out, uint16_t *remaining)
 {
-    OCStackResult ret = OC_STACK_OK;
+    if(!out || !remaining)
+    {
+        return OC_STACK_INVALID_PARAM;
+    }
+
+    OCStackResult ret = OC_STACK_ERROR;
+    char *jsonStr = NULL;
 
-    char *jsonStr = GetJSONStringFromPlatformInfo(savedPlatformInfo);
+    jsonStr = GetJSONStringFromPlatformInfo(savedPlatformInfo);
 
     if(jsonStr)
     {
-        size_t jsonLen = strlen(jsonStr);
-
-        if (jsonLen < *remaining)
-        {
-            OICStrcpy(out, *remaining, jsonStr);
-            *remaining = *remaining - jsonLen;
-            ret = OC_STACK_OK;
-        }
-        else
-        {
-            OC_LOG_V(ERROR, TAG, PCF("Platform info string too big. len: %u"), jsonLen);
-            ret = OC_STACK_ERROR;
-        }
+        ret = copyStringIfEnoughSpace(out, jsonStr, remaining);
         OICFree(jsonStr);
     }
     else
@@ -534,10 +514,9 @@ OCStackResult BuildVirtualResourceResponseForPlatform(char *out, uint16_t *remai
         ret = OC_STACK_ERROR;
     }
 
-
     return ret;
-
 }
+
 const char * GetVirtualResourceUri( OCVirtualResources resource)
 {
     if (resource < OC_MAX_VIRTUAL_RESOURCES)
@@ -720,150 +699,264 @@ OCStackResult EntityHandlerCodeToOCStackCode(OCEntityHandlerResult ehResult)
     return result;
 }
 
-static OCStackResult
-HandleVirtualResource(OCServerRequest *request, OCResource *resource)
+static bool resourceMatchesRTFilter(OCResource *resource, char *resourceTypeFilter)
+{
+    if (!resource)
+    {
+        return false;
+    }
+
+    // Null or empty is analogous to no filter.
+    if (resourceTypeFilter == NULL || *resourceTypeFilter == 0)
+    {
+        return true;
+    }
+
+    OCResourceType *resourceTypePtr = resource->rsrcType;
+
+    while (resourceTypePtr)
+    {
+        if (strcmp (resourceTypePtr->resourcetypename, resourceTypeFilter) == 0)
+        {
+            return true;
+        }
+        resourceTypePtr = resourceTypePtr->next;
+    }
+
+    OC_LOG_V(INFO, TAG, PCF("%s does not contain rt=%s."), resource->uri, resourceTypeFilter);
+    return false;
+}
+
+static bool resourceMatchesIFFilter(OCResource *resource, char *interfaceFilter)
+{
+    if (!resource)
+    {
+        return false;
+    }
+
+    // Null or empty is analogous to no filter.
+    if (interfaceFilter == NULL || *interfaceFilter == 0)
+    {
+        return true;
+    }
+
+    OCResourceInterface *interfacePtr = resource->rsrcInterface;
+
+    while (interfacePtr)
+    {
+        if (strcmp (interfacePtr->name, interfaceFilter) == 0)
+        {
+            return true;
+        }
+        interfacePtr = interfacePtr->next;
+    }
+
+    OC_LOG_V(INFO, TAG, PCF("%s does not contain if=%s."), resource->uri, interfaceFilter);
+    return false;
+}
+
+/*
+ * If the filters are null, they will be assumed to NOT be present
+ * and the resource will not be matched against them.
+ * Function will return true if all non null AND non empty filters passed in find a match.
+ */
+static bool includeThisResourceInResponse(OCResource *resource,
+                                                 char *interfaceFilter,
+                                                 char *resourceTypeFilter)
+{
+    if (!resource)
+    {
+        OC_LOG(ERROR, TAG, PCF("Invalid resource"));
+        return false;
+    }
+
+    if ( !(resource->resourceProperties & OC_ACTIVE) ||
+         !(resource->resourceProperties & OC_DISCOVERABLE))
+    {
+        OC_LOG_V(INFO, TAG, PCF("%s not ACTIVE or DISCOVERABLE"), resource->uri);
+        return false;
+    }
+
+    return resourceMatchesIFFilter(resource, interfaceFilter) &&
+           resourceMatchesRTFilter(resource, resourceTypeFilter);
+
+}
+
+OCStackResult SendNonPersistantDiscoveryResponse(OCServerRequest *request, OCResource *resource,
+                                char *discoveryPayload)
+{
+    OCEntityHandlerResponse response = {};
+
+    response.ehResult = OC_EH_OK;
+    response.payload = discoveryPayload;
+    response.payloadSize = strlen((const char *)discoveryPayload) + 1;
+    response.persistentBufferFlag = 0;
+    response.requestHandle = (OCRequestHandle) request;
+    response.resourceHandle = (OCResourceHandle) resource;
+
+    return OCDoResponse(&response);
+}
+
+static OCStackResult appendDeviceIDAndOpenLinksArray (char *out, uint16_t *remaining)
+{
+    if (!out || !remaining)
+    {
+        return OC_STACK_INVALID_PARAM;
+    }
+
+    const char *deviceID = OCGetServerInstanceIDString();
+
+    char deviceIDJSONField[MAX_RESPONSE_LENGTH] = {};
+
+    // Format is : {"di":"UUID","links":[
+    sprintf (deviceIDJSONField, "{\"%s\":\"%s\"%s\"%s\":[", OC_RSRVD_DEVICE_ID, deviceID,
+                            OC_JSON_SEPARATOR_STR, OC_RSRVD_LINKS);
+
+    uint16_t lenDeviceIDJSON = strlen(deviceIDJSONField);
+
+    if (lenDeviceIDJSON > *remaining)
+    {
+        return OC_STACK_ERROR;
+    }
+    else
+    {
+        OICStrcat(out, *remaining, deviceIDJSONField);
+        *remaining -= lenDeviceIDJSON;
+    }
+
+    return OC_STACK_OK;
+}
+
+static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource* resource)
 {
     if (!request || !resource)
     {
         return OC_STACK_INVALID_PARAM;
     }
 
-    OCStackResult result = OC_STACK_ERROR;
-    char *filterValue = NULL;
-    uint8_t filterOn = 0;
-    uint16_t remaining = 0;
-    char * ptr = NULL;
-    uint8_t firstLoopDone = 0;
-    char discoveryResBuf[MAX_RESPONSE_LENGTH] = {};
+    OCStackResult discoveryResult = OC_STACK_ERROR;
+    char *filterOne = NULL;
+    char *filterTwo = NULL;
+    uint16_t remaining  = 0;
+
+    char discoveryBuffer[MAX_RESPONSE_LENGTH] = {};
 
     OC_LOG(INFO, TAG, PCF("Entering HandleVirtualResource"));
 
-    result = ValidateUrlQuery (request->resourceUrl,
-            request->query, &filterOn,
-            &filterValue);
+    OCVirtualResources virtualUriInRequest = GetTypeOfVirtualURI (request->resourceUrl);
 
-    if (result == OC_STACK_OK)
+    remaining = MAX_RESPONSE_LENGTH - sizeof ('\0');
+
+    if (virtualUriInRequest == OC_WELL_KNOWN_URI)
     {
-        if (strcmp ((char *)request->resourceUrl, GetVirtualResourceUri(OC_WELL_KNOWN_URI)) == 0)
+        discoveryResult = getQueryParamsForFiltering (virtualUriInRequest, request->query,
+                                            &filterOne, &filterTwo);
+        if (discoveryResult != OC_STACK_OK)
         {
-            ptr = discoveryResBuf;
-            remaining = MAX_RESPONSE_LENGTH;
+            OC_LOG_V(ERROR, TAG, "Error (%d) validating query.\n", discoveryResult);
+            return discoveryResult;
+        }
+        char *ptrIntoBuffer = discoveryBuffer;
 
-            // Check if valid resource and enough space in buffer for atleast
-            // the null character.
-            while(resource && (remaining > 1))
-            {
-                if((resource->resourceProperties & OC_ACTIVE)
-                        && (resource->resourceProperties & OC_DISCOVERABLE))
-                {
-                    // if there is data on the buffer, we have already added a response,
-                    // so we need to add a comma before we do anything
-                    if(firstLoopDone
-                            && remaining >= (sizeof(OC_JSON_SEPARATOR)+1))
-                    {
-                        *ptr = OC_JSON_SEPARATOR;
-                        ptr++;
-                        remaining--;
-                    }
-                    firstLoopDone = 1;
-                    result = BuildVirtualResourceResponse(resource, filterOn,
-                                filterValue, (char*)ptr, &remaining,
-                                (CATransportAdapter_t)request->devAddr.adapter);
-
-                    if (result != OC_STACK_OK)
-                    {
-                        // if this failed, we need to remove the comma added above.
-                        if(firstLoopDone)
-                        {
-                            ptr--;
-                            *ptr = '\0';
-                            remaining++;
-                        }
-                        break;
-                    }
-                    ptr += strlen((char *)ptr);
-                }
-                resource = resource->next;
-            }
+        discoveryResult = appendDeviceIDAndOpenLinksArray(ptrIntoBuffer, &remaining);
 
-            if(strlen((const char *)discoveryResBuf) > 0)
+        while(resource && discoveryResult == OC_STACK_OK)
+        {
+            if(includeThisResourceInResponse(resource, filterOne, filterTwo))
             {
-                OCEntityHandlerResponse response = {};
+                ptrIntoBuffer += strlen(ptrIntoBuffer);
 
-                response.ehResult = OC_EH_OK;
-                response.payload = discoveryResBuf;
-                response.payloadSize = strlen((const char *)discoveryResBuf) + 1;
-                response.persistentBufferFlag = 0;
-                response.requestHandle = (OCRequestHandle) request;
-                response.resourceHandle = (OCResourceHandle) resource;
+                discoveryResult = BuildVirtualResourceResponse(resource, ptrIntoBuffer,
+                                        &remaining, (CATransportAdapter_t)request->devAddr.adapter);
 
-                result = OCDoResponse(&response);
-            }
-        }
-        else if (strcmp ((char *)request->resourceUrl, GetVirtualResourceUri(OC_DEVICE_URI)) == 0)
-        {
-            remaining = MAX_RESPONSE_LENGTH;
-            ptr = discoveryResBuf;
+                if (discoveryResult != OC_STACK_OK)
+                {
+                    OC_LOG_V(INFO, TAG, "Error (%d) encoding %s", discoveryResult, resource->uri);
+                    break;
+                }
 
-            result = BuildVirtualResourceResponseForDevice(filterOn, filterValue,
-                    (char*)ptr, &remaining);
+                ptrIntoBuffer += strlen(ptrIntoBuffer);
+                if (remaining > sizeof(OC_JSON_SEPARATOR))
+                {
+                    *ptrIntoBuffer = OC_JSON_SEPARATOR;
+                    ++ptrIntoBuffer;
+                    --remaining;
+                }
+                else
+                {
+                    OC_LOG(INFO, TAG, PCF("Out of space in buffer"));
+                    break;
+                }
 
-            if(result == OC_STACK_OK)
-            {
-                ptr += strlen((char*)ptr);
             }
+            resource = resource->next;
+        }
 
-            if(remaining < MAX_RESPONSE_LENGTH)
-            {
-                OCEntityHandlerResponse response = {0};
-
-                response.ehResult = OC_EH_OK;
-                response.payload = discoveryResBuf;
-                response.payloadSize = strlen((const char *)discoveryResBuf) + 1;
-                response.persistentBufferFlag = 0;
-                response.requestHandle = (OCRequestHandle) request;
-                response.resourceHandle = (OCResourceHandle) resource;
+        size_t lenDiscoveryBuff = strlen (discoveryBuffer);
 
-                result = OCDoResponse(&response);
-            }
+        // Traling separator replaced with closing of "links" array.
+        // discoveryBuffer guaranteed to be null terminated as "remaining"
+        // accounts for '\0' when initialized.
+        if (lenDiscoveryBuff > 0 && discoveryBuffer[lenDiscoveryBuff - 1] == OC_JSON_SEPARATOR)
+        {
+            discoveryBuffer[lenDiscoveryBuff - 1] = ']';
         }
-        else if (strcmp ((char *)request->resourceUrl, GetVirtualResourceUri(OC_PLATFORM_URI)) == 0)
+        else
         {
-            remaining = MAX_RESPONSE_LENGTH;
-            ptr = discoveryResBuf;
-
-            result = BuildVirtualResourceResponseForPlatform((char*)ptr, &remaining);
+            // If No trailing separator then close links array anyway.
+            discoveryBuffer[lenDiscoveryBuff] = ']';
+        }
+        --remaining;
+        ptrIntoBuffer += strlen(ptrIntoBuffer);
 
-            if(result == OC_STACK_OK)
-            {
-                ptr += strlen((char*)ptr);
-            }
+        // Close array element brace.
+        if (remaining > sizeof ('}'))
+        {
+            *ptrIntoBuffer = '}';
+            --remaining;
+        }
+        else
+        {
+            discoveryResult = OC_STACK_ERROR;
+        }
+    }
 
-            if(remaining < MAX_RESPONSE_LENGTH)
-            {
-                OCEntityHandlerResponse response = {0};
+    else if (virtualUriInRequest == OC_DEVICE_URI)
+    {
+        discoveryResult = BuildVirtualResourceResponseForDevice(discoveryBuffer, &remaining);
+    }
+    else if (virtualUriInRequest == OC_PLATFORM_URI)
+    {
+        discoveryResult = BuildVirtualResourceResponseForPlatform(discoveryBuffer, &remaining);
+    }
 
-                response.ehResult = OC_EH_OK;
-                response.payload = discoveryResBuf;
-                response.payloadSize = strlen((const char *)discoveryResBuf) + 1;
-                response.persistentBufferFlag = 0;
-                response.requestHandle = (OCRequestHandle) request;
-                response.resourceHandle = (OCResourceHandle) resource;
+    #ifdef WITH_PRESENCE
+    else
+    {
+        if(resource->resourceProperties & OC_ACTIVE)
+        {
+            discoveryResult = SendPresenceNotification(resource->rsrcType,
+                                                OC_PRESENCE_TRIGGER_CHANGE);
+        }
+    }
+    #endif
 
-                result = OCDoResponse(&response);
-            }
+    // Presence uses observer notification api to respond via SendPresenceNotification.
+    if (virtualUriInRequest != OC_PRESENCE)
+    {
+        if(discoveryResult == OC_STACK_OK)
+        {
+            discoveryResult = SendNonPersistantDiscoveryResponse(request, resource,
+                                                        discoveryBuffer);
         }
-        #ifdef WITH_PRESENCE
         else
         {
-            if(resource->resourceProperties & OC_ACTIVE){
-                SendPresenceNotification(resource->rsrcType, OC_PRESENCE_TRIGGER_CHANGE);
-            }
+            OC_LOG_V(ERROR, TAG, "Error (%d) building (%d)  discovery response. "\
+                        "Not responding to request.", discoveryResult, virtualUriInRequest);
         }
-        #endif
     }
-    result = OC_STACK_OK;
-    return result;
+
+    return discoveryResult;
 }
 
 static OCStackResult
@@ -921,6 +1014,7 @@ HandleResourceWithEntityHandler (OCServerRequest *request,
     OCEntityHandlerRequest ehRequest = {};
 
     OC_LOG(INFO, TAG, PCF("Entering HandleResourceWithEntityHandler"));
+
     result = FormOCEntityHandlerRequest(&ehRequest, (OCRequestHandle) request,
             request->method, (OCResourceHandle) resource, request->query,
             request->reqJSONPayload, request->numRcvdVendorSpecificHeaderOptions,
@@ -933,10 +1027,10 @@ HandleResourceWithEntityHandler (OCServerRequest *request,
         OC_LOG(INFO, TAG, PCF("No observation requested"));
         ehFlag = OC_REQUEST_FLAG;
     }
-    else if(ehRequest.obsInfo.action == OC_OBSERVE_REGISTER &&
-            !collectionResource)
+    else if(ehRequest.obsInfo.action == OC_OBSERVE_REGISTER && !collectionResource)
     {
-        OC_LOG(INFO, TAG, PCF("Registering observation requested"));
+        OC_LOG(INFO, TAG, PCF("Observation registration requested"));
+
         result = GenerateObserverId(&ehRequest.obsInfo.obsId);
         VERIFY_SUCCESS(result, OC_STACK_OK);
 
@@ -955,9 +1049,9 @@ HandleResourceWithEntityHandler (OCServerRequest *request,
         else
         {
             result = OC_STACK_OK;
-            // The error in observeResult for the request will be
-            // used when responding to this request by omitting
-            // the observation option/sequence number.
+
+            // The error in observeResult for the request will be used when responding to this
+            // request by omitting the observation option/sequence number.
             request->observeResult = OC_STACK_ERROR;
             OC_LOG(ERROR, TAG, PCF("Observer Addition failed"));
             ehFlag = OC_REQUEST_FLAG;
index e606908..4d62ac2 100644 (file)
@@ -454,7 +454,7 @@ int main(int argc, char* argv[]) {
         // makes it so that all boolean values are printed as 'true/false' in this stream
         std::cout.setf(std::ios::boolalpha);
         // Find all resources
-        requestURI << OC_MULTICAST_DISCOVERY_URI << "?rt=core.light";
+        requestURI << OC_MULTICAST_DISCOVERY_URI;// << "?rt=core.light";
 
         OCPlatform::findResource("", requestURI.str(),
                 CT_DEFAULT, &foundResource);
index 96b4b35..3668116 100644 (file)
@@ -36,100 +36,155 @@ namespace OC
             Secure
         };
 
-        class ListenResourceContainer
+        class DiscoveredResources
         {
-            class ListenResourcePolicyContainer
+            class Resource
             {
                 friend class cereal::access;
-                friend class ListenResourceContainer;
-                friend class ListenResourcePropertiesContainer;
 
-                template<class Archive>
-                void serialize(Archive& ar)
+                class ResourcePolicy
                 {
-                    try
+                    friend class cereal::access;
+                    friend class Resource;
+
+                    template<class Archive>
+                    void serialize(Archive& ar)
                     {
-                        m_observable = false;
-                        ar(cereal::make_nvp(OC::Key::BMKEY, m_bm));
-                        // In case of observable
-                        if(m_bm & OC_OBSERVABLE)
+                        try
+                        {
+                            m_observable = false;
+                            ar(cereal::make_nvp(OC::Key::BMKEY, m_bm));
+
+                            if(m_bm & OC_OBSERVABLE)
+                            {
+                                m_observable = true;
+                            }
+                        }
+                        catch(cereal::Exception&)
+                        {
+                            ar.setNextName(nullptr);
+                        }
+
+                        try
+                        {
+                            m_secure = false;
+                            int secureTemp;
+                            ar(cereal::make_nvp(OC::Key::SECUREKEY, secureTemp));
+                            m_secure = secureTemp != 0;
+
+                            m_port = -1;
+                            ar(cereal::make_nvp(OC::Key::PORTKEY, m_port));
+                        }
+                        catch(cereal::Exception&)
                         {
-                            m_observable = true;
+                            ar.setNextName(nullptr);
                         }
+                     }
+
+                     bool m_observable;
+                     uint8_t m_bm;
+                     bool m_secure;
+                     int m_port;
+                };
+
+                template <class Archive>
+                void serialize(Archive& ar)
+                {
+                    try
+                    {
+                        ar(cereal::make_nvp(OC::Key::URIKEY, m_uri));
+                        m_loaded = true;
                     }
                     catch(cereal::Exception&)
                     {
                         ar.setNextName(nullptr);
                     }
+
                     try
                     {
-                        m_secure = false;
-                        int secureTemp;
-                        ar(cereal::make_nvp(OC::Key::SECUREKEY, secureTemp));
-                        m_secure = secureTemp != 0;
-
-                        m_port = -1;
-                        ar(cereal::make_nvp(OC::Key::PORTKEY, m_port));
+                        ar(cereal::make_nvp(OC::Key::RESOURCETYPESKEY,m_resourceTypes));
+                        m_loaded = true;
                     }
                     catch(cereal::Exception&)
                     {
-                       ar.setNextName(nullptr);
+                        ar.setNextName(nullptr);
                     }
 
-                 }
-
-                 bool m_observable;
-                 uint8_t m_bm;
-                 bool m_secure;
-                 int m_port;
-            };
-
-            class ListenResourcePropertiesContainer
-            {
-                friend class cereal::access;
-                friend class ListenResourceContainer;
-
-                template<class Archive>
-                void serialize(Archive& ar)
-                {
                     try
                     {
-                        ar(cereal::make_nvp(OC::Key::POLICYKEY, m_policy));
-
+                        ar(cereal::make_nvp(OC::Key::INTERFACESKEY, m_interfaces));
+                        m_loaded = true;
                     }
                     catch(cereal::Exception&)
                     {
-                        // we swallow this exception, since it means the key
-                        // doesn't exist, allowing these to be optional
-                        oclog() << "Invalid POLICYKEY"<<std::flush;
                         ar.setNextName(nullptr);
                     }
 
                     try
                     {
-                        ar(cereal::make_nvp(OC::Key::RESOURCETYPESKEY,m_resourceTypes));
+                        ar(cereal::make_nvp(OC::Key::POLICYKEY, m_policy));
+                        m_loaded = true;
                     }
                     catch(cereal::Exception&)
                     {
                         ar.setNextName(nullptr);
                     }
+
+                    // Although not expected, a server id as part of a resource's own
+                    // representation is legal. It may be used if needed.
                     try
                     {
-                        ar(cereal::make_nvp(OC::Key::INTERFACESKEY, m_interfaces));
+                        ar(cereal::make_nvp(OC::Key::DEVICEIDKEY, m_serverId));
+                        m_loaded = true;
                     }
                     catch(cereal::Exception&)
                     {
                         ar.setNextName(nullptr);
                     }
                 }
+            public:
+                Resource(): m_loaded(false)
+                {}
+
+                bool observable() const
+                {
+                    return m_policy.m_observable;
+                }
+
+                OCSecureType secureType() const
+                {
+                    return m_policy.m_secure ? OCSecureType::Secure : OCSecureType::NotSecure;
+                }
+
+                int port() const
+                {
+                    return m_policy.m_port;
+                }
+
+                std::vector<std::string> resourceTypes() const
+                {
+                    return m_resourceTypes;
+                }
+
+                std::vector<std::string> interfaces() const
+                {
+                    return m_interfaces;
+                }
+
+                bool loaded() const{
+                    return m_loaded;
+                }
 
+                std::string m_uri;
+                std::string m_serverId;
                 std::vector<std::string> m_resourceTypes;
                 std::vector<std::string> m_interfaces;
-                ListenResourcePolicyContainer m_policy;
+                ResourcePolicy m_policy;
+                bool m_loaded;
             };
 
             public:
-            ListenResourceContainer() : m_loaded(false)
+            DiscoveredResources()
             {}
 
             private:
@@ -141,68 +196,19 @@ namespace OC
             {
                 try
                 {
-                    ar(cereal::make_nvp(OC::Key::URIKEY, m_uri));
-                    m_loaded=true;
-                }
-                catch(cereal::Exception&)
-                {
-                    ar.setNextName(nullptr);
-                }
-                try
-                {
-                    ar(cereal::make_nvp(OC::Key::SERVERIDKEY, m_serverId));
-                    m_loaded=true;
-                }
-                catch(cereal::Exception&)
-                {
-                    ar.setNextName(nullptr);
+                    ar(cereal::make_nvp(OC::Key::DEVICEIDKEY, m_serverIdOfThisDevice));
                 }
+                catch(cereal::Exception&) { ar.setNextName(nullptr); }
+
                 try
                 {
-                    ar(cereal::make_nvp(OC::Key::PROPERTYKEY, m_props));
-                    m_loaded=true;
+                    ar(cereal::make_nvp(OC::Key::LINKS, resources));
                 }
-                catch(cereal::Exception&)
-                {
-                    ar.setNextName(nullptr);
-                }
-
-            }
-
-            std::string m_uri;
-            std::string m_serverId;
-            bool m_loaded;
-            ListenResourcePropertiesContainer m_props;
-
-            bool loaded() const
-            {
-                return m_loaded;
-            }
-
-            bool observable() const
-            {
-                return m_props.m_policy.m_observable;
-            }
-
-            OCSecureType secureType() const
-            {
-                return m_props.m_policy.m_secure ? OCSecureType::Secure : OCSecureType::NotSecure;
-            }
-
-            int port() const
-            {
-                return m_props.m_policy.m_port;
+                catch(cereal::Exception&) { ar.setNextName(nullptr); }
             }
 
-            std::vector<std::string> resourceTypes() const
-            {
-                return m_props.m_resourceTypes;
-            }
-
-            std::vector<std::string> interfaces() const
-            {
-                return m_props.m_interfaces;
-            }
+            std::string m_serverIdOfThisDevice;
+            std::vector<Resource> resources;
         };
 
         private:
@@ -210,13 +216,14 @@ namespace OC
             template <class Archive>
             void serialize(Archive& ar)
             {
-                std::vector<ListenResourceContainer> resources;
+                std::vector<DiscoveredResources> resources;
                 ar(resources);
             }
         public:
             ListenOCContainer(std::weak_ptr<IClientWrapper> cw,
                     const OCDevAddr& devAddr, std::stringstream& json)
                     : m_clientWrapper(cw), m_devAddr(devAddr)
+
             {
                 LoadFromJson(json);
             }
@@ -231,28 +238,41 @@ namespace OC
             {
                 cereal::JSONInputArchive archive(json);
 
-                std::vector<ListenResourceContainer> resources;
+                std::vector<DiscoveredResources> resources;
                 archive(cereal::make_nvp(OC::Key::OCKEY, resources));
 
                 m_resources.clear();
 
-                for(const auto& res : resources)
+                for(const auto& resourcesAtDevice : resources)
                 {
-                    try
+                    std::string serverIDForThisResourceRep = resourcesAtDevice.m_serverIdOfThisDevice;
+
+                    for (const auto& resource : resourcesAtDevice.resources)
                     {
-                        if(res.loaded())
+                        try
                         {
-                            m_resources.push_back(std::shared_ptr<OCResource>(
-                                new OCResource(m_clientWrapper, m_devAddr,
-                                    res.m_uri, res.m_serverId, res.observable(),
-                                    res.resourceTypes(), res.interfaces())));
+                            if(resource.loaded())
+                            {
+                                m_resources.push_back(std::shared_ptr<OCResource>
+                                (
+                                    new OCResource
+                                    (
+                                        m_clientWrapper,
+                                        m_devAddr,
+                                        resource.m_uri,
+                                        serverIDForThisResourceRep,
+                                        resource.observable(),
+                                        resource.resourceTypes(),
+                                        resource.interfaces()
+                                    )
+                                ));
+                            }
+                        }
+                        catch(ResourceInitException& e)
+                        {
+                            oclog() << "listenCallback(): failed to create resource: " << e.what()
+                                    << std::flush;
                         }
-
-                    }
-                    catch(ResourceInitException& e)
-                    {
-                        oclog() << "listenCallback(): failed to create resource: " << e.what()
-                                << std::flush;
                     }
                 }
             }
index beb70b2..3c1588b 100644 (file)
@@ -132,7 +132,9 @@ namespace OC
         static const std::string REPKEY                     = "rep";
         static const std::string SECUREKEY                  = "sec";
         static const std::string PORTKEY                    = "port";
-        static const std::string SERVERIDKEY                = "sid";
+        static const std::string DEVICEIDKEY                = "di";
+        static const std::string LINKS                      = "links";
+
     }
 
 }