1 //******************************************************************
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21 // Defining _POSIX_C_SOURCE macro with 200112L (or greater) as value
22 // causes header files to expose definitions
23 // corresponding to the POSIX.1-2001 base
24 // specification (excluding the XSI extension).
25 // For POSIX.1-2001 base specification,
26 // Refer http://pubs.opengroup.org/onlinepubs/009695399/
27 #define _POSIX_C_SOURCE 200112L
28 #include "occollection.h"
31 #include "ocstackinternal.h"
32 #include "ocresourcehandler.h"
35 #include "oic_malloc.h"
36 #include "oic_string.h"
41 #define WITH_GROUPACTION 1
45 #define TAG PCF("occollection")
47 #define NUM_PARAM_IN_QUERY 2 // The expected number of parameters in a query
48 #define NUM_FIELDS_IN_QUERY 2 // The expected number of fields in a query
50 static OCStackResult CheckRTParamSupport(const OCResource* resource, const char* rtPtr)
52 if(!resource || !rtPtr)
54 return OC_STACK_INVALID_PARAM;
57 OCResourceType* rTPointer = resource->rsrcType;
60 if( strcmp (rTPointer->resourcetypename, rtPtr) == 0)
65 rTPointer = rTPointer->next;
67 return OC_STACK_ERROR;
70 static OCStackResult CheckIFParamSupport(const OCResource* resource, const char* ifPtr)
72 if(!resource || !ifPtr)
74 return OC_STACK_INVALID_PARAM;
77 OCResourceInterface* iFPointer = resource->rsrcInterface;
80 if( strcmp (iFPointer->name, ifPtr) == 0)
85 iFPointer = iFPointer->next;
87 return OC_STACK_ERROR;
91 ValidateQuery (const char *query, OCResourceHandle resource,
92 OCStackIfTypes *ifParam, char **rtParam)
94 uint8_t numFields = 0;
97 //TODO: Query and URL validation is being done for virtual resource case
98 // using ValidateUrlQuery function. We should be able to merge it with this
100 OC_LOG(INFO, TAG, PCF("Entering ValidateQuery"));
103 return OC_STACK_ERROR;
105 if(!ifParam || !rtParam)
107 return OC_STACK_INVALID_PARAM;
112 // Query string is empty
113 OC_LOG_V(INFO, TAG, PCF("Empty query string, use default IF and RT"));
114 *ifParam = STACK_IF_DEFAULT;
115 *rtParam = (char *) OCGetResourceTypeName (resource, 0);
119 // Break the query string to validate it and determine IF and RT parameters
120 // Validate there are atmost 2 parameters in string and that one is 'if' and other 'rt'
121 // separated by token '&' or ';'. Stack will accept both the versions.
123 char *endStr, *ifPtr = NULL, *rtPtr = NULL;
124 char *token = strtok_r ((char *)query, OC_QUERY_SEPARATOR , &endStr);
126 // External loop breaks query string into fields using the & separator
127 while (token != NULL)
131 char *innerToken = strtok_r (token, "=", &endToken);
134 // Internal loop parses the field to extract values (parameters) assigned to each field
135 while (innerToken != NULL)
138 if (strncmp (innerToken, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE)) == 0)
140 // Determine the value of IF parameter
141 innerToken = strtok_r (NULL, "=", &endToken);
144 else if (strcmp (innerToken, OC_RSRVD_RESOURCE_TYPE) == 0)
146 // Determine the value of RT parameter
147 innerToken = strtok_r (NULL, "=", &endToken);
152 innerToken = strtok_r (NULL, "=", &endToken);
155 if (numParam != NUM_PARAM_IN_QUERY)
157 // Query parameter should be of the form if=<string>. String should not have & or =
158 return OC_STACK_INVALID_QUERY;
160 token = strtok_r (NULL, OC_QUERY_SEPARATOR, &endStr);
162 if (numFields > NUM_FIELDS_IN_QUERY)
164 // current release supports one IF value, one RT value and no other params
165 return OC_STACK_INVALID_QUERY;
170 if(CheckIFParamSupport((OCResource *)resource, ifPtr) != OC_STACK_OK)
172 return OC_STACK_INVALID_QUERY;
174 if (strcmp (ifPtr, OC_RSRVD_INTERFACE_DEFAULT) == 0)
176 *ifParam = STACK_IF_DEFAULT;
178 else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_LL) == 0)
180 *ifParam = STACK_IF_LL;
182 else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_BATCH) == 0)
184 *ifParam = STACK_IF_BATCH;
186 else if(strcmp (ifPtr, OC_RSRVD_INTERFACE_GROUP) == 0)
188 *ifParam = STACK_IF_GROUP;
192 return OC_STACK_ERROR;
197 // IF not specified in query, use default IF
198 *ifParam = STACK_IF_DEFAULT;
203 if (CheckRTParamSupport((OCResource *)resource, rtPtr) == OC_STACK_OK)
209 return OC_STACK_INVALID_QUERY;
214 // RT not specified in query. Use the first resource type for the resource as default.
215 *rtParam = (char *) OCGetResourceTypeName (resource, 0);
217 OC_LOG_V(INFO, TAG, "Query params: IF = %d, RT = %s", *ifParam, *rtParam);
223 static OCStackResult BuildRootResourceJSON(OCResource *resource,
224 char * bufferPtr, uint16_t *remaining)
226 OCStackResult ret = OC_STACK_ERROR;
227 cJSON *resObj = NULL;
228 char *jsonStr = NULL;
231 OC_LOG(INFO, TAG, PCF("Entering BuildRootResourceJSON"));
232 resObj = cJSON_CreateObject();
236 ret = OC_STACK_NO_MEMORY;
240 cJSON_AddItemToObject (resObj, OC_RSRVD_HREF, cJSON_CreateString(resource->uri));
241 jsonStr = cJSON_PrintUnformatted (resObj);
245 cJSON_Delete(resObj);
246 return OC_STACK_NO_MEMORY;
249 jsonLen = strlen(jsonStr);
250 if (jsonLen < *remaining)
252 OICStrcpy(bufferPtr, *remaining, jsonStr);
253 *remaining -= jsonLen;
254 bufferPtr += jsonLen;
260 ret = OC_STACK_INVALID_PARAM;
263 cJSON_Delete (resObj);
271 HandleLinkedListInterface(OCEntityHandlerRequest *ehRequest, uint8_t filterOn, char *filterValue)
275 return OC_STACK_INVALID_PARAM;
278 OCStackResult ret = OC_STACK_ERROR;
279 char jsonbuffer[MAX_RESPONSE_LENGTH] = {};
280 size_t jsonbufferLength = 0;
281 uint16_t remaining = 0;
283 OCResource *collResource = (OCResource *)ehRequest->resource;
286 remaining = MAX_RESPONSE_LENGTH;
288 ret = BuildRootResourceJSON(collResource, ptr, &remaining);
290 if (ret == OC_STACK_OK && remaining >= (sizeof (OC_JSON_SEPARATOR) + 1))
292 ptr += strlen((char*)ptr);
293 *ptr = OC_JSON_SEPARATOR;
299 ret = OC_STACK_ERROR;
302 if (ret == OC_STACK_OK)
304 for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
306 OCResource* temp = collResource->rsrcResources[i];
309 //TODO : Update needed here to get correct connectivity type
310 //from ServerRequest data structure.
312 // Function will return error if not enough space in buffer.
313 ret = BuildVirtualResourceResponse(temp, filterOn, filterValue,
314 (char*)ptr, &remaining, CA_ADAPTER_IP);
315 if (ret != OC_STACK_OK)
319 ptr += strlen((char*)ptr);
321 // Check if we have added all resources.
322 if ((i + 1) == MAX_CONTAINED_RESOURCES)
326 // Add separator if more resources and enough space present.
327 if (collResource->rsrcResources[i+1] && remaining > sizeof(OC_JSON_SEPARATOR))
329 *ptr = OC_JSON_SEPARATOR;
333 // No point continuing as no more space on buffer
334 // and/or no more resources.
347 jsonbufferLength = strlen((const char *)jsonbuffer);
348 if(ret == OC_STACK_OK && jsonbufferLength)
350 OCEntityHandlerResponse response = {};
351 response.ehResult = OC_EH_OK;
352 response.payload = jsonbuffer;
353 response.payloadSize = jsonbufferLength + 1;
354 response.persistentBufferFlag = 0;
355 response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
356 response.resourceHandle = (OCResourceHandle) collResource;
357 ret = OCDoResponse(&response);
363 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
365 OCStackResult stackRet = OC_STACK_ERROR;
366 OCEntityHandlerResult ehResult = OC_EH_ERROR;
367 char jsonbuffer[MAX_RESPONSE_LENGTH] = {0};
368 size_t jsonbufferLength = 0;
369 uint16_t remaining = 0;
371 OCResource * collResource = (OCResource *) ehRequest->resource;
374 remaining = MAX_RESPONSE_LENGTH;
376 stackRet = BuildRootResourceJSON(collResource, ptr, &remaining);
377 ptr += strlen((char*)ptr);
379 jsonbufferLength = strlen((const char *)jsonbuffer);
382 OCEntityHandlerResponse response = {};
383 response.ehResult = OC_EH_OK;
384 response.payload = jsonbuffer;
385 response.payloadSize = jsonbufferLength + 1;
386 response.persistentBufferFlag = 0;
387 response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
388 response.resourceHandle = (OCResourceHandle) collResource;
389 stackRet = OCDoResponse(&response);
392 if (stackRet == OC_STACK_OK)
394 for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
396 OCResource* temp = collResource->rsrcResources[i];
399 // Note that all entity handlers called through a collection
400 // will get the same pointer to ehRequest, the only difference
401 // is ehRequest->resource
402 ehRequest->resource = (OCResourceHandle) temp;
404 ehResult = temp->entityHandler(OC_REQUEST_FLAG, ehRequest,
405 temp->entityHandlerCallbackParam);
407 // The default collection handler is returning as OK
408 if(stackRet != OC_STACK_SLOW_RESOURCE)
410 stackRet = OC_STACK_OK;
412 // if a single resource is slow, then entire response will be treated
414 if(ehResult == OC_EH_SLOW)
416 OC_LOG(INFO, TAG, PCF("This is a slow resource"));
417 ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
418 stackRet = EntityHandlerCodeToOCStackCode(ehResult);
426 ehRequest->resource = (OCResourceHandle) collResource;
431 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
436 for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
438 if (resource->rsrcResources[i])
452 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
453 OCEntityHandlerRequest *ehRequest)
455 if(!ehRequest || !ehRequest->query)
457 return OC_STACK_INVALID_PARAM;
460 OCStackResult result = OC_STACK_ERROR;
461 OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
462 char *rtQueryParam = NULL;
464 OC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
466 if (flag != OC_REQUEST_FLAG)
468 return OC_STACK_ERROR;
471 result = ValidateQuery (ehRequest->query,
472 ehRequest->resource, &ifQueryParam, &rtQueryParam);
474 if (result != OC_STACK_OK)
479 if(!((ehRequest->method == OC_REST_GET) ||
480 (ehRequest->method == OC_REST_PUT) ||
481 (ehRequest->method == OC_REST_POST)))
483 return OC_STACK_ERROR;
486 if (ehRequest->method == OC_REST_GET)
488 switch (ifQueryParam)
490 case STACK_IF_DEFAULT:
491 // Get attributes of collection resource and properties of contined resource
492 // M1 release does not support attributes for collection resource, so the GET
493 // operation is same as the GET on LL interface.
494 OC_LOG(INFO, TAG, PCF("STACK_IF_DEFAULT"));
495 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
498 OC_LOG(INFO, TAG, PCF("STACK_IF_LL"));
499 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
502 OC_LOG(INFO, TAG, PCF("STACK_IF_BATCH"));
503 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
504 HandleAggregateResponse;
505 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
506 GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
507 return HandleBatchInterface(ehRequest);
509 return BuildCollectionGroupActionJSONResponse(OC_REST_GET/*flag*/,
510 (OCResource *) ehRequest->resource, ehRequest);
512 return OC_STACK_ERROR;
515 else if (ehRequest->method == OC_REST_PUT)
517 switch (ifQueryParam)
519 case STACK_IF_DEFAULT:
520 // M1 release does not support PUT on default interface
521 return OC_STACK_ERROR;
524 // LL interface only supports GET
525 return OC_STACK_ERROR;
528 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
529 HandleAggregateResponse;
530 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
531 GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
532 return HandleBatchInterface(ehRequest);
536 OC_LOG_V(INFO, TAG, "IF_COLLECTION PUT with request ::\n%s\n ",
537 ehRequest->reqJSONPayload);
538 return BuildCollectionGroupActionJSONResponse(OC_REST_PUT/*flag*/,
539 (OCResource *) ehRequest->resource, ehRequest);
542 return OC_STACK_ERROR;
545 else if (ehRequest->method == OC_REST_POST)
548 switch (ifQueryParam)
552 OC_LOG_V(INFO, TAG, "IF_COLLECTION POST with request :: \n%s\n ",
553 ehRequest->reqJSONPayload);
554 return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
555 (OCResource *) ehRequest->resource, ehRequest);
558 return OC_STACK_ERROR;
561 else if (ehRequest->method == OC_REST_POST)
564 if(ifQueryParam == STACK_IF_GROUP)
566 OC_LOG_V(INFO, TAG, "IF_COLLECTION POST with request :: \n%s\n ",
567 ehRequest->reqJSONPayload);
568 return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
569 (OCResource *) ehRequest->resource, ehRequest);
573 return OC_STACK_ERROR;