blob: d61fe71dcef9b2ad0c34d6a5cae40e7c972cb69e [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
Nanang Izzuddina62ffc92011-05-05 06:14:19 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono834aee32006-02-19 01:38:06 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjsip-simple/evsub.h>
21#include <pjsip-simple/evsub_msg.h>
22#include <pjsip-simple/errno.h>
23#include <pjsip/sip_errno.h>
24#include <pjsip/sip_module.h>
25#include <pjsip/sip_endpoint.h>
26#include <pjsip/sip_dialog.h>
27#include <pjsip/sip_auth.h>
28#include <pjsip/sip_transaction.h>
29#include <pjsip/sip_event.h>
30#include <pj/assert.h>
31#include <pj/guid.h>
32#include <pj/log.h>
33#include <pj/os.h>
34#include <pj/pool.h>
35#include <pj/string.h>
36
37
38#define THIS_FILE "evsub.c"
39
40/*
41 * Global constant
42 */
43
44/* Let's define this enum, so that it'll trigger compilation error
45 * when somebody define the same enum in sip_msg.h
46 */
47enum
48{
49 PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD,
50 PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD
51};
52
Benny Prijono1f61a8f2007-08-16 10:11:44 +000053PJ_DEF_DATA(const pjsip_method) pjsip_subscribe_method =
Benny Prijono834aee32006-02-19 01:38:06 +000054{
Benny Prijono9d4469d2007-05-02 05:14:29 +000055 (pjsip_method_e) PJSIP_SUBSCRIBE_METHOD,
Benny Prijono834aee32006-02-19 01:38:06 +000056 { "SUBSCRIBE", 9 }
57};
58
Benny Prijono1f61a8f2007-08-16 10:11:44 +000059PJ_DEF_DATA(const pjsip_method) pjsip_notify_method =
Benny Prijono834aee32006-02-19 01:38:06 +000060{
Benny Prijono9d4469d2007-05-02 05:14:29 +000061 (pjsip_method_e) PJSIP_NOTIFY_METHOD,
Benny Prijono834aee32006-02-19 01:38:06 +000062 { "NOTIFY", 6 }
63};
64
Benny Prijono1f61a8f2007-08-16 10:11:44 +000065/**
66 * SUBSCRIBE method constant.
67 */
68PJ_DEF(const pjsip_method*) pjsip_get_subscribe_method()
69{
70 return &pjsip_subscribe_method;
71}
72
73/**
74 * NOTIFY method constant.
75 */
76PJ_DEF(const pjsip_method*) pjsip_get_notify_method()
77{
78 return &pjsip_notify_method;
79}
80
81
Benny Prijono834aee32006-02-19 01:38:06 +000082/*
83 * Static prototypes.
84 */
85static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*);
86static pj_status_t mod_evsub_unload(void);
87
88
89/*
90 * State names.
91 */
92static pj_str_t evsub_state_names[] =
93{
94 { "NULL", 4},
95 { "SENT", 4},
96 { "ACCEPTED", 8},
97 { "PENDING", 7},
98 { "ACTIVE", 6},
99 { "TERMINATED", 10},
100 { "UNKNOWN", 7}
101};
102
103/*
104 * Timer constants.
105 */
106
107/* Number of seconds to send SUBSCRIBE before the actual expiration */
Benny Prijonocf2e6732009-06-01 15:39:52 +0000108#define TIME_UAC_REFRESH PJSIP_EVSUB_TIME_UAC_REFRESH
Benny Prijono834aee32006-02-19 01:38:06 +0000109
110/* Time to wait for the final NOTIFY after sending unsubscription */
Benny Prijonocf2e6732009-06-01 15:39:52 +0000111#define TIME_UAC_TERMINATE PJSIP_EVSUB_TIME_UAC_TERMINATE
Benny Prijono834aee32006-02-19 01:38:06 +0000112
113/* If client responds NOTIFY with non-2xx final response (such as 401),
114 * wait for this seconds for further NOTIFY, otherwise client will
115 * unsubscribe
116 */
Benny Prijonocf2e6732009-06-01 15:39:52 +0000117#define TIME_UAC_WAIT_NOTIFY PJSIP_EVSUB_TIME_UAC_WAIT_NOTIFY
Benny Prijono834aee32006-02-19 01:38:06 +0000118
119
120/*
121 * Timer id
122 */
123enum timer_id
124{
125 /* No timer. */
126 TIMER_TYPE_NONE,
127
128 /* Time to refresh client subscription.
129 * The action is to call on_client_refresh() callback.
130 */
131 TIMER_TYPE_UAC_REFRESH,
132
133 /* UAS timeout after to subscription refresh.
134 * The action is to call on_server_timeout() callback.
135 */
136 TIMER_TYPE_UAS_TIMEOUT,
137
138 /* UAC waiting for final NOTIFY after unsubscribing
139 * The action is to terminate.
140 */
141 TIMER_TYPE_UAC_TERMINATE,
142
143 /* UAC waiting for further NOTIFY after sending non-2xx response to
144 * NOTIFY. The action is to unsubscribe.
145 */
146 TIMER_TYPE_UAC_WAIT_NOTIFY,
147
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000148 /* Max nb of timer types. */
149 TIMER_TYPE_MAX
Benny Prijono834aee32006-02-19 01:38:06 +0000150};
151
152static const char *timer_names[] =
153{
154 "None",
155 "UAC_REFRESH",
156 "UAS_TIMEOUT"
157 "UAC_TERMINATE",
158 "UAC_WAIT_NOTIFY",
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000159 "INVALID_TIMER"
Benny Prijono834aee32006-02-19 01:38:06 +0000160};
161
162/*
163 * Definition of event package.
164 */
165struct evpkg
166{
167 PJ_DECL_LIST_MEMBER(struct evpkg);
168
169 pj_str_t pkg_name;
170 pjsip_module *pkg_mod;
171 unsigned pkg_expires;
172 pjsip_accept_hdr *pkg_accept;
173};
174
175
176/*
177 * Event subscription module (mod-evsub).
178 */
179static struct mod_evsub
180{
181 pjsip_module mod;
182 pj_pool_t *pool;
183 pjsip_endpoint *endpt;
184 struct evpkg pkg_list;
185 pjsip_allow_events_hdr *allow_events_hdr;
186
187} mod_evsub =
188{
189 {
Benny Prijono2f8992b2006-02-25 21:16:36 +0000190 NULL, NULL, /* prev, next. */
191 { "mod-evsub", 9 }, /* Name. */
192 -1, /* Id */
193 PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */
194 NULL, /* load() */
195 NULL, /* start() */
196 NULL, /* stop() */
197 &mod_evsub_unload, /* unload() */
198 NULL, /* on_rx_request() */
199 NULL, /* on_rx_response() */
200 NULL, /* on_tx_request. */
201 NULL, /* on_tx_response() */
202 &mod_evsub_on_tsx_state, /* on_tsx_state() */
Benny Prijono834aee32006-02-19 01:38:06 +0000203 }
204};
205
206
207/*
208 * Event subscription session.
209 */
210struct pjsip_evsub
211{
212 char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
213 pj_pool_t *pool; /**< Pool. */
214 pjsip_endpoint *endpt; /**< Endpoint instance. */
215 pjsip_dialog *dlg; /**< Underlying dialog. */
216 struct evpkg *pkg; /**< The event package. */
Benny Prijono26ff9062006-02-21 23:47:00 +0000217 unsigned option; /**< Options. */
Benny Prijono834aee32006-02-19 01:38:06 +0000218 pjsip_evsub_user user; /**< Callback. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000219 pj_bool_t call_cb; /**< Notify callback? */
Benny Prijono834aee32006-02-19 01:38:06 +0000220 pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */
221 pjsip_evsub_state state; /**< Subscription state. */
222 pj_str_t state_str; /**< String describing the state. */
223 pjsip_evsub_state dst_state; /**< Pending state to be set. */
224 pj_str_t dst_state_str;/**< Pending state to be set. */
Benny Prijono75130572008-07-17 13:53:41 +0000225 pj_str_t term_reason; /**< Termination reason. */
Benny Prijono834aee32006-02-19 01:38:06 +0000226 pjsip_method method; /**< Method that established subscr.*/
227 pjsip_event_hdr *event; /**< Event description. */
228 pjsip_expires_hdr *expires; /**< Expires header */
229 pjsip_accept_hdr *accept; /**< Local Accept header. */
Sauw Ming5c2f6da2011-02-11 07:39:14 +0000230 pjsip_hdr sub_hdr_list; /**< User-defined header. */
Benny Prijono834aee32006-02-19 01:38:06 +0000231
232 pj_time_val refresh_time; /**< Time to refresh. */
233 pj_timer_entry timer; /**< Internal timer. */
234 int pending_tsx; /**< Number of pending transactions.*/
Benny Prijono69b98ab2006-03-03 10:23:35 +0000235 pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */
Benny Prijono834aee32006-02-19 01:38:06 +0000236
237 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
238};
239
240
241/*
242 * This is the structure that will be "attached" to dialog.
243 * The purpose is to allow multiple subscriptions inside a dialog.
244 */
245struct dlgsub
246{
247 PJ_DECL_LIST_MEMBER(struct dlgsub);
248 pjsip_evsub *sub;
249};
250
251
252/* Static vars. */
253static const pj_str_t STR_EVENT = { "Event", 5 };
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000254static const pj_str_t STR_EVENT_S = { "Event", 5 };
Benny Prijono834aee32006-02-19 01:38:06 +0000255static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
256static const pj_str_t STR_TERMINATED = { "terminated", 10 };
257static const pj_str_t STR_ACTIVE = { "active", 6 };
258static const pj_str_t STR_PENDING = { "pending", 7 };
259static const pj_str_t STR_TIMEOUT = { "timeout", 7};
260
Benny Prijono26ff9062006-02-21 23:47:00 +0000261
Benny Prijono834aee32006-02-19 01:38:06 +0000262/*
263 * On unload module.
264 */
265static pj_status_t mod_evsub_unload(void)
266{
267 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
268 mod_evsub.pool = NULL;
269
270 return PJ_SUCCESS;
271}
272
Benny Prijono26ff9062006-02-21 23:47:00 +0000273/* Proto for pjsipsimple_strerror().
274 * Defined in errno.c
275 */
276PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode,
277 char *buf, pj_size_t bufsize );
278
Benny Prijono834aee32006-02-19 01:38:06 +0000279/*
280 * Init and register module.
281 */
282PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
283{
284 pj_status_t status;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000285 pj_str_t method_tags[] = {
286 { "SUBSCRIBE", 9},
287 { "NOTIFY", 6}
288 };
Benny Prijono834aee32006-02-19 01:38:06 +0000289
Nanang Izzuddin3a86f322011-03-16 07:34:16 +0000290 status = pj_register_strerror(PJSIP_SIMPLE_ERRNO_START,
291 PJ_ERRNO_SPACE_SIZE,
292 &pjsipsimple_strerror);
293 pj_assert(status == PJ_SUCCESS);
Benny Prijono26ff9062006-02-21 23:47:00 +0000294
Benny Prijono834aee32006-02-19 01:38:06 +0000295 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
296 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
297
298 /* Keep endpoint for future reference: */
299 mod_evsub.endpt = endpt;
300
301 /* Init event package list: */
302 pj_list_init(&mod_evsub.pkg_list);
303
304 /* Create pool: */
Benny Prijono10d8dbd2008-07-13 13:12:36 +0000305 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 512, 512);
Benny Prijono834aee32006-02-19 01:38:06 +0000306 if (!mod_evsub.pool)
307 return PJ_ENOMEM;
308
309 /* Register module: */
310 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
311 if (status != PJ_SUCCESS)
312 goto on_error;
313
314 /* Create Allow-Events header: */
315 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
316
317 /* Register SIP-event specific headers parser: */
318 pjsip_evsub_init_parser();
319
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000320 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
321 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
322 2, method_tags);
323
324 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000325 return PJ_SUCCESS;
326
327on_error:
328 if (mod_evsub.pool) {
329 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
330 mod_evsub.pool = NULL;
331 }
332 mod_evsub.endpt = NULL;
333 return status;
334}
335
336
337/*
338 * Get the instance of the module.
339 */
340PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
341{
342 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
343
344 return &mod_evsub.mod;
345}
346
347
348/*
349 * Get the event subscription instance in the transaction.
350 */
351PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
352{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000353 return (pjsip_evsub*) tsx->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000354}
355
356
357/*
358 * Set event subscription's module data.
359 */
360PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
361 void *data )
362{
363 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
364 sub->mod_data[mod_id] = data;
365}
366
367
368/*
369 * Get event subscription's module data.
370 */
371PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
372{
373 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
374 return sub->mod_data[mod_id];
375}
376
377
378/*
379 * Find registered event package with matching name.
380 */
381static struct evpkg* find_pkg(const pj_str_t *event_name)
382{
383 struct evpkg *pkg;
384
385 pkg = mod_evsub.pkg_list.next;
386 while (pkg != &mod_evsub.pkg_list) {
387
388 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
389 return pkg;
390 }
391
392 pkg = pkg->next;
393 }
394
395 return NULL;
396}
397
398/*
399 * Register an event package
400 */
401PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
402 const pj_str_t *event_name,
403 unsigned expires,
404 unsigned accept_cnt,
405 const pj_str_t accept[])
406{
407 struct evpkg *pkg;
408 unsigned i;
409
410 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
411 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
412 PJ_ETOOMANY);
413
Benny Prijonoc2456cc2007-10-25 03:19:58 +0000414 /* Make sure evsub module has been initialized */
415 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, PJ_EINVALIDOP);
416
Benny Prijono834aee32006-02-19 01:38:06 +0000417 /* Make sure no module with the specified name already registered: */
418
419 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
420
421
422 /* Create new event package: */
423
Benny Prijono9d4469d2007-05-02 05:14:29 +0000424 pkg = PJ_POOL_ALLOC_T(mod_evsub.pool, struct evpkg);
Benny Prijono834aee32006-02-19 01:38:06 +0000425 pkg->pkg_mod = pkg_mod;
426 pkg->pkg_expires = expires;
427 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
428
429 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
430 pkg->pkg_accept->count = accept_cnt;
431 for (i=0; i<accept_cnt; ++i) {
432 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
433 }
434
435 /* Add to package list: */
436
437 pj_list_push_back(&mod_evsub.pkg_list, pkg);
438
439 /* Add to Allow-Events header: */
440
441 if (mod_evsub.allow_events_hdr->count !=
442 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
443 {
444 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
445 pkg->pkg_name;
446 ++mod_evsub.allow_events_hdr->count;
447 }
448
Benny Prijono56315612006-07-18 14:39:40 +0000449 /* Add to endpoint's Accept header */
450 pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod,
451 PJSIP_H_ACCEPT, NULL,
452 pkg->pkg_accept->count,
453 pkg->pkg_accept->values);
454
Benny Prijono834aee32006-02-19 01:38:06 +0000455
456 /* Done */
457
458 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
459 (int)event_name->slen, event_name->ptr,
460 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
461
462 return PJ_SUCCESS;
463}
464
465
Benny Prijono56315612006-07-18 14:39:40 +0000466/*
467 * Retrieve Allow-Events header
468 */
469PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m)
470{
471 struct mod_evsub *mod;
472
473 if (m == NULL)
474 m = pjsip_evsub_instance();
475
476 mod = (struct mod_evsub*)m;
477
478 return (pjsip_hdr*) mod->allow_events_hdr;
479}
480
Benny Prijono834aee32006-02-19 01:38:06 +0000481
482/*
483 * Update expiration time.
484 */
485static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
486{
487 pj_gettimeofday(&sub->refresh_time);
488 sub->refresh_time.sec += interval;
489}
490
491
492/*
493 * Schedule timer.
494 */
495static void set_timer( pjsip_evsub *sub, int timer_id,
496 pj_int32_t seconds)
497{
498 if (sub->timer.id != TIMER_TYPE_NONE) {
499 PJ_LOG(5,(sub->obj_name, "%s %s timer",
500 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
501 timer_names[sub->timer.id]));
502 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
503 sub->timer.id = TIMER_TYPE_NONE;
504 }
505
506 if (timer_id != TIMER_TYPE_NONE) {
507 pj_time_val timeout;
508
509 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000510 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
511 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000512
513 timeout.sec = seconds;
514 timeout.msec = 0;
515 sub->timer.id = timer_id;
516
517 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
518
519 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
520 timer_names[sub->timer.id], timeout.sec));
521 }
522}
523
524
525/*
526 * Destroy session.
527 */
528static void evsub_destroy( pjsip_evsub *sub )
529{
530 struct dlgsub *dlgsub_head, *dlgsub;
531
532 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
533
534 /* Kill timer */
535 set_timer(sub, TIMER_TYPE_NONE, 0);
536
Benny Prijono9d4469d2007-05-02 05:14:29 +0000537 /* Remove this session from dialog's list of subscription */
538 dlgsub_head = (struct dlgsub *) sub->dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000539 dlgsub = dlgsub_head->next;
540 while (dlgsub != dlgsub_head) {
541
542 if (dlgsub->sub == sub) {
543 pj_list_erase(dlgsub);
544 break;
545 }
546
547 dlgsub = dlgsub->next;
548 }
549
550 /* Decrement dialog's session */
551 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
552}
553
554/*
555 * Set subscription session state.
556 */
557static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
Benny Prijono75130572008-07-17 13:53:41 +0000558 const pj_str_t *state_str, pjsip_event *event,
559 const pj_str_t *reason)
Benny Prijono834aee32006-02-19 01:38:06 +0000560{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000561 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000562 pj_str_t old_state_str = sub->state_str;
Benny Prijonobb0348e2010-10-13 11:13:22 +0000563 pjsip_event dummy_event;
Benny Prijono834aee32006-02-19 01:38:06 +0000564
565 sub->state = state;
566
567 if (state_str && state_str->slen)
568 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
569 else
570 sub->state_str = evsub_state_names[state];
571
Benny Prijono75130572008-07-17 13:53:41 +0000572 if (reason && sub->term_reason.slen==0)
573 pj_strdup(sub->pool, &sub->term_reason, reason);
574
Benny Prijono834aee32006-02-19 01:38:06 +0000575 PJ_LOG(4,(sub->obj_name,
576 "Subscription state changed %.*s --> %.*s",
577 (int)old_state_str.slen,
578 old_state_str.ptr,
579 (int)sub->state_str.slen,
580 sub->state_str.ptr));
Benny Prijonob90fd382011-09-18 14:59:56 +0000581 pj_log_push_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000582
Benny Prijonobb0348e2010-10-13 11:13:22 +0000583 /* don't call the callback with NULL event, it may crash the app! */
584 if (!event) {
585 PJSIP_EVENT_INIT_USER(dummy_event, 0, 0, 0, 0);
586 event = &dummy_event;
587 }
588
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000589 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000590 (*sub->user.on_evsub_state)(sub, event);
591
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000592 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
593 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
594 {
Benny Prijono834aee32006-02-19 01:38:06 +0000595 if (sub->pending_tsx == 0) {
596 evsub_destroy(sub);
597 }
598 }
Benny Prijonob90fd382011-09-18 14:59:56 +0000599
600 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000601}
602
603
604/*
605 * Timer callback.
606 */
607static void on_timer( pj_timer_heap_t *timer_heap,
608 struct pj_timer_entry *entry)
609{
610 pjsip_evsub *sub;
611 int timer_id;
612
613 PJ_UNUSED_ARG(timer_heap);
614
Benny Prijono9d4469d2007-05-02 05:14:29 +0000615 sub = (pjsip_evsub*) entry->user_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000616
617 pjsip_dlg_inc_lock(sub->dlg);
618
619 timer_id = entry->id;
620 entry->id = TIMER_TYPE_NONE;
621
622 switch (timer_id) {
623
624 case TIMER_TYPE_UAC_REFRESH:
625 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000626 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000627 (*sub->user.on_client_refresh)(sub);
628 } else {
629 pjsip_tx_data *tdata;
630 pj_status_t status;
631
632 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
Benny Prijonob90fd382011-09-18 14:59:56 +0000633 pj_log_push_indent();
Benny Prijono736d0f72006-09-13 22:45:38 +0000634 status = pjsip_evsub_initiate(sub, NULL,
Benny Prijono834aee32006-02-19 01:38:06 +0000635 sub->expires->ivalue,
636 &tdata);
637 if (status == PJ_SUCCESS)
638 pjsip_evsub_send_request(sub, tdata);
Benny Prijonob90fd382011-09-18 14:59:56 +0000639
640 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000641 }
642 break;
643
644 case TIMER_TYPE_UAS_TIMEOUT:
645 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000646 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000647 (*sub->user.on_server_timeout)(sub);
648 } else {
649 pjsip_tx_data *tdata;
650 pj_status_t status;
651
652 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
653 "Sending NOTIFY to terminate."));
Benny Prijonob90fd382011-09-18 14:59:56 +0000654 pj_log_push_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000655 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
656 NULL, &STR_TIMEOUT, &tdata);
657 if (status == PJ_SUCCESS)
658 pjsip_evsub_send_request(sub, tdata);
Benny Prijonob90fd382011-09-18 14:59:56 +0000659
660 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000661 }
662 break;
663
664 case TIMER_TYPE_UAC_TERMINATE:
665 {
Benny Prijono75130572008-07-17 13:53:41 +0000666 pj_str_t timeout = {"timeout", 7};
667
Benny Prijono834aee32006-02-19 01:38:06 +0000668 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
669 "Terminating.."));
Benny Prijonob90fd382011-09-18 14:59:56 +0000670 pj_log_push_indent();
Benny Prijono75130572008-07-17 13:53:41 +0000671 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
672 &timeout);
Benny Prijonob90fd382011-09-18 14:59:56 +0000673 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000674 }
675 break;
676
677 case TIMER_TYPE_UAC_WAIT_NOTIFY:
678 {
679 pjsip_tx_data *tdata;
680 pj_status_t status;
681
682 PJ_LOG(5,(sub->obj_name,
683 "Timeout waiting for subsequent NOTIFY (we did "
684 "send non-2xx response for previous NOTIFY). "
685 "Unsubscribing.."));
Benny Prijonob90fd382011-09-18 14:59:56 +0000686 pj_log_push_indent();
Benny Prijono736d0f72006-09-13 22:45:38 +0000687 status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000688 if (status == PJ_SUCCESS)
689 pjsip_evsub_send_request(sub, tdata);
Benny Prijonob90fd382011-09-18 14:59:56 +0000690
691 pj_log_pop_indent();
Benny Prijono834aee32006-02-19 01:38:06 +0000692 }
693 break;
694
695 default:
696 pj_assert(!"Invalid timer id");
697 }
698
699 pjsip_dlg_dec_lock(sub->dlg);
700}
701
702
703/*
704 * Create subscription session, used for both client and notifier.
705 */
706static pj_status_t evsub_create( pjsip_dialog *dlg,
707 pjsip_role_e role,
708 const pjsip_evsub_user *user_cb,
709 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000710 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000711 pjsip_evsub **p_evsub )
712{
713 pjsip_evsub *sub;
714 struct evpkg *pkg;
715 struct dlgsub *dlgsub_head, *dlgsub;
716 pj_status_t status;
717
718 /* Make sure there's package register for the event name: */
719
720 pkg = find_pkg(event);
721 if (pkg == NULL)
722 return PJSIP_SIMPLE_ENOPKG;
723
724
Benny Prijono8eae8382006-08-10 21:44:26 +0000725 /* Must lock dialog before using pool etc. */
726 pjsip_dlg_inc_lock(dlg);
727
Benny Prijono834aee32006-02-19 01:38:06 +0000728 /* Init attributes: */
729
Benny Prijono9d4469d2007-05-02 05:14:29 +0000730 sub = PJ_POOL_ZALLOC_T(dlg->pool, struct pjsip_evsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000731 sub->pool = dlg->pool;
732 sub->endpt = dlg->endpt;
733 sub->dlg = dlg;
734 sub->pkg = pkg;
735 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000736 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000737 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000738 sub->state = PJSIP_EVSUB_STATE_NULL;
739 sub->state_str = evsub_state_names[sub->state];
740 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
Benny Prijono9d4469d2007-05-02 05:14:29 +0000741 sub->accept = (pjsip_accept_hdr*)
742 pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
Sauw Ming5c2f6da2011-02-11 07:39:14 +0000743 pj_list_init(&sub->sub_hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000744
745 sub->timer.user_data = sub;
746 sub->timer.cb = &on_timer;
747
748 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000749 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
750 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000751
752
753 /* Copy callback, if any: */
754 if (user_cb)
755 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
756
757
758 /* Create Event header: */
759 sub->event = pjsip_event_hdr_create(sub->pool);
760 pj_strdup(sub->pool, &sub->event->event_type, event);
761
762
Benny Prijono15d3a702010-01-21 10:04:26 +0000763 /* Check if another subscription has been registered to the dialog. In
764 * that case, just add ourselves to the subscription list, otherwise
765 * create and register a new subscription list.
766 */
767 if (pjsip_dlg_has_usage(dlg, &mod_evsub.mod)) {
768 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
769 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
770 dlgsub->sub = sub;
771 pj_list_push_back(dlgsub_head, dlgsub);
772 } else {
773 dlgsub_head = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
774 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
775 dlgsub->sub = sub;
Benny Prijono834aee32006-02-19 01:38:06 +0000776
Benny Prijono15d3a702010-01-21 10:04:26 +0000777 pj_list_init(dlgsub_head);
778 pj_list_push_back(dlgsub_head, dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000779
780
Benny Prijono15d3a702010-01-21 10:04:26 +0000781 /* Register as dialog usage: */
Benny Prijono834aee32006-02-19 01:38:06 +0000782
Benny Prijono15d3a702010-01-21 10:04:26 +0000783 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
784 if (status != PJ_SUCCESS) {
785 pjsip_dlg_dec_lock(dlg);
786 return status;
787 }
Benny Prijono8eae8382006-08-10 21:44:26 +0000788 }
Benny Prijono834aee32006-02-19 01:38:06 +0000789
Benny Prijono834aee32006-02-19 01:38:06 +0000790 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
791 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
792 dlg->obj_name));
793
794 *p_evsub = sub;
Benny Prijono8eae8382006-08-10 21:44:26 +0000795 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000796
797 return PJ_SUCCESS;
798}
799
800
801
802/*
803 * Create client subscription session.
804 */
805PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
806 const pjsip_evsub_user *user_cb,
807 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000808 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000809 pjsip_evsub **p_evsub)
810{
811 pjsip_evsub *sub;
812 pj_status_t status;
813
814 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
815
816 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000817 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000818 if (status != PJ_SUCCESS)
819 goto on_return;
820
Benny Prijono26ff9062006-02-21 23:47:00 +0000821 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
822 * is not specified.
823 */
824 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
825 pj_create_unique_string(sub->pool, &sub->event->id_param);
826 }
Benny Prijono834aee32006-02-19 01:38:06 +0000827
828 /* Increment dlg session. */
829 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
830
831 /* Done */
832 *p_evsub = sub;
833
834on_return:
835 pjsip_dlg_dec_lock(dlg);
836 return status;
837}
838
839
840/*
841 * Create server subscription session from incoming request.
842 */
843PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
844 const pjsip_evsub_user *user_cb,
845 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000846 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000847 pjsip_evsub **p_evsub)
848{
849 pjsip_evsub *sub;
850 pjsip_transaction *tsx;
851 pjsip_accept_hdr *accept_hdr;
852 pjsip_event_hdr *event_hdr;
853 pjsip_expires_hdr *expires_hdr;
854 pj_status_t status;
855
856 /* Check arguments: */
857 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
858
859 /* MUST be request message: */
860 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
861 PJSIP_ENOTREQUESTMSG);
862
863 /* Transaction MUST have been created (in the dialog) */
864 tsx = pjsip_rdata_get_tsx(rdata);
865 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
866
867 /* No subscription must have been attached to transaction */
868 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
869 PJSIP_ETYPEEXISTS);
870
871 /* Package MUST implement on_rx_refresh */
872 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
873
Benny Prijono26ff9062006-02-21 23:47:00 +0000874 /* Request MUST have "Event" header. We need the Event header to get
875 * the package name (don't want to add more arguments in the function).
876 */
Benny Prijono834aee32006-02-19 01:38:06 +0000877 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000878 pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &STR_EVENT,
879 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000880 if (event_hdr == NULL) {
881 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
882 }
883
884 /* Start locking the mutex: */
885
886 pjsip_dlg_inc_lock(dlg);
887
888 /* Create the session: */
889
890 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000891 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000892 if (status != PJ_SUCCESS)
893 goto on_return;
894
895 /* Just duplicate Event header from the request */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000896 sub->event = (pjsip_event_hdr*) pjsip_hdr_clone(sub->pool, event_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000897
898 /* Set the method: */
899 pjsip_method_copy(sub->pool, &sub->method,
900 &rdata->msg_info.msg->line.req.method);
901
902 /* Update expiration time according to client request: */
903
904 expires_hdr = (pjsip_expires_hdr*)
905 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
906 if (expires_hdr) {
907 sub->expires->ivalue = expires_hdr->ivalue;
908 }
909
910 /* Update time. */
911 update_expires(sub, sub->expires->ivalue);
912
913 /* Update Accept header: */
914
915 accept_hdr = (pjsip_accept_hdr*)
916 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
917 if (accept_hdr)
Benny Prijono9d4469d2007-05-02 05:14:29 +0000918 sub->accept = (pjsip_accept_hdr*)pjsip_hdr_clone(sub->pool,accept_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000919
920 /* We can start the session: */
921
922 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
923 sub->pending_tsx++;
924 tsx->mod_data[mod_evsub.mod.id] = sub;
925
926
927 /* Done. */
928 *p_evsub = sub;
929
930
931on_return:
932 pjsip_dlg_dec_lock(dlg);
933 return status;
934}
935
936
937/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000938 * Forcefully destroy subscription.
939 */
940PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
941 pj_bool_t notify )
942{
943 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
944
945 pjsip_dlg_inc_lock(sub->dlg);
946
Benny Prijonod524e822006-09-22 12:48:18 +0000947 /* I think it's pretty safe to disable this check.
948
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000949 if (sub->pending_tsx) {
950 pj_assert(!"Unable to terminate when there's pending tsx");
951 pjsip_dlg_dec_lock(sub->dlg);
952 return PJ_EINVALIDOP;
953 }
Benny Prijonod524e822006-09-22 12:48:18 +0000954 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000955
956 sub->call_cb = notify;
Benny Prijono75130572008-07-17 13:53:41 +0000957 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, NULL);
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000958
959 pjsip_dlg_dec_lock(sub->dlg);
960 return PJ_SUCCESS;
961}
962
963/*
Benny Prijono834aee32006-02-19 01:38:06 +0000964 * Get subscription state.
965 */
966PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
967{
968 return sub->state;
969}
970
971/*
972 * Get state name.
973 */
974PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
975{
976 return sub->state_str.ptr;
977}
978
Benny Prijono75130572008-07-17 13:53:41 +0000979/*
980 * Get termination reason.
981 */
982PJ_DEF(const pj_str_t*) pjsip_evsub_get_termination_reason(pjsip_evsub *sub)
983{
984 return &sub->term_reason;
985}
Benny Prijono834aee32006-02-19 01:38:06 +0000986
987/*
988 * Initiate client subscription
989 */
990PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
991 const pjsip_method *method,
992 pj_int32_t expires,
993 pjsip_tx_data **p_tdata)
994{
995 pjsip_tx_data *tdata;
996 pj_status_t status;
997
998 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
999
1000 /* Use SUBSCRIBE if method is not specified */
1001 if (method == NULL)
1002 method = &pjsip_subscribe_method;
1003
1004 pjsip_dlg_inc_lock(sub->dlg);
1005
1006 /* Update method: */
1007 if (sub->state == PJSIP_EVSUB_STATE_NULL)
1008 pjsip_method_copy(sub->pool, &sub->method, method);
1009
1010 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
1011 if (status != PJ_SUCCESS)
1012 goto on_return;
1013
1014
1015 /* Add Event header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001016 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001017 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1018
1019 /* Update and add expires header: */
1020 if (expires >= 0)
1021 sub->expires->ivalue = expires;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001022 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001023 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1024
Benny Prijono89e52612010-10-01 04:09:08 +00001025 /* Add Supported header (it's optional in RFC 3265, but some event package
1026 * RFC may bring this requirement to SHOULD strength - e.g. RFC 5373)
1027 */
1028 {
1029 const pjsip_hdr *hdr = pjsip_endpt_get_capability(sub->endpt,
1030 PJSIP_H_SUPPORTED,
1031 NULL);
1032 if (hdr) {
1033 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
1034 pjsip_hdr_shallow_clone(tdata->pool, hdr));
1035 }
1036 }
1037
Benny Prijono834aee32006-02-19 01:38:06 +00001038 /* Add Accept header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001039 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001040 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
1041
1042
1043 /* Add Allow-Events header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001044 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001045 pjsip_hdr_shallow_clone(tdata->pool,
1046 mod_evsub.allow_events_hdr));
1047
Sauw Ming5c2f6da2011-02-11 07:39:14 +00001048
1049 /* Add custom headers */
1050 {
1051 const pjsip_hdr *hdr = sub->sub_hdr_list.next;
1052 while (hdr != &sub->sub_hdr_list) {
1053 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
1054 pjsip_hdr_shallow_clone(tdata->pool, hdr));
1055 hdr = hdr->next;
1056 }
1057 }
1058
1059
Benny Prijono834aee32006-02-19 01:38:06 +00001060 *p_tdata = tdata;
1061
1062
1063on_return:
1064
1065 pjsip_dlg_dec_lock(sub->dlg);
1066 return status;
1067}
1068
1069
1070/*
Sauw Ming5c2f6da2011-02-11 07:39:14 +00001071 * Add custom headers.
1072 */
1073PJ_DEF(pj_status_t) pjsip_evsub_add_header( pjsip_evsub *sub,
1074 const pjsip_hdr *hdr_list )
1075{
1076 const pjsip_hdr *hdr;
1077
1078 PJ_ASSERT_RETURN(sub && hdr_list, PJ_EINVAL);
1079
1080 hdr = hdr_list->next;
1081 while (hdr != hdr_list) {
1082 pj_list_push_back(&sub->sub_hdr_list, (pjsip_hdr*)
1083 pjsip_hdr_clone(sub->pool, hdr));
1084 hdr = hdr->next;
1085 }
1086
1087 return PJ_SUCCESS;
1088}
1089
1090
1091/*
Benny Prijono834aee32006-02-19 01:38:06 +00001092 * Accept incoming subscription request.
1093 */
1094PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
1095 pjsip_rx_data *rdata,
1096 int st_code,
1097 const pjsip_hdr *hdr_list )
1098{
1099 pjsip_tx_data *tdata;
1100 pjsip_transaction *tsx;
1101 pj_status_t status;
1102
1103 /* Check arguments */
1104 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
1105
1106 /* Can only be for server subscription: */
1107 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
1108
1109 /* Only expect 2xx status code (for now) */
1110 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
1111
1112 /* Subscription MUST have been attached to the transaction.
1113 * Initial subscription request will be attached on evsub_create_uas(),
1114 * while subsequent requests will be attached in tsx_state()
1115 */
1116 tsx = pjsip_rdata_get_tsx(rdata);
1117 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
1118 PJ_EINVALIDOP);
1119
1120 /* Lock dialog */
1121 pjsip_dlg_inc_lock(sub->dlg);
1122
1123 /* Create response: */
1124 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1125 &tdata);
1126 if (status != PJ_SUCCESS)
1127 goto on_return;
1128
1129
1130 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001131 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001132 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1133
Benny Prijonob0808372006-03-02 21:18:58 +00001134 /* Add additional header, if any. */
1135 if (hdr_list) {
1136 const pjsip_hdr *hdr = hdr_list->next;
1137 while (hdr != hdr_list) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001138 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonob0808372006-03-02 21:18:58 +00001139 pjsip_hdr_clone(tdata->pool, hdr));
1140 hdr = hdr->next;
1141 }
1142 }
Benny Prijono834aee32006-02-19 01:38:06 +00001143
1144 /* Send the response: */
1145 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1146 if (status != PJ_SUCCESS)
1147 goto on_return;
1148
1149
1150on_return:
1151
1152 pjsip_dlg_dec_lock(sub->dlg);
1153 return status;
1154}
1155
1156
1157/*
1158 * Create Subscription-State header based on current server subscription
1159 * state.
1160 */
1161static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1162 pjsip_evsub *sub,
1163 pjsip_evsub_state state,
1164 const pj_str_t *state_str,
1165 const pj_str_t *reason )
1166{
1167 pjsip_sub_state_hdr *sub_state;
1168 pj_time_val now, delay;
1169
1170 /* Get the remaining time before refresh is required */
1171 pj_gettimeofday(&now);
1172 delay = sub->refresh_time;
1173 PJ_TIME_VAL_SUB(delay, now);
1174
1175 /* Create the Subscription-State header */
1176 sub_state = pjsip_sub_state_hdr_create(pool);
1177
1178 /* Fill up the header */
1179 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001180 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001181 case PJSIP_EVSUB_STATE_SENT:
Benny Prijono834aee32006-02-19 01:38:06 +00001182 pj_assert(!"Invalid state!");
1183 /* Treat as pending */
1184
Benny Prijono75130572008-07-17 13:53:41 +00001185 case PJSIP_EVSUB_STATE_ACCEPTED:
Benny Prijono834aee32006-02-19 01:38:06 +00001186 case PJSIP_EVSUB_STATE_PENDING:
1187 sub_state->sub_state = STR_PENDING;
1188 sub_state->expires_param = delay.sec;
1189 break;
1190
1191 case PJSIP_EVSUB_STATE_ACTIVE:
1192 sub_state->sub_state = STR_ACTIVE;
1193 sub_state->expires_param = delay.sec;
1194 break;
1195
1196 case PJSIP_EVSUB_STATE_TERMINATED:
1197 sub_state->sub_state = STR_TERMINATED;
1198 if (reason != NULL)
1199 pj_strdup(pool, &sub_state->reason_param, reason);
1200 break;
1201
1202 case PJSIP_EVSUB_STATE_UNKNOWN:
1203 pj_assert(state_str != NULL);
1204 pj_strdup(pool, &sub_state->sub_state, state_str);
1205 break;
1206 }
1207
1208 return sub_state;
1209}
1210
1211/*
1212 * Create and send NOTIFY request.
1213 */
1214PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1215 pjsip_evsub_state state,
1216 const pj_str_t *state_str,
1217 const pj_str_t *reason,
1218 pjsip_tx_data **p_tdata)
1219{
1220 pjsip_tx_data *tdata;
1221 pjsip_sub_state_hdr *sub_state;
1222 pj_status_t status;
1223
1224 /* Check arguments. */
1225 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1226
1227 /* Lock dialog. */
1228 pjsip_dlg_inc_lock(sub->dlg);
1229
1230 /* Create NOTIFY request */
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001231 status = pjsip_dlg_create_request( sub->dlg, pjsip_get_notify_method(),
1232 -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001233 if (status != PJ_SUCCESS)
1234 goto on_return;
1235
1236 /* Add Event header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001237 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001238 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1239
1240 /* Add Subscription-State header */
1241 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1242 reason);
1243 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1244
1245 /* Add Allow-Events header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001246 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001247 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1248
1249 /* Add Authentication headers. */
1250 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1251
Benny Prijono75130572008-07-17 13:53:41 +00001252 /* Update reason */
1253 if (reason)
1254 pj_strdup(sub->dlg->pool, &sub->term_reason, reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001255
1256 /* Save destination state. */
1257 sub->dst_state = state;
1258 if (state_str)
1259 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1260 else
1261 sub->dst_state_str.slen = 0;
1262
1263
1264 *p_tdata = tdata;
1265
1266on_return:
1267 /* Unlock dialog */
1268 pjsip_dlg_dec_lock(sub->dlg);
1269 return status;
1270}
1271
1272
1273/*
1274 * Create NOTIFY to reflect current status.
1275 */
1276PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1277 pjsip_tx_data **p_tdata )
1278{
1279 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1280 NULL, p_tdata );
1281}
1282
1283
1284/*
1285 * Send request.
1286 */
1287PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1288 pjsip_tx_data *tdata)
1289{
1290 pj_status_t status;
1291
1292 /* Must be request message. */
1293 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1294 PJSIP_ENOTREQUESTMSG);
1295
1296 /* Lock */
1297 pjsip_dlg_inc_lock(sub->dlg);
1298
1299 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001300 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001301 if (status != PJ_SUCCESS)
1302 goto on_return;
1303
1304
1305 /* Special case for NOTIFY:
1306 * The new state was set in pjsip_evsub_notify(), but we apply the
1307 * new state now, when the request was actually sent.
1308 */
1309 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1310 &pjsip_notify_method)==0)
1311 {
1312 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1313 {goto on_return;});
1314
1315 set_state(sub, sub->dst_state,
1316 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
Benny Prijono75130572008-07-17 13:53:41 +00001317 NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001318
1319 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1320 sub->dst_state_str.slen = 0;
1321
1322 }
1323
1324
1325on_return:
1326 pjsip_dlg_dec_lock(sub->dlg);
1327 return status;
1328}
1329
1330
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001331/* Callback to be called to terminate transaction. */
1332static void terminate_timer_cb(pj_timer_heap_t *timer_heap,
1333 struct pj_timer_entry *entry)
1334{
1335 pj_str_t *key;
1336 pjsip_transaction *tsx;
1337
1338 PJ_UNUSED_ARG(timer_heap);
1339
1340 key = (pj_str_t*)entry->user_data;
1341 tsx = pjsip_tsx_layer_find_tsx(key, PJ_FALSE);
1342 /* Chance of race condition here */
1343 if (tsx) {
1344 pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_UPDATED);
1345 }
1346}
1347
Benny Prijono834aee32006-02-19 01:38:06 +00001348
1349/*
1350 * Attach subscription session to newly created transaction, if appropriate.
1351 */
1352static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1353 pjsip_event *event)
1354{
1355 /*
1356 * Newly created transaction will not have subscription session
1357 * attached to it. Find the subscription session from the dialog,
1358 * by matching the Event header.
1359 */
1360 pjsip_dialog *dlg;
1361 pjsip_event_hdr *event_hdr;
1362 pjsip_msg *msg;
1363 struct dlgsub *dlgsub_head, *dlgsub;
1364 pjsip_evsub *sub;
1365
1366 dlg = pjsip_tsx_get_dlg(tsx);
1367 if (!dlg) {
1368 pj_assert(!"Transaction should have a dialog instance!");
1369 return NULL;
1370 }
1371
Benny Prijono26ff9062006-02-21 23:47:00 +00001372
Benny Prijono834aee32006-02-19 01:38:06 +00001373 switch (event->body.tsx_state.type) {
1374 case PJSIP_EVENT_RX_MSG:
1375 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1376 break;
1377 case PJSIP_EVENT_TX_MSG:
1378 msg = event->body.tsx_state.src.tdata->msg;
1379 break;
1380 default:
1381 if (tsx->role == PJSIP_ROLE_UAC)
1382 msg = tsx->last_tx->msg;
1383 else
1384 msg = NULL;
1385 break;
1386 }
1387
1388 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001389 //Note:
1390 // this transaction can be other transaction in the dialog.
1391 // The assertion below probably only valid for dialog that
1392 // only has one event subscription usage.
1393 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001394 return NULL;
1395 }
1396
Benny Prijono9d4469d2007-05-02 05:14:29 +00001397 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001398 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1399 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001400 if (!event_hdr) {
1401 /* Not subscription related message */
1402 return NULL;
1403 }
1404
1405 /* Find the subscription in the dialog, based on the content
1406 * of Event header:
1407 */
1408
Benny Prijono9d4469d2007-05-02 05:14:29 +00001409 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +00001410 if (dlgsub_head == NULL) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001411 dlgsub_head = PJ_POOL_ALLOC_T(dlg->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +00001412 pj_list_init(dlgsub_head);
1413 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1414 }
1415 dlgsub = dlgsub_head->next;
1416
1417 while (dlgsub != dlgsub_head) {
1418
Benny Prijono26ff9062006-02-21 23:47:00 +00001419 if (pj_stricmp(&dlgsub->sub->event->event_type,
1420 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001421 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001422 /* Event type matched.
1423 * Check if event ID matched too.
1424 */
1425 if (pj_strcmp(&dlgsub->sub->event->id_param,
1426 &event_hdr->id_param)==0)
1427 {
1428
1429 break;
1430
1431 }
1432 /*
1433 * Otherwise if it is an UAC subscription, AND
1434 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1435 * the session's event id is NULL, AND
1436 * the incoming request is NOTIFY with event ID, then
1437 * we consider it as a match, and update the
1438 * session's event id.
1439 */
1440 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1441 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1442 dlgsub->sub->event->id_param.slen==0 &&
1443 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1444 {
1445 /* Update session's event id. */
1446 pj_strdup(dlgsub->sub->pool,
1447 &dlgsub->sub->event->id_param,
1448 &event_hdr->id_param);
1449
1450 break;
1451 }
Benny Prijono834aee32006-02-19 01:38:06 +00001452 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001453
1454
1455
Benny Prijono834aee32006-02-19 01:38:06 +00001456 dlgsub = dlgsub->next;
1457 }
1458
Benny Prijonoce00fa02009-06-30 13:47:44 +00001459 /* Note:
1460 * the second condition is for http://trac.pjsip.org/repos/ticket/911
1461 */
1462 if (dlgsub == dlgsub_head ||
1463 (dlgsub->sub &&
1464 pjsip_evsub_get_state(dlgsub->sub)==PJSIP_EVSUB_STATE_TERMINATED))
1465 {
1466 const char *reason_msg =
1467 (dlgsub == dlgsub_head ? "Subscription Does Not Exist" :
1468 "Subscription already terminated");
1469
Benny Prijono834aee32006-02-19 01:38:06 +00001470 /* This could be incoming request to create new subscription */
1471 PJ_LOG(4,(THIS_FILE,
Benny Prijonoce00fa02009-06-30 13:47:44 +00001472 "%s for %.*s, event=%.*s;id=%.*s",
1473 reason_msg,
Benny Prijono26ff9062006-02-21 23:47:00 +00001474 (int)tsx->method.name.slen,
1475 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001476 (int)event_hdr->event_type.slen,
1477 event_hdr->event_type.ptr,
1478 (int)event_hdr->id_param.slen,
1479 event_hdr->id_param.ptr));
1480
1481 /* If this is an incoming NOTIFY, reject with 481 */
1482 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1483 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1484 {
Benny Prijonoce00fa02009-06-30 13:47:44 +00001485 pj_str_t reason;
Benny Prijono834aee32006-02-19 01:38:06 +00001486 pjsip_tx_data *tdata;
1487 pj_status_t status;
1488
Benny Prijonoce00fa02009-06-30 13:47:44 +00001489 pj_cstr(&reason, reason_msg);
Benny Prijono834aee32006-02-19 01:38:06 +00001490 status = pjsip_dlg_create_response(dlg,
1491 event->body.tsx_state.src.rdata,
1492 481, &reason,
1493 &tdata);
1494 if (status == PJ_SUCCESS) {
1495 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1496 }
1497 }
1498 return NULL;
1499 }
1500
1501 /* Found! */
1502 sub = dlgsub->sub;
1503
1504 /* Attach session to the transaction */
1505 tsx->mod_data[mod_evsub.mod.id] = sub;
1506 sub->pending_tsx++;
1507
Benny Prijono69b98ab2006-03-03 10:23:35 +00001508 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1509 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1510 * transaction is started while previous one still alive, terminate
1511 * the older one.
1512 *
1513 * Sample scenario:
1514 * - subscribe sent to destination that doesn't exist, transaction
1515 * is still retransmitting request, then unsubscribe is sent.
1516 */
1517 if (tsx->role == PJSIP_ROLE_UAC &&
1518 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001519 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1520 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001521 {
1522
1523 if (sub->pending_sub &&
1524 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1525 {
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001526 pj_timer_entry *timer;
1527 pj_str_t *key;
1528 pj_time_val timeout = {0, 0};
1529
Benny Prijono69b98ab2006-03-03 10:23:35 +00001530 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001531 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001532
1533 /* By convention, we use 490 (Request Updated) status code.
1534 * When transaction handler (below) see this status code, it
1535 * will ignore the transaction.
1536 */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001537 /* This unfortunately may cause deadlock, because at the moment
1538 * we are holding dialog's mutex. If a response to this
1539 * transaction is in progress in another thread, that thread
1540 * will deadlock when trying to acquire dialog mutex, because
1541 * it is holding the transaction mutex.
1542 *
1543 * So the solution is to register timer to kill this transaction.
1544 */
1545 //pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1546 timer = PJ_POOL_ZALLOC_T(dlg->pool, pj_timer_entry);
1547 key = PJ_POOL_ALLOC_T(dlg->pool, pj_str_t);
1548 pj_strdup(dlg->pool, key, &sub->pending_sub->transaction_key);
1549 timer->cb = &terminate_timer_cb;
1550 timer->user_data = key;
1551
1552 pjsip_endpt_schedule_timer(dlg->endpt, timer, &timeout);
Benny Prijono69b98ab2006-03-03 10:23:35 +00001553 }
1554
1555 sub->pending_sub = tsx;
1556
Benny Prijono69b98ab2006-03-03 10:23:35 +00001557 }
1558
Benny Prijono834aee32006-02-19 01:38:06 +00001559 return sub;
1560}
1561
1562
1563/*
1564 * Create response, adding custome headers and msg body.
1565 */
1566static pj_status_t create_response( pjsip_evsub *sub,
1567 pjsip_rx_data *rdata,
1568 int st_code,
1569 const pj_str_t *st_text,
1570 const pjsip_hdr *res_hdr,
1571 const pjsip_msg_body *body,
1572 pjsip_tx_data **p_tdata)
1573{
1574 pjsip_tx_data *tdata;
1575 pjsip_hdr *hdr;
1576 pj_status_t status;
1577
1578 status = pjsip_dlg_create_response(sub->dlg, rdata,
1579 st_code, st_text, &tdata);
1580 if (status != PJ_SUCCESS)
1581 return status;
1582
1583 *p_tdata = tdata;
1584
1585 /* Add response headers. */
1586 hdr = res_hdr->next;
1587 while (hdr != res_hdr) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001588 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001589 pjsip_hdr_clone(tdata->pool, hdr));
1590 hdr = hdr->next;
1591 }
1592
1593 /* Add msg body, if any */
1594 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001595 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1596 if (tdata->msg->body == NULL) {
1597
1598 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1599
Benny Prijono834aee32006-02-19 01:38:06 +00001600 /* Ignore */
1601 return PJ_SUCCESS;
1602 }
1603 }
1604
1605 return PJ_SUCCESS;
1606}
1607
1608/*
1609 * Get subscription state from the value of Subscription-State header.
1610 */
1611static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1612 pjsip_evsub_state *state,
1613 pj_str_t **state_str )
1614{
1615 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1616
1617 *state = PJSIP_EVSUB_STATE_TERMINATED;
1618 *state_str = NULL;
1619
1620 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1621
1622 *state = PJSIP_EVSUB_STATE_ACTIVE;
1623 *state_str = NULL;
1624
1625 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1626
1627 *state = PJSIP_EVSUB_STATE_PENDING;
1628 *state_str = NULL;
1629
1630 } else {
1631
1632 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1633 *state_str = &sub_state->sub_state;
1634
1635 }
1636}
1637
1638/*
1639 * Transaction event processing by UAC, after subscription is sent.
1640 */
1641static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1642 pjsip_event *event )
1643{
1644
Benny Prijono736d0f72006-09-13 22:45:38 +00001645 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1646 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1647 {
Benny Prijono834aee32006-02-19 01:38:06 +00001648
1649 /* Received response to outgoing request that establishes/refresh
1650 * subscription.
1651 */
1652
1653 /* First time initial request is sent. */
1654 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1655 tsx->state == PJSIP_TSX_STATE_CALLING)
1656 {
Benny Prijono75130572008-07-17 13:53:41 +00001657 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001658 return;
1659 }
1660
1661 /* Only interested in final response */
1662 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1663 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1664 {
1665 return;
1666 }
1667
Benny Prijono1d8d6082006-04-29 12:38:25 +00001668 /* Clear pending subscription */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001669 if (tsx == sub->pending_sub) {
Benny Prijono1d8d6082006-04-29 12:38:25 +00001670 sub->pending_sub = NULL;
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001671 } else if (sub->pending_sub != NULL) {
1672 /* This SUBSCRIBE transaction has been "renewed" with another
1673 * SUBSCRIBE, so we can just ignore this. For example, user
1674 * sent SUBSCRIBE followed immediately with UN-SUBSCRIBE.
1675 */
1676 return;
1677 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001678
Benny Prijono834aee32006-02-19 01:38:06 +00001679 /* Handle authentication. */
1680 if (tsx->status_code==401 || tsx->status_code==407) {
1681 pjsip_tx_data *tdata;
1682 pj_status_t status;
1683
1684 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1685 /* Previously failed transaction has terminated */
1686 return;
1687 }
1688
1689 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1690 event->body.tsx_state.src.rdata,
1691 tsx->last_tx, &tdata);
1692 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001693 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001694
1695 if (status != PJ_SUCCESS) {
1696 /* Authentication failed! */
1697 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001698 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001699 return;
1700 }
1701
1702 return;
1703 }
1704
1705 if (tsx->status_code/100 == 2) {
1706
1707 /* Successfull SUBSCRIBE request!
1708 * This could be:
1709 * - response to initial SUBSCRIBE request
1710 * - response to subsequent refresh
1711 * - response to unsubscription
1712 */
1713
1714 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1715 /* Ignore; this transaction has been processed before */
1716 return;
1717 }
1718
1719 /* Update UAC refresh time, if response contains Expires header,
1720 * only when we're not unsubscribing.
1721 */
1722 if (sub->expires->ivalue != 0) {
1723 pjsip_msg *msg;
1724 pjsip_expires_hdr *expires;
1725
1726 msg = event->body.tsx_state.src.rdata->msg_info.msg;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001727 expires = (pjsip_expires_hdr*)
1728 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001729 if (expires) {
1730 sub->expires->ivalue = expires->ivalue;
1731 }
1732 }
1733
1734 /* Update time */
1735 update_expires(sub, sub->expires->ivalue);
1736
1737 /* Start UAC refresh timer, only when we're not unsubscribing */
1738 if (sub->expires->ivalue != 0) {
1739 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1740 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1741
1742 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1743 timeout));
1744 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1745
1746 } else {
1747 /* Otherwise set timer to terminate client subscription when
1748 * NOTIFY to end subscription is not received.
1749 */
1750 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1751 }
1752
1753 /* Set state, if necessary */
1754 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1755 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
Benny Prijono75130572008-07-17 13:53:41 +00001756 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001757 }
1758
1759 } else {
1760
1761 /* Failed SUBSCRIBE request!
1762 *
1763 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1764 * other than 481, the subscription is still considered valid for
1765 * the duration of the last Expires.
1766 *
1767 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1768 * expiration, theoritically the expiration is still valid for the
1769 * next 5 seconds even when we receive non-481 failed response.
1770 *
1771 * Ah, what the heck!
1772 *
1773 * Just terminate now!
1774 *
1775 */
1776
1777 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1778 /* Ignore, has been handled before */
1779 return;
1780 }
1781
Benny Prijono69b98ab2006-03-03 10:23:35 +00001782 /* Ignore 490 (Request Updated) status.
1783 * This happens when application sends SUBSCRIBE/REFER while
1784 * another one is still in progress.
1785 */
1786 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1787 return;
1788 }
1789
Benny Prijono834aee32006-02-19 01:38:06 +00001790 /* Kill any timer. */
1791 set_timer(sub, TIMER_TYPE_NONE, 0);
1792
1793 /* Set state to TERMINATED */
1794 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001795 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001796
1797 }
1798
1799 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1800
1801 /* Incoming NOTIFY.
1802 * This can be the result of:
1803 * - Initial subscription response
1804 * - UAS updating the resource info.
1805 * - Unsubscription response.
1806 */
1807 int st_code = 200;
1808 pj_str_t *st_text = NULL;
1809 pjsip_hdr res_hdr;
1810 pjsip_msg_body *body = NULL;
1811
1812 pjsip_rx_data *rdata;
1813 pjsip_msg *msg;
1814 pjsip_sub_state_hdr *sub_state;
1815
1816 pjsip_evsub_state new_state;
1817 pj_str_t *new_state_str;
1818
1819 pjsip_tx_data *tdata;
1820 pj_status_t status;
1821 int next_refresh;
1822
1823 /* Only want to handle initial NOTIFY receive event. */
1824 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1825 return;
1826
1827
1828 rdata = event->body.tsx_state.src.rdata;
1829 msg = rdata->msg_info.msg;
1830
1831 pj_list_init(&res_hdr);
1832
1833 /* Get subscription state header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001834 sub_state = (pjsip_sub_state_hdr*)
1835 pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001836 if (sub_state == NULL) {
1837
1838 pjsip_warning_hdr *warn_hdr;
1839 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1840
1841 /* Bad request! Add warning header. */
1842 st_code = PJSIP_SC_BAD_REQUEST;
1843 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1844 pjsip_endpt_name(sub->endpt),
1845 &warn_text);
1846 pj_list_push_back(&res_hdr, warn_hdr);
1847 }
1848
1849 /* Call application registered callback to handle incoming NOTIFY,
1850 * if any.
1851 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001852 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001853 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1854 &res_hdr, &body);
1855
1856 /* Application MUST specify final response! */
1857 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1858
1859 /* Must be a valid status code */
1860 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1861 }
1862
1863
1864 /* If non-2xx should be returned, then send the response.
1865 * No need to update server subscription state.
1866 */
1867 if (st_code >= 300) {
1868 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1869 body, &tdata);
1870 if (status == PJ_SUCCESS) {
1871 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1872 }
1873
1874 /* Start timer to terminate subscription, just in case server
1875 * is not able to generate NOTIFY to our response.
1876 */
1877 if (status == PJ_SUCCESS) {
1878 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1879 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1880 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001881 char errmsg[PJ_ERR_MSG_SIZE];
1882 pj_str_t reason;
1883
1884 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1885 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
1886 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001887 }
1888
1889 return;
1890 }
1891
1892 /* Update expiration from the value of expires param in
1893 * Subscription-State header, but ONLY when subscription state
1894 * is "active" or "pending", AND the header contains expires param.
1895 */
1896 if (sub->expires->ivalue != 0 &&
1897 sub_state->expires_param >= 0 &&
1898 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1899 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1900 {
1901 next_refresh = sub_state->expires_param;
1902
1903 } else {
1904 next_refresh = sub->expires->ivalue;
1905 }
1906
1907 /* Update time */
1908 update_expires(sub, next_refresh);
1909
1910 /* Start UAC refresh timer, only when we're not unsubscribing */
1911 if (sub->expires->ivalue != 0) {
1912 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1913 next_refresh - TIME_UAC_REFRESH : next_refresh;
1914
1915 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1916 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1917 }
1918
1919 /* Find out the state */
1920 get_hdr_state(sub_state, &new_state, &new_state_str);
1921
1922 /* Send response. */
1923 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1924 body, &tdata);
1925 if (status == PJ_SUCCESS)
1926 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1927
1928 /* Set the state */
1929 if (status == PJ_SUCCESS) {
Benny Prijono75130572008-07-17 13:53:41 +00001930 set_state(sub, new_state, new_state_str, event,
1931 &sub_state->reason_param);
Benny Prijono834aee32006-02-19 01:38:06 +00001932 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001933 char errmsg[PJ_ERR_MSG_SIZE];
1934 pj_str_t reason;
1935
1936 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1937 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
1938 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001939 }
1940
1941
1942 } else {
1943
1944 /*
1945 * Unexpected method!
1946 */
1947 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1948 (int)tsx->method.name.slen, tsx->method.name.ptr));
1949 }
1950}
1951
1952
1953/*
1954 * Transaction event processing by UAS, after subscription is accepted.
1955 */
1956static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1957 pjsip_event *event)
1958{
1959
Benny Prijono736d0f72006-09-13 22:45:38 +00001960 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1961 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1962 {
Benny Prijono834aee32006-02-19 01:38:06 +00001963
1964 /*
1965 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1966 *
1967 */
1968 pjsip_rx_data *rdata;
1969 pjsip_event_hdr *event_hdr;
1970 pjsip_expires_hdr *expires;
1971 pjsip_msg *msg;
1972 pjsip_tx_data *tdata;
1973 int st_code = 200;
1974 pj_str_t *st_text = NULL;
1975 pjsip_hdr res_hdr;
1976 pjsip_msg_body *body = NULL;
1977 pjsip_evsub_state old_state;
1978 pj_str_t old_state_str;
Benny Prijono75130572008-07-17 13:53:41 +00001979 pj_str_t reason = { NULL, 0 };
Benny Prijono834aee32006-02-19 01:38:06 +00001980 pj_status_t status;
1981
1982
1983 /* Only wants to handle the first event when the request is
1984 * received.
1985 */
1986 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1987 return;
1988
1989 rdata = event->body.tsx_state.src.rdata;
1990 msg = rdata->msg_info.msg;
1991
1992 /* Set expiration time based on client request (in Expires header),
1993 * or package default expiration time.
1994 */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001995 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001996 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1997 &STR_EVENT, NULL);
Benny Prijono9d4469d2007-05-02 05:14:29 +00001998 expires = (pjsip_expires_hdr*)
1999 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00002000 if (event_hdr && expires) {
2001 struct evpkg *evpkg;
2002
2003 evpkg = find_pkg(&event_hdr->event_type);
2004 if (evpkg) {
2005 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
2006 sub->expires->ivalue = expires->ivalue;
2007 else
2008 sub->expires->ivalue = evpkg->pkg_expires;
2009 }
2010 }
2011
2012 /* Update time (before calling on_rx_refresh, since application
2013 * will send NOTIFY.
2014 */
2015 update_expires(sub, sub->expires->ivalue);
2016
2017
2018 /* Save old state.
2019 * If application respond with non-2xx, revert to old state.
2020 */
2021 old_state = sub->state;
2022 old_state_str = sub->state_str;
2023
2024 if (sub->expires->ivalue == 0) {
2025 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
2026 sub->state_str = evsub_state_names[sub->state];
2027 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
2028 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
2029 sub->state_str = evsub_state_names[sub->state];
2030 }
2031
2032 /* Call application's on_rx_refresh, just in case it wants to send
2033 * response other than 200 (OK)
2034 */
2035 pj_list_init(&res_hdr);
2036
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002037 if (sub->user.on_rx_refresh && sub->call_cb) {
2038 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
2039 &res_hdr, &body);
2040 }
Benny Prijono834aee32006-02-19 01:38:06 +00002041
2042 /* Application MUST specify final response! */
2043 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
2044
2045 /* Must be a valid status code */
2046 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
2047
2048
2049 /* Create and send response */
2050 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
2051 body, &tdata);
2052 if (status == PJ_SUCCESS) {
2053 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00002054 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00002055 pjsip_hdr_shallow_clone(tdata->pool,
2056 sub->expires));
2057
2058 /* Send */
2059 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
2060 }
2061
2062 /* Update state or revert state */
2063 if (st_code/100==2) {
2064
2065 if (sub->expires->ivalue == 0) {
Benny Prijono75130572008-07-17 13:53:41 +00002066 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00002067 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
Benny Prijono75130572008-07-17 13:53:41 +00002068 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00002069 }
2070
2071 /* Set UAS timeout timer, when state is not terminated. */
2072 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
2073 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
2074 sub->expires->ivalue));
2075 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
2076 sub->expires->ivalue);
2077 }
2078
2079 } else {
2080 sub->state = old_state;
2081 sub->state_str = old_state_str;
2082 }
2083
2084
2085 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
2086
2087 /* Handle authentication */
2088 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
2089 (tsx->status_code==401 || tsx->status_code==407))
2090 {
2091 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
2092 pjsip_tx_data *tdata;
2093 pj_status_t status;
2094
2095 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
2096 tsx->last_tx, &tdata);
2097 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00002098 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00002099
2100 if (status != PJ_SUCCESS) {
2101 /* Can't authenticate. Terminate session (?) */
Benny Prijono75130572008-07-17 13:53:41 +00002102 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
2103 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002104 return;
Benny Prijono834aee32006-02-19 01:38:06 +00002105 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002106
2107 }
2108 /*
2109 * Terminate event usage if we receive 481, 408, and 7 class
2110 * responses.
2111 */
2112 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
2113 (tsx->status_code==481 || tsx->status_code==408 ||
2114 tsx->status_code/100 == 7))
2115 {
Benny Prijono75130572008-07-17 13:53:41 +00002116 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
2117 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002118 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00002119 }
Benny Prijono834aee32006-02-19 01:38:06 +00002120
2121 } else {
2122
2123 /*
2124 * Unexpected method!
2125 */
2126 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
2127 (int)tsx->method.name.slen, tsx->method.name.ptr));
2128
2129 }
2130}
2131
2132
2133/*
2134 * Notification when transaction state has changed!
2135 */
2136static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
2137{
2138 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
2139
2140 if (sub == NULL) {
2141 sub = on_new_transaction(tsx, event);
2142 if (sub == NULL)
2143 return;
2144 }
2145
2146
2147 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002148 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00002149 (*sub->user.on_tsx_state)(sub, tsx, event);
2150
2151
2152 /* Process the event: */
2153
2154 if (sub->role == PJSIP_ROLE_UAC) {
2155 on_tsx_state_uac(sub, tsx, event);
2156 } else {
2157 on_tsx_state_uas(sub, tsx, event);
2158 }
2159
2160
2161 /* Check transaction TERMINATE event */
2162 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
2163
2164 --sub->pending_tsx;
2165
2166 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
2167 sub->pending_tsx == 0)
2168 {
2169 evsub_destroy(sub);
2170 }
2171
2172 }
2173}
2174
2175