Imported Upstream version 0.9.2
[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     OCRepPayload* payload = NULL;
235
236     if(ret == OC_STACK_OK)
237     {
238         ret = BuildResponseRepresentation(collResource, &payload);
239     }
240
241     if (ret == OC_STACK_OK)
242     {
243         for  (int i = 0; i < MAX_CONTAINED_RESOURCES && ret == OC_STACK_OK; i++)
244         {
245             OCResource* temp = collResource->rsrcResources[i];
246             if (temp)
247             {
248                 //TODO : Add resource type filtering once collections
249                 // start supporting queries.
250                 ret = BuildResponseRepresentation(temp, &payload);
251             }
252         }
253     }
254
255     if(ret == OC_STACK_OK)
256     {
257         OCEntityHandlerResponse response = {};
258         response.ehResult = OC_EH_OK;
259         response.payload = (OCPayload*)payload;
260         response.persistentBufferFlag = 0;
261         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
262         response.resourceHandle = (OCResourceHandle) collResource;
263         ret = OCDoResponse(&response);
264     }
265     OCRepPayloadDestroy(payload);
266     return ret;
267 }
268
269 static OCStackResult
270 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
271 {
272     OCStackResult stackRet = OC_STACK_OK;
273     OCEntityHandlerResult ehResult = OC_EH_ERROR;
274     OCResource * collResource = (OCResource *) ehRequest->resource;
275
276     OCRepPayload* payload = OCRepPayloadCreate();
277     if(!payload)
278     {
279         stackRet = OC_STACK_NO_MEMORY;
280     }
281
282     if(stackRet == OC_STACK_OK)
283     {
284         OCRepPayloadSetUri(payload, collResource->uri);
285     }
286
287     if(stackRet == OC_STACK_OK)
288     {
289         OCEntityHandlerResponse response = {};
290         response.ehResult = OC_EH_OK;
291         response.payload = (OCPayload*)payload;
292         response.persistentBufferFlag = 0;
293         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
294         response.resourceHandle = (OCResourceHandle) collResource;
295         stackRet = OCDoResponse(&response);
296     }
297
298     if (stackRet == OC_STACK_OK)
299     {
300         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
301         {
302             OCResource* temp = collResource->rsrcResources[i];
303             if (temp)
304             {
305                 // Note that all entity handlers called through a collection
306                 // will get the same pointer to ehRequest, the only difference
307                 // is ehRequest->resource
308                 ehRequest->resource = (OCResourceHandle) temp;
309
310                 ehResult = temp->entityHandler(OC_REQUEST_FLAG, ehRequest,
311                                         temp->entityHandlerCallbackParam);
312
313                 // The default collection handler is returning as OK
314                 if(stackRet != OC_STACK_SLOW_RESOURCE)
315                 {
316                     stackRet = OC_STACK_OK;
317                 }
318                 // if a single resource is slow, then entire response will be treated
319                 // as slow response
320                 if(ehResult == OC_EH_SLOW)
321                 {
322                     OC_LOG(INFO, TAG, PCF("This is a slow resource"));
323                     ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
324                     stackRet = EntityHandlerCodeToOCStackCode(ehResult);
325                 }
326             }
327             else
328             {
329                 break;
330             }
331         }
332         ehRequest->resource = (OCResourceHandle) collResource;
333     }
334     return stackRet;
335 }
336
337 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
338 {
339     if(resource)
340     {
341         uint8_t num = 0;
342         for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
343         {
344             if (resource->rsrcResources[i])
345             {
346                 num++;
347             }
348         }
349         return num;
350     }
351     else
352     {
353         return -1;
354     }
355 }
356
357
358 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
359                                               OCEntityHandlerRequest *ehRequest)
360 {
361     if(!ehRequest || !ehRequest->query)
362     {
363         return OC_STACK_INVALID_PARAM;
364     }
365
366     OCStackResult result = OC_STACK_ERROR;
367     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
368     char *rtQueryParam = NULL;
369
370     OC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
371
372     if (flag != OC_REQUEST_FLAG)
373     {
374         return OC_STACK_ERROR;
375     }
376
377     result = ValidateQuery (ehRequest->query,
378                             ehRequest->resource, &ifQueryParam, &rtQueryParam);
379
380     if (result != OC_STACK_OK)
381     {
382         return result;
383     }
384
385     if(!((ehRequest->method == OC_REST_GET) ||
386         (ehRequest->method == OC_REST_PUT) ||
387         (ehRequest->method == OC_REST_POST)))
388     {
389         return OC_STACK_ERROR;
390     }
391
392     if (ehRequest->method == OC_REST_GET)
393     {
394         switch (ifQueryParam)
395         {
396             case STACK_IF_DEFAULT:
397                 // Get attributes of collection resource and properties of contined resource
398                 // M1 release does not support attributes for collection resource, so the GET
399                 // operation is same as the GET on LL interface.
400                 OC_LOG(INFO, TAG, PCF("STACK_IF_DEFAULT"));
401                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
402
403             case STACK_IF_LL:
404                 OC_LOG(INFO, TAG, PCF("STACK_IF_LL"));
405                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
406
407             case STACK_IF_BATCH:
408                 OC_LOG(INFO, TAG, PCF("STACK_IF_BATCH"));
409                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
410                                                                         HandleAggregateResponse;
411
412                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
413                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
414
415                 return HandleBatchInterface(ehRequest);
416
417             case STACK_IF_GROUP:
418                 return BuildCollectionGroupActionJSONResponse(OC_REST_GET/*flag*/,
419                         (OCResource *) ehRequest->resource, ehRequest);
420             default:
421                 return OC_STACK_ERROR;
422         }
423     }
424     else if (ehRequest->method == OC_REST_PUT)
425     {
426         switch (ifQueryParam)
427         {
428             case STACK_IF_DEFAULT:
429                 // M1 release does not support PUT on default interface
430                 return OC_STACK_ERROR;
431
432             case STACK_IF_LL:
433                 // LL interface only supports GET
434                 return OC_STACK_ERROR;
435
436             case STACK_IF_BATCH:
437                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
438                                                                         HandleAggregateResponse;
439                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
440                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
441                 return HandleBatchInterface(ehRequest);
442
443             case STACK_IF_GROUP:
444             {
445                 OC_LOG(INFO, TAG, PCF("IF_COLLECTION PUT with request ::\n"));
446                 OC_LOG_PAYLOAD(INFO, TAG, ehRequest->payload);
447                 return BuildCollectionGroupActionJSONResponse(OC_REST_PUT/*flag*/,
448                         (OCResource *) ehRequest->resource, ehRequest);
449             }
450             default:
451                 return OC_STACK_ERROR;
452         }
453     }
454     else if (ehRequest->method == OC_REST_POST)
455     {
456
457         switch (ifQueryParam)
458         {
459             case STACK_IF_GROUP:
460             {
461                 OC_LOG(INFO, TAG, PCF("IF_COLLECTION POST with request ::\n"));
462                 OC_LOG_PAYLOAD(INFO, TAG, ehRequest->payload);
463                 return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
464                         (OCResource *) ehRequest->resource, ehRequest);
465             }
466             default:
467                 return OC_STACK_ERROR;
468         }
469     }
470     else if (ehRequest->method == OC_REST_POST)
471     {
472
473         if(ifQueryParam == STACK_IF_GROUP)
474         {
475             OC_LOG(INFO, TAG, PCF("IF_COLLECTION POST with request ::\n"));
476             OC_LOG_PAYLOAD(INFO, TAG, ehRequest->payload);
477             return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
478                     (OCResource *) ehRequest->resource, ehRequest);
479         }
480         else
481         {
482             return OC_STACK_ERROR;
483         }
484     }
485     return result;
486 }