blob: a69409de79b4e1cda0bd99308db267601309e287 [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."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000593 status = pjsip_evsub_initiate(sub, NULL,
Benny Prijono834aee32006-02-19 01:38:06 +0000594 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.."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000635 status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000636 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
Benny Prijono8eae8382006-08-10 21:44:26 +0000671 /* Must lock dialog before using pool etc. */
672 pjsip_dlg_inc_lock(dlg);
673
Benny Prijono834aee32006-02-19 01:38:06 +0000674 /* Init attributes: */
675
676 sub = pj_pool_zalloc(dlg->pool, sizeof(struct pjsip_evsub));
677 sub->pool = dlg->pool;
678 sub->endpt = dlg->endpt;
679 sub->dlg = dlg;
680 sub->pkg = pkg;
681 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000682 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000683 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000684 sub->state = PJSIP_EVSUB_STATE_NULL;
685 sub->state_str = evsub_state_names[sub->state];
686 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
687 sub->accept = pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
688
689 sub->timer.user_data = sub;
690 sub->timer.cb = &on_timer;
691
692 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000693 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
694 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000695
696
697 /* Copy callback, if any: */
698 if (user_cb)
699 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
700
701
702 /* Create Event header: */
703 sub->event = pjsip_event_hdr_create(sub->pool);
704 pj_strdup(sub->pool, &sub->event->event_type, event);
705
706
707 /* Create subcription list: */
708
709 dlgsub_head = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
710 dlgsub = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
711 dlgsub->sub = sub;
712
713 pj_list_init(dlgsub_head);
714 pj_list_push_back(dlgsub_head, dlgsub);
715
716
717 /* Register as dialog usage: */
718
719 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
Benny Prijono8eae8382006-08-10 21:44:26 +0000720 if (status != PJ_SUCCESS) {
721 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000722 return status;
Benny Prijono8eae8382006-08-10 21:44:26 +0000723 }
Benny Prijono834aee32006-02-19 01:38:06 +0000724
725
726 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
727 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
728 dlg->obj_name));
729
730 *p_evsub = sub;
Benny Prijono8eae8382006-08-10 21:44:26 +0000731 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000732
733 return PJ_SUCCESS;
734}
735
736
737
738/*
739 * Create client subscription session.
740 */
741PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
742 const pjsip_evsub_user *user_cb,
743 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000744 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000745 pjsip_evsub **p_evsub)
746{
747 pjsip_evsub *sub;
748 pj_status_t status;
749
750 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
751
752 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000753 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000754 if (status != PJ_SUCCESS)
755 goto on_return;
756
Benny Prijono26ff9062006-02-21 23:47:00 +0000757 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
758 * is not specified.
759 */
760 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
761 pj_create_unique_string(sub->pool, &sub->event->id_param);
762 }
Benny Prijono834aee32006-02-19 01:38:06 +0000763
764 /* Increment dlg session. */
765 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
766
767 /* Done */
768 *p_evsub = sub;
769
770on_return:
771 pjsip_dlg_dec_lock(dlg);
772 return status;
773}
774
775
776/*
777 * Create server subscription session from incoming request.
778 */
779PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
780 const pjsip_evsub_user *user_cb,
781 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000782 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000783 pjsip_evsub **p_evsub)
784{
785 pjsip_evsub *sub;
786 pjsip_transaction *tsx;
787 pjsip_accept_hdr *accept_hdr;
788 pjsip_event_hdr *event_hdr;
789 pjsip_expires_hdr *expires_hdr;
790 pj_status_t status;
791
792 /* Check arguments: */
793 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
794
795 /* MUST be request message: */
796 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
797 PJSIP_ENOTREQUESTMSG);
798
799 /* Transaction MUST have been created (in the dialog) */
800 tsx = pjsip_rdata_get_tsx(rdata);
801 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
802
803 /* No subscription must have been attached to transaction */
804 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
805 PJSIP_ETYPEEXISTS);
806
807 /* Package MUST implement on_rx_refresh */
808 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
809
Benny Prijono26ff9062006-02-21 23:47:00 +0000810 /* Request MUST have "Event" header. We need the Event header to get
811 * the package name (don't want to add more arguments in the function).
812 */
Benny Prijono834aee32006-02-19 01:38:06 +0000813 event_hdr = (pjsip_event_hdr*)
814 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
815 if (event_hdr == NULL) {
816 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
817 }
818
819 /* Start locking the mutex: */
820
821 pjsip_dlg_inc_lock(dlg);
822
823 /* Create the session: */
824
825 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000826 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000827 if (status != PJ_SUCCESS)
828 goto on_return;
829
830 /* Just duplicate Event header from the request */
831 sub->event = pjsip_hdr_clone(sub->pool, event_hdr);
832
833 /* Set the method: */
834 pjsip_method_copy(sub->pool, &sub->method,
835 &rdata->msg_info.msg->line.req.method);
836
837 /* Update expiration time according to client request: */
838
839 expires_hdr = (pjsip_expires_hdr*)
840 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
841 if (expires_hdr) {
842 sub->expires->ivalue = expires_hdr->ivalue;
843 }
844
845 /* Update time. */
846 update_expires(sub, sub->expires->ivalue);
847
848 /* Update Accept header: */
849
850 accept_hdr = (pjsip_accept_hdr*)
851 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
852 if (accept_hdr)
853 sub->accept = pjsip_hdr_clone(sub->pool, accept_hdr);
854
855 /* We can start the session: */
856
857 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
858 sub->pending_tsx++;
859 tsx->mod_data[mod_evsub.mod.id] = sub;
860
861
862 /* Done. */
863 *p_evsub = sub;
864
865
866on_return:
867 pjsip_dlg_dec_lock(dlg);
868 return status;
869}
870
871
872/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000873 * Forcefully destroy subscription.
874 */
875PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
876 pj_bool_t notify )
877{
878 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
879
880 pjsip_dlg_inc_lock(sub->dlg);
881
882 if (sub->pending_tsx) {
883 pj_assert(!"Unable to terminate when there's pending tsx");
884 pjsip_dlg_dec_lock(sub->dlg);
885 return PJ_EINVALIDOP;
886 }
887
888 sub->call_cb = notify;
889 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
890
891 pjsip_dlg_dec_lock(sub->dlg);
892 return PJ_SUCCESS;
893}
894
895/*
Benny Prijono834aee32006-02-19 01:38:06 +0000896 * Get subscription state.
897 */
898PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
899{
900 return sub->state;
901}
902
903/*
904 * Get state name.
905 */
906PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
907{
908 return sub->state_str.ptr;
909}
910
911
912/*
913 * Initiate client subscription
914 */
915PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
916 const pjsip_method *method,
917 pj_int32_t expires,
918 pjsip_tx_data **p_tdata)
919{
920 pjsip_tx_data *tdata;
921 pj_status_t status;
922
923 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
924
925 /* Use SUBSCRIBE if method is not specified */
926 if (method == NULL)
927 method = &pjsip_subscribe_method;
928
929 pjsip_dlg_inc_lock(sub->dlg);
930
931 /* Update method: */
932 if (sub->state == PJSIP_EVSUB_STATE_NULL)
933 pjsip_method_copy(sub->pool, &sub->method, method);
934
935 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
936 if (status != PJ_SUCCESS)
937 goto on_return;
938
939
940 /* Add Event header: */
941 pjsip_msg_add_hdr( tdata->msg,
942 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
943
944 /* Update and add expires header: */
945 if (expires >= 0)
946 sub->expires->ivalue = expires;
947 pjsip_msg_add_hdr( tdata->msg,
948 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
949
950 /* Add Accept header: */
951 pjsip_msg_add_hdr( tdata->msg,
952 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
953
954
955 /* Add Allow-Events header: */
956 pjsip_msg_add_hdr( tdata->msg,
957 pjsip_hdr_shallow_clone(tdata->pool,
958 mod_evsub.allow_events_hdr));
959
960
961 *p_tdata = tdata;
962
963
964on_return:
965
966 pjsip_dlg_dec_lock(sub->dlg);
967 return status;
968}
969
970
971/*
972 * Accept incoming subscription request.
973 */
974PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
975 pjsip_rx_data *rdata,
976 int st_code,
977 const pjsip_hdr *hdr_list )
978{
979 pjsip_tx_data *tdata;
980 pjsip_transaction *tsx;
981 pj_status_t status;
982
983 /* Check arguments */
984 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
985
986 /* Can only be for server subscription: */
987 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
988
989 /* Only expect 2xx status code (for now) */
990 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
991
992 /* Subscription MUST have been attached to the transaction.
993 * Initial subscription request will be attached on evsub_create_uas(),
994 * while subsequent requests will be attached in tsx_state()
995 */
996 tsx = pjsip_rdata_get_tsx(rdata);
997 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
998 PJ_EINVALIDOP);
999
1000 /* Lock dialog */
1001 pjsip_dlg_inc_lock(sub->dlg);
1002
1003 /* Create response: */
1004 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1005 &tdata);
1006 if (status != PJ_SUCCESS)
1007 goto on_return;
1008
1009
1010 /* Add expires header: */
1011 pjsip_msg_add_hdr( tdata->msg,
1012 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1013
Benny Prijonob0808372006-03-02 21:18:58 +00001014 /* Add additional header, if any. */
1015 if (hdr_list) {
1016 const pjsip_hdr *hdr = hdr_list->next;
1017 while (hdr != hdr_list) {
1018 pjsip_msg_add_hdr( tdata->msg,
1019 pjsip_hdr_clone(tdata->pool, hdr));
1020 hdr = hdr->next;
1021 }
1022 }
Benny Prijono834aee32006-02-19 01:38:06 +00001023
1024 /* Send the response: */
1025 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1026 if (status != PJ_SUCCESS)
1027 goto on_return;
1028
1029
1030on_return:
1031
1032 pjsip_dlg_dec_lock(sub->dlg);
1033 return status;
1034}
1035
1036
1037/*
1038 * Create Subscription-State header based on current server subscription
1039 * state.
1040 */
1041static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1042 pjsip_evsub *sub,
1043 pjsip_evsub_state state,
1044 const pj_str_t *state_str,
1045 const pj_str_t *reason )
1046{
1047 pjsip_sub_state_hdr *sub_state;
1048 pj_time_val now, delay;
1049
1050 /* Get the remaining time before refresh is required */
1051 pj_gettimeofday(&now);
1052 delay = sub->refresh_time;
1053 PJ_TIME_VAL_SUB(delay, now);
1054
1055 /* Create the Subscription-State header */
1056 sub_state = pjsip_sub_state_hdr_create(pool);
1057
1058 /* Fill up the header */
1059 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001060 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001061 case PJSIP_EVSUB_STATE_SENT:
1062 case PJSIP_EVSUB_STATE_ACCEPTED:
1063 pj_assert(!"Invalid state!");
1064 /* Treat as pending */
1065
1066 case PJSIP_EVSUB_STATE_PENDING:
1067 sub_state->sub_state = STR_PENDING;
1068 sub_state->expires_param = delay.sec;
1069 break;
1070
1071 case PJSIP_EVSUB_STATE_ACTIVE:
1072 sub_state->sub_state = STR_ACTIVE;
1073 sub_state->expires_param = delay.sec;
1074 break;
1075
1076 case PJSIP_EVSUB_STATE_TERMINATED:
1077 sub_state->sub_state = STR_TERMINATED;
1078 if (reason != NULL)
1079 pj_strdup(pool, &sub_state->reason_param, reason);
1080 break;
1081
1082 case PJSIP_EVSUB_STATE_UNKNOWN:
1083 pj_assert(state_str != NULL);
1084 pj_strdup(pool, &sub_state->sub_state, state_str);
1085 break;
1086 }
1087
1088 return sub_state;
1089}
1090
1091/*
1092 * Create and send NOTIFY request.
1093 */
1094PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1095 pjsip_evsub_state state,
1096 const pj_str_t *state_str,
1097 const pj_str_t *reason,
1098 pjsip_tx_data **p_tdata)
1099{
1100 pjsip_tx_data *tdata;
1101 pjsip_sub_state_hdr *sub_state;
1102 pj_status_t status;
1103
1104 /* Check arguments. */
1105 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1106
1107 /* Lock dialog. */
1108 pjsip_dlg_inc_lock(sub->dlg);
1109
1110 /* Create NOTIFY request */
1111 status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1,
1112 &tdata);
1113 if (status != PJ_SUCCESS)
1114 goto on_return;
1115
1116 /* Add Event header */
1117 pjsip_msg_add_hdr(tdata->msg,
1118 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1119
1120 /* Add Subscription-State header */
1121 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1122 reason);
1123 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1124
1125 /* Add Allow-Events header */
1126 pjsip_msg_add_hdr(tdata->msg,
1127 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1128
1129 /* Add Authentication headers. */
1130 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1131
1132
1133
1134 /* Save destination state. */
1135 sub->dst_state = state;
1136 if (state_str)
1137 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1138 else
1139 sub->dst_state_str.slen = 0;
1140
1141
1142 *p_tdata = tdata;
1143
1144on_return:
1145 /* Unlock dialog */
1146 pjsip_dlg_dec_lock(sub->dlg);
1147 return status;
1148}
1149
1150
1151/*
1152 * Create NOTIFY to reflect current status.
1153 */
1154PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1155 pjsip_tx_data **p_tdata )
1156{
1157 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1158 NULL, p_tdata );
1159}
1160
1161
1162/*
1163 * Send request.
1164 */
1165PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1166 pjsip_tx_data *tdata)
1167{
1168 pj_status_t status;
1169
1170 /* Must be request message. */
1171 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1172 PJSIP_ENOTREQUESTMSG);
1173
1174 /* Lock */
1175 pjsip_dlg_inc_lock(sub->dlg);
1176
1177 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001178 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001179 if (status != PJ_SUCCESS)
1180 goto on_return;
1181
1182
1183 /* Special case for NOTIFY:
1184 * The new state was set in pjsip_evsub_notify(), but we apply the
1185 * new state now, when the request was actually sent.
1186 */
1187 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1188 &pjsip_notify_method)==0)
1189 {
1190 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1191 {goto on_return;});
1192
1193 set_state(sub, sub->dst_state,
1194 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
1195 NULL);
1196
1197 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1198 sub->dst_state_str.slen = 0;
1199
1200 }
1201
1202
1203on_return:
1204 pjsip_dlg_dec_lock(sub->dlg);
1205 return status;
1206}
1207
1208
1209
1210/*
1211 * Attach subscription session to newly created transaction, if appropriate.
1212 */
1213static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1214 pjsip_event *event)
1215{
1216 /*
1217 * Newly created transaction will not have subscription session
1218 * attached to it. Find the subscription session from the dialog,
1219 * by matching the Event header.
1220 */
1221 pjsip_dialog *dlg;
1222 pjsip_event_hdr *event_hdr;
1223 pjsip_msg *msg;
1224 struct dlgsub *dlgsub_head, *dlgsub;
1225 pjsip_evsub *sub;
1226
1227 dlg = pjsip_tsx_get_dlg(tsx);
1228 if (!dlg) {
1229 pj_assert(!"Transaction should have a dialog instance!");
1230 return NULL;
1231 }
1232
Benny Prijono26ff9062006-02-21 23:47:00 +00001233
Benny Prijono834aee32006-02-19 01:38:06 +00001234 switch (event->body.tsx_state.type) {
1235 case PJSIP_EVENT_RX_MSG:
1236 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1237 break;
1238 case PJSIP_EVENT_TX_MSG:
1239 msg = event->body.tsx_state.src.tdata->msg;
1240 break;
1241 default:
1242 if (tsx->role == PJSIP_ROLE_UAC)
1243 msg = tsx->last_tx->msg;
1244 else
1245 msg = NULL;
1246 break;
1247 }
1248
1249 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001250 //Note:
1251 // this transaction can be other transaction in the dialog.
1252 // The assertion below probably only valid for dialog that
1253 // only has one event subscription usage.
1254 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001255 return NULL;
1256 }
1257
1258 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1259 if (!event_hdr) {
1260 /* Not subscription related message */
1261 return NULL;
1262 }
1263
1264 /* Find the subscription in the dialog, based on the content
1265 * of Event header:
1266 */
1267
1268 dlgsub_head = dlg->mod_data[mod_evsub.mod.id];
1269 if (dlgsub_head == NULL) {
1270 dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub));
1271 pj_list_init(dlgsub_head);
1272 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1273 }
1274 dlgsub = dlgsub_head->next;
1275
1276 while (dlgsub != dlgsub_head) {
1277
Benny Prijono26ff9062006-02-21 23:47:00 +00001278 if (pj_stricmp(&dlgsub->sub->event->event_type,
1279 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001280 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001281 /* Event type matched.
1282 * Check if event ID matched too.
1283 */
1284 if (pj_strcmp(&dlgsub->sub->event->id_param,
1285 &event_hdr->id_param)==0)
1286 {
1287
1288 break;
1289
1290 }
1291 /*
1292 * Otherwise if it is an UAC subscription, AND
1293 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1294 * the session's event id is NULL, AND
1295 * the incoming request is NOTIFY with event ID, then
1296 * we consider it as a match, and update the
1297 * session's event id.
1298 */
1299 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1300 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1301 dlgsub->sub->event->id_param.slen==0 &&
1302 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1303 {
1304 /* Update session's event id. */
1305 pj_strdup(dlgsub->sub->pool,
1306 &dlgsub->sub->event->id_param,
1307 &event_hdr->id_param);
1308
1309 break;
1310 }
Benny Prijono834aee32006-02-19 01:38:06 +00001311 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001312
1313
1314
Benny Prijono834aee32006-02-19 01:38:06 +00001315 dlgsub = dlgsub->next;
1316 }
1317
1318 if (dlgsub == dlgsub_head) {
1319 /* This could be incoming request to create new subscription */
1320 PJ_LOG(4,(THIS_FILE,
1321 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001322 (int)tsx->method.name.slen,
1323 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001324 (int)event_hdr->event_type.slen,
1325 event_hdr->event_type.ptr,
1326 (int)event_hdr->id_param.slen,
1327 event_hdr->id_param.ptr));
1328
1329 /* If this is an incoming NOTIFY, reject with 481 */
1330 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1331 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1332 {
1333 pj_str_t reason = pj_str("Subscription Does Not Exist");
1334 pjsip_tx_data *tdata;
1335 pj_status_t status;
1336
1337 status = pjsip_dlg_create_response(dlg,
1338 event->body.tsx_state.src.rdata,
1339 481, &reason,
1340 &tdata);
1341 if (status == PJ_SUCCESS) {
1342 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1343 }
1344 }
1345 return NULL;
1346 }
1347
1348 /* Found! */
1349 sub = dlgsub->sub;
1350
1351 /* Attach session to the transaction */
1352 tsx->mod_data[mod_evsub.mod.id] = sub;
1353 sub->pending_tsx++;
1354
Benny Prijono69b98ab2006-03-03 10:23:35 +00001355 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1356 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1357 * transaction is started while previous one still alive, terminate
1358 * the older one.
1359 *
1360 * Sample scenario:
1361 * - subscribe sent to destination that doesn't exist, transaction
1362 * is still retransmitting request, then unsubscribe is sent.
1363 */
1364 if (tsx->role == PJSIP_ROLE_UAC &&
1365 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001366 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1367 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001368 {
1369
1370 if (sub->pending_sub &&
1371 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1372 {
1373 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001374 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001375
1376 /* By convention, we use 490 (Request Updated) status code.
1377 * When transaction handler (below) see this status code, it
1378 * will ignore the transaction.
1379 */
1380 pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1381 }
1382
1383 sub->pending_sub = tsx;
1384
Benny Prijono69b98ab2006-03-03 10:23:35 +00001385 }
1386
Benny Prijono834aee32006-02-19 01:38:06 +00001387 return sub;
1388}
1389
1390
1391/*
1392 * Create response, adding custome headers and msg body.
1393 */
1394static pj_status_t create_response( pjsip_evsub *sub,
1395 pjsip_rx_data *rdata,
1396 int st_code,
1397 const pj_str_t *st_text,
1398 const pjsip_hdr *res_hdr,
1399 const pjsip_msg_body *body,
1400 pjsip_tx_data **p_tdata)
1401{
1402 pjsip_tx_data *tdata;
1403 pjsip_hdr *hdr;
1404 pj_status_t status;
1405
1406 status = pjsip_dlg_create_response(sub->dlg, rdata,
1407 st_code, st_text, &tdata);
1408 if (status != PJ_SUCCESS)
1409 return status;
1410
1411 *p_tdata = tdata;
1412
1413 /* Add response headers. */
1414 hdr = res_hdr->next;
1415 while (hdr != res_hdr) {
1416 pjsip_msg_add_hdr( tdata->msg,
1417 pjsip_hdr_clone(tdata->pool, hdr));
1418 hdr = hdr->next;
1419 }
1420
1421 /* Add msg body, if any */
1422 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001423 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1424 if (tdata->msg->body == NULL) {
1425
1426 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1427
Benny Prijono834aee32006-02-19 01:38:06 +00001428 /* Ignore */
1429 return PJ_SUCCESS;
1430 }
1431 }
1432
1433 return PJ_SUCCESS;
1434}
1435
1436/*
1437 * Get subscription state from the value of Subscription-State header.
1438 */
1439static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1440 pjsip_evsub_state *state,
1441 pj_str_t **state_str )
1442{
1443 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1444
1445 *state = PJSIP_EVSUB_STATE_TERMINATED;
1446 *state_str = NULL;
1447
1448 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1449
1450 *state = PJSIP_EVSUB_STATE_ACTIVE;
1451 *state_str = NULL;
1452
1453 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1454
1455 *state = PJSIP_EVSUB_STATE_PENDING;
1456 *state_str = NULL;
1457
1458 } else {
1459
1460 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1461 *state_str = &sub_state->sub_state;
1462
1463 }
1464}
1465
1466/*
1467 * Transaction event processing by UAC, after subscription is sent.
1468 */
1469static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1470 pjsip_event *event )
1471{
1472
Benny Prijono736d0f72006-09-13 22:45:38 +00001473 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1474 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1475 {
Benny Prijono834aee32006-02-19 01:38:06 +00001476
1477 /* Received response to outgoing request that establishes/refresh
1478 * subscription.
1479 */
1480
1481 /* First time initial request is sent. */
1482 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1483 tsx->state == PJSIP_TSX_STATE_CALLING)
1484 {
1485 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
1486 return;
1487 }
1488
1489 /* Only interested in final response */
1490 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1491 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1492 {
1493 return;
1494 }
1495
Benny Prijono1d8d6082006-04-29 12:38:25 +00001496 /* Clear pending subscription */
1497 if (tsx == sub->pending_sub)
1498 sub->pending_sub = NULL;
1499
Benny Prijono834aee32006-02-19 01:38:06 +00001500 /* Handle authentication. */
1501 if (tsx->status_code==401 || tsx->status_code==407) {
1502 pjsip_tx_data *tdata;
1503 pj_status_t status;
1504
1505 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1506 /* Previously failed transaction has terminated */
1507 return;
1508 }
1509
1510 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1511 event->body.tsx_state.src.rdata,
1512 tsx->last_tx, &tdata);
1513 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001514 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001515
1516 if (status != PJ_SUCCESS) {
1517 /* Authentication failed! */
1518 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1519 NULL,
1520 event);
1521 return;
1522 }
1523
1524 return;
1525 }
1526
1527 if (tsx->status_code/100 == 2) {
1528
1529 /* Successfull SUBSCRIBE request!
1530 * This could be:
1531 * - response to initial SUBSCRIBE request
1532 * - response to subsequent refresh
1533 * - response to unsubscription
1534 */
1535
1536 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1537 /* Ignore; this transaction has been processed before */
1538 return;
1539 }
1540
1541 /* Update UAC refresh time, if response contains Expires header,
1542 * only when we're not unsubscribing.
1543 */
1544 if (sub->expires->ivalue != 0) {
1545 pjsip_msg *msg;
1546 pjsip_expires_hdr *expires;
1547
1548 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1549 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1550 if (expires) {
1551 sub->expires->ivalue = expires->ivalue;
1552 }
1553 }
1554
1555 /* Update time */
1556 update_expires(sub, sub->expires->ivalue);
1557
1558 /* Start UAC refresh timer, only when we're not unsubscribing */
1559 if (sub->expires->ivalue != 0) {
1560 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1561 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1562
1563 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1564 timeout));
1565 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1566
1567 } else {
1568 /* Otherwise set timer to terminate client subscription when
1569 * NOTIFY to end subscription is not received.
1570 */
1571 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1572 }
1573
1574 /* Set state, if necessary */
1575 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1576 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
1577 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
1578 }
1579
1580 } else {
1581
1582 /* Failed SUBSCRIBE request!
1583 *
1584 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1585 * other than 481, the subscription is still considered valid for
1586 * the duration of the last Expires.
1587 *
1588 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1589 * expiration, theoritically the expiration is still valid for the
1590 * next 5 seconds even when we receive non-481 failed response.
1591 *
1592 * Ah, what the heck!
1593 *
1594 * Just terminate now!
1595 *
1596 */
1597
1598 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1599 /* Ignore, has been handled before */
1600 return;
1601 }
1602
Benny Prijono69b98ab2006-03-03 10:23:35 +00001603 /* Ignore 490 (Request Updated) status.
1604 * This happens when application sends SUBSCRIBE/REFER while
1605 * another one is still in progress.
1606 */
1607 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1608 return;
1609 }
1610
Benny Prijono834aee32006-02-19 01:38:06 +00001611 /* Kill any timer. */
1612 set_timer(sub, TIMER_TYPE_NONE, 0);
1613
1614 /* Set state to TERMINATED */
1615 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1616 NULL, event);
1617
1618 }
1619
1620 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1621
1622 /* Incoming NOTIFY.
1623 * This can be the result of:
1624 * - Initial subscription response
1625 * - UAS updating the resource info.
1626 * - Unsubscription response.
1627 */
1628 int st_code = 200;
1629 pj_str_t *st_text = NULL;
1630 pjsip_hdr res_hdr;
1631 pjsip_msg_body *body = NULL;
1632
1633 pjsip_rx_data *rdata;
1634 pjsip_msg *msg;
1635 pjsip_sub_state_hdr *sub_state;
1636
1637 pjsip_evsub_state new_state;
1638 pj_str_t *new_state_str;
1639
1640 pjsip_tx_data *tdata;
1641 pj_status_t status;
1642 int next_refresh;
1643
1644 /* Only want to handle initial NOTIFY receive event. */
1645 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1646 return;
1647
1648
1649 rdata = event->body.tsx_state.src.rdata;
1650 msg = rdata->msg_info.msg;
1651
1652 pj_list_init(&res_hdr);
1653
1654 /* Get subscription state header. */
1655 sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
1656 if (sub_state == NULL) {
1657
1658 pjsip_warning_hdr *warn_hdr;
1659 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1660
1661 /* Bad request! Add warning header. */
1662 st_code = PJSIP_SC_BAD_REQUEST;
1663 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1664 pjsip_endpt_name(sub->endpt),
1665 &warn_text);
1666 pj_list_push_back(&res_hdr, warn_hdr);
1667 }
1668
1669 /* Call application registered callback to handle incoming NOTIFY,
1670 * if any.
1671 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001672 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001673 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1674 &res_hdr, &body);
1675
1676 /* Application MUST specify final response! */
1677 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1678
1679 /* Must be a valid status code */
1680 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1681 }
1682
1683
1684 /* If non-2xx should be returned, then send the response.
1685 * No need to update server subscription state.
1686 */
1687 if (st_code >= 300) {
1688 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1689 body, &tdata);
1690 if (status == PJ_SUCCESS) {
1691 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1692 }
1693
1694 /* Start timer to terminate subscription, just in case server
1695 * is not able to generate NOTIFY to our response.
1696 */
1697 if (status == PJ_SUCCESS) {
1698 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1699 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1700 } else {
1701 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1702 }
1703
1704 return;
1705 }
1706
1707 /* Update expiration from the value of expires param in
1708 * Subscription-State header, but ONLY when subscription state
1709 * is "active" or "pending", AND the header contains expires param.
1710 */
1711 if (sub->expires->ivalue != 0 &&
1712 sub_state->expires_param >= 0 &&
1713 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1714 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1715 {
1716 next_refresh = sub_state->expires_param;
1717
1718 } else {
1719 next_refresh = sub->expires->ivalue;
1720 }
1721
1722 /* Update time */
1723 update_expires(sub, next_refresh);
1724
1725 /* Start UAC refresh timer, only when we're not unsubscribing */
1726 if (sub->expires->ivalue != 0) {
1727 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1728 next_refresh - TIME_UAC_REFRESH : next_refresh;
1729
1730 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1731 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1732 }
1733
1734 /* Find out the state */
1735 get_hdr_state(sub_state, &new_state, &new_state_str);
1736
1737 /* Send response. */
1738 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1739 body, &tdata);
1740 if (status == PJ_SUCCESS)
1741 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1742
1743 /* Set the state */
1744 if (status == PJ_SUCCESS) {
1745 set_state(sub, new_state, new_state_str, event);
1746 } else {
1747 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1748 }
1749
1750
1751 } else {
1752
1753 /*
1754 * Unexpected method!
1755 */
1756 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1757 (int)tsx->method.name.slen, tsx->method.name.ptr));
1758 }
1759}
1760
1761
1762/*
1763 * Transaction event processing by UAS, after subscription is accepted.
1764 */
1765static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1766 pjsip_event *event)
1767{
1768
Benny Prijono736d0f72006-09-13 22:45:38 +00001769 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1770 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1771 {
Benny Prijono834aee32006-02-19 01:38:06 +00001772
1773 /*
1774 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1775 *
1776 */
1777 pjsip_rx_data *rdata;
1778 pjsip_event_hdr *event_hdr;
1779 pjsip_expires_hdr *expires;
1780 pjsip_msg *msg;
1781 pjsip_tx_data *tdata;
1782 int st_code = 200;
1783 pj_str_t *st_text = NULL;
1784 pjsip_hdr res_hdr;
1785 pjsip_msg_body *body = NULL;
1786 pjsip_evsub_state old_state;
1787 pj_str_t old_state_str;
1788 pj_status_t status;
1789
1790
1791 /* Only wants to handle the first event when the request is
1792 * received.
1793 */
1794 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1795 return;
1796
1797 rdata = event->body.tsx_state.src.rdata;
1798 msg = rdata->msg_info.msg;
1799
1800 /* Set expiration time based on client request (in Expires header),
1801 * or package default expiration time.
1802 */
1803 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1804 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1805 if (event_hdr && expires) {
1806 struct evpkg *evpkg;
1807
1808 evpkg = find_pkg(&event_hdr->event_type);
1809 if (evpkg) {
1810 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1811 sub->expires->ivalue = expires->ivalue;
1812 else
1813 sub->expires->ivalue = evpkg->pkg_expires;
1814 }
1815 }
1816
1817 /* Update time (before calling on_rx_refresh, since application
1818 * will send NOTIFY.
1819 */
1820 update_expires(sub, sub->expires->ivalue);
1821
1822
1823 /* Save old state.
1824 * If application respond with non-2xx, revert to old state.
1825 */
1826 old_state = sub->state;
1827 old_state_str = sub->state_str;
1828
1829 if (sub->expires->ivalue == 0) {
1830 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1831 sub->state_str = evsub_state_names[sub->state];
1832 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1833 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1834 sub->state_str = evsub_state_names[sub->state];
1835 }
1836
1837 /* Call application's on_rx_refresh, just in case it wants to send
1838 * response other than 200 (OK)
1839 */
1840 pj_list_init(&res_hdr);
1841
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001842 if (sub->user.on_rx_refresh && sub->call_cb) {
1843 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1844 &res_hdr, &body);
1845 }
Benny Prijono834aee32006-02-19 01:38:06 +00001846
1847 /* Application MUST specify final response! */
1848 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1849
1850 /* Must be a valid status code */
1851 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1852
1853
1854 /* Create and send response */
1855 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1856 body, &tdata);
1857 if (status == PJ_SUCCESS) {
1858 /* Add expires header: */
1859 pjsip_msg_add_hdr( tdata->msg,
1860 pjsip_hdr_shallow_clone(tdata->pool,
1861 sub->expires));
1862
1863 /* Send */
1864 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1865 }
1866
1867 /* Update state or revert state */
1868 if (st_code/100==2) {
1869
1870 if (sub->expires->ivalue == 0) {
1871 set_state(sub, sub->state, NULL, event);
1872 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1873 set_state(sub, sub->state, NULL, event);
1874 }
1875
1876 /* Set UAS timeout timer, when state is not terminated. */
1877 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1878 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1879 sub->expires->ivalue));
1880 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1881 sub->expires->ivalue);
1882 }
1883
1884 } else {
1885 sub->state = old_state;
1886 sub->state_str = old_state_str;
1887 }
1888
1889
1890 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1891
1892 /* Handle authentication */
1893 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1894 (tsx->status_code==401 || tsx->status_code==407))
1895 {
1896 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1897 pjsip_tx_data *tdata;
1898 pj_status_t status;
1899
1900 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
1901 tsx->last_tx, &tdata);
1902 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001903 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00001904
1905 if (status != PJ_SUCCESS) {
1906 /* Can't authenticate. Terminate session (?) */
1907 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono441ce002006-03-07 15:15:01 +00001908 return;
Benny Prijono834aee32006-02-19 01:38:06 +00001909 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001910
1911 }
1912 /*
1913 * Terminate event usage if we receive 481, 408, and 7 class
1914 * responses.
1915 */
1916 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
1917 (tsx->status_code==481 || tsx->status_code==408 ||
1918 tsx->status_code/100 == 7))
1919 {
1920 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
Benny Prijono441ce002006-03-07 15:15:01 +00001921 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001922 }
Benny Prijono834aee32006-02-19 01:38:06 +00001923
1924 } else {
1925
1926 /*
1927 * Unexpected method!
1928 */
1929 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1930 (int)tsx->method.name.slen, tsx->method.name.ptr));
1931
1932 }
1933}
1934
1935
1936/*
1937 * Notification when transaction state has changed!
1938 */
1939static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
1940{
1941 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
1942
1943 if (sub == NULL) {
1944 sub = on_new_transaction(tsx, event);
1945 if (sub == NULL)
1946 return;
1947 }
1948
1949
1950 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001951 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00001952 (*sub->user.on_tsx_state)(sub, tsx, event);
1953
1954
1955 /* Process the event: */
1956
1957 if (sub->role == PJSIP_ROLE_UAC) {
1958 on_tsx_state_uac(sub, tsx, event);
1959 } else {
1960 on_tsx_state_uas(sub, tsx, event);
1961 }
1962
1963
1964 /* Check transaction TERMINATE event */
1965 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1966
1967 --sub->pending_tsx;
1968
1969 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
1970 sub->pending_tsx == 0)
1971 {
1972 evsub_destroy(sub);
1973 }
1974
1975 }
1976}
1977
1978