blob: 8218c76ec07068356aac10335295320267b6490f [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsip-simple/evsub.h>
20#include <pjsip-simple/evsub_msg.h>
21#include <pjsip-simple/errno.h>
22#include <pjsip/sip_errno.h>
23#include <pjsip/sip_module.h>
24#include <pjsip/sip_endpoint.h>
25#include <pjsip/sip_dialog.h>
26#include <pjsip/sip_auth.h>
27#include <pjsip/sip_transaction.h>
28#include <pjsip/sip_event.h>
29#include <pj/assert.h>
30#include <pj/guid.h>
31#include <pj/log.h>
32#include <pj/os.h>
33#include <pj/pool.h>
34#include <pj/string.h>
35
36
37#define THIS_FILE "evsub.c"
38
39/*
40 * Global constant
41 */
42
43/* Let's define this enum, so that it'll trigger compilation error
44 * when somebody define the same enum in sip_msg.h
45 */
46enum
47{
48 PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD,
49 PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD
50};
51
52const pjsip_method pjsip_subscribe_method =
53{
54 PJSIP_SUBSCRIBE_METHOD,
55 { "SUBSCRIBE", 9 }
56};
57
58const pjsip_method pjsip_notify_method =
59{
60 PJSIP_NOTIFY_METHOD,
61 { "NOTIFY", 6 }
62};
63
64/*
65 * Static prototypes.
66 */
67static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*);
68static pj_status_t mod_evsub_unload(void);
69
70
71/*
72 * State names.
73 */
74static pj_str_t evsub_state_names[] =
75{
76 { "NULL", 4},
77 { "SENT", 4},
78 { "ACCEPTED", 8},
79 { "PENDING", 7},
80 { "ACTIVE", 6},
81 { "TERMINATED", 10},
82 { "UNKNOWN", 7}
83};
84
85/*
86 * Timer constants.
87 */
88
89/* Number of seconds to send SUBSCRIBE before the actual expiration */
90#define TIME_UAC_REFRESH 5
91
92/* Time to wait for the final NOTIFY after sending unsubscription */
93#define TIME_UAC_TERMINATE 5
94
95/* If client responds NOTIFY with non-2xx final response (such as 401),
96 * wait for this seconds for further NOTIFY, otherwise client will
97 * unsubscribe
98 */
99#define TIME_UAC_WAIT_NOTIFY 5
100
101
102/*
103 * Timer id
104 */
105enum timer_id
106{
107 /* No timer. */
108 TIMER_TYPE_NONE,
109
110 /* Time to refresh client subscription.
111 * The action is to call on_client_refresh() callback.
112 */
113 TIMER_TYPE_UAC_REFRESH,
114
115 /* UAS timeout after to subscription refresh.
116 * The action is to call on_server_timeout() callback.
117 */
118 TIMER_TYPE_UAS_TIMEOUT,
119
120 /* UAC waiting for final NOTIFY after unsubscribing
121 * The action is to terminate.
122 */
123 TIMER_TYPE_UAC_TERMINATE,
124
125 /* UAC waiting for further NOTIFY after sending non-2xx response to
126 * NOTIFY. The action is to unsubscribe.
127 */
128 TIMER_TYPE_UAC_WAIT_NOTIFY,
129
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000130 /* Max nb of timer types. */
131 TIMER_TYPE_MAX
Benny Prijono834aee32006-02-19 01:38:06 +0000132};
133
134static const char *timer_names[] =
135{
136 "None",
137 "UAC_REFRESH",
138 "UAS_TIMEOUT"
139 "UAC_TERMINATE",
140 "UAC_WAIT_NOTIFY",
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000141 "INVALID_TIMER"
Benny Prijono834aee32006-02-19 01:38:06 +0000142};
143
144/*
145 * Definition of event package.
146 */
147struct evpkg
148{
149 PJ_DECL_LIST_MEMBER(struct evpkg);
150
151 pj_str_t pkg_name;
152 pjsip_module *pkg_mod;
153 unsigned pkg_expires;
154 pjsip_accept_hdr *pkg_accept;
155};
156
157
158/*
159 * Event subscription module (mod-evsub).
160 */
161static struct mod_evsub
162{
163 pjsip_module mod;
164 pj_pool_t *pool;
165 pjsip_endpoint *endpt;
166 struct evpkg pkg_list;
167 pjsip_allow_events_hdr *allow_events_hdr;
168
169} mod_evsub =
170{
171 {
Benny Prijono2f8992b2006-02-25 21:16:36 +0000172 NULL, NULL, /* prev, next. */
173 { "mod-evsub", 9 }, /* Name. */
174 -1, /* Id */
175 PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */
176 NULL, /* load() */
177 NULL, /* start() */
178 NULL, /* stop() */
179 &mod_evsub_unload, /* unload() */
180 NULL, /* on_rx_request() */
181 NULL, /* on_rx_response() */
182 NULL, /* on_tx_request. */
183 NULL, /* on_tx_response() */
184 &mod_evsub_on_tsx_state, /* on_tsx_state() */
Benny Prijono834aee32006-02-19 01:38:06 +0000185 }
186};
187
188
189/*
190 * Event subscription session.
191 */
192struct pjsip_evsub
193{
194 char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
195 pj_pool_t *pool; /**< Pool. */
196 pjsip_endpoint *endpt; /**< Endpoint instance. */
197 pjsip_dialog *dlg; /**< Underlying dialog. */
198 struct evpkg *pkg; /**< The event package. */
Benny Prijono26ff9062006-02-21 23:47:00 +0000199 unsigned option; /**< Options. */
Benny Prijono834aee32006-02-19 01:38:06 +0000200 pjsip_evsub_user user; /**< Callback. */
201 pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */
202 pjsip_evsub_state state; /**< Subscription state. */
203 pj_str_t state_str; /**< String describing the state. */
204 pjsip_evsub_state dst_state; /**< Pending state to be set. */
205 pj_str_t dst_state_str;/**< Pending state to be set. */
206 pjsip_method method; /**< Method that established subscr.*/
207 pjsip_event_hdr *event; /**< Event description. */
208 pjsip_expires_hdr *expires; /**< Expires header */
209 pjsip_accept_hdr *accept; /**< Local Accept header. */
210
211 pj_time_val refresh_time; /**< Time to refresh. */
212 pj_timer_entry timer; /**< Internal timer. */
213 int pending_tsx; /**< Number of pending transactions.*/
Benny Prijono69b98ab2006-03-03 10:23:35 +0000214 pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */
Benny Prijono834aee32006-02-19 01:38:06 +0000215
216 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
217};
218
219
220/*
221 * This is the structure that will be "attached" to dialog.
222 * The purpose is to allow multiple subscriptions inside a dialog.
223 */
224struct dlgsub
225{
226 PJ_DECL_LIST_MEMBER(struct dlgsub);
227 pjsip_evsub *sub;
228};
229
230
231/* Static vars. */
232static const pj_str_t STR_EVENT = { "Event", 5 };
233static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
234static const pj_str_t STR_TERMINATED = { "terminated", 10 };
235static const pj_str_t STR_ACTIVE = { "active", 6 };
236static const pj_str_t STR_PENDING = { "pending", 7 };
237static const pj_str_t STR_TIMEOUT = { "timeout", 7};
238
Benny Prijono26ff9062006-02-21 23:47:00 +0000239
Benny Prijono834aee32006-02-19 01:38:06 +0000240/*
241 * On unload module.
242 */
243static pj_status_t mod_evsub_unload(void)
244{
245 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
246 mod_evsub.pool = NULL;
247
248 return PJ_SUCCESS;
249}
250
Benny Prijono26ff9062006-02-21 23:47:00 +0000251/* Proto for pjsipsimple_strerror().
252 * Defined in errno.c
253 */
254PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode,
255 char *buf, pj_size_t bufsize );
256
Benny Prijono834aee32006-02-19 01:38:06 +0000257/*
258 * Init and register module.
259 */
260PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
261{
262 pj_status_t status;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000263 pj_str_t method_tags[] = {
264 { "SUBSCRIBE", 9},
265 { "NOTIFY", 6}
266 };
Benny Prijono834aee32006-02-19 01:38:06 +0000267
Benny Prijono26ff9062006-02-21 23:47:00 +0000268 pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
269 &pjsipsimple_strerror);
270
Benny Prijono834aee32006-02-19 01:38:06 +0000271 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
272 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
273
274 /* Keep endpoint for future reference: */
275 mod_evsub.endpt = endpt;
276
277 /* Init event package list: */
278 pj_list_init(&mod_evsub.pkg_list);
279
280 /* Create pool: */
281 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 4000, 4000);
282 if (!mod_evsub.pool)
283 return PJ_ENOMEM;
284
285 /* Register module: */
286 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
287 if (status != PJ_SUCCESS)
288 goto on_error;
289
290 /* Create Allow-Events header: */
291 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
292
293 /* Register SIP-event specific headers parser: */
294 pjsip_evsub_init_parser();
295
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000296 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
297 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
298 2, method_tags);
299
300 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000301 return PJ_SUCCESS;
302
303on_error:
304 if (mod_evsub.pool) {
305 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
306 mod_evsub.pool = NULL;
307 }
308 mod_evsub.endpt = NULL;
309 return status;
310}
311
312
313/*
314 * Get the instance of the module.
315 */
316PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
317{
318 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
319
320 return &mod_evsub.mod;
321}
322
323
324/*
325 * Get the event subscription instance in the transaction.
326 */
327PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
328{
329 return tsx->mod_data[mod_evsub.mod.id];
330}
331
332
333/*
334 * Set event subscription's module data.
335 */
336PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
337 void *data )
338{
339 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
340 sub->mod_data[mod_id] = data;
341}
342
343
344/*
345 * Get event subscription's module data.
346 */
347PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
348{
349 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
350 return sub->mod_data[mod_id];
351}
352
353
354/*
355 * Find registered event package with matching name.
356 */
357static struct evpkg* find_pkg(const pj_str_t *event_name)
358{
359 struct evpkg *pkg;
360
361 pkg = mod_evsub.pkg_list.next;
362 while (pkg != &mod_evsub.pkg_list) {
363
364 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
365 return pkg;
366 }
367
368 pkg = pkg->next;
369 }
370
371 return NULL;
372}
373
374/*
375 * Register an event package
376 */
377PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
378 const pj_str_t *event_name,
379 unsigned expires,
380 unsigned accept_cnt,
381 const pj_str_t accept[])
382{
383 struct evpkg *pkg;
384 unsigned i;
385
386 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
387 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
388 PJ_ETOOMANY);
389
390 /* Make sure no module with the specified name already registered: */
391
392 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
393
394
395 /* Create new event package: */
396
397 pkg = pj_pool_alloc(mod_evsub.pool, sizeof(struct evpkg));
398 pkg->pkg_mod = pkg_mod;
399 pkg->pkg_expires = expires;
400 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
401
402 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
403 pkg->pkg_accept->count = accept_cnt;
404 for (i=0; i<accept_cnt; ++i) {
405 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
406 }
407
408 /* Add to package list: */
409
410 pj_list_push_back(&mod_evsub.pkg_list, pkg);
411
412 /* Add to Allow-Events header: */
413
414 if (mod_evsub.allow_events_hdr->count !=
415 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
416 {
417 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
418 pkg->pkg_name;
419 ++mod_evsub.allow_events_hdr->count;
420 }
421
422
423 /* Done */
424
425 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
426 (int)event_name->slen, event_name->ptr,
427 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
428
429 return PJ_SUCCESS;
430}
431
432
433
434/*
435 * Update expiration time.
436 */
437static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
438{
439 pj_gettimeofday(&sub->refresh_time);
440 sub->refresh_time.sec += interval;
441}
442
443
444/*
445 * Schedule timer.
446 */
447static void set_timer( pjsip_evsub *sub, int timer_id,
448 pj_int32_t seconds)
449{
450 if (sub->timer.id != TIMER_TYPE_NONE) {
451 PJ_LOG(5,(sub->obj_name, "%s %s timer",
452 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
453 timer_names[sub->timer.id]));
454 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
455 sub->timer.id = TIMER_TYPE_NONE;
456 }
457
458 if (timer_id != TIMER_TYPE_NONE) {
459 pj_time_val timeout;
460
461 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000462 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
463 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000464
465 timeout.sec = seconds;
466 timeout.msec = 0;
467 sub->timer.id = timer_id;
468
469 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
470
471 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
472 timer_names[sub->timer.id], timeout.sec));
473 }
474}
475
476
477/*
478 * Destroy session.
479 */
480static void evsub_destroy( pjsip_evsub *sub )
481{
482 struct dlgsub *dlgsub_head, *dlgsub;
483
484 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
485
486 /* Kill timer */
487 set_timer(sub, TIMER_TYPE_NONE, 0);
488
489 /* Remote this session from dialog's list of subscription */
490 dlgsub_head = sub->dlg->mod_data[mod_evsub.mod.id];
491 dlgsub = dlgsub_head->next;
492 while (dlgsub != dlgsub_head) {
493
494 if (dlgsub->sub == sub) {
495 pj_list_erase(dlgsub);
496 break;
497 }
498
499 dlgsub = dlgsub->next;
500 }
501
502 /* Decrement dialog's session */
503 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
504}
505
506/*
507 * Set subscription session state.
508 */
509static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
510 const pj_str_t *state_str, pjsip_event *event)
511{
512 pj_str_t old_state_str = sub->state_str;
513
514 sub->state = state;
515
516 if (state_str && state_str->slen)
517 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
518 else
519 sub->state_str = evsub_state_names[state];
520
521 PJ_LOG(4,(sub->obj_name,
522 "Subscription state changed %.*s --> %.*s",
523 (int)old_state_str.slen,
524 old_state_str.ptr,
525 (int)sub->state_str.slen,
526 sub->state_str.ptr));
527
528 if (sub->user.on_evsub_state)
529 (*sub->user.on_evsub_state)(sub, event);
530
531 if (state == PJSIP_EVSUB_STATE_TERMINATED) {
532
533 if (sub->pending_tsx == 0) {
534 evsub_destroy(sub);
535 }
536 }
537}
538
539
540/*
541 * Timer callback.
542 */
543static void on_timer( pj_timer_heap_t *timer_heap,
544 struct pj_timer_entry *entry)
545{
546 pjsip_evsub *sub;
547 int timer_id;
548
549 PJ_UNUSED_ARG(timer_heap);
550
551 sub = entry->user_data;
552
553 pjsip_dlg_inc_lock(sub->dlg);
554
555 timer_id = entry->id;
556 entry->id = TIMER_TYPE_NONE;
557
558 switch (timer_id) {
559
560 case TIMER_TYPE_UAC_REFRESH:
561 /* Time for UAC to refresh subscription */
562 if (sub->user.on_client_refresh) {
563 (*sub->user.on_client_refresh)(sub);
564 } else {
565 pjsip_tx_data *tdata;
566 pj_status_t status;
567
568 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
569 status = pjsip_evsub_initiate(sub, &sub->method,
570 sub->expires->ivalue,
571 &tdata);
572 if (status == PJ_SUCCESS)
573 pjsip_evsub_send_request(sub, tdata);
574 }
575 break;
576
577 case TIMER_TYPE_UAS_TIMEOUT:
578 /* Refresh from UAC has not been received */
579 if (sub->user.on_server_timeout) {
580 (*sub->user.on_server_timeout)(sub);
581 } else {
582 pjsip_tx_data *tdata;
583 pj_status_t status;
584
585 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
586 "Sending NOTIFY to terminate."));
587 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
588 NULL, &STR_TIMEOUT, &tdata);
589 if (status == PJ_SUCCESS)
590 pjsip_evsub_send_request(sub, tdata);
591 }
592 break;
593
594 case TIMER_TYPE_UAC_TERMINATE:
595 {
Benny Prijono834aee32006-02-19 01:38:06 +0000596 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
597 "Terminating.."));
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000598 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000599 }
600 break;
601
602 case TIMER_TYPE_UAC_WAIT_NOTIFY:
603 {
604 pjsip_tx_data *tdata;
605 pj_status_t status;
606
607 PJ_LOG(5,(sub->obj_name,
608 "Timeout waiting for subsequent NOTIFY (we did "
609 "send non-2xx response for previous NOTIFY). "
610 "Unsubscribing.."));
611 status = pjsip_evsub_initiate( sub, &sub->method, 0, &tdata);
612 if (status == PJ_SUCCESS)
613 pjsip_evsub_send_request(sub, tdata);
614 }
615 break;
616
617 default:
618 pj_assert(!"Invalid timer id");
619 }
620
621 pjsip_dlg_dec_lock(sub->dlg);
622}
623
624
625/*
626 * Create subscription session, used for both client and notifier.
627 */
628static pj_status_t evsub_create( pjsip_dialog *dlg,
629 pjsip_role_e role,
630 const pjsip_evsub_user *user_cb,
631 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000632 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000633 pjsip_evsub **p_evsub )
634{
635 pjsip_evsub *sub;
636 struct evpkg *pkg;
637 struct dlgsub *dlgsub_head, *dlgsub;
638 pj_status_t status;
639
640 /* Make sure there's package register for the event name: */
641
642 pkg = find_pkg(event);
643 if (pkg == NULL)
644 return PJSIP_SIMPLE_ENOPKG;
645
646
647 /* Init attributes: */
648
649 sub = pj_pool_zalloc(dlg->pool, sizeof(struct pjsip_evsub));
650 sub->pool = dlg->pool;
651 sub->endpt = dlg->endpt;
652 sub->dlg = dlg;
653 sub->pkg = pkg;
654 sub->role = role;
Benny Prijono26ff9062006-02-21 23:47:00 +0000655 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000656 sub->state = PJSIP_EVSUB_STATE_NULL;
657 sub->state_str = evsub_state_names[sub->state];
658 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
659 sub->accept = pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
660
661 sub->timer.user_data = sub;
662 sub->timer.cb = &on_timer;
663
664 /* Set name. */
665 pj_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
666 "evsub%p", sub);
667
668
669 /* Copy callback, if any: */
670 if (user_cb)
671 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
672
673
674 /* Create Event header: */
675 sub->event = pjsip_event_hdr_create(sub->pool);
676 pj_strdup(sub->pool, &sub->event->event_type, event);
677
678
679 /* Create subcription list: */
680
681 dlgsub_head = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
682 dlgsub = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
683 dlgsub->sub = sub;
684
685 pj_list_init(dlgsub_head);
686 pj_list_push_back(dlgsub_head, dlgsub);
687
688
689 /* Register as dialog usage: */
690
691 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
692 if (status != PJ_SUCCESS)
693 return status;
694
695
696 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
697 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
698 dlg->obj_name));
699
700 *p_evsub = sub;
701
702 return PJ_SUCCESS;
703}
704
705
706
707/*
708 * Create client subscription session.
709 */
710PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
711 const pjsip_evsub_user *user_cb,
712 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000713 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000714 pjsip_evsub **p_evsub)
715{
716 pjsip_evsub *sub;
717 pj_status_t status;
718
719 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
720
721 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000722 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000723 if (status != PJ_SUCCESS)
724 goto on_return;
725
Benny Prijono26ff9062006-02-21 23:47:00 +0000726 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
727 * is not specified.
728 */
729 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
730 pj_create_unique_string(sub->pool, &sub->event->id_param);
731 }
Benny Prijono834aee32006-02-19 01:38:06 +0000732
733 /* Increment dlg session. */
734 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
735
736 /* Done */
737 *p_evsub = sub;
738
739on_return:
740 pjsip_dlg_dec_lock(dlg);
741 return status;
742}
743
744
745/*
746 * Create server subscription session from incoming request.
747 */
748PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
749 const pjsip_evsub_user *user_cb,
750 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000751 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000752 pjsip_evsub **p_evsub)
753{
754 pjsip_evsub *sub;
755 pjsip_transaction *tsx;
756 pjsip_accept_hdr *accept_hdr;
757 pjsip_event_hdr *event_hdr;
758 pjsip_expires_hdr *expires_hdr;
759 pj_status_t status;
760
761 /* Check arguments: */
762 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
763
764 /* MUST be request message: */
765 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
766 PJSIP_ENOTREQUESTMSG);
767
768 /* Transaction MUST have been created (in the dialog) */
769 tsx = pjsip_rdata_get_tsx(rdata);
770 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
771
772 /* No subscription must have been attached to transaction */
773 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
774 PJSIP_ETYPEEXISTS);
775
776 /* Package MUST implement on_rx_refresh */
777 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
778
Benny Prijono26ff9062006-02-21 23:47:00 +0000779 /* Request MUST have "Event" header. We need the Event header to get
780 * the package name (don't want to add more arguments in the function).
781 */
Benny Prijono834aee32006-02-19 01:38:06 +0000782 event_hdr = (pjsip_event_hdr*)
783 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
784 if (event_hdr == NULL) {
785 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
786 }
787
788 /* Start locking the mutex: */
789
790 pjsip_dlg_inc_lock(dlg);
791
792 /* Create the session: */
793
794 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000795 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000796 if (status != PJ_SUCCESS)
797 goto on_return;
798
799 /* Just duplicate Event header from the request */
800 sub->event = pjsip_hdr_clone(sub->pool, event_hdr);
801
802 /* Set the method: */
803 pjsip_method_copy(sub->pool, &sub->method,
804 &rdata->msg_info.msg->line.req.method);
805
806 /* Update expiration time according to client request: */
807
808 expires_hdr = (pjsip_expires_hdr*)
809 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
810 if (expires_hdr) {
811 sub->expires->ivalue = expires_hdr->ivalue;
812 }
813
814 /* Update time. */
815 update_expires(sub, sub->expires->ivalue);
816
817 /* Update Accept header: */
818
819 accept_hdr = (pjsip_accept_hdr*)
820 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
821 if (accept_hdr)
822 sub->accept = pjsip_hdr_clone(sub->pool, accept_hdr);
823
824 /* We can start the session: */
825
826 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
827 sub->pending_tsx++;
828 tsx->mod_data[mod_evsub.mod.id] = sub;
829
830
831 /* Done. */
832 *p_evsub = sub;
833
834
835on_return:
836 pjsip_dlg_dec_lock(dlg);
837 return status;
838}
839
840
841/*
842 * Get subscription state.
843 */
844PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
845{
846 return sub->state;
847}
848
849/*
850 * Get state name.
851 */
852PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
853{
854 return sub->state_str.ptr;
855}
856
857
858/*
859 * Initiate client subscription
860 */
861PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
862 const pjsip_method *method,
863 pj_int32_t expires,
864 pjsip_tx_data **p_tdata)
865{
866 pjsip_tx_data *tdata;
867 pj_status_t status;
868
869 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
870
871 /* Use SUBSCRIBE if method is not specified */
872 if (method == NULL)
873 method = &pjsip_subscribe_method;
874
875 pjsip_dlg_inc_lock(sub->dlg);
876
877 /* Update method: */
878 if (sub->state == PJSIP_EVSUB_STATE_NULL)
879 pjsip_method_copy(sub->pool, &sub->method, method);
880
881 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
882 if (status != PJ_SUCCESS)
883 goto on_return;
884
885
886 /* Add Event header: */
887 pjsip_msg_add_hdr( tdata->msg,
888 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
889
890 /* Update and add expires header: */
891 if (expires >= 0)
892 sub->expires->ivalue = expires;
893 pjsip_msg_add_hdr( tdata->msg,
894 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
895
896 /* Add Accept header: */
897 pjsip_msg_add_hdr( tdata->msg,
898 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
899
900
901 /* Add Allow-Events header: */
902 pjsip_msg_add_hdr( tdata->msg,
903 pjsip_hdr_shallow_clone(tdata->pool,
904 mod_evsub.allow_events_hdr));
905
906
907 *p_tdata = tdata;
908
909
910on_return:
911
912 pjsip_dlg_dec_lock(sub->dlg);
913 return status;
914}
915
916
917/*
918 * Accept incoming subscription request.
919 */
920PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
921 pjsip_rx_data *rdata,
922 int st_code,
923 const pjsip_hdr *hdr_list )
924{
925 pjsip_tx_data *tdata;
926 pjsip_transaction *tsx;
927 pj_status_t status;
928
929 /* Check arguments */
930 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
931
932 /* Can only be for server subscription: */
933 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
934
935 /* Only expect 2xx status code (for now) */
936 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
937
938 /* Subscription MUST have been attached to the transaction.
939 * Initial subscription request will be attached on evsub_create_uas(),
940 * while subsequent requests will be attached in tsx_state()
941 */
942 tsx = pjsip_rdata_get_tsx(rdata);
943 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
944 PJ_EINVALIDOP);
945
946 /* Lock dialog */
947 pjsip_dlg_inc_lock(sub->dlg);
948
949 /* Create response: */
950 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
951 &tdata);
952 if (status != PJ_SUCCESS)
953 goto on_return;
954
955
956 /* Add expires header: */
957 pjsip_msg_add_hdr( tdata->msg,
958 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
959
Benny Prijonob0808372006-03-02 21:18:58 +0000960 /* Add additional header, if any. */
961 if (hdr_list) {
962 const pjsip_hdr *hdr = hdr_list->next;
963 while (hdr != hdr_list) {
964 pjsip_msg_add_hdr( tdata->msg,
965 pjsip_hdr_clone(tdata->pool, hdr));
966 hdr = hdr->next;
967 }
968 }
Benny Prijono834aee32006-02-19 01:38:06 +0000969
970 /* Send the response: */
971 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
972 if (status != PJ_SUCCESS)
973 goto on_return;
974
975
976on_return:
977
978 pjsip_dlg_dec_lock(sub->dlg);
979 return status;
980}
981
982
983/*
984 * Create Subscription-State header based on current server subscription
985 * state.
986 */
987static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
988 pjsip_evsub *sub,
989 pjsip_evsub_state state,
990 const pj_str_t *state_str,
991 const pj_str_t *reason )
992{
993 pjsip_sub_state_hdr *sub_state;
994 pj_time_val now, delay;
995
996 /* Get the remaining time before refresh is required */
997 pj_gettimeofday(&now);
998 delay = sub->refresh_time;
999 PJ_TIME_VAL_SUB(delay, now);
1000
1001 /* Create the Subscription-State header */
1002 sub_state = pjsip_sub_state_hdr_create(pool);
1003
1004 /* Fill up the header */
1005 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001006 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001007 case PJSIP_EVSUB_STATE_SENT:
1008 case PJSIP_EVSUB_STATE_ACCEPTED:
1009 pj_assert(!"Invalid state!");
1010 /* Treat as pending */
1011
1012 case PJSIP_EVSUB_STATE_PENDING:
1013 sub_state->sub_state = STR_PENDING;
1014 sub_state->expires_param = delay.sec;
1015 break;
1016
1017 case PJSIP_EVSUB_STATE_ACTIVE:
1018 sub_state->sub_state = STR_ACTIVE;
1019 sub_state->expires_param = delay.sec;
1020 break;
1021
1022 case PJSIP_EVSUB_STATE_TERMINATED:
1023 sub_state->sub_state = STR_TERMINATED;
1024 if (reason != NULL)
1025 pj_strdup(pool, &sub_state->reason_param, reason);
1026 break;
1027
1028 case PJSIP_EVSUB_STATE_UNKNOWN:
1029 pj_assert(state_str != NULL);
1030 pj_strdup(pool, &sub_state->sub_state, state_str);
1031 break;
1032 }
1033
1034 return sub_state;
1035}
1036
1037/*
1038 * Create and send NOTIFY request.
1039 */
1040PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1041 pjsip_evsub_state state,
1042 const pj_str_t *state_str,
1043 const pj_str_t *reason,
1044 pjsip_tx_data **p_tdata)
1045{
1046 pjsip_tx_data *tdata;
1047 pjsip_sub_state_hdr *sub_state;
1048 pj_status_t status;
1049
1050 /* Check arguments. */
1051 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1052
1053 /* Lock dialog. */
1054 pjsip_dlg_inc_lock(sub->dlg);
1055
1056 /* Create NOTIFY request */
1057 status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1,
1058 &tdata);
1059 if (status != PJ_SUCCESS)
1060 goto on_return;
1061
1062 /* Add Event header */
1063 pjsip_msg_add_hdr(tdata->msg,
1064 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1065
1066 /* Add Subscription-State header */
1067 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1068 reason);
1069 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1070
1071 /* Add Allow-Events header */
1072 pjsip_msg_add_hdr(tdata->msg,
1073 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1074
1075 /* Add Authentication headers. */
1076 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1077
1078
1079
1080 /* Save destination state. */
1081 sub->dst_state = state;
1082 if (state_str)
1083 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1084 else
1085 sub->dst_state_str.slen = 0;
1086
1087
1088 *p_tdata = tdata;
1089
1090on_return:
1091 /* Unlock dialog */
1092 pjsip_dlg_dec_lock(sub->dlg);
1093 return status;
1094}
1095
1096
1097/*
1098 * Create NOTIFY to reflect current status.
1099 */
1100PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1101 pjsip_tx_data **p_tdata )
1102{
1103 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1104 NULL, p_tdata );
1105}
1106
1107
1108/*
1109 * Send request.
1110 */
1111PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1112 pjsip_tx_data *tdata)
1113{
1114 pj_status_t status;
1115
1116 /* Must be request message. */
1117 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1118 PJSIP_ENOTREQUESTMSG);
1119
1120 /* Lock */
1121 pjsip_dlg_inc_lock(sub->dlg);
1122
1123 /* Send the request. */
1124 status = pjsip_dlg_send_request(sub->dlg, tdata, NULL);
1125 if (status != PJ_SUCCESS)
1126 goto on_return;
1127
1128
1129 /* Special case for NOTIFY:
1130 * The new state was set in pjsip_evsub_notify(), but we apply the
1131 * new state now, when the request was actually sent.
1132 */
1133 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1134 &pjsip_notify_method)==0)
1135 {
1136 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1137 {goto on_return;});
1138
1139 set_state(sub, sub->dst_state,
1140 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
1141 NULL);
1142
1143 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1144 sub->dst_state_str.slen = 0;
1145
1146 }
1147
1148
1149on_return:
1150 pjsip_dlg_dec_lock(sub->dlg);
1151 return status;
1152}
1153
1154
1155
1156/*
1157 * Attach subscription session to newly created transaction, if appropriate.
1158 */
1159static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1160 pjsip_event *event)
1161{
1162 /*
1163 * Newly created transaction will not have subscription session
1164 * attached to it. Find the subscription session from the dialog,
1165 * by matching the Event header.
1166 */
1167 pjsip_dialog *dlg;
1168 pjsip_event_hdr *event_hdr;
1169 pjsip_msg *msg;
1170 struct dlgsub *dlgsub_head, *dlgsub;
1171 pjsip_evsub *sub;
1172
1173 dlg = pjsip_tsx_get_dlg(tsx);
1174 if (!dlg) {
1175 pj_assert(!"Transaction should have a dialog instance!");
1176 return NULL;
1177 }
1178
Benny Prijono26ff9062006-02-21 23:47:00 +00001179
Benny Prijono834aee32006-02-19 01:38:06 +00001180 switch (event->body.tsx_state.type) {
1181 case PJSIP_EVENT_RX_MSG:
1182 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1183 break;
1184 case PJSIP_EVENT_TX_MSG:
1185 msg = event->body.tsx_state.src.tdata->msg;
1186 break;
1187 default:
1188 if (tsx->role == PJSIP_ROLE_UAC)
1189 msg = tsx->last_tx->msg;
1190 else
1191 msg = NULL;
1192 break;
1193 }
1194
1195 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001196 //Note:
1197 // this transaction can be other transaction in the dialog.
1198 // The assertion below probably only valid for dialog that
1199 // only has one event subscription usage.
1200 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001201 return NULL;
1202 }
1203
1204 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1205 if (!event_hdr) {
1206 /* Not subscription related message */
1207 return NULL;
1208 }
1209
1210 /* Find the subscription in the dialog, based on the content
1211 * of Event header:
1212 */
1213
1214 dlgsub_head = dlg->mod_data[mod_evsub.mod.id];
1215 if (dlgsub_head == NULL) {
1216 dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub));
1217 pj_list_init(dlgsub_head);
1218 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1219 }
1220 dlgsub = dlgsub_head->next;
1221
1222 while (dlgsub != dlgsub_head) {
1223
Benny Prijono26ff9062006-02-21 23:47:00 +00001224 if (pj_stricmp(&dlgsub->sub->event->event_type,
1225 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001226 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001227 /* Event type matched.
1228 * Check if event ID matched too.
1229 */
1230 if (pj_strcmp(&dlgsub->sub->event->id_param,
1231 &event_hdr->id_param)==0)
1232 {
1233
1234 break;
1235
1236 }
1237 /*
1238 * Otherwise if it is an UAC subscription, AND
1239 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1240 * the session's event id is NULL, AND
1241 * the incoming request is NOTIFY with event ID, then
1242 * we consider it as a match, and update the
1243 * session's event id.
1244 */
1245 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1246 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1247 dlgsub->sub->event->id_param.slen==0 &&
1248 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1249 {
1250 /* Update session's event id. */
1251 pj_strdup(dlgsub->sub->pool,
1252 &dlgsub->sub->event->id_param,
1253 &event_hdr->id_param);
1254
1255 break;
1256 }
Benny Prijono834aee32006-02-19 01:38:06 +00001257 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001258
1259
1260
Benny Prijono834aee32006-02-19 01:38:06 +00001261 dlgsub = dlgsub->next;
1262 }
1263
1264 if (dlgsub == dlgsub_head) {
1265 /* This could be incoming request to create new subscription */
1266 PJ_LOG(4,(THIS_FILE,
1267 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001268 (int)tsx->method.name.slen,
1269 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001270 (int)event_hdr->event_type.slen,
1271 event_hdr->event_type.ptr,
1272 (int)event_hdr->id_param.slen,
1273 event_hdr->id_param.ptr));
1274
1275 /* If this is an incoming NOTIFY, reject with 481 */
1276 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1277 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1278 {
1279 pj_str_t reason = pj_str("Subscription Does Not Exist");
1280 pjsip_tx_data *tdata;
1281 pj_status_t status;
1282
1283 status = pjsip_dlg_create_response(dlg,
1284 event->body.tsx_state.src.rdata,
1285 481, &reason,
1286 &tdata);
1287 if (status == PJ_SUCCESS) {
1288 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1289 }
1290 }
1291 return NULL;
1292 }
1293
1294 /* Found! */
1295 sub = dlgsub->sub;
1296
1297 /* Attach session to the transaction */
1298 tsx->mod_data[mod_evsub.mod.id] = sub;
1299 sub->pending_tsx++;
1300
Benny Prijono69b98ab2006-03-03 10:23:35 +00001301 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1302 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1303 * transaction is started while previous one still alive, terminate
1304 * the older one.
1305 *
1306 * Sample scenario:
1307 * - subscribe sent to destination that doesn't exist, transaction
1308 * is still retransmitting request, then unsubscribe is sent.
1309 */
1310 if (tsx->role == PJSIP_ROLE_UAC &&
1311 tsx->state == PJSIP_TSX_STATE_CALLING &&
1312 pjsip_method_cmp(&tsx->method, &sub->method) == 0)
1313 {
1314
1315 if (sub->pending_sub &&
1316 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1317 {
1318 PJ_LOG(4,(sub->obj_name,
1319 "Cancelling pending %.*s request",
1320 (int)sub->method.name.slen, sub->method.name.ptr));
1321
1322 /* By convention, we use 490 (Request Updated) status code.
1323 * When transaction handler (below) see this status code, it
1324 * will ignore the transaction.
1325 */
1326 pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1327 }
1328
1329 sub->pending_sub = tsx;
1330
1331 } else if (tsx == sub->pending_sub &&
1332 tsx->state >= PJSIP_TSX_STATE_COMPLETED)
1333 {
1334 sub->pending_sub = NULL;
1335 }
1336
Benny Prijono834aee32006-02-19 01:38:06 +00001337 return sub;
1338}
1339
1340
1341/*
1342 * Create response, adding custome headers and msg body.
1343 */
1344static pj_status_t create_response( pjsip_evsub *sub,
1345 pjsip_rx_data *rdata,
1346 int st_code,
1347 const pj_str_t *st_text,
1348 const pjsip_hdr *res_hdr,
1349 const pjsip_msg_body *body,
1350 pjsip_tx_data **p_tdata)
1351{
1352 pjsip_tx_data *tdata;
1353 pjsip_hdr *hdr;
1354 pj_status_t status;
1355
1356 status = pjsip_dlg_create_response(sub->dlg, rdata,
1357 st_code, st_text, &tdata);
1358 if (status != PJ_SUCCESS)
1359 return status;
1360
1361 *p_tdata = tdata;
1362
1363 /* Add response headers. */
1364 hdr = res_hdr->next;
1365 while (hdr != res_hdr) {
1366 pjsip_msg_add_hdr( tdata->msg,
1367 pjsip_hdr_clone(tdata->pool, hdr));
1368 hdr = hdr->next;
1369 }
1370
1371 /* Add msg body, if any */
1372 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001373 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1374 if (tdata->msg->body == NULL) {
1375
1376 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1377
Benny Prijono834aee32006-02-19 01:38:06 +00001378 /* Ignore */
1379 return PJ_SUCCESS;
1380 }
1381 }
1382
1383 return PJ_SUCCESS;
1384}
1385
1386/*
1387 * Get subscription state from the value of Subscription-State header.
1388 */
1389static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1390 pjsip_evsub_state *state,
1391 pj_str_t **state_str )
1392{
1393 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1394
1395 *state = PJSIP_EVSUB_STATE_TERMINATED;
1396 *state_str = NULL;
1397
1398 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1399
1400 *state = PJSIP_EVSUB_STATE_ACTIVE;
1401 *state_str = NULL;
1402
1403 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1404
1405 *state = PJSIP_EVSUB_STATE_PENDING;
1406 *state_str = NULL;
1407
1408 } else {
1409
1410 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1411 *state_str = &sub_state->sub_state;
1412
1413 }
1414}
1415
1416/*
1417 * Transaction event processing by UAC, after subscription is sent.
1418 */
1419static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1420 pjsip_event *event )
1421{
1422
1423 if (pjsip_method_cmp(&tsx->method, &sub->method)==0) {
1424
1425 /* Received response to outgoing request that establishes/refresh
1426 * subscription.
1427 */
1428
1429 /* First time initial request is sent. */
1430 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1431 tsx->state == PJSIP_TSX_STATE_CALLING)
1432 {
1433 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
1434 return;
1435 }
1436
1437 /* Only interested in final response */
1438 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1439 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1440 {
1441 return;
1442 }
1443
1444 /* Handle authentication. */
1445 if (tsx->status_code==401 || tsx->status_code==407) {
1446 pjsip_tx_data *tdata;
1447 pj_status_t status;
1448
1449 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1450 /* Previously failed transaction has terminated */
1451 return;
1452 }
1453
1454 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1455 event->body.tsx_state.src.rdata,
1456 tsx->last_tx, &tdata);
1457 if (status == PJ_SUCCESS)
1458 status = pjsip_dlg_send_request(sub->dlg, tdata, NULL);
1459
1460 if (status != PJ_SUCCESS) {
1461 /* Authentication failed! */
1462 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1463 NULL,
1464 event);
1465 return;
1466 }
1467
1468 return;
1469 }
1470
1471 if (tsx->status_code/100 == 2) {
1472
1473 /* Successfull SUBSCRIBE request!
1474 * This could be:
1475 * - response to initial SUBSCRIBE request
1476 * - response to subsequent refresh
1477 * - response to unsubscription
1478 */
1479
1480 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1481 /* Ignore; this transaction has been processed before */
1482 return;
1483 }
1484
1485 /* Update UAC refresh time, if response contains Expires header,
1486 * only when we're not unsubscribing.
1487 */
1488 if (sub->expires->ivalue != 0) {
1489 pjsip_msg *msg;
1490 pjsip_expires_hdr *expires;
1491
1492 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1493 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1494 if (expires) {
1495 sub->expires->ivalue = expires->ivalue;
1496 }
1497 }
1498
1499 /* Update time */
1500 update_expires(sub, sub->expires->ivalue);
1501
1502 /* Start UAC refresh timer, only when we're not unsubscribing */
1503 if (sub->expires->ivalue != 0) {
1504 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1505 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1506
1507 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1508 timeout));
1509 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1510
1511 } else {
1512 /* Otherwise set timer to terminate client subscription when
1513 * NOTIFY to end subscription is not received.
1514 */
1515 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1516 }
1517
1518 /* Set state, if necessary */
1519 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1520 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
1521 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
1522 }
1523
1524 } else {
1525
1526 /* Failed SUBSCRIBE request!
1527 *
1528 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1529 * other than 481, the subscription is still considered valid for
1530 * the duration of the last Expires.
1531 *
1532 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1533 * expiration, theoritically the expiration is still valid for the
1534 * next 5 seconds even when we receive non-481 failed response.
1535 *
1536 * Ah, what the heck!
1537 *
1538 * Just terminate now!
1539 *
1540 */
1541
1542 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1543 /* Ignore, has been handled before */
1544 return;
1545 }
1546
Benny Prijono69b98ab2006-03-03 10:23:35 +00001547 /* Ignore 490 (Request Updated) status.
1548 * This happens when application sends SUBSCRIBE/REFER while
1549 * another one is still in progress.
1550 */
1551 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1552 return;
1553 }
1554
Benny Prijono834aee32006-02-19 01:38:06 +00001555 /* Kill any timer. */
1556 set_timer(sub, TIMER_TYPE_NONE, 0);
1557
1558 /* Set state to TERMINATED */
1559 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1560 NULL, event);
1561
1562 }
1563
1564 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1565
1566 /* Incoming NOTIFY.
1567 * This can be the result of:
1568 * - Initial subscription response
1569 * - UAS updating the resource info.
1570 * - Unsubscription response.
1571 */
1572 int st_code = 200;
1573 pj_str_t *st_text = NULL;
1574 pjsip_hdr res_hdr;
1575 pjsip_msg_body *body = NULL;
1576
1577 pjsip_rx_data *rdata;
1578 pjsip_msg *msg;
1579 pjsip_sub_state_hdr *sub_state;
1580
1581 pjsip_evsub_state new_state;
1582 pj_str_t *new_state_str;
1583
1584 pjsip_tx_data *tdata;
1585 pj_status_t status;
1586 int next_refresh;
1587
1588 /* Only want to handle initial NOTIFY receive event. */
1589 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1590 return;
1591
1592
1593 rdata = event->body.tsx_state.src.rdata;
1594 msg = rdata->msg_info.msg;
1595
1596 pj_list_init(&res_hdr);
1597
1598 /* Get subscription state header. */
1599 sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
1600 if (sub_state == NULL) {
1601
1602 pjsip_warning_hdr *warn_hdr;
1603 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1604
1605 /* Bad request! Add warning header. */
1606 st_code = PJSIP_SC_BAD_REQUEST;
1607 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1608 pjsip_endpt_name(sub->endpt),
1609 &warn_text);
1610 pj_list_push_back(&res_hdr, warn_hdr);
1611 }
1612
1613 /* Call application registered callback to handle incoming NOTIFY,
1614 * if any.
1615 */
1616 if (st_code==200 && sub->user.on_rx_notify) {
1617 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1618 &res_hdr, &body);
1619
1620 /* Application MUST specify final response! */
1621 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1622
1623 /* Must be a valid status code */
1624 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1625 }
1626
1627
1628 /* If non-2xx should be returned, then send the response.
1629 * No need to update server subscription state.
1630 */
1631 if (st_code >= 300) {
1632 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1633 body, &tdata);
1634 if (status == PJ_SUCCESS) {
1635 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1636 }
1637
1638 /* Start timer to terminate subscription, just in case server
1639 * is not able to generate NOTIFY to our response.
1640 */
1641 if (status == PJ_SUCCESS) {
1642 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1643 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1644 } else {
1645 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1646 }
1647
1648 return;
1649 }
1650
1651 /* Update expiration from the value of expires param in
1652 * Subscription-State header, but ONLY when subscription state
1653 * is "active" or "pending", AND the header contains expires param.
1654 */
1655 if (sub->expires->ivalue != 0 &&
1656 sub_state->expires_param >= 0 &&
1657 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1658 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1659 {
1660 next_refresh = sub_state->expires_param;
1661
1662 } else {
1663 next_refresh = sub->expires->ivalue;
1664 }
1665
1666 /* Update time */
1667 update_expires(sub, next_refresh);
1668
1669 /* Start UAC refresh timer, only when we're not unsubscribing */
1670 if (sub->expires->ivalue != 0) {
1671 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1672 next_refresh - TIME_UAC_REFRESH : next_refresh;
1673
1674 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1675 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1676 }
1677
1678 /* Find out the state */
1679 get_hdr_state(sub_state, &new_state, &new_state_str);
1680
1681 /* Send response. */
1682 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1683 body, &tdata);
1684 if (status == PJ_SUCCESS)
1685 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1686
1687 /* Set the state */
1688 if (status == PJ_SUCCESS) {
1689 set_state(sub, new_state, new_state_str, event);
1690 } else {
1691 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1692 }
1693
1694
1695 } else {
1696
1697 /*
1698 * Unexpected method!
1699 */
1700 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1701 (int)tsx->method.name.slen, tsx->method.name.ptr));
1702 }
1703}
1704
1705
1706/*
1707 * Transaction event processing by UAS, after subscription is accepted.
1708 */
1709static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1710 pjsip_event *event)
1711{
1712
1713 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0) {
1714
1715 /*
1716 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1717 *
1718 */
1719 pjsip_rx_data *rdata;
1720 pjsip_event_hdr *event_hdr;
1721 pjsip_expires_hdr *expires;
1722 pjsip_msg *msg;
1723 pjsip_tx_data *tdata;
1724 int st_code = 200;
1725 pj_str_t *st_text = NULL;
1726 pjsip_hdr res_hdr;
1727 pjsip_msg_body *body = NULL;
1728 pjsip_evsub_state old_state;
1729 pj_str_t old_state_str;
1730 pj_status_t status;
1731
1732
1733 /* Only wants to handle the first event when the request is
1734 * received.
1735 */
1736 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1737 return;
1738
1739 rdata = event->body.tsx_state.src.rdata;
1740 msg = rdata->msg_info.msg;
1741
1742 /* Set expiration time based on client request (in Expires header),
1743 * or package default expiration time.
1744 */
1745 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1746 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1747 if (event_hdr && expires) {
1748 struct evpkg *evpkg;
1749
1750 evpkg = find_pkg(&event_hdr->event_type);
1751 if (evpkg) {
1752 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1753 sub->expires->ivalue = expires->ivalue;
1754 else
1755 sub->expires->ivalue = evpkg->pkg_expires;
1756 }
1757 }
1758
1759 /* Update time (before calling on_rx_refresh, since application
1760 * will send NOTIFY.
1761 */
1762 update_expires(sub, sub->expires->ivalue);
1763
1764
1765 /* Save old state.
1766 * If application respond with non-2xx, revert to old state.
1767 */
1768 old_state = sub->state;
1769 old_state_str = sub->state_str;
1770
1771 if (sub->expires->ivalue == 0) {
1772 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1773 sub->state_str = evsub_state_names[sub->state];
1774 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1775 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1776 sub->state_str = evsub_state_names[sub->state];
1777 }
1778
1779 /* Call application's on_rx_refresh, just in case it wants to send
1780 * response other than 200 (OK)
1781 */
1782 pj_list_init(&res_hdr);
1783
1784 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1785 &res_hdr, &body);
1786
1787 /* Application MUST specify final response! */
1788 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1789
1790 /* Must be a valid status code */
1791 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1792
1793
1794 /* Create and send response */
1795 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1796 body, &tdata);
1797 if (status == PJ_SUCCESS) {
1798 /* Add expires header: */
1799 pjsip_msg_add_hdr( tdata->msg,
1800 pjsip_hdr_shallow_clone(tdata->pool,
1801 sub->expires));
1802
1803 /* Send */
1804 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1805 }
1806
1807 /* Update state or revert state */
1808 if (st_code/100==2) {
1809
1810 if (sub->expires->ivalue == 0) {
1811 set_state(sub, sub->state, NULL, event);
1812 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1813 set_state(sub, sub->state, NULL, event);
1814 }
1815
1816 /* Set UAS timeout timer, when state is not terminated. */
1817 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1818 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1819 sub->expires->ivalue));
1820 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1821 sub->expires->ivalue);
1822 }
1823
1824 } else {
1825 sub->state = old_state;
1826 sub->state_str = old_state_str;
1827 }
1828
1829
1830 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1831
1832 /* Handle authentication */
1833 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1834 (tsx->status_code==401 || tsx->status_code==407))
1835 {
1836 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1837 pjsip_tx_data *tdata;
1838 pj_status_t status;
1839
1840 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
1841 tsx->last_tx, &tdata);
1842 if (status == PJ_SUCCESS)
1843 status = pjsip_dlg_send_request( sub->dlg, tdata, NULL );
1844
1845 if (status != PJ_SUCCESS) {
1846 /* Can't authenticate. Terminate session (?) */
1847 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1848 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001849
1850 }
1851 /*
1852 * Terminate event usage if we receive 481, 408, and 7 class
1853 * responses.
1854 */
1855 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
1856 (tsx->status_code==481 || tsx->status_code==408 ||
1857 tsx->status_code/100 == 7))
1858 {
1859 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1860 }
Benny Prijono834aee32006-02-19 01:38:06 +00001861
1862 } else {
1863
1864 /*
1865 * Unexpected method!
1866 */
1867 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1868 (int)tsx->method.name.slen, tsx->method.name.ptr));
1869
1870 }
1871}
1872
1873
1874/*
1875 * Notification when transaction state has changed!
1876 */
1877static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
1878{
1879 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
1880
1881 if (sub == NULL) {
1882 sub = on_new_transaction(tsx, event);
1883 if (sub == NULL)
1884 return;
1885 }
1886
1887
1888 /* Call on_tsx_state callback, if any. */
1889 if (sub->user.on_tsx_state)
1890 (*sub->user.on_tsx_state)(sub, tsx, event);
1891
1892
1893 /* Process the event: */
1894
1895 if (sub->role == PJSIP_ROLE_UAC) {
1896 on_tsx_state_uac(sub, tsx, event);
1897 } else {
1898 on_tsx_state_uas(sub, tsx, event);
1899 }
1900
1901
1902 /* Check transaction TERMINATE event */
1903 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1904
1905 --sub->pending_tsx;
1906
1907 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
1908 sub->pending_tsx == 0)
1909 {
1910 evsub_destroy(sub);
1911 }
1912
1913 }
1914}
1915
1916