Implemented libcoap's tinyDTLS interface
[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, PCF("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         // secure resource will entertain only authorized requests
308         if ((resourcePtr->resourceProperties & OC_SECURE) && (request->secure == 0))
309         {
310             OC_LOG(INFO, TAG, PCF("Un-authorized request. Ignore it!"));
311             return OC_STACK_RESOURCE_ERROR;
312         }
313
314         if (IsCollectionResource (resourcePtr))
315         {
316             // Collection resource
317             if (resourcePtr->entityHandler != defaultResourceEHandler)
318             {
319                 *handling = OC_RESOURCE_COLLECTION_WITH_ENTITYHANDLER;
320                 return OC_STACK_OK;
321             } else {
322                 *handling = OC_RESOURCE_COLLECTION_DEFAULT_ENTITYHANDLER;
323                 return OC_STACK_OK;
324             }
325         } else {
326             // Resource not a collection
327             if (resourcePtr->entityHandler != defaultResourceEHandler)
328             {
329                 *handling = OC_RESOURCE_NOT_COLLECTION_WITH_ENTITYHANDLER;
330                 return OC_STACK_OK;
331             } else {
332                 *handling = OC_RESOURCE_NOT_COLLECTION_DEFAULT_ENTITYHANDLER;
333                 return OC_STACK_OK;
334             }
335         }
336     }
337 }
338
339 OCStackResult EntityHandlerCodeToOCStackCode(OCEntityHandlerResult ehResult)
340 {
341     OCStackResult result;
342
343     switch (ehResult)
344     {
345         case OC_EH_OK:
346             result = OC_STACK_OK;
347             break;
348         case OC_EH_ERROR:
349             result = OC_STACK_ERROR;
350             break;
351         case OC_EH_FORBIDDEN:
352             result = OC_STACK_RESOURCE_ERROR;
353             break;
354         case OC_EH_RESOURCE_CREATED:
355             result = OC_STACK_RESOURCE_CREATED;
356             break;
357         case OC_EH_RESOURCE_DELETED:
358             result = OC_STACK_NO_RESOURCE;
359             break;
360         default:
361             result = OC_STACK_ERROR;
362     }
363
364     return result;
365 }
366
367 static OCStackResult
368 HandleVirtualResource (OCRequest *request, OCResource* resource)
369 {
370     OCStackResult result = OC_STACK_ERROR;
371     char *filterValue = NULL;
372     uint8_t filterOn = 0;
373     uint16_t remaining = 0;
374     unsigned char *buffer = NULL;
375
376     OC_LOG(INFO, TAG, PCF("Entering HandleVirtualResource"));
377
378     result = ValidateUrlQuery (request->resourceUrl,
379             request->entityHandlerRequest->query, &filterOn,
380             &filterValue);
381
382     if (result == OC_STACK_OK)
383     {
384         if (strcmp ((char *)request->resourceUrl, GetVirtualResourceUri(OC_WELL_KNOWN_URI)) == 0)
385         {
386             remaining = request->entityHandlerRequest->resJSONPayloadLen;
387             buffer = request->entityHandlerRequest->resJSONPayload;
388             while(resource)
389             {
390                 if((resource->resourceProperties & OC_ACTIVE)
391                         && (resource->resourceProperties & OC_DISCOVERABLE))
392                 {
393                     // if there is data on the buffer, we have already added a response,
394                     // so we need to add a comma before we do anything
395                     if(buffer != request->entityHandlerRequest->resJSONPayload
396                         && remaining >= (sizeof(OC_JSON_SEPARATOR)+1))
397                     {
398                         *buffer = OC_JSON_SEPARATOR;
399                         buffer++;
400                         remaining--;
401                     }
402
403                     result = BuildVirtualResourceResponse(resource, filterOn, filterValue,
404                             (char*)buffer, &remaining);
405                     if (result != OC_STACK_OK)
406                     {
407                         // if this failed, we need to remove the comma added above.
408                         if(buffer != request->entityHandlerRequest->resJSONPayload)
409                         {
410                             buffer--;
411                             *buffer = '\0';
412                             remaining++;
413                         }
414
415                         break;
416                     }
417                     buffer += strlen((char*)buffer);
418                 }
419                 resource = resource->next;
420             }
421         }
422         #ifdef WITH_PRESENCE
423         else
424         {
425             if(resource->resourceProperties & OC_ACTIVE){
426                 OCNotifyAllObservers((OCResourceHandle) resource, OC_LOW_QOS);
427             }
428             result = OC_STACK_PRESENCE_DO_NOT_HANDLE;
429         }
430         #endif
431     }
432
433     if (result == OC_STACK_OK)
434     {
435         request->entityHandlerRequest->resJSONPayloadLen = remaining;
436         request->entityHandlerRequest->resJSONPayload = buffer;
437     }
438
439     return result;
440 }
441
442 static OCStackResult
443 HandleDefaultDeviceEntityHandler (OCRequest *request)
444 {
445     OCStackResult result = OC_STACK_OK;
446     OCEntityHandlerResult ehResult;
447     OCEntityHandlerRequest *ehRequest = request->entityHandlerRequest;
448
449     // At this point we know for sure that defaultDeviceHandler exists
450     ehResult = defaultDeviceHandler(OC_REQUEST_FLAG, ehRequest,
451                                   (char*) request->resourceUrl);
452
453     result = EntityHandlerCodeToOCStackCode(ehResult);
454
455     ehRequest->resJSONPayloadLen = ehRequest->resJSONPayloadLen -
456                                     strlen((char*)ehRequest->resJSONPayload);
457     ehRequest->resJSONPayload += strlen((char*)ehRequest->resJSONPayload);
458
459     return result;
460 }
461
462 static OCStackResult
463 HandleResourceWithEntityHandler (OCRequest *request,
464                                  OCResource *resource,
465                                  uint8_t collectionResource)
466 {
467     OCStackResult result = OC_STACK_OK;
468     OCEntityHandlerResult ehResult = OC_EH_OK;
469
470     OCEntityHandlerRequest *ehRequest = request->entityHandlerRequest;
471
472     OC_LOG(INFO, TAG, PCF("Entering HandleResourceWithEntityHandler"));
473
474     ehRequest->resource = (OCResourceHandle)resource;
475
476     // status code from entity handler is ignored unless observe call
477     if (request->observe == NULL)
478     {
479         ehResult = resource->entityHandler(OC_REQUEST_FLAG, ehRequest);
480         result = EntityHandlerCodeToOCStackCode(ehResult);
481     }
482     else
483     {
484         // If an observation register/deregister is included handle separately
485         if (!collectionResource)
486         {
487             result = ProcessObserveRequest (resource, request);
488         }
489         else
490         {
491             // Observation on collection resources not currently supported
492             result = OC_STACK_ERROR;
493         }
494     }
495
496     if (result == OC_STACK_OK || OC_STACK_RESOURCE_CREATED)
497     {
498         ehRequest->resJSONPayloadLen = ehRequest->resJSONPayloadLen -
499             strlen((char*)ehRequest->resJSONPayload);
500         ehRequest->resJSONPayload += strlen((char*)ehRequest->resJSONPayload);
501     }
502
503     return result;
504 }
505
506
507 static OCStackResult
508 HandleCollectionResourceDefaultEntityHandler (OCRequest *request,
509                                               OCResource *resource)
510 {
511     request->entityHandlerRequest->resource = (OCResourceHandle)resource;
512     return (DefaultCollectionEntityHandler (OC_REQUEST_FLAG, request->entityHandlerRequest));
513 }
514
515
516 OCStackResult
517 BuildJSONResponse(ResourceHandling resHandling, OCResource *resource, OCRequest *request)
518 {
519     OCStackResult ret = OC_STACK_OK;
520
521     // save the response payload pointer, this pointer will be moved as
522     // different entity handlers will be called
523     unsigned char* saveJSONPayLoadPtr = request->entityHandlerRequest->resJSONPayload;
524     unsigned char* buffer = saveJSONPayLoadPtr;
525     uint16_t remaining = request->entityHandlerRequest->resJSONPayloadLen;
526
527     // add suffix in payload
528     if (remaining > OC_JSON_PREFIX_LEN)
529     {
530         strcpy((char*)buffer, OC_JSON_PREFIX);
531         remaining -= OC_JSON_PREFIX_LEN;
532         buffer += OC_JSON_PREFIX_LEN;
533     }
534
535     // move the entity handler payload pointer and update
536     // remaining valid bytes to fill data
537     request->entityHandlerRequest->resJSONPayload = buffer;
538     request->entityHandlerRequest->resJSONPayloadLen = remaining;
539
540     switch (resHandling)
541     {
542         case OC_RESOURCE_VIRTUAL:
543             {
544                 ret = HandleVirtualResource (request, resource);
545                 break;
546             }
547
548         case OC_RESOURCE_DEFAULT_DEVICE_ENTITYHANDLER:
549             {
550                 ret = HandleDefaultDeviceEntityHandler(request);
551                 break;
552             }
553         case OC_RESOURCE_NOT_COLLECTION_DEFAULT_ENTITYHANDLER:
554             {
555                 OC_LOG(INFO, TAG, PCF("OC_RESOURCE_NOT_COLLECTION_DEFAULT_ENTITYHANDLER"));
556                 return OC_STACK_ERROR;
557             }
558
559         case OC_RESOURCE_NOT_COLLECTION_WITH_ENTITYHANDLER:
560             {
561                 ret = HandleResourceWithEntityHandler (request, resource, 0);
562                 break;
563             }
564         case OC_RESOURCE_COLLECTION_WITH_ENTITYHANDLER:
565             {
566                 ret = HandleResourceWithEntityHandler (request, resource, 1);
567                 break;
568             }
569
570         case OC_RESOURCE_COLLECTION_DEFAULT_ENTITYHANDLER:
571             {
572                 ret = HandleCollectionResourceDefaultEntityHandler (request, resource);
573                 break;
574             }
575
576         default:
577             {
578                 OC_LOG(INFO, TAG, PCF("Invalid Resource Determination"));
579                 return OC_STACK_ERROR;
580             }
581     }
582
583     remaining = request->entityHandlerRequest->resJSONPayloadLen;
584
585     if (remaining > OC_JSON_SUFFIX_LEN)
586     {
587         strcpy((char*)request->entityHandlerRequest->resJSONPayload, OC_JSON_SUFFIX);
588     }
589
590     // update payload pointer with it's original location and original length
591     request->entityHandlerRequest->resJSONPayload = saveJSONPayLoadPtr;
592     request->entityHandlerRequest->resJSONPayloadLen = MAX_RESPONSE_LENGTH;
593
594     return ret;
595 }
596
597