Imported Upstream version 0.9.1
[platform/upstream/iotivity.git] / resource / csdk / stack / src / occollection.c
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
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
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
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.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
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"
29 #include <string.h>
30 #include "ocstack.h"
31 #include "ocstackinternal.h"
32 #include "ocresourcehandler.h"
33 #include "logger.h"
34 #include "ocmalloc.h"
35 #include "cJSON.h"
36 #include "ocmalloc.h"
37
38 /// Module Name
39 #include <stdio.h>
40
41 #define WITH_GROUPACTION 1
42
43 #include "oicgroup.h"
44
45 #define TAG PCF("occollection")
46
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
49
50 static OCStackResult CheckRTParamSupport(const OCResource* resource, const char* rtPtr)
51 {
52     if(!resource || !rtPtr)
53     {
54         return OC_STACK_INVALID_PARAM;
55     }
56
57     OCResourceType* rTPointer = resource->rsrcType;
58     while (rTPointer)
59     {
60         if( strcmp (rTPointer->resourcetypename, rtPtr) == 0)
61         {
62             return OC_STACK_OK;
63         }
64
65         rTPointer = rTPointer->next;
66     }
67     return OC_STACK_ERROR;
68 }
69
70 static OCStackResult CheckIFParamSupport(const OCResource* resource, const char* ifPtr)
71 {
72     if(!resource || !ifPtr)
73     {
74         return OC_STACK_INVALID_PARAM;
75     }
76
77     OCResourceInterface* iFPointer = resource->rsrcInterface;
78     while (iFPointer)
79     {
80         if( strcmp (iFPointer->name, ifPtr) == 0)
81         {
82             return OC_STACK_OK;
83         }
84
85         iFPointer = iFPointer->next;
86     }
87     return OC_STACK_ERROR;
88 }
89
90 static OCStackResult
91 ValidateQuery (const char *query, OCResourceHandle resource,
92                              OCStackIfTypes *ifParam, char **rtParam)
93 {
94     uint8_t numFields = 0;
95     uint8_t numParam;
96
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
99     // function.
100     OC_LOG(INFO, TAG, PCF("Entering ValidateQuery"));
101
102     if (!query)
103         return OC_STACK_ERROR;
104
105     if(!ifParam || !rtParam)
106     {
107         return OC_STACK_INVALID_PARAM;
108     }
109
110     if (!(*query))
111     {
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);
116         return OC_STACK_OK;
117     }
118
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     char *endStr, *ifPtr = NULL, *rtPtr = NULL;
122     char *token = strtok_r ((char *)query, "&", &endStr);
123
124     // External loop breaks query string into fields using the & separator
125     while (token != NULL)
126     {
127         numFields++;
128         char *endToken;
129         char *innerToken = strtok_r (token, "=", &endToken);
130         numParam = 0;
131
132         // Internal loop parses the field to extract values (parameters) assigned to each field
133         while (innerToken != NULL)
134         {
135             numParam++;
136             if (strncmp (innerToken, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE)) == 0)
137             {
138                 // Determine the value of IF parameter
139                 innerToken = strtok_r (NULL, "=", &endToken);
140                 ifPtr = innerToken;
141             }
142             else if (strcmp (innerToken, OC_RSRVD_RESOURCE_TYPE) == 0)
143             {
144                 // Determine the value of RT parameter
145                 innerToken = strtok_r (NULL, "=", &endToken);
146                 rtPtr = innerToken;
147             }
148             else
149             {
150                 innerToken = strtok_r (NULL, "=", &endToken);
151             }
152         }
153         if (numParam != NUM_PARAM_IN_QUERY)
154         {
155             // Query parameter should be of the form if=<string>. String should not have & or =
156             return OC_STACK_INVALID_QUERY;
157         }
158         token = strtok_r (NULL, "&", &endStr);
159     }
160     if (numFields > NUM_FIELDS_IN_QUERY)
161     {
162         // current release supports one IF value, one RT value and no other params
163         return OC_STACK_INVALID_QUERY;
164     }
165
166     if (ifPtr)
167     {
168         if(CheckIFParamSupport((OCResource *)resource, ifPtr) != OC_STACK_OK)
169         {
170             return OC_STACK_INVALID_QUERY;
171         }
172         if (strcmp (ifPtr, OC_RSRVD_INTERFACE_DEFAULT) == 0)
173         {
174             *ifParam = STACK_IF_DEFAULT;
175         }
176         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_LL) == 0)
177         {
178             *ifParam = STACK_IF_LL;
179         }
180         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_BATCH) == 0)
181         {
182             *ifParam = STACK_IF_BATCH;
183         }
184         else if(strcmp (ifPtr, OC_RSRVD_INTERFACE_GROUP) == 0)
185         {
186             *ifParam = STACK_IF_GROUP;
187         }
188         else
189         {
190             return OC_STACK_ERROR;
191         }
192     }
193     else
194     {
195         // IF not specified in query, use default IF
196         *ifParam = STACK_IF_DEFAULT;
197     }
198
199     if (rtPtr)
200     {
201         if (CheckRTParamSupport((OCResource *)resource, rtPtr) == OC_STACK_OK)
202         {
203             *rtParam = rtPtr;
204         }
205         else
206         {
207             return OC_STACK_INVALID_QUERY;
208         }
209     }
210     else
211     {
212         // RT not specified in query. Use the first resource type for the resource as default.
213         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
214     }
215     OC_LOG_V(INFO, TAG, "Query params: IF = %d, RT = %s", *ifParam, *rtParam);
216
217     return OC_STACK_OK;
218 }
219
220
221 static OCStackResult BuildRootResourceJSON(OCResource *resource,
222         char * bufferPtr, uint16_t *remaining)
223 {
224     OCStackResult ret = OC_STACK_ERROR;
225     cJSON *resObj = NULL;
226     char *jsonStr = NULL;
227     uint16_t jsonLen;
228
229     OC_LOG(INFO, TAG, PCF("Entering BuildRootResourceJSON"));
230     resObj = cJSON_CreateObject();
231
232     if ( ! resObj)
233     {
234         ret = OC_STACK_NO_MEMORY;
235     }
236     else if (resource)
237     {
238         cJSON_AddItemToObject (resObj, OC_RSRVD_HREF, cJSON_CreateString(resource->uri));
239         jsonStr = cJSON_PrintUnformatted (resObj);
240
241         if(!jsonStr)
242         {
243             cJSON_Delete(resObj);
244             return OC_STACK_NO_MEMORY;
245         }
246
247         jsonLen = strlen(jsonStr);
248         if (jsonLen < *remaining)
249         {
250             strncpy(bufferPtr, jsonStr, jsonLen);
251             *remaining -= jsonLen;
252             bufferPtr += jsonLen;
253             ret = OC_STACK_OK;
254         }
255     }
256     else
257     {
258         ret = OC_STACK_INVALID_PARAM;
259     }
260
261     cJSON_Delete (resObj);
262     OCFree(jsonStr);
263
264     return ret;
265 }
266
267
268 static OCStackResult
269 HandleLinkedListInterface(OCEntityHandlerRequest *ehRequest,
270                        uint8_t filterOn, char *filterValue)
271 {
272     if(!ehRequest)
273     {
274         return OC_STACK_INVALID_PARAM;
275     }
276
277     OCStackResult ret = OC_STACK_ERROR;
278     char jsonbuffer[MAX_RESPONSE_LENGTH] = {};
279     size_t jsonbufferLength = 0;
280     uint16_t remaining = 0;
281     char * ptr = NULL;
282     OCResource * collResource = (OCResource *) ehRequest->resource;
283
284     ptr = jsonbuffer;
285     remaining = MAX_RESPONSE_LENGTH;
286
287     ret = BuildRootResourceJSON(collResource, ptr, &remaining);
288
289     if (ret == OC_STACK_OK && remaining >= (sizeof(OC_JSON_SEPARATOR) + 1))
290     {
291         ptr += strlen((char*)ptr);
292         *ptr = OC_JSON_SEPARATOR;
293         ptr++;
294         remaining--;
295     }
296     else
297     {
298         ret = OC_STACK_ERROR;
299     }
300
301     if (ret == OC_STACK_OK)
302     {
303         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
304         {
305             OCResource* temp = collResource->rsrcResources[i];
306             if (temp)
307             {
308                 //TODO : Update needed here to get correct connectivity type
309                 //from ServerRequest data structure.
310
311                 // Function will return error if not enough space in buffer.
312                 ret = BuildVirtualResourceResponse(temp, filterOn, filterValue,
313                          (char*)ptr, &remaining, CA_IPV4 );
314                 if (ret != OC_STACK_OK)
315                 {
316                     break;
317                 }
318                 ptr += strlen((char*)ptr);
319
320                 // Check if we have added all resources.
321                 if ((i + 1) == MAX_CONTAINED_RESOURCES)
322                 {
323                     break;
324                 }
325                 // Add separator if more resources and enough space present.
326                 if (collResource->rsrcResources[i+1] && remaining > sizeof(OC_JSON_SEPARATOR))
327                 {
328                     *ptr = OC_JSON_SEPARATOR;
329                     ptr++;
330                     remaining--;
331                 }
332                 // No point continuing as no more space on buffer
333                 // and/or no more resources.
334                 else
335                 {
336                     break;
337                 }
338             }
339             else
340             {
341                 break;
342             }
343         }
344     }
345
346     jsonbufferLength = strlen((const char *)jsonbuffer);
347     if(ret == OC_STACK_OK && jsonbufferLength)
348     {
349         OCEntityHandlerResponse response = {};
350         response.ehResult = OC_EH_OK;
351         response.payload = jsonbuffer;
352         response.payloadSize = jsonbufferLength + 1;
353         response.persistentBufferFlag = 0;
354         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
355         response.resourceHandle = (OCResourceHandle) collResource;
356         ret = OCDoResponse(&response);
357     }
358     return ret;
359 }
360
361 static OCStackResult
362 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
363 {
364     OCStackResult stackRet = OC_STACK_ERROR;
365     OCEntityHandlerResult ehResult = OC_EH_ERROR;
366     char jsonbuffer[MAX_RESPONSE_LENGTH] = {0};
367     size_t jsonbufferLength = 0;
368     uint16_t remaining = 0;
369     char * ptr = NULL;
370     OCResource * collResource = (OCResource *) ehRequest->resource;
371
372     ptr = jsonbuffer;
373     remaining = MAX_RESPONSE_LENGTH;
374
375     stackRet = BuildRootResourceJSON(collResource, ptr, &remaining);
376     ptr += strlen((char*)ptr);
377
378     jsonbufferLength = strlen((const char *)jsonbuffer);
379     if(jsonbufferLength)
380     {
381         OCEntityHandlerResponse response = {};
382         response.ehResult = OC_EH_OK;
383         response.payload = jsonbuffer;
384         response.payloadSize = jsonbufferLength + 1;
385         response.persistentBufferFlag = 0;
386         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
387         response.resourceHandle = (OCResourceHandle) collResource;
388         stackRet = OCDoResponse(&response);
389     }
390
391     if (stackRet == OC_STACK_OK)
392     {
393         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
394         {
395             OCResource* temp = collResource->rsrcResources[i];
396             if (temp)
397             {
398                 // Note that all entity handlers called through a collection
399                 // will get the same pointer to ehRequest, the only difference
400                 // is ehRequest->resource
401                 ehRequest->resource = (OCResourceHandle) temp;
402
403                 ehResult = temp->entityHandler(OC_REQUEST_FLAG, ehRequest);
404
405                 // The default collection handler is returning as OK
406                 if(stackRet != OC_STACK_SLOW_RESOURCE)
407                 {
408                     stackRet = OC_STACK_OK;
409                 }
410                 // if a single resource is slow, then entire response will be treated
411                 // as slow response
412                 if(ehResult == OC_EH_SLOW)
413                 {
414                     OC_LOG(INFO, TAG, PCF("This is a slow resource"));
415                     ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
416                     stackRet = EntityHandlerCodeToOCStackCode(ehResult);
417                 }
418             }
419             else
420             {
421                 break;
422             }
423         }
424         ehRequest->resource = (OCResourceHandle) collResource;
425     }
426     return stackRet;
427 }
428
429 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
430 {
431     if(resource)
432     {
433         uint8_t num = 0;
434         for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
435         {
436             if (resource->rsrcResources[i])
437             {
438                 num++;
439             }
440         }
441         return num;
442     }
443     else
444     {
445         return -1;
446     }
447 }
448
449
450 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
451                                               OCEntityHandlerRequest *ehRequest)
452 {
453     if(!ehRequest || !ehRequest->query)
454     {
455         return OC_STACK_INVALID_PARAM;
456     }
457
458     OCStackResult result = OC_STACK_ERROR;
459     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
460     char *rtQueryParam = NULL;
461
462     OC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
463
464     if (flag != OC_REQUEST_FLAG)
465     {
466         return OC_STACK_ERROR;
467     }
468
469     result = ValidateQuery (ehRequest->query,
470                             ehRequest->resource, &ifQueryParam, &rtQueryParam);
471
472     if (result != OC_STACK_OK)
473     {
474         return result;
475     }
476
477     if(!((ehRequest->method == OC_REST_GET) ||
478         (ehRequest->method == OC_REST_PUT) ||
479         (ehRequest->method == OC_REST_POST)))
480     {
481         return OC_STACK_ERROR;
482     }
483
484     if (ehRequest->method == OC_REST_GET)
485     {
486         switch (ifQueryParam)
487         {
488             case STACK_IF_DEFAULT:
489                 // Get attributes of collection resource and properties of contined resource
490                 // M1 release does not support attributes for collection resource, so the GET
491                 // operation is same as the GET on LL interface.
492                 OC_LOG(INFO, TAG, PCF("STACK_IF_DEFAULT"));
493                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
494
495             case STACK_IF_LL:
496                 OC_LOG(INFO, TAG, PCF("STACK_IF_LL"));
497                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
498
499             case STACK_IF_BATCH:
500                 OC_LOG(INFO, TAG, PCF("STACK_IF_BATCH"));
501                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
502                                                                         HandleAggregateResponse;
503                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
504                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
505                 return HandleBatchInterface(ehRequest);
506             case STACK_IF_GROUP:
507                 return BuildCollectionGroupActionJSONResponse(OC_REST_GET/*flag*/,
508                         (OCResource *) ehRequest->resource, ehRequest);
509             default:
510                 return OC_STACK_ERROR;
511         }
512     }
513     else if (ehRequest->method == OC_REST_PUT)
514     {
515         switch (ifQueryParam)
516         {
517             case STACK_IF_DEFAULT:
518                 // M1 release does not support PUT on default interface
519                 return OC_STACK_ERROR;
520
521             case STACK_IF_LL:
522                 // LL interface only supports GET
523                 return OC_STACK_ERROR;
524
525             case STACK_IF_BATCH:
526                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
527                                                                         HandleAggregateResponse;
528                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
529                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
530                 return HandleBatchInterface(ehRequest);
531
532             case STACK_IF_GROUP:
533             {
534                 OC_LOG_V(INFO, TAG, "IF_COLLECTION PUT with request ::\n%s\n ",
535                         ehRequest->reqJSONPayload);
536                 return BuildCollectionGroupActionJSONResponse(OC_REST_PUT/*flag*/,
537                         (OCResource *) ehRequest->resource, ehRequest);
538             }
539             default:
540                 return OC_STACK_ERROR;
541         }
542     }
543     else if (ehRequest->method == OC_REST_POST)
544     {
545
546         switch (ifQueryParam)
547         {
548             case STACK_IF_GROUP:
549             {
550                 OC_LOG_V(INFO, TAG, "IF_COLLECTION POST with request :: \n%s\n ",
551                         ehRequest->reqJSONPayload);
552                 return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
553                         (OCResource *) ehRequest->resource, ehRequest);
554             }
555             default:
556                 return OC_STACK_ERROR;
557         }
558     }
559     else if (ehRequest->method == OC_REST_POST)
560     {
561
562         if(ifQueryParam == STACK_IF_GROUP)
563         {
564             OC_LOG_V(INFO, TAG, "IF_COLLECTION POST with request :: \n%s\n ",
565                     ehRequest->reqJSONPayload);
566             return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
567                     (OCResource *) ehRequest->resource, ehRequest);
568         }
569         else
570         {
571             return OC_STACK_ERROR;
572         }
573     }
574     return result;
575 }
576
577