blob: 632384cdd30f850ce5e090fef477a17fb3b63136 [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
Benny Prijonod524e822006-09-22 12:48:18 +0000882 /* I think it's pretty safe to disable this check.
883
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000884 if (sub->pending_tsx) {
885 pj_assert(!"Unable to terminate when there's pending tsx");
886 pjsip_dlg_dec_lock(sub->dlg);
887 return PJ_EINVALIDOP;
888 }
Benny Prijonod524e822006-09-22 12:48:18 +0000889 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000890
891 sub->call_cb = notify;
892 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
893
894 pjsip_dlg_dec_lock(sub->dlg);
895 return PJ_SUCCESS;
896}
897
898/*
Benny Prijono834aee32006-02-19 01:38:06 +0000899 * Get subscription state.
900 */
901PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
902{
903 return sub->state;
904}
905
906/*
907 * Get state name.
908 */
909PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
910{
911 return sub->state_str.ptr;
912}
913
914
915/*
916 * Initiate client subscription
917 */
918PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
919 const pjsip_method *method,
920 pj_int32_t expires,
921 pjsip_tx_data **p_tdata)
922{
923 pjsip_tx_data *tdata;
924 pj_status_t status;
925
926 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
927
928 /* Use SUBSCRIBE if method is not specified */
929 if (method == NULL)
930 method = &pjsip_subscribe_method;
931
932 pjsip_dlg_inc_lock(sub->dlg);
933
934 /* Update method: */
935 if (sub->state == PJSIP_EVSUB_STATE_NULL)
936 pjsip_method_copy(sub->pool, &sub->method, method);
937
938 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
939 if (status != PJ_SUCCESS)
940 goto on_return;
941
942
943 /* Add Event header: */
944 pjsip_msg_add_hdr( tdata->msg,
945 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
946
947 /* Update and add expires header: */
948 if (expires >= 0)
949 sub->expires->ivalue = expires;
950 pjsip_msg_add_hdr( tdata->msg,
951 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
952
953 /* Add Accept header: */
954 pjsip_msg_add_hdr( tdata->msg,
955 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
956
957
958 /* Add Allow-Events header: */
959 pjsip_msg_add_hdr( tdata->msg,
960 pjsip_hdr_shallow_clone(tdata->pool,
961 mod_evsub.allow_events_hdr));
962
963
964 *p_tdata = tdata;
965
966
967on_return:
968
969 pjsip_dlg_dec_lock(sub->dlg);
970 return status;
971}
972
973
974/*
975 * Accept incoming subscription request.
976 */
977PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
978 pjsip_rx_data *rdata,
979 int st_code,
980 const pjsip_hdr *hdr_list )
981{
982 pjsip_tx_data *tdata;
983 pjsip_transaction *tsx;
984 pj_status_t status;
985
986 /* Check arguments */
987 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
988
989 /* Can only be for server subscription: */
990 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
991
992 /* Only expect 2xx status code (for now) */
993 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
994
995 /* Subscription MUST have been attached to the transaction.
996 * Initial subscription request will be attached on evsub_create_uas(),
997 * while subsequent requests will be attached in tsx_state()
998 */
999 tsx = pjsip_rdata_get_tsx(rdata);
1000 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
1001 PJ_EINVALIDOP);
1002
1003 /* Lock dialog */
1004 pjsip_dlg_inc_lock(sub->dlg);
1005
1006 /* Create response: */
1007 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1008 &tdata);
1009 if (status != PJ_SUCCESS)
1010 goto on_return;
1011
1012
1013 /* Add expires header: */
1014 pjsip_msg_add_hdr( tdata->msg,
1015 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1016
Benny Prijonob0808372006-03-02 21:18:58 +00001017 /* Add additional header, if any. */
1018 if (hdr_list) {
1019 const pjsip_hdr *hdr = hdr_list->next;
1020 while (hdr != hdr_list) {
1021 pjsip_msg_add_hdr( tdata->msg,
1022 pjsip_hdr_clone(tdata->pool, hdr));
1023 hdr = hdr->next;
1024 }
1025 }
Benny Prijono834aee32006-02-19 01:38:06 +00001026
1027 /* Send the response: */
1028 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1029 if (status != PJ_SUCCESS)
1030 goto on_return;
1031
1032
1033on_return:
1034
1035 pjsip_dlg_dec_lock(sub->dlg);
1036 return status;
1037}
1038
1039
1040/*
1041 * Create Subscription-State header based on current server subscription
1042 * state.
1043 */
1044static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1045 pjsip_evsub *sub,
1046 pjsip_evsub_state state,
1047 const pj_str_t *state_str,
1048 const pj_str_t *reason )
1049{
1050 pjsip_sub_state_hdr *sub_state;
1051 pj_time_val now, delay;
1052
1053 /* Get the remaining time before refresh is required */
1054 pj_gettimeofday(&now);
1055 delay = sub->refresh_time;
1056 PJ_TIME_VAL_SUB(delay, now);
1057
1058 /* Create the Subscription-State header */
1059 sub_state = pjsip_sub_state_hdr_create(pool);
1060
1061 /* Fill up the header */
1062 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001063 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001064 case PJSIP_EVSUB_STATE_SENT:
1065 case PJSIP_EVSUB_STATE_ACCEPTED:
1066 pj_assert(!"Invalid state!");
1067 /* Treat as pending */
1068
1069 case PJSIP_EVSUB_STATE_PENDING:
1070 sub_state->sub_state = STR_PENDING;
1071 sub_state->expires_param = delay.sec;
1072 break;
1073
1074 case PJSIP_EVSUB_STATE_ACTIVE:
1075 sub_state->sub_state = STR_ACTIVE;
1076 sub_state->expires_param = delay.sec;
1077 break;
1078
1079 case PJSIP_EVSUB_STATE_TERMINATED:
1080 sub_state->sub_state = STR_TERMINATED;
1081 if (reason != NULL)
1082 pj_strdup(pool, &sub_state->reason_param, reason);
1083 break;
1084
1085 case PJSIP_EVSUB_STATE_UNKNOWN:
1086 pj_assert(state_str != NULL);
1087 pj_strdup(pool, &sub_state->sub_state, state_str);
1088 break;
1089 }
1090
1091 return sub_state;
1092}
1093
1094/*
1095 * Create and send NOTIFY request.
1096 */
1097PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1098 pjsip_evsub_state state,
1099 const pj_str_t *state_str,
1100 const pj_str_t *reason,
1101 pjsip_tx_data **p_tdata)
1102{
1103 pjsip_tx_data *tdata;
1104 pjsip_sub_state_hdr *sub_state;
1105 pj_status_t status;
1106
1107 /* Check arguments. */
1108 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1109
1110 /* Lock dialog. */
1111 pjsip_dlg_inc_lock(sub->dlg);
1112
1113 /* Create NOTIFY request */
1114 status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1,
1115 &tdata);
1116 if (status != PJ_SUCCESS)
1117 goto on_return;
1118
1119 /* Add Event header */
1120 pjsip_msg_add_hdr(tdata->msg,
1121 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1122
1123 /* Add Subscription-State header */
1124 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1125 reason);
1126 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1127
1128 /* Add Allow-Events header */
1129 pjsip_msg_add_hdr(tdata->msg,
1130 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1131
1132 /* Add Authentication headers. */
1133 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1134
1135
1136
1137 /* Save destination state. */
1138 sub->dst_state = state;
1139 if (state_str)
1140 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1141 else
1142 sub->dst_state_str.slen = 0;
1143
1144
1145 *p_tdata = tdata;
1146
1147on_return:
1148 /* Unlock dialog */
1149 pjsip_dlg_dec_lock(sub->dlg);
1150 return status;
1151}
1152
1153
1154/*
1155 * Create NOTIFY to reflect current status.
1156 */
1157PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1158 pjsip_tx_data **p_tdata )
1159{
1160 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1161 NULL, p_tdata );
1162}
1163
1164
1165/*
1166 * Send request.
1167 */
1168PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1169 pjsip_tx_data *tdata)
1170{
1171 pj_status_t status;
1172
1173 /* Must be request message. */
1174 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1175 PJSIP_ENOTREQUESTMSG);
1176
1177 /* Lock */
1178 pjsip_dlg_inc_lock(sub->dlg);
1179
1180 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001181 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001182 if (status != PJ_SUCCESS)
1183 goto on_return;
1184
1185
1186 /* Special case for NOTIFY:
1187 * The new state was set in pjsip_evsub_notify(), but we apply the
1188 * new state now, when the request was actually sent.
1189 */
1190 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1191 &pjsip_notify_method)==0)
1192 {
1193 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1194 {goto on_return;});
1195
1196 set_state(sub, sub->dst_state,
1197 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
1198 NULL);
1199
1200 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1201 sub->dst_state_str.slen = 0;
1202
1203 }
1204
1205
1206on_return:
1207 pjsip_dlg_dec_lock(sub->dlg);
1208 return status;
1209}
1210
1211
1212
1213/*
1214 * Attach subscription session to newly created transaction, if appropriate.
1215 */
1216static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1217 pjsip_event *event)
1218{
1219 /*
1220 * Newly created transaction will not have subscription session
1221 * attached to it. Find the subscription session from the dialog,
1222 * by matching the Event header.
1223 */
1224 pjsip_dialog *dlg;
1225 pjsip_event_hdr *event_hdr;
1226 pjsip_msg *msg;
1227 struct dlgsub *dlgsub_head, *dlgsub;
1228 pjsip_evsub *sub;
1229
1230 dlg = pjsip_tsx_get_dlg(tsx);
1231 if (!dlg) {
1232 pj_assert(!"Transaction should have a dialog instance!");
1233 return NULL;
1234 }
1235
Benny Prijono26ff9062006-02-21 23:47:00 +00001236
Benny Prijono834aee32006-02-19 01:38:06 +00001237 switch (event->body.tsx_state.type) {
1238 case PJSIP_EVENT_RX_MSG:
1239 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1240 break;
1241 case PJSIP_EVENT_TX_MSG:
1242 msg = event->body.tsx_state.src.tdata->msg;
1243 break;
1244 default:
1245 if (tsx->role == PJSIP_ROLE_UAC)
1246 msg = tsx->last_tx->msg;
1247 else
1248 msg = NULL;
1249 break;
1250 }
1251
1252 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001253 //Note:
1254 // this transaction can be other transaction in the dialog.
1255 // The assertion below probably only valid for dialog that
1256 // only has one event subscription usage.
1257 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001258 return NULL;
1259 }
1260
1261 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1262 if (!event_hdr) {
1263 /* Not subscription related message */
1264 return NULL;
1265 }
1266
1267 /* Find the subscription in the dialog, based on the content
1268 * of Event header:
1269 */
1270
1271 dlgsub_head = dlg->mod_data[mod_evsub.mod.id];
1272 if (dlgsub_head == NULL) {
1273 dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub));
1274 pj_list_init(dlgsub_head);
1275 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1276 }
1277 dlgsub = dlgsub_head->next;
1278
1279 while (dlgsub != dlgsub_head) {
1280
Benny Prijono26ff9062006-02-21 23:47:00 +00001281 if (pj_stricmp(&dlgsub->sub->event->event_type,
1282 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001283 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001284 /* Event type matched.
1285 * Check if event ID matched too.
1286 */
1287 if (pj_strcmp(&dlgsub->sub->event->id_param,
1288 &event_hdr->id_param)==0)
1289 {
1290
1291 break;
1292
1293 }
1294 /*
1295 * Otherwise if it is an UAC subscription, AND
1296 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1297 * the session's event id is NULL, AND
1298 * the incoming request is NOTIFY with event ID, then
1299 * we consider it as a match, and update the
1300 * session's event id.
1301 */
1302 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1303 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1304 dlgsub->sub->event->id_param.slen==0 &&
1305 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1306 {
1307 /* Update session's event id. */
1308 pj_strdup(dlgsub->sub->pool,
1309 &dlgsub->sub->event->id_param,
1310 &event_hdr->id_param);
1311
1312 break;
1313 }
Benny Prijono834aee32006-02-19 01:38:06 +00001314 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001315
1316
1317
Benny Prijono834aee32006-02-19 01:38:06 +00001318 dlgsub = dlgsub->next;
1319 }
1320
1321 if (dlgsub == dlgsub_head) {
1322 /* This could be incoming request to create new subscription */
1323 PJ_LOG(4,(THIS_FILE,
1324 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001325 (int)tsx->method.name.slen,
1326 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001327 (int)event_hdr->event_type.slen,
1328 event_hdr->event_type.ptr,
1329 (int)event_hdr->id_param.slen,
1330 event_hdr->id_param.ptr));
1331
1332 /* If this is an incoming NOTIFY, reject with 481 */
1333 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1334 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1335 {
1336 pj_str_t reason = pj_str("Subscription Does Not Exist");
1337 pjsip_tx_data *tdata;
1338 pj_status_t status;
1339
1340 status = pjsip_dlg_create_response(dlg,
1341 event->body.tsx_state.src.rdata,
1342 481, &reason,
1343 &tdata);
1344 if (status == PJ_SUCCESS) {
1345 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1346 }
1347 }
1348 return NULL;
1349 }
1350
1351 /* Found! */
1352 sub = dlgsub->sub;
1353
1354 /* Attach session to the transaction */
1355 tsx->mod_data[mod_evsub.mod.id] = sub;
1356 sub->pending_tsx++;
1357
Benny Prijono69b98ab2006-03-03 10:23:35 +00001358 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1359 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1360 * transaction is started while previous one still alive, terminate
1361 * the older one.
1362 *
1363 * Sample scenario:
1364 * - subscribe sent to destination that doesn't exist, transaction
1365 * is still retransmitting request, then unsubscribe is sent.
1366 */
1367 if (tsx->role == PJSIP_ROLE_UAC &&
1368 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001369 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1370 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001371 {
1372
1373 if (sub->pending_sub &&
1374 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1375 {
1376 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001377 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001378
1379 /* By convention, we use 490 (Request Updated) status code.
1380 * When transaction handler (below) see this status code, it
1381 * will ignore the transaction.
1382 */
1383 pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1384 }
1385
1386 sub->pending_sub = tsx;
1387
Benny Prijono69b98ab2006-03-03 10:23:35 +00001388 }
1389
Benny Prijono834aee32006-02-19 01:38:06 +00001390 return sub;
1391}
1392
1393
1394/*
1395 * Create response, adding custome headers and msg body.
1396 */
1397static pj_status_t create_response( pjsip_evsub *sub,
1398 pjsip_rx_data *rdata,
1399 int st_code,
1400 const pj_str_t *st_text,
1401 const pjsip_hdr *res_hdr,
1402 const pjsip_msg_body *body,
1403 pjsip_tx_data **p_tdata)
1404{
1405 pjsip_tx_data *tdata;
1406 pjsip_hdr *hdr;
1407 pj_status_t status;
1408
1409 status = pjsip_dlg_create_response(sub->dlg, rdata,
1410 st_code, st_text, &tdata);
1411 if (status != PJ_SUCCESS)
1412 return status;
1413
1414 *p_tdata = tdata;
1415
1416 /* Add response headers. */
1417 hdr = res_hdr->next;
1418 while (hdr != res_hdr) {
1419 pjsip_msg_add_hdr( tdata->msg,
1420 pjsip_hdr_clone(tdata->pool, hdr));
1421 hdr = hdr->next;
1422 }
1423
1424 /* Add msg body, if any */
1425 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001426 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1427 if (tdata->msg->body == NULL) {
1428
1429 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1430
Benny Prijono834aee32006-02-19 01:38:06 +00001431 /* Ignore */
1432 return PJ_SUCCESS;
1433 }
1434 }
1435
1436 return PJ_SUCCESS;
1437}
1438
1439/*
1440 * Get subscription state from the value of Subscription-State header.
1441 */
1442static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1443 pjsip_evsub_state *state,
1444 pj_str_t **state_str )
1445{
1446 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1447
1448 *state = PJSIP_EVSUB_STATE_TERMINATED;
1449 *state_str = NULL;
1450
1451 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1452
1453 *state = PJSIP_EVSUB_STATE_ACTIVE;
1454 *state_str = NULL;
1455
1456 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1457
1458 *state = PJSIP_EVSUB_STATE_PENDING;
1459 *state_str = NULL;
1460
1461 } else {
1462
1463 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1464 *state_str = &sub_state->sub_state;
1465
1466 }
1467}
1468
1469/*
1470 * Transaction event processing by UAC, after subscription is sent.
1471 */
1472static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1473 pjsip_event *event )
1474{
1475
Benny Prijono736d0f72006-09-13 22:45:38 +00001476 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1477 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1478 {
Benny Prijono834aee32006-02-19 01:38:06 +00001479
1480 /* Received response to outgoing request that establishes/refresh
1481 * subscription.
1482 */
1483
1484 /* First time initial request is sent. */
1485 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1486 tsx->state == PJSIP_TSX_STATE_CALLING)
1487 {
1488 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
1489 return;
1490 }
1491
1492 /* Only interested in final response */
1493 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1494 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1495 {
1496 return;
1497 }
1498
Benny Prijono1d8d6082006-04-29 12:38:25 +00001499 /* Clear pending subscription */
1500 if (tsx == sub->pending_sub)
1501 sub->pending_sub = NULL;
1502
Benny Prijono834aee32006-02-19 01:38:06 +00001503 /* Handle authentication. */
1504 if (tsx->status_code==401 || tsx->status_code==407) {
1505 pjsip_tx_data *tdata;
1506 pj_status_t status;
1507
1508 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1509 /* Previously failed transaction has terminated */
1510 return;
1511 }
1512
1513 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1514 event->body.tsx_state.src.rdata,
1515 tsx->last_tx, &tdata);
1516 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001517 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001518
1519 if (status != PJ_SUCCESS) {
1520 /* Authentication failed! */
1521 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1522 NULL,
1523 event);
1524 return;
1525 }
1526
1527 return;
1528 }
1529
1530 if (tsx->status_code/100 == 2) {
1531
1532 /* Successfull SUBSCRIBE request!
1533 * This could be:
1534 * - response to initial SUBSCRIBE request
1535 * - response to subsequent refresh
1536 * - response to unsubscription
1537 */
1538
1539 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1540 /* Ignore; this transaction has been processed before */
1541 return;
1542 }
1543
1544 /* Update UAC refresh time, if response contains Expires header,
1545 * only when we're not unsubscribing.
1546 */
1547 if (sub->expires->ivalue != 0) {
1548 pjsip_msg *msg;
1549 pjsip_expires_hdr *expires;
1550
1551 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1552 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1553 if (expires) {
1554 sub->expires->ivalue = expires->ivalue;
1555 }
1556 }
1557
1558 /* Update time */
1559 update_expires(sub, sub->expires->ivalue);
1560
1561 /* Start UAC refresh timer, only when we're not unsubscribing */
1562 if (sub->expires->ivalue != 0) {
1563 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1564 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1565
1566 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1567 timeout));
1568 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1569
1570 } else {
1571 /* Otherwise set timer to terminate client subscription when
1572 * NOTIFY to end subscription is not received.
1573 */
1574 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1575 }
1576
1577 /* Set state, if necessary */
1578 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1579 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
1580 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
1581 }
1582
1583 } else {
1584
1585 /* Failed SUBSCRIBE request!
1586 *
1587 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1588 * other than 481, the subscription is still considered valid for
1589 * the duration of the last Expires.
1590 *
1591 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1592 * expiration, theoritically the expiration is still valid for the
1593 * next 5 seconds even when we receive non-481 failed response.
1594 *
1595 * Ah, what the heck!
1596 *
1597 * Just terminate now!
1598 *
1599 */
1600
1601 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1602 /* Ignore, has been handled before */
1603 return;
1604 }
1605
Benny Prijono69b98ab2006-03-03 10:23:35 +00001606 /* Ignore 490 (Request Updated) status.
1607 * This happens when application sends SUBSCRIBE/REFER while
1608 * another one is still in progress.
1609 */
1610 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1611 return;
1612 }
1613
Benny Prijono834aee32006-02-19 01:38:06 +00001614 /* Kill any timer. */
1615 set_timer(sub, TIMER_TYPE_NONE, 0);
1616
1617 /* Set state to TERMINATED */
1618 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1619 NULL, event);
1620
1621 }
1622
1623 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1624
1625 /* Incoming NOTIFY.
1626 * This can be the result of:
1627 * - Initial subscription response
1628 * - UAS updating the resource info.
1629 * - Unsubscription response.
1630 */
1631 int st_code = 200;
1632 pj_str_t *st_text = NULL;
1633 pjsip_hdr res_hdr;
1634 pjsip_msg_body *body = NULL;
1635
1636 pjsip_rx_data *rdata;
1637 pjsip_msg *msg;
1638 pjsip_sub_state_hdr *sub_state;
1639
1640 pjsip_evsub_state new_state;
1641 pj_str_t *new_state_str;
1642
1643 pjsip_tx_data *tdata;
1644 pj_status_t status;
1645 int next_refresh;
1646
1647 /* Only want to handle initial NOTIFY receive event. */
1648 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1649 return;
1650
1651
1652 rdata = event->body.tsx_state.src.rdata;
1653 msg = rdata->msg_info.msg;
1654
1655 pj_list_init(&res_hdr);
1656
1657 /* Get subscription state header. */
1658 sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
1659 if (sub_state == NULL) {
1660
1661 pjsip_warning_hdr *warn_hdr;
1662 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1663
1664 /* Bad request! Add warning header. */
1665 st_code = PJSIP_SC_BAD_REQUEST;
1666 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1667 pjsip_endpt_name(sub->endpt),
1668 &warn_text);
1669 pj_list_push_back(&res_hdr, warn_hdr);
1670 }
1671
1672 /* Call application registered callback to handle incoming NOTIFY,
1673 * if any.
1674 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001675 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001676 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1677 &res_hdr, &body);
1678
1679 /* Application MUST specify final response! */
1680 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1681
1682 /* Must be a valid status code */
1683 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1684 }
1685
1686
1687 /* If non-2xx should be returned, then send the response.
1688 * No need to update server subscription state.
1689 */
1690 if (st_code >= 300) {
1691 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1692 body, &tdata);
1693 if (status == PJ_SUCCESS) {
1694 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1695 }
1696
1697 /* Start timer to terminate subscription, just in case server
1698 * is not able to generate NOTIFY to our response.
1699 */
1700 if (status == PJ_SUCCESS) {
1701 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1702 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1703 } else {
1704 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1705 }
1706
1707 return;
1708 }
1709
1710 /* Update expiration from the value of expires param in
1711 * Subscription-State header, but ONLY when subscription state
1712 * is "active" or "pending", AND the header contains expires param.
1713 */
1714 if (sub->expires->ivalue != 0 &&
1715 sub_state->expires_param >= 0 &&
1716 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1717 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1718 {
1719 next_refresh = sub_state->expires_param;
1720
1721 } else {
1722 next_refresh = sub->expires->ivalue;
1723 }
1724
1725 /* Update time */
1726 update_expires(sub, next_refresh);
1727
1728 /* Start UAC refresh timer, only when we're not unsubscribing */
1729 if (sub->expires->ivalue != 0) {
1730 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1731 next_refresh - TIME_UAC_REFRESH : next_refresh;
1732
1733 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1734 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1735 }
1736
1737 /* Find out the state */
1738 get_hdr_state(sub_state, &new_state, &new_state_str);
1739
1740 /* Send response. */
1741 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1742 body, &tdata);
1743 if (status == PJ_SUCCESS)
1744 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1745
1746 /* Set the state */
1747 if (status == PJ_SUCCESS) {
1748 set_state(sub, new_state, new_state_str, event);
1749 } else {
1750 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1751 }
1752
1753
1754 } else {
1755
1756 /*
1757 * Unexpected method!
1758 */
1759 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1760 (int)tsx->method.name.slen, tsx->method.name.ptr));
1761 }
1762}
1763
1764
1765/*
1766 * Transaction event processing by UAS, after subscription is accepted.
1767 */
1768static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1769 pjsip_event *event)
1770{
1771
Benny Prijono736d0f72006-09-13 22:45:38 +00001772 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1773 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1774 {
Benny Prijono834aee32006-02-19 01:38:06 +00001775
1776 /*
1777 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1778 *
1779 */
1780 pjsip_rx_data *rdata;
1781 pjsip_event_hdr *event_hdr;
1782 pjsip_expires_hdr *expires;
1783 pjsip_msg *msg;
1784 pjsip_tx_data *tdata;
1785 int st_code = 200;
1786 pj_str_t *st_text = NULL;
1787 pjsip_hdr res_hdr;
1788 pjsip_msg_body *body = NULL;
1789 pjsip_evsub_state old_state;
1790 pj_str_t old_state_str;
1791 pj_status_t status;
1792
1793
1794 /* Only wants to handle the first event when the request is
1795 * received.
1796 */
1797 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1798 return;
1799
1800 rdata = event->body.tsx_state.src.rdata;
1801 msg = rdata->msg_info.msg;
1802
1803 /* Set expiration time based on client request (in Expires header),
1804 * or package default expiration time.
1805 */
1806 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1807 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
1808 if (event_hdr && expires) {
1809 struct evpkg *evpkg;
1810
1811 evpkg = find_pkg(&event_hdr->event_type);
1812 if (evpkg) {
1813 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1814 sub->expires->ivalue = expires->ivalue;
1815 else
1816 sub->expires->ivalue = evpkg->pkg_expires;
1817 }
1818 }
1819
1820 /* Update time (before calling on_rx_refresh, since application
1821 * will send NOTIFY.
1822 */
1823 update_expires(sub, sub->expires->ivalue);
1824
1825
1826 /* Save old state.
1827 * If application respond with non-2xx, revert to old state.
1828 */
1829 old_state = sub->state;
1830 old_state_str = sub->state_str;
1831
1832 if (sub->expires->ivalue == 0) {
1833 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1834 sub->state_str = evsub_state_names[sub->state];
1835 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1836 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1837 sub->state_str = evsub_state_names[sub->state];
1838 }
1839
1840 /* Call application's on_rx_refresh, just in case it wants to send
1841 * response other than 200 (OK)
1842 */
1843 pj_list_init(&res_hdr);
1844
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001845 if (sub->user.on_rx_refresh && sub->call_cb) {
1846 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1847 &res_hdr, &body);
1848 }
Benny Prijono834aee32006-02-19 01:38:06 +00001849
1850 /* Application MUST specify final response! */
1851 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1852
1853 /* Must be a valid status code */
1854 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1855
1856
1857 /* Create and send response */
1858 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1859 body, &tdata);
1860 if (status == PJ_SUCCESS) {
1861 /* Add expires header: */
1862 pjsip_msg_add_hdr( tdata->msg,
1863 pjsip_hdr_shallow_clone(tdata->pool,
1864 sub->expires));
1865
1866 /* Send */
1867 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1868 }
1869
1870 /* Update state or revert state */
1871 if (st_code/100==2) {
1872
1873 if (sub->expires->ivalue == 0) {
1874 set_state(sub, sub->state, NULL, event);
1875 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1876 set_state(sub, sub->state, NULL, event);
1877 }
1878
1879 /* Set UAS timeout timer, when state is not terminated. */
1880 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1881 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1882 sub->expires->ivalue));
1883 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1884 sub->expires->ivalue);
1885 }
1886
1887 } else {
1888 sub->state = old_state;
1889 sub->state_str = old_state_str;
1890 }
1891
1892
1893 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1894
1895 /* Handle authentication */
1896 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1897 (tsx->status_code==401 || tsx->status_code==407))
1898 {
1899 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1900 pjsip_tx_data *tdata;
1901 pj_status_t status;
1902
1903 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
1904 tsx->last_tx, &tdata);
1905 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001906 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00001907
1908 if (status != PJ_SUCCESS) {
1909 /* Can't authenticate. Terminate session (?) */
1910 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono441ce002006-03-07 15:15:01 +00001911 return;
Benny Prijono834aee32006-02-19 01:38:06 +00001912 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001913
1914 }
1915 /*
1916 * Terminate event usage if we receive 481, 408, and 7 class
1917 * responses.
1918 */
1919 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
1920 (tsx->status_code==481 || tsx->status_code==408 ||
1921 tsx->status_code/100 == 7))
1922 {
1923 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
Benny Prijono441ce002006-03-07 15:15:01 +00001924 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001925 }
Benny Prijono834aee32006-02-19 01:38:06 +00001926
1927 } else {
1928
1929 /*
1930 * Unexpected method!
1931 */
1932 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1933 (int)tsx->method.name.slen, tsx->method.name.ptr));
1934
1935 }
1936}
1937
1938
1939/*
1940 * Notification when transaction state has changed!
1941 */
1942static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
1943{
1944 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
1945
1946 if (sub == NULL) {
1947 sub = on_new_transaction(tsx, event);
1948 if (sub == NULL)
1949 return;
1950 }
1951
1952
1953 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001954 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00001955 (*sub->user.on_tsx_state)(sub, tsx, event);
1956
1957
1958 /* Process the event: */
1959
1960 if (sub->role == PJSIP_ROLE_UAC) {
1961 on_tsx_state_uac(sub, tsx, event);
1962 } else {
1963 on_tsx_state_uas(sub, tsx, event);
1964 }
1965
1966
1967 /* Check transaction TERMINATE event */
1968 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1969
1970 --sub->pending_tsx;
1971
1972 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
1973 sub->pending_tsx == 0)
1974 {
1975 evsub_destroy(sub);
1976 }
1977
1978 }
1979}
1980
1981