Merge branch 'master' into group-manager
[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 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     OIC_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         OIC_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     OIC_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     OCResource *collResource = (OCResource *)ehRequest->resource;
240     OCChildResource *tempChildResource = NULL;
241     OCRepPayload* payload = NULL;
242
243     if(!collResource)
244     {
245         return OC_STACK_INVALID_PARAM;
246     }
247
248     OCStackResult ret = BuildResponseRepresentation(collResource, &payload);
249     if (ret == OC_STACK_OK)
250     {
251         tempChildResource = collResource->rsrcChildResourcesHead;
252         while (tempChildResource && ret == OC_STACK_OK)
253         {
254             OCResource* temp = tempChildResource->rsrcResource;
255             if (temp)
256             {
257                 //TODO : Add resource type filtering once collections
258                 // start supporting queries.
259                 ret = BuildResponseRepresentation(temp, &payload);
260             }
261
262             tempChildResource = tempChildResource->next;
263         }
264     }
265
266     if (ret == OC_STACK_OK)
267     {
268         OCEntityHandlerResponse response = {0};
269         response.ehResult = OC_EH_OK;
270         response.payload = (OCPayload*)payload;
271         response.persistentBufferFlag = 0;
272         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
273         response.resourceHandle = (OCResourceHandle) collResource;
274         ret = OCDoResponse(&response);
275     }
276
277     OCRepPayloadDestroy(payload);
278     return ret;
279 }
280
281 static OCStackResult
282 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
283 {
284     if (!ehRequest)
285     {
286         return OC_STACK_INVALID_PARAM;
287     }
288
289     OCResource * collResource = (OCResource *) ehRequest->resource;
290     OCChildResource *tempChildResource = NULL;
291
292     OCRepPayload* payload = OCRepPayloadCreate();
293     if (!payload)
294     {
295         return OC_STACK_NO_MEMORY;
296     }
297
298     if (collResource)
299     {
300         OCRepPayloadSetUri(payload, collResource->uri);
301     }
302
303     OCEntityHandlerResponse response = {0};
304     response.ehResult = OC_EH_OK;
305     response.payload = (OCPayload*)payload;
306     response.persistentBufferFlag = 0;
307     response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
308     response.resourceHandle = (OCResourceHandle) collResource;
309     OCStackResult stackRet = OCDoResponse(&response);
310
311     if (stackRet == OC_STACK_OK)
312     {
313         tempChildResource = collResource->rsrcChildResourcesHead;
314
315         while(tempChildResource)
316         {
317             OCResource* tempRsrcResource = tempChildResource->rsrcResource;
318
319             if (tempRsrcResource)
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) tempRsrcResource;
325
326                 OCEntityHandlerResult ehResult = tempRsrcResource->entityHandler(OC_REQUEST_FLAG, ehRequest,
327                                                         tempRsrcResource->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                     OIC_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             tempChildResource = tempChildResource->next;
349
350         }
351
352         ehRequest->resource = (OCResourceHandle) collResource;
353     }
354     return stackRet;
355 }
356
357 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
358 {
359     if (resource)
360     {
361         uint8_t num = 0;
362         OCChildResource *tempChildResource = NULL;
363
364         tempChildResource = resource->rsrcChildResourcesHead;
365
366         while(tempChildResource)
367         {
368             num++;
369             tempChildResource = tempChildResource->next;
370         }
371
372         return num;
373     }
374     else
375     {
376         return -1;
377     }
378 }
379
380
381 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
382                                               OCEntityHandlerRequest *ehRequest)
383 {
384     if (!ehRequest || !ehRequest->query)
385     {
386         return OC_STACK_INVALID_PARAM;
387     }
388
389     OIC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
390
391     if (flag != OC_REQUEST_FLAG)
392     {
393         return OC_STACK_ERROR;
394     }
395
396     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
397     char *rtQueryParam = NULL;
398     OCStackResult result = ValidateQuery (ehRequest->query,
399                                           ehRequest->resource, &ifQueryParam, &rtQueryParam);
400
401     if (result != OC_STACK_OK)
402     {
403         return result;
404     }
405
406     switch (ehRequest->method)
407     {
408         case OC_REST_GET:
409             switch (ifQueryParam)
410             {
411                 case STACK_IF_DEFAULT:
412                     // Get attributes of collection resource and properties of contained resources
413                     // M1 release does not support attributes for collection resource, so the GET
414                     // operation is same as the GET on LL interface.
415                     OIC_LOG(INFO, TAG, "STACK_IF_DEFAULT");
416                     return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
417
418                 case STACK_IF_LL:
419                     OIC_LOG(INFO, TAG, "STACK_IF_LL");
420                     return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
421
422                 case STACK_IF_BATCH:
423                     OIC_LOG(INFO, TAG, "STACK_IF_BATCH");
424                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
425                                                                             HandleAggregateResponse;
426
427                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
428                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
429
430                     return HandleBatchInterface(ehRequest);
431
432                 case STACK_IF_GROUP:
433                     return BuildCollectionGroupActionCBORResponse(OC_REST_GET/*flag*/,
434                             (OCResource *) ehRequest->resource, ehRequest);
435
436                 default:
437                     return OC_STACK_ERROR;
438             }
439
440         case OC_REST_PUT:
441             switch (ifQueryParam)
442             {
443                 case STACK_IF_DEFAULT:
444                     // M1 release does not support PUT on default interface
445                     return OC_STACK_ERROR;
446
447                 case STACK_IF_LL:
448                     // LL interface only supports GET
449                     return OC_STACK_ERROR;
450
451                 case STACK_IF_BATCH:
452                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
453                                                                             HandleAggregateResponse;
454                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
455                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
456                     return HandleBatchInterface(ehRequest);
457
458                 case STACK_IF_GROUP:
459                     OIC_LOG(INFO, TAG, "IF_COLLECTION PUT with request ::\n");
460                     OIC_LOG_PAYLOAD(INFO, ehRequest->payload);
461                     return BuildCollectionGroupActionCBORResponse(OC_REST_PUT/*flag*/,
462                             (OCResource *) ehRequest->resource, ehRequest);
463
464                 default:
465                     return OC_STACK_ERROR;
466             }
467
468         case OC_REST_POST:
469             switch (ifQueryParam)
470             {
471                 case STACK_IF_DEFAULT:
472                     // M1 release does not support POST on default interface
473                     return OC_STACK_ERROR;
474
475                 case STACK_IF_LL:
476                     // LL interface only supports GET
477                     return OC_STACK_ERROR;
478
479                 case STACK_IF_BATCH:
480                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
481                                                                             HandleAggregateResponse;
482                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
483                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
484                     return HandleBatchInterface(ehRequest);
485
486                 case STACK_IF_GROUP:
487                     OIC_LOG(INFO, TAG, "IF_COLLECTION POST with request ::\n");
488                     OIC_LOG_PAYLOAD(INFO, ehRequest->payload);
489                     return BuildCollectionGroupActionCBORResponse(OC_REST_POST/*flag*/,
490                             (OCResource *) ehRequest->resource, ehRequest);
491
492                 default:
493                     return OC_STACK_ERROR;
494             }
495
496         case OC_REST_DELETE:
497             // TODO implement DELETE accordingly to the desired behavior
498             return OC_STACK_ERROR;
499
500         default:
501             return OC_STACK_ERROR;
502     }
503 }