Converting OTA payload for advertisements to JSON packaging.
[contrib/iotivity.git] / resource / csdk / stack / src / ocobserve.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 "ocobserve.h"
26 #include "ocresourcehandler.h"
27 #include "ocrandom.h"
28 #include "ocmalloc.h"
29 #include "ocserverrequest.h"
30 #include "cJSON.h"
31
32 #include "utlist.h"
33 #include "pdu.h"
34
35 // Module Name
36 #define MOD_NAME PCF("ocobserve")
37
38 #define TAG  PCF("OCStackObserve")
39
40 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
41
42 static struct ResourceObserver * serverObsList = NULL;
43 #ifdef WITH_PRESENCE
44 static char* GetJSONStringForPresence(uint32_t ttl, uint32_t nonce,
45         OCPresenceTrigger trigger, OCResourceType *resourceType)
46 {
47     cJSON *rootObj = cJSON_CreateObject();
48     if (!rootObj)
49     {
50         return NULL;
51     }
52
53     char *jsonEncodedInfo = NULL;
54     const char * triggerStr = NULL;
55     cJSON_AddItemToObject (rootObj, OC_RSRVD_TTL, cJSON_CreateNumber(ttl));
56
57     cJSON_AddItemToObject (rootObj, OC_RSRVD_NONCE, cJSON_CreateNumber(nonce));
58
59     triggerStr = convertTriggerEnumToString(trigger);
60     cJSON_AddItemToObject (rootObj, OC_RSRVD_TRIGGER, cJSON_CreateString(triggerStr));
61
62     if(resourceType && resourceType->resourcetypename)
63     {
64         cJSON_AddItemToObject (rootObj, OC_RSRVD_RESOURCE_TYPE,
65                 cJSON_CreateString(resourceType->resourcetypename));
66     }
67
68     jsonEncodedInfo = cJSON_PrintUnformatted (rootObj);
69
70 exit:
71     cJSON_Delete(rootObj);
72
73     return jsonEncodedInfo;
74
75 }
76
77 static OCStackResult BuildPresenceResponse(char *out, uint16_t *remaining,
78         uint32_t ttl, uint32_t nonce, OCPresenceTrigger trigger,
79         OCResourceType *resourceType)
80 {
81     if(!out || !remaining)
82     {
83         return OC_STACK_INVALID_PARAM;
84     }
85
86     OCStackResult ret = OC_STACK_ERROR;
87     char *jsonStr = NULL;
88     uint16_t jsonLen = 0;
89
90     jsonStr = GetJSONStringForPresence(ttl, nonce, trigger, resourceType);
91
92     if(jsonStr)
93     {
94         jsonLen = strlen(jsonStr);
95
96         if (jsonLen < *remaining)
97         {
98             strncpy(out, jsonStr, (jsonLen + 1));
99             *remaining = *remaining - jsonLen;
100             ret = OC_STACK_OK;
101         }
102         else
103         {
104             ret = OC_STACK_ERROR;
105         }
106
107         OCFree(jsonStr);
108     }
109     else
110     {
111         OC_LOG(ERROR, TAG, PCF("Error encoding presence payload."));
112         ret = OC_STACK_ERROR;
113     }
114     return ret;
115 }
116 #endif // WITH_PRESENCE
117 /**
118  * Determine observe QOS based on the QOS of the request.
119  * The qos passed as a parameter overrides what the client requested.
120  * If we want the client preference taking high priority make:
121  *     qos = resourceObserver->qos;
122  *
123  * @param method RESTful method.
124  * @param resourceObserver Observer.
125  * @param appQoS Quality of service.
126  * @return The quality of service of the observer.
127  */
128 static OCQualityOfService DetermineObserverQoS(OCMethod method,
129         ResourceObserver * resourceObserver, OCQualityOfService appQoS)
130 {
131     if(!resourceObserver)
132     {
133         OC_LOG(ERROR, TAG, "DetermineObserverQoS called with invalid resourceObserver");
134         return OC_NA_QOS;
135     }
136
137     OCQualityOfService decidedQoS = appQoS;
138     if(appQoS == OC_NA_QOS)
139     {
140         decidedQoS = resourceObserver->qos;
141     }
142
143     if(appQoS != OC_HIGH_QOS)
144     {
145         OC_LOG_V(INFO, TAG, "Current NON count for this observer is %d",
146                 resourceObserver->lowQosCount);
147         #ifdef WITH_PRESENCE
148         if((resourceObserver->forceHighQos \
149                 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT) \
150                 && method != OC_REST_PRESENCE)
151         #else
152         if(resourceObserver->forceHighQos \
153                 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT)
154         #endif
155         {
156             resourceObserver->lowQosCount = 0;
157             // at some point we have to to send CON to check on the
158             // availability of observer
159             OC_LOG(INFO, TAG, PCF("This time we are sending the  notification as High qos"));
160             decidedQoS = OC_HIGH_QOS;
161         }
162         else
163         {
164             (resourceObserver->lowQosCount)++;
165         }
166     }
167     return decidedQoS;
168 }
169
170 #ifdef WITH_PRESENCE
171 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
172         OCPresenceTrigger trigger, OCResourceType *resourceType, OCQualityOfService qos)
173 #else
174 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
175         OCQualityOfService qos)
176 #endif
177 {
178     OC_LOG(INFO, TAG, PCF("Entering SendObserverNotification"));
179     if(!resPtr)
180     {
181         return OC_STACK_INVALID_PARAM;
182     }
183
184     OCStackResult result = OC_STACK_ERROR;
185     ResourceObserver * resourceObserver = serverObsList;
186     uint8_t numObs = 0;
187     OCServerRequest * request = NULL;
188     OCEntityHandlerRequest ehRequest = {};
189     OCEntityHandlerResult ehResult = OC_EH_ERROR;
190     bool observeErrorFlag = false;
191
192     // Find clients that are observing this resource
193     while (resourceObserver)
194     {
195         if (resourceObserver->resource == resPtr)
196         {
197             numObs++;
198             #ifdef WITH_PRESENCE
199             if(method != OC_REST_PRESENCE)
200             {
201             #endif
202                 qos = DetermineObserverQoS(method, resourceObserver, qos);
203
204                 result = AddServerRequest(&request, 0, 0, 0, 1, OC_REST_GET,
205                         0, resPtr->sequenceNum, qos, resourceObserver->query,
206                         NULL, NULL,
207                         resourceObserver->token, resourceObserver->tokenLength,
208                         resourceObserver->resUri, 0,
209                         &(resourceObserver->addressInfo), resourceObserver->connectivityType);
210
211                 if(request)
212                 {
213                     request->observeResult = OC_STACK_OK;
214                     if(result == OC_STACK_OK)
215                     {
216                         result = FormOCEntityHandlerRequest(&ehRequest, (OCRequestHandle) request,
217                                     request->method, (OCResourceHandle) resPtr, request->query,
218                                     request->reqJSONPayload,
219                                     request->numRcvdVendorSpecificHeaderOptions,
220                                     request->rcvdVendorSpecificHeaderOptions,
221                                     OC_OBSERVE_NO_OPTION, 0);
222                         if(result == OC_STACK_OK)
223                         {
224                             ehResult = resPtr->entityHandler(OC_REQUEST_FLAG, &ehRequest);
225                             if(ehResult == OC_EH_ERROR)
226                             {
227                                 FindAndDeleteServerRequest(request);
228                             }
229                         }
230                     }
231                 }
232             #ifdef WITH_PRESENCE
233             }
234             else
235             {
236                 OCEntityHandlerResponse ehResponse = {};
237                 char presenceResBuf[MAX_RESPONSE_LENGTH] = {};
238
239                 //This is effectively the implementation for the presence entity handler.
240                 OC_LOG(DEBUG, TAG, PCF("This notification is for Presence"));
241
242                 result = AddServerRequest(&request, 0, 0, 0, 1, OC_REST_GET,
243                         0, resPtr->sequenceNum, qos, resourceObserver->query,
244                         NULL, NULL,
245                         resourceObserver->token, resourceObserver->tokenLength,
246                         resourceObserver->resUri, 0,
247                         &(resourceObserver->addressInfo), resourceObserver->connectivityType);
248
249                 if(result == OC_STACK_OK)
250                 {
251                     uint16_t remaining = MAX_RESPONSE_LENGTH;
252                     // create the payload here
253                     result = BuildPresenceResponse(presenceResBuf, &remaining,
254                             maxAge, resPtr->sequenceNum, trigger,
255                             resourceType);
256
257                     if(result == OC_STACK_OK && remaining < MAX_RESPONSE_LENGTH)
258                     {
259                         ehResponse.ehResult = OC_EH_OK;
260                         ehResponse.payload = presenceResBuf;
261                         ehResponse.payloadSize = strlen((const char *)presenceResBuf) + 1;
262                         ehResponse.persistentBufferFlag = 0;
263                         ehResponse.requestHandle = (OCRequestHandle) request;
264                         ehResponse.resourceHandle = (OCResourceHandle) resPtr;
265                         strcpy((char *)ehResponse.resourceUri,
266                                 (const char *)resourceObserver->resUri);
267                         result = OCDoResponse(&ehResponse);
268                     }
269                 }
270             }
271             #endif
272
273             // Since we are in a loop, set an error flag to indicate at least one error occurred.
274             if (result != OC_STACK_OK)
275             {
276                 observeErrorFlag = true;
277             }
278         }
279         resourceObserver = resourceObserver->next;
280     }
281
282     if (numObs == 0)
283     {
284         OC_LOG(INFO, TAG, PCF("Resource has no observers"));
285         result = OC_STACK_NO_OBSERVERS;
286     }
287     else if (observeErrorFlag)
288     {
289         OC_LOG(ERROR, TAG, PCF("Observer notification error"));
290         result = OC_STACK_ERROR;
291     }
292     return result;
293 }
294
295 OCStackResult SendListObserverNotification (OCResource * resource,
296         OCObservationId  *obsIdList, uint8_t numberOfIds,
297         const char *notificationJSONPayload, uint32_t maxAge,
298         OCQualityOfService qos)
299 {
300     if(!resource || !obsIdList || !notificationJSONPayload)
301     {
302         return OC_STACK_INVALID_PARAM;
303     }
304
305     uint8_t numIds = numberOfIds;
306     ResourceObserver *observer = NULL;
307     uint8_t numSentNotification = 0;
308     OCServerRequest * request = NULL;
309     OCStackResult result = OC_STACK_ERROR;
310     bool observeErrorFlag = false;
311
312     OC_LOG(INFO, TAG, PCF("Entering SendListObserverNotification"));
313     while(numIds)
314     {
315         observer = GetObserverUsingId (*obsIdList);
316         if(observer)
317         {
318             // Found observer - verify if it matches the resource handle
319             if (observer->resource == resource)
320             {
321                 qos = DetermineObserverQoS(OC_REST_GET, observer, qos);
322
323
324                 result = AddServerRequest(&request, 0, 0, 0, 1, OC_REST_GET,
325                         0, resource->sequenceNum, qos, observer->query,
326                         NULL, NULL, observer->token, observer->tokenLength,
327                         observer->resUri, 0,
328                         &(observer->addressInfo), observer->connectivityType);
329
330                 if(request)
331                 {
332                     request->observeResult = OC_STACK_OK;
333                     if(result == OC_STACK_OK)
334                     {
335                         OCEntityHandlerResponse ehResponse = {};
336                         ehResponse.ehResult = OC_EH_OK;
337                         ehResponse.payload = (char *) OCMalloc(MAX_RESPONSE_LENGTH + 1);
338                         if(!ehResponse.payload)
339                         {
340                             FindAndDeleteServerRequest(request);
341                             continue;
342                         }
343                         strncpy(ehResponse.payload, notificationJSONPayload, MAX_RESPONSE_LENGTH-1);
344                         ehResponse.payload[MAX_RESPONSE_LENGTH] = '\0';
345                         ehResponse.payloadSize = strlen(ehResponse.payload) + 1;
346                         ehResponse.persistentBufferFlag = 0;
347                         ehResponse.requestHandle = (OCRequestHandle) request;
348                         ehResponse.resourceHandle = (OCResourceHandle) resource;
349                         result = OCDoResponse(&ehResponse);
350                         if(result == OC_STACK_OK)
351                         {
352                             OC_LOG_V(INFO, TAG, "Observer id %d notified.", *obsIdList);
353
354                             // Increment only if OCDoResponse is successful
355                             numSentNotification++;
356
357                             OCFree(ehResponse.payload);
358                             FindAndDeleteServerRequest(request);
359                         }
360                         else
361                         {
362                             OC_LOG_V(INFO, TAG, "Error notifying observer id %d.", *obsIdList);
363                         }
364                     }
365                     else
366                     {
367                         FindAndDeleteServerRequest(request);
368                     }
369                 }
370                 // Since we are in a loop, set an error flag to indicate
371                 // at least one error occurred.
372                 if (result != OC_STACK_OK)
373                 {
374                     observeErrorFlag = true;
375                 }
376             }
377         }
378         obsIdList++;
379         numIds--;
380     }
381
382     if(numSentNotification == numberOfIds && !observeErrorFlag)
383     {
384         return OC_STACK_OK;
385     }
386     else if(numSentNotification == 0)
387     {
388         return OC_STACK_NO_OBSERVERS;
389     }
390     else
391     {
392         OC_LOG(ERROR, TAG, PCF("Observer notification error"));
393         return OC_STACK_ERROR;
394     }
395 }
396
397 OCStackResult GenerateObserverId (OCObservationId *observationId)
398 {
399     ResourceObserver *resObs = NULL;
400
401     OC_LOG(INFO, TAG, PCF("Entering GenerateObserverId"));
402     VERIFY_NON_NULL (observationId);
403
404     do
405     {
406         *observationId = OCGetRandomByte();
407         // Check if observation Id already exists
408         resObs = GetObserverUsingId (*observationId);
409     } while (NULL != resObs);
410
411     OC_LOG_V(INFO, TAG, "Generated bservation ID is %u", *observationId);
412
413     return OC_STACK_OK;
414 exit:
415     return OC_STACK_ERROR;
416 }
417
418 OCStackResult AddObserver (const char         *resUri,
419                            const char         *query,
420                            OCObservationId    obsId,
421                            CAToken_t          token,
422                            uint8_t            tokenLength,
423                            OCResource         *resHandle,
424                            OCQualityOfService qos,
425                            const CAAddress_t  *addressInfo,
426                            CATransportType_t connectivityType)
427 {
428     // Check if resource exists and is observable.
429     if (!resHandle)
430     {
431         return OC_STACK_INVALID_PARAM;
432     }
433     if (!(resHandle->resourceProperties & OC_OBSERVABLE))
434     {
435         return OC_STACK_RESOURCE_ERROR;
436     }
437     ResourceObserver *obsNode = NULL;
438
439     if(!resUri || !token || !*token)
440     {
441         return OC_STACK_INVALID_PARAM;
442     }
443
444     obsNode = (ResourceObserver *) OCCalloc(1, sizeof(ResourceObserver));
445     if (obsNode)
446     {
447         obsNode->observeId = obsId;
448
449         obsNode->resUri = (char *)OCMalloc(strlen(resUri)+1);
450         VERIFY_NON_NULL (obsNode->resUri);
451         memcpy (obsNode->resUri, resUri, strlen(resUri)+1);
452
453         obsNode->qos = qos;
454         if(query)
455         {
456             obsNode->query = (char *)OCMalloc(strlen(query)+1);
457             VERIFY_NON_NULL (obsNode->query);
458             memcpy (obsNode->query, query, strlen(query)+1);
459         }
460         // If tokenLength is zero, the return value depends on the
461         // particular library implementation (it may or may not be a null pointer).
462         if(tokenLength)
463         {
464             obsNode->token = (CAToken_t)OCMalloc(tokenLength);
465             VERIFY_NON_NULL (obsNode->token);
466             memcpy(obsNode->token, token, tokenLength);
467         }
468         obsNode->tokenLength = tokenLength;
469         obsNode->addressInfo = *addressInfo;
470         obsNode->connectivityType = connectivityType;
471         obsNode->resource = resHandle;
472         LL_APPEND (serverObsList, obsNode);
473         return OC_STACK_OK;
474     }
475
476 exit:
477     if (obsNode)
478     {
479         OCFree(obsNode->resUri);
480         OCFree(obsNode->query);
481         OCFree(obsNode);
482     }
483     return OC_STACK_NO_MEMORY;
484 }
485
486 ResourceObserver* GetObserverUsingId (const OCObservationId observeId)
487 {
488     ResourceObserver *out = NULL;
489
490     if (observeId)
491     {
492         LL_FOREACH (serverObsList, out)
493         {
494             if (out->observeId == observeId)
495             {
496                 return out;
497             }
498         }
499     }
500     OC_LOG(INFO, TAG, PCF("Observer node not found!!"));
501     return NULL;
502 }
503
504 ResourceObserver* GetObserverUsingToken (const CAToken_t token, uint8_t tokenLength)
505 {
506     ResourceObserver *out = NULL;
507
508     if(token && *token)
509     {
510         LL_FOREACH (serverObsList, out)
511         {
512             OC_LOG(INFO, TAG,PCF("comparing tokens"));
513             OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)token, tokenLength);
514             OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)out->token, tokenLength);
515             if((memcmp(out->token, token, tokenLength) == 0))
516             {
517                 return out;
518             }
519         }
520     }
521     OC_LOG(INFO, TAG, PCF("Observer node not found!!"));
522     return NULL;
523 }
524
525 OCStackResult DeleteObserverUsingToken (CAToken_t token, uint8_t tokenLength)
526 {
527     if(!token || !*token)
528     {
529         return OC_STACK_INVALID_PARAM;
530     }
531
532     ResourceObserver *obsNode = NULL;
533
534     obsNode = GetObserverUsingToken (token, tokenLength);
535     if (obsNode)
536     {
537         OC_LOG_V(INFO, TAG, PCF("deleting tokens"));
538         OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)obsNode->token, tokenLength);
539         LL_DELETE (serverObsList, obsNode);
540         OCFree(obsNode->resUri);
541         OCFree(obsNode->query);
542         OCFree(obsNode->token);
543         OCFree(obsNode);
544     }
545     // it is ok if we did not find the observer...
546     return OC_STACK_OK;
547 }
548
549 void DeleteObserverList()
550 {
551     ResourceObserver *out = NULL;
552     ResourceObserver *tmp = NULL;
553     LL_FOREACH_SAFE (serverObsList, out, tmp)
554     {
555         if(out)
556         {
557             DeleteObserverUsingToken ((out->token), out->tokenLength);
558         }
559     }
560     serverObsList = NULL;
561 }
562
563 /*
564  * CA layer expects observe registration/de-reg/notiifcations to be passed as a header
565  * option, which breaks the protocol abstraction requirement between RI & CA, and
566  * has to be fixed in the future. The function below adds the header option for observe.
567  * It should be noted that the observe header option is assumed to be the first option
568  * in the list of user defined header options and hence it is inserted at the front
569  * of the header options list and number of options adjusted accordingly.
570  */
571 OCStackResult
572 CreateObserveHeaderOption (CAHeaderOption_t **caHdrOpt,
573                            OCHeaderOption *ocHdrOpt,
574                            uint8_t numOptions,
575                            uint8_t observeFlag)
576 {
577     if(!caHdrOpt)
578     {
579         return OC_STACK_INVALID_PARAM;
580     }
581
582     CAHeaderOption_t *tmpHdrOpt = NULL;
583
584     tmpHdrOpt = (CAHeaderOption_t *) OCCalloc ((numOptions+1), sizeof(CAHeaderOption_t));
585     if (NULL == tmpHdrOpt)
586     {
587         return OC_STACK_NO_MEMORY;
588     }
589     tmpHdrOpt[0].protocolID = CA_COAP_ID;
590     tmpHdrOpt[0].optionID = COAP_OPTION_OBSERVE;
591     tmpHdrOpt[0].optionLength = sizeof(uint32_t);
592     tmpHdrOpt[0].optionData[0] = observeFlag;
593     for (uint8_t i = 0; i < numOptions; i++)
594     {
595         memcpy (&(tmpHdrOpt[i+1]), &(ocHdrOpt[i]), sizeof(CAHeaderOption_t));
596     }
597
598     *caHdrOpt = tmpHdrOpt;
599     return OC_STACK_OK;
600 }
601
602 /*
603  * CA layer passes observe information to the RI layer as a header option, which
604  * breaks the protocol abstraction requirement between RI & CA, and has to be fixed
605  * in the future. The function below removes the observe header option and processes it.
606  * It should be noted that the observe header option is always assumed to be the first
607  * option in the list of user defined header options and hence it is deleted from the
608  * front of the header options list and the number of options is adjusted accordingly.
609  */
610 OCStackResult
611 GetObserveHeaderOption (uint32_t * observationOption,
612                         CAHeaderOption_t *options,
613                         uint8_t * numOptions)
614 {
615     if(!observationOption)
616     {
617         return OC_STACK_INVALID_PARAM;
618     }
619     *observationOption = OC_OBSERVE_NO_OPTION;
620
621     if(!options || !numOptions)
622     {
623         return OC_STACK_INVALID_PARAM;
624     }
625
626     for(uint8_t i = 0; i < *numOptions; i++)
627     {
628         if(options[i].protocolID == CA_COAP_ID &&
629                 options[i].optionID == COAP_OPTION_OBSERVE)
630         {
631             *observationOption = options[i].optionData[0];
632             for(uint8_t c = i; c < *numOptions-1; c++)
633             {
634                 options[i].protocolID = options[i+1].protocolID;
635                 options[i].optionID = options[i+1].optionID;
636                 options[i].optionLength = options[i+1].optionLength;
637                 memcpy(options[i].optionData, options[i+1].optionData, options[i+1].optionLength);
638             }
639             (*numOptions)--;
640             return OC_STACK_OK;
641         }
642     }
643     return OC_STACK_OK;
644 }
645