blob: 7b13d548d67825d8498ca14613ebf181a851a09e [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. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000201 pj_bool_t call_cb; /**< Notify callback? */
Benny Prijono834aee32006-02-19 01:38:06 +0000202 pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */
203 pjsip_evsub_state state; /**< Subscription state. */
204 pj_str_t state_str; /**< String describing the state. */
205 pjsip_evsub_state dst_state; /**< Pending state to be set. */
206 pj_str_t dst_state_str;/**< Pending state to be set. */
207 pjsip_method method; /**< Method that established subscr.*/
208 pjsip_event_hdr *event; /**< Event description. */
209 pjsip_expires_hdr *expires; /**< Expires header */
210 pjsip_accept_hdr *accept; /**< Local Accept header. */
211
212 pj_time_val refresh_time; /**< Time to refresh. */
213 pj_timer_entry timer; /**< Internal timer. */
214 int pending_tsx; /**< Number of pending transactions.*/
Benny Prijono69b98ab2006-03-03 10:23:35 +0000215 pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */
Benny Prijono834aee32006-02-19 01:38:06 +0000216
217 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
218};
219
220
221/*
222 * This is the structure that will be "attached" to dialog.
223 * The purpose is to allow multiple subscriptions inside a dialog.
224 */
225struct dlgsub
226{
227 PJ_DECL_LIST_MEMBER(struct dlgsub);
228 pjsip_evsub *sub;
229};
230
231
232/* Static vars. */
233static const pj_str_t STR_EVENT = { "Event", 5 };
234static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
235static const pj_str_t STR_TERMINATED = { "terminated", 10 };
236static const pj_str_t STR_ACTIVE = { "active", 6 };
237static const pj_str_t STR_PENDING = { "pending", 7 };
238static const pj_str_t STR_TIMEOUT = { "timeout", 7};
239
Benny Prijono26ff9062006-02-21 23:47:00 +0000240
Benny Prijono834aee32006-02-19 01:38:06 +0000241/*
242 * On unload module.
243 */
244static pj_status_t mod_evsub_unload(void)
245{
246 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
247 mod_evsub.pool = NULL;
248
249 return PJ_SUCCESS;
250}
251
Benny Prijono26ff9062006-02-21 23:47:00 +0000252/* Proto for pjsipsimple_strerror().
253 * Defined in errno.c
254 */
255PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode,
256 char *buf, pj_size_t bufsize );
257
Benny Prijono834aee32006-02-19 01:38:06 +0000258/*
259 * Init and register module.
260 */
261PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
262{
263 pj_status_t status;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000264 pj_str_t method_tags[] = {
265 { "SUBSCRIBE", 9},
266 { "NOTIFY", 6}
267 };
Benny Prijono834aee32006-02-19 01:38:06 +0000268
Benny Prijono26ff9062006-02-21 23:47:00 +0000269 pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
270 &pjsipsimple_strerror);
271
Benny Prijono834aee32006-02-19 01:38:06 +0000272 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
273 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
274
275 /* Keep endpoint for future reference: */
276 mod_evsub.endpt = endpt;
277
278 /* Init event package list: */
279 pj_list_init(&mod_evsub.pkg_list);
280
281 /* Create pool: */
282 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 4000, 4000);
283 if (!mod_evsub.pool)
284 return PJ_ENOMEM;
285
286 /* Register module: */
287 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
288 if (status != PJ_SUCCESS)
289 goto on_error;
290
291 /* Create Allow-Events header: */
292 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
293
294 /* Register SIP-event specific headers parser: */
295 pjsip_evsub_init_parser();
296
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000297 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
298 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
299 2, method_tags);
300
301 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000302 return PJ_SUCCESS;
303
304on_error:
305 if (mod_evsub.pool) {
306 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
307 mod_evsub.pool = NULL;
308 }
309 mod_evsub.endpt = NULL;
310 return status;
311}
312
313
314/*
315 * Get the instance of the module.
316 */
317PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
318{
319 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
320
321 return &mod_evsub.mod;
322}
323
324
325/*
326 * Get the event subscription instance in the transaction.
327 */
328PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
329{
330 return tsx->mod_data[mod_evsub.mod.id];
331}
332
333
334/*
335 * Set event subscription's module data.
336 */
337PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
338 void *data )
339{
340 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
341 sub->mod_data[mod_id] = data;
342}
343
344
345/*
346 * Get event subscription's module data.
347 */
348PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
349{
350 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
351 return sub->mod_data[mod_id];
352}
353
354
355/*
356 * Find registered event package with matching name.
357 */
358static struct evpkg* find_pkg(const pj_str_t *event_name)
359{
360 struct evpkg *pkg;
361
362 pkg = mod_evsub.pkg_list.next;
363 while (pkg != &mod_evsub.pkg_list) {
364
365 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
366 return pkg;
367 }
368
369 pkg = pkg->next;
370 }
371
372 return NULL;
373}
374
375/*
376 * Register an event package
377 */
378PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
379 const pj_str_t *event_name,
380 unsigned expires,
381 unsigned accept_cnt,
382 const pj_str_t accept[])
383{
384 struct evpkg *pkg;
385 unsigned i;
386
387 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
388 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
389 PJ_ETOOMANY);
390
391 /* Make sure no module with the specified name already registered: */
392
393 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
394
395
396 /* Create new event package: */
397
398 pkg = pj_pool_alloc(mod_evsub.pool, sizeof(struct evpkg));
399 pkg->pkg_mod = pkg_mod;
400 pkg->pkg_expires = expires;
401 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
402
403 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
404 pkg->pkg_accept->count = accept_cnt;
405 for (i=0; i<accept_cnt; ++i) {
406 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
407 }
408
409 /* Add to package list: */
410
411 pj_list_push_back(&mod_evsub.pkg_list, pkg);
412
413 /* Add to Allow-Events header: */
414
415 if (mod_evsub.allow_events_hdr->count !=
416 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
417 {
418 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
419 pkg->pkg_name;
420 ++mod_evsub.allow_events_hdr->count;
421 }
422
423
424 /* Done */
425
426 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
427 (int)event_name->slen, event_name->ptr,
428 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
429
430 return PJ_SUCCESS;
431}
432
433
434
435/*
436 * Update expiration time.
437 */
438static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
439{
440 pj_gettimeofday(&sub->refresh_time);
441 sub->refresh_time.sec += interval;
442}
443
444
445/*
446 * Schedule timer.
447 */
448static void set_timer( pjsip_evsub *sub, int timer_id,
449 pj_int32_t seconds)
450{
451 if (sub->timer.id != TIMER_TYPE_NONE) {
452 PJ_LOG(5,(sub->obj_name, "%s %s timer",
453 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
454 timer_names[sub->timer.id]));
455 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
456 sub->timer.id = TIMER_TYPE_NONE;
457 }
458
459 if (timer_id != TIMER_TYPE_NONE) {
460 pj_time_val timeout;
461
462 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000463 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
464 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000465
466 timeout.sec = seconds;
467 timeout.msec = 0;
468 sub->timer.id = timer_id;
469
470 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
471
472 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
473 timer_names[sub->timer.id], timeout.sec));
474 }
475}
476
477
478/*
479 * Destroy session.
480 */
481static void evsub_destroy( pjsip_evsub *sub )
482{
483 struct dlgsub *dlgsub_head, *dlgsub;
484
485 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
486
487 /* Kill timer */
488 set_timer(sub, TIMER_TYPE_NONE, 0);
489
490 /* Remote this session from dialog's list of subscription */
491 dlgsub_head = sub->dlg->mod_data[mod_evsub.mod.id];
492 dlgsub = dlgsub_head->next;
493 while (dlgsub != dlgsub_head) {
494
495 if (dlgsub->sub == sub) {
496 pj_list_erase(dlgsub);
497 break;
498 }
499
500 dlgsub = dlgsub->next;
501 }
502
503 /* Decrement dialog's session */
504 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
505}
506
507/*
508 * Set subscription session state.
509 */
510static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
511 const pj_str_t *state_str, pjsip_event *event)
512{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000513 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000514 pj_str_t old_state_str = sub->state_str;
515
516 sub->state = state;
517
518 if (state_str && state_str->slen)
519 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
520 else
521 sub->state_str = evsub_state_names[state];
522
523 PJ_LOG(4,(sub->obj_name,
524 "Subscription state changed %.*s --> %.*s",
525 (int)old_state_str.slen,
526 old_state_str.ptr,
527 (int)sub->state_str.slen,
528 sub->state_str.ptr));
529
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000530 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000531 (*sub->user.on_evsub_state)(sub, event);
532
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000533 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
534 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
535 {
Benny Prijono834aee32006-02-19 01:38:06 +0000536 if (sub->pending_tsx == 0) {
537 evsub_destroy(sub);
538 }
539 }
540}
541
542
543/*
544 * Timer callback.
545 */
546static void on_timer( pj_timer_heap_t *timer_heap,
547 struct pj_timer_entry *entry)
548{
549 pjsip_evsub *sub;
550 int timer_id;
551
552 PJ_UNUSED_ARG(timer_heap);
553
554 sub = entry->user_data;
555
556 pjsip_dlg_inc_lock(sub->dlg);
557
558 timer_id = entry->id;
559 entry->id = TIMER_TYPE_NONE;
560
561 switch (timer_id) {
562
563 case TIMER_TYPE_UAC_REFRESH:
564 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000565 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000566 (*sub->user.on_client_refresh)(sub);
567 } else {
568 pjsip_tx_data *tdata;
569 pj_status_t status;
570
571 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
572 status = pjsip_evsub_initiate(sub, &sub->method,
573 sub->expires->ivalue,
574 &tdata);
575 if (status == PJ_SUCCESS)
576 pjsip_evsub_send_request(sub, tdata);
577 }
578 break;
579
580 case TIMER_TYPE_UAS_TIMEOUT:
581 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000582 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000583 (*sub->user.on_server_timeout)(sub);
584 } else {
585 pjsip_tx_data *tdata;
586 pj_status_t status;
587
588 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
589 "Sending NOTIFY to terminate."));
590 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
591 NULL, &STR_TIMEOUT, &tdata);
592 if (status == PJ_SUCCESS)
593 pjsip_evsub_send_request(sub, tdata);
594 }
595 break;
596
597 case TIMER_TYPE_UAC_TERMINATE:
598 {
Benny Prijono834aee32006-02-19 01:38:06 +0000599 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
600 "Terminating.."));
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000601 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000602 }
603 break;
604
605 case TIMER_TYPE_UAC_WAIT_NOTIFY:
606 {
607 pjsip_tx_data *tdata;
608 pj_status_t status;
609
610 PJ_LOG(5,(sub->obj_name,
611 "Timeout waiting for subsequent NOTIFY (we did "
612 "send non-2xx response for previous NOTIFY). "
613 "Unsubscribing.."));
614 status = pjsip_evsub_initiate( sub, &sub->method, 0, &tdata);
615 if (status == PJ_SUCCESS)
616 pjsip_evsub_send_request(sub, tdata);
617 }
618 break;
619
620 default:
621 pj_assert(!"Invalid timer id");
622 }
623
624 pjsip_dlg_dec_lock(sub->dlg);
625}
626
627
628/*
629 * Create subscription session, used for both client and notifier.
630 */
631static pj_status_t evsub_create( pjsip_dialog *dlg,
632 pjsip_role_e role,
633 const pjsip_evsub_user *user_cb,
634 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000635 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000636 pjsip_evsub **p_evsub )
637{
638 pjsip_evsub *sub;
639 struct evpkg *pkg;
640 struct dlgsub *dlgsub_head, *dlgsub;
641 pj_status_t status;
642
643 /* Make sure there's package register for the event name: */
644
645 pkg = find_pkg(event);
646 if (pkg == NULL)
647 return PJSIP_SIMPLE_ENOPKG;
648
649
650 /* Init attributes: */
651
652 sub = pj_pool_zalloc(dlg->pool, sizeof(struct pjsip_evsub));
653 sub->pool = dlg->pool;
654 sub->endpt = dlg->endpt;
655 sub->dlg = dlg;
656 sub->pkg = pkg;
657 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000658 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000659 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000660 sub->state = PJSIP_EVSUB_STATE_NULL;
661 sub->state_str = evsub_state_names[sub->state];
662 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
663 sub->accept = pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
664
665 sub->timer.user_data = sub;
666 sub->timer.cb = &on_timer;
667
668 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000669 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
670 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000671
672
673 /* Copy callback, if any: */
674 if (user_cb)
675 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
676
677
678 /* Create Event header: */
679 sub->event = pjsip_event_hdr_create(sub->pool);
680 pj_strdup(sub->pool, &sub->event->event_type, event);
681
682
683 /* Create subcription list: */
684
685 dlgsub_head = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
686 dlgsub = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
687 dlgsub->sub = sub;
688
689 pj_list_init(dlgsub_head);
690 pj_list_push_back(dlgsub_head, dlgsub);
691
692
693 /* Register as dialog usage: */
694
695 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
696 if (status != PJ_SUCCESS)
697 return status;
698
699
700 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
701 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
702 dlg->obj_name));
703
704 *p_evsub = sub;
705
706 return PJ_SUCCESS;
707}
708
709
710
711/*
712 * Create client subscription session.
713 */
714PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
715 const pjsip_evsub_user *user_cb,
716 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000717 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000718 pjsip_evsub **p_evsub)
719{
720 pjsip_evsub *sub;
721 pj_status_t status;
722
723 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
724
725 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000726 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000727 if (status != PJ_SUCCESS)
728 goto on_return;
729
Benny Prijono26ff9062006-02-21 23:47:00 +0000730 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
731 * is not specified.
732 */
733 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
734 pj_create_unique_string(sub->pool, &sub->event->id_param);
735 }
Benny Prijono834aee32006-02-19 01:38:06 +0000736
737 /* Increment dlg session. */
738 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
739
740 /* Done */
741 *p_evsub = sub;
742
743on_return:
744 pjsip_dlg_dec_lock(dlg);
745 return status;
746}
747
748
749/*
750 * Create server subscription session from incoming request.
751 */
752PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
753 const pjsip_evsub_user *user_cb,
754 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000755 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000756 pjsip_evsub **p_evsub)
757{
758 pjsip_evsub *sub;
759 pjsip_transaction *tsx;
760 pjsip_accept_hdr *accept_hdr;
761 pjsip_event_hdr *event_hdr;
762 pjsip_expires_hdr *expires_hdr;
763 pj_status_t status;
764
765 /* Check arguments: */
766 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
767
768 /* MUST be request message: */
769 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
770 PJSIP_ENOTREQUESTMSG);
771
772 /* Transaction MUST have been created (in the dialog) */
773 tsx = pjsip_rdata_get_tsx(rdata);
774 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
775
776 /* No subscription must have been attached to transaction */
777 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
778 PJSIP_ETYPEEXISTS);
779
780 /* Package MUST implement on_rx_refresh */
781 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
782
Benny Prijono26ff9062006-02-21 23:47:00 +0000783 /* Request MUST have "Event" header. We need the Event header to get
784 * the package name (don't want to add more arguments in the function).
785 */
Benny Prijono834aee32006-02-19 01:38:06 +0000786 event_hdr = (pjsip_event_hdr*)
787 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
788 if (event_hdr == NULL) {
789 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
790 }
791
792 /* Start locking the mutex: */
793
794 pjsip_dlg_inc_lock(dlg);
795
796 /* Create the session: */
797
798 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000799 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000800 if (status != PJ_SUCCESS)
801 goto on_return;
802
803 /* Just duplicate Event header from the request */
804 sub->event = pjsip_hdr_clone(sub->pool, event_hdr);
805
806 /* Set the method: */
807 pjsip_method_copy(sub->pool, &sub->method,
808 &rdata->msg_info.msg->line.req.method);
809
810 /* Update expiration time according to client request: */
811
812 expires_hdr = (pjsip_expires_hdr*)
813 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
814 if (expires_hdr) {
815 sub->expires->ivalue = expires_hdr->ivalue;
816 }
817
818 /* Update time. */
819 update_expires(sub, sub->expires->ivalue);
820
821 /* Update Accept header: */
822
823 accept_hdr = (pjsip_accept_hdr*)
824 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
825 if (accept_hdr)
826 sub->accept = pjsip_hdr_clone(sub->pool, accept_hdr);
827
828 /* We can start the session: */
829
830 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
831 sub->pending_tsx++;
832 tsx->mod_data[mod_evsub.mod.id] = sub;
833
834
835 /* Done. */
836 *p_evsub = sub;
837
838
839on_return:
840 pjsip_dlg_dec_lock(dlg);
841 return status;
842}
843
844
845/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000846 * Forcefully destroy subscription.
847 */
848PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
849 pj_bool_t notify )
850{
851 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
852
853 pjsip_dlg_inc_lock(sub->dlg);
854
855 if (sub->pending_tsx) {
856 pj_assert(!"Unable to terminate when there's pending tsx");
857 pjsip_dlg_dec_lock(sub->dlg);
858 return PJ_EINVALIDOP;
859 }
860
861 sub->call_cb = notify;
862 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
863
864 pjsip_dlg_dec_lock(sub->dlg);
865 return PJ_SUCCESS;
866}
867
868/*
Benny Prijono834aee32006-02-19 01:38:06 +0000869 * Get subscription state.
870 */
871PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
872{
873 return sub->state;
874}
875
876/*
877 * Get state name.
878 */
879PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
880{
881 return sub->state_str.ptr;
882}
883
884
885/*
886 * Initiate client subscription
887 */
888PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
889 const pjsip_method *method,
890 pj_int32_t expires,
891 pjsip_tx_data **p_tdata)
892{
893 pjsip_tx_data *tdata;
894 pj_status_t status;
895
896 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
897
898 /* Use SUBSCRIBE if method is not specified */
899 if (method == NULL)
900 method = &pjsip_subscribe_method;
901
902 pjsip_dlg_inc_lock(sub->dlg);
903
904 /* Update method: */
905 if (sub->state == PJSIP_EVSUB_STATE_NULL)
906 pjsip_method_copy(sub->pool, &sub->method, method);
907
908 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
909 if (status != PJ_SUCCESS)
910 goto on_return;
911
912
913 /* Add Event header: */
914 pjsip_msg_add_hdr( tdata->msg,
915 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
916
917 /* Update and add expires header: */
918 if (expires >= 0)
919 sub->expires->ivalue = expires;
920 pjsip_msg_add_hdr( tdata->msg,
921 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
922
923 /* Add Accept header: */
924 pjsip_msg_add_hdr( tdata->msg,
925 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
926
927
928 /* Add Allow-Events header: */
929 pjsip_msg_add_hdr( tdata->msg,
930 pjsip_hdr_shallow_clone(tdata->pool,
931 mod_evsub.allow_events_hdr));
932
933
934 *p_tdata = tdata;
935
936
937on_return:
938
939 pjsip_dlg_dec_lock(sub->dlg);
940 return status;
941}
942
943
944/*
945 * Accept incoming subscription request.
946 */
947PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
948 pjsip_rx_data *rdata,
949 int st_code,
950 const pjsip_hdr *hdr_list )
951{
952 pjsip_tx_data *tdata;
953 pjsip_transaction *tsx;
954 pj_status_t status;
955
956 /* Check arguments */
957 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
958
959 /* Can only be for server subscription: */
960 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
961
962 /* Only expect 2xx status code (for now) */
963 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
964
965 /* Subscription MUST have been attached to the transaction.
966 * Initial subscription request will be attached on evsub_create_uas(),
967 * while subsequent requests will be attached in tsx_state()
968 */
969 tsx = pjsip_rdata_get_tsx(rdata);
970 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
971 PJ_EINVALIDOP);
972
973 /* Lock dialog */
974 pjsip_dlg_inc_lock(sub->dlg);
975
976 /* Create response: */
977 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
978 &tdata);
979 if (status != PJ_SUCCESS)
980 goto on_return;
981
982
983 /* Add expires header: */
984 pjsip_msg_add_hdr( tdata->msg,
985 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
986
Benny Prijonob0808372006-03-02 21:18:58 +0000987 /* Add additional header, if any. */
988 if (hdr_list) {
989 const pjsip_hdr *hdr = hdr_list->next;
990 while (hdr != hdr_list) {
991 pjsip_msg_add_hdr( tdata->msg,
992 pjsip_hdr_clone(tdata->pool, hdr));
993 hdr = hdr->next;
994 }
995 }
Benny Prijono834aee32006-02-19 01:38:06 +0000996
997 /* Send the response: */
998 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
999 if (status != PJ_SUCCESS)
1000 goto on_return;
1001
1002
1003on_return:
1004
1005 pjsip_dlg_dec_lock(sub->dlg);
1006 return status;
1007}
1008
1009
1010/*
1011 * Create Subscription-State header based on current server subscription
1012 * state.
1013 */
1014static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1015 pjsip_evsub *sub,
1016 pjsip_evsub_state state,
1017 const pj_str_t *state_str,
1018 const pj_str_t *reason )
1019{
1020 pjsip_sub_state_hdr *sub_state;
1021 pj_time_val now, delay;
1022
1023 /* Get the remaining time before refresh is required */
1024 pj_gettimeofday(&now);
1025 delay = sub->refresh_time;
1026 PJ_TIME_VAL_SUB(delay, now);
1027
1028 /* Create the Subscription-State header */
1029 sub_state = pjsip_sub_state_hdr_create(pool);
1030
1031 /* Fill up the header */
1032 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001033 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001034 case PJSIP_EVSUB_STATE_SENT:
1035 case PJSIP_EVSUB_STATE_ACCEPTED:
1036 pj_assert(!"Invalid state!");
1037 /* Treat as pending */
1038
1039 case PJSIP_EVSUB_STATE_PENDING:
1040 sub_state->sub_state = STR_PENDING;
1041 sub_state->expires_param = delay.sec;
1042 break;
1043
1044 case PJSIP_EVSUB_STATE_ACTIVE:
1045 sub_state->sub_state = STR_ACTIVE;
1046 sub_state->expires_param = delay.sec;
1047 break;
1048
1049 case PJSIP_EVSUB_STATE_TERMINATED:
1050 sub_state->sub_state = STR_TERMINATED;
1051 if (reason != NULL)
1052 pj_strdup(pool, &sub_state->reason_param, reason);
1053 break;
1054
1055 case PJSIP_EVSUB_STATE_UNKNOWN:
1056 pj_assert(state_str != NULL);
1057 pj_strdup(pool, &sub_state->sub_state, state_str);
1058 break;
1059 }
1060
1061 return sub_state;
1062}
1063
1064/*
1065 * Create and send NOTIFY request.
1066 */
1067PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1068 pjsip_evsub_state state,
1069 const pj_str_t *state_str,
1070 const pj_str_t *reason,
1071 pjsip_tx_data **p_tdata)
1072{
1073 pjsip_tx_data *tdata;
1074 pjsip_sub_state_hdr *sub_state;
1075 pj_status_t status;
1076
1077 /* Check arguments. */
1078 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1079
1080 /* Lock dialog. */
1081 pjsip_dlg_inc_lock(sub->dlg);
1082
1083 /* Create NOTIFY request */
1084 status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1,
1085 &tdata);
1086 if (status != PJ_SUCCESS)
1087 goto on_return;
1088
1089 /* Add Event header */
1090 pjsip_msg_add_hdr(tdata->msg,
1091 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1092
1093 /* Add Subscription-State header */
1094 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1095 reason);
1096 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1097
1098 /* Add Allow-Events header */
1099 pjsip_msg_add_hdr(tdata->msg,
1100 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1101
1102 /* Add Authentication headers. */
1103 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1104
1105
1106
1107 /* Save destination state. */
1108 sub->dst_state = state;
1109 if (state_str)
1110 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1111 else
1112 sub->dst_state_str.slen = 0;
1113
1114
1115 *p_tdata = tdata;
1116
1117on_return:
1118 /* Unlock dialog */
1119 pjsip_dlg_dec_lock(sub->dlg);
1120 return status;
1121}
1122
1123
1124/*
1125 * Create NOTIFY to reflect current status.
1126 */
1127PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1128 pjsip_tx_data **p_tdata )
1129{
1130 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1131 NULL, p_tdata );
1132}
1133
1134
1135/*
1136 * Send request.
1137 */
1138PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1139 pjsip_tx_data *tdata)
1140{
1141 pj_status_t status;
1142
1143 /* Must be request message. */
1144 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1145 PJSIP_ENOTREQUESTMSG);
1146
1147 /* Lock */
1148 pjsip_dlg_inc_lock(sub->dlg);
1149
1150 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001151 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001152 if (status != PJ_SUCCESS)
1153 goto on_return;
1154
1155
1156 /* Special case for NOTIFY:
1157 * The new state was set in pjsip_evsub_notify(), but we apply the
1158 * new state now, when the request was actually sent.
1159 */
1160 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1161 &pjsip_notify_method)==0)
1162 {
1163 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1164 {goto on_return;});
1165
1166 set_state(sub, sub->dst_state,
1167 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
1168 NULL);
1169
1170 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1171 sub->dst_state_str.slen = 0;
1172
1173 }
1174
1175
1176on_return:
1177 pjsip_dlg_dec_lock(sub->dlg);
1178 return status;
1179}
1180
1181
1182
1183/*
1184 * Attach subscription session to newly created transaction, if appropriate.
1185 */
1186static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1187 pjsip_event *event)
1188{
1189 /*
1190 * Newly created transaction will not have subscription session
1191 * attached to it. Find the subscription session from the dialog,
1192 * by matching the Event header.
1193 */
1194 pjsip_dialog *dlg;
1195 pjsip_event_hdr *event_hdr;
1196 pjsip_msg *msg;
1197 struct dlgsub *dlgsub_head, *dlgsub;
1198 pjsip_evsub *sub;
1199
1200 dlg = pjsip_tsx_get_dlg(tsx);
1201 if (!dlg) {
1202 pj_assert(!"Transaction should have a dialog instance!");
1203 return NULL;
1204 }
1205
Benny Prijono26ff9062006-02-21 23:47:00 +00001206
Benny Prijono834aee32006-02-19 01:38:06 +00001207 switch (event->body.tsx_state.type) {
1208 case PJSIP_EVENT_RX_MSG:
1209 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1210 break;
1211 case PJSIP_EVENT_TX_MSG:
1212 msg = event->body.tsx_state.src.tdata->msg;
1213 break;
1214 default:
1215 if (tsx->role == PJSIP_ROLE_UAC)
1216 msg = tsx->last_tx->msg;
1217 else
1218 msg = NULL;
1219 break;
1220 }
1221
1222 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001223 //Note:
1224 // this transaction can be other transaction in the dialog.
1225 // The assertion below probably only valid for dialog that
1226 // only has one event subscription usage.
1227 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001228 return NULL;
1229 }
1230
1231 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1232 if (!event_hdr) {
1233 /* Not subscription related message */
1234 return NULL;
1235 }
1236
1237 /* Find the subscription in the dialog, based on the content
1238 * of Event header:
1239 */
1240
1241 dlgsub_head = dlg->mod_data[mod_evsub.mod.id];
1242 if (dlgsub_head == NULL) {
1243 dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub));
1244 pj_list_init(dlgsub_head);
1245 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1246 }
1247 dlgsub = dlgsub_head->next;
1248
1249 while (dlgsub != dlgsub_head) {
1250
Benny Prijono26ff9062006-02-21 23:47:00 +00001251 if (pj_stricmp(&dlgsub->sub->event->event_type,
1252 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001253 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001254 /* Event type matched.
1255 * Check if event ID matched too.
1256 */
1257 if (pj_strcmp(&dlgsub->sub->event->id_param,
1258 &event_hdr->id_param)==0)
1259 {
1260
1261 break;
1262
1263 }
1264 /*
1265 * Otherwise if it is an UAC subscription, AND
1266 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1267 * the session's event id is NULL, AND
1268 * the incoming request is NOTIFY with event ID, then
1269 * we consider it as a match, and update the
1270 * session's event id.
1271 */
1272 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1273 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1274 dlgsub->sub->event->id_param.slen==0 &&
1275 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1276 {
1277 /* Update session's event id. */
1278 pj_strdup(dlgsub->sub->pool,
1279 &dlgsub->sub->event->id_param,
1280 &event_hdr->id_param);
1281
1282 break;
1283 }
Benny Prijono834aee32006-02-19 01:38:06 +00001284 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001285
1286
1287
Benny Prijono834aee32006-02-19 01:38:06 +00001288 dlgsub = dlgsub->next;
1289 }
1290
1291 if (dlgsub == dlgsub_head) {
1292 /* This could be incoming request to create new subscription */
1293 PJ_LOG(4,(THIS_FILE,
1294 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001295 (int)tsx->method.name.slen,
1296 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001297 (int)event_hdr->event_type.slen,
1298 event_hdr->event_type.ptr,
1299 (int)event_hdr->id_param.slen,
1300 event_hdr->id_param.ptr));
1301
1302 /* If this is an incoming NOTIFY, reject with 481 */
1303 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1304 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1305 {
1306 pj_str_t reason = pj_str("Subscription Does Not Exist");
1307 pjsip_tx_data *tdata;
1308 pj_status_t status;
1309
1310 status = pjsip_dlg_create_response(dlg,
1311 event->body.tsx_state.src.rdata,
1312 481, &reason,
1313 &tdata);
1314 if (status == PJ_SUCCESS) {
1315 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1316 }
1317 }
1318 return NULL;
1319 }
1320
1321 /* Found! */
1322 sub = dlgsub->sub;
1323
1324 /* Attach session to the transaction */
1325 tsx->mod_data[mod_evsub.mod.id] = sub;
1326 sub->pending_tsx++;
1327
Benny Prijono69b98ab2006-03-03 10:23:35 +00001328 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1329 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1330 * transaction is started while previous one still alive, terminate
1331 * the older one.
1332 *
1333 * Sample scenario:
1334 * - subscribe sent to destination that doesn't exist, transaction
1335 * is still retransmitting request, then unsubscribe is sent.
1336 */
1337 if (tsx->role == PJSIP_ROLE_UAC &&
1338 tsx->state == PJSIP_TSX_STATE_CALLING &&
1339 pjsip_method_cmp(&tsx->method, &sub->method) == 0)
1340 {
1341
1342 if (sub->pending_sub &&
1343 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1344 {
1345 PJ_LOG(4,(sub->obj_name,
1346 "Cancelling pending %.*s request",
1347 (int)sub->method.name.slen, sub->method.name.ptr));
1348
1349 /* By convention, we use 490 (Request Updated) status code.
1350 * When transaction handler (below) see this status code, it
1351 * will ignore the transaction.
1352 */
1353 pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1354 }
1355
1356 sub->pending_sub = tsx;
1357
Benny Prijono69b98ab2006-03-03 10:23:35 +00001358 }
1359
Benny Prijono834aee32006-02-19 01:38:06 +00001360 return sub;
1361}
1362
1363
1364/*
1365 * Create response, adding custome headers and msg body.
1366 */
1367static pj_status_t create_response( pjsip_evsub *sub,
1368 pjsip_rx_data *rdata,
1369 int st_code,
1370 const pj_str_t *st_text,
1371 const pjsip_hdr *res_hdr,
1372 const pjsip_msg_body *body,
1373 pjsip_tx_data **p_tdata)
1374{
1375 pjsip_tx_data *tdata;
1376 pjsip_hdr *hdr;
1377 pj_status_t status;
1378
1379 status = pjsip_dlg_create_response(sub->dlg, rdata,
1380 st_code, st_text, &tdata);
1381 if (status != PJ_SUCCESS)
1382 return status;
1383
1384 *p_tdata = tdata;
1385
1386 /* Add response headers. */
1387 hdr = res_hdr->next;
1388 while (hdr != res_hdr) {
1389 pjsip_msg_add_hdr( tdata->msg,
1390 pjsip_hdr_clone(tdata->pool, hdr));
1391 hdr = hdr->next;
1392 }
1393
1394 /* Add msg body, if any */
1395 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001396 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1397 if (tdata->msg->body == NULL) {
1398
1399 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1400
Benny Prijono834aee32006-02-19 01:38:06 +00001401 /* Ignore */
1402 return PJ_SUCCESS;
1403 }
1404 }
1405
1406 return PJ_SUCCESS;
1407}
1408
1409/*
1410 * Get subscription state from the value of Subscription-State header.
1411 */
1412static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1413 pjsip_evsub_state *state,
1414 pj_str_t **state_str )
1415{
1416 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1417
1418 *state = PJSIP_EVSUB_STATE_TERMINATED;
1419 *state_str = NULL;
1420
1421 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1422
1423 *state = PJSIP_EVSUB_STATE_ACTIVE;
1424 *state_str = NULL;
1425
1426 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1427
1428 *state = PJSIP_EVSUB_STATE_PENDING;
1429 *state_str = NULL;
1430
1431 } else {
1432
1433 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1434 *state_str = &sub_state->sub_state;
1435
1436 }
1437}
1438
1439/*
1440 * Transaction event processing by UAC, after subscription is sent.
1441 */
1442static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1443 pjsip_event *event )
1444{
1445
1446 if (pjsip_method_cmp(&tsx->method, &sub->method)==0) {
1447
1448 /* Received response to outgoing request that establishes/refresh
1449 * subscription.
1450 */
1451
1452 /* First time initial request is sent. */
1453 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1454 tsx->state == PJSIP_TSX_STATE_CALLING)
1455 {
1456 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
1457 return;
1458 }
1459
1460 /* Only interested in final response */
1461 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1462 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1463 {
1464 return;
1465 }
1466
Benny Prijono1d8d6082006-04-29 12:38:25 +00001467 /* Clear pending subscription */
1468 if (tsx == sub->pending_sub)
1469 sub->pending_sub = NULL;
1470
Benny Prijono834aee32006-02-19 01:38:06 +00001471 /* Handle authentication. */
1472 if (tsx->status_code==401 || tsx->status_code==407) {
1473 pjsip_tx_data *tdata;
1474 pj_status_t status;
1475
1476 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1477 /* Previously failed transaction has terminated */
1478 return;
1479 }
1480
1481 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1482 event->body.tsx_state.src.rdata,
1483 tsx->last_tx, &tdata);
1484 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001485 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001486
1487 if (status != PJ_SUCCESS) {
1488 /* Authentication failed! */
1489 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1490 NULL,
1491 event);
1492 return;
1493 }
1494
1495 return;
1496 }
1497
1498 if (tsx->status_code/100 == 2) {
1499
1500 /* Successfull SUBSCRIBE request!
1501 * This could be:
1502 * - response to initial SUBSCRIBE request
1503 * - response to subsequent refresh
1504 * - response to unsubscription
1505 */
1506
1507 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1508 /* Ignore; this transaction has been processed before */
1509 return;
1510 }
1511
1512 /* Update UAC refresh time, if response contains Expires header,
1513 * only when we're not unsubscribing.
1514 */
1515 if (sub->expires->ivalue != 0) {
1516 pjsip_msg *msg;
1517 pjsip_expires_hdr *expires;
1518
1519 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1520 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1521 if (expires) {
1522 sub->expires->ivalue = expires->ivalue;
1523 }
1524 }
1525
1526 /* Update time */
1527 update_expires(sub, sub->expires->ivalue);
1528
1529 /* Start UAC refresh timer, only when we're not unsubscribing */
1530 if (sub->expires->ivalue != 0) {
1531 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1532 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1533
1534 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1535 timeout));
1536 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1537
1538 } else {
1539 /* Otherwise set timer to terminate client subscription when
1540 * NOTIFY to end subscription is not received.
1541 */
1542 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1543 }
1544
1545 /* Set state, if necessary */
1546 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1547 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
1548 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
1549 }
1550
1551 } else {
1552
1553 /* Failed SUBSCRIBE request!
1554 *
1555 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1556 * other than 481, the subscription is still considered valid for
1557 * the duration of the last Expires.
1558 *
1559 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1560 * expiration, theoritically the expiration is still valid for the
1561 * next 5 seconds even when we receive non-481 failed response.
1562 *
1563 * Ah, what the heck!
1564 *
1565 * Just terminate now!
1566 *
1567 */
1568
1569 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1570 /* Ignore, has been handled before */
1571 return;
1572 }
1573
Benny Prijono69b98ab2006-03-03 10:23:35 +00001574 /* Ignore 490 (Request Updated) status.
1575 * This happens when application sends SUBSCRIBE/REFER while
1576 * another one is still in progress.
1577 */
1578 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1579 return;
1580 }
1581
Benny Prijono834aee32006-02-19 01:38:06 +00001582 /* Kill any timer. */
1583 set_timer(sub, TIMER_TYPE_NONE, 0);
1584
1585 /* Set state to TERMINATED */
1586 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1587 NULL, event);
1588
1589 }
1590
1591 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1592
1593 /* Incoming NOTIFY.
1594 * This can be the result of:
1595 * - Initial subscription response
1596 * - UAS updating the resource info.
1597 * - Unsubscription response.
1598 */
1599 int st_code = 200;
1600 pj_str_t *st_text = NULL;
1601 pjsip_hdr res_hdr;
1602 pjsip_msg_body *body = NULL;
1603
1604 pjsip_rx_data *rdata;
1605 pjsip_msg *msg;
1606 pjsip_sub_state_hdr *sub_state;
1607
1608 pjsip_evsub_state new_state;
1609 pj_str_t *new_state_str;
1610
1611 pjsip_tx_data *tdata;
1612 pj_status_t status;
1613 int next_refresh;
1614
1615 /* Only want to handle initial NOTIFY receive event. */
1616 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1617 return;
1618
1619
1620 rdata = event->body.tsx_state.src.rdata;
1621 msg = rdata->msg_info.msg;
1622
1623 pj_list_init(&res_hdr);
1624
1625 /* Get subscription state header. */
1626 sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
1627 if (sub_state == NULL) {
1628
1629 pjsip_warning_hdr *warn_hdr;
1630 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1631
1632 /* Bad request! Add warning header. */
1633 st_code = PJSIP_SC_BAD_REQUEST;
1634 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1635 pjsip_endpt_name(sub->endpt),
1636 &warn_text);
1637 pj_list_push_back(&res_hdr, warn_hdr);
1638 }
1639
1640 /* Call application registered callback to handle incoming NOTIFY,
1641 * if any.
1642 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001643 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001644 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1645 &res_hdr, &body);
1646
1647 /* Application MUST specify final response! */
1648 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1649
1650 /* Must be a valid status code */
1651 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1652 }
1653
1654
1655 /* If non-2xx should be returned, then send the response.
1656 * No need to update server subscription state.
1657 */
1658 if (st_code >= 300) {
1659 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1660 body, &tdata);
1661 if (status == PJ_SUCCESS) {
1662 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1663 }
1664
1665 /* Start timer to terminate subscription, just in case server
1666 * is not able to generate NOTIFY to our response.
1667 */
1668 if (status == PJ_SUCCESS) {
1669 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1670 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1671 } else {
1672 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1673 }
1674
1675 return;
1676 }
1677
1678 /* Update expiration from the value of expires param in
1679 * Subscription-State header, but ONLY when subscription state
1680 * is "active" or "pending", AND the header contains expires param.
1681 */
1682 if (sub->expires->ivalue != 0 &&
1683 sub_state->expires_param >= 0 &&
1684 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1685 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1686 {
1687 next_refresh = sub_state->expires_param;
1688
1689 } else {
1690 next_refresh = sub->expires->ivalue;
1691 }
1692
1693 /* Update time */
1694 update_expires(sub, next_refresh);
1695
1696 /* Start UAC refresh timer, only when we're not unsubscribing */
1697 if (sub->expires->ivalue != 0) {
1698 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1699 next_refresh - TIME_UAC_REFRESH : next_refresh;
1700
1701 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1702 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1703 }
1704
1705 /* Find out the state */
1706 get_hdr_state(sub_state, &new_state, &new_state_str);
1707
1708 /* Send response. */
1709 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1710 body, &tdata);
1711 if (status == PJ_SUCCESS)
1712 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1713
1714 /* Set the state */
1715 if (status == PJ_SUCCESS) {
1716 set_state(sub, new_state, new_state_str, event);
1717 } else {
1718 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1719 }
1720
1721
1722 } else {
1723
1724 /*
1725 * Unexpected method!
1726 */
1727 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1728 (int)tsx->method.name.slen, tsx->method.name.ptr));
1729 }
1730}
1731
1732
1733/*
1734 * Transaction event processing by UAS, after subscription is accepted.
1735 */
1736static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1737 pjsip_event *event)
1738{
1739
1740 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0) {
1741
1742 /*
1743 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1744 *
1745 */
1746 pjsip_rx_data *rdata;
1747 pjsip_event_hdr *event_hdr;
1748 pjsip_expires_hdr *expires;
1749 pjsip_msg *msg;
1750 pjsip_tx_data *tdata;
1751 int st_code = 200;
1752 pj_str_t *st_text = NULL;
1753 pjsip_hdr res_hdr;
1754 pjsip_msg_body *body = NULL;
1755 pjsip_evsub_state old_state;
1756 pj_str_t old_state_str;
1757 pj_status_t status;
1758
1759
1760 /* Only wants to handle the first event when the request is
1761 * received.
1762 */
1763 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1764 return;
1765
1766 rdata = event->body.tsx_state.src.rdata;
1767 msg = rdata->msg_info.msg;
1768
1769 /* Set expiration time based on client request (in Expires header),
1770 * or package default expiration time.
1771 */
1772 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1773 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1774 if (event_hdr && expires) {
1775 struct evpkg *evpkg;
1776
1777 evpkg = find_pkg(&event_hdr->event_type);
1778 if (evpkg) {
1779 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1780 sub->expires->ivalue = expires->ivalue;
1781 else
1782 sub->expires->ivalue = evpkg->pkg_expires;
1783 }
1784 }
1785
1786 /* Update time (before calling on_rx_refresh, since application
1787 * will send NOTIFY.
1788 */
1789 update_expires(sub, sub->expires->ivalue);
1790
1791
1792 /* Save old state.
1793 * If application respond with non-2xx, revert to old state.
1794 */
1795 old_state = sub->state;
1796 old_state_str = sub->state_str;
1797
1798 if (sub->expires->ivalue == 0) {
1799 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1800 sub->state_str = evsub_state_names[sub->state];
1801 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1802 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1803 sub->state_str = evsub_state_names[sub->state];
1804 }
1805
1806 /* Call application's on_rx_refresh, just in case it wants to send
1807 * response other than 200 (OK)
1808 */
1809 pj_list_init(&res_hdr);
1810
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001811 if (sub->user.on_rx_refresh && sub->call_cb) {
1812 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1813 &res_hdr, &body);
1814 }
Benny Prijono834aee32006-02-19 01:38:06 +00001815
1816 /* Application MUST specify final response! */
1817 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1818
1819 /* Must be a valid status code */
1820 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1821
1822
1823 /* Create and send response */
1824 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1825 body, &tdata);
1826 if (status == PJ_SUCCESS) {
1827 /* Add expires header: */
1828 pjsip_msg_add_hdr( tdata->msg,
1829 pjsip_hdr_shallow_clone(tdata->pool,
1830 sub->expires));
1831
1832 /* Send */
1833 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1834 }
1835
1836 /* Update state or revert state */
1837 if (st_code/100==2) {
1838
1839 if (sub->expires->ivalue == 0) {
1840 set_state(sub, sub->state, NULL, event);
1841 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1842 set_state(sub, sub->state, NULL, event);
1843 }
1844
1845 /* Set UAS timeout timer, when state is not terminated. */
1846 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1847 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1848 sub->expires->ivalue));
1849 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1850 sub->expires->ivalue);
1851 }
1852
1853 } else {
1854 sub->state = old_state;
1855 sub->state_str = old_state_str;
1856 }
1857
1858
1859 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1860
1861 /* Handle authentication */
1862 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1863 (tsx->status_code==401 || tsx->status_code==407))
1864 {
1865 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1866 pjsip_tx_data *tdata;
1867 pj_status_t status;
1868
1869 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
1870 tsx->last_tx, &tdata);
1871 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001872 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00001873
1874 if (status != PJ_SUCCESS) {
1875 /* Can't authenticate. Terminate session (?) */
1876 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono441ce002006-03-07 15:15:01 +00001877 return;
Benny Prijono834aee32006-02-19 01:38:06 +00001878 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001879
1880 }
1881 /*
1882 * Terminate event usage if we receive 481, 408, and 7 class
1883 * responses.
1884 */
1885 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
1886 (tsx->status_code==481 || tsx->status_code==408 ||
1887 tsx->status_code/100 == 7))
1888 {
1889 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
Benny Prijono441ce002006-03-07 15:15:01 +00001890 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001891 }
Benny Prijono834aee32006-02-19 01:38:06 +00001892
1893 } else {
1894
1895 /*
1896 * Unexpected method!
1897 */
1898 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1899 (int)tsx->method.name.slen, tsx->method.name.ptr));
1900
1901 }
1902}
1903
1904
1905/*
1906 * Notification when transaction state has changed!
1907 */
1908static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
1909{
1910 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
1911
1912 if (sub == NULL) {
1913 sub = on_new_transaction(tsx, event);
1914 if (sub == NULL)
1915 return;
1916 }
1917
1918
1919 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001920 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00001921 (*sub->user.on_tsx_state)(sub, tsx, event);
1922
1923
1924 /* Process the event: */
1925
1926 if (sub->role == PJSIP_ROLE_UAC) {
1927 on_tsx_state_uac(sub, tsx, event);
1928 } else {
1929 on_tsx_state_uas(sub, tsx, event);
1930 }
1931
1932
1933 /* Check transaction TERMINATE event */
1934 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1935
1936 --sub->pending_tsx;
1937
1938 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
1939 sub->pending_tsx == 0)
1940 {
1941 evsub_destroy(sub);
1942 }
1943
1944 }
1945}
1946
1947