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