blob: bf26a3db5656b1e6daf2167d935408ed1d6228ee [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 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
Benny Prijono26ff9062006-02-21 23:47:00 +0000290 pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
291 &pjsipsimple_strerror);
292
Benny Prijono834aee32006-02-19 01:38:06 +0000293 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
294 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
295
296 /* Keep endpoint for future reference: */
297 mod_evsub.endpt = endpt;
298
299 /* Init event package list: */
300 pj_list_init(&mod_evsub.pkg_list);
301
302 /* Create pool: */
Benny Prijono10d8dbd2008-07-13 13:12:36 +0000303 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 512, 512);
Benny Prijono834aee32006-02-19 01:38:06 +0000304 if (!mod_evsub.pool)
305 return PJ_ENOMEM;
306
307 /* Register module: */
308 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
309 if (status != PJ_SUCCESS)
310 goto on_error;
311
312 /* Create Allow-Events header: */
313 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
314
315 /* Register SIP-event specific headers parser: */
316 pjsip_evsub_init_parser();
317
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000318 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
319 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
320 2, method_tags);
321
322 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000323 return PJ_SUCCESS;
324
325on_error:
326 if (mod_evsub.pool) {
327 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
328 mod_evsub.pool = NULL;
329 }
330 mod_evsub.endpt = NULL;
331 return status;
332}
333
334
335/*
336 * Get the instance of the module.
337 */
338PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
339{
340 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
341
342 return &mod_evsub.mod;
343}
344
345
346/*
347 * Get the event subscription instance in the transaction.
348 */
349PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
350{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000351 return (pjsip_evsub*) tsx->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000352}
353
354
355/*
356 * Set event subscription's module data.
357 */
358PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
359 void *data )
360{
361 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
362 sub->mod_data[mod_id] = data;
363}
364
365
366/*
367 * Get event subscription's module data.
368 */
369PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
370{
371 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
372 return sub->mod_data[mod_id];
373}
374
375
376/*
377 * Find registered event package with matching name.
378 */
379static struct evpkg* find_pkg(const pj_str_t *event_name)
380{
381 struct evpkg *pkg;
382
383 pkg = mod_evsub.pkg_list.next;
384 while (pkg != &mod_evsub.pkg_list) {
385
386 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
387 return pkg;
388 }
389
390 pkg = pkg->next;
391 }
392
393 return NULL;
394}
395
396/*
397 * Register an event package
398 */
399PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
400 const pj_str_t *event_name,
401 unsigned expires,
402 unsigned accept_cnt,
403 const pj_str_t accept[])
404{
405 struct evpkg *pkg;
406 unsigned i;
407
408 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
409 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
410 PJ_ETOOMANY);
411
Benny Prijonoc2456cc2007-10-25 03:19:58 +0000412 /* Make sure evsub module has been initialized */
413 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, PJ_EINVALIDOP);
414
Benny Prijono834aee32006-02-19 01:38:06 +0000415 /* Make sure no module with the specified name already registered: */
416
417 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
418
419
420 /* Create new event package: */
421
Benny Prijono9d4469d2007-05-02 05:14:29 +0000422 pkg = PJ_POOL_ALLOC_T(mod_evsub.pool, struct evpkg);
Benny Prijono834aee32006-02-19 01:38:06 +0000423 pkg->pkg_mod = pkg_mod;
424 pkg->pkg_expires = expires;
425 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
426
427 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
428 pkg->pkg_accept->count = accept_cnt;
429 for (i=0; i<accept_cnt; ++i) {
430 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
431 }
432
433 /* Add to package list: */
434
435 pj_list_push_back(&mod_evsub.pkg_list, pkg);
436
437 /* Add to Allow-Events header: */
438
439 if (mod_evsub.allow_events_hdr->count !=
440 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
441 {
442 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
443 pkg->pkg_name;
444 ++mod_evsub.allow_events_hdr->count;
445 }
446
Benny Prijono56315612006-07-18 14:39:40 +0000447 /* Add to endpoint's Accept header */
448 pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod,
449 PJSIP_H_ACCEPT, NULL,
450 pkg->pkg_accept->count,
451 pkg->pkg_accept->values);
452
Benny Prijono834aee32006-02-19 01:38:06 +0000453
454 /* Done */
455
456 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
457 (int)event_name->slen, event_name->ptr,
458 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
459
460 return PJ_SUCCESS;
461}
462
463
Benny Prijono56315612006-07-18 14:39:40 +0000464/*
465 * Retrieve Allow-Events header
466 */
467PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m)
468{
469 struct mod_evsub *mod;
470
471 if (m == NULL)
472 m = pjsip_evsub_instance();
473
474 mod = (struct mod_evsub*)m;
475
476 return (pjsip_hdr*) mod->allow_events_hdr;
477}
478
Benny Prijono834aee32006-02-19 01:38:06 +0000479
480/*
481 * Update expiration time.
482 */
483static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
484{
485 pj_gettimeofday(&sub->refresh_time);
486 sub->refresh_time.sec += interval;
487}
488
489
490/*
491 * Schedule timer.
492 */
493static void set_timer( pjsip_evsub *sub, int timer_id,
494 pj_int32_t seconds)
495{
496 if (sub->timer.id != TIMER_TYPE_NONE) {
497 PJ_LOG(5,(sub->obj_name, "%s %s timer",
498 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
499 timer_names[sub->timer.id]));
500 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
501 sub->timer.id = TIMER_TYPE_NONE;
502 }
503
504 if (timer_id != TIMER_TYPE_NONE) {
505 pj_time_val timeout;
506
507 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000508 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
509 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000510
511 timeout.sec = seconds;
512 timeout.msec = 0;
513 sub->timer.id = timer_id;
514
515 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
516
517 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
518 timer_names[sub->timer.id], timeout.sec));
519 }
520}
521
522
523/*
524 * Destroy session.
525 */
526static void evsub_destroy( pjsip_evsub *sub )
527{
528 struct dlgsub *dlgsub_head, *dlgsub;
529
530 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
531
532 /* Kill timer */
533 set_timer(sub, TIMER_TYPE_NONE, 0);
534
Benny Prijono9d4469d2007-05-02 05:14:29 +0000535 /* Remove this session from dialog's list of subscription */
536 dlgsub_head = (struct dlgsub *) sub->dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000537 dlgsub = dlgsub_head->next;
538 while (dlgsub != dlgsub_head) {
539
540 if (dlgsub->sub == sub) {
541 pj_list_erase(dlgsub);
542 break;
543 }
544
545 dlgsub = dlgsub->next;
546 }
547
548 /* Decrement dialog's session */
549 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
550}
551
552/*
553 * Set subscription session state.
554 */
555static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
Benny Prijono75130572008-07-17 13:53:41 +0000556 const pj_str_t *state_str, pjsip_event *event,
557 const pj_str_t *reason)
Benny Prijono834aee32006-02-19 01:38:06 +0000558{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000559 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000560 pj_str_t old_state_str = sub->state_str;
Benny Prijonobb0348e2010-10-13 11:13:22 +0000561 pjsip_event dummy_event;
Benny Prijono834aee32006-02-19 01:38:06 +0000562
563 sub->state = state;
564
565 if (state_str && state_str->slen)
566 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
567 else
568 sub->state_str = evsub_state_names[state];
569
Benny Prijono75130572008-07-17 13:53:41 +0000570 if (reason && sub->term_reason.slen==0)
571 pj_strdup(sub->pool, &sub->term_reason, reason);
572
Benny Prijono834aee32006-02-19 01:38:06 +0000573 PJ_LOG(4,(sub->obj_name,
574 "Subscription state changed %.*s --> %.*s",
575 (int)old_state_str.slen,
576 old_state_str.ptr,
577 (int)sub->state_str.slen,
578 sub->state_str.ptr));
579
Benny Prijonobb0348e2010-10-13 11:13:22 +0000580 /* don't call the callback with NULL event, it may crash the app! */
581 if (!event) {
582 PJSIP_EVENT_INIT_USER(dummy_event, 0, 0, 0, 0);
583 event = &dummy_event;
584 }
585
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000586 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000587 (*sub->user.on_evsub_state)(sub, event);
588
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000589 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
590 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
591 {
Benny Prijono834aee32006-02-19 01:38:06 +0000592 if (sub->pending_tsx == 0) {
593 evsub_destroy(sub);
594 }
595 }
596}
597
598
599/*
600 * Timer callback.
601 */
602static void on_timer( pj_timer_heap_t *timer_heap,
603 struct pj_timer_entry *entry)
604{
605 pjsip_evsub *sub;
606 int timer_id;
607
608 PJ_UNUSED_ARG(timer_heap);
609
Benny Prijono9d4469d2007-05-02 05:14:29 +0000610 sub = (pjsip_evsub*) entry->user_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000611
612 pjsip_dlg_inc_lock(sub->dlg);
613
614 timer_id = entry->id;
615 entry->id = TIMER_TYPE_NONE;
616
617 switch (timer_id) {
618
619 case TIMER_TYPE_UAC_REFRESH:
620 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000621 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000622 (*sub->user.on_client_refresh)(sub);
623 } else {
624 pjsip_tx_data *tdata;
625 pj_status_t status;
626
627 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000628 status = pjsip_evsub_initiate(sub, NULL,
Benny Prijono834aee32006-02-19 01:38:06 +0000629 sub->expires->ivalue,
630 &tdata);
631 if (status == PJ_SUCCESS)
632 pjsip_evsub_send_request(sub, tdata);
633 }
634 break;
635
636 case TIMER_TYPE_UAS_TIMEOUT:
637 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000638 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000639 (*sub->user.on_server_timeout)(sub);
640 } else {
641 pjsip_tx_data *tdata;
642 pj_status_t status;
643
644 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
645 "Sending NOTIFY to terminate."));
646 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
647 NULL, &STR_TIMEOUT, &tdata);
648 if (status == PJ_SUCCESS)
649 pjsip_evsub_send_request(sub, tdata);
650 }
651 break;
652
653 case TIMER_TYPE_UAC_TERMINATE:
654 {
Benny Prijono75130572008-07-17 13:53:41 +0000655 pj_str_t timeout = {"timeout", 7};
656
Benny Prijono834aee32006-02-19 01:38:06 +0000657 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
658 "Terminating.."));
Benny Prijono75130572008-07-17 13:53:41 +0000659 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
660 &timeout);
Benny Prijono834aee32006-02-19 01:38:06 +0000661 }
662 break;
663
664 case TIMER_TYPE_UAC_WAIT_NOTIFY:
665 {
666 pjsip_tx_data *tdata;
667 pj_status_t status;
668
669 PJ_LOG(5,(sub->obj_name,
670 "Timeout waiting for subsequent NOTIFY (we did "
671 "send non-2xx response for previous NOTIFY). "
672 "Unsubscribing.."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000673 status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000674 if (status == PJ_SUCCESS)
675 pjsip_evsub_send_request(sub, tdata);
676 }
677 break;
678
679 default:
680 pj_assert(!"Invalid timer id");
681 }
682
683 pjsip_dlg_dec_lock(sub->dlg);
684}
685
686
687/*
688 * Create subscription session, used for both client and notifier.
689 */
690static pj_status_t evsub_create( pjsip_dialog *dlg,
691 pjsip_role_e role,
692 const pjsip_evsub_user *user_cb,
693 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000694 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000695 pjsip_evsub **p_evsub )
696{
697 pjsip_evsub *sub;
698 struct evpkg *pkg;
699 struct dlgsub *dlgsub_head, *dlgsub;
700 pj_status_t status;
701
702 /* Make sure there's package register for the event name: */
703
704 pkg = find_pkg(event);
705 if (pkg == NULL)
706 return PJSIP_SIMPLE_ENOPKG;
707
708
Benny Prijono8eae8382006-08-10 21:44:26 +0000709 /* Must lock dialog before using pool etc. */
710 pjsip_dlg_inc_lock(dlg);
711
Benny Prijono834aee32006-02-19 01:38:06 +0000712 /* Init attributes: */
713
Benny Prijono9d4469d2007-05-02 05:14:29 +0000714 sub = PJ_POOL_ZALLOC_T(dlg->pool, struct pjsip_evsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000715 sub->pool = dlg->pool;
716 sub->endpt = dlg->endpt;
717 sub->dlg = dlg;
718 sub->pkg = pkg;
719 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000720 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000721 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000722 sub->state = PJSIP_EVSUB_STATE_NULL;
723 sub->state_str = evsub_state_names[sub->state];
724 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
Benny Prijono9d4469d2007-05-02 05:14:29 +0000725 sub->accept = (pjsip_accept_hdr*)
726 pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
Sauw Ming5c2f6da2011-02-11 07:39:14 +0000727 pj_list_init(&sub->sub_hdr_list);
Benny Prijono834aee32006-02-19 01:38:06 +0000728
729 sub->timer.user_data = sub;
730 sub->timer.cb = &on_timer;
731
732 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000733 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
734 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000735
736
737 /* Copy callback, if any: */
738 if (user_cb)
739 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
740
741
742 /* Create Event header: */
743 sub->event = pjsip_event_hdr_create(sub->pool);
744 pj_strdup(sub->pool, &sub->event->event_type, event);
745
746
Benny Prijono15d3a702010-01-21 10:04:26 +0000747 /* Check if another subscription has been registered to the dialog. In
748 * that case, just add ourselves to the subscription list, otherwise
749 * create and register a new subscription list.
750 */
751 if (pjsip_dlg_has_usage(dlg, &mod_evsub.mod)) {
752 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
753 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
754 dlgsub->sub = sub;
755 pj_list_push_back(dlgsub_head, dlgsub);
756 } else {
757 dlgsub_head = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
758 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
759 dlgsub->sub = sub;
Benny Prijono834aee32006-02-19 01:38:06 +0000760
Benny Prijono15d3a702010-01-21 10:04:26 +0000761 pj_list_init(dlgsub_head);
762 pj_list_push_back(dlgsub_head, dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000763
764
Benny Prijono15d3a702010-01-21 10:04:26 +0000765 /* Register as dialog usage: */
Benny Prijono834aee32006-02-19 01:38:06 +0000766
Benny Prijono15d3a702010-01-21 10:04:26 +0000767 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
768 if (status != PJ_SUCCESS) {
769 pjsip_dlg_dec_lock(dlg);
770 return status;
771 }
Benny Prijono8eae8382006-08-10 21:44:26 +0000772 }
Benny Prijono834aee32006-02-19 01:38:06 +0000773
Benny Prijono834aee32006-02-19 01:38:06 +0000774 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
775 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
776 dlg->obj_name));
777
778 *p_evsub = sub;
Benny Prijono8eae8382006-08-10 21:44:26 +0000779 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000780
781 return PJ_SUCCESS;
782}
783
784
785
786/*
787 * Create client subscription session.
788 */
789PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
790 const pjsip_evsub_user *user_cb,
791 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000792 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000793 pjsip_evsub **p_evsub)
794{
795 pjsip_evsub *sub;
796 pj_status_t status;
797
798 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
799
800 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000801 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000802 if (status != PJ_SUCCESS)
803 goto on_return;
804
Benny Prijono26ff9062006-02-21 23:47:00 +0000805 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
806 * is not specified.
807 */
808 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
809 pj_create_unique_string(sub->pool, &sub->event->id_param);
810 }
Benny Prijono834aee32006-02-19 01:38:06 +0000811
812 /* Increment dlg session. */
813 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
814
815 /* Done */
816 *p_evsub = sub;
817
818on_return:
819 pjsip_dlg_dec_lock(dlg);
820 return status;
821}
822
823
824/*
825 * Create server subscription session from incoming request.
826 */
827PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
828 const pjsip_evsub_user *user_cb,
829 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000830 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000831 pjsip_evsub **p_evsub)
832{
833 pjsip_evsub *sub;
834 pjsip_transaction *tsx;
835 pjsip_accept_hdr *accept_hdr;
836 pjsip_event_hdr *event_hdr;
837 pjsip_expires_hdr *expires_hdr;
838 pj_status_t status;
839
840 /* Check arguments: */
841 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
842
843 /* MUST be request message: */
844 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
845 PJSIP_ENOTREQUESTMSG);
846
847 /* Transaction MUST have been created (in the dialog) */
848 tsx = pjsip_rdata_get_tsx(rdata);
849 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
850
851 /* No subscription must have been attached to transaction */
852 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
853 PJSIP_ETYPEEXISTS);
854
855 /* Package MUST implement on_rx_refresh */
856 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
857
Benny Prijono26ff9062006-02-21 23:47:00 +0000858 /* Request MUST have "Event" header. We need the Event header to get
859 * the package name (don't want to add more arguments in the function).
860 */
Benny Prijono834aee32006-02-19 01:38:06 +0000861 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000862 pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &STR_EVENT,
863 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000864 if (event_hdr == NULL) {
865 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
866 }
867
868 /* Start locking the mutex: */
869
870 pjsip_dlg_inc_lock(dlg);
871
872 /* Create the session: */
873
874 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000875 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000876 if (status != PJ_SUCCESS)
877 goto on_return;
878
879 /* Just duplicate Event header from the request */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000880 sub->event = (pjsip_event_hdr*) pjsip_hdr_clone(sub->pool, event_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000881
882 /* Set the method: */
883 pjsip_method_copy(sub->pool, &sub->method,
884 &rdata->msg_info.msg->line.req.method);
885
886 /* Update expiration time according to client request: */
887
888 expires_hdr = (pjsip_expires_hdr*)
889 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
890 if (expires_hdr) {
891 sub->expires->ivalue = expires_hdr->ivalue;
892 }
893
894 /* Update time. */
895 update_expires(sub, sub->expires->ivalue);
896
897 /* Update Accept header: */
898
899 accept_hdr = (pjsip_accept_hdr*)
900 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
901 if (accept_hdr)
Benny Prijono9d4469d2007-05-02 05:14:29 +0000902 sub->accept = (pjsip_accept_hdr*)pjsip_hdr_clone(sub->pool,accept_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000903
904 /* We can start the session: */
905
906 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
907 sub->pending_tsx++;
908 tsx->mod_data[mod_evsub.mod.id] = sub;
909
910
911 /* Done. */
912 *p_evsub = sub;
913
914
915on_return:
916 pjsip_dlg_dec_lock(dlg);
917 return status;
918}
919
920
921/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000922 * Forcefully destroy subscription.
923 */
924PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
925 pj_bool_t notify )
926{
927 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
928
929 pjsip_dlg_inc_lock(sub->dlg);
930
Benny Prijonod524e822006-09-22 12:48:18 +0000931 /* I think it's pretty safe to disable this check.
932
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000933 if (sub->pending_tsx) {
934 pj_assert(!"Unable to terminate when there's pending tsx");
935 pjsip_dlg_dec_lock(sub->dlg);
936 return PJ_EINVALIDOP;
937 }
Benny Prijonod524e822006-09-22 12:48:18 +0000938 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000939
940 sub->call_cb = notify;
Benny Prijono75130572008-07-17 13:53:41 +0000941 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, NULL);
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000942
943 pjsip_dlg_dec_lock(sub->dlg);
944 return PJ_SUCCESS;
945}
946
947/*
Benny Prijono834aee32006-02-19 01:38:06 +0000948 * Get subscription state.
949 */
950PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
951{
952 return sub->state;
953}
954
955/*
956 * Get state name.
957 */
958PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
959{
960 return sub->state_str.ptr;
961}
962
Benny Prijono75130572008-07-17 13:53:41 +0000963/*
964 * Get termination reason.
965 */
966PJ_DEF(const pj_str_t*) pjsip_evsub_get_termination_reason(pjsip_evsub *sub)
967{
968 return &sub->term_reason;
969}
Benny Prijono834aee32006-02-19 01:38:06 +0000970
971/*
972 * Initiate client subscription
973 */
974PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
975 const pjsip_method *method,
976 pj_int32_t expires,
977 pjsip_tx_data **p_tdata)
978{
979 pjsip_tx_data *tdata;
980 pj_status_t status;
981
982 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
983
984 /* Use SUBSCRIBE if method is not specified */
985 if (method == NULL)
986 method = &pjsip_subscribe_method;
987
988 pjsip_dlg_inc_lock(sub->dlg);
989
990 /* Update method: */
991 if (sub->state == PJSIP_EVSUB_STATE_NULL)
992 pjsip_method_copy(sub->pool, &sub->method, method);
993
994 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
995 if (status != PJ_SUCCESS)
996 goto on_return;
997
998
999 /* Add Event header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001000 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001001 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1002
1003 /* Update and add expires header: */
1004 if (expires >= 0)
1005 sub->expires->ivalue = expires;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001006 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001007 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1008
Benny Prijono89e52612010-10-01 04:09:08 +00001009 /* Add Supported header (it's optional in RFC 3265, but some event package
1010 * RFC may bring this requirement to SHOULD strength - e.g. RFC 5373)
1011 */
1012 {
1013 const pjsip_hdr *hdr = pjsip_endpt_get_capability(sub->endpt,
1014 PJSIP_H_SUPPORTED,
1015 NULL);
1016 if (hdr) {
1017 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
1018 pjsip_hdr_shallow_clone(tdata->pool, hdr));
1019 }
1020 }
1021
Benny Prijono834aee32006-02-19 01:38:06 +00001022 /* Add Accept header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001023 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001024 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
1025
1026
1027 /* Add Allow-Events header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001028 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001029 pjsip_hdr_shallow_clone(tdata->pool,
1030 mod_evsub.allow_events_hdr));
1031
Sauw Ming5c2f6da2011-02-11 07:39:14 +00001032
1033 /* Add custom headers */
1034 {
1035 const pjsip_hdr *hdr = sub->sub_hdr_list.next;
1036 while (hdr != &sub->sub_hdr_list) {
1037 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
1038 pjsip_hdr_shallow_clone(tdata->pool, hdr));
1039 hdr = hdr->next;
1040 }
1041 }
1042
1043
Benny Prijono834aee32006-02-19 01:38:06 +00001044 *p_tdata = tdata;
1045
1046
1047on_return:
1048
1049 pjsip_dlg_dec_lock(sub->dlg);
1050 return status;
1051}
1052
1053
1054/*
Sauw Ming5c2f6da2011-02-11 07:39:14 +00001055 * Add custom headers.
1056 */
1057PJ_DEF(pj_status_t) pjsip_evsub_add_header( pjsip_evsub *sub,
1058 const pjsip_hdr *hdr_list )
1059{
1060 const pjsip_hdr *hdr;
1061
1062 PJ_ASSERT_RETURN(sub && hdr_list, PJ_EINVAL);
1063
1064 hdr = hdr_list->next;
1065 while (hdr != hdr_list) {
1066 pj_list_push_back(&sub->sub_hdr_list, (pjsip_hdr*)
1067 pjsip_hdr_clone(sub->pool, hdr));
1068 hdr = hdr->next;
1069 }
1070
1071 return PJ_SUCCESS;
1072}
1073
1074
1075/*
Benny Prijono834aee32006-02-19 01:38:06 +00001076 * Accept incoming subscription request.
1077 */
1078PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
1079 pjsip_rx_data *rdata,
1080 int st_code,
1081 const pjsip_hdr *hdr_list )
1082{
1083 pjsip_tx_data *tdata;
1084 pjsip_transaction *tsx;
1085 pj_status_t status;
1086
1087 /* Check arguments */
1088 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
1089
1090 /* Can only be for server subscription: */
1091 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
1092
1093 /* Only expect 2xx status code (for now) */
1094 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
1095
1096 /* Subscription MUST have been attached to the transaction.
1097 * Initial subscription request will be attached on evsub_create_uas(),
1098 * while subsequent requests will be attached in tsx_state()
1099 */
1100 tsx = pjsip_rdata_get_tsx(rdata);
1101 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
1102 PJ_EINVALIDOP);
1103
1104 /* Lock dialog */
1105 pjsip_dlg_inc_lock(sub->dlg);
1106
1107 /* Create response: */
1108 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1109 &tdata);
1110 if (status != PJ_SUCCESS)
1111 goto on_return;
1112
1113
1114 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001115 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001116 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1117
Benny Prijonob0808372006-03-02 21:18:58 +00001118 /* Add additional header, if any. */
1119 if (hdr_list) {
1120 const pjsip_hdr *hdr = hdr_list->next;
1121 while (hdr != hdr_list) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001122 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonob0808372006-03-02 21:18:58 +00001123 pjsip_hdr_clone(tdata->pool, hdr));
1124 hdr = hdr->next;
1125 }
1126 }
Benny Prijono834aee32006-02-19 01:38:06 +00001127
1128 /* Send the response: */
1129 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1130 if (status != PJ_SUCCESS)
1131 goto on_return;
1132
1133
1134on_return:
1135
1136 pjsip_dlg_dec_lock(sub->dlg);
1137 return status;
1138}
1139
1140
1141/*
1142 * Create Subscription-State header based on current server subscription
1143 * state.
1144 */
1145static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1146 pjsip_evsub *sub,
1147 pjsip_evsub_state state,
1148 const pj_str_t *state_str,
1149 const pj_str_t *reason )
1150{
1151 pjsip_sub_state_hdr *sub_state;
1152 pj_time_val now, delay;
1153
1154 /* Get the remaining time before refresh is required */
1155 pj_gettimeofday(&now);
1156 delay = sub->refresh_time;
1157 PJ_TIME_VAL_SUB(delay, now);
1158
1159 /* Create the Subscription-State header */
1160 sub_state = pjsip_sub_state_hdr_create(pool);
1161
1162 /* Fill up the header */
1163 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001164 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001165 case PJSIP_EVSUB_STATE_SENT:
Benny Prijono834aee32006-02-19 01:38:06 +00001166 pj_assert(!"Invalid state!");
1167 /* Treat as pending */
1168
Benny Prijono75130572008-07-17 13:53:41 +00001169 case PJSIP_EVSUB_STATE_ACCEPTED:
Benny Prijono834aee32006-02-19 01:38:06 +00001170 case PJSIP_EVSUB_STATE_PENDING:
1171 sub_state->sub_state = STR_PENDING;
1172 sub_state->expires_param = delay.sec;
1173 break;
1174
1175 case PJSIP_EVSUB_STATE_ACTIVE:
1176 sub_state->sub_state = STR_ACTIVE;
1177 sub_state->expires_param = delay.sec;
1178 break;
1179
1180 case PJSIP_EVSUB_STATE_TERMINATED:
1181 sub_state->sub_state = STR_TERMINATED;
1182 if (reason != NULL)
1183 pj_strdup(pool, &sub_state->reason_param, reason);
1184 break;
1185
1186 case PJSIP_EVSUB_STATE_UNKNOWN:
1187 pj_assert(state_str != NULL);
1188 pj_strdup(pool, &sub_state->sub_state, state_str);
1189 break;
1190 }
1191
1192 return sub_state;
1193}
1194
1195/*
1196 * Create and send NOTIFY request.
1197 */
1198PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1199 pjsip_evsub_state state,
1200 const pj_str_t *state_str,
1201 const pj_str_t *reason,
1202 pjsip_tx_data **p_tdata)
1203{
1204 pjsip_tx_data *tdata;
1205 pjsip_sub_state_hdr *sub_state;
1206 pj_status_t status;
1207
1208 /* Check arguments. */
1209 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1210
1211 /* Lock dialog. */
1212 pjsip_dlg_inc_lock(sub->dlg);
1213
1214 /* Create NOTIFY request */
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001215 status = pjsip_dlg_create_request( sub->dlg, pjsip_get_notify_method(),
1216 -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001217 if (status != PJ_SUCCESS)
1218 goto on_return;
1219
1220 /* Add Event header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001221 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001222 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1223
1224 /* Add Subscription-State header */
1225 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1226 reason);
1227 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1228
1229 /* Add Allow-Events header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001230 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001231 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1232
1233 /* Add Authentication headers. */
1234 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1235
Benny Prijono75130572008-07-17 13:53:41 +00001236 /* Update reason */
1237 if (reason)
1238 pj_strdup(sub->dlg->pool, &sub->term_reason, reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001239
1240 /* Save destination state. */
1241 sub->dst_state = state;
1242 if (state_str)
1243 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1244 else
1245 sub->dst_state_str.slen = 0;
1246
1247
1248 *p_tdata = tdata;
1249
1250on_return:
1251 /* Unlock dialog */
1252 pjsip_dlg_dec_lock(sub->dlg);
1253 return status;
1254}
1255
1256
1257/*
1258 * Create NOTIFY to reflect current status.
1259 */
1260PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1261 pjsip_tx_data **p_tdata )
1262{
1263 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1264 NULL, p_tdata );
1265}
1266
1267
1268/*
1269 * Send request.
1270 */
1271PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1272 pjsip_tx_data *tdata)
1273{
1274 pj_status_t status;
1275
1276 /* Must be request message. */
1277 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1278 PJSIP_ENOTREQUESTMSG);
1279
1280 /* Lock */
1281 pjsip_dlg_inc_lock(sub->dlg);
1282
1283 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001284 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001285 if (status != PJ_SUCCESS)
1286 goto on_return;
1287
1288
1289 /* Special case for NOTIFY:
1290 * The new state was set in pjsip_evsub_notify(), but we apply the
1291 * new state now, when the request was actually sent.
1292 */
1293 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1294 &pjsip_notify_method)==0)
1295 {
1296 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1297 {goto on_return;});
1298
1299 set_state(sub, sub->dst_state,
1300 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
Benny Prijono75130572008-07-17 13:53:41 +00001301 NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001302
1303 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1304 sub->dst_state_str.slen = 0;
1305
1306 }
1307
1308
1309on_return:
1310 pjsip_dlg_dec_lock(sub->dlg);
1311 return status;
1312}
1313
1314
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001315/* Callback to be called to terminate transaction. */
1316static void terminate_timer_cb(pj_timer_heap_t *timer_heap,
1317 struct pj_timer_entry *entry)
1318{
1319 pj_str_t *key;
1320 pjsip_transaction *tsx;
1321
1322 PJ_UNUSED_ARG(timer_heap);
1323
1324 key = (pj_str_t*)entry->user_data;
1325 tsx = pjsip_tsx_layer_find_tsx(key, PJ_FALSE);
1326 /* Chance of race condition here */
1327 if (tsx) {
1328 pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_UPDATED);
1329 }
1330}
1331
Benny Prijono834aee32006-02-19 01:38:06 +00001332
1333/*
1334 * Attach subscription session to newly created transaction, if appropriate.
1335 */
1336static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1337 pjsip_event *event)
1338{
1339 /*
1340 * Newly created transaction will not have subscription session
1341 * attached to it. Find the subscription session from the dialog,
1342 * by matching the Event header.
1343 */
1344 pjsip_dialog *dlg;
1345 pjsip_event_hdr *event_hdr;
1346 pjsip_msg *msg;
1347 struct dlgsub *dlgsub_head, *dlgsub;
1348 pjsip_evsub *sub;
1349
1350 dlg = pjsip_tsx_get_dlg(tsx);
1351 if (!dlg) {
1352 pj_assert(!"Transaction should have a dialog instance!");
1353 return NULL;
1354 }
1355
Benny Prijono26ff9062006-02-21 23:47:00 +00001356
Benny Prijono834aee32006-02-19 01:38:06 +00001357 switch (event->body.tsx_state.type) {
1358 case PJSIP_EVENT_RX_MSG:
1359 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1360 break;
1361 case PJSIP_EVENT_TX_MSG:
1362 msg = event->body.tsx_state.src.tdata->msg;
1363 break;
1364 default:
1365 if (tsx->role == PJSIP_ROLE_UAC)
1366 msg = tsx->last_tx->msg;
1367 else
1368 msg = NULL;
1369 break;
1370 }
1371
1372 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001373 //Note:
1374 // this transaction can be other transaction in the dialog.
1375 // The assertion below probably only valid for dialog that
1376 // only has one event subscription usage.
1377 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001378 return NULL;
1379 }
1380
Benny Prijono9d4469d2007-05-02 05:14:29 +00001381 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001382 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1383 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001384 if (!event_hdr) {
1385 /* Not subscription related message */
1386 return NULL;
1387 }
1388
1389 /* Find the subscription in the dialog, based on the content
1390 * of Event header:
1391 */
1392
Benny Prijono9d4469d2007-05-02 05:14:29 +00001393 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +00001394 if (dlgsub_head == NULL) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001395 dlgsub_head = PJ_POOL_ALLOC_T(dlg->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +00001396 pj_list_init(dlgsub_head);
1397 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1398 }
1399 dlgsub = dlgsub_head->next;
1400
1401 while (dlgsub != dlgsub_head) {
1402
Benny Prijono26ff9062006-02-21 23:47:00 +00001403 if (pj_stricmp(&dlgsub->sub->event->event_type,
1404 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001405 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001406 /* Event type matched.
1407 * Check if event ID matched too.
1408 */
1409 if (pj_strcmp(&dlgsub->sub->event->id_param,
1410 &event_hdr->id_param)==0)
1411 {
1412
1413 break;
1414
1415 }
1416 /*
1417 * Otherwise if it is an UAC subscription, AND
1418 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1419 * the session's event id is NULL, AND
1420 * the incoming request is NOTIFY with event ID, then
1421 * we consider it as a match, and update the
1422 * session's event id.
1423 */
1424 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1425 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1426 dlgsub->sub->event->id_param.slen==0 &&
1427 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1428 {
1429 /* Update session's event id. */
1430 pj_strdup(dlgsub->sub->pool,
1431 &dlgsub->sub->event->id_param,
1432 &event_hdr->id_param);
1433
1434 break;
1435 }
Benny Prijono834aee32006-02-19 01:38:06 +00001436 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001437
1438
1439
Benny Prijono834aee32006-02-19 01:38:06 +00001440 dlgsub = dlgsub->next;
1441 }
1442
Benny Prijonoce00fa02009-06-30 13:47:44 +00001443 /* Note:
1444 * the second condition is for http://trac.pjsip.org/repos/ticket/911
1445 */
1446 if (dlgsub == dlgsub_head ||
1447 (dlgsub->sub &&
1448 pjsip_evsub_get_state(dlgsub->sub)==PJSIP_EVSUB_STATE_TERMINATED))
1449 {
1450 const char *reason_msg =
1451 (dlgsub == dlgsub_head ? "Subscription Does Not Exist" :
1452 "Subscription already terminated");
1453
Benny Prijono834aee32006-02-19 01:38:06 +00001454 /* This could be incoming request to create new subscription */
1455 PJ_LOG(4,(THIS_FILE,
Benny Prijonoce00fa02009-06-30 13:47:44 +00001456 "%s for %.*s, event=%.*s;id=%.*s",
1457 reason_msg,
Benny Prijono26ff9062006-02-21 23:47:00 +00001458 (int)tsx->method.name.slen,
1459 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001460 (int)event_hdr->event_type.slen,
1461 event_hdr->event_type.ptr,
1462 (int)event_hdr->id_param.slen,
1463 event_hdr->id_param.ptr));
1464
1465 /* If this is an incoming NOTIFY, reject with 481 */
1466 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1467 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1468 {
Benny Prijonoce00fa02009-06-30 13:47:44 +00001469 pj_str_t reason;
Benny Prijono834aee32006-02-19 01:38:06 +00001470 pjsip_tx_data *tdata;
1471 pj_status_t status;
1472
Benny Prijonoce00fa02009-06-30 13:47:44 +00001473 pj_cstr(&reason, reason_msg);
Benny Prijono834aee32006-02-19 01:38:06 +00001474 status = pjsip_dlg_create_response(dlg,
1475 event->body.tsx_state.src.rdata,
1476 481, &reason,
1477 &tdata);
1478 if (status == PJ_SUCCESS) {
1479 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1480 }
1481 }
1482 return NULL;
1483 }
1484
1485 /* Found! */
1486 sub = dlgsub->sub;
1487
1488 /* Attach session to the transaction */
1489 tsx->mod_data[mod_evsub.mod.id] = sub;
1490 sub->pending_tsx++;
1491
Benny Prijono69b98ab2006-03-03 10:23:35 +00001492 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1493 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1494 * transaction is started while previous one still alive, terminate
1495 * the older one.
1496 *
1497 * Sample scenario:
1498 * - subscribe sent to destination that doesn't exist, transaction
1499 * is still retransmitting request, then unsubscribe is sent.
1500 */
1501 if (tsx->role == PJSIP_ROLE_UAC &&
1502 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001503 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1504 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001505 {
1506
1507 if (sub->pending_sub &&
1508 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1509 {
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001510 pj_timer_entry *timer;
1511 pj_str_t *key;
1512 pj_time_val timeout = {0, 0};
1513
Benny Prijono69b98ab2006-03-03 10:23:35 +00001514 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001515 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001516
1517 /* By convention, we use 490 (Request Updated) status code.
1518 * When transaction handler (below) see this status code, it
1519 * will ignore the transaction.
1520 */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001521 /* This unfortunately may cause deadlock, because at the moment
1522 * we are holding dialog's mutex. If a response to this
1523 * transaction is in progress in another thread, that thread
1524 * will deadlock when trying to acquire dialog mutex, because
1525 * it is holding the transaction mutex.
1526 *
1527 * So the solution is to register timer to kill this transaction.
1528 */
1529 //pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1530 timer = PJ_POOL_ZALLOC_T(dlg->pool, pj_timer_entry);
1531 key = PJ_POOL_ALLOC_T(dlg->pool, pj_str_t);
1532 pj_strdup(dlg->pool, key, &sub->pending_sub->transaction_key);
1533 timer->cb = &terminate_timer_cb;
1534 timer->user_data = key;
1535
1536 pjsip_endpt_schedule_timer(dlg->endpt, timer, &timeout);
Benny Prijono69b98ab2006-03-03 10:23:35 +00001537 }
1538
1539 sub->pending_sub = tsx;
1540
Benny Prijono69b98ab2006-03-03 10:23:35 +00001541 }
1542
Benny Prijono834aee32006-02-19 01:38:06 +00001543 return sub;
1544}
1545
1546
1547/*
1548 * Create response, adding custome headers and msg body.
1549 */
1550static pj_status_t create_response( pjsip_evsub *sub,
1551 pjsip_rx_data *rdata,
1552 int st_code,
1553 const pj_str_t *st_text,
1554 const pjsip_hdr *res_hdr,
1555 const pjsip_msg_body *body,
1556 pjsip_tx_data **p_tdata)
1557{
1558 pjsip_tx_data *tdata;
1559 pjsip_hdr *hdr;
1560 pj_status_t status;
1561
1562 status = pjsip_dlg_create_response(sub->dlg, rdata,
1563 st_code, st_text, &tdata);
1564 if (status != PJ_SUCCESS)
1565 return status;
1566
1567 *p_tdata = tdata;
1568
1569 /* Add response headers. */
1570 hdr = res_hdr->next;
1571 while (hdr != res_hdr) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001572 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001573 pjsip_hdr_clone(tdata->pool, hdr));
1574 hdr = hdr->next;
1575 }
1576
1577 /* Add msg body, if any */
1578 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001579 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1580 if (tdata->msg->body == NULL) {
1581
1582 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1583
Benny Prijono834aee32006-02-19 01:38:06 +00001584 /* Ignore */
1585 return PJ_SUCCESS;
1586 }
1587 }
1588
1589 return PJ_SUCCESS;
1590}
1591
1592/*
1593 * Get subscription state from the value of Subscription-State header.
1594 */
1595static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1596 pjsip_evsub_state *state,
1597 pj_str_t **state_str )
1598{
1599 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1600
1601 *state = PJSIP_EVSUB_STATE_TERMINATED;
1602 *state_str = NULL;
1603
1604 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1605
1606 *state = PJSIP_EVSUB_STATE_ACTIVE;
1607 *state_str = NULL;
1608
1609 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1610
1611 *state = PJSIP_EVSUB_STATE_PENDING;
1612 *state_str = NULL;
1613
1614 } else {
1615
1616 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1617 *state_str = &sub_state->sub_state;
1618
1619 }
1620}
1621
1622/*
1623 * Transaction event processing by UAC, after subscription is sent.
1624 */
1625static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1626 pjsip_event *event )
1627{
1628
Benny Prijono736d0f72006-09-13 22:45:38 +00001629 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1630 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1631 {
Benny Prijono834aee32006-02-19 01:38:06 +00001632
1633 /* Received response to outgoing request that establishes/refresh
1634 * subscription.
1635 */
1636
1637 /* First time initial request is sent. */
1638 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1639 tsx->state == PJSIP_TSX_STATE_CALLING)
1640 {
Benny Prijono75130572008-07-17 13:53:41 +00001641 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001642 return;
1643 }
1644
1645 /* Only interested in final response */
1646 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1647 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1648 {
1649 return;
1650 }
1651
Benny Prijono1d8d6082006-04-29 12:38:25 +00001652 /* Clear pending subscription */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001653 if (tsx == sub->pending_sub) {
Benny Prijono1d8d6082006-04-29 12:38:25 +00001654 sub->pending_sub = NULL;
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001655 } else if (sub->pending_sub != NULL) {
1656 /* This SUBSCRIBE transaction has been "renewed" with another
1657 * SUBSCRIBE, so we can just ignore this. For example, user
1658 * sent SUBSCRIBE followed immediately with UN-SUBSCRIBE.
1659 */
1660 return;
1661 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001662
Benny Prijono834aee32006-02-19 01:38:06 +00001663 /* Handle authentication. */
1664 if (tsx->status_code==401 || tsx->status_code==407) {
1665 pjsip_tx_data *tdata;
1666 pj_status_t status;
1667
1668 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1669 /* Previously failed transaction has terminated */
1670 return;
1671 }
1672
1673 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1674 event->body.tsx_state.src.rdata,
1675 tsx->last_tx, &tdata);
1676 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001677 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001678
1679 if (status != PJ_SUCCESS) {
1680 /* Authentication failed! */
1681 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001682 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001683 return;
1684 }
1685
1686 return;
1687 }
1688
1689 if (tsx->status_code/100 == 2) {
1690
1691 /* Successfull SUBSCRIBE request!
1692 * This could be:
1693 * - response to initial SUBSCRIBE request
1694 * - response to subsequent refresh
1695 * - response to unsubscription
1696 */
1697
1698 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1699 /* Ignore; this transaction has been processed before */
1700 return;
1701 }
1702
1703 /* Update UAC refresh time, if response contains Expires header,
1704 * only when we're not unsubscribing.
1705 */
1706 if (sub->expires->ivalue != 0) {
1707 pjsip_msg *msg;
1708 pjsip_expires_hdr *expires;
1709
1710 msg = event->body.tsx_state.src.rdata->msg_info.msg;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001711 expires = (pjsip_expires_hdr*)
1712 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001713 if (expires) {
1714 sub->expires->ivalue = expires->ivalue;
1715 }
1716 }
1717
1718 /* Update time */
1719 update_expires(sub, sub->expires->ivalue);
1720
1721 /* Start UAC refresh timer, only when we're not unsubscribing */
1722 if (sub->expires->ivalue != 0) {
1723 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1724 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1725
1726 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1727 timeout));
1728 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1729
1730 } else {
1731 /* Otherwise set timer to terminate client subscription when
1732 * NOTIFY to end subscription is not received.
1733 */
1734 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1735 }
1736
1737 /* Set state, if necessary */
1738 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1739 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
Benny Prijono75130572008-07-17 13:53:41 +00001740 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001741 }
1742
1743 } else {
1744
1745 /* Failed SUBSCRIBE request!
1746 *
1747 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1748 * other than 481, the subscription is still considered valid for
1749 * the duration of the last Expires.
1750 *
1751 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1752 * expiration, theoritically the expiration is still valid for the
1753 * next 5 seconds even when we receive non-481 failed response.
1754 *
1755 * Ah, what the heck!
1756 *
1757 * Just terminate now!
1758 *
1759 */
1760
1761 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1762 /* Ignore, has been handled before */
1763 return;
1764 }
1765
Benny Prijono69b98ab2006-03-03 10:23:35 +00001766 /* Ignore 490 (Request Updated) status.
1767 * This happens when application sends SUBSCRIBE/REFER while
1768 * another one is still in progress.
1769 */
1770 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1771 return;
1772 }
1773
Benny Prijono834aee32006-02-19 01:38:06 +00001774 /* Kill any timer. */
1775 set_timer(sub, TIMER_TYPE_NONE, 0);
1776
1777 /* Set state to TERMINATED */
1778 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001779 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001780
1781 }
1782
1783 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1784
1785 /* Incoming NOTIFY.
1786 * This can be the result of:
1787 * - Initial subscription response
1788 * - UAS updating the resource info.
1789 * - Unsubscription response.
1790 */
1791 int st_code = 200;
1792 pj_str_t *st_text = NULL;
1793 pjsip_hdr res_hdr;
1794 pjsip_msg_body *body = NULL;
1795
1796 pjsip_rx_data *rdata;
1797 pjsip_msg *msg;
1798 pjsip_sub_state_hdr *sub_state;
1799
1800 pjsip_evsub_state new_state;
1801 pj_str_t *new_state_str;
1802
1803 pjsip_tx_data *tdata;
1804 pj_status_t status;
1805 int next_refresh;
1806
1807 /* Only want to handle initial NOTIFY receive event. */
1808 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1809 return;
1810
1811
1812 rdata = event->body.tsx_state.src.rdata;
1813 msg = rdata->msg_info.msg;
1814
1815 pj_list_init(&res_hdr);
1816
1817 /* Get subscription state header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001818 sub_state = (pjsip_sub_state_hdr*)
1819 pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001820 if (sub_state == NULL) {
1821
1822 pjsip_warning_hdr *warn_hdr;
1823 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1824
1825 /* Bad request! Add warning header. */
1826 st_code = PJSIP_SC_BAD_REQUEST;
1827 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1828 pjsip_endpt_name(sub->endpt),
1829 &warn_text);
1830 pj_list_push_back(&res_hdr, warn_hdr);
1831 }
1832
1833 /* Call application registered callback to handle incoming NOTIFY,
1834 * if any.
1835 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001836 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001837 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1838 &res_hdr, &body);
1839
1840 /* Application MUST specify final response! */
1841 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1842
1843 /* Must be a valid status code */
1844 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1845 }
1846
1847
1848 /* If non-2xx should be returned, then send the response.
1849 * No need to update server subscription state.
1850 */
1851 if (st_code >= 300) {
1852 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1853 body, &tdata);
1854 if (status == PJ_SUCCESS) {
1855 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1856 }
1857
1858 /* Start timer to terminate subscription, just in case server
1859 * is not able to generate NOTIFY to our response.
1860 */
1861 if (status == PJ_SUCCESS) {
1862 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1863 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1864 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001865 char errmsg[PJ_ERR_MSG_SIZE];
1866 pj_str_t reason;
1867
1868 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1869 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
1870 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001871 }
1872
1873 return;
1874 }
1875
1876 /* Update expiration from the value of expires param in
1877 * Subscription-State header, but ONLY when subscription state
1878 * is "active" or "pending", AND the header contains expires param.
1879 */
1880 if (sub->expires->ivalue != 0 &&
1881 sub_state->expires_param >= 0 &&
1882 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1883 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1884 {
1885 next_refresh = sub_state->expires_param;
1886
1887 } else {
1888 next_refresh = sub->expires->ivalue;
1889 }
1890
1891 /* Update time */
1892 update_expires(sub, next_refresh);
1893
1894 /* Start UAC refresh timer, only when we're not unsubscribing */
1895 if (sub->expires->ivalue != 0) {
1896 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1897 next_refresh - TIME_UAC_REFRESH : next_refresh;
1898
1899 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1900 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1901 }
1902
1903 /* Find out the state */
1904 get_hdr_state(sub_state, &new_state, &new_state_str);
1905
1906 /* Send response. */
1907 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1908 body, &tdata);
1909 if (status == PJ_SUCCESS)
1910 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1911
1912 /* Set the state */
1913 if (status == PJ_SUCCESS) {
Benny Prijono75130572008-07-17 13:53:41 +00001914 set_state(sub, new_state, new_state_str, event,
1915 &sub_state->reason_param);
Benny Prijono834aee32006-02-19 01:38:06 +00001916 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001917 char errmsg[PJ_ERR_MSG_SIZE];
1918 pj_str_t reason;
1919
1920 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1921 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
1922 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001923 }
1924
1925
1926 } else {
1927
1928 /*
1929 * Unexpected method!
1930 */
1931 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1932 (int)tsx->method.name.slen, tsx->method.name.ptr));
1933 }
1934}
1935
1936
1937/*
1938 * Transaction event processing by UAS, after subscription is accepted.
1939 */
1940static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1941 pjsip_event *event)
1942{
1943
Benny Prijono736d0f72006-09-13 22:45:38 +00001944 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1945 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1946 {
Benny Prijono834aee32006-02-19 01:38:06 +00001947
1948 /*
1949 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1950 *
1951 */
1952 pjsip_rx_data *rdata;
1953 pjsip_event_hdr *event_hdr;
1954 pjsip_expires_hdr *expires;
1955 pjsip_msg *msg;
1956 pjsip_tx_data *tdata;
1957 int st_code = 200;
1958 pj_str_t *st_text = NULL;
1959 pjsip_hdr res_hdr;
1960 pjsip_msg_body *body = NULL;
1961 pjsip_evsub_state old_state;
1962 pj_str_t old_state_str;
Benny Prijono75130572008-07-17 13:53:41 +00001963 pj_str_t reason = { NULL, 0 };
Benny Prijono834aee32006-02-19 01:38:06 +00001964 pj_status_t status;
1965
1966
1967 /* Only wants to handle the first event when the request is
1968 * received.
1969 */
1970 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1971 return;
1972
1973 rdata = event->body.tsx_state.src.rdata;
1974 msg = rdata->msg_info.msg;
1975
1976 /* Set expiration time based on client request (in Expires header),
1977 * or package default expiration time.
1978 */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001979 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001980 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1981 &STR_EVENT, NULL);
Benny Prijono9d4469d2007-05-02 05:14:29 +00001982 expires = (pjsip_expires_hdr*)
1983 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001984 if (event_hdr && expires) {
1985 struct evpkg *evpkg;
1986
1987 evpkg = find_pkg(&event_hdr->event_type);
1988 if (evpkg) {
1989 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1990 sub->expires->ivalue = expires->ivalue;
1991 else
1992 sub->expires->ivalue = evpkg->pkg_expires;
1993 }
1994 }
1995
1996 /* Update time (before calling on_rx_refresh, since application
1997 * will send NOTIFY.
1998 */
1999 update_expires(sub, sub->expires->ivalue);
2000
2001
2002 /* Save old state.
2003 * If application respond with non-2xx, revert to old state.
2004 */
2005 old_state = sub->state;
2006 old_state_str = sub->state_str;
2007
2008 if (sub->expires->ivalue == 0) {
2009 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
2010 sub->state_str = evsub_state_names[sub->state];
2011 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
2012 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
2013 sub->state_str = evsub_state_names[sub->state];
2014 }
2015
2016 /* Call application's on_rx_refresh, just in case it wants to send
2017 * response other than 200 (OK)
2018 */
2019 pj_list_init(&res_hdr);
2020
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002021 if (sub->user.on_rx_refresh && sub->call_cb) {
2022 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
2023 &res_hdr, &body);
2024 }
Benny Prijono834aee32006-02-19 01:38:06 +00002025
2026 /* Application MUST specify final response! */
2027 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
2028
2029 /* Must be a valid status code */
2030 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
2031
2032
2033 /* Create and send response */
2034 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
2035 body, &tdata);
2036 if (status == PJ_SUCCESS) {
2037 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00002038 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00002039 pjsip_hdr_shallow_clone(tdata->pool,
2040 sub->expires));
2041
2042 /* Send */
2043 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
2044 }
2045
2046 /* Update state or revert state */
2047 if (st_code/100==2) {
2048
2049 if (sub->expires->ivalue == 0) {
Benny Prijono75130572008-07-17 13:53:41 +00002050 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00002051 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
Benny Prijono75130572008-07-17 13:53:41 +00002052 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00002053 }
2054
2055 /* Set UAS timeout timer, when state is not terminated. */
2056 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
2057 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
2058 sub->expires->ivalue));
2059 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
2060 sub->expires->ivalue);
2061 }
2062
2063 } else {
2064 sub->state = old_state;
2065 sub->state_str = old_state_str;
2066 }
2067
2068
2069 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
2070
2071 /* Handle authentication */
2072 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
2073 (tsx->status_code==401 || tsx->status_code==407))
2074 {
2075 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
2076 pjsip_tx_data *tdata;
2077 pj_status_t status;
2078
2079 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
2080 tsx->last_tx, &tdata);
2081 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00002082 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00002083
2084 if (status != PJ_SUCCESS) {
2085 /* Can't authenticate. Terminate session (?) */
Benny Prijono75130572008-07-17 13:53:41 +00002086 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
2087 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002088 return;
Benny Prijono834aee32006-02-19 01:38:06 +00002089 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002090
2091 }
2092 /*
2093 * Terminate event usage if we receive 481, 408, and 7 class
2094 * responses.
2095 */
2096 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
2097 (tsx->status_code==481 || tsx->status_code==408 ||
2098 tsx->status_code/100 == 7))
2099 {
Benny Prijono75130572008-07-17 13:53:41 +00002100 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
2101 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002102 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00002103 }
Benny Prijono834aee32006-02-19 01:38:06 +00002104
2105 } else {
2106
2107 /*
2108 * Unexpected method!
2109 */
2110 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
2111 (int)tsx->method.name.slen, tsx->method.name.ptr));
2112
2113 }
2114}
2115
2116
2117/*
2118 * Notification when transaction state has changed!
2119 */
2120static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
2121{
2122 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
2123
2124 if (sub == NULL) {
2125 sub = on_new_transaction(tsx, event);
2126 if (sub == NULL)
2127 return;
2128 }
2129
2130
2131 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002132 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00002133 (*sub->user.on_tsx_state)(sub, tsx, event);
2134
2135
2136 /* Process the event: */
2137
2138 if (sub->role == PJSIP_ROLE_UAC) {
2139 on_tsx_state_uac(sub, tsx, event);
2140 } else {
2141 on_tsx_state_uas(sub, tsx, event);
2142 }
2143
2144
2145 /* Check transaction TERMINATE event */
2146 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
2147
2148 --sub->pending_tsx;
2149
2150 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
2151 sub->pending_tsx == 0)
2152 {
2153 evsub_destroy(sub);
2154 }
2155
2156 }
2157}
2158
2159