iotivity 0.9.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 #define _POSIX_C_SOURCE 200112L
22 #include <string.h>
23 #include "ocstack.h"
24 #include "ocstackinternal.h"
25 #include "ocresourcehandler.h"
26 #include "logger.h"
27 #include "debug.h"
28 #include "cJSON.h"
29 /// Module Name
30 #include <stdio.h>
31
32 #define WITH_GROUPACTION 1
33
34 #include "oicgroup.h"
35
36 #define TAG PCF("occollection")
37
38 #define NUM_PARAM_IN_QUERY  2
39
40 static OCStackResult CheckRTParamSupport(const OCResource* resource, const char* rtPtr)
41 {
42     OCResourceType* rTPointer = resource->rsrcType;
43     while (rTPointer)
44     {
45         if( strcmp (rTPointer->resourcetypename, rtPtr) == 0)
46             return OC_STACK_OK;
47
48         rTPointer = rTPointer->next;
49     }
50     return OC_STACK_ERROR;
51 }
52
53 static OCStackResult CheckIFParamSupport(const OCResource* resource, const char* ifPtr)
54 {
55     OCResourceInterface* iFPointer = resource->rsrcInterface;
56     while (iFPointer)
57     {
58         if( strcmp (iFPointer->name, ifPtr) == 0)
59              return OC_STACK_OK;
60
61         iFPointer = iFPointer->next;
62     }
63     return OC_STACK_ERROR;
64 }
65
66 static OCStackResult
67 ValidateQuery (const unsigned char *query, OCResourceHandle resource,
68                              OCStackIfTypes *ifParam, char **rtParam)
69 {
70     uint8_t numFields = 0, numParam;
71
72     //TODO: Query and URL validation is being done for virtual resource case
73     // using ValidateUrlQuery function. We should be able to merge it with this
74     // function.
75     OC_LOG(INFO, TAG, PCF("Entering ValidateQuery"));
76
77     if (!query)
78         return OC_STACK_ERROR;
79
80     if (!(*query))
81     {
82         // Query string is empty
83         OC_LOG_V(INFO, TAG, PCF("Empty query string, use default IF and RT"));
84         *ifParam = STACK_IF_DEFAULT;
85         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
86         return OC_STACK_OK;
87     }
88
89     // Break the query string to validate it and determine IF and RT parameters
90     // Validate there are atmost 2 parameters in string and that one is 'if' and other 'rt'
91     char *endStr, *ifPtr = NULL, *rtPtr = NULL;
92     char *token = strtok_r ((char *)query, "&", &endStr);
93
94     // External loop breaks query string into fields using the & separator
95     while (token != NULL)
96     {
97         numFields++;
98         char *endToken;
99         char *innerToken = strtok_r (token, "=", &endToken);
100         numParam = 0;
101
102         // Internal loop parses the field to extract values (parameters) assigned to each field
103         while (innerToken != NULL)
104         {
105             numParam++;
106             if (strcmp (innerToken, OC_RSRVD_INTERFACE) == 0)
107             {
108                 // Determine the value of IF parameter
109                 innerToken = strtok_r (NULL, "=", &endToken);
110                 ifPtr = innerToken;
111             } else if (strcmp (innerToken, OC_RSRVD_RESOURCE_TYPE) == 0) {
112                 // Determine the value of RT parameter
113                 innerToken = strtok_r (NULL, "=", &endToken);
114                 rtPtr = innerToken;
115             } else {
116                 innerToken = strtok_r (NULL, "=", &endToken);
117             }
118         }
119         if (numParam != 2)
120         {
121             // Query parameter should be of the form if=<string>. String should not have & or =
122             return OC_STACK_INVALID_QUERY;
123         }
124         token = strtok_r (NULL, "&", &endStr);
125     }
126     if (numFields > NUM_PARAM_IN_QUERY)
127     {
128         // M1 release supports one IF value, one RT value and no other params
129         return OC_STACK_INVALID_QUERY;
130     }
131
132     if (ifPtr)
133     {
134         if(CheckIFParamSupport((OCResource *)resource, ifPtr) != OC_STACK_OK)
135         {
136             return OC_STACK_INVALID_QUERY;
137         }
138         if (strcmp (ifPtr, OC_RSRVD_INTERFACE_DEFAULT) == 0)
139         {
140             *ifParam = STACK_IF_DEFAULT;
141         }
142         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_LL) == 0)
143         {
144             *ifParam = STACK_IF_LL;
145         }
146         else if (strcmp (ifPtr, OC_RSRVD_INTERFACE_BATCH) == 0)
147         {
148             *ifParam = STACK_IF_BATCH;
149         }
150         else if(strcmp (ifPtr, OC_RSRVD_INTERFACE_GROUP) == 0)
151         {
152             *ifParam = STACK_IF_GROUP;
153         }
154         else
155         {
156             return OC_STACK_ERROR;
157         }
158     }
159     else
160     {
161         // IF not specified in query, use default IF
162         *ifParam = STACK_IF_DEFAULT;
163     }
164
165     if (rtPtr)
166     {
167         if (CheckRTParamSupport((OCResource *)resource, rtPtr) == OC_STACK_OK)
168         {
169             *rtParam = rtPtr;
170         }
171         else
172         {
173             return OC_STACK_INVALID_QUERY;
174         }
175     }
176     else
177     {
178         // RT not specified in query. Use the first resource type for the resource as default.
179         *rtParam = (char *) OCGetResourceTypeName (resource, 0);
180     }
181     OC_LOG_V(INFO, TAG, "Query params: IF = %d, RT = %s", *ifParam, *rtParam);
182
183     return OC_STACK_OK;
184 }
185
186
187 static OCStackResult BuildRootResourceJSON(OCResource *resource,
188         unsigned char * bufferPtr, uint16_t *remaining)
189 {
190     OCStackResult ret = OC_STACK_ERROR;
191     cJSON *resObj;
192     char *jsonStr;
193     uint16_t jsonLen;
194
195     OC_LOG(INFO, TAG, PCF("Entering BuildRootResourceJSON"));
196     resObj = cJSON_CreateObject();
197
198     if (resource)
199     {
200         cJSON_AddItemToObject (resObj, OC_RSRVD_HREF, cJSON_CreateString(resource->uri));
201     }
202     jsonStr = cJSON_PrintUnformatted (resObj);
203     jsonLen = strlen(jsonStr);
204     if (jsonLen < *remaining)
205     {
206         strcpy((char*) bufferPtr, jsonStr);
207         *remaining -= jsonLen;
208         bufferPtr += jsonLen;
209         ret = OC_STACK_OK;
210     }
211
212     cJSON_Delete (resObj);
213     free (jsonStr);
214
215     return ret;
216 }
217
218
219 static OCStackResult
220 HandleLinkedListInterface(OCEntityHandlerRequest *ehRequest,
221                        uint8_t filterOn, char *filterValue)
222 {
223     OCStackResult ret = OC_STACK_ERROR;
224     unsigned char jsonbuffer[MAX_RESPONSE_LENGTH] = {0};
225     size_t jsonbufferLength = 0;
226     uint16_t remaining = 0;
227     unsigned char * ptr = NULL;
228     OCResource * collResource = (OCResource *) ehRequest->resource;
229
230     ptr = jsonbuffer;
231     remaining = MAX_RESPONSE_LENGTH;
232
233     ret = BuildRootResourceJSON(collResource, ptr, &remaining);
234     ptr += strlen((char*)ptr);
235
236     if (ret == OC_STACK_OK && remaining >= (sizeof(OC_JSON_SEPARATOR) + 1))
237     {
238         *ptr = OC_JSON_SEPARATOR;
239         ptr++;
240         remaining--;
241     }
242     else
243     {
244         ret = OC_STACK_ERROR;
245     }
246     *(ptr + 1) = '\0';
247
248     if (ret == OC_STACK_OK)
249     {
250         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
251         {
252             OCResource* temp = collResource->rsrcResources[i];
253             if (temp)
254             {
255                 ret = BuildVirtualResourceResponse(temp, filterOn, filterValue, (char*)ptr, &remaining);
256                 if (ret != OC_STACK_OK)
257                 {
258                     break;
259                 }
260                 ptr += strlen((char*)ptr);
261                 if (collResource->rsrcResources[i+1] && remaining > sizeof(OC_JSON_SEPARATOR))
262                 {
263                     *ptr = OC_JSON_SEPARATOR;
264                     ptr++;
265                     remaining--;
266                 }
267                 *(ptr + 1) = '\0';
268             }
269             else
270             {
271                 break;
272             }
273         }
274     }
275
276     jsonbufferLength = strlen((const char *)jsonbuffer);
277     if(ret == OC_STACK_OK && jsonbufferLength)
278     {
279         OCEntityHandlerResponse response = {0};
280         response.ehResult = OC_EH_OK;
281         response.payload = jsonbuffer;
282         response.payloadSize = jsonbufferLength + 1;
283         response.persistentBufferFlag = 0;
284         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
285         response.resourceHandle = (OCResourceHandle) collResource;
286         ret = OCDoResponse(&response);
287     }
288     return ret;
289 }
290
291 static OCStackResult
292 HandleBatchInterface(OCEntityHandlerRequest *ehRequest)
293 {
294     OCStackResult stackRet = OC_STACK_ERROR;
295     OCEntityHandlerResult ehResult = OC_EH_ERROR;
296     unsigned char jsonbuffer[MAX_RESPONSE_LENGTH] = {0};
297     size_t jsonbufferLength = 0;
298     uint16_t remaining = 0;
299     unsigned char * ptr = NULL;
300     OCResource * collResource = (OCResource *) ehRequest->resource;
301
302     ptr = jsonbuffer;
303     remaining = MAX_RESPONSE_LENGTH;
304
305     stackRet = BuildRootResourceJSON(collResource, ptr, &remaining);
306     ptr += strlen((char*)ptr);
307     *(ptr + 1) = '\0';
308
309     jsonbufferLength = strlen((const char *)jsonbuffer);
310     if(jsonbufferLength)
311     {
312         OCEntityHandlerResponse response = {0};
313         response.ehResult = OC_EH_OK;
314         response.payload = jsonbuffer;
315         response.payloadSize = jsonbufferLength + 1;
316         response.persistentBufferFlag = 0;
317         response.requestHandle = (OCRequestHandle) ehRequest->requestHandle;
318         response.resourceHandle = (OCResourceHandle) collResource;
319         stackRet = OCDoResponse(&response);
320     }
321
322     if (stackRet == OC_STACK_OK)
323     {
324         for  (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
325         {
326             OCResource* temp = collResource->rsrcResources[i];
327             if (temp)
328             {
329                 // Note that all entity handlers called through a collection
330                 // will get the same pointer to ehRequest, the only difference
331                 // is ehRequest->resource
332                 ehRequest->resource = (OCResourceHandle) temp;
333
334                 ehResult = temp->entityHandler(OC_REQUEST_FLAG, ehRequest);
335
336                 // The default collection handler is returning as OK
337                 if(stackRet != OC_STACK_SLOW_RESOURCE)
338                 {
339                     stackRet = OC_STACK_OK;
340                 }
341                 // if a single resource is slow, then entire response will be treated
342                 // as slow response
343                 if(ehResult == OC_EH_SLOW)
344                 {
345                     OC_LOG(INFO, TAG, PCF("This is a slow resource"));
346                     ((OCServerRequest *)ehRequest->requestHandle)->slowFlag = 1;
347                     stackRet = EntityHandlerCodeToOCStackCode(ehResult);
348                 }
349             }
350             else
351             {
352                 break;
353             }
354         }
355         ehRequest->resource = (OCResourceHandle) collResource;
356     }
357     return stackRet;
358 }
359
360 uint8_t GetNumOfResourcesInCollection (OCResource *resource)
361 {
362     uint8_t num = 0;
363     for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
364     {
365         if (resource->rsrcResources[i])
366         {
367             num++;
368         }
369     }
370     return num;
371 }
372
373
374 OCStackResult DefaultCollectionEntityHandler (OCEntityHandlerFlag flag,
375                                               OCEntityHandlerRequest *ehRequest)
376 {
377     OCStackResult result = OC_STACK_ERROR;
378     OCStackIfTypes ifQueryParam = STACK_IF_INVALID;
379     char *rtQueryParam = NULL;
380
381     OC_LOG_V(INFO, TAG, "DefaultCollectionEntityHandler with query %s", ehRequest->query);
382
383     if (flag != OC_REQUEST_FLAG)
384         return OC_STACK_ERROR;
385
386     result = ValidateQuery ((const unsigned char *)ehRequest->query,
387                             ehRequest->resource, &ifQueryParam, &rtQueryParam);
388
389     if (result != OC_STACK_OK)
390         return result;
391
392   
393     if(!((ehRequest->method == OC_REST_GET) || 
394         (ehRequest->method == OC_REST_PUT) ||
395         (ehRequest->method == OC_REST_POST)))
396         return OC_STACK_ERROR;
397
398     if (ehRequest->method == OC_REST_GET)
399     {
400         switch (ifQueryParam)
401         {
402             case STACK_IF_DEFAULT:
403                 // Get attributes of collection resource and properties of contined resource
404                 // M1 release does not support attributes for collection resource, so the GET
405                 // operation is same as the GET on LL interface.
406                 OC_LOG(INFO, TAG, PCF("STACK_IF_DEFAULT"));
407                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
408
409             case STACK_IF_LL:
410                 OC_LOG(INFO, TAG, PCF("STACK_IF_LL"));
411                 return HandleLinkedListInterface(ehRequest, STACK_RES_DISCOVERY_NOFILTER, NULL);
412
413             case STACK_IF_BATCH:
414                 OC_LOG(INFO, TAG, PCF("STACK_IF_BATCH"));
415                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler = HandleAggregateResponse;
416                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
417                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
418                 return HandleBatchInterface(ehRequest);
419             case STACK_IF_GROUP:
420                 return BuildCollectionGroupActionJSONResponse(OC_REST_GET/*flag*/,
421                         (OCResource *) ehRequest->resource, ehRequest);
422             default:
423                 return OC_STACK_ERROR;
424         }
425     } else if (ehRequest->method == OC_REST_PUT) {
426         switch (ifQueryParam)
427         {
428             case STACK_IF_DEFAULT:
429                 // M1 release does not support PUT on default interface
430                 return OC_STACK_ERROR;
431
432             case STACK_IF_LL:
433                 // LL interface only supports GET
434                 return OC_STACK_ERROR;
435
436             case STACK_IF_BATCH:
437                 ((OCServerRequest *)ehRequest->requestHandle)->ehResponseHandler = HandleAggregateResponse;
438                 ((OCServerRequest *)ehRequest->requestHandle)->numResponses =
439                         GetNumOfResourcesInCollection((OCResource *)ehRequest->resource) + 1;
440                 return HandleBatchInterface(ehRequest);
441
442             case STACK_IF_GROUP:
443             {
444                 OC_LOG_V(INFO, TAG, "IF_COLLECTION PUT with request ::\n%s\n ",
445                         ehRequest->reqJSONPayload);
446                 return BuildCollectionGroupActionJSONResponse(OC_REST_PUT/*flag*/,
447                         (OCResource *) ehRequest->resource, ehRequest);
448             }
449             default:
450                 return OC_STACK_ERROR;
451         }
452     }
453     else if (ehRequest->method == OC_REST_POST)
454     {
455
456         switch (ifQueryParam)
457         {
458             case STACK_IF_GROUP:
459             {
460                 OC_LOG_V(INFO, TAG, "IF_COLLECTION POST with request :: \n%s\n ",
461                         ehRequest->reqJSONPayload);
462                 return BuildCollectionGroupActionJSONResponse(OC_REST_POST/*flag*/,
463                         (OCResource *) ehRequest->resource, ehRequest);
464             }
465             default:
466                 return OC_STACK_ERROR;
467         }
468     }
469     return result;
470 }
471