iotivity 0.9.0
[platform/upstream/iotivity.git] / resource / csdk / libcoap-4.1.1 / subscribe.c
1 /* subscribe.c -- subscription handling for CoAP
2  *                see draft-ietf-coap-observe-09
3  *
4  * Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>
5  *
6  * This file is part of the CoAP library libcoap. Please see
7  * README for terms of use. 
8  */
9
10 #include "config.h"
11
12 #if defined(HAVE_ASSERT_H) && !defined(assert)
13 # include <assert.h>
14 #endif
15
16 #include <stdio.h>
17 #include <limits.h>
18 #ifdef HAVE_ARPA_INET_H
19 # include <arpa/inet.h>
20 #endif
21
22 /* #include "resource.h" */
23
24 #include "mem.h"
25 #include "encode.h"
26 #include "debug.h"
27 #include "subscribe.h"
28
29 void
30 coap_subscription_init(coap_subscription_t *s) {
31   assert(s);
32   memset(s, 0, sizeof(coap_subscription_t));
33 }
34
35 #if 0
36 #define HMASK (ULONG_MAX >> 1)
37
38 void
39 notify(coap_context_t *context, coap_resource_t *res,
40        coap_subscription_t *sub, unsigned int duration, int code) {
41 #if 0
42   coap_pdu_t *pdu;
43   int ls, finished=0;
44   unsigned char ct, d;
45   unsigned int length;
46 #ifndef NDEBUG
47   char addr[INET6_ADDRSTRLEN];
48 #endif
49
50   if ( !context || !res || !sub || !(pdu = coap_new_pdu()) )
51     return;
52
53   pdu->hdr->type = COAP_MESSAGE_CON;
54   pdu->hdr->id = rand();        /* use a random transaction id */
55   pdu->hdr->code = code;
56
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,
61                      res->uri->host.s );
62
63   if (res->uri->path.length)
64     coap_add_option (pdu, COAP_OPTION_URI_PATH,
65                      res->uri->path.length,
66                      res->uri->path.s);
67
68   d = COAP_PSEUDOFP_ENCODE_8_4_DOWN(duration, ls);
69
70   coap_add_option ( pdu, COAP_OPTION_SUBSCRIPTION, 1, &d );
71
72   if (sub->token.length) {
73     coap_add_option (pdu, COAP_OPTION_TOKEN,
74                      sub->token.length,
75                      sub->token.s);
76   }
77
78   if (res->uri->query.length)
79     coap_add_option (pdu, COAP_OPTION_URI_QUERY,
80                      res->uri->query.length,
81                      res->uri->query.s );
82
83   if (res->data) {
84     length = (unsigned char *)pdu->hdr + COAP_MAX_PDU_SIZE - pdu->data;
85     ct = res->mediatype;
86     res->data(res->uri, &ct, 0, pdu->data, &length, &finished);
87     pdu->length += length;
88
89     /* TODO: add block option if not finished */
90     /* TODO: add mediatype */
91   }
92
93 #ifndef NDEBUG
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);
97   }
98 #endif
99   if (pdu && coap_send_confirmed(context,
100                                  &sub->subscriber.addr.sa,
101                                  sub->subscriber.size, pdu) 
102       == COAP_INVALID_TID) {
103 #ifndef NDEBUG
104     debug("coap_check_resource_list: error sending notification\n");
105 #endif
106     coap_delete_pdu(pdu);
107   }
108 #endif
109 }
110
111 void
112 coap_check_resource_list(coap_context_t *context) {
113   coap_resource_t *res, *tmp;
114   coap_list_t *sub;
115   coap_key_t key;
116   time_t now;
117
118   if ( !context || !context->resources /* || !context->subscribers */)
119     return;
120
121   time(&now);                   /* FIXME: use coap_ticks() */
122
123   HASH_ITER(hh, context->resources, res, tmp) {
124     if (res->dirty) {
125       debug("FIXME: notify subscribers\n");
126 #if 0
127       key = coap_uri_hash( COAP_RESOURCE(res)->uri ) ;
128
129       /* is subscribed? */
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);
135         }
136
137       }
138
139       COAP_RESOURCE(res)->dirty = 0;
140 #endif
141     }
142   }
143 }
144
145 #if 0
146 coap_resource_t *
147 coap_get_resource_from_key(coap_context_t *ctx, coap_key_t key) {
148   coap_list_t *node;
149
150   if (ctx) {
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) ) {
155         printf("found\n");
156         return COAP_RESOURCE(node);
157       }
158     }
159   }
160
161   printf("not found\n");
162   return NULL;
163 }
164
165 coap_resource_t *
166 coap_get_resource(coap_context_t *ctx, coap_uri_t *uri) {
167 #ifndef NDEBUG
168   int i;
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]);
172   }
173   printf("\n");
174 #endif
175   return uri ? coap_get_resource_from_key(ctx, coap_uri_hash(uri)) : NULL;
176 }
177 #endif
178
179 void
180 coap_check_subscriptions(coap_context_t *context) {
181   time_t now;
182   coap_list_t *node;
183 #ifndef NDEBUG
184   char addr[INET6_ADDRSTRLEN];
185 #endif
186
187   if ( !context )
188     return;
189
190   time(&now);
191
192   node = context->subscriptions;
193   while ( node && COAP_SUBSCRIPTION(node)->expires < now ) {
194 #ifndef NDEBUG
195     if (inet_ntop(COAP_SUBSCRIPTION(node)->subscriber.addr.sa.sa_family,
196                   &COAP_SUBSCRIPTION(node)->subscriber.addr,
197                   addr, sizeof(addr))) {
198       
199       debug("** removed expired subscription from [%s]\n", addr);
200     }
201 #endif
202 #if 0
203     notify(context,
204            coap_get_resource_from_key(context, COAP_SUBSCRIPTION(node)->resource),
205            COAP_SUBSCRIPTION(node),
206            0, COAP_RESPONSE_400);
207 #endif
208     context->subscriptions = node->next;
209     coap_delete(node);
210     node = context->subscriptions;
211   }
212 }
213
214 void
215 coap_free_resource(void *res) {
216   if ( res ) {
217 #if 0
218     coap_free(((coap_resource_t *)res)->uri);
219     coap_delete_string(((coap_resource_t *)res)->name);
220 #endif
221   }
222 }
223
224 #if 0
225 /**
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).
228  */
229 int
230 coap_delete_resource(coap_context_t *context, coap_key_t key) {
231   coap_list_t *prev, *node;
232
233   if (!context || key == COAP_INVALID_HASHKEY)
234     return 0;
235
236   for (prev = NULL, node = context->resources; node;
237        prev = node, node = node->next) {
238     if (coap_uri_hash(COAP_RESOURCE(node)->uri) == key) {
239 #ifndef NDEBUG
240       debug("removed key %lu (%s)\n",key,COAP_RESOURCE(node)->uri->path.s);
241 #endif
242       if (!prev)
243         context->resources = node->next;
244       else
245         prev->next = node->next;
246
247       coap_delete(node);
248       return 1;
249     }
250   }
251   return 0;
252 }
253 #endif
254
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;
259
260   if ( !context || !resource || !addr
261        || !(result = coap_malloc(sizeof(coap_subscription_t))))
262     return NULL;
263
264   result->resource = coap_uri_hash(resource);
265   result->expires = expiry;
266   memcpy(&result->subscriber.addr.sa, addr, addrlen);
267
268   memset(&result->token, 0, sizeof(str));
269
270   return result;
271
272 }
273
274 coap_list_t *
275 coap_list_push_first(coap_list_t **list, void *data, void (*delete_func)(void *) ) {
276   coap_list_t *node;
277   node = coap_new_listnode(data, delete_func);
278   if ( !node || !list )
279     return NULL;
280
281   if ( !*list ) {
282     *list = node;
283   } else {
284     node->next = *list;
285     *list = node;
286   }
287
288   return node;
289 }
290
291 int
292 _order_subscription(void *a, void *b) {
293   if ( !a || !b )
294     return a < b ? -1 : 1;
295
296   return ((coap_subscription_t *)a)->expires < ((coap_subscription_t *)b)->expires ? -1 : 1;
297 }
298
299 coap_key_t
300 coap_subscription_hash(coap_subscription_t *subscription) {
301   if ( !subscription )
302     return COAP_INVALID_HASHKEY;
303
304   return _hash2( subscription->resource, (unsigned char *)&subscription->subscriber,
305                  sizeof(subscription->subscriber) );
306 }
307
308 coap_key_t
309 coap_add_subscription(coap_context_t *context,
310                       coap_subscription_t *subscription) {
311   coap_list_t *node;
312   if ( !context || !subscription )
313     return COAP_INVALID_HASHKEY;
314
315   if ( !(node = coap_new_listnode(subscription, NULL)) )
316     return COAP_INVALID_HASHKEY;
317
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;
321   }
322
323   return coap_subscription_hash(subscription);
324 }
325
326 coap_subscription_t *
327 coap_find_subscription(coap_context_t *context,
328                        coap_key_t hashkey,
329                        struct sockaddr *addr,
330                        str *token) {
331 #if 0
332   coap_list_t *node;
333 #endif
334
335   if (!context || !addr || hashkey == COAP_INVALID_HASHKEY)
336     return NULL;
337
338   /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
339
340 #if 0
341   for (node = context->subscriptions; node; node = node->next) {
342     if (COAP_SUBSCRIPTION(node)->resource == hashkey) {
343
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,
347                    token->length) != 0)
348           continue;
349       }
350
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);
356     }
357   }
358 #endif
359   return NULL;
360 }
361
362 int
363 coap_delete_subscription(coap_context_t *context,
364                          coap_key_t key,
365                          struct sockaddr *addr) {
366 #if 0
367   coap_list_t *prev, *node;
368 #endif
369
370   if (!context || !addr || key == COAP_INVALID_HASHKEY)
371     return 0;
372
373   /* FIXME: I do not like the way subscriptions work right now. To be fixed. */
374
375 #if 0
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) {
383
384         if (!prev) {
385           context->subscriptions = node->next;
386           coap_free(COAP_SUBSCRIPTION(node)->token.s);
387           coap_delete(node);
388         } else {
389           prev->next = node->next;
390           coap_free(COAP_SUBSCRIPTION(node)->token.s);
391           coap_delete(node);
392         }
393         return 1;
394       }
395     }
396   }
397 #endif
398
399   return 0;
400 }
401 #endif