Use `URI` all caps in documentation
[iotivity.git] / resource / csdk / stack / src / occlientcb.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 "iotivity_config.h"
22 #include "occlientcb.h"
23 #include <coap/coap.h>
24 #include "experimental/logger.h"
25 #include "trace.h"
26 #include "oic_malloc.h"
27 #include <string.h>
28
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32
33 #include "cacommon.h"
34 #include "cainterface.h"
35
36 /// Module Name
37 #define TAG "OIC_RI_CLIENTCB"
38
39 //-------------------------------------------------------------------------------------------------
40 // Private variables
41 //-------------------------------------------------------------------------------------------------
42 //TODO: Now g_cbList is defined as extern variable since ocstack needs it to process presence.
43 //      This should be static variable after we make a presence feature separately.
44 struct ClientCB *g_cbList = NULL;
45
46 //-------------------------------------------------------------------------------------------------
47 // Local functions
48 //-------------------------------------------------------------------------------------------------
49 static void DeleteClientCBInternal(ClientCB * cbNode)
50 {
51     assert(cbNode);
52
53     OIC_TRACE_BEGIN(%s:DeleteClientCB, TAG);
54     OIC_LOG(INFO, TAG, "Deleting token");
55     OIC_LOG_BUFFER(INFO, TAG, (const uint8_t *)cbNode->token, cbNode->tokenLength);
56     OIC_TRACE_BUFFER("OIC_RI_CLIENTCB:DeleteClientCB:token:",
57                      (const uint8_t *)cbNode->token, cbNode->tokenLength);
58
59     LL_DELETE(g_cbList, cbNode);
60     CADestroyToken(cbNode->token);
61     OICFree(cbNode->devAddr);
62     OICFree(cbNode->handle);
63     if (cbNode->requestUri)
64     {
65         OIC_LOG_V(INFO, TAG, "Deleting callback with uri %s", cbNode->requestUri);
66         OIC_TRACE_MARK(%s:DeleteClientCB:uri:%s, TAG, cbNode->requestUri);
67         OICFree(cbNode->requestUri);
68     }
69     if (cbNode->options)
70     {
71         OICFree(cbNode->options);
72     }
73     if(cbNode->payload)
74     {
75         OICFree(cbNode->payload);
76     }
77 #ifdef WITH_PRESENCE
78     if (cbNode->presence)
79     {
80         OICFree(cbNode->presence->timeOut);
81         OICFree(cbNode->presence);
82     }
83     if (cbNode->method == OC_REST_PRESENCE)
84     {
85         OCResourceType * pointer = cbNode->interestingPresenceResourceType;
86         OCResourceType * next = NULL;
87         while (pointer)
88         {
89             next = pointer->next;
90             OICFree(pointer->resourcetypename);
91             OICFree(pointer);
92             pointer = next;
93         }
94     }
95 #endif // WITH_PRESENCE
96
97     if (cbNode->deleteCallback)
98     {
99         cbNode->deleteCallback(cbNode->context);
100     }
101
102     OICFree(cbNode);
103     cbNode = NULL;
104
105     OIC_TRACE_END();
106 }
107
108 /*
109  * This function checks if the node is past its time to live and
110  * deletes it if timed-out. Calling this function with a  presence or observe
111  * callback with ttl set to 0 will not delete anything as presence nodes have
112  * their own mechanisms for timeouts. A null argument will cause the function to
113  * silently return.
114  */
115 static void CheckAndDeleteTimedOutCB(ClientCB * cbNode)
116 {
117     assert(cbNode);
118
119     if (cbNode->TTL == 0)
120     {
121         return;
122     }
123     coap_tick_t now;
124     coap_ticks(&now);
125
126     if (cbNode->TTL < now)
127     {
128         OIC_LOG(INFO, TAG, "Deleting timed-out callback");
129         DeleteClientCBInternal(cbNode);
130     }
131 }
132
133 #ifdef WITH_PRESENCE
134 /**
135  * Inserts a new resource type filter into this cb node.
136  */
137 static OCStackResult InsertResourceTypeFilter(ClientCB * cbNode,
138                                               char * resourceTypeName)
139 {
140     assert(cbNode);
141     assert(resourceTypeName);
142
143     // Form a new resourceType member.
144     OCResourceType * newResourceType = (OCResourceType *) OICMalloc(sizeof(OCResourceType));
145     if (!newResourceType)
146     {
147         OIC_LOG(ERROR, TAG, "Failed to OICMalloc to newResourceType");
148         return OC_STACK_NO_MEMORY;
149     }
150
151     newResourceType->next = NULL;
152     newResourceType->resourcetypename = resourceTypeName;
153
154     LL_APPEND(cbNode->interestingPresenceResourceType, newResourceType);
155     return OC_STACK_OK;
156 }
157 #endif // WITH_PRESENCE
158
159 //-------------------------------------------------------------------------------------------------
160 // Internal APIs
161 //-------------------------------------------------------------------------------------------------
162 OCStackResult AddClientCB(ClientCB** clientCB, OCCallbackData* cbData,
163                           CAMessageType_t type,
164                           CAToken_t token, uint8_t tokenLength,
165                           CAHeaderOption_t *options, uint8_t numOptions,
166                           CAPayload_t payload, size_t payloadSize,
167                           CAPayloadFormat_t payloadFormat,
168                           OCDoHandle *handle, OCMethod method,
169                           OCDevAddr *devAddr, char *requestUri,
170                           char *resourceTypeName, uint32_t ttl)
171 {
172     if (!clientCB || !cbData || !handle || tokenLength > CA_MAX_TOKEN_LEN)
173     {
174         return OC_STACK_INVALID_PARAM;
175     }
176
177     OIC_TRACE_BEGIN(%s:AddClientCB, TAG);
178
179     ClientCB *cbNode = NULL;
180
181 #ifdef WITH_PRESENCE
182     if (method == OC_REST_PRESENCE)
183     {   // Retrieve the presence callback structure for this specific requestUri.
184         cbNode = GetClientCBUsingUri(requestUri);
185     }
186
187     if (!cbNode)// If it does not already exist, create new node.
188 #endif // WITH_PRESENCE
189     {
190         cbNode = (ClientCB*) OICMalloc(sizeof(ClientCB));
191         if (!cbNode)
192         {
193             *clientCB = NULL;
194             goto exit;
195         }
196
197         OIC_LOG(INFO, TAG, "Adding client callback with token");
198         OIC_LOG_BUFFER(INFO, TAG, (const uint8_t *)token, tokenLength);
199         OIC_TRACE_BUFFER("OIC_RI_CLIENTCB:AddClientCB:token:",
200                          (const uint8_t *)token, tokenLength);
201         cbNode->callBack = cbData->cb;
202         cbNode->context = cbData->context;
203         cbNode->deleteCallback = cbData->cd;
204
205         if (!options || !numOptions)
206         {
207             OIC_LOG (INFO, TAG, "No options present");
208             cbNode->options = NULL;
209             cbNode->numOptions = 0;
210         }
211         else
212         {
213             cbNode->options = (CAHeaderOption_t *) OICCalloc(numOptions, sizeof(CAHeaderOption_t));
214             if (!cbNode->options)
215             {
216                 OIC_LOG(ERROR, TAG, "Out of memory");
217                 OICFree(cbNode);
218                 return OC_STACK_NO_MEMORY;
219             }
220             memcpy(cbNode->options, options, sizeof(CAHeaderOption_t) * numOptions);
221             cbNode->numOptions = numOptions;
222         }
223
224         if (!payload || !payloadSize)
225         {
226             OIC_LOG (INFO, TAG, "No payload present");
227             cbNode->payload = NULL;
228             cbNode->payloadSize = 0;
229         }
230         else
231         {
232             cbNode->payload = (CAPayload_t) OICCalloc(1, payloadSize);
233             if (!cbNode->payload)
234             {
235                 OIC_LOG(ERROR, TAG, "Out of memory");
236                 if (cbNode->options)
237                 {
238                     OICFree(cbNode->options);
239                 }
240                 OICFree(cbNode);
241                 return OC_STACK_NO_MEMORY;
242             }
243             memcpy(cbNode->payload, payload, payloadSize);
244             cbNode->payloadSize = payloadSize;
245         }
246
247         cbNode->payloadFormat = payloadFormat;
248         cbNode->type = type;
249         //Note: token memory is allocated in the caller OCDoResource
250         //but freed in DeleteClientCB
251         cbNode->token = token;
252         cbNode->tokenLength = tokenLength;
253         cbNode->handle = *handle;
254         cbNode->method = method;
255         cbNode->sequenceNumber = 0;
256 #ifdef WITH_PRESENCE
257         cbNode->presence = NULL;
258         cbNode->interestingPresenceResourceType = NULL;
259 #endif // WITH_PRESENCE
260
261         if (method == OC_REST_PRESENCE ||
262             method == OC_REST_OBSERVE  ||
263             method == OC_REST_OBSERVE_ALL)
264         {
265             cbNode->TTL = 0;
266         }
267         else
268         {
269             cbNode->TTL = ttl;
270         }
271         cbNode->requestUri = requestUri;    // I own it now
272         cbNode->devAddr = devAddr;          // I own it now
273         OIC_LOG_V(INFO, TAG, "Added Callback for uri : %s", requestUri);
274         OIC_TRACE_MARK(%s:AddClientCB:uri:%s, TAG, requestUri);
275         LL_APPEND(g_cbList, cbNode);
276         *clientCB = cbNode;
277     }
278 #ifdef WITH_PRESENCE
279     else
280     {
281         // Ensure that the handle the SDK hands back up to the application layer for the
282         // OCDoResource call matches the found ClientCB Node.
283         *clientCB = cbNode;
284
285         if (cbData->cd)
286         {
287             cbData->cd(cbData->context);
288         }
289
290         CADestroyToken(token);
291         OICFree(*handle);
292         OICFree(requestUri);
293         OICFree(devAddr);
294         *handle = cbNode->handle;
295     }
296
297     if (method == OC_REST_PRESENCE && resourceTypeName)
298     {
299         OIC_TRACE_END();
300         // Amend the found or created node by adding a new resourceType to it.
301         return InsertResourceTypeFilter(cbNode, (char *)resourceTypeName);
302         // I own resourceTypName now.
303     }
304     else
305     {
306         OICFree(resourceTypeName);
307     }
308 #else
309     OICFree(resourceTypeName);
310 #endif
311
312     OIC_TRACE_END();
313     return OC_STACK_OK;
314
315 exit:
316     OIC_TRACE_END();
317     return OC_STACK_NO_MEMORY;
318 }
319
320 void DeleteClientCB(ClientCB * cbNode)
321 {
322     if (cbNode)
323     {
324         ClientCB* out = NULL;
325         ClientCB* tmp = NULL;
326         LL_FOREACH_SAFE(g_cbList, out, tmp)
327         {
328             if (cbNode == out)
329             {
330                 DeleteClientCBInternal(out);
331                 break;
332             }
333         }
334     }
335 }
336
337 void DeleteClientCBList(void)
338 {
339     ClientCB* out = NULL;
340     ClientCB* tmp = NULL;
341     LL_FOREACH_SAFE(g_cbList, out, tmp)
342     {
343         DeleteClientCBInternal(out);
344     }
345     g_cbList = NULL;
346 }
347
348 ClientCB* GetClientCBUsingToken(const CAToken_t token,
349                                 const uint8_t tokenLength)
350 {
351     if (!token || tokenLength > CA_MAX_TOKEN_LEN || tokenLength <= 0)
352     {
353         OIC_LOG(ERROR, TAG, "Invalid parameter");
354         return NULL;
355     }
356
357     OIC_LOG (INFO, TAG, "Looking for token");
358     OIC_LOG_BUFFER(INFO, TAG, (const uint8_t *)token, tokenLength);
359
360     ClientCB* out = NULL;
361     ClientCB* tmp = NULL;
362     LL_FOREACH_SAFE(g_cbList, out, tmp)
363     {
364         /* de-annotate below line if want to see all token in g_cbList */
365         //OIC_LOG_BUFFER(INFO, TAG, (const uint8_t *)out->token, tokenLength);
366         if (memcmp(out->token, token, tokenLength) == 0)
367         {
368             OIC_LOG(INFO, TAG, "Found in callback list");
369             return out;
370         }
371         CheckAndDeleteTimedOutCB(out);
372     }
373
374     OIC_LOG(INFO, TAG, "Callback Not found!");
375     return NULL;
376 }
377
378 ClientCB* GetClientCBUsingHandle(const OCDoHandle handle)
379 {
380     if (!handle)
381     {
382         OIC_LOG(ERROR, TAG, "Invalid parameter");
383         return NULL;
384     }
385
386     OIC_LOG(INFO, TAG,  "Looking for handle");
387
388     ClientCB* out = NULL;
389     ClientCB* tmp = NULL;
390     LL_FOREACH_SAFE(g_cbList, out, tmp)
391     {
392         if (out->handle == handle)
393         {
394             OIC_LOG(INFO, TAG, "Found in callback list");
395             return out;
396         }
397         CheckAndDeleteTimedOutCB(out);
398     }
399
400     OIC_LOG(INFO, TAG, "Callback Not found!");
401     return NULL;
402 }
403
404 #ifdef WITH_PRESENCE
405 ClientCB* GetClientCBUsingUri(const char *requestUri)
406 {
407     if (!requestUri)
408     {
409         OIC_LOG(ERROR, TAG, "Invalid parameter");
410         return NULL;
411     }
412
413     OIC_LOG_V(INFO, TAG, "Looking for uri %s", requestUri);
414
415     ClientCB* out = NULL;
416     ClientCB* tmp = NULL;
417     LL_FOREACH_SAFE(g_cbList, out, tmp)
418     {
419         /* de-annotate below line if want to see all URI in g_cbList */
420         //OIC_LOG_V(INFO, TAG, "%s", out->requestUri);
421         if (out->requestUri && strcmp(out->requestUri, requestUri ) == 0)
422         {
423             OIC_LOG(INFO, TAG, "Found in callback list");
424             return out;
425         }
426         CheckAndDeleteTimedOutCB(out);
427     }
428
429     OIC_LOG(INFO, TAG, "Callback Not found!");
430     return NULL;
431 }
432 #endif // WITH_PRESENCE