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"
29 void 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)
48 char addr[INET6_ADDRSTRLEN];
51 if ( !context || !res || !sub || !(pdu = coap_new_pdu()) )
54 pdu->hdr->coap_hdr_udp_t.type = COAP_MESSAGE_CON;
55 pdu->hdr->coap_hdr_udp_t.id = rand(); /* use a random transaction id */
56 pdu->hdr->coap_hdr_udp_t.code = code;
58 /* FIXME: content-type and data (how about block?) */
59 if (res->uri->host.length)
60 coap_add_option (pdu, COAP_OPTION_URI_HOST,
61 res->uri->host.length,
64 if (res->uri->path.length)
65 coap_add_option (pdu, COAP_OPTION_URI_PATH,
66 res->uri->path.length,
69 d = COAP_PSEUDOFP_ENCODE_8_4_DOWN(duration, ls);
71 coap_add_option ( pdu, COAP_OPTION_SUBSCRIPTION, 1, &d );
73 if (sub->token.length)
75 coap_add_option (pdu, COAP_OPTION_TOKEN,
80 if (res->uri->query.length)
81 coap_add_option (pdu, COAP_OPTION_URI_QUERY,
82 res->uri->query.length,
87 length = (unsigned char *)pdu->hdr + COAP_MAX_PDU_SIZE - pdu->data;
89 res->data(res->uri, &ct, 0, pdu->data, &length, &finished);
90 pdu->length += length;
92 /* TODO: add block option if not finished */
93 /* TODO: add mediatype */
97 if ( inet_ntop(sub->subscriber.addr.sa.sa_family,
98 &sub->subscriber.addr, addr, sizeof(addr)) )
100 debug("*** notify for %s to [%s]\n", res->uri->path.s, addr);
103 if (pdu && coap_send_confirmed(context,
104 &sub->subscriber.addr.sa,
105 sub->subscriber.size, pdu)
109 debug("coap_check_resource_list: error sending notification\n");
111 coap_delete_pdu(pdu);
117 coap_check_resource_list(coap_context_t *context)
119 coap_resource_t *res, *tmp;
124 if ( !context || !context->resources /* || !context->subscribers */)
127 time(&now); /* FIXME: use coap_ticks() */
129 HASH_ITER(hh, context->resources, res, tmp)
133 debug("FIXME: notify subscribers\n");
135 key = coap_uri_hash( COAP_RESOURCE(res)->uri );
138 for (sub = context->subscriptions; sub; sub = sub->next)
140 if ( COAP_SUBSCRIPTION(sub)->resource == key )
142 /* notify subscriber */
143 notify(context, COAP_RESOURCE(res), COAP_SUBSCRIPTION(sub),
144 COAP_SUBSCRIPTION(sub)->expires - now, COAP_RESPONSE_200);
149 COAP_RESOURCE(res)->dirty = 0;
157 coap_get_resource_from_key(coap_context_t *ctx, coap_key_t key)
163 /* TODO: use hash table for resources with key to access */
164 for (node = ctx->resources; node; node = node->next)
166 printf("check %ux\n", coap_uri_hash(COAP_RESOURCE(node)->uri));
167 if ( key == coap_uri_hash(COAP_RESOURCE(node)->uri) )
170 return COAP_RESOURCE(node);
175 printf("not found\n");
180 coap_get_resource(coap_context_t *ctx, coap_uri_t *uri)
184 printf("search resource %ux", coap_uri_hash(uri));
185 for (i=0; i < uri->path.length; ++i)
187 printf(" %02x", uri->path.s[i]);
191 return uri ? coap_get_resource_from_key(ctx, coap_uri_hash(uri)) : NULL;
196 coap_check_subscriptions(coap_context_t *context)
201 char addr[INET6_ADDRSTRLEN];
209 node = context->subscriptions;
210 while ( node && COAP_SUBSCRIPTION(node)->expires < now )
213 if (inet_ntop(COAP_SUBSCRIPTION(node)->subscriber.addr.sa.sa_family,
214 &COAP_SUBSCRIPTION(node)->subscriber.addr,
218 debug("** removed expired subscription from [%s]\n", addr);
223 coap_get_resource_from_key(context, COAP_SUBSCRIPTION(node)->resource),
224 COAP_SUBSCRIPTION(node),
225 0, COAP_RESPONSE_400);
227 context->subscriptions = node->next;
229 node = context->subscriptions;
234 coap_free_resource(void *res)
239 coap_free(((coap_resource_t *)res)->uri);
240 coap_delete_string(((coap_resource_t *)res)->name);
247 * Deletes the resource that is identified by key. Returns 1 if the resource was
248 * removed, 0 on error (e.g. if no such resource exists).
251 coap_delete_resource(coap_context_t *context, coap_key_t key)
253 coap_list_t *prev, *node;
255 if (!context || key == COAP_INVALID_HASHKEY)
258 for (prev = NULL, node = context->resources; node;
259 prev = node, node = node->next)
261 if (coap_uri_hash(COAP_RESOURCE(node)->uri) == key)
264 debug("removed key %lu (%s)\n",key,COAP_RESOURCE(node)->uri->path.s);
267 context->resources = node->next;
269 prev->next = node->next;
279 coap_subscription_t *
280 coap_new_subscription(coap_context_t *context, const coap_uri_t *resource,
281 const struct sockaddr *addr, socklen_t addrlen, time_t expiry)
283 coap_subscription_t *result;
285 if ( !context || !resource || !addr
286 || !(result = coap_malloc(sizeof(coap_subscription_t))))
289 result->resource = coap_uri_hash(resource);
290 result->expires = expiry;
291 memcpy(&result->subscriber.addr.sa, addr, addrlen);
293 memset(&result->token, 0, sizeof(str));
300 coap_list_push_first(coap_list_t **list, void *data, void (*delete_func)(void *) )
303 node = coap_new_listnode(data, delete_func);
304 if ( !node || !list )
321 _order_subscription(void *a, void *b)
324 return a < b ? -1 : 1;
326 return ((coap_subscription_t *)a)->expires < ((coap_subscription_t *)b)->expires ? -1 : 1;
330 coap_subscription_hash(coap_subscription_t *subscription)
333 return COAP_INVALID_HASHKEY;
335 return _hash2( subscription->resource, (unsigned char *)&subscription->subscriber,
336 sizeof(subscription->subscriber) );
340 coap_add_subscription(coap_context_t *context,
341 coap_subscription_t *subscription)
344 if ( !context || !subscription )
345 return COAP_INVALID_HASHKEY;
347 if ( !(node = coap_new_listnode(subscription, NULL)) )
348 return COAP_INVALID_HASHKEY;
350 if ( !coap_insert(&context->subscriptions, node, _order_subscription ) )
352 coap_free( node ); /* do not call coap_delete(), so subscription object will survive */
353 return COAP_INVALID_HASHKEY;
356 return coap_subscription_hash(subscription);
359 coap_subscription_t *
360 coap_find_subscription(coap_context_t *context,
362 struct sockaddr *addr,
369 if (!context || !addr || hashkey == COAP_INVALID_HASHKEY)
372 /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
375 for (node = context->subscriptions; node; node = node->next)
377 if (COAP_SUBSCRIPTION(node)->resource == hashkey)
381 { /* do not proceed if tokens do not match */
382 if (token->length != COAP_SUBSCRIPTION(node)->token.length ||
383 memcmp(token->s, COAP_SUBSCRIPTION(node)->token.s,
388 if (subscriber->sin6_port == COAP_SUBSCRIPTION(node)->subscriber.sin6_port
389 && memcmp(&subscriber->sin6_addr,
390 &COAP_SUBSCRIPTION(node)->subscriber.sin6_addr,
391 sizeof(struct in6_addr)) == 0)
392 return COAP_SUBSCRIPTION(node);
400 coap_delete_subscription(coap_context_t *context,
402 struct sockaddr *addr)
405 coap_list_t *prev, *node;
408 if (!context || !addr || key == COAP_INVALID_HASHKEY)
411 /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
414 for (prev = NULL, node = context->subscriptions; node;
415 prev = node, node = node->next)
417 if (COAP_SUBSCRIPTION(node)->resource == key)
419 if (subscriber->sin6_port == COAP_SUBSCRIPTION(node)->subscriber.sin6_port
420 && memcmp(&subscriber->sin6_addr,
421 &COAP_SUBSCRIPTION(node)->subscriber.sin6_addr,
422 sizeof(struct in6_addr)) == 0)
427 context->subscriptions = node->next;
428 coap_free(COAP_SUBSCRIPTION(node)->token.s);
433 prev->next = node->next;
434 coap_free(COAP_SUBSCRIPTION(node)->token.s);