merge master code to build iotivity
[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 PCF("occollection")
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 static OCStackResult CheckRTParamSupport(const OCResource* resource, const char* rtPtr)
53 {
54     if(!resource || !rtPtr)
55     {
56         return OC_STACK_INVALID_PARAM;
57     }
58
59     OCResourceType* rTPointer = resource->rsrcType;
60     while (rTPointer)
61     {
62         if( strcmp (rTPointer->resourcetypename, rtPtr) == 0)
63         {
64             return OC_STACK_OK;
65         }
66
67         rTPointer = rTPointer->next;
68     }
69     return OC_STACK_ERROR;
70 }
71
72 static OCStackResult CheckIFParamSupport(const OCResource* resource, const char* ifPtr)
73 {
74     if(!resource || !ifPtr)
75     {
76         return OC_STACK_INVALID_PARAM;
77     }
78
79     OCResourceInterface* iFPointer = resource->rsrcInterface;
80     while (iFPointer)
81     {
82         if( strcmp (iFPointer->name, ifPtr) == 0)
83         {
84             return OC_STACK_OK;
85         }
86
87         iFPointer = iFPointer->next;
88     }
89     return OC_STACK_ERROR;
90 }
91
92 static OCStackResult
93 ValidateQuery (const char *query, OCResourceHandle resource,
94                              OCStackIfTypes *ifParam, char **rtParam)
95 {
96     uint8_t numFields = 0;
97     uint8_t numParam;
98
99     //TODO: Query and URL validation is being done for virtual resource case
100     // using ValidateUrlQuery function. We should be able to merge it with this
101     // function.
102     OC_LOG(INFO, TAG, PCF("Entering ValidateQuery"));
103
104     if (!query)
105         return OC_STACK_ERROR;
106
107     if(!ifParam || !rtParam)
108     {
109         return OC_STACK_INVALID_PARAM;
110     }
111
112     if (!(*query))
113     {
114         // Query string is empty
115         OC_LOG_V(INFO, TAG, PCF("Empty query string, use default IF and RT"));
116         *ifParam = STACK_IF_DEFAULT;
117         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
118         return OC_STACK_OK;
119     }
120
121     // Break the query string to validate it and determine IF and RT parameters
122     // Validate there are atmost 2 parameters in string and that one is 'if' and other 'rt'
123     // separated by token '&' or ';'. Stack will accept both the versions.
124
125     char *endStr, *ifPtr = NULL, *rtPtr = NULL;
126     char *token = strtok_r ((char *)query, OC_QUERY_SEPARATOR , &endStr);
127
128     // External loop breaks query string into fields using the & separator
129     while (token != NULL)
130     {
131         numFields++;
132         char *endToken;
133         char *innerToken = strtok_r (token, "=", &endToken);
134         numParam = 0;
135
136         // Internal loop parses the field to extract values (parameters) assigned to each field
137         while (innerToken != NULL)
138         {
139             numParam++;
140             if (strncmp (innerToken, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE)) == 0)
141             {
142                 // Determine the value of IF parameter
143                 innerToken = strtok_r (NULL, "=", &endToken);
144                 ifPtr = innerToken;
145             }
146             else if (strcmp (innerToken, OC_RSRVD_RESOURCE_TYPE) == 0)
147             {
148                 // Determine the value of RT parameter
149                 innerToken = strtok_r (NULL, "=", &endToken);
150                 rtPtr = innerToken;
151             }
152             else
153             {
154                 innerToken = strtok_r (NULL, "=", &endToken);
155             }
156         }
157         if (numParam != NUM_PARAM_IN_QUERY)
158         {
159             // Query parameter should be of the form if=<string>. String should not have & or =
160             return OC_STACK_INVALID_QUERY;
161         }
162         token = strtok_r (NULL, OC_QUERY_SEPARATOR, &endStr);
163     }
164     if (numFields > NUM_FIELDS_IN_QUERY)
165     {
166         // current release supports one IF value, one RT value and no other params
167         return OC_STACK_INVALID_QUERY;
168     }
169
170     if (ifPtr)
171     {
172         if(CheckIFParamSupport((OCResource *)resource, ifPtr) != OC_STACK_OK)
173         {
174             return OC_STACK_INVALID_QUERY;
175         }
176         if (strcmp (ifPtr, OC_RSRVD_INTERFACE_DEFAULT) == 0)
177         {
178             *ifParam = STACK_IF_DEFAULT;
179         }
180         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_LL) == 0)
181         {
182             *ifParam = STACK_IF_LL;
183         }
184         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_BATCH) == 0)
185         {
186             *ifParam = STACK_IF_BATCH;
187         }
188         else if(strcmp (ifPtr, OC_RSRVD_INTERFACE_GROUP) == 0)
189         {
190             *ifParam = STACK_IF_GROUP;
191         }
192         else
193         {
194             return OC_STACK_ERROR;
195         }
196     }
197     else
198     {
199         // IF not specified in query, use default IF
200         *ifParam = STACK_IF_DEFAULT;
201     }
202
203     if (rtPtr)
204     {
205         if (CheckRTParamSupport((OCResource *)resource, rtPtr) == OC_STACK_OK)
206         {
207             *rtParam = rtPtr;
208         }
209         else
210         {
211             return OC_STACK_INVALID_QUERY;
212         }
213     }
214     else
215     {
216         // RT not specified in query. Use the first resource type for the resource as default.
217         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
218     }
219     OC_LOG_V(INFO, TAG, "Query params: IF = %d, RT = %s", *ifParam, *rtParam);
220
221     return OC_STACK_OK;
222 }
223
224 static OCStackResult
225 HandleLinkedListInterface(OCEntityHandlerRequest *ehRequest,
226                           uint8_t filterOn,
227                           char *filterValue)
228 {
229     (void)filterOn;
230     (void)filterValue;
231     if(!ehRequest)
232     {
233         return OC_STACK_INVALID_PARAM;
234     }
235
236     OCStackResult ret = OC_STACK_OK;
237     OCResource *collResource = (OCResource *)ehRequest->resource;
238
239     OCRepPayload* payload = NULL;
240
241     if(ret == OC_STACK_OK)
242     {
243         ret = BuildResponseRepresentation(collResource, &payload);
244     }
245
246     if (ret == OC_STACK_OK)
247     {
248         for  (int i = 0; i < MAX_CONTAINED_RESOURCES && ret == OC_STACK_OK; i++)
249         {
250             OCResource* temp = collResource->rsrcResources[i];
251             if (temp)
252             {
253                 //TODO : Add resource type filtering once collections
254                 // start supporting queries.
255                 ret = BuildResponseRepresentation(temp, &payload);
256             }
257         }
258     }
259
260     if(ret == OC_STACK_OK)
261     {
262         OCEntityHandlerResponse response = {0};
263         response.ehResult = OC_EH_OK;
264         response.payload = (OCPayload*)payload;
265         response.persistentBufferFlag = 0;
266         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
267         response.resourceHandle = (OCResourceHandle) collResource;
268         ret = OCDoResponse(&response);
269     }
270     OCRepPayloadDestroy(payload);
271     return ret;
272 }
273
274 static OCStackResult
275 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
276 {
277     OCStackResult stackRet = OC_STACK_OK;
278     OCEntityHandlerResult ehResult = OC_EH_ERROR;
279     OCResource * collResource = (OCResource *) ehRequest->resource;
280
281     OCRepPayload* payload = OCRepPayloadCreate();
282     if(!payload)
283     {
284         stackRet = OC_STACK_NO_MEMORY;
285     }
286
287     if(stackRet == OC_STACK_OK)
288     {
289         OCRepPayloadSetUri(payload, collResource->uri);
290     }
291
292     if(stackRet == OC_STACK_OK)
293     {
294         OCEntityHandlerResponse response = {0};
295         response.ehResult = OC_EH_OK;
296         response.payload = (OCPayload*)payload;
297         response.persistentBufferFlag = 0;
298         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
299         response.resourceHandle = (OCResourceHandle) collResource;
300         stackRet = OCDoResponse(&response);
301     }
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 }