1 //******************************************************************
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
23 #include "ocstackconfig.h"
24 #include "ocstackinternal.h"
25 #include "ocobserve.h"
26 #include "ocresourcehandler.h"
29 #include "ocserverrequest.h"
36 #define MOD_NAME PCF("ocobserve")
38 #define TAG PCF("OCStackObserve")
40 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
42 static struct ResourceObserver * serverObsList = NULL;
44 static char* GetJSONStringForPresence(uint32_t ttl, uint32_t nonce,
45 OCPresenceTrigger trigger, OCResourceType *resourceType)
47 char *jsonEncodedInfo = NULL;
48 const char * triggerStr = NULL;
50 cJSON *rootObj = cJSON_CreateObject();
51 VERIFY_NON_NULL (rootObj);
53 cJSON_AddItemToObject (rootObj, OC_RSRVD_TTL, cJSON_CreateNumber(ttl));
55 cJSON_AddItemToObject (rootObj, OC_RSRVD_NONCE, cJSON_CreateNumber(nonce));
57 triggerStr = convertTriggerEnumToString(trigger);
58 cJSON_AddItemToObject (rootObj, OC_RSRVD_TRIGGER, cJSON_CreateString(triggerStr));
60 if(resourceType && resourceType->resourcetypename)
62 cJSON_AddItemToObject (rootObj, OC_RSRVD_RESOURCE_TYPE,
63 cJSON_CreateString(resourceType->resourcetypename));
66 jsonEncodedInfo = cJSON_PrintUnformatted (rootObj);
69 cJSON_Delete(rootObj);
71 return jsonEncodedInfo;
75 static OCStackResult BuildPresenceResponse(char *out, uint16_t *remaining,
76 uint32_t ttl, uint32_t nonce, OCPresenceTrigger trigger,
77 OCResourceType *resourceType)
79 if(!out || !remaining)
81 return OC_STACK_INVALID_PARAM;
84 OCStackResult ret = OC_STACK_ERROR;
88 jsonStr = GetJSONStringForPresence(ttl, nonce, trigger, resourceType);
92 jsonLen = strlen(jsonStr);
94 if (jsonLen < *remaining)
96 strncpy(out, jsonStr, (jsonLen + 1));
97 *remaining = *remaining - jsonLen;
102 ret = OC_STACK_ERROR;
109 OC_LOG(ERROR, TAG, PCF("Error encoding presence payload."));
110 ret = OC_STACK_ERROR;
114 #endif // WITH_PRESENCE
116 * Determine observe QOS based on the QOS of the request.
117 * The qos passed as a parameter overrides what the client requested.
118 * If we want the client preference taking high priority make:
119 * qos = resourceObserver->qos;
121 * @param method RESTful method.
122 * @param resourceObserver Observer.
123 * @param appQoS Quality of service.
124 * @return The quality of service of the observer.
126 static OCQualityOfService DetermineObserverQoS(OCMethod method,
127 ResourceObserver * resourceObserver, OCQualityOfService appQoS)
129 if(!resourceObserver)
131 OC_LOG(ERROR, TAG, "DetermineObserverQoS called with invalid resourceObserver");
135 OCQualityOfService decidedQoS = appQoS;
136 if(appQoS == OC_NA_QOS)
138 decidedQoS = resourceObserver->qos;
141 if(appQoS != OC_HIGH_QOS)
143 OC_LOG_V(INFO, TAG, "Current NON count for this observer is %d",
144 resourceObserver->lowQosCount);
146 if((resourceObserver->forceHighQos \
147 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT) \
148 && method != OC_REST_PRESENCE)
150 if(resourceObserver->forceHighQos \
151 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT)
154 resourceObserver->lowQosCount = 0;
155 // at some point we have to to send CON to check on the
156 // availability of observer
157 OC_LOG(INFO, TAG, PCF("This time we are sending the notification as High qos"));
158 decidedQoS = OC_HIGH_QOS;
162 (resourceObserver->lowQosCount)++;
169 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
170 OCPresenceTrigger trigger, OCResourceType *resourceType, OCQualityOfService qos)
172 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
173 OCQualityOfService qos)
176 OC_LOG(INFO, TAG, PCF("Entering SendObserverNotification"));
179 return OC_STACK_INVALID_PARAM;
182 OCStackResult result = OC_STACK_ERROR;
183 ResourceObserver * resourceObserver = serverObsList;
185 OCServerRequest * request = NULL;
186 OCEntityHandlerRequest ehRequest = {};
187 OCEntityHandlerResult ehResult = OC_EH_ERROR;
188 bool observeErrorFlag = false;
190 // Find clients that are observing this resource
191 while (resourceObserver)
193 if (resourceObserver->resource == resPtr)
197 if(method != OC_REST_PRESENCE)
200 qos = DetermineObserverQoS(method, resourceObserver, qos);
202 result = AddServerRequest(&request, 0, 0, 0, 1, OC_REST_GET,
203 0, resPtr->sequenceNum, qos, resourceObserver->query,
205 resourceObserver->token, resourceObserver->tokenLength,
206 resourceObserver->resUri, 0,
207 &(resourceObserver->addressInfo), resourceObserver->connectivityType);
211 request->observeResult = OC_STACK_OK;
212 if(result == OC_STACK_OK)
214 result = FormOCEntityHandlerRequest(&ehRequest, (OCRequestHandle) request,
215 request->method, (OCResourceHandle) resPtr, request->query,
216 request->reqJSONPayload,
217 request->numRcvdVendorSpecificHeaderOptions,
218 request->rcvdVendorSpecificHeaderOptions,
219 OC_OBSERVE_NO_OPTION, 0);
220 if(result == OC_STACK_OK)
222 ehResult = resPtr->entityHandler(OC_REQUEST_FLAG, &ehRequest);
223 if(ehResult == OC_EH_ERROR)
225 FindAndDeleteServerRequest(request);
234 OCEntityHandlerResponse ehResponse = {};
235 char presenceResBuf[MAX_RESPONSE_LENGTH] = {};
237 //This is effectively the implementation for the presence entity handler.
238 OC_LOG(DEBUG, TAG, PCF("This notification is for Presence"));
240 result = AddServerRequest(&request, 0, 0, 0, 1, OC_REST_GET,
241 0, resPtr->sequenceNum, qos, resourceObserver->query,
243 resourceObserver->token, resourceObserver->tokenLength,
244 resourceObserver->resUri, 0,
245 &(resourceObserver->addressInfo), resourceObserver->connectivityType);
247 if(result == OC_STACK_OK)
249 uint16_t remaining = MAX_RESPONSE_LENGTH;
250 // create the payload here
251 result = BuildPresenceResponse(presenceResBuf, &remaining,
252 maxAge, resPtr->sequenceNum, trigger,
255 if(result == OC_STACK_OK && remaining < MAX_RESPONSE_LENGTH)
257 ehResponse.ehResult = OC_EH_OK;
258 ehResponse.payload = presenceResBuf;
259 ehResponse.payloadSize = strlen((const char *)presenceResBuf) + 1;
260 ehResponse.persistentBufferFlag = 0;
261 ehResponse.requestHandle = (OCRequestHandle) request;
262 ehResponse.resourceHandle = (OCResourceHandle) resPtr;
263 strcpy((char *)ehResponse.resourceUri,
264 (const char *)resourceObserver->resUri);
265 result = OCDoResponse(&ehResponse);
271 // Since we are in a loop, set an error flag to indicate at least one error occurred.
272 if (result != OC_STACK_OK)
274 observeErrorFlag = true;
277 resourceObserver = resourceObserver->next;
282 OC_LOG(INFO, TAG, PCF("Resource has no observers"));
283 result = OC_STACK_NO_OBSERVERS;
285 else if (observeErrorFlag)
287 OC_LOG(ERROR, TAG, PCF("Observer notification error"));
288 result = OC_STACK_ERROR;
293 OCStackResult SendListObserverNotification (OCResource * resource,
294 OCObservationId *obsIdList, uint8_t numberOfIds,
295 const char *notificationJSONPayload, uint32_t maxAge,
296 OCQualityOfService qos)
298 if(!resource || !obsIdList || !notificationJSONPayload)
300 return OC_STACK_INVALID_PARAM;
303 uint8_t numIds = numberOfIds;
304 ResourceObserver *observer = NULL;
305 uint8_t numSentNotification = 0;
306 OCServerRequest * request = NULL;
307 OCStackResult result = OC_STACK_ERROR;
308 bool observeErrorFlag = false;
310 OC_LOG(INFO, TAG, PCF("Entering SendListObserverNotification"));
313 observer = GetObserverUsingId (*obsIdList);
316 // Found observer - verify if it matches the resource handle
317 if (observer->resource == resource)
319 qos = DetermineObserverQoS(OC_REST_GET, observer, qos);
322 result = AddServerRequest(&request, 0, 0, 0, 1, OC_REST_GET,
323 0, resource->sequenceNum, qos, observer->query,
324 NULL, NULL, observer->token, observer->tokenLength,
326 &(observer->addressInfo), observer->connectivityType);
330 request->observeResult = OC_STACK_OK;
331 if(result == OC_STACK_OK)
333 OCEntityHandlerResponse ehResponse = {};
334 ehResponse.ehResult = OC_EH_OK;
335 ehResponse.payload = (char *) OCMalloc(MAX_RESPONSE_LENGTH + 1);
336 if(!ehResponse.payload)
338 FindAndDeleteServerRequest(request);
341 strncpy(ehResponse.payload, notificationJSONPayload, MAX_RESPONSE_LENGTH-1);
342 ehResponse.payload[MAX_RESPONSE_LENGTH] = '\0';
343 ehResponse.payloadSize = strlen(ehResponse.payload) + 1;
344 ehResponse.persistentBufferFlag = 0;
345 ehResponse.requestHandle = (OCRequestHandle) request;
346 ehResponse.resourceHandle = (OCResourceHandle) resource;
347 result = OCDoResponse(&ehResponse);
348 if(result == OC_STACK_OK)
350 OC_LOG_V(INFO, TAG, "Observer id %d notified.", *obsIdList);
352 // Increment only if OCDoResponse is successful
353 numSentNotification++;
355 OCFree(ehResponse.payload);
356 FindAndDeleteServerRequest(request);
360 OC_LOG_V(INFO, TAG, "Error notifying observer id %d.", *obsIdList);
365 FindAndDeleteServerRequest(request);
368 // Since we are in a loop, set an error flag to indicate
369 // at least one error occurred.
370 if (result != OC_STACK_OK)
372 observeErrorFlag = true;
380 if(numSentNotification == numberOfIds && !observeErrorFlag)
384 else if(numSentNotification == 0)
386 return OC_STACK_NO_OBSERVERS;
390 OC_LOG(ERROR, TAG, PCF("Observer notification error"));
391 return OC_STACK_ERROR;
395 OCStackResult GenerateObserverId (OCObservationId *observationId)
397 ResourceObserver *resObs = NULL;
399 OC_LOG(INFO, TAG, PCF("Entering GenerateObserverId"));
400 VERIFY_NON_NULL (observationId);
404 *observationId = OCGetRandomByte();
405 // Check if observation Id already exists
406 resObs = GetObserverUsingId (*observationId);
407 } while (NULL != resObs);
409 OC_LOG_V(INFO, TAG, "Generated bservation ID is %u", *observationId);
413 return OC_STACK_ERROR;
416 OCStackResult AddObserver (const char *resUri,
418 OCObservationId obsId,
421 OCResource *resHandle,
422 OCQualityOfService qos,
423 const CAAddress_t *addressInfo,
424 CATransportType_t connectivityType)
426 // Check if resource exists and is observable.
429 return OC_STACK_INVALID_PARAM;
431 if (!(resHandle->resourceProperties & OC_OBSERVABLE))
433 return OC_STACK_RESOURCE_ERROR;
435 ResourceObserver *obsNode = NULL;
437 if(!resUri || !token || !*token)
439 return OC_STACK_INVALID_PARAM;
442 obsNode = (ResourceObserver *) OCCalloc(1, sizeof(ResourceObserver));
445 obsNode->observeId = obsId;
447 obsNode->resUri = (char *)OCMalloc(strlen(resUri)+1);
448 VERIFY_NON_NULL (obsNode->resUri);
449 memcpy (obsNode->resUri, resUri, strlen(resUri)+1);
454 obsNode->query = (char *)OCMalloc(strlen(query)+1);
455 VERIFY_NON_NULL (obsNode->query);
456 memcpy (obsNode->query, query, strlen(query)+1);
458 // If tokenLength is zero, the return value depends on the
459 // particular library implementation (it may or may not be a null pointer).
462 obsNode->token = (CAToken_t)OCMalloc(tokenLength);
463 VERIFY_NON_NULL (obsNode->token);
464 memcpy(obsNode->token, token, tokenLength);
466 obsNode->tokenLength = tokenLength;
467 obsNode->addressInfo = *addressInfo;
468 obsNode->connectivityType = connectivityType;
469 obsNode->resource = resHandle;
470 LL_APPEND (serverObsList, obsNode);
477 OCFree(obsNode->resUri);
478 OCFree(obsNode->query);
481 return OC_STACK_NO_MEMORY;
484 ResourceObserver* GetObserverUsingId (const OCObservationId observeId)
486 ResourceObserver *out = NULL;
490 LL_FOREACH (serverObsList, out)
492 if (out->observeId == observeId)
498 OC_LOG(INFO, TAG, PCF("Observer node not found!!"));
502 ResourceObserver* GetObserverUsingToken (const CAToken_t token, uint8_t tokenLength)
504 ResourceObserver *out = NULL;
508 LL_FOREACH (serverObsList, out)
510 OC_LOG(INFO, TAG,PCF("comparing tokens"));
511 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)token, tokenLength);
512 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)out->token, tokenLength);
513 if((memcmp(out->token, token, tokenLength) == 0))
519 OC_LOG(INFO, TAG, PCF("Observer node not found!!"));
523 OCStackResult DeleteObserverUsingToken (CAToken_t token, uint8_t tokenLength)
525 if(!token || !*token)
527 return OC_STACK_INVALID_PARAM;
530 ResourceObserver *obsNode = NULL;
532 obsNode = GetObserverUsingToken (token, tokenLength);
535 OC_LOG_V(INFO, TAG, PCF("deleting tokens"));
536 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)obsNode->token, tokenLength);
537 LL_DELETE (serverObsList, obsNode);
538 OCFree(obsNode->resUri);
539 OCFree(obsNode->query);
540 OCFree(obsNode->token);
543 // it is ok if we did not find the observer...
547 void DeleteObserverList()
549 ResourceObserver *out = NULL;
550 ResourceObserver *tmp = NULL;
551 LL_FOREACH_SAFE (serverObsList, out, tmp)
555 DeleteObserverUsingToken ((out->token), out->tokenLength);
558 serverObsList = NULL;
562 * CA layer expects observe registration/de-reg/notiifcations to be passed as a header
563 * option, which breaks the protocol abstraction requirement between RI & CA, and
564 * has to be fixed in the future. The function below adds the header option for observe.
565 * It should be noted that the observe header option is assumed to be the first option
566 * in the list of user defined header options and hence it is inserted at the front
567 * of the header options list and number of options adjusted accordingly.
570 CreateObserveHeaderOption (CAHeaderOption_t **caHdrOpt,
571 OCHeaderOption *ocHdrOpt,
577 return OC_STACK_INVALID_PARAM;
580 CAHeaderOption_t *tmpHdrOpt = NULL;
582 tmpHdrOpt = (CAHeaderOption_t *) OCCalloc ((numOptions+1), sizeof(CAHeaderOption_t));
583 if (NULL == tmpHdrOpt)
585 return OC_STACK_NO_MEMORY;
587 tmpHdrOpt[0].protocolID = CA_COAP_ID;
588 tmpHdrOpt[0].optionID = COAP_OPTION_OBSERVE;
589 tmpHdrOpt[0].optionLength = sizeof(uint32_t);
590 tmpHdrOpt[0].optionData[0] = observeFlag;
591 for (uint8_t i = 0; i < numOptions; i++)
593 memcpy (&(tmpHdrOpt[i+1]), &(ocHdrOpt[i]), sizeof(CAHeaderOption_t));
596 *caHdrOpt = tmpHdrOpt;
601 * CA layer passes observe information to the RI layer as a header option, which
602 * breaks the protocol abstraction requirement between RI & CA, and has to be fixed
603 * in the future. The function below removes the observe header option and processes it.
604 * It should be noted that the observe header option is always assumed to be the first
605 * option in the list of user defined header options and hence it is deleted from the
606 * front of the header options list and the number of options is adjusted accordingly.
609 GetObserveHeaderOption (uint32_t * observationOption,
610 CAHeaderOption_t *options,
611 uint8_t * numOptions)
613 if(!observationOption)
615 return OC_STACK_INVALID_PARAM;
617 *observationOption = OC_OBSERVE_NO_OPTION;
619 if(!options || !numOptions)
621 return OC_STACK_INVALID_PARAM;
624 for(uint8_t i = 0; i < *numOptions; i++)
626 if(options[i].protocolID == CA_COAP_ID &&
627 options[i].optionID == COAP_OPTION_OBSERVE)
629 *observationOption = options[i].optionData[0];
630 for(uint8_t c = i; c < *numOptions-1; c++)
632 options[i].protocolID = options[i+1].protocolID;
633 options[i].optionID = options[i+1].optionID;
634 options[i].optionLength = options[i+1].optionLength;
635 memcpy(options[i].optionData, options[i+1].optionData, options[i+1].optionLength);