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