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