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