blob: 2d629c04b5c846fc2f7a79474bd1008da364629b [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. */
Benny Prijono75130572008-07-17 13:53:41 +0000224 pj_str_t term_reason; /**< Termination reason. */
Benny Prijono834aee32006-02-19 01:38:06 +0000225 pjsip_method method; /**< Method that established subscr.*/
226 pjsip_event_hdr *event; /**< Event description. */
227 pjsip_expires_hdr *expires; /**< Expires header */
228 pjsip_accept_hdr *accept; /**< Local Accept header. */
229
230 pj_time_val refresh_time; /**< Time to refresh. */
231 pj_timer_entry timer; /**< Internal timer. */
232 int pending_tsx; /**< Number of pending transactions.*/
Benny Prijono69b98ab2006-03-03 10:23:35 +0000233 pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */
Benny Prijono834aee32006-02-19 01:38:06 +0000234
235 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
236};
237
238
239/*
240 * This is the structure that will be "attached" to dialog.
241 * The purpose is to allow multiple subscriptions inside a dialog.
242 */
243struct dlgsub
244{
245 PJ_DECL_LIST_MEMBER(struct dlgsub);
246 pjsip_evsub *sub;
247};
248
249
250/* Static vars. */
251static const pj_str_t STR_EVENT = { "Event", 5 };
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000252static const pj_str_t STR_EVENT_S = { "Event", 5 };
Benny Prijono834aee32006-02-19 01:38:06 +0000253static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
254static const pj_str_t STR_TERMINATED = { "terminated", 10 };
255static const pj_str_t STR_ACTIVE = { "active", 6 };
256static const pj_str_t STR_PENDING = { "pending", 7 };
257static const pj_str_t STR_TIMEOUT = { "timeout", 7};
258
Benny Prijono26ff9062006-02-21 23:47:00 +0000259
Benny Prijono834aee32006-02-19 01:38:06 +0000260/*
261 * On unload module.
262 */
263static pj_status_t mod_evsub_unload(void)
264{
265 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
266 mod_evsub.pool = NULL;
267
268 return PJ_SUCCESS;
269}
270
Benny Prijono26ff9062006-02-21 23:47:00 +0000271/* Proto for pjsipsimple_strerror().
272 * Defined in errno.c
273 */
274PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode,
275 char *buf, pj_size_t bufsize );
276
Benny Prijono834aee32006-02-19 01:38:06 +0000277/*
278 * Init and register module.
279 */
280PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
281{
282 pj_status_t status;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000283 pj_str_t method_tags[] = {
284 { "SUBSCRIBE", 9},
285 { "NOTIFY", 6}
286 };
Benny Prijono834aee32006-02-19 01:38:06 +0000287
Benny Prijono26ff9062006-02-21 23:47:00 +0000288 pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
289 &pjsipsimple_strerror);
290
Benny Prijono834aee32006-02-19 01:38:06 +0000291 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
292 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
293
294 /* Keep endpoint for future reference: */
295 mod_evsub.endpt = endpt;
296
297 /* Init event package list: */
298 pj_list_init(&mod_evsub.pkg_list);
299
300 /* Create pool: */
Benny Prijono10d8dbd2008-07-13 13:12:36 +0000301 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 512, 512);
Benny Prijono834aee32006-02-19 01:38:06 +0000302 if (!mod_evsub.pool)
303 return PJ_ENOMEM;
304
305 /* Register module: */
306 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
307 if (status != PJ_SUCCESS)
308 goto on_error;
309
310 /* Create Allow-Events header: */
311 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
312
313 /* Register SIP-event specific headers parser: */
314 pjsip_evsub_init_parser();
315
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000316 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
317 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
318 2, method_tags);
319
320 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000321 return PJ_SUCCESS;
322
323on_error:
324 if (mod_evsub.pool) {
325 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
326 mod_evsub.pool = NULL;
327 }
328 mod_evsub.endpt = NULL;
329 return status;
330}
331
332
333/*
334 * Get the instance of the module.
335 */
336PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
337{
338 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
339
340 return &mod_evsub.mod;
341}
342
343
344/*
345 * Get the event subscription instance in the transaction.
346 */
347PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
348{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000349 return (pjsip_evsub*) tsx->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000350}
351
352
353/*
354 * Set event subscription's module data.
355 */
356PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
357 void *data )
358{
359 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
360 sub->mod_data[mod_id] = data;
361}
362
363
364/*
365 * Get event subscription's module data.
366 */
367PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
368{
369 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
370 return sub->mod_data[mod_id];
371}
372
373
374/*
375 * Find registered event package with matching name.
376 */
377static struct evpkg* find_pkg(const pj_str_t *event_name)
378{
379 struct evpkg *pkg;
380
381 pkg = mod_evsub.pkg_list.next;
382 while (pkg != &mod_evsub.pkg_list) {
383
384 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
385 return pkg;
386 }
387
388 pkg = pkg->next;
389 }
390
391 return NULL;
392}
393
394/*
395 * Register an event package
396 */
397PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
398 const pj_str_t *event_name,
399 unsigned expires,
400 unsigned accept_cnt,
401 const pj_str_t accept[])
402{
403 struct evpkg *pkg;
404 unsigned i;
405
406 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
407 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
408 PJ_ETOOMANY);
409
Benny Prijonoc2456cc2007-10-25 03:19:58 +0000410 /* Make sure evsub module has been initialized */
411 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, PJ_EINVALIDOP);
412
Benny Prijono834aee32006-02-19 01:38:06 +0000413 /* Make sure no module with the specified name already registered: */
414
415 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
416
417
418 /* Create new event package: */
419
Benny Prijono9d4469d2007-05-02 05:14:29 +0000420 pkg = PJ_POOL_ALLOC_T(mod_evsub.pool, struct evpkg);
Benny Prijono834aee32006-02-19 01:38:06 +0000421 pkg->pkg_mod = pkg_mod;
422 pkg->pkg_expires = expires;
423 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
424
425 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
426 pkg->pkg_accept->count = accept_cnt;
427 for (i=0; i<accept_cnt; ++i) {
428 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
429 }
430
431 /* Add to package list: */
432
433 pj_list_push_back(&mod_evsub.pkg_list, pkg);
434
435 /* Add to Allow-Events header: */
436
437 if (mod_evsub.allow_events_hdr->count !=
438 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
439 {
440 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
441 pkg->pkg_name;
442 ++mod_evsub.allow_events_hdr->count;
443 }
444
Benny Prijono56315612006-07-18 14:39:40 +0000445 /* Add to endpoint's Accept header */
446 pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod,
447 PJSIP_H_ACCEPT, NULL,
448 pkg->pkg_accept->count,
449 pkg->pkg_accept->values);
450
Benny Prijono834aee32006-02-19 01:38:06 +0000451
452 /* Done */
453
454 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
455 (int)event_name->slen, event_name->ptr,
456 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
457
458 return PJ_SUCCESS;
459}
460
461
Benny Prijono56315612006-07-18 14:39:40 +0000462/*
463 * Retrieve Allow-Events header
464 */
465PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m)
466{
467 struct mod_evsub *mod;
468
469 if (m == NULL)
470 m = pjsip_evsub_instance();
471
472 mod = (struct mod_evsub*)m;
473
474 return (pjsip_hdr*) mod->allow_events_hdr;
475}
476
Benny Prijono834aee32006-02-19 01:38:06 +0000477
478/*
479 * Update expiration time.
480 */
481static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
482{
483 pj_gettimeofday(&sub->refresh_time);
484 sub->refresh_time.sec += interval;
485}
486
487
488/*
489 * Schedule timer.
490 */
491static void set_timer( pjsip_evsub *sub, int timer_id,
492 pj_int32_t seconds)
493{
494 if (sub->timer.id != TIMER_TYPE_NONE) {
495 PJ_LOG(5,(sub->obj_name, "%s %s timer",
496 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
497 timer_names[sub->timer.id]));
498 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
499 sub->timer.id = TIMER_TYPE_NONE;
500 }
501
502 if (timer_id != TIMER_TYPE_NONE) {
503 pj_time_val timeout;
504
505 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000506 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
507 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000508
509 timeout.sec = seconds;
510 timeout.msec = 0;
511 sub->timer.id = timer_id;
512
513 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
514
515 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
516 timer_names[sub->timer.id], timeout.sec));
517 }
518}
519
520
521/*
522 * Destroy session.
523 */
524static void evsub_destroy( pjsip_evsub *sub )
525{
526 struct dlgsub *dlgsub_head, *dlgsub;
527
528 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
529
530 /* Kill timer */
531 set_timer(sub, TIMER_TYPE_NONE, 0);
532
Benny Prijono9d4469d2007-05-02 05:14:29 +0000533 /* Remove this session from dialog's list of subscription */
534 dlgsub_head = (struct dlgsub *) sub->dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000535 dlgsub = dlgsub_head->next;
536 while (dlgsub != dlgsub_head) {
537
538 if (dlgsub->sub == sub) {
539 pj_list_erase(dlgsub);
540 break;
541 }
542
543 dlgsub = dlgsub->next;
544 }
545
546 /* Decrement dialog's session */
547 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
548}
549
550/*
551 * Set subscription session state.
552 */
553static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
Benny Prijono75130572008-07-17 13:53:41 +0000554 const pj_str_t *state_str, pjsip_event *event,
555 const pj_str_t *reason)
Benny Prijono834aee32006-02-19 01:38:06 +0000556{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000557 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000558 pj_str_t old_state_str = sub->state_str;
559
560 sub->state = state;
561
562 if (state_str && state_str->slen)
563 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
564 else
565 sub->state_str = evsub_state_names[state];
566
Benny Prijono75130572008-07-17 13:53:41 +0000567 if (reason && sub->term_reason.slen==0)
568 pj_strdup(sub->pool, &sub->term_reason, reason);
569
Benny Prijono834aee32006-02-19 01:38:06 +0000570 PJ_LOG(4,(sub->obj_name,
571 "Subscription state changed %.*s --> %.*s",
572 (int)old_state_str.slen,
573 old_state_str.ptr,
574 (int)sub->state_str.slen,
575 sub->state_str.ptr));
576
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000577 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000578 (*sub->user.on_evsub_state)(sub, event);
579
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000580 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
581 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
582 {
Benny Prijono834aee32006-02-19 01:38:06 +0000583 if (sub->pending_tsx == 0) {
584 evsub_destroy(sub);
585 }
586 }
587}
588
589
590/*
591 * Timer callback.
592 */
593static void on_timer( pj_timer_heap_t *timer_heap,
594 struct pj_timer_entry *entry)
595{
596 pjsip_evsub *sub;
597 int timer_id;
598
599 PJ_UNUSED_ARG(timer_heap);
600
Benny Prijono9d4469d2007-05-02 05:14:29 +0000601 sub = (pjsip_evsub*) entry->user_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000602
603 pjsip_dlg_inc_lock(sub->dlg);
604
605 timer_id = entry->id;
606 entry->id = TIMER_TYPE_NONE;
607
608 switch (timer_id) {
609
610 case TIMER_TYPE_UAC_REFRESH:
611 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000612 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000613 (*sub->user.on_client_refresh)(sub);
614 } else {
615 pjsip_tx_data *tdata;
616 pj_status_t status;
617
618 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000619 status = pjsip_evsub_initiate(sub, NULL,
Benny Prijono834aee32006-02-19 01:38:06 +0000620 sub->expires->ivalue,
621 &tdata);
622 if (status == PJ_SUCCESS)
623 pjsip_evsub_send_request(sub, tdata);
624 }
625 break;
626
627 case TIMER_TYPE_UAS_TIMEOUT:
628 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000629 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000630 (*sub->user.on_server_timeout)(sub);
631 } else {
632 pjsip_tx_data *tdata;
633 pj_status_t status;
634
635 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
636 "Sending NOTIFY to terminate."));
637 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
638 NULL, &STR_TIMEOUT, &tdata);
639 if (status == PJ_SUCCESS)
640 pjsip_evsub_send_request(sub, tdata);
641 }
642 break;
643
644 case TIMER_TYPE_UAC_TERMINATE:
645 {
Benny Prijono75130572008-07-17 13:53:41 +0000646 pj_str_t timeout = {"timeout", 7};
647
Benny Prijono834aee32006-02-19 01:38:06 +0000648 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
649 "Terminating.."));
Benny Prijono75130572008-07-17 13:53:41 +0000650 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
651 &timeout);
Benny Prijono834aee32006-02-19 01:38:06 +0000652 }
653 break;
654
655 case TIMER_TYPE_UAC_WAIT_NOTIFY:
656 {
657 pjsip_tx_data *tdata;
658 pj_status_t status;
659
660 PJ_LOG(5,(sub->obj_name,
661 "Timeout waiting for subsequent NOTIFY (we did "
662 "send non-2xx response for previous NOTIFY). "
663 "Unsubscribing.."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000664 status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000665 if (status == PJ_SUCCESS)
666 pjsip_evsub_send_request(sub, tdata);
667 }
668 break;
669
670 default:
671 pj_assert(!"Invalid timer id");
672 }
673
674 pjsip_dlg_dec_lock(sub->dlg);
675}
676
677
678/*
679 * Create subscription session, used for both client and notifier.
680 */
681static pj_status_t evsub_create( pjsip_dialog *dlg,
682 pjsip_role_e role,
683 const pjsip_evsub_user *user_cb,
684 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000685 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000686 pjsip_evsub **p_evsub )
687{
688 pjsip_evsub *sub;
689 struct evpkg *pkg;
690 struct dlgsub *dlgsub_head, *dlgsub;
691 pj_status_t status;
692
693 /* Make sure there's package register for the event name: */
694
695 pkg = find_pkg(event);
696 if (pkg == NULL)
697 return PJSIP_SIMPLE_ENOPKG;
698
699
Benny Prijono8eae8382006-08-10 21:44:26 +0000700 /* Must lock dialog before using pool etc. */
701 pjsip_dlg_inc_lock(dlg);
702
Benny Prijono834aee32006-02-19 01:38:06 +0000703 /* Init attributes: */
704
Benny Prijono9d4469d2007-05-02 05:14:29 +0000705 sub = PJ_POOL_ZALLOC_T(dlg->pool, struct pjsip_evsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000706 sub->pool = dlg->pool;
707 sub->endpt = dlg->endpt;
708 sub->dlg = dlg;
709 sub->pkg = pkg;
710 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000711 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000712 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000713 sub->state = PJSIP_EVSUB_STATE_NULL;
714 sub->state_str = evsub_state_names[sub->state];
715 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
Benny Prijono9d4469d2007-05-02 05:14:29 +0000716 sub->accept = (pjsip_accept_hdr*)
717 pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
Benny Prijono834aee32006-02-19 01:38:06 +0000718
719 sub->timer.user_data = sub;
720 sub->timer.cb = &on_timer;
721
722 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000723 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
724 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000725
726
727 /* Copy callback, if any: */
728 if (user_cb)
729 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
730
731
732 /* Create Event header: */
733 sub->event = pjsip_event_hdr_create(sub->pool);
734 pj_strdup(sub->pool, &sub->event->event_type, event);
735
736
737 /* Create subcription list: */
738
Benny Prijono9d4469d2007-05-02 05:14:29 +0000739 dlgsub_head = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
740 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000741 dlgsub->sub = sub;
742
743 pj_list_init(dlgsub_head);
744 pj_list_push_back(dlgsub_head, dlgsub);
745
746
747 /* Register as dialog usage: */
748
749 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
Benny Prijono8eae8382006-08-10 21:44:26 +0000750 if (status != PJ_SUCCESS) {
751 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000752 return status;
Benny Prijono8eae8382006-08-10 21:44:26 +0000753 }
Benny Prijono834aee32006-02-19 01:38:06 +0000754
755
756 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
757 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
758 dlg->obj_name));
759
760 *p_evsub = sub;
Benny Prijono8eae8382006-08-10 21:44:26 +0000761 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000762
763 return PJ_SUCCESS;
764}
765
766
767
768/*
769 * Create client subscription session.
770 */
771PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
772 const pjsip_evsub_user *user_cb,
773 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000774 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000775 pjsip_evsub **p_evsub)
776{
777 pjsip_evsub *sub;
778 pj_status_t status;
779
780 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
781
782 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000783 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000784 if (status != PJ_SUCCESS)
785 goto on_return;
786
Benny Prijono26ff9062006-02-21 23:47:00 +0000787 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
788 * is not specified.
789 */
790 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
791 pj_create_unique_string(sub->pool, &sub->event->id_param);
792 }
Benny Prijono834aee32006-02-19 01:38:06 +0000793
794 /* Increment dlg session. */
795 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
796
797 /* Done */
798 *p_evsub = sub;
799
800on_return:
801 pjsip_dlg_dec_lock(dlg);
802 return status;
803}
804
805
806/*
807 * Create server subscription session from incoming request.
808 */
809PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
810 const pjsip_evsub_user *user_cb,
811 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000812 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000813 pjsip_evsub **p_evsub)
814{
815 pjsip_evsub *sub;
816 pjsip_transaction *tsx;
817 pjsip_accept_hdr *accept_hdr;
818 pjsip_event_hdr *event_hdr;
819 pjsip_expires_hdr *expires_hdr;
820 pj_status_t status;
821
822 /* Check arguments: */
823 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
824
825 /* MUST be request message: */
826 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
827 PJSIP_ENOTREQUESTMSG);
828
829 /* Transaction MUST have been created (in the dialog) */
830 tsx = pjsip_rdata_get_tsx(rdata);
831 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
832
833 /* No subscription must have been attached to transaction */
834 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
835 PJSIP_ETYPEEXISTS);
836
837 /* Package MUST implement on_rx_refresh */
838 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
839
Benny Prijono26ff9062006-02-21 23:47:00 +0000840 /* Request MUST have "Event" header. We need the Event header to get
841 * the package name (don't want to add more arguments in the function).
842 */
Benny Prijono834aee32006-02-19 01:38:06 +0000843 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000844 pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &STR_EVENT,
845 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000846 if (event_hdr == NULL) {
847 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
848 }
849
850 /* Start locking the mutex: */
851
852 pjsip_dlg_inc_lock(dlg);
853
854 /* Create the session: */
855
856 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000857 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000858 if (status != PJ_SUCCESS)
859 goto on_return;
860
861 /* Just duplicate Event header from the request */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000862 sub->event = (pjsip_event_hdr*) pjsip_hdr_clone(sub->pool, event_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000863
864 /* Set the method: */
865 pjsip_method_copy(sub->pool, &sub->method,
866 &rdata->msg_info.msg->line.req.method);
867
868 /* Update expiration time according to client request: */
869
870 expires_hdr = (pjsip_expires_hdr*)
871 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
872 if (expires_hdr) {
873 sub->expires->ivalue = expires_hdr->ivalue;
874 }
875
876 /* Update time. */
877 update_expires(sub, sub->expires->ivalue);
878
879 /* Update Accept header: */
880
881 accept_hdr = (pjsip_accept_hdr*)
882 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
883 if (accept_hdr)
Benny Prijono9d4469d2007-05-02 05:14:29 +0000884 sub->accept = (pjsip_accept_hdr*)pjsip_hdr_clone(sub->pool,accept_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000885
886 /* We can start the session: */
887
888 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
889 sub->pending_tsx++;
890 tsx->mod_data[mod_evsub.mod.id] = sub;
891
892
893 /* Done. */
894 *p_evsub = sub;
895
896
897on_return:
898 pjsip_dlg_dec_lock(dlg);
899 return status;
900}
901
902
903/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000904 * Forcefully destroy subscription.
905 */
906PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
907 pj_bool_t notify )
908{
909 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
910
911 pjsip_dlg_inc_lock(sub->dlg);
912
Benny Prijonod524e822006-09-22 12:48:18 +0000913 /* I think it's pretty safe to disable this check.
914
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000915 if (sub->pending_tsx) {
916 pj_assert(!"Unable to terminate when there's pending tsx");
917 pjsip_dlg_dec_lock(sub->dlg);
918 return PJ_EINVALIDOP;
919 }
Benny Prijonod524e822006-09-22 12:48:18 +0000920 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000921
922 sub->call_cb = notify;
Benny Prijono75130572008-07-17 13:53:41 +0000923 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, NULL);
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000924
925 pjsip_dlg_dec_lock(sub->dlg);
926 return PJ_SUCCESS;
927}
928
929/*
Benny Prijono834aee32006-02-19 01:38:06 +0000930 * Get subscription state.
931 */
932PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
933{
934 return sub->state;
935}
936
937/*
938 * Get state name.
939 */
940PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
941{
942 return sub->state_str.ptr;
943}
944
Benny Prijono75130572008-07-17 13:53:41 +0000945/*
946 * Get termination reason.
947 */
948PJ_DEF(const pj_str_t*) pjsip_evsub_get_termination_reason(pjsip_evsub *sub)
949{
950 return &sub->term_reason;
951}
Benny Prijono834aee32006-02-19 01:38:06 +0000952
953/*
954 * Initiate client subscription
955 */
956PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
957 const pjsip_method *method,
958 pj_int32_t expires,
959 pjsip_tx_data **p_tdata)
960{
961 pjsip_tx_data *tdata;
962 pj_status_t status;
963
964 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
965
966 /* Use SUBSCRIBE if method is not specified */
967 if (method == NULL)
968 method = &pjsip_subscribe_method;
969
970 pjsip_dlg_inc_lock(sub->dlg);
971
972 /* Update method: */
973 if (sub->state == PJSIP_EVSUB_STATE_NULL)
974 pjsip_method_copy(sub->pool, &sub->method, method);
975
976 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
977 if (status != PJ_SUCCESS)
978 goto on_return;
979
980
981 /* Add Event 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, sub->event));
984
985 /* Update and add expires header: */
986 if (expires >= 0)
987 sub->expires->ivalue = expires;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000988 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000989 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
990
991 /* Add Accept header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000992 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000993 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
994
995
996 /* Add Allow-Events header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000997 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000998 pjsip_hdr_shallow_clone(tdata->pool,
999 mod_evsub.allow_events_hdr));
1000
1001
1002 *p_tdata = tdata;
1003
1004
1005on_return:
1006
1007 pjsip_dlg_dec_lock(sub->dlg);
1008 return status;
1009}
1010
1011
1012/*
1013 * Accept incoming subscription request.
1014 */
1015PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
1016 pjsip_rx_data *rdata,
1017 int st_code,
1018 const pjsip_hdr *hdr_list )
1019{
1020 pjsip_tx_data *tdata;
1021 pjsip_transaction *tsx;
1022 pj_status_t status;
1023
1024 /* Check arguments */
1025 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
1026
1027 /* Can only be for server subscription: */
1028 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
1029
1030 /* Only expect 2xx status code (for now) */
1031 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
1032
1033 /* Subscription MUST have been attached to the transaction.
1034 * Initial subscription request will be attached on evsub_create_uas(),
1035 * while subsequent requests will be attached in tsx_state()
1036 */
1037 tsx = pjsip_rdata_get_tsx(rdata);
1038 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
1039 PJ_EINVALIDOP);
1040
1041 /* Lock dialog */
1042 pjsip_dlg_inc_lock(sub->dlg);
1043
1044 /* Create response: */
1045 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1046 &tdata);
1047 if (status != PJ_SUCCESS)
1048 goto on_return;
1049
1050
1051 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001052 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001053 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1054
Benny Prijonob0808372006-03-02 21:18:58 +00001055 /* Add additional header, if any. */
1056 if (hdr_list) {
1057 const pjsip_hdr *hdr = hdr_list->next;
1058 while (hdr != hdr_list) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001059 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonob0808372006-03-02 21:18:58 +00001060 pjsip_hdr_clone(tdata->pool, hdr));
1061 hdr = hdr->next;
1062 }
1063 }
Benny Prijono834aee32006-02-19 01:38:06 +00001064
1065 /* Send the response: */
1066 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1067 if (status != PJ_SUCCESS)
1068 goto on_return;
1069
1070
1071on_return:
1072
1073 pjsip_dlg_dec_lock(sub->dlg);
1074 return status;
1075}
1076
1077
1078/*
1079 * Create Subscription-State header based on current server subscription
1080 * state.
1081 */
1082static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1083 pjsip_evsub *sub,
1084 pjsip_evsub_state state,
1085 const pj_str_t *state_str,
1086 const pj_str_t *reason )
1087{
1088 pjsip_sub_state_hdr *sub_state;
1089 pj_time_val now, delay;
1090
1091 /* Get the remaining time before refresh is required */
1092 pj_gettimeofday(&now);
1093 delay = sub->refresh_time;
1094 PJ_TIME_VAL_SUB(delay, now);
1095
1096 /* Create the Subscription-State header */
1097 sub_state = pjsip_sub_state_hdr_create(pool);
1098
1099 /* Fill up the header */
1100 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001101 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001102 case PJSIP_EVSUB_STATE_SENT:
Benny Prijono834aee32006-02-19 01:38:06 +00001103 pj_assert(!"Invalid state!");
1104 /* Treat as pending */
1105
Benny Prijono75130572008-07-17 13:53:41 +00001106 case PJSIP_EVSUB_STATE_ACCEPTED:
Benny Prijono834aee32006-02-19 01:38:06 +00001107 case PJSIP_EVSUB_STATE_PENDING:
1108 sub_state->sub_state = STR_PENDING;
1109 sub_state->expires_param = delay.sec;
1110 break;
1111
1112 case PJSIP_EVSUB_STATE_ACTIVE:
1113 sub_state->sub_state = STR_ACTIVE;
1114 sub_state->expires_param = delay.sec;
1115 break;
1116
1117 case PJSIP_EVSUB_STATE_TERMINATED:
1118 sub_state->sub_state = STR_TERMINATED;
1119 if (reason != NULL)
1120 pj_strdup(pool, &sub_state->reason_param, reason);
1121 break;
1122
1123 case PJSIP_EVSUB_STATE_UNKNOWN:
1124 pj_assert(state_str != NULL);
1125 pj_strdup(pool, &sub_state->sub_state, state_str);
1126 break;
1127 }
1128
1129 return sub_state;
1130}
1131
1132/*
1133 * Create and send NOTIFY request.
1134 */
1135PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1136 pjsip_evsub_state state,
1137 const pj_str_t *state_str,
1138 const pj_str_t *reason,
1139 pjsip_tx_data **p_tdata)
1140{
1141 pjsip_tx_data *tdata;
1142 pjsip_sub_state_hdr *sub_state;
1143 pj_status_t status;
1144
1145 /* Check arguments. */
1146 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1147
1148 /* Lock dialog. */
1149 pjsip_dlg_inc_lock(sub->dlg);
1150
1151 /* Create NOTIFY request */
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001152 status = pjsip_dlg_create_request( sub->dlg, pjsip_get_notify_method(),
1153 -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001154 if (status != PJ_SUCCESS)
1155 goto on_return;
1156
1157 /* Add Event header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001158 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001159 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1160
1161 /* Add Subscription-State header */
1162 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1163 reason);
1164 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1165
1166 /* Add Allow-Events header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001167 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001168 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1169
1170 /* Add Authentication headers. */
1171 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1172
Benny Prijono75130572008-07-17 13:53:41 +00001173 /* Update reason */
1174 if (reason)
1175 pj_strdup(sub->dlg->pool, &sub->term_reason, reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001176
1177 /* Save destination state. */
1178 sub->dst_state = state;
1179 if (state_str)
1180 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1181 else
1182 sub->dst_state_str.slen = 0;
1183
1184
1185 *p_tdata = tdata;
1186
1187on_return:
1188 /* Unlock dialog */
1189 pjsip_dlg_dec_lock(sub->dlg);
1190 return status;
1191}
1192
1193
1194/*
1195 * Create NOTIFY to reflect current status.
1196 */
1197PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1198 pjsip_tx_data **p_tdata )
1199{
1200 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1201 NULL, p_tdata );
1202}
1203
1204
1205/*
1206 * Send request.
1207 */
1208PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1209 pjsip_tx_data *tdata)
1210{
1211 pj_status_t status;
1212
1213 /* Must be request message. */
1214 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1215 PJSIP_ENOTREQUESTMSG);
1216
1217 /* Lock */
1218 pjsip_dlg_inc_lock(sub->dlg);
1219
1220 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001221 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001222 if (status != PJ_SUCCESS)
1223 goto on_return;
1224
1225
1226 /* Special case for NOTIFY:
1227 * The new state was set in pjsip_evsub_notify(), but we apply the
1228 * new state now, when the request was actually sent.
1229 */
1230 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1231 &pjsip_notify_method)==0)
1232 {
1233 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1234 {goto on_return;});
1235
1236 set_state(sub, sub->dst_state,
1237 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
Benny Prijono75130572008-07-17 13:53:41 +00001238 NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001239
1240 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1241 sub->dst_state_str.slen = 0;
1242
1243 }
1244
1245
1246on_return:
1247 pjsip_dlg_dec_lock(sub->dlg);
1248 return status;
1249}
1250
1251
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001252/* Callback to be called to terminate transaction. */
1253static void terminate_timer_cb(pj_timer_heap_t *timer_heap,
1254 struct pj_timer_entry *entry)
1255{
1256 pj_str_t *key;
1257 pjsip_transaction *tsx;
1258
1259 PJ_UNUSED_ARG(timer_heap);
1260
1261 key = (pj_str_t*)entry->user_data;
1262 tsx = pjsip_tsx_layer_find_tsx(key, PJ_FALSE);
1263 /* Chance of race condition here */
1264 if (tsx) {
1265 pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_UPDATED);
1266 }
1267}
1268
Benny Prijono834aee32006-02-19 01:38:06 +00001269
1270/*
1271 * Attach subscription session to newly created transaction, if appropriate.
1272 */
1273static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1274 pjsip_event *event)
1275{
1276 /*
1277 * Newly created transaction will not have subscription session
1278 * attached to it. Find the subscription session from the dialog,
1279 * by matching the Event header.
1280 */
1281 pjsip_dialog *dlg;
1282 pjsip_event_hdr *event_hdr;
1283 pjsip_msg *msg;
1284 struct dlgsub *dlgsub_head, *dlgsub;
1285 pjsip_evsub *sub;
1286
1287 dlg = pjsip_tsx_get_dlg(tsx);
1288 if (!dlg) {
1289 pj_assert(!"Transaction should have a dialog instance!");
1290 return NULL;
1291 }
1292
Benny Prijono26ff9062006-02-21 23:47:00 +00001293
Benny Prijono834aee32006-02-19 01:38:06 +00001294 switch (event->body.tsx_state.type) {
1295 case PJSIP_EVENT_RX_MSG:
1296 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1297 break;
1298 case PJSIP_EVENT_TX_MSG:
1299 msg = event->body.tsx_state.src.tdata->msg;
1300 break;
1301 default:
1302 if (tsx->role == PJSIP_ROLE_UAC)
1303 msg = tsx->last_tx->msg;
1304 else
1305 msg = NULL;
1306 break;
1307 }
1308
1309 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001310 //Note:
1311 // this transaction can be other transaction in the dialog.
1312 // The assertion below probably only valid for dialog that
1313 // only has one event subscription usage.
1314 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001315 return NULL;
1316 }
1317
Benny Prijono9d4469d2007-05-02 05:14:29 +00001318 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001319 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1320 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001321 if (!event_hdr) {
1322 /* Not subscription related message */
1323 return NULL;
1324 }
1325
1326 /* Find the subscription in the dialog, based on the content
1327 * of Event header:
1328 */
1329
Benny Prijono9d4469d2007-05-02 05:14:29 +00001330 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +00001331 if (dlgsub_head == NULL) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001332 dlgsub_head = PJ_POOL_ALLOC_T(dlg->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +00001333 pj_list_init(dlgsub_head);
1334 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1335 }
1336 dlgsub = dlgsub_head->next;
1337
1338 while (dlgsub != dlgsub_head) {
1339
Benny Prijono26ff9062006-02-21 23:47:00 +00001340 if (pj_stricmp(&dlgsub->sub->event->event_type,
1341 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001342 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001343 /* Event type matched.
1344 * Check if event ID matched too.
1345 */
1346 if (pj_strcmp(&dlgsub->sub->event->id_param,
1347 &event_hdr->id_param)==0)
1348 {
1349
1350 break;
1351
1352 }
1353 /*
1354 * Otherwise if it is an UAC subscription, AND
1355 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1356 * the session's event id is NULL, AND
1357 * the incoming request is NOTIFY with event ID, then
1358 * we consider it as a match, and update the
1359 * session's event id.
1360 */
1361 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1362 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1363 dlgsub->sub->event->id_param.slen==0 &&
1364 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1365 {
1366 /* Update session's event id. */
1367 pj_strdup(dlgsub->sub->pool,
1368 &dlgsub->sub->event->id_param,
1369 &event_hdr->id_param);
1370
1371 break;
1372 }
Benny Prijono834aee32006-02-19 01:38:06 +00001373 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001374
1375
1376
Benny Prijono834aee32006-02-19 01:38:06 +00001377 dlgsub = dlgsub->next;
1378 }
1379
1380 if (dlgsub == dlgsub_head) {
1381 /* This could be incoming request to create new subscription */
1382 PJ_LOG(4,(THIS_FILE,
1383 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001384 (int)tsx->method.name.slen,
1385 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001386 (int)event_hdr->event_type.slen,
1387 event_hdr->event_type.ptr,
1388 (int)event_hdr->id_param.slen,
1389 event_hdr->id_param.ptr));
1390
1391 /* If this is an incoming NOTIFY, reject with 481 */
1392 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1393 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1394 {
1395 pj_str_t reason = pj_str("Subscription Does Not Exist");
1396 pjsip_tx_data *tdata;
1397 pj_status_t status;
1398
1399 status = pjsip_dlg_create_response(dlg,
1400 event->body.tsx_state.src.rdata,
1401 481, &reason,
1402 &tdata);
1403 if (status == PJ_SUCCESS) {
1404 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1405 }
1406 }
1407 return NULL;
1408 }
1409
1410 /* Found! */
1411 sub = dlgsub->sub;
1412
1413 /* Attach session to the transaction */
1414 tsx->mod_data[mod_evsub.mod.id] = sub;
1415 sub->pending_tsx++;
1416
Benny Prijono69b98ab2006-03-03 10:23:35 +00001417 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1418 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1419 * transaction is started while previous one still alive, terminate
1420 * the older one.
1421 *
1422 * Sample scenario:
1423 * - subscribe sent to destination that doesn't exist, transaction
1424 * is still retransmitting request, then unsubscribe is sent.
1425 */
1426 if (tsx->role == PJSIP_ROLE_UAC &&
1427 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001428 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1429 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001430 {
1431
1432 if (sub->pending_sub &&
1433 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1434 {
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001435 pj_timer_entry *timer;
1436 pj_str_t *key;
1437 pj_time_val timeout = {0, 0};
1438
Benny Prijono69b98ab2006-03-03 10:23:35 +00001439 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001440 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001441
1442 /* By convention, we use 490 (Request Updated) status code.
1443 * When transaction handler (below) see this status code, it
1444 * will ignore the transaction.
1445 */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001446 /* This unfortunately may cause deadlock, because at the moment
1447 * we are holding dialog's mutex. If a response to this
1448 * transaction is in progress in another thread, that thread
1449 * will deadlock when trying to acquire dialog mutex, because
1450 * it is holding the transaction mutex.
1451 *
1452 * So the solution is to register timer to kill this transaction.
1453 */
1454 //pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1455 timer = PJ_POOL_ZALLOC_T(dlg->pool, pj_timer_entry);
1456 key = PJ_POOL_ALLOC_T(dlg->pool, pj_str_t);
1457 pj_strdup(dlg->pool, key, &sub->pending_sub->transaction_key);
1458 timer->cb = &terminate_timer_cb;
1459 timer->user_data = key;
1460
1461 pjsip_endpt_schedule_timer(dlg->endpt, timer, &timeout);
Benny Prijono69b98ab2006-03-03 10:23:35 +00001462 }
1463
1464 sub->pending_sub = tsx;
1465
Benny Prijono69b98ab2006-03-03 10:23:35 +00001466 }
1467
Benny Prijono834aee32006-02-19 01:38:06 +00001468 return sub;
1469}
1470
1471
1472/*
1473 * Create response, adding custome headers and msg body.
1474 */
1475static pj_status_t create_response( pjsip_evsub *sub,
1476 pjsip_rx_data *rdata,
1477 int st_code,
1478 const pj_str_t *st_text,
1479 const pjsip_hdr *res_hdr,
1480 const pjsip_msg_body *body,
1481 pjsip_tx_data **p_tdata)
1482{
1483 pjsip_tx_data *tdata;
1484 pjsip_hdr *hdr;
1485 pj_status_t status;
1486
1487 status = pjsip_dlg_create_response(sub->dlg, rdata,
1488 st_code, st_text, &tdata);
1489 if (status != PJ_SUCCESS)
1490 return status;
1491
1492 *p_tdata = tdata;
1493
1494 /* Add response headers. */
1495 hdr = res_hdr->next;
1496 while (hdr != res_hdr) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001497 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001498 pjsip_hdr_clone(tdata->pool, hdr));
1499 hdr = hdr->next;
1500 }
1501
1502 /* Add msg body, if any */
1503 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001504 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1505 if (tdata->msg->body == NULL) {
1506
1507 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1508
Benny Prijono834aee32006-02-19 01:38:06 +00001509 /* Ignore */
1510 return PJ_SUCCESS;
1511 }
1512 }
1513
1514 return PJ_SUCCESS;
1515}
1516
1517/*
1518 * Get subscription state from the value of Subscription-State header.
1519 */
1520static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1521 pjsip_evsub_state *state,
1522 pj_str_t **state_str )
1523{
1524 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1525
1526 *state = PJSIP_EVSUB_STATE_TERMINATED;
1527 *state_str = NULL;
1528
1529 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1530
1531 *state = PJSIP_EVSUB_STATE_ACTIVE;
1532 *state_str = NULL;
1533
1534 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1535
1536 *state = PJSIP_EVSUB_STATE_PENDING;
1537 *state_str = NULL;
1538
1539 } else {
1540
1541 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1542 *state_str = &sub_state->sub_state;
1543
1544 }
1545}
1546
1547/*
1548 * Transaction event processing by UAC, after subscription is sent.
1549 */
1550static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1551 pjsip_event *event )
1552{
1553
Benny Prijono736d0f72006-09-13 22:45:38 +00001554 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1555 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1556 {
Benny Prijono834aee32006-02-19 01:38:06 +00001557
1558 /* Received response to outgoing request that establishes/refresh
1559 * subscription.
1560 */
1561
1562 /* First time initial request is sent. */
1563 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1564 tsx->state == PJSIP_TSX_STATE_CALLING)
1565 {
Benny Prijono75130572008-07-17 13:53:41 +00001566 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001567 return;
1568 }
1569
1570 /* Only interested in final response */
1571 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1572 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1573 {
1574 return;
1575 }
1576
Benny Prijono1d8d6082006-04-29 12:38:25 +00001577 /* Clear pending subscription */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001578 if (tsx == sub->pending_sub) {
Benny Prijono1d8d6082006-04-29 12:38:25 +00001579 sub->pending_sub = NULL;
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001580 } else if (sub->pending_sub != NULL) {
1581 /* This SUBSCRIBE transaction has been "renewed" with another
1582 * SUBSCRIBE, so we can just ignore this. For example, user
1583 * sent SUBSCRIBE followed immediately with UN-SUBSCRIBE.
1584 */
1585 return;
1586 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001587
Benny Prijono834aee32006-02-19 01:38:06 +00001588 /* Handle authentication. */
1589 if (tsx->status_code==401 || tsx->status_code==407) {
1590 pjsip_tx_data *tdata;
1591 pj_status_t status;
1592
1593 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1594 /* Previously failed transaction has terminated */
1595 return;
1596 }
1597
1598 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1599 event->body.tsx_state.src.rdata,
1600 tsx->last_tx, &tdata);
1601 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001602 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001603
1604 if (status != PJ_SUCCESS) {
1605 /* Authentication failed! */
1606 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001607 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001608 return;
1609 }
1610
1611 return;
1612 }
1613
1614 if (tsx->status_code/100 == 2) {
1615
1616 /* Successfull SUBSCRIBE request!
1617 * This could be:
1618 * - response to initial SUBSCRIBE request
1619 * - response to subsequent refresh
1620 * - response to unsubscription
1621 */
1622
1623 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1624 /* Ignore; this transaction has been processed before */
1625 return;
1626 }
1627
1628 /* Update UAC refresh time, if response contains Expires header,
1629 * only when we're not unsubscribing.
1630 */
1631 if (sub->expires->ivalue != 0) {
1632 pjsip_msg *msg;
1633 pjsip_expires_hdr *expires;
1634
1635 msg = event->body.tsx_state.src.rdata->msg_info.msg;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001636 expires = (pjsip_expires_hdr*)
1637 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001638 if (expires) {
1639 sub->expires->ivalue = expires->ivalue;
1640 }
1641 }
1642
1643 /* Update time */
1644 update_expires(sub, sub->expires->ivalue);
1645
1646 /* Start UAC refresh timer, only when we're not unsubscribing */
1647 if (sub->expires->ivalue != 0) {
1648 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1649 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1650
1651 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1652 timeout));
1653 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1654
1655 } else {
1656 /* Otherwise set timer to terminate client subscription when
1657 * NOTIFY to end subscription is not received.
1658 */
1659 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1660 }
1661
1662 /* Set state, if necessary */
1663 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1664 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
Benny Prijono75130572008-07-17 13:53:41 +00001665 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001666 }
1667
1668 } else {
1669
1670 /* Failed SUBSCRIBE request!
1671 *
1672 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1673 * other than 481, the subscription is still considered valid for
1674 * the duration of the last Expires.
1675 *
1676 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1677 * expiration, theoritically the expiration is still valid for the
1678 * next 5 seconds even when we receive non-481 failed response.
1679 *
1680 * Ah, what the heck!
1681 *
1682 * Just terminate now!
1683 *
1684 */
1685
1686 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1687 /* Ignore, has been handled before */
1688 return;
1689 }
1690
Benny Prijono69b98ab2006-03-03 10:23:35 +00001691 /* Ignore 490 (Request Updated) status.
1692 * This happens when application sends SUBSCRIBE/REFER while
1693 * another one is still in progress.
1694 */
1695 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1696 return;
1697 }
1698
Benny Prijono834aee32006-02-19 01:38:06 +00001699 /* Kill any timer. */
1700 set_timer(sub, TIMER_TYPE_NONE, 0);
1701
1702 /* Set state to TERMINATED */
1703 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001704 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001705
1706 }
1707
1708 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1709
1710 /* Incoming NOTIFY.
1711 * This can be the result of:
1712 * - Initial subscription response
1713 * - UAS updating the resource info.
1714 * - Unsubscription response.
1715 */
1716 int st_code = 200;
1717 pj_str_t *st_text = NULL;
1718 pjsip_hdr res_hdr;
1719 pjsip_msg_body *body = NULL;
1720
1721 pjsip_rx_data *rdata;
1722 pjsip_msg *msg;
1723 pjsip_sub_state_hdr *sub_state;
1724
1725 pjsip_evsub_state new_state;
1726 pj_str_t *new_state_str;
1727
1728 pjsip_tx_data *tdata;
1729 pj_status_t status;
1730 int next_refresh;
1731
1732 /* Only want to handle initial NOTIFY receive event. */
1733 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1734 return;
1735
1736
1737 rdata = event->body.tsx_state.src.rdata;
1738 msg = rdata->msg_info.msg;
1739
1740 pj_list_init(&res_hdr);
1741
1742 /* Get subscription state header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001743 sub_state = (pjsip_sub_state_hdr*)
1744 pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001745 if (sub_state == NULL) {
1746
1747 pjsip_warning_hdr *warn_hdr;
1748 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1749
1750 /* Bad request! Add warning header. */
1751 st_code = PJSIP_SC_BAD_REQUEST;
1752 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1753 pjsip_endpt_name(sub->endpt),
1754 &warn_text);
1755 pj_list_push_back(&res_hdr, warn_hdr);
1756 }
1757
1758 /* Call application registered callback to handle incoming NOTIFY,
1759 * if any.
1760 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001761 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001762 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1763 &res_hdr, &body);
1764
1765 /* Application MUST specify final response! */
1766 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1767
1768 /* Must be a valid status code */
1769 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1770 }
1771
1772
1773 /* If non-2xx should be returned, then send the response.
1774 * No need to update server subscription state.
1775 */
1776 if (st_code >= 300) {
1777 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1778 body, &tdata);
1779 if (status == PJ_SUCCESS) {
1780 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1781 }
1782
1783 /* Start timer to terminate subscription, just in case server
1784 * is not able to generate NOTIFY to our response.
1785 */
1786 if (status == PJ_SUCCESS) {
1787 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1788 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1789 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001790 char errmsg[PJ_ERR_MSG_SIZE];
1791 pj_str_t reason;
1792
1793 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1794 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
1795 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001796 }
1797
1798 return;
1799 }
1800
1801 /* Update expiration from the value of expires param in
1802 * Subscription-State header, but ONLY when subscription state
1803 * is "active" or "pending", AND the header contains expires param.
1804 */
1805 if (sub->expires->ivalue != 0 &&
1806 sub_state->expires_param >= 0 &&
1807 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1808 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1809 {
1810 next_refresh = sub_state->expires_param;
1811
1812 } else {
1813 next_refresh = sub->expires->ivalue;
1814 }
1815
1816 /* Update time */
1817 update_expires(sub, next_refresh);
1818
1819 /* Start UAC refresh timer, only when we're not unsubscribing */
1820 if (sub->expires->ivalue != 0) {
1821 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1822 next_refresh - TIME_UAC_REFRESH : next_refresh;
1823
1824 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1825 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1826 }
1827
1828 /* Find out the state */
1829 get_hdr_state(sub_state, &new_state, &new_state_str);
1830
1831 /* Send response. */
1832 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1833 body, &tdata);
1834 if (status == PJ_SUCCESS)
1835 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1836
1837 /* Set the state */
1838 if (status == PJ_SUCCESS) {
Benny Prijono75130572008-07-17 13:53:41 +00001839 set_state(sub, new_state, new_state_str, event,
1840 &sub_state->reason_param);
Benny Prijono834aee32006-02-19 01:38:06 +00001841 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001842 char errmsg[PJ_ERR_MSG_SIZE];
1843 pj_str_t reason;
1844
1845 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1846 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
1847 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001848 }
1849
1850
1851 } else {
1852
1853 /*
1854 * Unexpected method!
1855 */
1856 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1857 (int)tsx->method.name.slen, tsx->method.name.ptr));
1858 }
1859}
1860
1861
1862/*
1863 * Transaction event processing by UAS, after subscription is accepted.
1864 */
1865static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1866 pjsip_event *event)
1867{
1868
Benny Prijono736d0f72006-09-13 22:45:38 +00001869 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1870 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1871 {
Benny Prijono834aee32006-02-19 01:38:06 +00001872
1873 /*
1874 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1875 *
1876 */
1877 pjsip_rx_data *rdata;
1878 pjsip_event_hdr *event_hdr;
1879 pjsip_expires_hdr *expires;
1880 pjsip_msg *msg;
1881 pjsip_tx_data *tdata;
1882 int st_code = 200;
1883 pj_str_t *st_text = NULL;
1884 pjsip_hdr res_hdr;
1885 pjsip_msg_body *body = NULL;
1886 pjsip_evsub_state old_state;
1887 pj_str_t old_state_str;
Benny Prijono75130572008-07-17 13:53:41 +00001888 pj_str_t reason = { NULL, 0 };
Benny Prijono834aee32006-02-19 01:38:06 +00001889 pj_status_t status;
1890
1891
1892 /* Only wants to handle the first event when the request is
1893 * received.
1894 */
1895 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1896 return;
1897
1898 rdata = event->body.tsx_state.src.rdata;
1899 msg = rdata->msg_info.msg;
1900
1901 /* Set expiration time based on client request (in Expires header),
1902 * or package default expiration time.
1903 */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001904 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001905 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1906 &STR_EVENT, NULL);
Benny Prijono9d4469d2007-05-02 05:14:29 +00001907 expires = (pjsip_expires_hdr*)
1908 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001909 if (event_hdr && expires) {
1910 struct evpkg *evpkg;
1911
1912 evpkg = find_pkg(&event_hdr->event_type);
1913 if (evpkg) {
1914 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1915 sub->expires->ivalue = expires->ivalue;
1916 else
1917 sub->expires->ivalue = evpkg->pkg_expires;
1918 }
1919 }
1920
1921 /* Update time (before calling on_rx_refresh, since application
1922 * will send NOTIFY.
1923 */
1924 update_expires(sub, sub->expires->ivalue);
1925
1926
1927 /* Save old state.
1928 * If application respond with non-2xx, revert to old state.
1929 */
1930 old_state = sub->state;
1931 old_state_str = sub->state_str;
1932
1933 if (sub->expires->ivalue == 0) {
1934 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1935 sub->state_str = evsub_state_names[sub->state];
1936 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1937 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1938 sub->state_str = evsub_state_names[sub->state];
1939 }
1940
1941 /* Call application's on_rx_refresh, just in case it wants to send
1942 * response other than 200 (OK)
1943 */
1944 pj_list_init(&res_hdr);
1945
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001946 if (sub->user.on_rx_refresh && sub->call_cb) {
1947 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1948 &res_hdr, &body);
1949 }
Benny Prijono834aee32006-02-19 01:38:06 +00001950
1951 /* Application MUST specify final response! */
1952 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1953
1954 /* Must be a valid status code */
1955 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1956
1957
1958 /* Create and send response */
1959 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1960 body, &tdata);
1961 if (status == PJ_SUCCESS) {
1962 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001963 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001964 pjsip_hdr_shallow_clone(tdata->pool,
1965 sub->expires));
1966
1967 /* Send */
1968 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1969 }
1970
1971 /* Update state or revert state */
1972 if (st_code/100==2) {
1973
1974 if (sub->expires->ivalue == 0) {
Benny Prijono75130572008-07-17 13:53:41 +00001975 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001976 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
Benny Prijono75130572008-07-17 13:53:41 +00001977 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001978 }
1979
1980 /* Set UAS timeout timer, when state is not terminated. */
1981 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1982 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1983 sub->expires->ivalue));
1984 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1985 sub->expires->ivalue);
1986 }
1987
1988 } else {
1989 sub->state = old_state;
1990 sub->state_str = old_state_str;
1991 }
1992
1993
1994 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1995
1996 /* Handle authentication */
1997 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1998 (tsx->status_code==401 || tsx->status_code==407))
1999 {
2000 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
2001 pjsip_tx_data *tdata;
2002 pj_status_t status;
2003
2004 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
2005 tsx->last_tx, &tdata);
2006 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00002007 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00002008
2009 if (status != PJ_SUCCESS) {
2010 /* Can't authenticate. Terminate session (?) */
Benny Prijono75130572008-07-17 13:53:41 +00002011 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
2012 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002013 return;
Benny Prijono834aee32006-02-19 01:38:06 +00002014 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002015
2016 }
2017 /*
2018 * Terminate event usage if we receive 481, 408, and 7 class
2019 * responses.
2020 */
2021 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
2022 (tsx->status_code==481 || tsx->status_code==408 ||
2023 tsx->status_code/100 == 7))
2024 {
Benny Prijono75130572008-07-17 13:53:41 +00002025 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
2026 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002027 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00002028 }
Benny Prijono834aee32006-02-19 01:38:06 +00002029
2030 } else {
2031
2032 /*
2033 * Unexpected method!
2034 */
2035 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
2036 (int)tsx->method.name.slen, tsx->method.name.ptr));
2037
2038 }
2039}
2040
2041
2042/*
2043 * Notification when transaction state has changed!
2044 */
2045static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
2046{
2047 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
2048
2049 if (sub == NULL) {
2050 sub = on_new_transaction(tsx, event);
2051 if (sub == NULL)
2052 return;
2053 }
2054
2055
2056 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002057 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00002058 (*sub->user.on_tsx_state)(sub, tsx, event);
2059
2060
2061 /* Process the event: */
2062
2063 if (sub->role == PJSIP_ROLE_UAC) {
2064 on_tsx_state_uac(sub, tsx, event);
2065 } else {
2066 on_tsx_state_uas(sub, tsx, event);
2067 }
2068
2069
2070 /* Check transaction TERMINATE event */
2071 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
2072
2073 --sub->pending_tsx;
2074
2075 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
2076 sub->pending_tsx == 0)
2077 {
2078 evsub_destroy(sub);
2079 }
2080
2081 }
2082}
2083
2084