blob: e5b4f52601b642d55b0f9ff3ad44e4435219a947 [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
Benny Prijono56315612006-07-18 14:39:40 +0000423 /* Add to endpoint's Accept header */
424 pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod,
425 PJSIP_H_ACCEPT, NULL,
426 pkg->pkg_accept->count,
427 pkg->pkg_accept->values);
428
Benny Prijono834aee32006-02-19 01:38:06 +0000429
430 /* Done */
431
432 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
433 (int)event_name->slen, event_name->ptr,
434 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
435
436 return PJ_SUCCESS;
437}
438
439
Benny Prijono56315612006-07-18 14:39:40 +0000440/*
441 * Retrieve Allow-Events header
442 */
443PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m)
444{
445 struct mod_evsub *mod;
446
447 if (m == NULL)
448 m = pjsip_evsub_instance();
449
450 mod = (struct mod_evsub*)m;
451
452 return (pjsip_hdr*) mod->allow_events_hdr;
453}
454
Benny Prijono834aee32006-02-19 01:38:06 +0000455
456/*
457 * Update expiration time.
458 */
459static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
460{
461 pj_gettimeofday(&sub->refresh_time);
462 sub->refresh_time.sec += interval;
463}
464
465
466/*
467 * Schedule timer.
468 */
469static void set_timer( pjsip_evsub *sub, int timer_id,
470 pj_int32_t seconds)
471{
472 if (sub->timer.id != TIMER_TYPE_NONE) {
473 PJ_LOG(5,(sub->obj_name, "%s %s timer",
474 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
475 timer_names[sub->timer.id]));
476 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
477 sub->timer.id = TIMER_TYPE_NONE;
478 }
479
480 if (timer_id != TIMER_TYPE_NONE) {
481 pj_time_val timeout;
482
483 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000484 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
485 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000486
487 timeout.sec = seconds;
488 timeout.msec = 0;
489 sub->timer.id = timer_id;
490
491 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
492
493 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
494 timer_names[sub->timer.id], timeout.sec));
495 }
496}
497
498
499/*
500 * Destroy session.
501 */
502static void evsub_destroy( pjsip_evsub *sub )
503{
504 struct dlgsub *dlgsub_head, *dlgsub;
505
506 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
507
508 /* Kill timer */
509 set_timer(sub, TIMER_TYPE_NONE, 0);
510
511 /* Remote this session from dialog's list of subscription */
512 dlgsub_head = sub->dlg->mod_data[mod_evsub.mod.id];
513 dlgsub = dlgsub_head->next;
514 while (dlgsub != dlgsub_head) {
515
516 if (dlgsub->sub == sub) {
517 pj_list_erase(dlgsub);
518 break;
519 }
520
521 dlgsub = dlgsub->next;
522 }
523
524 /* Decrement dialog's session */
525 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
526}
527
528/*
529 * Set subscription session state.
530 */
531static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
532 const pj_str_t *state_str, pjsip_event *event)
533{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000534 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000535 pj_str_t old_state_str = sub->state_str;
536
537 sub->state = state;
538
539 if (state_str && state_str->slen)
540 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
541 else
542 sub->state_str = evsub_state_names[state];
543
544 PJ_LOG(4,(sub->obj_name,
545 "Subscription state changed %.*s --> %.*s",
546 (int)old_state_str.slen,
547 old_state_str.ptr,
548 (int)sub->state_str.slen,
549 sub->state_str.ptr));
550
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000551 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000552 (*sub->user.on_evsub_state)(sub, event);
553
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000554 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
555 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
556 {
Benny Prijono834aee32006-02-19 01:38:06 +0000557 if (sub->pending_tsx == 0) {
558 evsub_destroy(sub);
559 }
560 }
561}
562
563
564/*
565 * Timer callback.
566 */
567static void on_timer( pj_timer_heap_t *timer_heap,
568 struct pj_timer_entry *entry)
569{
570 pjsip_evsub *sub;
571 int timer_id;
572
573 PJ_UNUSED_ARG(timer_heap);
574
575 sub = entry->user_data;
576
577 pjsip_dlg_inc_lock(sub->dlg);
578
579 timer_id = entry->id;
580 entry->id = TIMER_TYPE_NONE;
581
582 switch (timer_id) {
583
584 case TIMER_TYPE_UAC_REFRESH:
585 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000586 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000587 (*sub->user.on_client_refresh)(sub);
588 } else {
589 pjsip_tx_data *tdata;
590 pj_status_t status;
591
592 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
593 status = pjsip_evsub_initiate(sub, &sub->method,
594 sub->expires->ivalue,
595 &tdata);
596 if (status == PJ_SUCCESS)
597 pjsip_evsub_send_request(sub, tdata);
598 }
599 break;
600
601 case TIMER_TYPE_UAS_TIMEOUT:
602 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000603 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000604 (*sub->user.on_server_timeout)(sub);
605 } else {
606 pjsip_tx_data *tdata;
607 pj_status_t status;
608
609 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
610 "Sending NOTIFY to terminate."));
611 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
612 NULL, &STR_TIMEOUT, &tdata);
613 if (status == PJ_SUCCESS)
614 pjsip_evsub_send_request(sub, tdata);
615 }
616 break;
617
618 case TIMER_TYPE_UAC_TERMINATE:
619 {
Benny Prijono834aee32006-02-19 01:38:06 +0000620 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
621 "Terminating.."));
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000622 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000623 }
624 break;
625
626 case TIMER_TYPE_UAC_WAIT_NOTIFY:
627 {
628 pjsip_tx_data *tdata;
629 pj_status_t status;
630
631 PJ_LOG(5,(sub->obj_name,
632 "Timeout waiting for subsequent NOTIFY (we did "
633 "send non-2xx response for previous NOTIFY). "
634 "Unsubscribing.."));
635 status = pjsip_evsub_initiate( sub, &sub->method, 0, &tdata);
636 if (status == PJ_SUCCESS)
637 pjsip_evsub_send_request(sub, tdata);
638 }
639 break;
640
641 default:
642 pj_assert(!"Invalid timer id");
643 }
644
645 pjsip_dlg_dec_lock(sub->dlg);
646}
647
648
649/*
650 * Create subscription session, used for both client and notifier.
651 */
652static pj_status_t evsub_create( pjsip_dialog *dlg,
653 pjsip_role_e role,
654 const pjsip_evsub_user *user_cb,
655 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000656 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000657 pjsip_evsub **p_evsub )
658{
659 pjsip_evsub *sub;
660 struct evpkg *pkg;
661 struct dlgsub *dlgsub_head, *dlgsub;
662 pj_status_t status;
663
664 /* Make sure there's package register for the event name: */
665
666 pkg = find_pkg(event);
667 if (pkg == NULL)
668 return PJSIP_SIMPLE_ENOPKG;
669
670
671 /* Init attributes: */
672
673 sub = pj_pool_zalloc(dlg->pool, sizeof(struct pjsip_evsub));
674 sub->pool = dlg->pool;
675 sub->endpt = dlg->endpt;
676 sub->dlg = dlg;
677 sub->pkg = pkg;
678 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000679 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000680 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000681 sub->state = PJSIP_EVSUB_STATE_NULL;
682 sub->state_str = evsub_state_names[sub->state];
683 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
684 sub->accept = pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
685
686 sub->timer.user_data = sub;
687 sub->timer.cb = &on_timer;
688
689 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000690 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
691 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000692
693
694 /* Copy callback, if any: */
695 if (user_cb)
696 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
697
698
699 /* Create Event header: */
700 sub->event = pjsip_event_hdr_create(sub->pool);
701 pj_strdup(sub->pool, &sub->event->event_type, event);
702
703
704 /* Create subcription list: */
705
706 dlgsub_head = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
707 dlgsub = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
708 dlgsub->sub = sub;
709
710 pj_list_init(dlgsub_head);
711 pj_list_push_back(dlgsub_head, dlgsub);
712
713
714 /* Register as dialog usage: */
715
716 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
717 if (status != PJ_SUCCESS)
718 return status;
719
720
721 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
722 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
723 dlg->obj_name));
724
725 *p_evsub = sub;
726
727 return PJ_SUCCESS;
728}
729
730
731
732/*
733 * Create client subscription session.
734 */
735PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
736 const pjsip_evsub_user *user_cb,
737 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000738 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000739 pjsip_evsub **p_evsub)
740{
741 pjsip_evsub *sub;
742 pj_status_t status;
743
744 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
745
746 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000747 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000748 if (status != PJ_SUCCESS)
749 goto on_return;
750
Benny Prijono26ff9062006-02-21 23:47:00 +0000751 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
752 * is not specified.
753 */
754 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
755 pj_create_unique_string(sub->pool, &sub->event->id_param);
756 }
Benny Prijono834aee32006-02-19 01:38:06 +0000757
758 /* Increment dlg session. */
759 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
760
761 /* Done */
762 *p_evsub = sub;
763
764on_return:
765 pjsip_dlg_dec_lock(dlg);
766 return status;
767}
768
769
770/*
771 * Create server subscription session from incoming request.
772 */
773PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
774 const pjsip_evsub_user *user_cb,
775 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000776 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000777 pjsip_evsub **p_evsub)
778{
779 pjsip_evsub *sub;
780 pjsip_transaction *tsx;
781 pjsip_accept_hdr *accept_hdr;
782 pjsip_event_hdr *event_hdr;
783 pjsip_expires_hdr *expires_hdr;
784 pj_status_t status;
785
786 /* Check arguments: */
787 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
788
789 /* MUST be request message: */
790 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
791 PJSIP_ENOTREQUESTMSG);
792
793 /* Transaction MUST have been created (in the dialog) */
794 tsx = pjsip_rdata_get_tsx(rdata);
795 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
796
797 /* No subscription must have been attached to transaction */
798 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
799 PJSIP_ETYPEEXISTS);
800
801 /* Package MUST implement on_rx_refresh */
802 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
803
Benny Prijono26ff9062006-02-21 23:47:00 +0000804 /* Request MUST have "Event" header. We need the Event header to get
805 * the package name (don't want to add more arguments in the function).
806 */
Benny Prijono834aee32006-02-19 01:38:06 +0000807 event_hdr = (pjsip_event_hdr*)
808 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
809 if (event_hdr == NULL) {
810 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
811 }
812
813 /* Start locking the mutex: */
814
815 pjsip_dlg_inc_lock(dlg);
816
817 /* Create the session: */
818
819 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000820 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000821 if (status != PJ_SUCCESS)
822 goto on_return;
823
824 /* Just duplicate Event header from the request */
825 sub->event = pjsip_hdr_clone(sub->pool, event_hdr);
826
827 /* Set the method: */
828 pjsip_method_copy(sub->pool, &sub->method,
829 &rdata->msg_info.msg->line.req.method);
830
831 /* Update expiration time according to client request: */
832
833 expires_hdr = (pjsip_expires_hdr*)
834 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
835 if (expires_hdr) {
836 sub->expires->ivalue = expires_hdr->ivalue;
837 }
838
839 /* Update time. */
840 update_expires(sub, sub->expires->ivalue);
841
842 /* Update Accept header: */
843
844 accept_hdr = (pjsip_accept_hdr*)
845 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
846 if (accept_hdr)
847 sub->accept = pjsip_hdr_clone(sub->pool, accept_hdr);
848
849 /* We can start the session: */
850
851 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
852 sub->pending_tsx++;
853 tsx->mod_data[mod_evsub.mod.id] = sub;
854
855
856 /* Done. */
857 *p_evsub = sub;
858
859
860on_return:
861 pjsip_dlg_dec_lock(dlg);
862 return status;
863}
864
865
866/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000867 * Forcefully destroy subscription.
868 */
869PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
870 pj_bool_t notify )
871{
872 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
873
874 pjsip_dlg_inc_lock(sub->dlg);
875
876 if (sub->pending_tsx) {
877 pj_assert(!"Unable to terminate when there's pending tsx");
878 pjsip_dlg_dec_lock(sub->dlg);
879 return PJ_EINVALIDOP;
880 }
881
882 sub->call_cb = notify;
883 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
884
885 pjsip_dlg_dec_lock(sub->dlg);
886 return PJ_SUCCESS;
887}
888
889/*
Benny Prijono834aee32006-02-19 01:38:06 +0000890 * Get subscription state.
891 */
892PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
893{
894 return sub->state;
895}
896
897/*
898 * Get state name.
899 */
900PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
901{
902 return sub->state_str.ptr;
903}
904
905
906/*
907 * Initiate client subscription
908 */
909PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
910 const pjsip_method *method,
911 pj_int32_t expires,
912 pjsip_tx_data **p_tdata)
913{
914 pjsip_tx_data *tdata;
915 pj_status_t status;
916
917 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
918
919 /* Use SUBSCRIBE if method is not specified */
920 if (method == NULL)
921 method = &pjsip_subscribe_method;
922
923 pjsip_dlg_inc_lock(sub->dlg);
924
925 /* Update method: */
926 if (sub->state == PJSIP_EVSUB_STATE_NULL)
927 pjsip_method_copy(sub->pool, &sub->method, method);
928
929 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
930 if (status != PJ_SUCCESS)
931 goto on_return;
932
933
934 /* Add Event header: */
935 pjsip_msg_add_hdr( tdata->msg,
936 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
937
938 /* Update and add expires header: */
939 if (expires >= 0)
940 sub->expires->ivalue = expires;
941 pjsip_msg_add_hdr( tdata->msg,
942 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
943
944 /* Add Accept header: */
945 pjsip_msg_add_hdr( tdata->msg,
946 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
947
948
949 /* Add Allow-Events header: */
950 pjsip_msg_add_hdr( tdata->msg,
951 pjsip_hdr_shallow_clone(tdata->pool,
952 mod_evsub.allow_events_hdr));
953
954
955 *p_tdata = tdata;
956
957
958on_return:
959
960 pjsip_dlg_dec_lock(sub->dlg);
961 return status;
962}
963
964
965/*
966 * Accept incoming subscription request.
967 */
968PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
969 pjsip_rx_data *rdata,
970 int st_code,
971 const pjsip_hdr *hdr_list )
972{
973 pjsip_tx_data *tdata;
974 pjsip_transaction *tsx;
975 pj_status_t status;
976
977 /* Check arguments */
978 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
979
980 /* Can only be for server subscription: */
981 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
982
983 /* Only expect 2xx status code (for now) */
984 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
985
986 /* Subscription MUST have been attached to the transaction.
987 * Initial subscription request will be attached on evsub_create_uas(),
988 * while subsequent requests will be attached in tsx_state()
989 */
990 tsx = pjsip_rdata_get_tsx(rdata);
991 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
992 PJ_EINVALIDOP);
993
994 /* Lock dialog */
995 pjsip_dlg_inc_lock(sub->dlg);
996
997 /* Create response: */
998 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
999 &tdata);
1000 if (status != PJ_SUCCESS)
1001 goto on_return;
1002
1003
1004 /* Add expires header: */
1005 pjsip_msg_add_hdr( tdata->msg,
1006 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1007
Benny Prijonob0808372006-03-02 21:18:58 +00001008 /* Add additional header, if any. */
1009 if (hdr_list) {
1010 const pjsip_hdr *hdr = hdr_list->next;
1011 while (hdr != hdr_list) {
1012 pjsip_msg_add_hdr( tdata->msg,
1013 pjsip_hdr_clone(tdata->pool, hdr));
1014 hdr = hdr->next;
1015 }
1016 }
Benny Prijono834aee32006-02-19 01:38:06 +00001017
1018 /* Send the response: */
1019 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1020 if (status != PJ_SUCCESS)
1021 goto on_return;
1022
1023
1024on_return:
1025
1026 pjsip_dlg_dec_lock(sub->dlg);
1027 return status;
1028}
1029
1030
1031/*
1032 * Create Subscription-State header based on current server subscription
1033 * state.
1034 */
1035static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1036 pjsip_evsub *sub,
1037 pjsip_evsub_state state,
1038 const pj_str_t *state_str,
1039 const pj_str_t *reason )
1040{
1041 pjsip_sub_state_hdr *sub_state;
1042 pj_time_val now, delay;
1043
1044 /* Get the remaining time before refresh is required */
1045 pj_gettimeofday(&now);
1046 delay = sub->refresh_time;
1047 PJ_TIME_VAL_SUB(delay, now);
1048
1049 /* Create the Subscription-State header */
1050 sub_state = pjsip_sub_state_hdr_create(pool);
1051
1052 /* Fill up the header */
1053 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001054 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001055 case PJSIP_EVSUB_STATE_SENT:
1056 case PJSIP_EVSUB_STATE_ACCEPTED:
1057 pj_assert(!"Invalid state!");
1058 /* Treat as pending */
1059
1060 case PJSIP_EVSUB_STATE_PENDING:
1061 sub_state->sub_state = STR_PENDING;
1062 sub_state->expires_param = delay.sec;
1063 break;
1064
1065 case PJSIP_EVSUB_STATE_ACTIVE:
1066 sub_state->sub_state = STR_ACTIVE;
1067 sub_state->expires_param = delay.sec;
1068 break;
1069
1070 case PJSIP_EVSUB_STATE_TERMINATED:
1071 sub_state->sub_state = STR_TERMINATED;
1072 if (reason != NULL)
1073 pj_strdup(pool, &sub_state->reason_param, reason);
1074 break;
1075
1076 case PJSIP_EVSUB_STATE_UNKNOWN:
1077 pj_assert(state_str != NULL);
1078 pj_strdup(pool, &sub_state->sub_state, state_str);
1079 break;
1080 }
1081
1082 return sub_state;
1083}
1084
1085/*
1086 * Create and send NOTIFY request.
1087 */
1088PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1089 pjsip_evsub_state state,
1090 const pj_str_t *state_str,
1091 const pj_str_t *reason,
1092 pjsip_tx_data **p_tdata)
1093{
1094 pjsip_tx_data *tdata;
1095 pjsip_sub_state_hdr *sub_state;
1096 pj_status_t status;
1097
1098 /* Check arguments. */
1099 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1100
1101 /* Lock dialog. */
1102 pjsip_dlg_inc_lock(sub->dlg);
1103
1104 /* Create NOTIFY request */
1105 status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1,
1106 &tdata);
1107 if (status != PJ_SUCCESS)
1108 goto on_return;
1109
1110 /* Add Event header */
1111 pjsip_msg_add_hdr(tdata->msg,
1112 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1113
1114 /* Add Subscription-State header */
1115 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1116 reason);
1117 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1118
1119 /* Add Allow-Events header */
1120 pjsip_msg_add_hdr(tdata->msg,
1121 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1122
1123 /* Add Authentication headers. */
1124 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1125
1126
1127
1128 /* Save destination state. */
1129 sub->dst_state = state;
1130 if (state_str)
1131 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1132 else
1133 sub->dst_state_str.slen = 0;
1134
1135
1136 *p_tdata = tdata;
1137
1138on_return:
1139 /* Unlock dialog */
1140 pjsip_dlg_dec_lock(sub->dlg);
1141 return status;
1142}
1143
1144
1145/*
1146 * Create NOTIFY to reflect current status.
1147 */
1148PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1149 pjsip_tx_data **p_tdata )
1150{
1151 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1152 NULL, p_tdata );
1153}
1154
1155
1156/*
1157 * Send request.
1158 */
1159PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1160 pjsip_tx_data *tdata)
1161{
1162 pj_status_t status;
1163
1164 /* Must be request message. */
1165 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1166 PJSIP_ENOTREQUESTMSG);
1167
1168 /* Lock */
1169 pjsip_dlg_inc_lock(sub->dlg);
1170
1171 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001172 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001173 if (status != PJ_SUCCESS)
1174 goto on_return;
1175
1176
1177 /* Special case for NOTIFY:
1178 * The new state was set in pjsip_evsub_notify(), but we apply the
1179 * new state now, when the request was actually sent.
1180 */
1181 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1182 &pjsip_notify_method)==0)
1183 {
1184 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1185 {goto on_return;});
1186
1187 set_state(sub, sub->dst_state,
1188 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
1189 NULL);
1190
1191 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1192 sub->dst_state_str.slen = 0;
1193
1194 }
1195
1196
1197on_return:
1198 pjsip_dlg_dec_lock(sub->dlg);
1199 return status;
1200}
1201
1202
1203
1204/*
1205 * Attach subscription session to newly created transaction, if appropriate.
1206 */
1207static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1208 pjsip_event *event)
1209{
1210 /*
1211 * Newly created transaction will not have subscription session
1212 * attached to it. Find the subscription session from the dialog,
1213 * by matching the Event header.
1214 */
1215 pjsip_dialog *dlg;
1216 pjsip_event_hdr *event_hdr;
1217 pjsip_msg *msg;
1218 struct dlgsub *dlgsub_head, *dlgsub;
1219 pjsip_evsub *sub;
1220
1221 dlg = pjsip_tsx_get_dlg(tsx);
1222 if (!dlg) {
1223 pj_assert(!"Transaction should have a dialog instance!");
1224 return NULL;
1225 }
1226
Benny Prijono26ff9062006-02-21 23:47:00 +00001227
Benny Prijono834aee32006-02-19 01:38:06 +00001228 switch (event->body.tsx_state.type) {
1229 case PJSIP_EVENT_RX_MSG:
1230 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1231 break;
1232 case PJSIP_EVENT_TX_MSG:
1233 msg = event->body.tsx_state.src.tdata->msg;
1234 break;
1235 default:
1236 if (tsx->role == PJSIP_ROLE_UAC)
1237 msg = tsx->last_tx->msg;
1238 else
1239 msg = NULL;
1240 break;
1241 }
1242
1243 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001244 //Note:
1245 // this transaction can be other transaction in the dialog.
1246 // The assertion below probably only valid for dialog that
1247 // only has one event subscription usage.
1248 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001249 return NULL;
1250 }
1251
1252 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1253 if (!event_hdr) {
1254 /* Not subscription related message */
1255 return NULL;
1256 }
1257
1258 /* Find the subscription in the dialog, based on the content
1259 * of Event header:
1260 */
1261
1262 dlgsub_head = dlg->mod_data[mod_evsub.mod.id];
1263 if (dlgsub_head == NULL) {
1264 dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub));
1265 pj_list_init(dlgsub_head);
1266 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1267 }
1268 dlgsub = dlgsub_head->next;
1269
1270 while (dlgsub != dlgsub_head) {
1271
Benny Prijono26ff9062006-02-21 23:47:00 +00001272 if (pj_stricmp(&dlgsub->sub->event->event_type,
1273 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001274 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001275 /* Event type matched.
1276 * Check if event ID matched too.
1277 */
1278 if (pj_strcmp(&dlgsub->sub->event->id_param,
1279 &event_hdr->id_param)==0)
1280 {
1281
1282 break;
1283
1284 }
1285 /*
1286 * Otherwise if it is an UAC subscription, AND
1287 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1288 * the session's event id is NULL, AND
1289 * the incoming request is NOTIFY with event ID, then
1290 * we consider it as a match, and update the
1291 * session's event id.
1292 */
1293 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1294 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1295 dlgsub->sub->event->id_param.slen==0 &&
1296 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1297 {
1298 /* Update session's event id. */
1299 pj_strdup(dlgsub->sub->pool,
1300 &dlgsub->sub->event->id_param,
1301 &event_hdr->id_param);
1302
1303 break;
1304 }
Benny Prijono834aee32006-02-19 01:38:06 +00001305 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001306
1307
1308
Benny Prijono834aee32006-02-19 01:38:06 +00001309 dlgsub = dlgsub->next;
1310 }
1311
1312 if (dlgsub == dlgsub_head) {
1313 /* This could be incoming request to create new subscription */
1314 PJ_LOG(4,(THIS_FILE,
1315 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001316 (int)tsx->method.name.slen,
1317 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001318 (int)event_hdr->event_type.slen,
1319 event_hdr->event_type.ptr,
1320 (int)event_hdr->id_param.slen,
1321 event_hdr->id_param.ptr));
1322
1323 /* If this is an incoming NOTIFY, reject with 481 */
1324 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1325 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1326 {
1327 pj_str_t reason = pj_str("Subscription Does Not Exist");
1328 pjsip_tx_data *tdata;
1329 pj_status_t status;
1330
1331 status = pjsip_dlg_create_response(dlg,
1332 event->body.tsx_state.src.rdata,
1333 481, &reason,
1334 &tdata);
1335 if (status == PJ_SUCCESS) {
1336 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1337 }
1338 }
1339 return NULL;
1340 }
1341
1342 /* Found! */
1343 sub = dlgsub->sub;
1344
1345 /* Attach session to the transaction */
1346 tsx->mod_data[mod_evsub.mod.id] = sub;
1347 sub->pending_tsx++;
1348
Benny Prijono69b98ab2006-03-03 10:23:35 +00001349 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1350 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1351 * transaction is started while previous one still alive, terminate
1352 * the older one.
1353 *
1354 * Sample scenario:
1355 * - subscribe sent to destination that doesn't exist, transaction
1356 * is still retransmitting request, then unsubscribe is sent.
1357 */
1358 if (tsx->role == PJSIP_ROLE_UAC &&
1359 tsx->state == PJSIP_TSX_STATE_CALLING &&
1360 pjsip_method_cmp(&tsx->method, &sub->method) == 0)
1361 {
1362
1363 if (sub->pending_sub &&
1364 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1365 {
1366 PJ_LOG(4,(sub->obj_name,
1367 "Cancelling pending %.*s request",
1368 (int)sub->method.name.slen, sub->method.name.ptr));
1369
1370 /* By convention, we use 490 (Request Updated) status code.
1371 * When transaction handler (below) see this status code, it
1372 * will ignore the transaction.
1373 */
1374 pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1375 }
1376
1377 sub->pending_sub = tsx;
1378
Benny Prijono69b98ab2006-03-03 10:23:35 +00001379 }
1380
Benny Prijono834aee32006-02-19 01:38:06 +00001381 return sub;
1382}
1383
1384
1385/*
1386 * Create response, adding custome headers and msg body.
1387 */
1388static pj_status_t create_response( pjsip_evsub *sub,
1389 pjsip_rx_data *rdata,
1390 int st_code,
1391 const pj_str_t *st_text,
1392 const pjsip_hdr *res_hdr,
1393 const pjsip_msg_body *body,
1394 pjsip_tx_data **p_tdata)
1395{
1396 pjsip_tx_data *tdata;
1397 pjsip_hdr *hdr;
1398 pj_status_t status;
1399
1400 status = pjsip_dlg_create_response(sub->dlg, rdata,
1401 st_code, st_text, &tdata);
1402 if (status != PJ_SUCCESS)
1403 return status;
1404
1405 *p_tdata = tdata;
1406
1407 /* Add response headers. */
1408 hdr = res_hdr->next;
1409 while (hdr != res_hdr) {
1410 pjsip_msg_add_hdr( tdata->msg,
1411 pjsip_hdr_clone(tdata->pool, hdr));
1412 hdr = hdr->next;
1413 }
1414
1415 /* Add msg body, if any */
1416 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001417 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1418 if (tdata->msg->body == NULL) {
1419
1420 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1421
Benny Prijono834aee32006-02-19 01:38:06 +00001422 /* Ignore */
1423 return PJ_SUCCESS;
1424 }
1425 }
1426
1427 return PJ_SUCCESS;
1428}
1429
1430/*
1431 * Get subscription state from the value of Subscription-State header.
1432 */
1433static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1434 pjsip_evsub_state *state,
1435 pj_str_t **state_str )
1436{
1437 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1438
1439 *state = PJSIP_EVSUB_STATE_TERMINATED;
1440 *state_str = NULL;
1441
1442 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1443
1444 *state = PJSIP_EVSUB_STATE_ACTIVE;
1445 *state_str = NULL;
1446
1447 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1448
1449 *state = PJSIP_EVSUB_STATE_PENDING;
1450 *state_str = NULL;
1451
1452 } else {
1453
1454 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1455 *state_str = &sub_state->sub_state;
1456
1457 }
1458}
1459
1460/*
1461 * Transaction event processing by UAC, after subscription is sent.
1462 */
1463static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1464 pjsip_event *event )
1465{
1466
1467 if (pjsip_method_cmp(&tsx->method, &sub->method)==0) {
1468
1469 /* Received response to outgoing request that establishes/refresh
1470 * subscription.
1471 */
1472
1473 /* First time initial request is sent. */
1474 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1475 tsx->state == PJSIP_TSX_STATE_CALLING)
1476 {
1477 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
1478 return;
1479 }
1480
1481 /* Only interested in final response */
1482 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1483 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1484 {
1485 return;
1486 }
1487
Benny Prijono1d8d6082006-04-29 12:38:25 +00001488 /* Clear pending subscription */
1489 if (tsx == sub->pending_sub)
1490 sub->pending_sub = NULL;
1491
Benny Prijono834aee32006-02-19 01:38:06 +00001492 /* Handle authentication. */
1493 if (tsx->status_code==401 || tsx->status_code==407) {
1494 pjsip_tx_data *tdata;
1495 pj_status_t status;
1496
1497 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1498 /* Previously failed transaction has terminated */
1499 return;
1500 }
1501
1502 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1503 event->body.tsx_state.src.rdata,
1504 tsx->last_tx, &tdata);
1505 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001506 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001507
1508 if (status != PJ_SUCCESS) {
1509 /* Authentication failed! */
1510 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1511 NULL,
1512 event);
1513 return;
1514 }
1515
1516 return;
1517 }
1518
1519 if (tsx->status_code/100 == 2) {
1520
1521 /* Successfull SUBSCRIBE request!
1522 * This could be:
1523 * - response to initial SUBSCRIBE request
1524 * - response to subsequent refresh
1525 * - response to unsubscription
1526 */
1527
1528 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1529 /* Ignore; this transaction has been processed before */
1530 return;
1531 }
1532
1533 /* Update UAC refresh time, if response contains Expires header,
1534 * only when we're not unsubscribing.
1535 */
1536 if (sub->expires->ivalue != 0) {
1537 pjsip_msg *msg;
1538 pjsip_expires_hdr *expires;
1539
1540 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1541 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1542 if (expires) {
1543 sub->expires->ivalue = expires->ivalue;
1544 }
1545 }
1546
1547 /* Update time */
1548 update_expires(sub, sub->expires->ivalue);
1549
1550 /* Start UAC refresh timer, only when we're not unsubscribing */
1551 if (sub->expires->ivalue != 0) {
1552 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1553 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1554
1555 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1556 timeout));
1557 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1558
1559 } else {
1560 /* Otherwise set timer to terminate client subscription when
1561 * NOTIFY to end subscription is not received.
1562 */
1563 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1564 }
1565
1566 /* Set state, if necessary */
1567 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1568 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
1569 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
1570 }
1571
1572 } else {
1573
1574 /* Failed SUBSCRIBE request!
1575 *
1576 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1577 * other than 481, the subscription is still considered valid for
1578 * the duration of the last Expires.
1579 *
1580 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1581 * expiration, theoritically the expiration is still valid for the
1582 * next 5 seconds even when we receive non-481 failed response.
1583 *
1584 * Ah, what the heck!
1585 *
1586 * Just terminate now!
1587 *
1588 */
1589
1590 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1591 /* Ignore, has been handled before */
1592 return;
1593 }
1594
Benny Prijono69b98ab2006-03-03 10:23:35 +00001595 /* Ignore 490 (Request Updated) status.
1596 * This happens when application sends SUBSCRIBE/REFER while
1597 * another one is still in progress.
1598 */
1599 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1600 return;
1601 }
1602
Benny Prijono834aee32006-02-19 01:38:06 +00001603 /* Kill any timer. */
1604 set_timer(sub, TIMER_TYPE_NONE, 0);
1605
1606 /* Set state to TERMINATED */
1607 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1608 NULL, event);
1609
1610 }
1611
1612 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1613
1614 /* Incoming NOTIFY.
1615 * This can be the result of:
1616 * - Initial subscription response
1617 * - UAS updating the resource info.
1618 * - Unsubscription response.
1619 */
1620 int st_code = 200;
1621 pj_str_t *st_text = NULL;
1622 pjsip_hdr res_hdr;
1623 pjsip_msg_body *body = NULL;
1624
1625 pjsip_rx_data *rdata;
1626 pjsip_msg *msg;
1627 pjsip_sub_state_hdr *sub_state;
1628
1629 pjsip_evsub_state new_state;
1630 pj_str_t *new_state_str;
1631
1632 pjsip_tx_data *tdata;
1633 pj_status_t status;
1634 int next_refresh;
1635
1636 /* Only want to handle initial NOTIFY receive event. */
1637 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1638 return;
1639
1640
1641 rdata = event->body.tsx_state.src.rdata;
1642 msg = rdata->msg_info.msg;
1643
1644 pj_list_init(&res_hdr);
1645
1646 /* Get subscription state header. */
1647 sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
1648 if (sub_state == NULL) {
1649
1650 pjsip_warning_hdr *warn_hdr;
1651 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1652
1653 /* Bad request! Add warning header. */
1654 st_code = PJSIP_SC_BAD_REQUEST;
1655 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1656 pjsip_endpt_name(sub->endpt),
1657 &warn_text);
1658 pj_list_push_back(&res_hdr, warn_hdr);
1659 }
1660
1661 /* Call application registered callback to handle incoming NOTIFY,
1662 * if any.
1663 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001664 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001665 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1666 &res_hdr, &body);
1667
1668 /* Application MUST specify final response! */
1669 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1670
1671 /* Must be a valid status code */
1672 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1673 }
1674
1675
1676 /* If non-2xx should be returned, then send the response.
1677 * No need to update server subscription state.
1678 */
1679 if (st_code >= 300) {
1680 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1681 body, &tdata);
1682 if (status == PJ_SUCCESS) {
1683 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1684 }
1685
1686 /* Start timer to terminate subscription, just in case server
1687 * is not able to generate NOTIFY to our response.
1688 */
1689 if (status == PJ_SUCCESS) {
1690 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1691 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1692 } else {
1693 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1694 }
1695
1696 return;
1697 }
1698
1699 /* Update expiration from the value of expires param in
1700 * Subscription-State header, but ONLY when subscription state
1701 * is "active" or "pending", AND the header contains expires param.
1702 */
1703 if (sub->expires->ivalue != 0 &&
1704 sub_state->expires_param >= 0 &&
1705 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1706 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1707 {
1708 next_refresh = sub_state->expires_param;
1709
1710 } else {
1711 next_refresh = sub->expires->ivalue;
1712 }
1713
1714 /* Update time */
1715 update_expires(sub, next_refresh);
1716
1717 /* Start UAC refresh timer, only when we're not unsubscribing */
1718 if (sub->expires->ivalue != 0) {
1719 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1720 next_refresh - TIME_UAC_REFRESH : next_refresh;
1721
1722 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1723 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1724 }
1725
1726 /* Find out the state */
1727 get_hdr_state(sub_state, &new_state, &new_state_str);
1728
1729 /* Send response. */
1730 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1731 body, &tdata);
1732 if (status == PJ_SUCCESS)
1733 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1734
1735 /* Set the state */
1736 if (status == PJ_SUCCESS) {
1737 set_state(sub, new_state, new_state_str, event);
1738 } else {
1739 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1740 }
1741
1742
1743 } else {
1744
1745 /*
1746 * Unexpected method!
1747 */
1748 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1749 (int)tsx->method.name.slen, tsx->method.name.ptr));
1750 }
1751}
1752
1753
1754/*
1755 * Transaction event processing by UAS, after subscription is accepted.
1756 */
1757static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1758 pjsip_event *event)
1759{
1760
1761 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0) {
1762
1763 /*
1764 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1765 *
1766 */
1767 pjsip_rx_data *rdata;
1768 pjsip_event_hdr *event_hdr;
1769 pjsip_expires_hdr *expires;
1770 pjsip_msg *msg;
1771 pjsip_tx_data *tdata;
1772 int st_code = 200;
1773 pj_str_t *st_text = NULL;
1774 pjsip_hdr res_hdr;
1775 pjsip_msg_body *body = NULL;
1776 pjsip_evsub_state old_state;
1777 pj_str_t old_state_str;
1778 pj_status_t status;
1779
1780
1781 /* Only wants to handle the first event when the request is
1782 * received.
1783 */
1784 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1785 return;
1786
1787 rdata = event->body.tsx_state.src.rdata;
1788 msg = rdata->msg_info.msg;
1789
1790 /* Set expiration time based on client request (in Expires header),
1791 * or package default expiration time.
1792 */
1793 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1794 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1795 if (event_hdr && expires) {
1796 struct evpkg *evpkg;
1797
1798 evpkg = find_pkg(&event_hdr->event_type);
1799 if (evpkg) {
1800 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1801 sub->expires->ivalue = expires->ivalue;
1802 else
1803 sub->expires->ivalue = evpkg->pkg_expires;
1804 }
1805 }
1806
1807 /* Update time (before calling on_rx_refresh, since application
1808 * will send NOTIFY.
1809 */
1810 update_expires(sub, sub->expires->ivalue);
1811
1812
1813 /* Save old state.
1814 * If application respond with non-2xx, revert to old state.
1815 */
1816 old_state = sub->state;
1817 old_state_str = sub->state_str;
1818
1819 if (sub->expires->ivalue == 0) {
1820 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1821 sub->state_str = evsub_state_names[sub->state];
1822 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1823 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1824 sub->state_str = evsub_state_names[sub->state];
1825 }
1826
1827 /* Call application's on_rx_refresh, just in case it wants to send
1828 * response other than 200 (OK)
1829 */
1830 pj_list_init(&res_hdr);
1831
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001832 if (sub->user.on_rx_refresh && sub->call_cb) {
1833 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1834 &res_hdr, &body);
1835 }
Benny Prijono834aee32006-02-19 01:38:06 +00001836
1837 /* Application MUST specify final response! */
1838 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1839
1840 /* Must be a valid status code */
1841 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1842
1843
1844 /* Create and send response */
1845 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1846 body, &tdata);
1847 if (status == PJ_SUCCESS) {
1848 /* Add expires header: */
1849 pjsip_msg_add_hdr( tdata->msg,
1850 pjsip_hdr_shallow_clone(tdata->pool,
1851 sub->expires));
1852
1853 /* Send */
1854 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1855 }
1856
1857 /* Update state or revert state */
1858 if (st_code/100==2) {
1859
1860 if (sub->expires->ivalue == 0) {
1861 set_state(sub, sub->state, NULL, event);
1862 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1863 set_state(sub, sub->state, NULL, event);
1864 }
1865
1866 /* Set UAS timeout timer, when state is not terminated. */
1867 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1868 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1869 sub->expires->ivalue));
1870 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1871 sub->expires->ivalue);
1872 }
1873
1874 } else {
1875 sub->state = old_state;
1876 sub->state_str = old_state_str;
1877 }
1878
1879
1880 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1881
1882 /* Handle authentication */
1883 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1884 (tsx->status_code==401 || tsx->status_code==407))
1885 {
1886 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1887 pjsip_tx_data *tdata;
1888 pj_status_t status;
1889
1890 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
1891 tsx->last_tx, &tdata);
1892 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001893 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00001894
1895 if (status != PJ_SUCCESS) {
1896 /* Can't authenticate. Terminate session (?) */
1897 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono441ce002006-03-07 15:15:01 +00001898 return;
Benny Prijono834aee32006-02-19 01:38:06 +00001899 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001900
1901 }
1902 /*
1903 * Terminate event usage if we receive 481, 408, and 7 class
1904 * responses.
1905 */
1906 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
1907 (tsx->status_code==481 || tsx->status_code==408 ||
1908 tsx->status_code/100 == 7))
1909 {
1910 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
Benny Prijono441ce002006-03-07 15:15:01 +00001911 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001912 }
Benny Prijono834aee32006-02-19 01:38:06 +00001913
1914 } else {
1915
1916 /*
1917 * Unexpected method!
1918 */
1919 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1920 (int)tsx->method.name.slen, tsx->method.name.ptr));
1921
1922 }
1923}
1924
1925
1926/*
1927 * Notification when transaction state has changed!
1928 */
1929static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
1930{
1931 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
1932
1933 if (sub == NULL) {
1934 sub = on_new_transaction(tsx, event);
1935 if (sub == NULL)
1936 return;
1937 }
1938
1939
1940 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001941 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00001942 (*sub->user.on_tsx_state)(sub, tsx, event);
1943
1944
1945 /* Process the event: */
1946
1947 if (sub->role == PJSIP_ROLE_UAC) {
1948 on_tsx_state_uac(sub, tsx, event);
1949 } else {
1950 on_tsx_state_uas(sub, tsx, event);
1951 }
1952
1953
1954 /* Check transaction TERMINATE event */
1955 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1956
1957 --sub->pending_tsx;
1958
1959 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
1960 sub->pending_tsx == 0)
1961 {
1962 evsub_destroy(sub);
1963 }
1964
1965 }
1966}
1967
1968