1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@CFILE nea.c Nokia Event Client API agent implementation.
26 *
27 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
28 *
29 * @date Created: Wed Feb 14 18:32:58 2001 ppessi
30 */
31
32 #include "config.h"
33
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <assert.h>
40
41 #include <sofia-sip/su_tagarg.h>
42 #include <sofia-sip/su_string.h>
43
44 #include <sofia-sip/sip.h>
45 #include <sofia-sip/sip_header.h>
46 #include <sofia-sip/sip_util.h>
47 #include <sofia-sip/sip_status.h>
48
49 #define SU_TIMER_ARG_T struct nea_s
50 #define NTA_LEG_MAGIC_T struct nea_s
51 #define NTA_OUTGOING_MAGIC_T struct nea_s
52
53 #define NEA_TIMER_DELTA 2 /* time to resubscribe without expiration */
54 #define EXPIRES_DEFAULT 3600
55
56 #include <sofia-sip/su_wait.h>
57
58 #include "sofia-sip/nea.h"
59
60 struct nea_s {
61 su_home_t nea_home[1];
62 su_timer_t *nea_timer;
63
64 nta_agent_t *nea_agent;
65 nta_leg_t *nea_leg;
66 nta_outgoing_t *nea_oreq; /**< Outstanding request */
67 sip_to_t *nea_to; /**< The other end of subscription :) */
68 nea_notify_f nea_callback; /**< Notify callback */
69 nea_magic_t *nea_context; /**< Application context */
70
71 sip_contact_t *nea_contact; /**< */
72 sip_expires_t *nea_expires; /**< Proposed expiration time */
73
74 nea_state_t nea_state; /**< State of our subscription */
75 sip_time_t nea_deadline; /**< When our subscription expires */
76 tagi_t *nea_args;
77
78 unsigned nea_dialog : 1; /**< Dialog has been established */
79 unsigned nea_notify_received : 1;
80 unsigned nea_terminating : 1;
81 unsigned nea_strict_3265 : 1; /**< Strict mode */
82 };
83
84 int details = 0;
85
86 static int process_nea_request(nea_t *nea,
87 nta_leg_t *leg,
88 nta_incoming_t *ireq,
89 sip_t const *sip);
90
91 static int handle_notify(nta_leg_magic_t *lmagic,
92 nta_leg_t *leg,
93 nta_incoming_t *ireq,
94 sip_t const *sip);
95
96 static int response_to_subscribe(nea_t *nea,
97 nta_outgoing_t *req,
98 sip_t const *sip);
99
100 static int response_to_unsubscribe(nea_t *nea,
101 nta_outgoing_t *req,
102 sip_t const *sip);
103
104 static void nea_expires_renew(su_root_magic_t *magic,
105 su_timer_t *timer,
106 nea_t *nea);
107
108 /* ---------------------------------------------------------- */
109
110 /** Create a event watcher object.
111 *
112 */
nea_create(nta_agent_t * agent,su_root_t * root,nea_notify_f no_callback,nea_magic_t * context,tag_type_t tag,tag_value_t value,...)113 nea_t *nea_create(nta_agent_t *agent,
114 su_root_t *root,
115 nea_notify_f no_callback,
116 nea_magic_t *context,
117 tag_type_t tag, tag_value_t value, ...)
118 {
119 nea_t *nea = NULL;
120 ta_list ta;
121 int have_from, have_to, have_contact;
122 sip_expires_t const *expires = NULL;
123 char const *expires_str = NULL;
124 sip_method_t method = sip_method_subscribe;
125 char const *SUBSCRIBE = "SUBSCRIBE";
126 char const *method_name = SUBSCRIBE;
127
128 ta_start(ta, tag, value);
129
130 have_to =
131 tl_find(ta_args(ta), siptag_to) || tl_find(ta_args(ta), siptag_to_str);
132 have_from =
133 tl_find(ta_args(ta), siptag_from) || tl_find(ta_args(ta), siptag_from_str);
134 have_contact =
135 tl_find(ta_args(ta), siptag_contact) ||
136 tl_find(ta_args(ta), siptag_contact_str);
137
138 if (have_to && (nea = su_home_new(sizeof(nea_t)))) {
139 su_home_t *home = nea->nea_home;
140 sip_contact_t *m = nta_agent_contact(agent);
141 sip_from_t *from;
142 sip_to_t const *to;
143 int strict = 0;
144
145 nea->nea_agent = agent;
146 nea->nea_callback = no_callback;
147 nea->nea_context = context;
148
149 if (!have_from)
150 from = sip_from_create(home, (url_string_t*)m->m_url);
151 else
152 from = NULL;
153
154 nea->nea_args = tl_tlist(home,
155 TAG_IF(!have_contact, SIPTAG_CONTACT(m)),
156 ta_tags(ta));
157
158 /* Get and remove Expires header from tag list */
159 tl_gets(nea->nea_args,
160 SIPTAG_EXPIRES_REF(expires),
161 SIPTAG_EXPIRES_STR_REF(expires_str),
162 SIPTAG_TO_REF(to),
163 NEATAG_STRICT_3265_REF(strict),
164 NTATAG_METHOD_REF(method_name),
165 TAG_END());
166
167 nea->nea_strict_3265 = strict;
168
169 if (to)
170 nea->nea_to = sip_to_dup(home, to);
171
172 if (expires)
173 nea->nea_expires = sip_expires_dup(home, expires);
174 else if (expires_str)
175 nea->nea_expires = sip_expires_make(home, expires_str);
176 else
177 nea->nea_expires = sip_expires_create(home, EXPIRES_DEFAULT);
178
179 tl_tremove(nea->nea_args,
180 SIPTAG_EXPIRES(0),
181 SIPTAG_EXPIRES_STR(0),
182 TAG_END());
183
184 if (method_name != SUBSCRIBE)
185 method = sip_method_code(method_name);
186
187 if (method != sip_method_invalid)
188 /* Create the timer object */
189 nea->nea_timer = su_timer_create(su_root_task(root), 0L);
190
191 if (nea->nea_timer) {
192 /* Create leg for NOTIFY requests */
193 nea->nea_leg = nta_leg_tcreate(nea->nea_agent,
194 process_nea_request, nea,
195 TAG_IF(!have_from, SIPTAG_FROM(from)),
196 TAG_NEXT(nea->nea_args));
197
198 if (nea->nea_leg) {
199 nta_leg_tag(nea->nea_leg, NULL);
200 nea->nea_oreq = nta_outgoing_tcreate(nea->nea_leg,
201 response_to_subscribe, nea,
202 NULL,
203 method, method_name,
204 NULL,
205 SIPTAG_EXPIRES(nea->nea_expires),
206 TAG_NEXT(nea->nea_args));
207 }
208 }
209
210 if (!nea->nea_leg ||
211 !nea->nea_oreq ||
212 !nea->nea_timer)
213 nea_destroy(nea), nea = NULL;
214 }
215
216 ta_end(ta);
217 return nea;
218 }
219
220
nea_update(nea_t * nea,tag_type_t tag,tag_value_t value,...)221 int nea_update(nea_t *nea,
222 tag_type_t tag,
223 tag_value_t value,
224 ...)
225 {
226 ta_list ta;
227 sip_expires_t const *expires = NULL;
228 sip_payload_t const *pl = NULL;
229 sip_content_type_t const *ct = NULL;
230 char const *cts = NULL;
231
232 /* char const *expires_str = NULL; */
233 su_home_t *home = nea->nea_home;
234
235 /* XXX - hack, previous request still waiting for response */
236 if (!nea->nea_leg || nea->nea_oreq)
237 return -1;
238
239 ta_start(ta, tag, value);
240
241 tl_gets(ta_args(ta),
242 SIPTAG_CONTENT_TYPE_REF(ct),
243 SIPTAG_CONTENT_TYPE_STR_REF(cts),
244 SIPTAG_PAYLOAD_REF(pl),
245 SIPTAG_EXPIRES_REF(expires),
246 TAG_NULL());
247
248 if (!pl || (!ct && !cts)) {
249 ta_end(ta);
250 return -1;
251 }
252
253 tl_tremove(nea->nea_args,
254 SIPTAG_CONTENT_TYPE(0),
255 SIPTAG_CONTENT_TYPE_STR(0),
256 SIPTAG_PAYLOAD(0),
257 SIPTAG_PAYLOAD_STR(0),
258 TAG_END());
259
260 su_free(home, nea->nea_expires);
261
262 if (expires)
263 nea->nea_expires = sip_expires_dup(home, expires);
264 else
265 nea->nea_expires = sip_expires_create(home, EXPIRES_DEFAULT);
266
267 /* nta_leg_tag(nea->nea_leg, NULL); */
268 nea->nea_oreq = nta_outgoing_tcreate(nea->nea_leg,
269 response_to_subscribe, nea,
270 NULL,
271 SIP_METHOD_SUBSCRIBE,
272 NULL,
273 SIPTAG_TO(nea->nea_to),
274 SIPTAG_PAYLOAD(pl),
275 TAG_IF(ct, SIPTAG_CONTENT_TYPE(ct)),
276 TAG_IF(cts, SIPTAG_CONTENT_TYPE_STR(cts)),
277 SIPTAG_EXPIRES(nea->nea_expires),
278 TAG_NEXT(nea->nea_args));
279
280 ta_end(ta);
281
282 if (!nea->nea_oreq)
283 return -1;
284
285 return 0;
286 }
287
288
289 /** Unsubscribe the agent. */
nea_end(nea_t * nea)290 void nea_end(nea_t *nea)
291 {
292 if (nea == NULL)
293 return;
294
295 nea->nea_terminating = 1;
296
297 su_timer_destroy(nea->nea_timer), nea->nea_timer = NULL;
298
299 if (nea->nea_leg && nea->nea_deadline) {
300 nea->nea_oreq =
301 nta_outgoing_tcreate(nea->nea_leg,
302 response_to_unsubscribe,
303 nea,
304 NULL,
305 SIP_METHOD_SUBSCRIBE,
306 NULL,
307 SIPTAG_EXPIRES_STR("0"),
308 TAG_NEXT(nea->nea_args));
309 }
310 }
311
nea_destroy(nea_t * nea)312 void nea_destroy(nea_t *nea)
313 {
314 if (nea == NULL)
315 return;
316
317 if (nea->nea_oreq)
318 nta_outgoing_destroy(nea->nea_oreq), nea->nea_oreq = NULL;
319
320 if (nea->nea_leg)
321 nta_leg_destroy(nea->nea_leg), nea->nea_leg = NULL;
322
323 if (nea->nea_timer) {
324 su_timer_reset(nea->nea_timer);
325 su_timer_destroy(nea->nea_timer), nea->nea_timer = NULL;
326 }
327
328 su_free(NULL, nea);
329 }
330
331
332 /* Function called by NTA to handle incoming requests belonging to the leg */
process_nea_request(nea_t * nea,nta_leg_t * leg,nta_incoming_t * ireq,sip_t const * sip)333 int process_nea_request(nea_t *nea,
334 nta_leg_t *leg,
335 nta_incoming_t *ireq,
336 sip_t const *sip)
337 {
338
339 switch (sip->sip_request->rq_method) {
340 case sip_method_notify:
341 return handle_notify(nea, leg, ireq, sip);
342 case sip_method_ack:
343 return 400;
344 default:
345 nta_incoming_treply(ireq, SIP_405_METHOD_NOT_ALLOWED,
346 SIPTAG_ALLOW_STR("NOTIFY"), TAG_END());
347 return 405;
348 }
349 }
350
351
352 /* Callback function to handle subscription requests */
response_to_subscribe(nea_t * nea,nta_outgoing_t * oreq,sip_t const * sip)353 int response_to_subscribe(nea_t *nea,
354 nta_outgoing_t *oreq,
355 sip_t const *sip)
356 {
357 int status = sip->sip_status->st_status;
358 int error = status >= 300;
359
360 if (status >= 200 && oreq == nea->nea_oreq)
361 nea->nea_oreq = NULL;
362
363 nea->nea_callback(nea, nea->nea_context, sip);
364
365 if (status < 200)
366 return 0;
367
368 nea->nea_oreq = NULL;
369
370 if (status < 300) {
371 sip_time_t now = sip_now();
372 if (!nea->nea_notify_received) {
373 nea->nea_deadline = now +
374 sip_contact_expires(NULL, sip->sip_expires, sip->sip_date,
375 EXPIRES_DEFAULT, now);
376 if (sip->sip_to->a_tag && !nea->nea_dialog) {
377 nea->nea_dialog = 1;
378 nta_leg_rtag(nea->nea_leg, sip->sip_to->a_tag);
379 nta_leg_client_route(nea->nea_leg,
380 sip->sip_record_route, sip->sip_contact);
381 }
382 }
383 }
384 else {
385 nea->nea_deadline = 0;
386 nea->nea_state = nea_terminated;
387 if (status == 301 || status == 302 || status == 305) {
388 sip_contact_t *m;
389
390 for (m = sip->sip_contact; m; m = m->m_next) {
391 if (m->m_url->url_type == url_sip ||
392 m->m_url->url_type == url_sips)
393 break;
394 }
395
396 if (m) {
397 url_string_t const *proxy, *url;
398 if (status == 305)
399 url = NULL, proxy = (url_string_t *)m->m_url;
400 else
401 url = (url_string_t *)m->m_url, proxy = NULL;
402
403 nea->nea_oreq =
404 nta_outgoing_tcreate(nea->nea_leg,
405 response_to_subscribe,
406 nea,
407 proxy,
408 SIP_METHOD_SUBSCRIBE,
409 url,
410 SIPTAG_EXPIRES(nea->nea_expires),
411 TAG_NEXT(nea->nea_args));
412 }
413 } else if (status == 423 && sip->sip_min_expires) {
414 unsigned value = sip->sip_min_expires->me_delta;
415 su_free(nea->nea_home, nea->nea_expires);
416 nea->nea_expires = sip_expires_format(nea->nea_home, "%u", value);
417
418 nea->nea_oreq =
419 nta_outgoing_tcreate(nea->nea_leg,
420 response_to_subscribe,
421 nea,
422 NULL,
423 SIP_METHOD_SUBSCRIBE,
424 NULL,
425 SIPTAG_EXPIRES(nea->nea_expires),
426 TAG_NEXT(nea->nea_args));
427 }
428 }
429
430 if (status >= 200)
431 nta_outgoing_destroy(oreq);
432
433 if (nea->nea_oreq || !error) {
434 su_time_t now = su_now();
435 now.tv_sec = nea->nea_deadline;
436 su_timer_set_at(nea->nea_timer,
437 nea_expires_renew,
438 nea,
439 now);
440 }
441 else
442 nea->nea_callback(nea, nea->nea_context, NULL);
443
444 return 0;
445 }
446
447
response_to_unsubscribe(nea_t * nea,nta_outgoing_t * orq,sip_t const * sip)448 int response_to_unsubscribe(nea_t *nea,
449 nta_outgoing_t *orq,
450 sip_t const *sip)
451 {
452 int status = sip->sip_status->st_status;
453
454 nea->nea_callback(nea, nea->nea_context, sip);
455
456 if (status >= 200)
457 nta_outgoing_destroy(orq), nea->nea_oreq = NULL;
458 if (status >= 300)
459 nea->nea_callback(nea, nea->nea_context, NULL);
460
461 return 0;
462 }
463
464 /** handle notifications */
handle_notify(nea_t * nea,nta_leg_t * leg,nta_incoming_t * irq,sip_t const * sip)465 int handle_notify(nea_t *nea,
466 nta_leg_t *leg,
467 nta_incoming_t *irq,
468 sip_t const *sip)
469 {
470 sip_subscription_state_t *ss = sip->sip_subscription_state;
471 sip_subscription_state_t ss0[1];
472 char expires[32];
473
474 if (nea->nea_strict_3265) {
475 char const *phrase = NULL;
476
477 if (ss == NULL)
478 phrase = "NOTIFY Has No Subscription-State Header";
479 else if (sip->sip_event == NULL)
480 phrase = "Event Header Missing";
481
482 if (phrase) {
483 nta_incoming_treply(irq, 400, phrase, TAG_END());
484 nta_incoming_destroy(irq);
485 nta_leg_destroy(nea->nea_leg), nea->nea_leg = NULL;
486 nea->nea_state = nea_terminated;
487 nea->nea_callback(nea, nea->nea_context, NULL);
488 return 0;
489 }
490 }
491
492 if (ss == NULL) {
493 /* Do some compatibility stuff here */
494 unsigned long delta = 3600;
495
496 sip_subscription_state_init(ss = ss0);
497
498 if (sip->sip_expires)
499 delta = sip->sip_expires->ex_delta;
500
501 if (delta == 0)
502 ss->ss_substate = "terminated";
503 else
504 ss->ss_substate = "active";
505
506 if (delta > 0) {
507 snprintf(expires, sizeof expires, "%lu", delta);
508 ss->ss_expires = expires;
509 }
510 }
511
512 if (!nea->nea_dialog) {
513 nea->nea_dialog = 1;
514 nta_leg_rtag(nea->nea_leg, sip->sip_from->a_tag);
515 nta_leg_server_route(nea->nea_leg,
516 sip->sip_record_route, sip->sip_contact);
517 }
518
519 nea->nea_notify_received = 1;
520 nea->nea_callback(nea, nea->nea_context, sip);
521
522 if (su_casematch(ss->ss_substate, "terminated")) {
523 nta_leg_destroy(nea->nea_leg), nea->nea_leg = NULL;
524 nea->nea_state = nea_terminated;
525
526 if (su_casematch(ss->ss_reason, "deactivated")) {
527 nea->nea_state = nea_embryonic;
528 nea->nea_deadline = sip_now();
529 } else if (su_casematch(ss->ss_reason, "probation")) {
530 sip_time_t retry = sip_now() + NEA_TIMER_DELTA;
531
532 if (ss->ss_retry_after)
533 retry += strtoul(ss->ss_retry_after, NULL, 10);
534 else
535 retry += NEA_TIMER_DELTA;
536
537 nea->nea_state = nea_embryonic;
538 nea->nea_deadline = retry;
539 } else {
540 nea->nea_deadline = 0;
541 nea->nea_callback(nea, nea->nea_context, NULL);
542 return 200;
543 }
544 }
545 else if (su_casematch(ss->ss_substate, "pending"))
546 nea->nea_state = nea_pending;
547 else if (su_casematch(ss->ss_substate, "active"))
548 nea->nea_state = nea_active;
549 else
550 nea->nea_state = nea_extended;
551
552 if (nea->nea_state != nea_embryonic && ss->ss_expires) {
553 unsigned retry = strtoul(ss->ss_expires, NULL, 10);
554 if (retry > 60) retry -= 30; else retry /= 2;
555 nea->nea_deadline = sip_now() + retry;
556 }
557
558 {
559 su_time_t now = su_now();
560 now.tv_sec = nea->nea_deadline;
561 su_timer_set_at(nea->nea_timer,
562 nea_expires_renew,
563 nea,
564 now);
565 }
566
567 return 200;
568 }
569
nea_expires_renew(su_root_magic_t * magic,su_timer_t * timer,nea_t * nea)570 void nea_expires_renew(su_root_magic_t *magic,
571 su_timer_t *timer,
572 nea_t *nea)
573 {
574 sip_time_t now = sip_now();
575
576 /* re-subscribe if expires soon */
577 if (nea->nea_state == nea_terminated ||
578 nea->nea_deadline == 0 ||
579 nea->nea_deadline > now + NEA_TIMER_DELTA)
580 return;
581
582 if (!nea->nea_notify_received) /* Hmph. */
583 return;
584
585 nea->nea_notify_received = 0;
586
587 nea->nea_oreq =
588 nta_outgoing_tcreate(nea->nea_leg,
589 response_to_subscribe,
590 nea,
591 NULL,
592 SIP_METHOD_SUBSCRIBE,
593 NULL,
594 SIPTAG_EXPIRES(nea->nea_expires),
595 TAG_NEXT(nea->nea_args));
596
597 return;
598 }
599