New IP Adapter supports IPv6
[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 #include "ocpayload.h"
38
39 /// Module Name
40 #include <stdio.h>
41
42 #define WITH_GROUPACTION 1
43
44 #include "oicgroup.h"
45
46 #define TAG PCF("occollection")
47
48 #define NUM_PARAM_IN_QUERY   2 // The expected number of parameters in a query
49 #define NUM_FIELDS_IN_QUERY  2 // The expected number of fields in a query
50
51 static OCStackResult CheckRTParamSupport(const OCResource* resource, const char* rtPtr)
52 {
53     if(!resource || !rtPtr)
54     {
55         return OC_STACK_INVALID_PARAM;
56     }
57
58     OCResourceType* rTPointer = resource->rsrcType;
59     while (rTPointer)
60     {
61         if( strcmp (rTPointer->resourcetypename, rtPtr) == 0)
62         {
63             return OC_STACK_OK;
64         }
65
66         rTPointer = rTPointer->next;
67     }
68     return OC_STACK_ERROR;
69 }
70
71 static OCStackResult CheckIFParamSupport(const OCResource* resource, const char* ifPtr)
72 {
73     if(!resource || !ifPtr)
74     {
75         return OC_STACK_INVALID_PARAM;
76     }
77
78     OCResourceInterface* iFPointer = resource->rsrcInterface;
79     while (iFPointer)
80     {
81         if( strcmp (iFPointer->name, ifPtr) == 0)
82         {
83             return OC_STACK_OK;
84         }
85
86         iFPointer = iFPointer->next;
87     }
88     return OC_STACK_ERROR;
89 }
90
91 static OCStackResult
92 ValidateQuery (const char *query, OCResourceHandle resource,
93                              OCStackIfTypes *ifParam, char **rtParam)
94 {
95     uint8_t numFields = 0;
96     uint8_t numParam;
97
98     //TODO: Query and URL validation is being done for virtual resource case
99     // using ValidateUrlQuery function. We should be able to merge it with this
100     // function.
101     OC_LOG(INFO, TAG, PCF("Entering ValidateQuery"));
102
103     if (!query)
104         return OC_STACK_ERROR;
105
106     if(!ifParam || !rtParam)
107     {
108         return OC_STACK_INVALID_PARAM;
109     }
110
111     if (!(*query))
112     {
113         // Query string is empty
114         OC_LOG_V(INFO, TAG, PCF("Empty query string, use default IF and RT"));
115         *ifParam = STACK_IF_DEFAULT;
116         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
117         return OC_STACK_OK;
118     }
119
120     // Break the query string to validate it and determine IF and RT parameters
121     // Validate there are atmost 2 parameters in string and that one is 'if' and other 'rt'
122     // separated by token '&' or ';'. Stack will accept both the versions.
123
124     char *endStr, *ifPtr = NULL, *rtPtr = NULL;
125     char *token = strtok_r ((char *)query, OC_QUERY_SEPARATOR , &endStr);
126
127     // External loop breaks query string into fields using the & separator
128     while (token != NULL)
129     {
130         numFields++;
131         char *endToken;
132         char *innerToken = strtok_r (token, "=", &endToken);
133         numParam = 0;
134
135         // Internal loop parses the field to extract values (parameters) assigned to each field
136         while (innerToken != NULL)
137         {
138             numParam++;
139             if (strncmp (innerToken, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE)) == 0)
140             {
141                 // Determine the value of IF parameter
142                 innerToken = strtok_r (NULL, "=", &endToken);
143                 ifPtr = innerToken;
144             }
145             else if (strcmp (innerToken, OC_RSRVD_RESOURCE_TYPE) == 0)
146             {
147                 // Determine the value of RT parameter
148                 innerToken = strtok_r (NULL, "=", &endToken);
149                 rtPtr = innerToken;
150             }
151             else
152             {
153                 innerToken = strtok_r (NULL, "=", &endToken);
154             }
155         }
156         if (numParam != NUM_PARAM_IN_QUERY)
157         {
158             // Query parameter should be of the form if=<string>. String should not have & or =
159             return OC_STACK_INVALID_QUERY;
160         }
161         token = strtok_r (NULL, OC_QUERY_SEPARATOR, &endStr);
162     }
163     if (numFields > NUM_FIELDS_IN_QUERY)
164     {
165         // current release supports one IF value, one RT value and no other params
166         return OC_STACK_INVALID_QUERY;
167     }
168
169     if (ifPtr)
170     {
171         if(CheckIFParamSupport((OCResource *)resource, ifPtr) != OC_STACK_OK)
172         {
173             return OC_STACK_INVALID_QUERY;
174         }
175         if (strcmp (ifPtr, OC_RSRVD_INTERFACE_DEFAULT) == 0)
176         {
177             *ifParam = STACK_IF_DEFAULT;
178         }
179         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_LL) == 0)
180         {
181             *ifParam = STACK_IF_LL;
182         }
183         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_BATCH) == 0)
184         {
185             *ifParam = STACK_IF_BATCH;
186         }
187         else if(strcmp (ifPtr, OC_RSRVD_INTERFACE_GROUP) == 0)
188         {
189             *ifParam = STACK_IF_GROUP;
190         }
191         else
192         {
193             return OC_STACK_ERROR;
194         }
195     }
196     else
197     {
198         // IF not specified in query, use default IF
199         *ifParam = STACK_IF_DEFAULT;
200     }
201
202     if (rtPtr)
203     {
204         if (CheckRTParamSupport((OCResource *)resource, rtPtr) == OC_STACK_OK)
205         {
206             *rtParam = rtPtr;
207         }
208         else
209         {
210             return OC_STACK_INVALID_QUERY;
211         }
212     }
213     else
214     {
215         // RT not specified in query. Use the first resource type for the resource as default.
216         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
217     }
218     OC_LOG_V(INFO, TAG, "Query params: IF = %d, RT = %s", *ifParam, *rtParam);
219
220     return OC_STACK_OK;
221 }
222
223 static OCStackResult
224 HandleLinkedListInterface(OCEntityHandlerRequest *ehRequest, uint8_t filterOn, char *filterValue)
225 {
226     if(!ehRequest)
227     {
228         return OC_STACK_INVALID_PARAM;
229     }
230
231     OCStackResult ret = OC_STACK_OK;
232     OCResource *collResource = (OCResource *)ehRequest->resource;
233
234     OCDiscoveryPayload* payload = OCDiscoveryPayloadCreate();
235     if(!payload)
236     {
237         ret = OC_STACK_NO_MEMORY;
238     }
239
240     if(ret == OC_STACK_OK)
241     {
242         ret = BuildVirtualResourceResponse(collResource, payload, &ehRequest->devAddr);
243     }
244
245     if (ret == OC_STACK_OK)
246     {
247         for  (int i = 0; i < MAX_CONTAINED_RESOURCES && ret == OC_STACK_OK; i++)
248         {
249             OCResource* temp = collResource->rsrcResources[i];
250             if (temp)
251             {
252                 //TODO : Add resource type filtering once collections
253                 // start supporting queries.
254                 ret = BuildVirtualResourceResponse(temp, payload, &ehRequest->devAddr);
255             }
256         }
257     }
258
259     if(ret == OC_STACK_OK)
260     {
261         OCEntityHandlerResponse response = {};
262         response.ehResult = OC_EH_OK;
263         response.payload = (OCPayload*)payload;
264         response.persistentBufferFlag = 0;
265         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
266         response.resourceHandle = (OCResourceHandle) collResource;
267         ret = OCDoResponse(&response);
268     }
269     OCDiscoveryPayloadDestroy(payload);
270     return ret;
271 }
272
273 static OCStackResult
274 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
275 {
276     OCStackResult stackRet = OC_STACK_ERROR;
277     OCEntityHandlerResult ehResult = OC_EH_ERROR;
278     OCResource * collResource = (OCResource *) ehRequest->resource;
279
280     OCDiscoveryPayload* payload = OCDiscoveryPayloadCreate();
281     if(!payload)
282     {
283         stackRet = OC_STACK_NO_MEMORY;
284     }
285
286     if(stackRet == OC_STACK_OK)
287     {
288         stackRet = BuildVirtualResourceResponse(collResource, payload, &ehRequest->devAddr);
289     }
290
291     if(stackRet == OC_STACK_OK)
292     {
293         OCEntityHandlerResponse response = {};
294         response.ehResult = OC_EH_OK;
295         response.payload = (OCPayload*)payload;
296         response.persistentBufferFlag = 0;
297         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
298         response.resourceHandle = (OCResourceHandle) collResource;
299         stackRet = OCDoResponse(&response);
300     }
301     OCDiscoveryPayloadDestroy(payload);
302
303     if (stackRet == OC_STACK_OK)
304     {
305         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
306         {
307             OCResource* temp = collResource->rsrcResources[i];
308             if (temp)
309             {
310                 // Note that all entity handlers called through a collection
311                 // will get the same pointer to ehRequest, the only difference
312                 // is ehRequest->resource
313                 ehRequest->resource = (OCResourceHandle) temp;
314
315                 ehResult = temp->entityHandler(OC_REQUEST_FLAG, ehRequest,
316                                         temp->entityHandlerCallbackParam);
317
318                 // The default collection handler is returning as OK
319                 if(stackRet != OC_STACK_SLOW_RESOURCE)
320                 {
321                     stackRet = OC_STACK_OK;
322                 }
323                 // if a single resource is slow, then entire response will be treated
324                 // as slow response
325                 if(ehResult == OC_EH_SLOW)
326                 {
327                     OC_LOG(INFO, TAG, PCF("This is a slow resource"));
328                     ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
329                     stackRet = EntityHandlerCodeToOCStackCode(ehResult);
330                 }
331             }
332             else
333             {
334                 break;
335             }
336         }
337         ehRequest->resource = (OCResourceHandle) collResource;
338     }
339     return stackRet;
340 }
341
342 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
343 {
344     if(resource)
345     {
346         uint8_t num = 0;
347         for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
348         {
349             if (resource->rsrcResources[i])
350             {
351                 num++;
352             }
353         }
354         return num;
355     }
356     else
357     {
358         return -1;
359     }
360 }
361
362
363 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
364                                               OCEntityHandlerRequest *ehRequest)
365 {
366     if(!ehRequest || !ehRequest->query)
367     {
368         return OC_STACK_INVALID_PARAM;
369     }
370
371     OCStackResult result = OC_STACK_ERROR;
372     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
373     char *rtQueryParam = NULL;
374
375     OC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
376
377     if (flag != OC_REQUEST_FLAG)
378     {
379         return OC_STACK_ERROR;
380     }
381
382     result = ValidateQuery (ehRequest->query,
383                             ehRequest->resource, &ifQueryParam, &rtQueryParam);
384
385     if (result != OC_STACK_OK)
386     {
387         return result;
388     }
389
390     if(!((ehRequest->method == OC_REST_GET) ||
391         (ehRequest->method == OC_REST_PUT) ||
392         (ehRequest->method == OC_REST_POST)))
393     {
394         return OC_STACK_ERROR;
395     }
396
397     if (ehRequest->method == OC_REST_GET)
398     {
399         switch (ifQueryParam)
400         {
401             case STACK_IF_DEFAULT:
402                 // Get attributes of collection resource and properties of contined resource
403                 // M1 release does not support attributes for collection resource, so the GET
404                 // operation is same as the GET on LL interface.
405                 OC_LOG(INFO, TAG, PCF("STACK_IF_DEFAULT"));
406                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
407
408             case STACK_IF_LL:
409                 OC_LOG(INFO, TAG, PCF("STACK_IF_LL"));
410                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
411
412             case STACK_IF_BATCH:
413                 OC_LOG(INFO, TAG, PCF("STACK_IF_BATCH"));
414                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
415                                                                         HandleAggregateResponse;
416
417                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
418                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
419
420                 return HandleBatchInterface(ehRequest);
421
422             case STACK_IF_GROUP:
423                 return BuildCollectionGroupActionJSONResponse(OC_REST_GET/*flag*/,
424                         (OCResource *) ehRequest->resource, ehRequest);
425             default:
426                 return OC_STACK_ERROR;
427         }
428     }
429     else if (ehRequest->method == OC_REST_PUT)
430     {
431         switch (ifQueryParam)
432         {
433             case STACK_IF_DEFAULT:
434                 // M1 release does not support PUT on default interface
435                 return OC_STACK_ERROR;
436
437             case STACK_IF_LL:
438                 // LL interface only supports GET
439                 return OC_STACK_ERROR;
440
441             case STACK_IF_BATCH:
442                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
443                                                                         HandleAggregateResponse;
444                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
445                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
446                 return HandleBatchInterface(ehRequest);
447
448             case STACK_IF_GROUP:
449             {
450                 OC_LOG(INFO, TAG, PCF("IF_COLLECTION PUT with request ::\n"));
451                 OC_LOG_PAYLOAD(INFO, TAG, ehRequest->payload);
452                 return BuildCollectionGroupActionJSONResponse(OC_REST_PUT/*flag*/,
453                         (OCResource *) ehRequest->resource, ehRequest);
454             }
455             default:
456                 return OC_STACK_ERROR;
457         }
458     }
459     else if (ehRequest->method == OC_REST_POST)
460     {
461
462         switch (ifQueryParam)
463         {
464             case STACK_IF_GROUP:
465             {
466                 OC_LOG(INFO, TAG, PCF("IF_COLLECTION POST with request ::\n"));
467                 OC_LOG_PAYLOAD(INFO, TAG, ehRequest->payload);
468                 return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
469                         (OCResource *) ehRequest->resource, ehRequest);
470             }
471             default:
472                 return OC_STACK_ERROR;
473         }
474     }
475     else if (ehRequest->method == OC_REST_POST)
476     {
477
478         if(ifQueryParam == STACK_IF_GROUP)
479         {
480             OC_LOG(INFO, TAG, PCF("IF_COLLECTION POST with request ::\n"));
481             OC_LOG_PAYLOAD(INFO, TAG, ehRequest->payload);
482             return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
483                     (OCResource *) ehRequest->resource, ehRequest);
484         }
485         else
486         {
487             return OC_STACK_ERROR;
488         }
489     }
490     return result;
491 }