Implementation of connectivity abstraction feature release v0.2
[platform/upstream/iotivity.git] / resource / csdk / connectivity / lib / libcoap-4.1.1 / resource.c
1 /* resource.c -- generic resource handling
2  *
3  * Copyright (C) 2010--2014 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use.
7  */
8
9 #include "config.h"
10 #include "net.h"
11 #include "debug.h"
12 #include "resource.h"
13 #include "subscribe.h"
14
15 #ifdef WITH_LWIP
16 #include "utlist.h"
17 /* mem.h is only needed for the string free calls for
18  * COAP_ATTR_FLAGS_RELEASE_NAME / COAP_ATTR_FLAGS_RELEASE_VALUE /
19  * COAP_RESOURCE_FLAGS_RELEASE_URI. not sure what those lines should actually
20  * do on lwip. */
21 #include "mem.h"
22
23 #include <lwip/memp.h>
24
25 #define COAP_MALLOC_TYPE(Type) \
26   ((coap_##Type##_t *)memp_malloc(MEMP_COAP_##Type))
27 #define COAP_FREE_TYPE(Type, Object) memp_free(MEMP_COAP_##Type, Object)
28
29 #endif
30 #ifdef WITH_POSIX
31 #include "utlist.h"
32 #include "mem.h"
33
34 #define COAP_MALLOC_TYPE(Type) \
35   ((coap_##Type##_t *)coap_malloc(sizeof(coap_##Type##_t)))
36 #define COAP_FREE_TYPE(Type, Object) coap_free(Object)
37
38 #endif /* WITH_POSIX */
39 #ifdef WITH_CONTIKI
40 #include "memb.h"
41
42 MEMB(resource_storage, coap_resource_t, COAP_MAX_RESOURCES);
43 MEMB(attribute_storage, coap_attr_t, COAP_MAX_ATTRIBUTES);
44 MEMB(subscription_storage, coap_subscription_t, COAP_MAX_SUBSCRIBERS);
45
46 void
47 coap_resources_init()
48 {
49     memb_init(&resource_storage);
50     memb_init(&attribute_storage);
51     memb_init(&subscription_storage);
52 }
53
54 static inline coap_subscription_t *
55 coap_malloc_subscription()
56 {
57     return memb_alloc(&subscription_storage);
58 }
59
60 static inline void
61 coap_free_subscription(coap_subscription_t *subscription)
62 {
63     memb_free(&subscription_storage, subscription);
64 }
65 #endif /* WITH_CONTIKI */
66
67 #define min(a,b) ((a) < (b) ? (a) : (b))
68
69 /* Helper functions for conditional output of character sequences into
70  * a given buffer. The first Offset characters are skipped.
71  */
72
73 /**
74  * Adds Char to Buf if Offset is zero. Otherwise, Char is not written
75  * and Offset is decremented.
76  */
77 #define PRINT_WITH_OFFSET(Buf,Offset,Char)      \
78   if ((Offset) == 0) {                  \
79     (*(Buf)++) = (Char);                \
80   } else {                      \
81     (Offset)--;                     \
82   }                         \
83
84 /**
85  * Adds Char to Buf if Offset is zero and Buf is less than Bufend.
86  */
87 #define PRINT_COND_WITH_OFFSET(Buf,Bufend,Offset,Char,Result) {     \
88     if ((Buf) < (Bufend)) {                     \
89       PRINT_WITH_OFFSET(Buf,Offset,Char);               \
90     }                                   \
91     (Result)++;                             \
92   }
93
94 /**
95  * Copies at most Length characters of Str to Buf. The first Offset
96  * characters are skipped. Output may be truncated to Bufend - Buf
97  * characters.
98  */
99 #define COPY_COND_WITH_OFFSET(Buf,Bufend,Offset,Str,Length,Result) {    \
100     size_t i;                               \
101     for (i = 0; i < (Length); i++) {                    \
102       PRINT_COND_WITH_OFFSET((Buf), (Bufend), (Offset), (Str)[i], (Result)); \
103     }                                   \
104   }
105
106 int match(const str *text, const str *pattern, int match_prefix, int match_substring)
107 {
108     assert(text);
109     assert(pattern);
110
111     if (text->length < pattern->length)
112         return 0;
113
114     if (match_substring)
115     {
116         unsigned char *next_token = text->s;
117         size_t remaining_length = text->length;
118         while (remaining_length)
119         {
120             size_t token_length;
121             unsigned char *token = next_token;
122             next_token = memchr(token, ' ', remaining_length);
123
124             if (next_token)
125             {
126                 token_length = next_token - token;
127                 remaining_length -= (token_length + 1);
128                 next_token++;
129             }
130             else
131             {
132                 token_length = remaining_length;
133                 remaining_length = 0;
134             }
135
136             if ((match_prefix || pattern->length == token_length)
137                     && memcmp(token, pattern->s, pattern->length) == 0)
138                 return 1;
139         }
140         return 0;
141     }
142
143     return (match_prefix || pattern->length == text->length)
144             && memcmp(text->s, pattern->s, pattern->length) == 0;
145 }
146
147 /**
148  * Prints the names of all known resources to @p buf. This function
149  * sets @p buflen to the number of bytes actually written and returns
150  * @c 1 on succes. On error, the value in @p buflen is undefined and
151  * the return value will be @c 0.
152  *
153  * @param context The context with the resource map.
154  * @param buf     The buffer to write the result.
155  * @param buflen  Must be initialized to the maximum length of @p buf and will be
156  *                set to the length of the well-known response on return.
157  * @param offset  The offset in bytes where the output shall start and is
158  *                shifted accordingly with the characters that have been
159  *                processed. This parameter is used to support the block
160  *                option.
161  * @param query_filter A filter query according to <a href="http://tools.ietf.org/html/draft-ietf-core-link-format-11#section-4.1">Link Format</a>
162  *
163  * @return COAP_PRINT_STATUS_ERROR on error. Otherwise, the lower 28 bits are
164  *         set to the number of bytes that have actually been written to
165  *         @p buf. COAP_PRINT_STATUS_TRUNC is set when the output has been
166  *         truncated.
167  */
168 #if defined(__GNUC__) && defined(WITHOUT_QUERY_FILTER)
169 coap_print_status_t
170 print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
171         size_t offset,
172         coap_opt_t *query_filter __attribute__ ((unused)))
173 {
174 #else /* not a GCC */
175 coap_print_status_t print_wellknown(coap_context_t *context, unsigned char *buf, size_t *buflen,
176         size_t offset, coap_opt_t *query_filter)
177 {
178 #endif /* GCC */
179     coap_resource_t *r;
180     unsigned char *p = buf;
181     const unsigned char *bufend = buf + *buflen;
182     size_t left, written = 0;
183     coap_print_status_t result;
184     const size_t old_offset = offset;
185     int subsequent_resource = 0;
186 #ifndef COAP_RESOURCES_NOHASH
187     coap_resource_t *tmp;
188 #endif
189 #ifndef WITHOUT_QUERY_FILTER
190     str resource_param =
191     { 0, NULL }, query_pattern =
192     { 0, NULL };
193     int flags = 0; /* MATCH_SUBSTRING, MATCH_PREFIX, MATCH_URI */
194 #define MATCH_URI       0x01
195 #define MATCH_PREFIX    0x02
196 #define MATCH_SUBSTRING 0x04
197     static const str _rt_attributes[] =
198     {
199     { 2, (unsigned char *) "rt" },
200     { 2, (unsigned char *) "if" },
201     { 3, (unsigned char *) "rel" },
202     { 0, NULL } };
203 #endif /* WITHOUT_QUERY_FILTER */
204
205 #ifdef WITH_CONTIKI
206     int i;
207 #endif /* WITH_CONTIKI */
208
209 #ifndef WITHOUT_QUERY_FILTER
210     /* split query filter, if any */
211     if (query_filter)
212     {
213         resource_param.s = COAP_OPT_VALUE(query_filter);
214         while (resource_param.length < COAP_OPT_LENGTH(query_filter)
215                 && resource_param.s[resource_param.length] != '=')
216             resource_param.length++;
217
218         if (resource_param.length < COAP_OPT_LENGTH(query_filter))
219         {
220             const str *rt_attributes;
221             if (resource_param.length == 4 && memcmp(resource_param.s, "href", 4) == 0)
222                 flags |= MATCH_URI;
223
224             for (rt_attributes = _rt_attributes; rt_attributes->s; rt_attributes++)
225             {
226                 if (resource_param.length == rt_attributes->length
227                         && memcmp(resource_param.s, rt_attributes->s, rt_attributes->length) == 0)
228                 {
229                     flags |= MATCH_SUBSTRING;
230                     break;
231                 }
232             }
233
234             /* rest is query-pattern */
235             query_pattern.s = COAP_OPT_VALUE(query_filter) + resource_param.length + 1;
236
237             assert((resource_param.length + 1) <= COAP_OPT_LENGTH(query_filter));
238             query_pattern.length = COAP_OPT_LENGTH(query_filter) - (resource_param.length + 1);
239
240             if ((query_pattern.s[0] == '/') && ((flags & MATCH_URI) == MATCH_URI))
241             {
242                 query_pattern.s++;
243                 query_pattern.length--;
244             }
245
246             if (query_pattern.length && query_pattern.s[query_pattern.length - 1] == '*')
247             {
248                 query_pattern.length--;
249                 flags |= MATCH_PREFIX;
250             }
251         }
252     }
253 #endif /* WITHOUT_QUERY_FILTER */
254
255 #ifndef WITH_CONTIKI
256
257 #ifdef COAP_RESOURCES_NOHASH
258     LL_FOREACH(context->resources, r)
259     {
260 #else
261     HASH_ITER(hh, context->resources, r, tmp)
262     {
263 #endif
264 #else /* WITH_CONTIKI */
265         r = (coap_resource_t *)resource_storage.mem;
266         for (i = 0; i < resource_storage.num; ++i, ++r)
267         {
268             if (!resource_storage.count[i])
269             continue;
270 #endif /* WITH_CONTIKI */
271
272 #ifndef WITHOUT_QUERY_FILTER
273         if (resource_param.length)
274         { /* there is a query filter */
275
276             if (flags & MATCH_URI)
277             { /* match resource URI */
278                 if (!match(&r->uri, &query_pattern, (flags & MATCH_PREFIX) != 0,
279                         (flags & MATCH_SUBSTRING) != 0))
280                     continue;
281             }
282             else
283             { /* match attribute */
284                 coap_attr_t *attr;
285                 str unquoted_val;
286                 attr = coap_find_attr(r, resource_param.s, resource_param.length);
287                 if (!attr)
288                     continue;
289                 if (attr->value.s[0] == '"')
290                 { /* if attribute has a quoted value, remove double quotes */
291                     unquoted_val.length = attr->value.length - 2;
292                     unquoted_val.s = attr->value.s + 1;
293                 }
294                 else
295                 {
296                     unquoted_val = attr->value;
297                 }
298                 if (!(match(&unquoted_val, &query_pattern, (flags & MATCH_PREFIX) != 0,
299                         (flags & MATCH_SUBSTRING) != 0)))
300                     continue;
301             }
302         }
303 #endif /* WITHOUT_QUERY_FILTER */
304
305         if (!subsequent_resource)
306         { /* this is the first resource  */
307             subsequent_resource = 1;
308         }
309         else
310         {
311             PRINT_COND_WITH_OFFSET(p, bufend, offset, ',', written);
312         }
313
314         left = bufend - p; /* calculate available space */
315         result = coap_print_link(r, p, &left, &offset);
316
317         if (result & COAP_PRINT_STATUS_ERROR)
318         {
319             break;
320         }
321
322         /* coap_print_link() returns the number of characters that
323          * where actually written to p. Now advance to its end. */
324         p += COAP_PRINT_OUTPUT_LENGTH(result);
325         written += left;
326     }
327
328     *buflen = written;
329     result = p - buf;
330     if (result + old_offset - offset < *buflen)
331     {
332         result |= COAP_PRINT_STATUS_TRUNC;
333     }
334     return result;
335 }
336
337 coap_resource_t *
338 coap_resource_init(const unsigned char *uri, size_t len, int flags)
339 {
340     coap_resource_t *r;
341
342 #ifdef WITH_POSIX
343     r = (coap_resource_t *)coap_malloc(sizeof(coap_resource_t));
344 #endif
345 #ifdef WITH_LWIP
346     r = (coap_resource_t *)memp_malloc(MEMP_COAP_RESOURCE);
347 #endif
348 #ifdef WITH_CONTIKI
349     r = (coap_resource_t *)memb_alloc(&resource_storage);
350 #endif
351     if (r)
352     {
353         memset(r, 0, sizeof(coap_resource_t));
354
355 #ifdef WITH_CONTIKI
356         LIST_STRUCT_INIT(r, link_attr);
357 #endif /* WITH_CONTIKI */
358         LIST_STRUCT_INIT(r, subscribers);
359
360         r->uri.s = (unsigned char *) uri;
361         r->uri.length = len;
362
363         coap_hash_path(r->uri.s, r->uri.length, r->key);
364
365         r->flags = flags;
366     }
367     else
368     {
369         debug("coap_resource_init: no memory left\n");
370     }
371
372     return r;
373 }
374
375 coap_attr_t *
376 coap_add_attr(coap_resource_t *resource, const unsigned char *name, size_t nlen,
377         const unsigned char *val, size_t vlen, int flags)
378 {
379     coap_attr_t *attr;
380
381     if (!resource || !name)
382         return NULL;
383
384 #ifdef WITH_POSIX
385     attr = (coap_attr_t *)coap_malloc(sizeof(coap_attr_t));
386 #endif
387 #ifdef WITH_LWIP
388     attr = (coap_attr_t *)memp_malloc(MEMP_COAP_RESOURCEATTR);
389 #endif
390 #ifdef WITH_CONTIKI
391     attr = (coap_attr_t *)memb_alloc(&attribute_storage);
392 #endif
393
394     if (attr)
395     {
396         attr->name.length = nlen;
397         attr->value.length = val ? vlen : 0;
398
399         attr->name.s = (unsigned char *) name;
400         attr->value.s = (unsigned char *) val;
401
402         attr->flags = flags;
403
404         /* add attribute to resource list */
405 #ifndef WITH_CONTIKI
406         LL_PREPEND(resource->link_attr, attr);
407 #else /* WITH_CONTIKI */
408         list_add(resource->link_attr, attr);
409 #endif /* WITH_CONTIKI */
410     }
411     else
412     {
413         debug("coap_add_attr: no memory left\n");
414     }
415
416     return attr;
417 }
418
419 coap_attr_t *
420 coap_find_attr(coap_resource_t *resource, const unsigned char *name, size_t nlen)
421 {
422     coap_attr_t *attr;
423
424     if (!resource || !name)
425         return NULL;
426
427 #ifndef WITH_CONTIKI
428     LL_FOREACH(resource->link_attr, attr)
429     {
430 #else /* WITH_CONTIKI */
431         for (attr = list_head(resource->link_attr); attr;
432                 attr = list_item_next(attr))
433         {
434 #endif /* WITH_CONTIKI */
435         if (attr->name.length == nlen && memcmp(attr->name.s, name, nlen) == 0)
436             return attr;
437     }
438
439     return NULL;
440 }
441
442 void coap_delete_attr(coap_attr_t *attr)
443 {
444     if (!attr)
445         return;
446     if (attr->flags & COAP_ATTR_FLAGS_RELEASE_NAME)
447         coap_free(attr->name.s);
448     if (attr->flags & COAP_ATTR_FLAGS_RELEASE_VALUE)
449         coap_free(attr->value.s);
450 #ifdef POSIX
451     coap_free(attr);
452 #endif
453 #ifdef WITH_LWIP
454     memp_free(MEMP_COAP_RESOURCEATTR, attr);
455 #endif
456 #ifdef WITH_CONTIKI
457     /* FIXME it looks like this was never implemented */
458 #endif
459 }
460
461 void coap_hash_request_uri(const coap_pdu_t *request, coap_key_t key)
462 {
463     coap_opt_iterator_t opt_iter;
464     coap_opt_filter_t filter;
465     coap_opt_t *option;
466
467     memset(key, 0, sizeof(coap_key_t));
468
469     coap_option_filter_clear(filter);
470     coap_option_setb(filter, COAP_OPTION_URI_PATH);
471
472     coap_option_iterator_init((coap_pdu_t *) request, &opt_iter, filter);
473     while ((option = coap_option_next(&opt_iter)))
474         coap_hash(COAP_OPT_VALUE(option), COAP_OPT_LENGTH(option), key);
475 }
476
477 void coap_add_resource(coap_context_t *context, coap_resource_t *resource)
478 {
479 #ifndef WITH_CONTIKI
480 #ifdef COAP_RESOURCES_NOHASH
481     LL_PREPEND(context->resources, resource);
482 #else
483     HASH_ADD(hh, context->resources, key, sizeof(coap_key_t), resource);
484 #endif
485 #endif /* WITH_CONTIKI */
486 }
487
488 int coap_delete_resource(coap_context_t *context, coap_key_t key)
489 {
490     coap_resource_t *resource;
491     coap_attr_t *attr, *tmp;
492 #ifdef WITH_CONTIKI
493     coap_subscription_t *obs;
494 #endif
495
496     if (!context)
497         return 0;
498
499     resource = coap_get_resource_from_key(context, key);
500
501     if (!resource)
502         return 0;
503
504 #if defined(WITH_POSIX) || defined(WITH_LWIP)
505 #ifdef COAP_RESOURCES_NOHASH
506     LL_DELETE(context->resources, resource);
507 #else
508     HASH_DELETE(hh, context->resources, resource);
509 #endif
510
511     /* delete registered attributes */
512     LL_FOREACH_SAFE(resource->link_attr, attr, tmp) coap_delete_attr(attr);
513
514     if (resource->flags & COAP_RESOURCE_FLAGS_RELEASE_URI)
515     coap_free(resource->uri.s);
516
517 #ifdef WITH_POSIX
518     coap_free(resource);
519 #endif
520 #ifdef WITH_LWIP
521     memp_free(MEMP_COAP_RESOURCE, resource);
522 #endif
523 #else /* not (WITH_POSIX || WITH_LWIP) */
524     /* delete registered attributes */
525     while ((attr = list_pop(resource->link_attr)))
526         memb_free(&attribute_storage, attr);
527
528     /* delete subscribers */
529     while ((obs = list_pop(resource->subscribers)))
530     {
531         /* FIXME: notify observer that its subscription has been removed */
532         memb_free(&subscription_storage, obs);
533     }
534
535     memb_free(&resource_storage, resource);
536 #endif /* WITH_CONTIKI */
537
538     return 1;
539 }
540
541 coap_resource_t *
542 coap_get_resource_from_key(coap_context_t *context, coap_key_t key)
543 {
544 #ifndef WITH_CONTIKI
545     coap_resource_t *resource;
546 #ifdef COAP_RESOURCES_NOHASH
547     resource = NULL;
548     LL_FOREACH(context->resources, resource)
549     {
550         /* if you think you can outspart the compiler and speed things up by (eg by
551          * casting to uint32* and comparing alues), increment this counter: 1 */
552         if (memcmp(key, resource->key, sizeof(coap_key_t)) == 0)
553         return resource;
554     }
555     return NULL;
556 #else
557     HASH_FIND(hh, context->resources, key, sizeof(coap_key_t), resource);
558
559     return resource;
560 #endif
561 #else /* WITH_CONTIKI */
562     int i;
563     coap_resource_t *ptr2;
564
565     /* the search function is basically taken from memb.c */
566     ptr2 = (coap_resource_t *)resource_storage.mem;
567     for (i = 0; i < resource_storage.num; ++i)
568     {
569         if (resource_storage.count[i] &&
570                 (memcmp(ptr2->key, key, sizeof(coap_key_t)) == 0))
571         return (coap_resource_t *)ptr2;
572         ++ptr2;
573     }
574
575     return NULL;
576 #endif /* WITH_CONTIKI */
577 }
578
579 coap_print_status_t coap_print_link(const coap_resource_t *resource, unsigned char *buf,
580         size_t *len, size_t *offset)
581 {
582     unsigned char *p = buf;
583     const unsigned char *bufend = buf + *len;
584     coap_attr_t *attr;
585     coap_print_status_t result = 0;
586     const size_t old_offset = *offset;
587
588     *len = 0;
589     PRINT_COND_WITH_OFFSET(p, bufend, *offset, '<', *len);
590     PRINT_COND_WITH_OFFSET(p, bufend, *offset, '/', *len);
591
592     COPY_COND_WITH_OFFSET(p, bufend, *offset, resource->uri.s, resource->uri.length, *len);
593
594     PRINT_COND_WITH_OFFSET(p, bufend, *offset, '>', *len);
595
596 #ifndef WITH_CONTIKI
597     LL_FOREACH(resource->link_attr, attr)
598     {
599 #else /* WITH_CONTIKI */
600         for (attr = list_head(resource->link_attr); attr;
601                 attr = list_item_next(attr))
602         {
603 #endif /* WITH_CONTIKI */
604
605         PRINT_COND_WITH_OFFSET(p, bufend, *offset, ';', *len);
606
607         COPY_COND_WITH_OFFSET(p, bufend, *offset, attr->name.s, attr->name.length, *len);
608
609         if (attr->value.s)
610         {
611             PRINT_COND_WITH_OFFSET(p, bufend, *offset, '=', *len);
612
613             COPY_COND_WITH_OFFSET(p, bufend, *offset, attr->value.s, attr->value.length, *len);
614         }
615
616     }
617     if (resource->observable)
618     {
619         COPY_COND_WITH_OFFSET(p, bufend, *offset, ";obs", 4, *len);
620     }
621
622     result = p - buf;
623     if (result + old_offset - *offset < *len)
624     {
625         result |= COAP_PRINT_STATUS_TRUNC;
626     }
627
628     return result;
629 }
630
631 #ifndef WITHOUT_OBSERVE
632 coap_subscription_t *
633 coap_find_observer(coap_resource_t *resource, const coap_address_t *peer, const str *token)
634 {
635     coap_subscription_t *s;
636
637     assert(resource);
638     assert(peer);
639
640     for (s = list_head(resource->subscribers); s; s = list_item_next(s))
641     {
642         if (coap_address_equals(&s->subscriber, peer)
643                 && (!token
644                         || (token->length == s->token_length
645                                 && memcmp(token->s, s->token, token->length) == 0)))
646             return s;
647     }
648
649     return NULL;
650 }
651
652 coap_subscription_t *
653 coap_add_observer(coap_resource_t *resource, const coap_address_t *observer, const str *token)
654 {
655     coap_subscription_t *s;
656
657     assert(observer);
658
659     /* Check if there is already a subscription for this peer. */
660     s = coap_find_observer(resource, observer, token);
661
662     /* We are done if subscription was found. */
663     if (s)
664         return s;
665
666     /* s points to a different subscription, so we have to create
667      * another one. */
668     s = COAP_MALLOC_TYPE(subscription);
669
670     if (!s)
671         return NULL;
672
673     coap_subscription_init(s);
674     memcpy(&s->subscriber, observer, sizeof(coap_address_t));
675
676     if (token && token->length)
677     {
678         s->token_length = token->length;
679         memcpy(s->token, token->s, min(s->token_length, 8));
680     }
681
682     /* add subscriber to resource */
683     list_add(resource->subscribers, s);
684
685     return s;
686 }
687
688 void coap_touch_observer(coap_context_t *context, const coap_address_t *observer, const str *token)
689 {
690     coap_resource_t *r;
691     coap_subscription_t *s;
692
693 #ifndef WITH_CONTIKI
694 #ifdef COAP_RESOURCES_NOHASH
695     LL_FOREACH(context->resources, r)
696     {
697 #else
698     coap_resource_t *tmp;
699     HASH_ITER(hh, context->resources, r, tmp)
700     {
701 #endif
702         s = coap_find_observer(r, observer, token);
703         if (s)
704         {
705             s->fail_cnt = 0;
706         }
707     }
708 #else /* WITH_CONTIKI */
709     r = (coap_resource_t *)resource_storage.mem;
710     for (i = 0; i < resource_storage.num; ++i, ++r)
711     {
712         if (resource_storage.count[i])
713         {
714             s = coap_find_observer(r, observer, token);
715             if (s)
716             {
717                 s->fail_cnt = 0;
718             }
719         }
720     }
721 #endif /* WITH_CONTIKI */
722 }
723
724 void coap_delete_observer(coap_resource_t *resource, const coap_address_t *observer,
725         const str *token)
726 {
727     coap_subscription_t *s;
728
729     s = coap_find_observer(resource, observer, token);
730
731     if (s)
732     {
733         list_remove(resource->subscribers, s);
734
735         COAP_FREE_TYPE(subscription, s);
736     }
737 }
738
739 static void coap_notify_observers(coap_context_t *context, coap_resource_t *r)
740 {
741     coap_method_handler_t h;
742     coap_subscription_t *obs;
743     str token;
744     coap_pdu_t *response;
745
746     if (r->observable && (r->dirty || r->partiallydirty))
747     {
748         r->partiallydirty = 0;
749
750         /* retrieve GET handler, prepare response */
751         h = r->handler[COAP_REQUEST_GET - 1];
752         assert(h); /* we do not allow subscriptions if no
753          * GET handler is defined */
754
755         for (obs = list_head(r->subscribers); obs; obs = list_item_next(obs))
756         {
757             if (r->dirty == 0 && obs->dirty == 0)
758                 /* running this resource due to partiallydirty, but this observation's notification was already enqueued */
759                 continue;
760
761             coap_tid_t tid = COAP_INVALID_TID;
762             obs->dirty = 0;
763             /* initialize response */
764             response = coap_pdu_init(COAP_MESSAGE_CON, 0, 0, COAP_MAX_PDU_SIZE);
765             if (!response)
766             {
767                 obs->dirty = 1;
768                 r->partiallydirty = 1;
769                 debug("coap_check_notify: pdu init failed, resource stays partially dirty\n");
770                 continue;
771             }
772
773             if (!coap_add_token(response, obs->token_length, obs->token))
774             {
775                 obs->dirty = 1;
776                 r->partiallydirty = 1;
777                 debug("coap_check_notify: cannot add token, resource stays partially dirty\n");
778                 coap_delete_pdu(response);
779                 continue;
780             }
781
782             token.length = obs->token_length;
783             token.s = obs->token;
784
785             response->hdr->id = coap_new_message_id(context);
786             if (obs->non && obs->non_cnt < COAP_OBS_MAX_NON)
787             {
788                 response->hdr->type = COAP_MESSAGE_NON;
789             }
790             else
791             {
792                 response->hdr->type = COAP_MESSAGE_CON;
793             }
794             /* fill with observer-specific data */
795             h(context, r, &obs->subscriber, NULL, &token, response);
796
797             if (response->hdr->type == COAP_MESSAGE_CON)
798             {
799                 tid = coap_send_confirmed(context, &obs->subscriber, response);
800                 obs->non_cnt = 0;
801             }
802             else
803             {
804                 tid = coap_send(context, &obs->subscriber, response);
805                 obs->non_cnt++;
806             }
807
808             if (COAP_INVALID_TID == tid || response->hdr->type != COAP_MESSAGE_CON)
809                 coap_delete_pdu(response);
810             if (COAP_INVALID_TID == tid)
811             {
812                 debug("coap_check_notify: sending failed, resource stays partially dirty\n");
813                 obs->dirty = 1;
814                 r->partiallydirty = 1;
815             }
816
817         }
818
819         /* Increment value for next Observe use. */
820         context->observe++;
821     }
822     r->dirty = 0;
823 }
824
825 void coap_check_notify(coap_context_t *context)
826 {
827     coap_resource_t *r;
828 #ifndef WITH_CONTIKI
829
830 #ifdef COAP_RESOURCES_NOHASH
831     LL_FOREACH(context->resources, r)
832     {
833 #else
834     coap_resource_t *tmp;
835     HASH_ITER(hh, context->resources, r, tmp)
836     {
837 #endif
838         coap_notify_observers(context, r);
839     }
840 #else /* WITH_CONTIKI */
841     int i;
842
843     r = (coap_resource_t *)resource_storage.mem;
844     for (i = 0; i < resource_storage.num; ++i, ++r)
845     {
846         if (resource_storage.count[i])
847         {
848             coap_notify_observers(context, r);
849         }
850     }
851 #endif /* WITH_CONTIKI */
852 }
853
854 /**
855  * Checks the failure counter for (peer, token) and removes peer from
856  * the list of observers for the given resource when COAP_OBS_MAX_FAIL
857  * is reached.
858  *
859  * @param context  The CoAP context to use
860  * @param resource The resource to check for (peer, token)
861  * @param peer     The observer's address
862  * @param token    The token that has been used for subscription.
863  */
864 static void coap_remove_failed_observers(coap_context_t *context, coap_resource_t *resource,
865         const coap_address_t *peer, const str *token)
866 {
867     coap_subscription_t *obs;
868
869     for (obs = list_head(resource->subscribers); obs; obs = list_item_next(obs))
870     {
871         if (coap_address_equals(peer, &obs->subscriber) && token->length == obs->token_length
872                 && memcmp(token->s, obs->token, token->length) == 0)
873         {
874
875             /* count failed notifies and remove when
876              * COAP_MAX_FAILED_NOTIFY is reached */
877             if (obs->fail_cnt < COAP_OBS_MAX_FAIL)
878                 obs->fail_cnt++;
879             else
880             {
881                 list_remove(resource->subscribers, obs);
882                 obs->fail_cnt = 0;
883
884 #ifndef NDEBUG
885                 if (LOG_DEBUG <= coap_get_log_level())
886                 {
887 #ifndef INET6_ADDRSTRLEN
888 #define INET6_ADDRSTRLEN 40
889 #endif
890                     unsigned char addr[INET6_ADDRSTRLEN + 8];
891
892                     if (coap_print_addr(&obs->subscriber, addr, INET6_ADDRSTRLEN + 8))
893                         debug("** removed observer %s\n", addr);
894                 }
895 #endif
896                 coap_cancel_all_messages(context, &obs->subscriber, obs->token, obs->token_length);
897
898                 COAP_FREE_TYPE(subscription, obs);
899             }
900         }
901         break; /* break loop if observer was found */
902     }
903 }
904
905 void coap_handle_failed_notify(coap_context_t *context, const coap_address_t *peer,
906         const str *token)
907 {
908     coap_resource_t *r;
909
910 #ifndef WITH_CONTIKI
911
912 #ifdef COAP_RESOURCES_NOHASH
913     LL_FOREACH(context->resources, r)
914     {
915 #else
916     coap_resource_t *tmp;
917     HASH_ITER(hh, context->resources, r, tmp)
918     {
919 #endif
920         coap_remove_failed_observers(context, r, peer, token);
921     }
922 #else /* WITH_CONTIKI */
923     int i;
924
925     r = (coap_resource_t *)resource_storage.mem;
926     for (i = 0; i < resource_storage.num; ++i, ++r)
927     {
928         if (resource_storage.count[i])
929         {
930             coap_remove_failed_observers(context, r, peer, token);
931         }
932     }
933 #endif /* WITH_CONTIKI */
934 }
935 #endif /* WITHOUT_NOTIFY */