d680a0ff98501085a91d6afc69181a2af0300ff4
[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 "cJSON.h"
35 #include "oic_malloc.h"
36 #include "oic_string.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     // separated by token '&' or ';'. Stack will accept both the versions.
122
123     char *endStr, *ifPtr = NULL, *rtPtr = NULL;
124     char *token = strtok_r ((char *)query, OC_QUERY_SEPARATOR , &endStr);
125
126     // External loop breaks query string into fields using the & separator
127     while (token != NULL)
128     {
129         numFields++;
130         char *endToken;
131         char *innerToken = strtok_r (token, "=", &endToken);
132         numParam = 0;
133
134         // Internal loop parses the field to extract values (parameters) assigned to each field
135         while (innerToken != NULL)
136         {
137             numParam++;
138             if (strncmp (innerToken, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE)) == 0)
139             {
140                 // Determine the value of IF parameter
141                 innerToken = strtok_r (NULL, "=", &endToken);
142                 ifPtr = innerToken;
143             }
144             else if (strcmp (innerToken, OC_RSRVD_RESOURCE_TYPE) == 0)
145             {
146                 // Determine the value of RT parameter
147                 innerToken = strtok_r (NULL, "=", &endToken);
148                 rtPtr = innerToken;
149             }
150             else
151             {
152                 innerToken = strtok_r (NULL, "=", &endToken);
153             }
154         }
155         if (numParam != NUM_PARAM_IN_QUERY)
156         {
157             // Query parameter should be of the form if=<string>. String should not have & or =
158             return OC_STACK_INVALID_QUERY;
159         }
160         token = strtok_r (NULL, OC_QUERY_SEPARATOR, &endStr);
161     }
162     if (numFields > NUM_FIELDS_IN_QUERY)
163     {
164         // current release supports one IF value, one RT value and no other params
165         return OC_STACK_INVALID_QUERY;
166     }
167
168     if (ifPtr)
169     {
170         if(CheckIFParamSupport((OCResource *)resource, ifPtr) != OC_STACK_OK)
171         {
172             return OC_STACK_INVALID_QUERY;
173         }
174         if (strcmp (ifPtr, OC_RSRVD_INTERFACE_DEFAULT) == 0)
175         {
176             *ifParam = STACK_IF_DEFAULT;
177         }
178         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_LL) == 0)
179         {
180             *ifParam = STACK_IF_LL;
181         }
182         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_BATCH) == 0)
183         {
184             *ifParam = STACK_IF_BATCH;
185         }
186         else if(strcmp (ifPtr, OC_RSRVD_INTERFACE_GROUP) == 0)
187         {
188             *ifParam = STACK_IF_GROUP;
189         }
190         else
191         {
192             return OC_STACK_ERROR;
193         }
194     }
195     else
196     {
197         // IF not specified in query, use default IF
198         *ifParam = STACK_IF_DEFAULT;
199     }
200
201     if (rtPtr)
202     {
203         if (CheckRTParamSupport((OCResource *)resource, rtPtr) == OC_STACK_OK)
204         {
205             *rtParam = rtPtr;
206         }
207         else
208         {
209             return OC_STACK_INVALID_QUERY;
210         }
211     }
212     else
213     {
214         // RT not specified in query. Use the first resource type for the resource as default.
215         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
216     }
217     OC_LOG_V(INFO, TAG, "Query params: IF = %d, RT = %s", *ifParam, *rtParam);
218
219     return OC_STACK_OK;
220 }
221
222
223 static OCStackResult BuildRootResourceJSON(OCResource *resource,
224         char * bufferPtr, uint16_t *remaining)
225 {
226     OCStackResult ret = OC_STACK_ERROR;
227     cJSON *resObj = NULL;
228     char *jsonStr = NULL;
229     uint16_t jsonLen;
230
231     OC_LOG(INFO, TAG, PCF("Entering BuildRootResourceJSON"));
232     resObj = cJSON_CreateObject();
233
234     if ( ! resObj)
235     {
236         ret = OC_STACK_NO_MEMORY;
237     }
238     else if (resource)
239     {
240         cJSON_AddItemToObject (resObj, OC_RSRVD_HREF, cJSON_CreateString(resource->uri));
241         jsonStr = cJSON_PrintUnformatted (resObj);
242
243         if(!jsonStr)
244         {
245             cJSON_Delete(resObj);
246             return OC_STACK_NO_MEMORY;
247         }
248
249         jsonLen = strlen(jsonStr);
250         if (jsonLen < *remaining)
251         {
252             OICStrcpy(bufferPtr, *remaining, jsonStr);
253             *remaining -= jsonLen;
254             bufferPtr += jsonLen;
255             ret = OC_STACK_OK;
256         }
257     }
258     else
259     {
260         ret = OC_STACK_INVALID_PARAM;
261     }
262
263     cJSON_Delete (resObj);
264     OICFree(jsonStr);
265
266     return ret;
267 }
268
269
270 static OCStackResult
271 HandleLinkedListInterface(OCEntityHandlerRequest *ehRequest, uint8_t filterOn, char *filterValue)
272 {
273     if(!ehRequest)
274     {
275         return OC_STACK_INVALID_PARAM;
276     }
277
278     OCStackResult ret = OC_STACK_ERROR;
279     char jsonbuffer[MAX_RESPONSE_LENGTH] = {};
280     size_t jsonbufferLength = 0;
281     uint16_t remaining = 0;
282     char *ptr = NULL;
283     OCResource *collResource = (OCResource *)ehRequest->resource;
284
285     ptr = jsonbuffer;
286     remaining = MAX_RESPONSE_LENGTH;
287
288     ret = BuildRootResourceJSON(collResource, ptr, &remaining);
289
290     if (ret == OC_STACK_OK && remaining >= (sizeof (OC_JSON_SEPARATOR) + 1))
291     {
292         ptr += strlen((char*)ptr);
293         *ptr = OC_JSON_SEPARATOR;
294         ptr++;
295         remaining--;
296     }
297     else
298     {
299         ret = OC_STACK_ERROR;
300     }
301
302     if (ret == OC_STACK_OK)
303     {
304         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
305         {
306             OCResource* temp = collResource->rsrcResources[i];
307             if (temp)
308             {
309                 //TODO : Update needed here to get correct connectivity type
310                 //from ServerRequest data structure.
311
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)
316                 {
317                     break;
318                 }
319                 ptr += strlen((char*)ptr);
320
321                 // Check if we have added all resources.
322                 if ((i + 1) == MAX_CONTAINED_RESOURCES)
323                 {
324                     break;
325                 }
326                 // Add separator if more resources and enough space present.
327                 if (collResource->rsrcResources[i+1] && remaining > sizeof(OC_JSON_SEPARATOR))
328                 {
329                     *ptr = OC_JSON_SEPARATOR;
330                     ptr++;
331                     remaining--;
332                 }
333                 // No point continuing as no more space on buffer
334                 // and/or no more resources.
335                 else
336                 {
337                     break;
338                 }
339             }
340             else
341             {
342                 break;
343             }
344         }
345     }
346
347     jsonbufferLength = strlen((const char *)jsonbuffer);
348     if(ret == OC_STACK_OK && jsonbufferLength)
349     {
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);
358     }
359     return ret;
360 }
361
362 static OCStackResult
363 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
364 {
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;
370     char * ptr = NULL;
371     OCResource * collResource = (OCResource *) ehRequest->resource;
372
373     ptr = jsonbuffer;
374     remaining = MAX_RESPONSE_LENGTH;
375
376     stackRet = BuildRootResourceJSON(collResource, ptr, &remaining);
377     ptr += strlen((char*)ptr);
378
379     jsonbufferLength = strlen((const char *)jsonbuffer);
380     if(jsonbufferLength)
381     {
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);
390     }
391
392     if (stackRet == OC_STACK_OK)
393     {
394         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
395         {
396             OCResource* temp = collResource->rsrcResources[i];
397             if (temp)
398             {
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;
403
404                 ehResult = temp->entityHandler(OC_REQUEST_FLAG, ehRequest,
405                                         temp->entityHandlerCallbackParam);
406
407                 // The default collection handler is returning as OK
408                 if(stackRet != OC_STACK_SLOW_RESOURCE)
409                 {
410                     stackRet = OC_STACK_OK;
411                 }
412                 // if a single resource is slow, then entire response will be treated
413                 // as slow response
414                 if(ehResult == OC_EH_SLOW)
415                 {
416                     OC_LOG(INFO, TAG, PCF("This is a slow resource"));
417                     ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
418                     stackRet = EntityHandlerCodeToOCStackCode(ehResult);
419                 }
420             }
421             else
422             {
423                 break;
424             }
425         }
426         ehRequest->resource = (OCResourceHandle) collResource;
427     }
428     return stackRet;
429 }
430
431 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
432 {
433     if(resource)
434     {
435         uint8_t num = 0;
436         for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
437         {
438             if (resource->rsrcResources[i])
439             {
440                 num++;
441             }
442         }
443         return num;
444     }
445     else
446     {
447         return -1;
448     }
449 }
450
451
452 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
453                                               OCEntityHandlerRequest *ehRequest)
454 {
455     if(!ehRequest || !ehRequest->query)
456     {
457         return OC_STACK_INVALID_PARAM;
458     }
459
460     OCStackResult result = OC_STACK_ERROR;
461     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
462     char *rtQueryParam = NULL;
463
464     OC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
465
466     if (flag != OC_REQUEST_FLAG)
467     {
468         return OC_STACK_ERROR;
469     }
470
471     result = ValidateQuery (ehRequest->query,
472                             ehRequest->resource, &ifQueryParam, &rtQueryParam);
473
474     if (result != OC_STACK_OK)
475     {
476         return result;
477     }
478
479     if(!((ehRequest->method == OC_REST_GET) ||
480         (ehRequest->method == OC_REST_PUT) ||
481         (ehRequest->method == OC_REST_POST)))
482     {
483         return OC_STACK_ERROR;
484     }
485
486     if (ehRequest->method == OC_REST_GET)
487     {
488         switch (ifQueryParam)
489         {
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);
496
497             case STACK_IF_LL:
498                 OC_LOG(INFO, TAG, PCF("STACK_IF_LL"));
499                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
500
501             case STACK_IF_BATCH:
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);
508             case STACK_IF_GROUP:
509                 return BuildCollectionGroupActionJSONResponse(OC_REST_GET/*flag*/,
510                         (OCResource *) ehRequest->resource, ehRequest);
511             default:
512                 return OC_STACK_ERROR;
513         }
514     }
515     else if (ehRequest->method == OC_REST_PUT)
516     {
517         switch (ifQueryParam)
518         {
519             case STACK_IF_DEFAULT:
520                 // M1 release does not support PUT on default interface
521                 return OC_STACK_ERROR;
522
523             case STACK_IF_LL:
524                 // LL interface only supports GET
525                 return OC_STACK_ERROR;
526
527             case STACK_IF_BATCH:
528                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
529                                                                         HandleAggregateResponse;
530                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
531                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
532                 return HandleBatchInterface(ehRequest);
533
534             case STACK_IF_GROUP:
535             {
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);
540             }
541             default:
542                 return OC_STACK_ERROR;
543         }
544     }
545     else if (ehRequest->method == OC_REST_POST)
546     {
547
548         switch (ifQueryParam)
549         {
550             case STACK_IF_GROUP:
551             {
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);
556             }
557             default:
558                 return OC_STACK_ERROR;
559         }
560     }
561     else if (ehRequest->method == OC_REST_POST)
562     {
563
564         if(ifQueryParam == STACK_IF_GROUP)
565         {
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);
570         }
571         else
572         {
573             return OC_STACK_ERROR;
574         }
575     }
576     return result;
577 }
578
579