Merge branch 'master' into windows-port
[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
291     OCRepPayload* payload = OCRepPayloadCreate();
292     if (!payload)
293     {
294         return OC_STACK_NO_MEMORY;
295     }
296
297     if (collResource)
298     {
299         OCRepPayloadSetUri(payload, collResource->uri);
300     }
301
302     OCEntityHandlerResponse response = {0};
303     response.ehResult = OC_EH_OK;
304     response.payload = (OCPayload*)payload;
305     response.persistentBufferFlag = 0;
306     response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
307     response.resourceHandle = (OCResourceHandle) collResource;
308     OCStackResult stackRet = OCDoResponse(&response);
309
310     if (stackRet == OC_STACK_OK)
311     {
312         OCChildResource *tempChildResource = (collResource) ? collResource->rsrcChildResourcesHead
313                                                               : NULL;
314         while(tempChildResource)
315         {
316             OCResource* tempRsrcResource = tempChildResource->rsrcResource;
317
318             if (tempRsrcResource)
319             {
320                 // Note that all entity handlers called through a collection
321                 // will get the same pointer to ehRequest, the only difference
322                 // is ehRequest->resource
323                 ehRequest->resource = (OCResourceHandle) tempRsrcResource;
324
325                 OCEntityHandlerResult ehResult = tempRsrcResource->entityHandler(OC_REQUEST_FLAG,
326                                            ehRequest, tempRsrcResource->entityHandlerCallbackParam);
327
328                 // The default collection handler is returning as OK
329                 if (stackRet != OC_STACK_SLOW_RESOURCE)
330                 {
331                     stackRet = OC_STACK_OK;
332                 }
333                 // if a single resource is slow, then entire response will be treated
334                 // as slow response
335                 if (ehResult == OC_EH_SLOW)
336                 {
337                     OIC_LOG(INFO, TAG, "This is a slow resource");
338                     ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
339                     stackRet = EntityHandlerCodeToOCStackCode(ehResult);
340                 }
341             }
342             else
343             {
344                 break;
345             }
346
347             tempChildResource = tempChildResource->next;
348
349         }
350
351         ehRequest->resource = (OCResourceHandle) collResource;
352     }
353     return stackRet;
354 }
355
356 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
357 {
358     if (resource)
359     {
360         uint8_t num = 0;
361         OCChildResource *tempChildResource = NULL;
362
363         tempChildResource = resource->rsrcChildResourcesHead;
364
365         while(tempChildResource)
366         {
367             num++;
368             tempChildResource = tempChildResource->next;
369         }
370
371         return num;
372     }
373     else
374     {
375         return -1;
376     }
377 }
378
379
380 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
381                                               OCEntityHandlerRequest *ehRequest)
382 {
383     if (!ehRequest || !ehRequest->query)
384     {
385         return OC_STACK_INVALID_PARAM;
386     }
387
388     OIC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
389
390     if (flag != OC_REQUEST_FLAG)
391     {
392         return OC_STACK_ERROR;
393     }
394
395     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
396     char *rtQueryParam = NULL;
397     OCStackResult result = ValidateQuery (ehRequest->query,
398                                           ehRequest->resource, &ifQueryParam, &rtQueryParam);
399
400     if (result != OC_STACK_OK)
401     {
402         return result;
403     }
404
405     switch (ehRequest->method)
406     {
407         case OC_REST_GET:
408             switch (ifQueryParam)
409             {
410                 case STACK_IF_DEFAULT:
411                     // Get attributes of collection resource and properties of contained resources
412                     // M1 release does not support attributes for collection resource, so the GET
413                     // operation is same as the GET on LL interface.
414                     OIC_LOG(INFO, TAG, "STACK_IF_DEFAULT");
415                     return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
416
417                 case STACK_IF_LL:
418                     OIC_LOG(INFO, TAG, "STACK_IF_LL");
419                     return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
420
421                 case STACK_IF_BATCH:
422                     OIC_LOG(INFO, TAG, "STACK_IF_BATCH");
423                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
424                                                                             HandleAggregateResponse;
425
426                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
427                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
428
429                     return HandleBatchInterface(ehRequest);
430
431                 case STACK_IF_GROUP:
432                     return BuildCollectionGroupActionCBORResponse(OC_REST_GET/*flag*/,
433                             (OCResource *) ehRequest->resource, ehRequest);
434
435                 default:
436                     return OC_STACK_ERROR;
437             }
438
439         case OC_REST_PUT:
440             switch (ifQueryParam)
441             {
442                 case STACK_IF_DEFAULT:
443                     // M1 release does not support PUT on default interface
444                     return OC_STACK_ERROR;
445
446                 case STACK_IF_LL:
447                     // LL interface only supports GET
448                     return OC_STACK_ERROR;
449
450                 case STACK_IF_BATCH:
451                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
452                                                                             HandleAggregateResponse;
453                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
454                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
455                     return HandleBatchInterface(ehRequest);
456
457                 case STACK_IF_GROUP:
458                     OIC_LOG(INFO, TAG, "IF_COLLECTION PUT with request ::\n");
459                     OIC_LOG_PAYLOAD(INFO, ehRequest->payload);
460                     return BuildCollectionGroupActionCBORResponse(OC_REST_PUT/*flag*/,
461                             (OCResource *) ehRequest->resource, ehRequest);
462
463                 default:
464                     return OC_STACK_ERROR;
465             }
466
467         case OC_REST_POST:
468             switch (ifQueryParam)
469             {
470                 case STACK_IF_DEFAULT:
471                     // M1 release does not support POST on default interface
472                     return OC_STACK_ERROR;
473
474                 case STACK_IF_LL:
475                     // LL interface only supports GET
476                     return OC_STACK_ERROR;
477
478                 case STACK_IF_BATCH:
479                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
480                                                                             HandleAggregateResponse;
481                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
482                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
483                     return HandleBatchInterface(ehRequest);
484
485                 case STACK_IF_GROUP:
486                     OIC_LOG(INFO, TAG, "IF_COLLECTION POST with request ::\n");
487                     OIC_LOG_PAYLOAD(INFO, ehRequest->payload);
488                     return BuildCollectionGroupActionCBORResponse(OC_REST_POST/*flag*/,
489                             (OCResource *) ehRequest->resource, ehRequest);
490
491                 default:
492                     return OC_STACK_ERROR;
493             }
494
495         case OC_REST_DELETE:
496             // TODO implement DELETE accordingly to the desired behavior
497             return OC_STACK_ERROR;
498
499         default:
500             return OC_STACK_ERROR;
501     }
502 }