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"
28 #include "oic_malloc.h"
29 #include "oic_string.h"
30 #include "ocserverrequest.h"
38 #define MOD_NAME PCF("ocobserve")
40 #define TAG PCF("OCStackObserve")
42 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
44 static struct ResourceObserver * serverObsList = NULL;
46 static char* GetJSONStringForPresence(uint32_t ttl, uint32_t nonce,
47 OCPresenceTrigger trigger, OCResourceType *resourceType)
49 char *jsonEncodedInfo = NULL;
50 const char * triggerStr = NULL;
52 cJSON *rootObj = cJSON_CreateObject();
53 VERIFY_NON_NULL (rootObj);
55 cJSON_AddItemToObject (rootObj, OC_RSRVD_TTL, cJSON_CreateNumber(ttl));
57 cJSON_AddItemToObject (rootObj, OC_RSRVD_NONCE, cJSON_CreateNumber(nonce));
59 triggerStr = convertTriggerEnumToString(trigger);
60 cJSON_AddItemToObject (rootObj, OC_RSRVD_TRIGGER, cJSON_CreateString(triggerStr));
62 if(resourceType && resourceType->resourcetypename)
64 cJSON_AddItemToObject (rootObj, OC_RSRVD_RESOURCE_TYPE,
65 cJSON_CreateString(resourceType->resourcetypename));
68 jsonEncodedInfo = cJSON_PrintUnformatted (rootObj);
71 cJSON_Delete(rootObj);
73 return jsonEncodedInfo;
77 static OCStackResult BuildPresenceResponse(char *out, uint16_t *remaining,
78 uint32_t ttl, uint32_t nonce, OCPresenceTrigger trigger,
79 OCResourceType *resourceType)
81 if(!out || !remaining)
83 return OC_STACK_INVALID_PARAM;
86 OCStackResult ret = OC_STACK_ERROR;
90 jsonStr = GetJSONStringForPresence(ttl, nonce, trigger, resourceType);
94 jsonLen = strlen(jsonStr);
96 if (jsonLen < *remaining)
98 OICStrcpy(out, *remaining, jsonStr);
99 *remaining = *remaining - jsonLen;
104 ret = OC_STACK_ERROR;
111 OC_LOG(ERROR, TAG, PCF("Error encoding presence payload."));
112 ret = OC_STACK_ERROR;
116 #endif // WITH_PRESENCE
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;
123 * @param method RESTful method.
124 * @param resourceObserver Observer.
125 * @param appQoS Quality of service.
126 * @return The quality of service of the observer.
128 static OCQualityOfService DetermineObserverQoS(OCMethod method,
129 ResourceObserver * resourceObserver, OCQualityOfService appQoS)
131 if(!resourceObserver)
133 OC_LOG(ERROR, TAG, "DetermineObserverQoS called with invalid resourceObserver");
137 OCQualityOfService decidedQoS = appQoS;
138 if(appQoS == OC_NA_QOS)
140 decidedQoS = resourceObserver->qos;
143 if(appQoS != OC_HIGH_QOS)
145 OC_LOG_V(INFO, TAG, "Current NON count for this observer is %d",
146 resourceObserver->lowQosCount);
148 if((resourceObserver->forceHighQos \
149 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT) \
150 && method != OC_REST_PRESENCE)
152 if(resourceObserver->forceHighQos \
153 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT)
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;
164 (resourceObserver->lowQosCount)++;
171 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
172 OCPresenceTrigger trigger, OCResourceType *resourceType, OCQualityOfService qos)
174 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
175 OCQualityOfService qos)
178 OC_LOG(INFO, TAG, PCF("Entering SendObserverNotification"));
181 return OC_STACK_INVALID_PARAM;
184 OCStackResult result = OC_STACK_ERROR;
185 ResourceObserver * resourceObserver = serverObsList;
187 OCServerRequest * request = NULL;
188 OCEntityHandlerRequest ehRequest = {};
189 OCEntityHandlerResult ehResult = OC_EH_ERROR;
190 bool observeErrorFlag = false;
192 // Find clients that are observing this resource
193 while (resourceObserver)
195 if (resourceObserver->resource == resPtr)
199 if(method != OC_REST_PRESENCE)
202 qos = DetermineObserverQoS(method, resourceObserver, qos);
204 result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
205 0, resPtr->sequenceNum, qos, resourceObserver->query,
207 resourceObserver->token, resourceObserver->tokenLength,
208 resourceObserver->resUri, 0,
209 &resourceObserver->devAddr);
213 request->observeResult = OC_STACK_OK;
214 if(result == OC_STACK_OK)
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)
224 ehResult = resPtr->entityHandler(OC_REQUEST_FLAG, &ehRequest,
225 resPtr->entityHandlerCallbackParam);
226 if(ehResult == OC_EH_ERROR)
228 FindAndDeleteServerRequest(request);
237 OCEntityHandlerResponse ehResponse = {};
238 char presenceResBuf[MAX_RESPONSE_LENGTH] = {};
240 //This is effectively the implementation for the presence entity handler.
241 OC_LOG(DEBUG, TAG, PCF("This notification is for Presence"));
242 result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
243 0, resPtr->sequenceNum, qos, resourceObserver->query,
245 resourceObserver->token, resourceObserver->tokenLength,
246 resourceObserver->resUri, 0,
247 &resourceObserver->devAddr);
249 if(result == OC_STACK_OK)
251 uint16_t remaining = MAX_RESPONSE_LENGTH;
252 // create the payload here
253 result = BuildPresenceResponse(presenceResBuf, &remaining,
254 maxAge, resPtr->sequenceNum, trigger,
257 if(result == OC_STACK_OK && remaining < MAX_RESPONSE_LENGTH)
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 OICStrcpy(ehResponse.resourceUri, sizeof(ehResponse.resourceUri),
266 resourceObserver->resUri);
267 result = OCDoResponse(&ehResponse);
273 // Since we are in a loop, set an error flag to indicate at least one error occurred.
274 if (result != OC_STACK_OK)
276 observeErrorFlag = true;
279 resourceObserver = resourceObserver->next;
284 OC_LOG(INFO, TAG, PCF("Resource has no observers"));
285 result = OC_STACK_NO_OBSERVERS;
287 else if (observeErrorFlag)
289 OC_LOG(ERROR, TAG, PCF("Observer notification error"));
290 result = OC_STACK_ERROR;
295 OCStackResult SendListObserverNotification (OCResource * resource,
296 OCObservationId *obsIdList, uint8_t numberOfIds,
297 const char *notificationJSONPayload, uint32_t maxAge,
298 OCQualityOfService qos)
300 if(!resource || !obsIdList || !notificationJSONPayload)
302 return OC_STACK_INVALID_PARAM;
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;
312 OC_LOG(INFO, TAG, PCF("Entering SendListObserverNotification"));
315 observer = GetObserverUsingId (*obsIdList);
318 // Found observer - verify if it matches the resource handle
319 if (observer->resource == resource)
321 qos = DetermineObserverQoS(OC_REST_GET, observer, qos);
324 result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
325 0, resource->sequenceNum, qos, observer->query,
326 NULL, NULL, observer->token, observer->tokenLength,
332 request->observeResult = OC_STACK_OK;
333 if(result == OC_STACK_OK)
335 OCEntityHandlerResponse ehResponse = {};
336 ehResponse.ehResult = OC_EH_OK;
337 ehResponse.payload = (char *) OICMalloc(MAX_RESPONSE_LENGTH + 1);
338 if(!ehResponse.payload)
340 FindAndDeleteServerRequest(request);
343 OICStrcpy(ehResponse.payload, MAX_RESPONSE_LENGTH + 1,
344 notificationJSONPayload);
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)
352 OC_LOG_V(INFO, TAG, "Observer id %d notified.", *obsIdList);
354 // Increment only if OCDoResponse is successful
355 numSentNotification++;
357 OICFree(ehResponse.payload);
358 FindAndDeleteServerRequest(request);
362 OC_LOG_V(INFO, TAG, "Error notifying observer id %d.", *obsIdList);
367 FindAndDeleteServerRequest(request);
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)
374 observeErrorFlag = true;
382 if(numSentNotification == numberOfIds && !observeErrorFlag)
386 else if(numSentNotification == 0)
388 return OC_STACK_NO_OBSERVERS;
392 OC_LOG(ERROR, TAG, PCF("Observer notification error"));
393 return OC_STACK_ERROR;
397 OCStackResult GenerateObserverId (OCObservationId *observationId)
399 ResourceObserver *resObs = NULL;
401 OC_LOG(INFO, TAG, PCF("Entering GenerateObserverId"));
402 VERIFY_NON_NULL (observationId);
406 *observationId = OCGetRandomByte();
407 // Check if observation Id already exists
408 resObs = GetObserverUsingId (*observationId);
409 } while (NULL != resObs);
411 OC_LOG_V(INFO, TAG, "Generated bservation ID is %u", *observationId);
415 return OC_STACK_ERROR;
418 OCStackResult AddObserver (const char *resUri,
420 OCObservationId obsId,
423 OCResource *resHandle,
424 OCQualityOfService qos,
425 const OCDevAddr *devAddr)
427 // Check if resource exists and is observable.
430 return OC_STACK_INVALID_PARAM;
432 if (!(resHandle->resourceProperties & OC_OBSERVABLE))
434 return OC_STACK_RESOURCE_ERROR;
436 ResourceObserver *obsNode = NULL;
438 if(!resUri || !token || !*token)
440 return OC_STACK_INVALID_PARAM;
443 obsNode = (ResourceObserver *) OICCalloc(1, sizeof(ResourceObserver));
446 obsNode->observeId = obsId;
448 obsNode->resUri = OICStrdup(resUri);
449 VERIFY_NON_NULL (obsNode->resUri);
454 obsNode->query = OICStrdup(query);
455 VERIFY_NON_NULL (obsNode->query);
457 // If tokenLength is zero, the return value depends on the
458 // particular library implementation (it may or may not be a null pointer).
461 obsNode->token = (CAToken_t)OICMalloc(tokenLength);
462 VERIFY_NON_NULL (obsNode->token);
463 memcpy(obsNode->token, token, tokenLength);
465 obsNode->tokenLength = tokenLength;
467 obsNode->devAddr = *devAddr;
468 obsNode->resource = resHandle;
470 LL_APPEND (serverObsList, obsNode);
478 OICFree(obsNode->resUri);
479 OICFree(obsNode->query);
482 return OC_STACK_NO_MEMORY;
485 ResourceObserver* GetObserverUsingId (const OCObservationId observeId)
487 ResourceObserver *out = NULL;
491 LL_FOREACH (serverObsList, out)
493 if (out->observeId == observeId)
499 OC_LOG(INFO, TAG, PCF("Observer node not found!!"));
503 ResourceObserver* GetObserverUsingToken (const CAToken_t token, uint8_t tokenLength)
505 ResourceObserver *out = NULL;
509 LL_FOREACH (serverObsList, out)
511 OC_LOG(INFO, TAG,PCF("comparing tokens"));
512 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)token, tokenLength);
513 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)out->token, tokenLength);
514 if((memcmp(out->token, token, tokenLength) == 0))
520 OC_LOG(INFO, TAG, PCF("Observer node not found!!"));
524 OCStackResult DeleteObserverUsingToken (CAToken_t token, uint8_t tokenLength)
526 if(!token || !*token)
528 return OC_STACK_INVALID_PARAM;
531 ResourceObserver *obsNode = NULL;
533 obsNode = GetObserverUsingToken (token, tokenLength);
536 OC_LOG_V(INFO, TAG, PCF("deleting tokens"));
537 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)obsNode->token, tokenLength);
538 LL_DELETE (serverObsList, obsNode);
539 OICFree(obsNode->resUri);
540 OICFree(obsNode->query);
541 OICFree(obsNode->token);
544 // it is ok if we did not find the observer...
548 void DeleteObserverList()
550 ResourceObserver *out = NULL;
551 ResourceObserver *tmp = NULL;
552 LL_FOREACH_SAFE (serverObsList, out, tmp)
556 DeleteObserverUsingToken ((out->token), out->tokenLength);
559 serverObsList = NULL;
563 * CA layer expects observe registration/de-reg/notiifcations to be passed as a header
564 * option, which breaks the protocol abstraction requirement between RI & CA, and
565 * has to be fixed in the future. The function below adds the header option for observe.
566 * It should be noted that the observe header option is assumed to be the first option
567 * in the list of user defined header options and hence it is inserted at the front
568 * of the header options list and number of options adjusted accordingly.
571 CreateObserveHeaderOption (CAHeaderOption_t **caHdrOpt,
572 OCHeaderOption *ocHdrOpt,
578 return OC_STACK_INVALID_PARAM;
581 CAHeaderOption_t *tmpHdrOpt = NULL;
583 tmpHdrOpt = (CAHeaderOption_t *) OICCalloc ((numOptions+1), sizeof(CAHeaderOption_t));
584 if (NULL == tmpHdrOpt)
586 return OC_STACK_NO_MEMORY;
588 tmpHdrOpt[0].protocolID = CA_COAP_ID;
589 tmpHdrOpt[0].optionID = COAP_OPTION_OBSERVE;
590 tmpHdrOpt[0].optionLength = sizeof(uint32_t);
591 tmpHdrOpt[0].optionData[0] = observeFlag;
592 for (uint8_t i = 0; i < numOptions; i++)
594 memcpy (&(tmpHdrOpt[i+1]), &(ocHdrOpt[i]), sizeof(CAHeaderOption_t));
597 *caHdrOpt = tmpHdrOpt;
602 * CA layer passes observe information to the RI layer as a header option, which
603 * breaks the protocol abstraction requirement between RI & CA, and has to be fixed
604 * in the future. The function below removes the observe header option and processes it.
605 * It should be noted that the observe header option is always assumed to be the first
606 * option in the list of user defined header options and hence it is deleted from the
607 * front of the header options list and the number of options is adjusted accordingly.
610 GetObserveHeaderOption (uint32_t * observationOption,
611 CAHeaderOption_t *options,
612 uint8_t * numOptions)
614 if(!observationOption)
616 return OC_STACK_INVALID_PARAM;
618 *observationOption = OC_OBSERVE_NO_OPTION;
620 if(!options || !numOptions)
622 return OC_STACK_INVALID_PARAM;
625 for(uint8_t i = 0; i < *numOptions; i++)
627 if(options[i].protocolID == CA_COAP_ID &&
628 options[i].optionID == COAP_OPTION_OBSERVE)
630 *observationOption = options[i].optionData[0];
631 for(uint8_t c = i; c < *numOptions-1; c++)
633 options[i] = options[i+1];