replaced OC_LOG with OIC_LOG in stack
[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
241     OCRepPayload* payload = NULL;
242     OCStackResult ret = BuildResponseRepresentation(collResource, &payload);
243     if (ret == OC_STACK_OK)
244     {
245         for (size_t i = 0; i < MAX_CONTAINED_RESOURCES && ret == OC_STACK_OK; i++)
246         {
247             if (collResource)
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
260     if (ret == OC_STACK_OK)
261     {
262         OCEntityHandlerResponse response = {0};
263         response.ehResult = OC_EH_OK;
264         response.payload = (OCPayload*)payload;
265         response.persistentBufferFlag = 0;
266         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
267         response.resourceHandle = (OCResourceHandle) collResource;
268         ret = OCDoResponse(&response);
269     }
270     OCRepPayloadDestroy(payload);
271     return ret;
272 }
273
274 static OCStackResult
275 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
276 {
277     if (!ehRequest)
278     {
279         return OC_STACK_INVALID_PARAM;
280     }
281
282     OCResource * collResource = (OCResource *) ehRequest->resource;
283
284     OCRepPayload* payload = OCRepPayloadCreate();
285     if (!payload)
286     {
287         return OC_STACK_NO_MEMORY;
288     }
289
290     if (collResource)
291     {
292         OCRepPayloadSetUri(payload, collResource->uri);
293     }
294
295     OCEntityHandlerResponse response = {0};
296     response.ehResult = OC_EH_OK;
297     response.payload = (OCPayload*)payload;
298     response.persistentBufferFlag = 0;
299     response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
300     response.resourceHandle = (OCResourceHandle) collResource;
301     OCStackResult stackRet = OCDoResponse(&response);
302
303     if (stackRet == OC_STACK_OK)
304     {
305         for (uint8_t i = 0; i < MAX_CONTAINED_RESOURCES; i++)
306         {
307             OCResource* temp = collResource->rsrcResources[i];
308             if (temp)
309             {
310                 // Note that all entity handlers called through a collection
311                 // will get the same pointer to ehRequest, the only difference
312                 // is ehRequest->resource
313                 ehRequest->resource = (OCResourceHandle) temp;
314
315                 OCEntityHandlerResult ehResult = temp->entityHandler(OC_REQUEST_FLAG, ehRequest,
316                                                                      temp->entityHandlerCallbackParam);
317
318                 // The default collection handler is returning as OK
319                 if (stackRet != OC_STACK_SLOW_RESOURCE)
320                 {
321                     stackRet = OC_STACK_OK;
322                 }
323                 // if a single resource is slow, then entire response will be treated
324                 // as slow response
325                 if (ehResult == OC_EH_SLOW)
326                 {
327                     OIC_LOG(INFO, TAG, "This is a slow resource");
328                     ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
329                     stackRet = EntityHandlerCodeToOCStackCode(ehResult);
330                 }
331             }
332             else
333             {
334                 break;
335             }
336         }
337         ehRequest->resource = (OCResourceHandle) collResource;
338     }
339     return stackRet;
340 }
341
342 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
343 {
344     if (resource)
345     {
346         uint8_t num = 0;
347         for (uint8_t i = 0; i < MAX_CONTAINED_RESOURCES; i++)
348         {
349             if (resource->rsrcResources[i])
350             {
351                 num++;
352             }
353         }
354         return num;
355     }
356     else
357     {
358         return -1;
359     }
360 }
361
362
363 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
364                                               OCEntityHandlerRequest *ehRequest)
365 {
366     if (!ehRequest || !ehRequest->query)
367     {
368         return OC_STACK_INVALID_PARAM;
369     }
370
371     OIC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
372
373     if (flag != OC_REQUEST_FLAG)
374     {
375         return OC_STACK_ERROR;
376     }
377
378     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
379     char *rtQueryParam = NULL;
380     OCStackResult result = ValidateQuery (ehRequest->query,
381                                           ehRequest->resource, &ifQueryParam, &rtQueryParam);
382
383     if (result != OC_STACK_OK)
384     {
385         return result;
386     }
387
388     switch (ehRequest->method)
389     {
390         case OC_REST_GET:
391             switch (ifQueryParam)
392             {
393                 case STACK_IF_DEFAULT:
394                     // Get attributes of collection resource and properties of contained resources
395                     // M1 release does not support attributes for collection resource, so the GET
396                     // operation is same as the GET on LL interface.
397                     OIC_LOG(INFO, TAG, "STACK_IF_DEFAULT");
398                     return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
399
400                 case STACK_IF_LL:
401                     OIC_LOG(INFO, TAG, "STACK_IF_LL");
402                     return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
403
404                 case STACK_IF_BATCH:
405                     OIC_LOG(INFO, TAG, "STACK_IF_BATCH");
406                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
407                                                                             HandleAggregateResponse;
408
409                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
410                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
411
412                     return HandleBatchInterface(ehRequest);
413
414                 case STACK_IF_GROUP:
415                     return BuildCollectionGroupActionCBORResponse(OC_REST_GET/*flag*/,
416                             (OCResource *) ehRequest->resource, ehRequest);
417
418                 default:
419                     return OC_STACK_ERROR;
420             }
421
422         case OC_REST_PUT:
423             switch (ifQueryParam)
424             {
425                 case STACK_IF_DEFAULT:
426                     // M1 release does not support PUT on default interface
427                     return OC_STACK_ERROR;
428
429                 case STACK_IF_LL:
430                     // LL interface only supports GET
431                     return OC_STACK_ERROR;
432
433                 case STACK_IF_BATCH:
434                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
435                                                                             HandleAggregateResponse;
436                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
437                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
438                     return HandleBatchInterface(ehRequest);
439
440                 case STACK_IF_GROUP:
441                     OIC_LOG(INFO, TAG, "IF_COLLECTION PUT with request ::\n");
442                     OIC_LOG_PAYLOAD(INFO, ehRequest->payload);
443                     return BuildCollectionGroupActionCBORResponse(OC_REST_PUT/*flag*/,
444                             (OCResource *) ehRequest->resource, ehRequest);
445
446                 default:
447                     return OC_STACK_ERROR;
448             }
449
450         case OC_REST_POST:
451             switch (ifQueryParam)
452             {
453                 case STACK_IF_DEFAULT:
454                     // M1 release does not support POST 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                     ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler =
463                                                                             HandleAggregateResponse;
464                     ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
465                             GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
466                     return HandleBatchInterface(ehRequest);
467
468                 case STACK_IF_GROUP:
469                     OIC_LOG(INFO, TAG, "IF_COLLECTION POST with request ::\n");
470                     OIC_LOG_PAYLOAD(INFO, ehRequest->payload);
471                     return BuildCollectionGroupActionCBORResponse(OC_REST_POST/*flag*/,
472                             (OCResource *) ehRequest->resource, ehRequest);
473
474                 default:
475                     return OC_STACK_ERROR;
476             }
477
478         case OC_REST_DELETE:
479             // TODO implement DELETE accordingly to the desired behavior
480             return OC_STACK_ERROR;
481
482         default:
483             return OC_STACK_ERROR;
484     }
485 }