69355d10ed9fbc399ffcd13fe38116031224a20d
[contrib/iotivity.git] / resource / csdk / stack / src / ocresource.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 #include <string.h>
22 #include "ocstack.h"
23 #include "ocstackconfig.h"
24 #include "ocstackinternal.h"
25 #include "ocresource.h"
26 #include "ocobserve.h"
27 #include "occollection.h"
28 #include "logger.h"
29 #include "debug.h"
30 #include "cJSON.h"
31
32 /// Module Name
33 #define TAG PCF("ocresource")
34 #define VERIFY_NON_NULL(arg, logLevel, retVal) { if (!(arg)) { OC_LOG((logLevel), \
35              TAG, PCF(#arg " is NULL")); return (retVal); } }
36
37 extern OCResource *headResource;
38
39 static const char * VIRTUAL_RSRCS[] = {
40        "/oc/core",
41        "/oc/core/d",
42        "/oc/core/types/d",
43        #ifdef WITH_PRESENCE
44        "/oc/presence"
45        #endif
46 };
47
48 //-----------------------------------------------------------------------------
49 // Default resource entity handler function
50 //-----------------------------------------------------------------------------
51 OCEntityHandlerResult defaultResourceEHandler(OCEntityHandlerFlag flag,
52         OCEntityHandlerRequest * request) {
53     TODO ("Implement me!!!!");
54     // TODO:  remove silence unused param warnings
55     (void) flag;
56     (void) request;
57     return  OC_EH_OK; // Making sure that the Default EH and the Vendor EH have matching signatures
58 }
59
60 static OCStackResult ValidateUrlQuery (unsigned char *url, unsigned char *query,
61                                 uint8_t *filterOn, char **filterValue)
62 {
63     char *filterParam;
64
65     OC_LOG(INFO, TAG, PCF("Entering ValidateUrlQuery"));
66     if (!url)
67         return OC_STACK_INVALID_URI;
68
69     if (strcmp ((char *)url, GetVirtualResourceUri(OC_WELL_KNOWN_URI)) == 0) {
70         *filterOn = STACK_RES_DISCOVERY_NOFILTER;
71         if (query && *query) {
72             filterParam = strtok ((char *)query, "=");
73             *filterValue = strtok (NULL, " ");
74             if (!(*filterValue)) {
75                 return OC_STACK_INVALID_QUERY;
76             } else if (strcmp (filterParam, OC_RSRVD_INTERFACE) == 0) {
77                 // Resource discovery with interface filter
78                 *filterOn = STACK_RES_DISCOVERY_IF_FILTER;
79             } else if (strcmp (filterParam, OC_RSRVD_RESOURCE_TYPE) == 0) {
80                 // Resource discovery with resource type filter
81                 *filterOn = STACK_RES_DISCOVERY_RT_FILTER;
82             } else {
83                 // Other filter types not supported
84                 return OC_STACK_INVALID_QUERY;
85             }
86         }
87     }
88     #ifdef WITH_PRESENCE
89     else if (strcmp((char *)url, GetVirtualResourceUri(OC_PRESENCE)) == 0) {
90         //Nothing needs to be done, except for pass a OC_PRESENCE query through as OC_STACK_OK.
91         OC_LOG(INFO, TAG, "OC_PRESENCE Request!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
92         *filterOn = STACK_RES_DISCOVERY_NOFILTER;
93     }
94     #endif
95     else {
96         // Other URIs not yet supported
97         return OC_STACK_INVALID_URI;
98     }
99     OC_LOG(INFO, TAG, PCF("Exiting ValidateUrlQuery"));
100     return OC_STACK_OK;
101 }
102
103 OCStackResult BuildVirtualResourceResponse(OCResource *resourcePtr, uint8_t filterOn,
104                                             char *filterValue, char * out, uint16_t *remaining)
105 {
106     OCResourceType *resourceTypePtr;
107     OCResourceInterface *interfacePtr;
108     cJSON *resObj, *propObj, *rtArray;
109     char *jsonStr;
110     uint8_t encodeRes = 0;
111     OCStackResult ret = OC_STACK_OK;
112     uint16_t jsonLen;
113
114     OC_LOG(INFO, TAG, PCF("Entering BuildVirtualResourceResponse"));
115     resObj = cJSON_CreateObject();
116
117     if (resourcePtr)
118     {
119         encodeRes = 0;
120         if (filterOn == STACK_RES_DISCOVERY_RT_FILTER) {
121             resourceTypePtr = resourcePtr->rsrcType;
122             while (resourceTypePtr) {
123                 if (strcmp (resourceTypePtr->resourcetypename, filterValue) == 0) {
124                     encodeRes = 1;
125                     break;
126                 }
127                 resourceTypePtr = resourceTypePtr->next;
128             }
129         } else if (filterOn == STACK_RES_DISCOVERY_IF_FILTER) {
130             interfacePtr = resourcePtr->rsrcInterface;
131             while (interfacePtr) {
132                 if (strcmp (interfacePtr->name, filterValue) == 0) {
133                     encodeRes = 1;
134                     break;
135                 }
136                 interfacePtr = interfacePtr->next;
137             }
138         } else if (filterOn == STACK_RES_DISCOVERY_NOFILTER) {
139             encodeRes = 1;
140         } else {
141             //TODO: Unsupported query filter
142             return OC_STACK_INVALID_QUERY;
143         }
144
145         if (encodeRes) {
146             // Add URIs
147             cJSON_AddItemToObject (resObj, OC_RSRVD_HREF, cJSON_CreateString(resourcePtr->uri));
148
149             cJSON_AddItemToObject (resObj, "prop", propObj = cJSON_CreateObject());
150             // Add resource types
151             cJSON_AddItemToObject (propObj, OC_RSRVD_RESOURCE_TYPE, rtArray = cJSON_CreateArray());
152             resourceTypePtr = resourcePtr->rsrcType;
153             while (resourceTypePtr) {
154                 cJSON_AddItemToArray (rtArray,
155                                       cJSON_CreateString(resourceTypePtr->resourcetypename));
156                 resourceTypePtr = resourceTypePtr->next;
157             }
158             // Add interface types
159             cJSON_AddItemToObject (propObj, OC_RSRVD_INTERFACE, rtArray = cJSON_CreateArray());
160             interfacePtr = resourcePtr->rsrcInterface;
161             while (interfacePtr) {
162                 cJSON_AddItemToArray (rtArray, cJSON_CreateString(interfacePtr->name));
163                 interfacePtr = interfacePtr->next;
164             }
165             // If resource is observable, set observability flag.
166             // Resources that are not observable will not have the flag.
167             if (resourcePtr->resourceProperties & OC_OBSERVABLE) {
168                 cJSON_AddItemToObject (propObj, OC_RSRVD_OBSERVABLE,
169                                        cJSON_CreateNumber(OC_RESOURCE_OBSERVABLE));
170             }
171         }
172     }
173     jsonStr = cJSON_PrintUnformatted (resObj);
174     jsonLen = strlen(jsonStr);
175     if (jsonLen < *remaining)
176     {
177         strcpy(out, jsonStr);
178         *remaining = *remaining - jsonLen;
179     }
180     else
181     {
182         ret = OC_STACK_ERROR;
183     }
184     cJSON_Delete (resObj);
185     free (jsonStr);
186
187
188     OC_LOG(INFO, TAG, PCF("Exiting BuildVirtualResourceResponse"));
189     return ret;
190 }
191
192 OCEntityHandlerResult
193 BuildObsJSONResponse(OCResource *resource, OCEntityHandlerRequest *ehRequest)
194 {
195     OCEntityHandlerResult ret = OC_EH_ERROR;
196     unsigned char* saveJSONPayLoadPtr = ehRequest->resJSONPayload;
197
198     if (ehRequest->resJSONPayloadLen > OC_JSON_PREFIX_LEN)
199     {
200         strcpy((char*)ehRequest->resJSONPayload, OC_JSON_PREFIX);
201         ehRequest->resJSONPayloadLen -= OC_JSON_PREFIX_LEN;
202         ehRequest->resJSONPayload += OC_JSON_PREFIX_LEN;
203     }
204
205     ret = resource->entityHandler(OC_REQUEST_FLAG, ehRequest);
206
207     ehRequest->resJSONPayloadLen = ehRequest->resJSONPayloadLen -
208             strlen((char*)ehRequest->resJSONPayload);
209     ehRequest->resJSONPayload += strlen((char*)ehRequest->resJSONPayload);
210
211     if (ehRequest->resJSONPayloadLen > OC_JSON_SUFFIX_LEN)
212     {
213         strcpy((char*)ehRequest->resJSONPayload, OC_JSON_SUFFIX);
214     }
215     ehRequest->resJSONPayload = saveJSONPayLoadPtr;
216     return ret;
217 }
218
219
220 TODO ("Does it make sense to make this method as inline")
221 const char * GetVirtualResourceUri( OCVirtualResources resource)
222 {
223     if (resource < OC_MAX_VIRTUAL_RESOURCES)
224     {
225         return VIRTUAL_RSRCS[resource];
226     }
227
228     return NULL;
229 }
230
231 uint8_t IsVirtualResource(const char* resourceUri)
232 {
233     for (int i = 0; i < OC_MAX_VIRTUAL_RESOURCES; i++)
234     {
235         if (strcmp(resourceUri, GetVirtualResourceUri((OCVirtualResources)i)) == 0)
236         {
237             return 1;
238         }
239     }
240     return 0;
241 }
242
243 uint8_t IsCollectionResource (OCResource *resource)
244 {
245     for (int i = 0; i < MAX_CONTAINED_RESOURCES; i++)
246     {
247         if (resource->rsrcResources[i])
248         {
249             return 1;
250         }
251     }
252     return 0;
253 }
254
255 OCResource *FindResourceByUri(const char* resourceUri)
256 {
257     OCResource * pointer = headResource;
258     while (pointer) {
259         if (strcmp(resourceUri, pointer->uri) == 0) {
260             return pointer;
261         }
262         pointer = pointer->next;
263     }
264     OC_LOG(INFO, TAG, PCF("Resource not found"));
265     return NULL;
266 }
267
268
269 OCStackResult DetermineResourceHandling (OCRequest *request,
270                                          ResourceHandling *handling,
271                                          OCResource **resource)
272 {
273
274     OC_LOG(INFO, TAG, PCF("Entering DetermineResourceHandling"));
275
276     // Check if virtual resource
277     if (IsVirtualResource((const char*)request->resourceUrl))
278     {
279         *handling = OC_RESOURCE_VIRTUAL;
280         *resource = headResource;
281         return OC_STACK_OK;
282     }
283     if (NULL == request->resourceUrl || (strlen((const char*)(request->resourceUrl)) == 0))
284     {
285         // Resource URL not specified
286         *handling = OC_RESOURCE_NOT_SPECIFIED;
287         return OC_STACK_OK;
288     }
289     else
290     {
291         OCResource *resourcePtr = NULL;
292         resourcePtr = FindResourceByUri((const char*)request->resourceUrl);
293         *resource = resourcePtr;
294         if (!resourcePtr)
295         {
296             if(defaultDeviceHandler)
297             {
298                 *handling = OC_RESOURCE_DEFAULT_DEVICE_ENTITYHANDLER;
299                 return OC_STACK_OK;
300             }
301
302             // Resource does not exist
303             // and default device handler does not exist
304             return OC_STACK_NO_RESOURCE;
305         }
306
307         if (IsCollectionResource (resourcePtr))
308         {
309             // Collection resource
310             if (resourcePtr->entityHandler != defaultResourceEHandler)
311             {
312                 *handling = OC_RESOURCE_COLLECTION_WITH_ENTITYHANDLER;
313                 return OC_STACK_OK;
314             } else {
315                 *handling = OC_RESOURCE_COLLECTION_DEFAULT_ENTITYHANDLER;
316                 return OC_STACK_OK;
317             }
318         } else {
319             // Resource not a collection
320             if (resourcePtr->entityHandler != defaultResourceEHandler)
321             {
322                 *handling = OC_RESOURCE_NOT_COLLECTION_WITH_ENTITYHANDLER;
323                 return OC_STACK_OK;
324             } else {
325                 *handling = OC_RESOURCE_NOT_COLLECTION_DEFAULT_ENTITYHANDLER;
326                 return OC_STACK_OK;
327             }
328         }
329     }
330 }
331
332 OCStackResult EntityHandlerCodeToOCStackCode(OCEntityHandlerResult ehResult)
333 {
334     OCStackResult result;
335
336     switch (ehResult)
337     {
338         case OC_EH_OK:
339             result = OC_STACK_OK;
340             break;
341         case OC_EH_ERROR:
342             result = OC_STACK_ERROR;
343             break;
344         case OC_EH_FORBIDDEN:
345             result = OC_STACK_RESOURCE_ERROR;
346             break;
347         case OC_EH_RESOURCE_CREATED:
348             result = OC_STACK_RESOURCE_CREATED;
349             break;
350         case OC_EH_RESOURCE_DELETED:
351             result = OC_STACK_NO_RESOURCE;
352             break;
353         default:
354             result = OC_STACK_ERROR;
355     }
356
357     return result;
358 }
359
360 static OCStackResult
361 HandleVirtualResource (OCRequest *request, OCResource* resource)
362 {
363     OCStackResult result = OC_STACK_ERROR;
364     char *filterValue = NULL;
365     uint8_t filterOn = 0;
366     uint16_t remaining = 0;
367     unsigned char *buffer = NULL;
368
369     OC_LOG(INFO, TAG, PCF("Entering HandleVirtualResource"));
370
371     result = ValidateUrlQuery (request->resourceUrl,
372             request->entityHandlerRequest->query, &filterOn,
373             &filterValue);
374
375     if (result == OC_STACK_OK)
376     {
377         if (strcmp ((char *)request->resourceUrl, GetVirtualResourceUri(OC_WELL_KNOWN_URI)) == 0)
378         {
379             remaining = request->entityHandlerRequest->resJSONPayloadLen;
380             buffer = request->entityHandlerRequest->resJSONPayload;
381             while(resource)
382             {
383                 if((resource->resourceProperties & OC_ACTIVE)
384                         && (resource->resourceProperties & OC_DISCOVERABLE))
385                 {
386                     // if there is data on the buffer, we have already added a response,
387                     // so we need to add a comma before we do anything
388                     if(buffer != request->entityHandlerRequest->resJSONPayload
389                         && remaining >= (sizeof(OC_JSON_SEPARATOR)+1))
390                     {
391                         *buffer = OC_JSON_SEPARATOR;
392                         buffer++;
393                         remaining--;
394                     }
395
396                     result = BuildVirtualResourceResponse(resource, filterOn, filterValue,
397                             (char*)buffer, &remaining);
398                     if (result != OC_STACK_OK)
399                     {
400                         // if this failed, we need to remove the comma added above.
401                         if(buffer != request->entityHandlerRequest->resJSONPayload)
402                         {
403                             buffer--;
404                             *buffer = '\0';
405                             remaining++;
406                         }
407
408                         break;
409                     }
410                     buffer += strlen((char*)buffer);
411                 }
412                 resource = resource->next;
413             }
414         }
415         #ifdef WITH_PRESENCE
416         else
417         {
418             if(resource->resourceProperties & OC_ACTIVE){
419                 OCNotifyAllObservers((OCResourceHandle) resource, OC_LOW_QOS);
420             }
421             result = OC_STACK_PRESENCE_DO_NOT_HANDLE;
422         }
423         #endif
424     }
425
426     if (result == OC_STACK_OK)
427     {
428         request->entityHandlerRequest->resJSONPayloadLen = remaining;
429         request->entityHandlerRequest->resJSONPayload = buffer;
430     }
431
432     return result;
433 }
434
435 static OCStackResult
436 HandleDefaultDeviceEntityHandler (OCRequest *request)
437 {
438     OCStackResult result = OC_STACK_OK;
439     OCEntityHandlerResult ehResult;
440     OCEntityHandlerRequest *ehRequest = request->entityHandlerRequest;
441
442     // At this point we know for sure that defaultDeviceHandler exists
443     ehResult = defaultDeviceHandler(OC_REQUEST_FLAG, ehRequest,
444                                   (char*) request->resourceUrl);
445
446     result = EntityHandlerCodeToOCStackCode(ehResult);
447
448     ehRequest->resJSONPayloadLen = ehRequest->resJSONPayloadLen -
449                                     strlen((char*)ehRequest->resJSONPayload);
450     ehRequest->resJSONPayload += strlen((char*)ehRequest->resJSONPayload);
451
452     return result;
453 }
454
455 static OCStackResult
456 HandleResourceWithEntityHandler (OCRequest *request,
457                                  OCResource *resource,
458                                  uint8_t collectionResource)
459 {
460     OCStackResult result = OC_STACK_OK;
461     OCEntityHandlerResult ehResult = OC_EH_OK;
462
463     OCEntityHandlerRequest *ehRequest = request->entityHandlerRequest;
464
465     OC_LOG(INFO, TAG, PCF("Entering HandleResourceWithEntityHandler"));
466
467     ehRequest->resource = (OCResourceHandle)resource;
468
469     // status code from entity handler is ignored unless observe call
470     if (request->observe == NULL)
471     {
472         ehResult = resource->entityHandler(OC_REQUEST_FLAG, ehRequest);
473         result = EntityHandlerCodeToOCStackCode(ehResult);
474     }
475     else
476     {
477         // If an observation register/deregister is included handle separately
478         if (!collectionResource)
479         {
480             result = ProcessObserveRequest (resource, request);
481         }
482         else
483         {
484             // Observation on collection resources not currently supported
485             result = OC_STACK_ERROR;
486         }
487     }
488
489     if (result == OC_STACK_OK || OC_STACK_RESOURCE_CREATED)
490     {
491         ehRequest->resJSONPayloadLen = ehRequest->resJSONPayloadLen -
492             strlen((char*)ehRequest->resJSONPayload);
493         ehRequest->resJSONPayload += strlen((char*)ehRequest->resJSONPayload);
494     }
495
496     return result;
497 }
498
499
500 static OCStackResult
501 HandleCollectionResourceDefaultEntityHandler (OCRequest *request,
502                                               OCResource *resource)
503 {
504     request->entityHandlerRequest->resource = (OCResourceHandle)resource;
505     return (DefaultCollectionEntityHandler (OC_REQUEST_FLAG, request->entityHandlerRequest));
506 }
507
508
509 OCStackResult
510 BuildJSONResponse(ResourceHandling resHandling, OCResource *resource, OCRequest *request)
511 {
512     OCStackResult ret = OC_STACK_OK;
513
514     // save the response payload pointer, this pointer will be moved as
515     // different entity handlers will be called
516     unsigned char* saveJSONPayLoadPtr = request->entityHandlerRequest->resJSONPayload;
517     unsigned char* buffer = saveJSONPayLoadPtr;
518     uint16_t remaining = request->entityHandlerRequest->resJSONPayloadLen;
519
520     // add suffix in payload
521     if (remaining > OC_JSON_PREFIX_LEN)
522     {
523         strcpy((char*)buffer, OC_JSON_PREFIX);
524         remaining -= OC_JSON_PREFIX_LEN;
525         buffer += OC_JSON_PREFIX_LEN;
526     }
527
528     // move the entity handler payload pointer and update
529     // remaining valid bytes to fill data
530     request->entityHandlerRequest->resJSONPayload = buffer;
531     request->entityHandlerRequest->resJSONPayloadLen = remaining;
532
533     switch (resHandling)
534     {
535         case OC_RESOURCE_VIRTUAL:
536             {
537                 ret = HandleVirtualResource (request, resource);
538                 break;
539             }
540
541         case OC_RESOURCE_DEFAULT_DEVICE_ENTITYHANDLER:
542             {
543                 ret = HandleDefaultDeviceEntityHandler(request);
544                 break;
545             }
546         case OC_RESOURCE_NOT_COLLECTION_DEFAULT_ENTITYHANDLER:
547             {
548                 OC_LOG(INFO, TAG, PCF("OC_RESOURCE_NOT_COLLECTION_DEFAULT_ENTITYHANDLER"));
549                 return OC_STACK_ERROR;
550             }
551
552         case OC_RESOURCE_NOT_COLLECTION_WITH_ENTITYHANDLER:
553             {
554                 ret = HandleResourceWithEntityHandler (request, resource, 0);
555                 break;
556             }
557         case OC_RESOURCE_COLLECTION_WITH_ENTITYHANDLER:
558             {
559                 ret = HandleResourceWithEntityHandler (request, resource, 1);
560                 break;
561             }
562
563         case OC_RESOURCE_COLLECTION_DEFAULT_ENTITYHANDLER:
564             {
565                 ret = HandleCollectionResourceDefaultEntityHandler (request, resource);
566                 break;
567             }
568
569         default:
570             {
571                 OC_LOG(INFO, TAG, PCF("Invalid Resource Determination"));
572                 return OC_STACK_ERROR;
573             }
574     }
575
576     remaining = request->entityHandlerRequest->resJSONPayloadLen;
577
578     if (remaining > OC_JSON_SUFFIX_LEN)
579     {
580         strcpy((char*)request->entityHandlerRequest->resJSONPayload, OC_JSON_SUFFIX);
581     }
582
583     // update payload pointer with it's original location and original length
584     request->entityHandlerRequest->resJSONPayload = saveJSONPayLoadPtr;
585     request->entityHandlerRequest->resJSONPayloadLen = MAX_RESPONSE_LENGTH;
586
587     return ret;
588 }
589
590