1 /* subscribe.c -- subscription handling for CoAP
2 * see draft-ietf-coap-observe-09
4 * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
6 * This file is part of the CoAP library libcoap. Please see
7 * README for terms of use.
12 #if defined(HAVE_ASSERT_H) && !defined(assert)
18 #ifdef HAVE_ARPA_INET_H
19 # include <arpa/inet.h>
22 /* #include "resource.h" */
27 #include "subscribe.h"
30 coap_subscription_init(coap_subscription_t *s) {
32 memset(s, 0, sizeof(coap_subscription_t));
36 #define HMASK (ULONG_MAX >> 1)
39 notify(coap_context_t *context, coap_resource_t *res,
40 coap_subscription_t *sub, unsigned int duration, int code) {
47 char addr[INET6_ADDRSTRLEN];
50 if ( !context || !res || !sub || !(pdu = coap_new_pdu()) )
53 pdu->hdr->type = COAP_MESSAGE_CON;
54 pdu->hdr->id = rand(); /* use a random transaction id */
55 pdu->hdr->code = code;
57 /* FIXME: content-type and data (how about block?) */
58 if (res->uri->host.length)
59 coap_add_option (pdu, COAP_OPTION_URI_HOST,
60 res->uri->host.length,
63 if (res->uri->path.length)
64 coap_add_option (pdu, COAP_OPTION_URI_PATH,
65 res->uri->path.length,
68 d = COAP_PSEUDOFP_ENCODE_8_4_DOWN(duration, ls);
70 coap_add_option ( pdu, COAP_OPTION_SUBSCRIPTION, 1, &d );
72 if (sub->token.length) {
73 coap_add_option (pdu, COAP_OPTION_TOKEN,
78 if (res->uri->query.length)
79 coap_add_option (pdu, COAP_OPTION_URI_QUERY,
80 res->uri->query.length,
84 length = (unsigned char *)pdu->hdr + COAP_MAX_PDU_SIZE - pdu->data;
86 res->data(res->uri, &ct, 0, pdu->data, &length, &finished);
87 pdu->length += length;
89 /* TODO: add block option if not finished */
90 /* TODO: add mediatype */
94 if ( inet_ntop(sub->subscriber.addr.sa.sa_family,
95 &sub->subscriber.addr, addr, sizeof(addr)) ) {
96 debug("*** notify for %s to [%s]\n", res->uri->path.s, addr);
99 if (pdu && coap_send_confirmed(context,
100 &sub->subscriber.addr.sa,
101 sub->subscriber.size, pdu)
102 == COAP_INVALID_TID) {
104 debug("coap_check_resource_list: error sending notification\n");
106 coap_delete_pdu(pdu);
112 coap_check_resource_list(coap_context_t *context) {
113 coap_resource_t *res, *tmp;
118 if ( !context || !context->resources /* || !context->subscribers */)
121 time(&now); /* FIXME: use coap_ticks() */
123 HASH_ITER(hh, context->resources, res, tmp) {
125 debug("FIXME: notify subscribers\n");
127 key = coap_uri_hash( COAP_RESOURCE(res)->uri ) ;
130 for (sub = context->subscriptions; sub; sub = sub->next) {
131 if ( COAP_SUBSCRIPTION(sub)->resource == key ) {
132 /* notify subscriber */
133 notify(context, COAP_RESOURCE(res), COAP_SUBSCRIPTION(sub),
134 COAP_SUBSCRIPTION(sub)->expires - now, COAP_RESPONSE_200);
139 COAP_RESOURCE(res)->dirty = 0;
147 coap_get_resource_from_key(coap_context_t *ctx, coap_key_t key) {
151 /* TODO: use hash table for resources with key to access */
152 for (node = ctx->resources; node; node = node->next) {
153 printf("check %ux\n", coap_uri_hash(COAP_RESOURCE(node)->uri));
154 if ( key == coap_uri_hash(COAP_RESOURCE(node)->uri) ) {
156 return COAP_RESOURCE(node);
161 printf("not found\n");
166 coap_get_resource(coap_context_t *ctx, coap_uri_t *uri) {
169 printf("search resource %ux", coap_uri_hash(uri));
170 for (i=0; i < uri->path.length; ++i) {
171 printf(" %02x", uri->path.s[i]);
175 return uri ? coap_get_resource_from_key(ctx, coap_uri_hash(uri)) : NULL;
180 coap_check_subscriptions(coap_context_t *context) {
184 char addr[INET6_ADDRSTRLEN];
192 node = context->subscriptions;
193 while ( node && COAP_SUBSCRIPTION(node)->expires < now ) {
195 if (inet_ntop(COAP_SUBSCRIPTION(node)->subscriber.addr.sa.sa_family,
196 &COAP_SUBSCRIPTION(node)->subscriber.addr,
197 addr, sizeof(addr))) {
199 debug("** removed expired subscription from [%s]\n", addr);
204 coap_get_resource_from_key(context, COAP_SUBSCRIPTION(node)->resource),
205 COAP_SUBSCRIPTION(node),
206 0, COAP_RESPONSE_400);
208 context->subscriptions = node->next;
210 node = context->subscriptions;
215 coap_free_resource(void *res) {
218 coap_free(((coap_resource_t *)res)->uri);
219 coap_delete_string(((coap_resource_t *)res)->name);
226 * Deletes the resource that is identified by key. Returns 1 if the resource was
227 * removed, 0 on error (e.g. if no such resource exists).
230 coap_delete_resource(coap_context_t *context, coap_key_t key) {
231 coap_list_t *prev, *node;
233 if (!context || key == COAP_INVALID_HASHKEY)
236 for (prev = NULL, node = context->resources; node;
237 prev = node, node = node->next) {
238 if (coap_uri_hash(COAP_RESOURCE(node)->uri) == key) {
240 debug("removed key %lu (%s)\n",key,COAP_RESOURCE(node)->uri->path.s);
243 context->resources = node->next;
245 prev->next = node->next;
255 coap_subscription_t *
256 coap_new_subscription(coap_context_t *context, const coap_uri_t *resource,
257 const struct sockaddr *addr, socklen_t addrlen, time_t expiry) {
258 coap_subscription_t *result;
260 if ( !context || !resource || !addr
261 || !(result = coap_malloc(sizeof(coap_subscription_t))))
264 result->resource = coap_uri_hash(resource);
265 result->expires = expiry;
266 memcpy(&result->subscriber.addr.sa, addr, addrlen);
268 memset(&result->token, 0, sizeof(str));
275 coap_list_push_first(coap_list_t **list, void *data, void (*delete_func)(void *) ) {
277 node = coap_new_listnode(data, delete_func);
278 if ( !node || !list )
292 _order_subscription(void *a, void *b) {
294 return a < b ? -1 : 1;
296 return ((coap_subscription_t *)a)->expires < ((coap_subscription_t *)b)->expires ? -1 : 1;
300 coap_subscription_hash(coap_subscription_t *subscription) {
302 return COAP_INVALID_HASHKEY;
304 return _hash2( subscription->resource, (unsigned char *)&subscription->subscriber,
305 sizeof(subscription->subscriber) );
309 coap_add_subscription(coap_context_t *context,
310 coap_subscription_t *subscription) {
312 if ( !context || !subscription )
313 return COAP_INVALID_HASHKEY;
315 if ( !(node = coap_new_listnode(subscription, NULL)) )
316 return COAP_INVALID_HASHKEY;
318 if ( !coap_insert(&context->subscriptions, node, _order_subscription ) ) {
319 coap_free( node ); /* do not call coap_delete(), so subscription object will survive */
320 return COAP_INVALID_HASHKEY;
323 return coap_subscription_hash(subscription);
326 coap_subscription_t *
327 coap_find_subscription(coap_context_t *context,
329 struct sockaddr *addr,
335 if (!context || !addr || hashkey == COAP_INVALID_HASHKEY)
338 /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
341 for (node = context->subscriptions; node; node = node->next) {
342 if (COAP_SUBSCRIPTION(node)->resource == hashkey) {
344 if (token) { /* do not proceed if tokens do not match */
345 if (token->length != COAP_SUBSCRIPTION(node)->token.length ||
346 memcmp(token->s, COAP_SUBSCRIPTION(node)->token.s,
351 if (subscriber->sin6_port == COAP_SUBSCRIPTION(node)->subscriber.sin6_port
352 && memcmp(&subscriber->sin6_addr,
353 &COAP_SUBSCRIPTION(node)->subscriber.sin6_addr,
354 sizeof(struct in6_addr)) == 0)
355 return COAP_SUBSCRIPTION(node);
363 coap_delete_subscription(coap_context_t *context,
365 struct sockaddr *addr) {
367 coap_list_t *prev, *node;
370 if (!context || !addr || key == COAP_INVALID_HASHKEY)
373 /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
376 for (prev = NULL, node = context->subscriptions; node;
377 prev = node, node = node->next) {
378 if (COAP_SUBSCRIPTION(node)->resource == key) {
379 if (subscriber->sin6_port == COAP_SUBSCRIPTION(node)->subscriber.sin6_port
380 && memcmp(&subscriber->sin6_addr,
381 &COAP_SUBSCRIPTION(node)->subscriber.sin6_addr,
382 sizeof(struct in6_addr)) == 0) {
385 context->subscriptions = node->next;
386 coap_free(COAP_SUBSCRIPTION(node)->token.s);
389 prev->next = node->next;
390 coap_free(COAP_SUBSCRIPTION(node)->token.s);