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