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