blob: 085d496e65049e14fe832d59506581affe5fd777 [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C) 2003-2008 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
Benny Prijono1f61a8f2007-08-16 10:11:44 +000052PJ_DEF_DATA(const pjsip_method) pjsip_subscribe_method =
Benny Prijono834aee32006-02-19 01:38:06 +000053{
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
Benny Prijono1f61a8f2007-08-16 10:11:44 +000058PJ_DEF_DATA(const pjsip_method) pjsip_notify_method =
Benny Prijono834aee32006-02-19 01:38:06 +000059{
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
Benny Prijono1f61a8f2007-08-16 10:11:44 +000064/**
65 * SUBSCRIBE method constant.
66 */
67PJ_DEF(const pjsip_method*) pjsip_get_subscribe_method()
68{
69 return &pjsip_subscribe_method;
70}
71
72/**
73 * NOTIFY method constant.
74 */
75PJ_DEF(const pjsip_method*) pjsip_get_notify_method()
76{
77 return &pjsip_notify_method;
78}
79
80
Benny Prijono834aee32006-02-19 01:38:06 +000081/*
82 * Static prototypes.
83 */
84static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*);
85static pj_status_t mod_evsub_unload(void);
86
87
88/*
89 * State names.
90 */
91static pj_str_t evsub_state_names[] =
92{
93 { "NULL", 4},
94 { "SENT", 4},
95 { "ACCEPTED", 8},
96 { "PENDING", 7},
97 { "ACTIVE", 6},
98 { "TERMINATED", 10},
99 { "UNKNOWN", 7}
100};
101
102/*
103 * Timer constants.
104 */
105
106/* Number of seconds to send SUBSCRIBE before the actual expiration */
107#define TIME_UAC_REFRESH 5
108
109/* Time to wait for the final NOTIFY after sending unsubscription */
110#define TIME_UAC_TERMINATE 5
111
112/* If client responds NOTIFY with non-2xx final response (such as 401),
113 * wait for this seconds for further NOTIFY, otherwise client will
114 * unsubscribe
115 */
116#define TIME_UAC_WAIT_NOTIFY 5
117
118
119/*
120 * Timer id
121 */
122enum timer_id
123{
124 /* No timer. */
125 TIMER_TYPE_NONE,
126
127 /* Time to refresh client subscription.
128 * The action is to call on_client_refresh() callback.
129 */
130 TIMER_TYPE_UAC_REFRESH,
131
132 /* UAS timeout after to subscription refresh.
133 * The action is to call on_server_timeout() callback.
134 */
135 TIMER_TYPE_UAS_TIMEOUT,
136
137 /* UAC waiting for final NOTIFY after unsubscribing
138 * The action is to terminate.
139 */
140 TIMER_TYPE_UAC_TERMINATE,
141
142 /* UAC waiting for further NOTIFY after sending non-2xx response to
143 * NOTIFY. The action is to unsubscribe.
144 */
145 TIMER_TYPE_UAC_WAIT_NOTIFY,
146
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000147 /* Max nb of timer types. */
148 TIMER_TYPE_MAX
Benny Prijono834aee32006-02-19 01:38:06 +0000149};
150
151static const char *timer_names[] =
152{
153 "None",
154 "UAC_REFRESH",
155 "UAS_TIMEOUT"
156 "UAC_TERMINATE",
157 "UAC_WAIT_NOTIFY",
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000158 "INVALID_TIMER"
Benny Prijono834aee32006-02-19 01:38:06 +0000159};
160
161/*
162 * Definition of event package.
163 */
164struct evpkg
165{
166 PJ_DECL_LIST_MEMBER(struct evpkg);
167
168 pj_str_t pkg_name;
169 pjsip_module *pkg_mod;
170 unsigned pkg_expires;
171 pjsip_accept_hdr *pkg_accept;
172};
173
174
175/*
176 * Event subscription module (mod-evsub).
177 */
178static struct mod_evsub
179{
180 pjsip_module mod;
181 pj_pool_t *pool;
182 pjsip_endpoint *endpt;
183 struct evpkg pkg_list;
184 pjsip_allow_events_hdr *allow_events_hdr;
185
186} mod_evsub =
187{
188 {
Benny Prijono2f8992b2006-02-25 21:16:36 +0000189 NULL, NULL, /* prev, next. */
190 { "mod-evsub", 9 }, /* Name. */
191 -1, /* Id */
192 PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */
193 NULL, /* load() */
194 NULL, /* start() */
195 NULL, /* stop() */
196 &mod_evsub_unload, /* unload() */
197 NULL, /* on_rx_request() */
198 NULL, /* on_rx_response() */
199 NULL, /* on_tx_request. */
200 NULL, /* on_tx_response() */
201 &mod_evsub_on_tsx_state, /* on_tsx_state() */
Benny Prijono834aee32006-02-19 01:38:06 +0000202 }
203};
204
205
206/*
207 * Event subscription session.
208 */
209struct pjsip_evsub
210{
211 char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
212 pj_pool_t *pool; /**< Pool. */
213 pjsip_endpoint *endpt; /**< Endpoint instance. */
214 pjsip_dialog *dlg; /**< Underlying dialog. */
215 struct evpkg *pkg; /**< The event package. */
Benny Prijono26ff9062006-02-21 23:47:00 +0000216 unsigned option; /**< Options. */
Benny Prijono834aee32006-02-19 01:38:06 +0000217 pjsip_evsub_user user; /**< Callback. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000218 pj_bool_t call_cb; /**< Notify callback? */
Benny Prijono834aee32006-02-19 01:38:06 +0000219 pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */
220 pjsip_evsub_state state; /**< Subscription state. */
221 pj_str_t state_str; /**< String describing the state. */
222 pjsip_evsub_state dst_state; /**< Pending state to be set. */
223 pj_str_t dst_state_str;/**< Pending state to be set. */
224 pjsip_method method; /**< Method that established subscr.*/
225 pjsip_event_hdr *event; /**< Event description. */
226 pjsip_expires_hdr *expires; /**< Expires header */
227 pjsip_accept_hdr *accept; /**< Local Accept header. */
228
229 pj_time_val refresh_time; /**< Time to refresh. */
230 pj_timer_entry timer; /**< Internal timer. */
231 int pending_tsx; /**< Number of pending transactions.*/
Benny Prijono69b98ab2006-03-03 10:23:35 +0000232 pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */
Benny Prijono834aee32006-02-19 01:38:06 +0000233
234 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
235};
236
237
238/*
239 * This is the structure that will be "attached" to dialog.
240 * The purpose is to allow multiple subscriptions inside a dialog.
241 */
242struct dlgsub
243{
244 PJ_DECL_LIST_MEMBER(struct dlgsub);
245 pjsip_evsub *sub;
246};
247
248
249/* Static vars. */
250static const pj_str_t STR_EVENT = { "Event", 5 };
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000251static const pj_str_t STR_EVENT_S = { "Event", 5 };
Benny Prijono834aee32006-02-19 01:38:06 +0000252static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
253static const pj_str_t STR_TERMINATED = { "terminated", 10 };
254static const pj_str_t STR_ACTIVE = { "active", 6 };
255static const pj_str_t STR_PENDING = { "pending", 7 };
256static const pj_str_t STR_TIMEOUT = { "timeout", 7};
257
Benny Prijono26ff9062006-02-21 23:47:00 +0000258
Benny Prijono834aee32006-02-19 01:38:06 +0000259/*
260 * On unload module.
261 */
262static pj_status_t mod_evsub_unload(void)
263{
264 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
265 mod_evsub.pool = NULL;
266
267 return PJ_SUCCESS;
268}
269
Benny Prijono26ff9062006-02-21 23:47:00 +0000270/* Proto for pjsipsimple_strerror().
271 * Defined in errno.c
272 */
273PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode,
274 char *buf, pj_size_t bufsize );
275
Benny Prijono834aee32006-02-19 01:38:06 +0000276/*
277 * Init and register module.
278 */
279PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
280{
281 pj_status_t status;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000282 pj_str_t method_tags[] = {
283 { "SUBSCRIBE", 9},
284 { "NOTIFY", 6}
285 };
Benny Prijono834aee32006-02-19 01:38:06 +0000286
Benny Prijono26ff9062006-02-21 23:47:00 +0000287 pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
288 &pjsipsimple_strerror);
289
Benny Prijono834aee32006-02-19 01:38:06 +0000290 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
291 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
292
293 /* Keep endpoint for future reference: */
294 mod_evsub.endpt = endpt;
295
296 /* Init event package list: */
297 pj_list_init(&mod_evsub.pkg_list);
298
299 /* Create pool: */
Benny Prijono10d8dbd2008-07-13 13:12:36 +0000300 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 512, 512);
Benny Prijono834aee32006-02-19 01:38:06 +0000301 if (!mod_evsub.pool)
302 return PJ_ENOMEM;
303
304 /* Register module: */
305 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
306 if (status != PJ_SUCCESS)
307 goto on_error;
308
309 /* Create Allow-Events header: */
310 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
311
312 /* Register SIP-event specific headers parser: */
313 pjsip_evsub_init_parser();
314
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000315 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
316 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
317 2, method_tags);
318
319 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000320 return PJ_SUCCESS;
321
322on_error:
323 if (mod_evsub.pool) {
324 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
325 mod_evsub.pool = NULL;
326 }
327 mod_evsub.endpt = NULL;
328 return status;
329}
330
331
332/*
333 * Get the instance of the module.
334 */
335PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
336{
337 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
338
339 return &mod_evsub.mod;
340}
341
342
343/*
344 * Get the event subscription instance in the transaction.
345 */
346PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
347{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000348 return (pjsip_evsub*) tsx->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000349}
350
351
352/*
353 * Set event subscription's module data.
354 */
355PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
356 void *data )
357{
358 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
359 sub->mod_data[mod_id] = data;
360}
361
362
363/*
364 * Get event subscription's module data.
365 */
366PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
367{
368 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
369 return sub->mod_data[mod_id];
370}
371
372
373/*
374 * Find registered event package with matching name.
375 */
376static struct evpkg* find_pkg(const pj_str_t *event_name)
377{
378 struct evpkg *pkg;
379
380 pkg = mod_evsub.pkg_list.next;
381 while (pkg != &mod_evsub.pkg_list) {
382
383 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
384 return pkg;
385 }
386
387 pkg = pkg->next;
388 }
389
390 return NULL;
391}
392
393/*
394 * Register an event package
395 */
396PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
397 const pj_str_t *event_name,
398 unsigned expires,
399 unsigned accept_cnt,
400 const pj_str_t accept[])
401{
402 struct evpkg *pkg;
403 unsigned i;
404
405 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
406 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
407 PJ_ETOOMANY);
408
Benny Prijonoc2456cc2007-10-25 03:19:58 +0000409 /* Make sure evsub module has been initialized */
410 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, PJ_EINVALIDOP);
411
Benny Prijono834aee32006-02-19 01:38:06 +0000412 /* Make sure no module with the specified name already registered: */
413
414 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
415
416
417 /* Create new event package: */
418
Benny Prijono9d4469d2007-05-02 05:14:29 +0000419 pkg = PJ_POOL_ALLOC_T(mod_evsub.pool, struct evpkg);
Benny Prijono834aee32006-02-19 01:38:06 +0000420 pkg->pkg_mod = pkg_mod;
421 pkg->pkg_expires = expires;
422 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
423
424 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
425 pkg->pkg_accept->count = accept_cnt;
426 for (i=0; i<accept_cnt; ++i) {
427 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
428 }
429
430 /* Add to package list: */
431
432 pj_list_push_back(&mod_evsub.pkg_list, pkg);
433
434 /* Add to Allow-Events header: */
435
436 if (mod_evsub.allow_events_hdr->count !=
437 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
438 {
439 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
440 pkg->pkg_name;
441 ++mod_evsub.allow_events_hdr->count;
442 }
443
Benny Prijono56315612006-07-18 14:39:40 +0000444 /* Add to endpoint's Accept header */
445 pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod,
446 PJSIP_H_ACCEPT, NULL,
447 pkg->pkg_accept->count,
448 pkg->pkg_accept->values);
449
Benny Prijono834aee32006-02-19 01:38:06 +0000450
451 /* Done */
452
453 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
454 (int)event_name->slen, event_name->ptr,
455 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
456
457 return PJ_SUCCESS;
458}
459
460
Benny Prijono56315612006-07-18 14:39:40 +0000461/*
462 * Retrieve Allow-Events header
463 */
464PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m)
465{
466 struct mod_evsub *mod;
467
468 if (m == NULL)
469 m = pjsip_evsub_instance();
470
471 mod = (struct mod_evsub*)m;
472
473 return (pjsip_hdr*) mod->allow_events_hdr;
474}
475
Benny Prijono834aee32006-02-19 01:38:06 +0000476
477/*
478 * Update expiration time.
479 */
480static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
481{
482 pj_gettimeofday(&sub->refresh_time);
483 sub->refresh_time.sec += interval;
484}
485
486
487/*
488 * Schedule timer.
489 */
490static void set_timer( pjsip_evsub *sub, int timer_id,
491 pj_int32_t seconds)
492{
493 if (sub->timer.id != TIMER_TYPE_NONE) {
494 PJ_LOG(5,(sub->obj_name, "%s %s timer",
495 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
496 timer_names[sub->timer.id]));
497 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
498 sub->timer.id = TIMER_TYPE_NONE;
499 }
500
501 if (timer_id != TIMER_TYPE_NONE) {
502 pj_time_val timeout;
503
504 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000505 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
506 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000507
508 timeout.sec = seconds;
509 timeout.msec = 0;
510 sub->timer.id = timer_id;
511
512 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
513
514 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
515 timer_names[sub->timer.id], timeout.sec));
516 }
517}
518
519
520/*
521 * Destroy session.
522 */
523static void evsub_destroy( pjsip_evsub *sub )
524{
525 struct dlgsub *dlgsub_head, *dlgsub;
526
527 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
528
529 /* Kill timer */
530 set_timer(sub, TIMER_TYPE_NONE, 0);
531
Benny Prijono9d4469d2007-05-02 05:14:29 +0000532 /* Remove this session from dialog's list of subscription */
533 dlgsub_head = (struct dlgsub *) sub->dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000534 dlgsub = dlgsub_head->next;
535 while (dlgsub != dlgsub_head) {
536
537 if (dlgsub->sub == sub) {
538 pj_list_erase(dlgsub);
539 break;
540 }
541
542 dlgsub = dlgsub->next;
543 }
544
545 /* Decrement dialog's session */
546 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
547}
548
549/*
550 * Set subscription session state.
551 */
552static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
553 const pj_str_t *state_str, pjsip_event *event)
554{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000555 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000556 pj_str_t old_state_str = sub->state_str;
557
558 sub->state = state;
559
560 if (state_str && state_str->slen)
561 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
562 else
563 sub->state_str = evsub_state_names[state];
564
565 PJ_LOG(4,(sub->obj_name,
566 "Subscription state changed %.*s --> %.*s",
567 (int)old_state_str.slen,
568 old_state_str.ptr,
569 (int)sub->state_str.slen,
570 sub->state_str.ptr));
571
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000572 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000573 (*sub->user.on_evsub_state)(sub, event);
574
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000575 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
576 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
577 {
Benny Prijono834aee32006-02-19 01:38:06 +0000578 if (sub->pending_tsx == 0) {
579 evsub_destroy(sub);
580 }
581 }
582}
583
584
585/*
586 * Timer callback.
587 */
588static void on_timer( pj_timer_heap_t *timer_heap,
589 struct pj_timer_entry *entry)
590{
591 pjsip_evsub *sub;
592 int timer_id;
593
594 PJ_UNUSED_ARG(timer_heap);
595
Benny Prijono9d4469d2007-05-02 05:14:29 +0000596 sub = (pjsip_evsub*) entry->user_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000597
598 pjsip_dlg_inc_lock(sub->dlg);
599
600 timer_id = entry->id;
601 entry->id = TIMER_TYPE_NONE;
602
603 switch (timer_id) {
604
605 case TIMER_TYPE_UAC_REFRESH:
606 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000607 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000608 (*sub->user.on_client_refresh)(sub);
609 } else {
610 pjsip_tx_data *tdata;
611 pj_status_t status;
612
613 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000614 status = pjsip_evsub_initiate(sub, NULL,
Benny Prijono834aee32006-02-19 01:38:06 +0000615 sub->expires->ivalue,
616 &tdata);
617 if (status == PJ_SUCCESS)
618 pjsip_evsub_send_request(sub, tdata);
619 }
620 break;
621
622 case TIMER_TYPE_UAS_TIMEOUT:
623 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000624 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000625 (*sub->user.on_server_timeout)(sub);
626 } else {
627 pjsip_tx_data *tdata;
628 pj_status_t status;
629
630 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
631 "Sending NOTIFY to terminate."));
632 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
633 NULL, &STR_TIMEOUT, &tdata);
634 if (status == PJ_SUCCESS)
635 pjsip_evsub_send_request(sub, tdata);
636 }
637 break;
638
639 case TIMER_TYPE_UAC_TERMINATE:
640 {
Benny Prijono834aee32006-02-19 01:38:06 +0000641 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
642 "Terminating.."));
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000643 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000644 }
645 break;
646
647 case TIMER_TYPE_UAC_WAIT_NOTIFY:
648 {
649 pjsip_tx_data *tdata;
650 pj_status_t status;
651
652 PJ_LOG(5,(sub->obj_name,
653 "Timeout waiting for subsequent NOTIFY (we did "
654 "send non-2xx response for previous NOTIFY). "
655 "Unsubscribing.."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000656 status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000657 if (status == PJ_SUCCESS)
658 pjsip_evsub_send_request(sub, tdata);
659 }
660 break;
661
662 default:
663 pj_assert(!"Invalid timer id");
664 }
665
666 pjsip_dlg_dec_lock(sub->dlg);
667}
668
669
670/*
671 * Create subscription session, used for both client and notifier.
672 */
673static pj_status_t evsub_create( pjsip_dialog *dlg,
674 pjsip_role_e role,
675 const pjsip_evsub_user *user_cb,
676 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000677 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000678 pjsip_evsub **p_evsub )
679{
680 pjsip_evsub *sub;
681 struct evpkg *pkg;
682 struct dlgsub *dlgsub_head, *dlgsub;
683 pj_status_t status;
684
685 /* Make sure there's package register for the event name: */
686
687 pkg = find_pkg(event);
688 if (pkg == NULL)
689 return PJSIP_SIMPLE_ENOPKG;
690
691
Benny Prijono8eae8382006-08-10 21:44:26 +0000692 /* Must lock dialog before using pool etc. */
693 pjsip_dlg_inc_lock(dlg);
694
Benny Prijono834aee32006-02-19 01:38:06 +0000695 /* Init attributes: */
696
Benny Prijono9d4469d2007-05-02 05:14:29 +0000697 sub = PJ_POOL_ZALLOC_T(dlg->pool, struct pjsip_evsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000698 sub->pool = dlg->pool;
699 sub->endpt = dlg->endpt;
700 sub->dlg = dlg;
701 sub->pkg = pkg;
702 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000703 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000704 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000705 sub->state = PJSIP_EVSUB_STATE_NULL;
706 sub->state_str = evsub_state_names[sub->state];
707 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
Benny Prijono9d4469d2007-05-02 05:14:29 +0000708 sub->accept = (pjsip_accept_hdr*)
709 pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
Benny Prijono834aee32006-02-19 01:38:06 +0000710
711 sub->timer.user_data = sub;
712 sub->timer.cb = &on_timer;
713
714 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000715 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
716 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000717
718
719 /* Copy callback, if any: */
720 if (user_cb)
721 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
722
723
724 /* Create Event header: */
725 sub->event = pjsip_event_hdr_create(sub->pool);
726 pj_strdup(sub->pool, &sub->event->event_type, event);
727
728
729 /* Create subcription list: */
730
Benny Prijono9d4469d2007-05-02 05:14:29 +0000731 dlgsub_head = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
732 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000733 dlgsub->sub = sub;
734
735 pj_list_init(dlgsub_head);
736 pj_list_push_back(dlgsub_head, dlgsub);
737
738
739 /* Register as dialog usage: */
740
741 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
Benny Prijono8eae8382006-08-10 21:44:26 +0000742 if (status != PJ_SUCCESS) {
743 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000744 return status;
Benny Prijono8eae8382006-08-10 21:44:26 +0000745 }
Benny Prijono834aee32006-02-19 01:38:06 +0000746
747
748 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
749 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
750 dlg->obj_name));
751
752 *p_evsub = sub;
Benny Prijono8eae8382006-08-10 21:44:26 +0000753 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000754
755 return PJ_SUCCESS;
756}
757
758
759
760/*
761 * Create client subscription session.
762 */
763PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
764 const pjsip_evsub_user *user_cb,
765 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000766 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000767 pjsip_evsub **p_evsub)
768{
769 pjsip_evsub *sub;
770 pj_status_t status;
771
772 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
773
774 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000775 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000776 if (status != PJ_SUCCESS)
777 goto on_return;
778
Benny Prijono26ff9062006-02-21 23:47:00 +0000779 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
780 * is not specified.
781 */
782 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
783 pj_create_unique_string(sub->pool, &sub->event->id_param);
784 }
Benny Prijono834aee32006-02-19 01:38:06 +0000785
786 /* Increment dlg session. */
787 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
788
789 /* Done */
790 *p_evsub = sub;
791
792on_return:
793 pjsip_dlg_dec_lock(dlg);
794 return status;
795}
796
797
798/*
799 * Create server subscription session from incoming request.
800 */
801PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
802 const pjsip_evsub_user *user_cb,
803 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000804 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000805 pjsip_evsub **p_evsub)
806{
807 pjsip_evsub *sub;
808 pjsip_transaction *tsx;
809 pjsip_accept_hdr *accept_hdr;
810 pjsip_event_hdr *event_hdr;
811 pjsip_expires_hdr *expires_hdr;
812 pj_status_t status;
813
814 /* Check arguments: */
815 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
816
817 /* MUST be request message: */
818 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
819 PJSIP_ENOTREQUESTMSG);
820
821 /* Transaction MUST have been created (in the dialog) */
822 tsx = pjsip_rdata_get_tsx(rdata);
823 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
824
825 /* No subscription must have been attached to transaction */
826 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
827 PJSIP_ETYPEEXISTS);
828
829 /* Package MUST implement on_rx_refresh */
830 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
831
Benny Prijono26ff9062006-02-21 23:47:00 +0000832 /* Request MUST have "Event" header. We need the Event header to get
833 * the package name (don't want to add more arguments in the function).
834 */
Benny Prijono834aee32006-02-19 01:38:06 +0000835 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000836 pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &STR_EVENT,
837 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000838 if (event_hdr == NULL) {
839 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
840 }
841
842 /* Start locking the mutex: */
843
844 pjsip_dlg_inc_lock(dlg);
845
846 /* Create the session: */
847
848 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000849 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000850 if (status != PJ_SUCCESS)
851 goto on_return;
852
853 /* Just duplicate Event header from the request */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000854 sub->event = (pjsip_event_hdr*) pjsip_hdr_clone(sub->pool, event_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000855
856 /* Set the method: */
857 pjsip_method_copy(sub->pool, &sub->method,
858 &rdata->msg_info.msg->line.req.method);
859
860 /* Update expiration time according to client request: */
861
862 expires_hdr = (pjsip_expires_hdr*)
863 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
864 if (expires_hdr) {
865 sub->expires->ivalue = expires_hdr->ivalue;
866 }
867
868 /* Update time. */
869 update_expires(sub, sub->expires->ivalue);
870
871 /* Update Accept header: */
872
873 accept_hdr = (pjsip_accept_hdr*)
874 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
875 if (accept_hdr)
Benny Prijono9d4469d2007-05-02 05:14:29 +0000876 sub->accept = (pjsip_accept_hdr*)pjsip_hdr_clone(sub->pool,accept_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000877
878 /* We can start the session: */
879
880 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
881 sub->pending_tsx++;
882 tsx->mod_data[mod_evsub.mod.id] = sub;
883
884
885 /* Done. */
886 *p_evsub = sub;
887
888
889on_return:
890 pjsip_dlg_dec_lock(dlg);
891 return status;
892}
893
894
895/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000896 * Forcefully destroy subscription.
897 */
898PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
899 pj_bool_t notify )
900{
901 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
902
903 pjsip_dlg_inc_lock(sub->dlg);
904
Benny Prijonod524e822006-09-22 12:48:18 +0000905 /* I think it's pretty safe to disable this check.
906
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000907 if (sub->pending_tsx) {
908 pj_assert(!"Unable to terminate when there's pending tsx");
909 pjsip_dlg_dec_lock(sub->dlg);
910 return PJ_EINVALIDOP;
911 }
Benny Prijonod524e822006-09-22 12:48:18 +0000912 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000913
914 sub->call_cb = notify;
915 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
916
917 pjsip_dlg_dec_lock(sub->dlg);
918 return PJ_SUCCESS;
919}
920
921/*
Benny Prijono834aee32006-02-19 01:38:06 +0000922 * Get subscription state.
923 */
924PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
925{
926 return sub->state;
927}
928
929/*
930 * Get state name.
931 */
932PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
933{
934 return sub->state_str.ptr;
935}
936
937
938/*
939 * Initiate client subscription
940 */
941PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
942 const pjsip_method *method,
943 pj_int32_t expires,
944 pjsip_tx_data **p_tdata)
945{
946 pjsip_tx_data *tdata;
947 pj_status_t status;
948
949 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
950
951 /* Use SUBSCRIBE if method is not specified */
952 if (method == NULL)
953 method = &pjsip_subscribe_method;
954
955 pjsip_dlg_inc_lock(sub->dlg);
956
957 /* Update method: */
958 if (sub->state == PJSIP_EVSUB_STATE_NULL)
959 pjsip_method_copy(sub->pool, &sub->method, method);
960
961 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
962 if (status != PJ_SUCCESS)
963 goto on_return;
964
965
966 /* Add Event header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000967 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000968 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
969
970 /* Update and add expires header: */
971 if (expires >= 0)
972 sub->expires->ivalue = expires;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000973 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000974 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
975
976 /* Add Accept header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000977 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000978 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
979
980
981 /* Add Allow-Events header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000982 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000983 pjsip_hdr_shallow_clone(tdata->pool,
984 mod_evsub.allow_events_hdr));
985
986
987 *p_tdata = tdata;
988
989
990on_return:
991
992 pjsip_dlg_dec_lock(sub->dlg);
993 return status;
994}
995
996
997/*
998 * Accept incoming subscription request.
999 */
1000PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
1001 pjsip_rx_data *rdata,
1002 int st_code,
1003 const pjsip_hdr *hdr_list )
1004{
1005 pjsip_tx_data *tdata;
1006 pjsip_transaction *tsx;
1007 pj_status_t status;
1008
1009 /* Check arguments */
1010 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
1011
1012 /* Can only be for server subscription: */
1013 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
1014
1015 /* Only expect 2xx status code (for now) */
1016 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
1017
1018 /* Subscription MUST have been attached to the transaction.
1019 * Initial subscription request will be attached on evsub_create_uas(),
1020 * while subsequent requests will be attached in tsx_state()
1021 */
1022 tsx = pjsip_rdata_get_tsx(rdata);
1023 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
1024 PJ_EINVALIDOP);
1025
1026 /* Lock dialog */
1027 pjsip_dlg_inc_lock(sub->dlg);
1028
1029 /* Create response: */
1030 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1031 &tdata);
1032 if (status != PJ_SUCCESS)
1033 goto on_return;
1034
1035
1036 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001037 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001038 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1039
Benny Prijonob0808372006-03-02 21:18:58 +00001040 /* Add additional header, if any. */
1041 if (hdr_list) {
1042 const pjsip_hdr *hdr = hdr_list->next;
1043 while (hdr != hdr_list) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001044 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonob0808372006-03-02 21:18:58 +00001045 pjsip_hdr_clone(tdata->pool, hdr));
1046 hdr = hdr->next;
1047 }
1048 }
Benny Prijono834aee32006-02-19 01:38:06 +00001049
1050 /* Send the response: */
1051 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1052 if (status != PJ_SUCCESS)
1053 goto on_return;
1054
1055
1056on_return:
1057
1058 pjsip_dlg_dec_lock(sub->dlg);
1059 return status;
1060}
1061
1062
1063/*
1064 * Create Subscription-State header based on current server subscription
1065 * state.
1066 */
1067static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1068 pjsip_evsub *sub,
1069 pjsip_evsub_state state,
1070 const pj_str_t *state_str,
1071 const pj_str_t *reason )
1072{
1073 pjsip_sub_state_hdr *sub_state;
1074 pj_time_val now, delay;
1075
1076 /* Get the remaining time before refresh is required */
1077 pj_gettimeofday(&now);
1078 delay = sub->refresh_time;
1079 PJ_TIME_VAL_SUB(delay, now);
1080
1081 /* Create the Subscription-State header */
1082 sub_state = pjsip_sub_state_hdr_create(pool);
1083
1084 /* Fill up the header */
1085 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001086 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001087 case PJSIP_EVSUB_STATE_SENT:
1088 case PJSIP_EVSUB_STATE_ACCEPTED:
1089 pj_assert(!"Invalid state!");
1090 /* Treat as pending */
1091
1092 case PJSIP_EVSUB_STATE_PENDING:
1093 sub_state->sub_state = STR_PENDING;
1094 sub_state->expires_param = delay.sec;
1095 break;
1096
1097 case PJSIP_EVSUB_STATE_ACTIVE:
1098 sub_state->sub_state = STR_ACTIVE;
1099 sub_state->expires_param = delay.sec;
1100 break;
1101
1102 case PJSIP_EVSUB_STATE_TERMINATED:
1103 sub_state->sub_state = STR_TERMINATED;
1104 if (reason != NULL)
1105 pj_strdup(pool, &sub_state->reason_param, reason);
1106 break;
1107
1108 case PJSIP_EVSUB_STATE_UNKNOWN:
1109 pj_assert(state_str != NULL);
1110 pj_strdup(pool, &sub_state->sub_state, state_str);
1111 break;
1112 }
1113
1114 return sub_state;
1115}
1116
1117/*
1118 * Create and send NOTIFY request.
1119 */
1120PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1121 pjsip_evsub_state state,
1122 const pj_str_t *state_str,
1123 const pj_str_t *reason,
1124 pjsip_tx_data **p_tdata)
1125{
1126 pjsip_tx_data *tdata;
1127 pjsip_sub_state_hdr *sub_state;
1128 pj_status_t status;
1129
1130 /* Check arguments. */
1131 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1132
1133 /* Lock dialog. */
1134 pjsip_dlg_inc_lock(sub->dlg);
1135
1136 /* Create NOTIFY request */
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001137 status = pjsip_dlg_create_request( sub->dlg, pjsip_get_notify_method(),
1138 -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001139 if (status != PJ_SUCCESS)
1140 goto on_return;
1141
1142 /* Add Event header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001143 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001144 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1145
1146 /* Add Subscription-State header */
1147 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1148 reason);
1149 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1150
1151 /* Add Allow-Events header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001152 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001153 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1154
1155 /* Add Authentication headers. */
1156 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1157
1158
1159
1160 /* Save destination state. */
1161 sub->dst_state = state;
1162 if (state_str)
1163 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1164 else
1165 sub->dst_state_str.slen = 0;
1166
1167
1168 *p_tdata = tdata;
1169
1170on_return:
1171 /* Unlock dialog */
1172 pjsip_dlg_dec_lock(sub->dlg);
1173 return status;
1174}
1175
1176
1177/*
1178 * Create NOTIFY to reflect current status.
1179 */
1180PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1181 pjsip_tx_data **p_tdata )
1182{
1183 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1184 NULL, p_tdata );
1185}
1186
1187
1188/*
1189 * Send request.
1190 */
1191PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1192 pjsip_tx_data *tdata)
1193{
1194 pj_status_t status;
1195
1196 /* Must be request message. */
1197 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1198 PJSIP_ENOTREQUESTMSG);
1199
1200 /* Lock */
1201 pjsip_dlg_inc_lock(sub->dlg);
1202
1203 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001204 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001205 if (status != PJ_SUCCESS)
1206 goto on_return;
1207
1208
1209 /* Special case for NOTIFY:
1210 * The new state was set in pjsip_evsub_notify(), but we apply the
1211 * new state now, when the request was actually sent.
1212 */
1213 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1214 &pjsip_notify_method)==0)
1215 {
1216 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1217 {goto on_return;});
1218
1219 set_state(sub, sub->dst_state,
1220 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
1221 NULL);
1222
1223 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1224 sub->dst_state_str.slen = 0;
1225
1226 }
1227
1228
1229on_return:
1230 pjsip_dlg_dec_lock(sub->dlg);
1231 return status;
1232}
1233
1234
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001235/* Callback to be called to terminate transaction. */
1236static void terminate_timer_cb(pj_timer_heap_t *timer_heap,
1237 struct pj_timer_entry *entry)
1238{
1239 pj_str_t *key;
1240 pjsip_transaction *tsx;
1241
1242 PJ_UNUSED_ARG(timer_heap);
1243
1244 key = (pj_str_t*)entry->user_data;
1245 tsx = pjsip_tsx_layer_find_tsx(key, PJ_FALSE);
1246 /* Chance of race condition here */
1247 if (tsx) {
1248 pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_UPDATED);
1249 }
1250}
1251
Benny Prijono834aee32006-02-19 01:38:06 +00001252
1253/*
1254 * Attach subscription session to newly created transaction, if appropriate.
1255 */
1256static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1257 pjsip_event *event)
1258{
1259 /*
1260 * Newly created transaction will not have subscription session
1261 * attached to it. Find the subscription session from the dialog,
1262 * by matching the Event header.
1263 */
1264 pjsip_dialog *dlg;
1265 pjsip_event_hdr *event_hdr;
1266 pjsip_msg *msg;
1267 struct dlgsub *dlgsub_head, *dlgsub;
1268 pjsip_evsub *sub;
1269
1270 dlg = pjsip_tsx_get_dlg(tsx);
1271 if (!dlg) {
1272 pj_assert(!"Transaction should have a dialog instance!");
1273 return NULL;
1274 }
1275
Benny Prijono26ff9062006-02-21 23:47:00 +00001276
Benny Prijono834aee32006-02-19 01:38:06 +00001277 switch (event->body.tsx_state.type) {
1278 case PJSIP_EVENT_RX_MSG:
1279 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1280 break;
1281 case PJSIP_EVENT_TX_MSG:
1282 msg = event->body.tsx_state.src.tdata->msg;
1283 break;
1284 default:
1285 if (tsx->role == PJSIP_ROLE_UAC)
1286 msg = tsx->last_tx->msg;
1287 else
1288 msg = NULL;
1289 break;
1290 }
1291
1292 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001293 //Note:
1294 // this transaction can be other transaction in the dialog.
1295 // The assertion below probably only valid for dialog that
1296 // only has one event subscription usage.
1297 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001298 return NULL;
1299 }
1300
Benny Prijono9d4469d2007-05-02 05:14:29 +00001301 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001302 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1303 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001304 if (!event_hdr) {
1305 /* Not subscription related message */
1306 return NULL;
1307 }
1308
1309 /* Find the subscription in the dialog, based on the content
1310 * of Event header:
1311 */
1312
Benny Prijono9d4469d2007-05-02 05:14:29 +00001313 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +00001314 if (dlgsub_head == NULL) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001315 dlgsub_head = PJ_POOL_ALLOC_T(dlg->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +00001316 pj_list_init(dlgsub_head);
1317 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1318 }
1319 dlgsub = dlgsub_head->next;
1320
1321 while (dlgsub != dlgsub_head) {
1322
Benny Prijono26ff9062006-02-21 23:47:00 +00001323 if (pj_stricmp(&dlgsub->sub->event->event_type,
1324 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001325 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001326 /* Event type matched.
1327 * Check if event ID matched too.
1328 */
1329 if (pj_strcmp(&dlgsub->sub->event->id_param,
1330 &event_hdr->id_param)==0)
1331 {
1332
1333 break;
1334
1335 }
1336 /*
1337 * Otherwise if it is an UAC subscription, AND
1338 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1339 * the session's event id is NULL, AND
1340 * the incoming request is NOTIFY with event ID, then
1341 * we consider it as a match, and update the
1342 * session's event id.
1343 */
1344 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1345 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1346 dlgsub->sub->event->id_param.slen==0 &&
1347 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1348 {
1349 /* Update session's event id. */
1350 pj_strdup(dlgsub->sub->pool,
1351 &dlgsub->sub->event->id_param,
1352 &event_hdr->id_param);
1353
1354 break;
1355 }
Benny Prijono834aee32006-02-19 01:38:06 +00001356 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001357
1358
1359
Benny Prijono834aee32006-02-19 01:38:06 +00001360 dlgsub = dlgsub->next;
1361 }
1362
1363 if (dlgsub == dlgsub_head) {
1364 /* This could be incoming request to create new subscription */
1365 PJ_LOG(4,(THIS_FILE,
1366 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001367 (int)tsx->method.name.slen,
1368 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001369 (int)event_hdr->event_type.slen,
1370 event_hdr->event_type.ptr,
1371 (int)event_hdr->id_param.slen,
1372 event_hdr->id_param.ptr));
1373
1374 /* If this is an incoming NOTIFY, reject with 481 */
1375 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1376 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1377 {
1378 pj_str_t reason = pj_str("Subscription Does Not Exist");
1379 pjsip_tx_data *tdata;
1380 pj_status_t status;
1381
1382 status = pjsip_dlg_create_response(dlg,
1383 event->body.tsx_state.src.rdata,
1384 481, &reason,
1385 &tdata);
1386 if (status == PJ_SUCCESS) {
1387 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1388 }
1389 }
1390 return NULL;
1391 }
1392
1393 /* Found! */
1394 sub = dlgsub->sub;
1395
1396 /* Attach session to the transaction */
1397 tsx->mod_data[mod_evsub.mod.id] = sub;
1398 sub->pending_tsx++;
1399
Benny Prijono69b98ab2006-03-03 10:23:35 +00001400 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1401 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1402 * transaction is started while previous one still alive, terminate
1403 * the older one.
1404 *
1405 * Sample scenario:
1406 * - subscribe sent to destination that doesn't exist, transaction
1407 * is still retransmitting request, then unsubscribe is sent.
1408 */
1409 if (tsx->role == PJSIP_ROLE_UAC &&
1410 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001411 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1412 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001413 {
1414
1415 if (sub->pending_sub &&
1416 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1417 {
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001418 pj_timer_entry *timer;
1419 pj_str_t *key;
1420 pj_time_val timeout = {0, 0};
1421
Benny Prijono69b98ab2006-03-03 10:23:35 +00001422 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001423 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001424
1425 /* By convention, we use 490 (Request Updated) status code.
1426 * When transaction handler (below) see this status code, it
1427 * will ignore the transaction.
1428 */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001429 /* This unfortunately may cause deadlock, because at the moment
1430 * we are holding dialog's mutex. If a response to this
1431 * transaction is in progress in another thread, that thread
1432 * will deadlock when trying to acquire dialog mutex, because
1433 * it is holding the transaction mutex.
1434 *
1435 * So the solution is to register timer to kill this transaction.
1436 */
1437 //pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1438 timer = PJ_POOL_ZALLOC_T(dlg->pool, pj_timer_entry);
1439 key = PJ_POOL_ALLOC_T(dlg->pool, pj_str_t);
1440 pj_strdup(dlg->pool, key, &sub->pending_sub->transaction_key);
1441 timer->cb = &terminate_timer_cb;
1442 timer->user_data = key;
1443
1444 pjsip_endpt_schedule_timer(dlg->endpt, timer, &timeout);
Benny Prijono69b98ab2006-03-03 10:23:35 +00001445 }
1446
1447 sub->pending_sub = tsx;
1448
Benny Prijono69b98ab2006-03-03 10:23:35 +00001449 }
1450
Benny Prijono834aee32006-02-19 01:38:06 +00001451 return sub;
1452}
1453
1454
1455/*
1456 * Create response, adding custome headers and msg body.
1457 */
1458static pj_status_t create_response( pjsip_evsub *sub,
1459 pjsip_rx_data *rdata,
1460 int st_code,
1461 const pj_str_t *st_text,
1462 const pjsip_hdr *res_hdr,
1463 const pjsip_msg_body *body,
1464 pjsip_tx_data **p_tdata)
1465{
1466 pjsip_tx_data *tdata;
1467 pjsip_hdr *hdr;
1468 pj_status_t status;
1469
1470 status = pjsip_dlg_create_response(sub->dlg, rdata,
1471 st_code, st_text, &tdata);
1472 if (status != PJ_SUCCESS)
1473 return status;
1474
1475 *p_tdata = tdata;
1476
1477 /* Add response headers. */
1478 hdr = res_hdr->next;
1479 while (hdr != res_hdr) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001480 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001481 pjsip_hdr_clone(tdata->pool, hdr));
1482 hdr = hdr->next;
1483 }
1484
1485 /* Add msg body, if any */
1486 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001487 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1488 if (tdata->msg->body == NULL) {
1489
1490 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1491
Benny Prijono834aee32006-02-19 01:38:06 +00001492 /* Ignore */
1493 return PJ_SUCCESS;
1494 }
1495 }
1496
1497 return PJ_SUCCESS;
1498}
1499
1500/*
1501 * Get subscription state from the value of Subscription-State header.
1502 */
1503static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1504 pjsip_evsub_state *state,
1505 pj_str_t **state_str )
1506{
1507 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1508
1509 *state = PJSIP_EVSUB_STATE_TERMINATED;
1510 *state_str = NULL;
1511
1512 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1513
1514 *state = PJSIP_EVSUB_STATE_ACTIVE;
1515 *state_str = NULL;
1516
1517 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1518
1519 *state = PJSIP_EVSUB_STATE_PENDING;
1520 *state_str = NULL;
1521
1522 } else {
1523
1524 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1525 *state_str = &sub_state->sub_state;
1526
1527 }
1528}
1529
1530/*
1531 * Transaction event processing by UAC, after subscription is sent.
1532 */
1533static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1534 pjsip_event *event )
1535{
1536
Benny Prijono736d0f72006-09-13 22:45:38 +00001537 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1538 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1539 {
Benny Prijono834aee32006-02-19 01:38:06 +00001540
1541 /* Received response to outgoing request that establishes/refresh
1542 * subscription.
1543 */
1544
1545 /* First time initial request is sent. */
1546 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1547 tsx->state == PJSIP_TSX_STATE_CALLING)
1548 {
1549 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
1550 return;
1551 }
1552
1553 /* Only interested in final response */
1554 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1555 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1556 {
1557 return;
1558 }
1559
Benny Prijono1d8d6082006-04-29 12:38:25 +00001560 /* Clear pending subscription */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001561 if (tsx == sub->pending_sub) {
Benny Prijono1d8d6082006-04-29 12:38:25 +00001562 sub->pending_sub = NULL;
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001563 } else if (sub->pending_sub != NULL) {
1564 /* This SUBSCRIBE transaction has been "renewed" with another
1565 * SUBSCRIBE, so we can just ignore this. For example, user
1566 * sent SUBSCRIBE followed immediately with UN-SUBSCRIBE.
1567 */
1568 return;
1569 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001570
Benny Prijono834aee32006-02-19 01:38:06 +00001571 /* Handle authentication. */
1572 if (tsx->status_code==401 || tsx->status_code==407) {
1573 pjsip_tx_data *tdata;
1574 pj_status_t status;
1575
1576 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1577 /* Previously failed transaction has terminated */
1578 return;
1579 }
1580
1581 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1582 event->body.tsx_state.src.rdata,
1583 tsx->last_tx, &tdata);
1584 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001585 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001586
1587 if (status != PJ_SUCCESS) {
1588 /* Authentication failed! */
1589 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1590 NULL,
1591 event);
1592 return;
1593 }
1594
1595 return;
1596 }
1597
1598 if (tsx->status_code/100 == 2) {
1599
1600 /* Successfull SUBSCRIBE request!
1601 * This could be:
1602 * - response to initial SUBSCRIBE request
1603 * - response to subsequent refresh
1604 * - response to unsubscription
1605 */
1606
1607 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1608 /* Ignore; this transaction has been processed before */
1609 return;
1610 }
1611
1612 /* Update UAC refresh time, if response contains Expires header,
1613 * only when we're not unsubscribing.
1614 */
1615 if (sub->expires->ivalue != 0) {
1616 pjsip_msg *msg;
1617 pjsip_expires_hdr *expires;
1618
1619 msg = event->body.tsx_state.src.rdata->msg_info.msg;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001620 expires = (pjsip_expires_hdr*)
1621 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001622 if (expires) {
1623 sub->expires->ivalue = expires->ivalue;
1624 }
1625 }
1626
1627 /* Update time */
1628 update_expires(sub, sub->expires->ivalue);
1629
1630 /* Start UAC refresh timer, only when we're not unsubscribing */
1631 if (sub->expires->ivalue != 0) {
1632 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1633 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1634
1635 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1636 timeout));
1637 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1638
1639 } else {
1640 /* Otherwise set timer to terminate client subscription when
1641 * NOTIFY to end subscription is not received.
1642 */
1643 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1644 }
1645
1646 /* Set state, if necessary */
1647 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1648 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
1649 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
1650 }
1651
1652 } else {
1653
1654 /* Failed SUBSCRIBE request!
1655 *
1656 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1657 * other than 481, the subscription is still considered valid for
1658 * the duration of the last Expires.
1659 *
1660 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1661 * expiration, theoritically the expiration is still valid for the
1662 * next 5 seconds even when we receive non-481 failed response.
1663 *
1664 * Ah, what the heck!
1665 *
1666 * Just terminate now!
1667 *
1668 */
1669
1670 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1671 /* Ignore, has been handled before */
1672 return;
1673 }
1674
Benny Prijono69b98ab2006-03-03 10:23:35 +00001675 /* Ignore 490 (Request Updated) status.
1676 * This happens when application sends SUBSCRIBE/REFER while
1677 * another one is still in progress.
1678 */
1679 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1680 return;
1681 }
1682
Benny Prijono834aee32006-02-19 01:38:06 +00001683 /* Kill any timer. */
1684 set_timer(sub, TIMER_TYPE_NONE, 0);
1685
1686 /* Set state to TERMINATED */
1687 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1688 NULL, event);
1689
1690 }
1691
1692 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1693
1694 /* Incoming NOTIFY.
1695 * This can be the result of:
1696 * - Initial subscription response
1697 * - UAS updating the resource info.
1698 * - Unsubscription response.
1699 */
1700 int st_code = 200;
1701 pj_str_t *st_text = NULL;
1702 pjsip_hdr res_hdr;
1703 pjsip_msg_body *body = NULL;
1704
1705 pjsip_rx_data *rdata;
1706 pjsip_msg *msg;
1707 pjsip_sub_state_hdr *sub_state;
1708
1709 pjsip_evsub_state new_state;
1710 pj_str_t *new_state_str;
1711
1712 pjsip_tx_data *tdata;
1713 pj_status_t status;
1714 int next_refresh;
1715
1716 /* Only want to handle initial NOTIFY receive event. */
1717 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1718 return;
1719
1720
1721 rdata = event->body.tsx_state.src.rdata;
1722 msg = rdata->msg_info.msg;
1723
1724 pj_list_init(&res_hdr);
1725
1726 /* Get subscription state header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001727 sub_state = (pjsip_sub_state_hdr*)
1728 pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001729 if (sub_state == NULL) {
1730
1731 pjsip_warning_hdr *warn_hdr;
1732 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1733
1734 /* Bad request! Add warning header. */
1735 st_code = PJSIP_SC_BAD_REQUEST;
1736 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1737 pjsip_endpt_name(sub->endpt),
1738 &warn_text);
1739 pj_list_push_back(&res_hdr, warn_hdr);
1740 }
1741
1742 /* Call application registered callback to handle incoming NOTIFY,
1743 * if any.
1744 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001745 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001746 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1747 &res_hdr, &body);
1748
1749 /* Application MUST specify final response! */
1750 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1751
1752 /* Must be a valid status code */
1753 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1754 }
1755
1756
1757 /* If non-2xx should be returned, then send the response.
1758 * No need to update server subscription state.
1759 */
1760 if (st_code >= 300) {
1761 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1762 body, &tdata);
1763 if (status == PJ_SUCCESS) {
1764 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1765 }
1766
1767 /* Start timer to terminate subscription, just in case server
1768 * is not able to generate NOTIFY to our response.
1769 */
1770 if (status == PJ_SUCCESS) {
1771 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1772 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1773 } else {
1774 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1775 }
1776
1777 return;
1778 }
1779
1780 /* Update expiration from the value of expires param in
1781 * Subscription-State header, but ONLY when subscription state
1782 * is "active" or "pending", AND the header contains expires param.
1783 */
1784 if (sub->expires->ivalue != 0 &&
1785 sub_state->expires_param >= 0 &&
1786 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1787 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1788 {
1789 next_refresh = sub_state->expires_param;
1790
1791 } else {
1792 next_refresh = sub->expires->ivalue;
1793 }
1794
1795 /* Update time */
1796 update_expires(sub, next_refresh);
1797
1798 /* Start UAC refresh timer, only when we're not unsubscribing */
1799 if (sub->expires->ivalue != 0) {
1800 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1801 next_refresh - TIME_UAC_REFRESH : next_refresh;
1802
1803 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1804 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1805 }
1806
1807 /* Find out the state */
1808 get_hdr_state(sub_state, &new_state, &new_state_str);
1809
1810 /* Send response. */
1811 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1812 body, &tdata);
1813 if (status == PJ_SUCCESS)
1814 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1815
1816 /* Set the state */
1817 if (status == PJ_SUCCESS) {
1818 set_state(sub, new_state, new_state_str, event);
1819 } else {
1820 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1821 }
1822
1823
1824 } else {
1825
1826 /*
1827 * Unexpected method!
1828 */
1829 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1830 (int)tsx->method.name.slen, tsx->method.name.ptr));
1831 }
1832}
1833
1834
1835/*
1836 * Transaction event processing by UAS, after subscription is accepted.
1837 */
1838static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1839 pjsip_event *event)
1840{
1841
Benny Prijono736d0f72006-09-13 22:45:38 +00001842 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1843 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1844 {
Benny Prijono834aee32006-02-19 01:38:06 +00001845
1846 /*
1847 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1848 *
1849 */
1850 pjsip_rx_data *rdata;
1851 pjsip_event_hdr *event_hdr;
1852 pjsip_expires_hdr *expires;
1853 pjsip_msg *msg;
1854 pjsip_tx_data *tdata;
1855 int st_code = 200;
1856 pj_str_t *st_text = NULL;
1857 pjsip_hdr res_hdr;
1858 pjsip_msg_body *body = NULL;
1859 pjsip_evsub_state old_state;
1860 pj_str_t old_state_str;
1861 pj_status_t status;
1862
1863
1864 /* Only wants to handle the first event when the request is
1865 * received.
1866 */
1867 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1868 return;
1869
1870 rdata = event->body.tsx_state.src.rdata;
1871 msg = rdata->msg_info.msg;
1872
1873 /* Set expiration time based on client request (in Expires header),
1874 * or package default expiration time.
1875 */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001876 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001877 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1878 &STR_EVENT, NULL);
Benny Prijono9d4469d2007-05-02 05:14:29 +00001879 expires = (pjsip_expires_hdr*)
1880 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001881 if (event_hdr && expires) {
1882 struct evpkg *evpkg;
1883
1884 evpkg = find_pkg(&event_hdr->event_type);
1885 if (evpkg) {
1886 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1887 sub->expires->ivalue = expires->ivalue;
1888 else
1889 sub->expires->ivalue = evpkg->pkg_expires;
1890 }
1891 }
1892
1893 /* Update time (before calling on_rx_refresh, since application
1894 * will send NOTIFY.
1895 */
1896 update_expires(sub, sub->expires->ivalue);
1897
1898
1899 /* Save old state.
1900 * If application respond with non-2xx, revert to old state.
1901 */
1902 old_state = sub->state;
1903 old_state_str = sub->state_str;
1904
1905 if (sub->expires->ivalue == 0) {
1906 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1907 sub->state_str = evsub_state_names[sub->state];
1908 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1909 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1910 sub->state_str = evsub_state_names[sub->state];
1911 }
1912
1913 /* Call application's on_rx_refresh, just in case it wants to send
1914 * response other than 200 (OK)
1915 */
1916 pj_list_init(&res_hdr);
1917
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001918 if (sub->user.on_rx_refresh && sub->call_cb) {
1919 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1920 &res_hdr, &body);
1921 }
Benny Prijono834aee32006-02-19 01:38:06 +00001922
1923 /* Application MUST specify final response! */
1924 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1925
1926 /* Must be a valid status code */
1927 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1928
1929
1930 /* Create and send response */
1931 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1932 body, &tdata);
1933 if (status == PJ_SUCCESS) {
1934 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001935 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001936 pjsip_hdr_shallow_clone(tdata->pool,
1937 sub->expires));
1938
1939 /* Send */
1940 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1941 }
1942
1943 /* Update state or revert state */
1944 if (st_code/100==2) {
1945
1946 if (sub->expires->ivalue == 0) {
1947 set_state(sub, sub->state, NULL, event);
1948 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1949 set_state(sub, sub->state, NULL, event);
1950 }
1951
1952 /* Set UAS timeout timer, when state is not terminated. */
1953 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1954 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1955 sub->expires->ivalue));
1956 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1957 sub->expires->ivalue);
1958 }
1959
1960 } else {
1961 sub->state = old_state;
1962 sub->state_str = old_state_str;
1963 }
1964
1965
1966 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1967
1968 /* Handle authentication */
1969 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1970 (tsx->status_code==401 || tsx->status_code==407))
1971 {
1972 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1973 pjsip_tx_data *tdata;
1974 pj_status_t status;
1975
1976 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
1977 tsx->last_tx, &tdata);
1978 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001979 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00001980
1981 if (status != PJ_SUCCESS) {
1982 /* Can't authenticate. Terminate session (?) */
1983 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono441ce002006-03-07 15:15:01 +00001984 return;
Benny Prijono834aee32006-02-19 01:38:06 +00001985 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001986
1987 }
1988 /*
1989 * Terminate event usage if we receive 481, 408, and 7 class
1990 * responses.
1991 */
1992 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
1993 (tsx->status_code==481 || tsx->status_code==408 ||
1994 tsx->status_code/100 == 7))
1995 {
1996 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
Benny Prijono441ce002006-03-07 15:15:01 +00001997 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001998 }
Benny Prijono834aee32006-02-19 01:38:06 +00001999
2000 } else {
2001
2002 /*
2003 * Unexpected method!
2004 */
2005 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
2006 (int)tsx->method.name.slen, tsx->method.name.ptr));
2007
2008 }
2009}
2010
2011
2012/*
2013 * Notification when transaction state has changed!
2014 */
2015static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
2016{
2017 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
2018
2019 if (sub == NULL) {
2020 sub = on_new_transaction(tsx, event);
2021 if (sub == NULL)
2022 return;
2023 }
2024
2025
2026 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002027 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00002028 (*sub->user.on_tsx_state)(sub, tsx, event);
2029
2030
2031 /* Process the event: */
2032
2033 if (sub->role == PJSIP_ROLE_UAC) {
2034 on_tsx_state_uac(sub, tsx, event);
2035 } else {
2036 on_tsx_state_uas(sub, tsx, event);
2037 }
2038
2039
2040 /* Check transaction TERMINATE event */
2041 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
2042
2043 --sub->pending_tsx;
2044
2045 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
2046 sub->pending_tsx == 0)
2047 {
2048 evsub_destroy(sub);
2049 }
2050
2051 }
2052}
2053
2054