blob: c52b0b5f6d0e2496a031f922351c037eb13edecb [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. */
230
231 pj_time_val refresh_time; /**< Time to refresh. */
232 pj_timer_entry timer; /**< Internal timer. */
233 int pending_tsx; /**< Number of pending transactions.*/
Benny Prijono69b98ab2006-03-03 10:23:35 +0000234 pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */
Benny Prijono834aee32006-02-19 01:38:06 +0000235
236 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
237};
238
239
240/*
241 * This is the structure that will be "attached" to dialog.
242 * The purpose is to allow multiple subscriptions inside a dialog.
243 */
244struct dlgsub
245{
246 PJ_DECL_LIST_MEMBER(struct dlgsub);
247 pjsip_evsub *sub;
248};
249
250
251/* Static vars. */
252static const pj_str_t STR_EVENT = { "Event", 5 };
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000253static const pj_str_t STR_EVENT_S = { "Event", 5 };
Benny Prijono834aee32006-02-19 01:38:06 +0000254static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
255static const pj_str_t STR_TERMINATED = { "terminated", 10 };
256static const pj_str_t STR_ACTIVE = { "active", 6 };
257static const pj_str_t STR_PENDING = { "pending", 7 };
258static const pj_str_t STR_TIMEOUT = { "timeout", 7};
259
Benny Prijono26ff9062006-02-21 23:47:00 +0000260
Benny Prijono834aee32006-02-19 01:38:06 +0000261/*
262 * On unload module.
263 */
264static pj_status_t mod_evsub_unload(void)
265{
266 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
267 mod_evsub.pool = NULL;
268
269 return PJ_SUCCESS;
270}
271
Benny Prijono26ff9062006-02-21 23:47:00 +0000272/* Proto for pjsipsimple_strerror().
273 * Defined in errno.c
274 */
275PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode,
276 char *buf, pj_size_t bufsize );
277
Benny Prijono834aee32006-02-19 01:38:06 +0000278/*
279 * Init and register module.
280 */
281PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
282{
283 pj_status_t status;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000284 pj_str_t method_tags[] = {
285 { "SUBSCRIBE", 9},
286 { "NOTIFY", 6}
287 };
Benny Prijono834aee32006-02-19 01:38:06 +0000288
Benny Prijono26ff9062006-02-21 23:47:00 +0000289 pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
290 &pjsipsimple_strerror);
291
Benny Prijono834aee32006-02-19 01:38:06 +0000292 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
293 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
294
295 /* Keep endpoint for future reference: */
296 mod_evsub.endpt = endpt;
297
298 /* Init event package list: */
299 pj_list_init(&mod_evsub.pkg_list);
300
301 /* Create pool: */
Benny Prijono10d8dbd2008-07-13 13:12:36 +0000302 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 512, 512);
Benny Prijono834aee32006-02-19 01:38:06 +0000303 if (!mod_evsub.pool)
304 return PJ_ENOMEM;
305
306 /* Register module: */
307 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
308 if (status != PJ_SUCCESS)
309 goto on_error;
310
311 /* Create Allow-Events header: */
312 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
313
314 /* Register SIP-event specific headers parser: */
315 pjsip_evsub_init_parser();
316
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000317 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
318 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
319 2, method_tags);
320
321 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000322 return PJ_SUCCESS;
323
324on_error:
325 if (mod_evsub.pool) {
326 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
327 mod_evsub.pool = NULL;
328 }
329 mod_evsub.endpt = NULL;
330 return status;
331}
332
333
334/*
335 * Get the instance of the module.
336 */
337PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
338{
339 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
340
341 return &mod_evsub.mod;
342}
343
344
345/*
346 * Get the event subscription instance in the transaction.
347 */
348PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
349{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000350 return (pjsip_evsub*) tsx->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000351}
352
353
354/*
355 * Set event subscription's module data.
356 */
357PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
358 void *data )
359{
360 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
361 sub->mod_data[mod_id] = data;
362}
363
364
365/*
366 * Get event subscription's module data.
367 */
368PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
369{
370 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
371 return sub->mod_data[mod_id];
372}
373
374
375/*
376 * Find registered event package with matching name.
377 */
378static struct evpkg* find_pkg(const pj_str_t *event_name)
379{
380 struct evpkg *pkg;
381
382 pkg = mod_evsub.pkg_list.next;
383 while (pkg != &mod_evsub.pkg_list) {
384
385 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
386 return pkg;
387 }
388
389 pkg = pkg->next;
390 }
391
392 return NULL;
393}
394
395/*
396 * Register an event package
397 */
398PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
399 const pj_str_t *event_name,
400 unsigned expires,
401 unsigned accept_cnt,
402 const pj_str_t accept[])
403{
404 struct evpkg *pkg;
405 unsigned i;
406
407 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
408 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
409 PJ_ETOOMANY);
410
Benny Prijonoc2456cc2007-10-25 03:19:58 +0000411 /* Make sure evsub module has been initialized */
412 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, PJ_EINVALIDOP);
413
Benny Prijono834aee32006-02-19 01:38:06 +0000414 /* Make sure no module with the specified name already registered: */
415
416 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
417
418
419 /* Create new event package: */
420
Benny Prijono9d4469d2007-05-02 05:14:29 +0000421 pkg = PJ_POOL_ALLOC_T(mod_evsub.pool, struct evpkg);
Benny Prijono834aee32006-02-19 01:38:06 +0000422 pkg->pkg_mod = pkg_mod;
423 pkg->pkg_expires = expires;
424 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
425
426 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
427 pkg->pkg_accept->count = accept_cnt;
428 for (i=0; i<accept_cnt; ++i) {
429 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
430 }
431
432 /* Add to package list: */
433
434 pj_list_push_back(&mod_evsub.pkg_list, pkg);
435
436 /* Add to Allow-Events header: */
437
438 if (mod_evsub.allow_events_hdr->count !=
439 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
440 {
441 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
442 pkg->pkg_name;
443 ++mod_evsub.allow_events_hdr->count;
444 }
445
Benny Prijono56315612006-07-18 14:39:40 +0000446 /* Add to endpoint's Accept header */
447 pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod,
448 PJSIP_H_ACCEPT, NULL,
449 pkg->pkg_accept->count,
450 pkg->pkg_accept->values);
451
Benny Prijono834aee32006-02-19 01:38:06 +0000452
453 /* Done */
454
455 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
456 (int)event_name->slen, event_name->ptr,
457 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
458
459 return PJ_SUCCESS;
460}
461
462
Benny Prijono56315612006-07-18 14:39:40 +0000463/*
464 * Retrieve Allow-Events header
465 */
466PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m)
467{
468 struct mod_evsub *mod;
469
470 if (m == NULL)
471 m = pjsip_evsub_instance();
472
473 mod = (struct mod_evsub*)m;
474
475 return (pjsip_hdr*) mod->allow_events_hdr;
476}
477
Benny Prijono834aee32006-02-19 01:38:06 +0000478
479/*
480 * Update expiration time.
481 */
482static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
483{
484 pj_gettimeofday(&sub->refresh_time);
485 sub->refresh_time.sec += interval;
486}
487
488
489/*
490 * Schedule timer.
491 */
492static void set_timer( pjsip_evsub *sub, int timer_id,
493 pj_int32_t seconds)
494{
495 if (sub->timer.id != TIMER_TYPE_NONE) {
496 PJ_LOG(5,(sub->obj_name, "%s %s timer",
497 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
498 timer_names[sub->timer.id]));
499 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
500 sub->timer.id = TIMER_TYPE_NONE;
501 }
502
503 if (timer_id != TIMER_TYPE_NONE) {
504 pj_time_val timeout;
505
506 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000507 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
508 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000509
510 timeout.sec = seconds;
511 timeout.msec = 0;
512 sub->timer.id = timer_id;
513
514 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
515
516 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
517 timer_names[sub->timer.id], timeout.sec));
518 }
519}
520
521
522/*
523 * Destroy session.
524 */
525static void evsub_destroy( pjsip_evsub *sub )
526{
527 struct dlgsub *dlgsub_head, *dlgsub;
528
529 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
530
531 /* Kill timer */
532 set_timer(sub, TIMER_TYPE_NONE, 0);
533
Benny Prijono9d4469d2007-05-02 05:14:29 +0000534 /* Remove this session from dialog's list of subscription */
535 dlgsub_head = (struct dlgsub *) sub->dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000536 dlgsub = dlgsub_head->next;
537 while (dlgsub != dlgsub_head) {
538
539 if (dlgsub->sub == sub) {
540 pj_list_erase(dlgsub);
541 break;
542 }
543
544 dlgsub = dlgsub->next;
545 }
546
547 /* Decrement dialog's session */
548 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
549}
550
551/*
552 * Set subscription session state.
553 */
554static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
Benny Prijono75130572008-07-17 13:53:41 +0000555 const pj_str_t *state_str, pjsip_event *event,
556 const pj_str_t *reason)
Benny Prijono834aee32006-02-19 01:38:06 +0000557{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000558 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000559 pj_str_t old_state_str = sub->state_str;
560
561 sub->state = state;
562
563 if (state_str && state_str->slen)
564 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
565 else
566 sub->state_str = evsub_state_names[state];
567
Benny Prijono75130572008-07-17 13:53:41 +0000568 if (reason && sub->term_reason.slen==0)
569 pj_strdup(sub->pool, &sub->term_reason, reason);
570
Benny Prijono834aee32006-02-19 01:38:06 +0000571 PJ_LOG(4,(sub->obj_name,
572 "Subscription state changed %.*s --> %.*s",
573 (int)old_state_str.slen,
574 old_state_str.ptr,
575 (int)sub->state_str.slen,
576 sub->state_str.ptr));
577
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000578 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000579 (*sub->user.on_evsub_state)(sub, event);
580
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000581 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
582 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
583 {
Benny Prijono834aee32006-02-19 01:38:06 +0000584 if (sub->pending_tsx == 0) {
585 evsub_destroy(sub);
586 }
587 }
588}
589
590
591/*
592 * Timer callback.
593 */
594static void on_timer( pj_timer_heap_t *timer_heap,
595 struct pj_timer_entry *entry)
596{
597 pjsip_evsub *sub;
598 int timer_id;
599
600 PJ_UNUSED_ARG(timer_heap);
601
Benny Prijono9d4469d2007-05-02 05:14:29 +0000602 sub = (pjsip_evsub*) entry->user_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000603
604 pjsip_dlg_inc_lock(sub->dlg);
605
606 timer_id = entry->id;
607 entry->id = TIMER_TYPE_NONE;
608
609 switch (timer_id) {
610
611 case TIMER_TYPE_UAC_REFRESH:
612 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000613 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000614 (*sub->user.on_client_refresh)(sub);
615 } else {
616 pjsip_tx_data *tdata;
617 pj_status_t status;
618
619 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000620 status = pjsip_evsub_initiate(sub, NULL,
Benny Prijono834aee32006-02-19 01:38:06 +0000621 sub->expires->ivalue,
622 &tdata);
623 if (status == PJ_SUCCESS)
624 pjsip_evsub_send_request(sub, tdata);
625 }
626 break;
627
628 case TIMER_TYPE_UAS_TIMEOUT:
629 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000630 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000631 (*sub->user.on_server_timeout)(sub);
632 } else {
633 pjsip_tx_data *tdata;
634 pj_status_t status;
635
636 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
637 "Sending NOTIFY to terminate."));
638 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
639 NULL, &STR_TIMEOUT, &tdata);
640 if (status == PJ_SUCCESS)
641 pjsip_evsub_send_request(sub, tdata);
642 }
643 break;
644
645 case TIMER_TYPE_UAC_TERMINATE:
646 {
Benny Prijono75130572008-07-17 13:53:41 +0000647 pj_str_t timeout = {"timeout", 7};
648
Benny Prijono834aee32006-02-19 01:38:06 +0000649 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
650 "Terminating.."));
Benny Prijono75130572008-07-17 13:53:41 +0000651 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
652 &timeout);
Benny Prijono834aee32006-02-19 01:38:06 +0000653 }
654 break;
655
656 case TIMER_TYPE_UAC_WAIT_NOTIFY:
657 {
658 pjsip_tx_data *tdata;
659 pj_status_t status;
660
661 PJ_LOG(5,(sub->obj_name,
662 "Timeout waiting for subsequent NOTIFY (we did "
663 "send non-2xx response for previous NOTIFY). "
664 "Unsubscribing.."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000665 status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000666 if (status == PJ_SUCCESS)
667 pjsip_evsub_send_request(sub, tdata);
668 }
669 break;
670
671 default:
672 pj_assert(!"Invalid timer id");
673 }
674
675 pjsip_dlg_dec_lock(sub->dlg);
676}
677
678
679/*
680 * Create subscription session, used for both client and notifier.
681 */
682static pj_status_t evsub_create( pjsip_dialog *dlg,
683 pjsip_role_e role,
684 const pjsip_evsub_user *user_cb,
685 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000686 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000687 pjsip_evsub **p_evsub )
688{
689 pjsip_evsub *sub;
690 struct evpkg *pkg;
691 struct dlgsub *dlgsub_head, *dlgsub;
692 pj_status_t status;
693
694 /* Make sure there's package register for the event name: */
695
696 pkg = find_pkg(event);
697 if (pkg == NULL)
698 return PJSIP_SIMPLE_ENOPKG;
699
700
Benny Prijono8eae8382006-08-10 21:44:26 +0000701 /* Must lock dialog before using pool etc. */
702 pjsip_dlg_inc_lock(dlg);
703
Benny Prijono834aee32006-02-19 01:38:06 +0000704 /* Init attributes: */
705
Benny Prijono9d4469d2007-05-02 05:14:29 +0000706 sub = PJ_POOL_ZALLOC_T(dlg->pool, struct pjsip_evsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000707 sub->pool = dlg->pool;
708 sub->endpt = dlg->endpt;
709 sub->dlg = dlg;
710 sub->pkg = pkg;
711 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000712 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000713 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000714 sub->state = PJSIP_EVSUB_STATE_NULL;
715 sub->state_str = evsub_state_names[sub->state];
716 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
Benny Prijono9d4469d2007-05-02 05:14:29 +0000717 sub->accept = (pjsip_accept_hdr*)
718 pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
Benny Prijono834aee32006-02-19 01:38:06 +0000719
720 sub->timer.user_data = sub;
721 sub->timer.cb = &on_timer;
722
723 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000724 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
725 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000726
727
728 /* Copy callback, if any: */
729 if (user_cb)
730 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
731
732
733 /* Create Event header: */
734 sub->event = pjsip_event_hdr_create(sub->pool);
735 pj_strdup(sub->pool, &sub->event->event_type, event);
736
737
Benny Prijono15d3a702010-01-21 10:04:26 +0000738 /* Check if another subscription has been registered to the dialog. In
739 * that case, just add ourselves to the subscription list, otherwise
740 * create and register a new subscription list.
741 */
742 if (pjsip_dlg_has_usage(dlg, &mod_evsub.mod)) {
743 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
744 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
745 dlgsub->sub = sub;
746 pj_list_push_back(dlgsub_head, dlgsub);
747 } else {
748 dlgsub_head = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
749 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
750 dlgsub->sub = sub;
Benny Prijono834aee32006-02-19 01:38:06 +0000751
Benny Prijono15d3a702010-01-21 10:04:26 +0000752 pj_list_init(dlgsub_head);
753 pj_list_push_back(dlgsub_head, dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000754
755
Benny Prijono15d3a702010-01-21 10:04:26 +0000756 /* Register as dialog usage: */
Benny Prijono834aee32006-02-19 01:38:06 +0000757
Benny Prijono15d3a702010-01-21 10:04:26 +0000758 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
759 if (status != PJ_SUCCESS) {
760 pjsip_dlg_dec_lock(dlg);
761 return status;
762 }
Benny Prijono8eae8382006-08-10 21:44:26 +0000763 }
Benny Prijono834aee32006-02-19 01:38:06 +0000764
Benny Prijono834aee32006-02-19 01:38:06 +0000765 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
766 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
767 dlg->obj_name));
768
769 *p_evsub = sub;
Benny Prijono8eae8382006-08-10 21:44:26 +0000770 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000771
772 return PJ_SUCCESS;
773}
774
775
776
777/*
778 * Create client subscription session.
779 */
780PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
781 const pjsip_evsub_user *user_cb,
782 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000783 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000784 pjsip_evsub **p_evsub)
785{
786 pjsip_evsub *sub;
787 pj_status_t status;
788
789 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
790
791 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000792 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000793 if (status != PJ_SUCCESS)
794 goto on_return;
795
Benny Prijono26ff9062006-02-21 23:47:00 +0000796 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
797 * is not specified.
798 */
799 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
800 pj_create_unique_string(sub->pool, &sub->event->id_param);
801 }
Benny Prijono834aee32006-02-19 01:38:06 +0000802
803 /* Increment dlg session. */
804 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
805
806 /* Done */
807 *p_evsub = sub;
808
809on_return:
810 pjsip_dlg_dec_lock(dlg);
811 return status;
812}
813
814
815/*
816 * Create server subscription session from incoming request.
817 */
818PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
819 const pjsip_evsub_user *user_cb,
820 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000821 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000822 pjsip_evsub **p_evsub)
823{
824 pjsip_evsub *sub;
825 pjsip_transaction *tsx;
826 pjsip_accept_hdr *accept_hdr;
827 pjsip_event_hdr *event_hdr;
828 pjsip_expires_hdr *expires_hdr;
829 pj_status_t status;
830
831 /* Check arguments: */
832 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
833
834 /* MUST be request message: */
835 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
836 PJSIP_ENOTREQUESTMSG);
837
838 /* Transaction MUST have been created (in the dialog) */
839 tsx = pjsip_rdata_get_tsx(rdata);
840 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
841
842 /* No subscription must have been attached to transaction */
843 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
844 PJSIP_ETYPEEXISTS);
845
846 /* Package MUST implement on_rx_refresh */
847 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
848
Benny Prijono26ff9062006-02-21 23:47:00 +0000849 /* Request MUST have "Event" header. We need the Event header to get
850 * the package name (don't want to add more arguments in the function).
851 */
Benny Prijono834aee32006-02-19 01:38:06 +0000852 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +0000853 pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &STR_EVENT,
854 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000855 if (event_hdr == NULL) {
856 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
857 }
858
859 /* Start locking the mutex: */
860
861 pjsip_dlg_inc_lock(dlg);
862
863 /* Create the session: */
864
865 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000866 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000867 if (status != PJ_SUCCESS)
868 goto on_return;
869
870 /* Just duplicate Event header from the request */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000871 sub->event = (pjsip_event_hdr*) pjsip_hdr_clone(sub->pool, event_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000872
873 /* Set the method: */
874 pjsip_method_copy(sub->pool, &sub->method,
875 &rdata->msg_info.msg->line.req.method);
876
877 /* Update expiration time according to client request: */
878
879 expires_hdr = (pjsip_expires_hdr*)
880 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
881 if (expires_hdr) {
882 sub->expires->ivalue = expires_hdr->ivalue;
883 }
884
885 /* Update time. */
886 update_expires(sub, sub->expires->ivalue);
887
888 /* Update Accept header: */
889
890 accept_hdr = (pjsip_accept_hdr*)
891 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
892 if (accept_hdr)
Benny Prijono9d4469d2007-05-02 05:14:29 +0000893 sub->accept = (pjsip_accept_hdr*)pjsip_hdr_clone(sub->pool,accept_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000894
895 /* We can start the session: */
896
897 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
898 sub->pending_tsx++;
899 tsx->mod_data[mod_evsub.mod.id] = sub;
900
901
902 /* Done. */
903 *p_evsub = sub;
904
905
906on_return:
907 pjsip_dlg_dec_lock(dlg);
908 return status;
909}
910
911
912/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000913 * Forcefully destroy subscription.
914 */
915PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
916 pj_bool_t notify )
917{
918 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
919
920 pjsip_dlg_inc_lock(sub->dlg);
921
Benny Prijonod524e822006-09-22 12:48:18 +0000922 /* I think it's pretty safe to disable this check.
923
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000924 if (sub->pending_tsx) {
925 pj_assert(!"Unable to terminate when there's pending tsx");
926 pjsip_dlg_dec_lock(sub->dlg);
927 return PJ_EINVALIDOP;
928 }
Benny Prijonod524e822006-09-22 12:48:18 +0000929 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000930
931 sub->call_cb = notify;
Benny Prijono75130572008-07-17 13:53:41 +0000932 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, NULL);
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000933
934 pjsip_dlg_dec_lock(sub->dlg);
935 return PJ_SUCCESS;
936}
937
938/*
Benny Prijono834aee32006-02-19 01:38:06 +0000939 * Get subscription state.
940 */
941PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
942{
943 return sub->state;
944}
945
946/*
947 * Get state name.
948 */
949PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
950{
951 return sub->state_str.ptr;
952}
953
Benny Prijono75130572008-07-17 13:53:41 +0000954/*
955 * Get termination reason.
956 */
957PJ_DEF(const pj_str_t*) pjsip_evsub_get_termination_reason(pjsip_evsub *sub)
958{
959 return &sub->term_reason;
960}
Benny Prijono834aee32006-02-19 01:38:06 +0000961
962/*
963 * Initiate client subscription
964 */
965PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
966 const pjsip_method *method,
967 pj_int32_t expires,
968 pjsip_tx_data **p_tdata)
969{
970 pjsip_tx_data *tdata;
971 pj_status_t status;
972
973 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
974
975 /* Use SUBSCRIBE if method is not specified */
976 if (method == NULL)
977 method = &pjsip_subscribe_method;
978
979 pjsip_dlg_inc_lock(sub->dlg);
980
981 /* Update method: */
982 if (sub->state == PJSIP_EVSUB_STATE_NULL)
983 pjsip_method_copy(sub->pool, &sub->method, method);
984
985 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
986 if (status != PJ_SUCCESS)
987 goto on_return;
988
989
990 /* Add Event header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000991 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000992 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
993
994 /* Update and add expires header: */
995 if (expires >= 0)
996 sub->expires->ivalue = expires;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000997 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000998 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
999
1000 /* Add Accept header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001001 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001002 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
1003
1004
1005 /* Add Allow-Events header: */
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,
1008 mod_evsub.allow_events_hdr));
1009
1010
1011 *p_tdata = tdata;
1012
1013
1014on_return:
1015
1016 pjsip_dlg_dec_lock(sub->dlg);
1017 return status;
1018}
1019
1020
1021/*
1022 * Accept incoming subscription request.
1023 */
1024PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
1025 pjsip_rx_data *rdata,
1026 int st_code,
1027 const pjsip_hdr *hdr_list )
1028{
1029 pjsip_tx_data *tdata;
1030 pjsip_transaction *tsx;
1031 pj_status_t status;
1032
1033 /* Check arguments */
1034 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
1035
1036 /* Can only be for server subscription: */
1037 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
1038
1039 /* Only expect 2xx status code (for now) */
1040 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
1041
1042 /* Subscription MUST have been attached to the transaction.
1043 * Initial subscription request will be attached on evsub_create_uas(),
1044 * while subsequent requests will be attached in tsx_state()
1045 */
1046 tsx = pjsip_rdata_get_tsx(rdata);
1047 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
1048 PJ_EINVALIDOP);
1049
1050 /* Lock dialog */
1051 pjsip_dlg_inc_lock(sub->dlg);
1052
1053 /* Create response: */
1054 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1055 &tdata);
1056 if (status != PJ_SUCCESS)
1057 goto on_return;
1058
1059
1060 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001061 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001062 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1063
Benny Prijonob0808372006-03-02 21:18:58 +00001064 /* Add additional header, if any. */
1065 if (hdr_list) {
1066 const pjsip_hdr *hdr = hdr_list->next;
1067 while (hdr != hdr_list) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001068 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonob0808372006-03-02 21:18:58 +00001069 pjsip_hdr_clone(tdata->pool, hdr));
1070 hdr = hdr->next;
1071 }
1072 }
Benny Prijono834aee32006-02-19 01:38:06 +00001073
1074 /* Send the response: */
1075 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1076 if (status != PJ_SUCCESS)
1077 goto on_return;
1078
1079
1080on_return:
1081
1082 pjsip_dlg_dec_lock(sub->dlg);
1083 return status;
1084}
1085
1086
1087/*
1088 * Create Subscription-State header based on current server subscription
1089 * state.
1090 */
1091static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1092 pjsip_evsub *sub,
1093 pjsip_evsub_state state,
1094 const pj_str_t *state_str,
1095 const pj_str_t *reason )
1096{
1097 pjsip_sub_state_hdr *sub_state;
1098 pj_time_val now, delay;
1099
1100 /* Get the remaining time before refresh is required */
1101 pj_gettimeofday(&now);
1102 delay = sub->refresh_time;
1103 PJ_TIME_VAL_SUB(delay, now);
1104
1105 /* Create the Subscription-State header */
1106 sub_state = pjsip_sub_state_hdr_create(pool);
1107
1108 /* Fill up the header */
1109 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001110 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001111 case PJSIP_EVSUB_STATE_SENT:
Benny Prijono834aee32006-02-19 01:38:06 +00001112 pj_assert(!"Invalid state!");
1113 /* Treat as pending */
1114
Benny Prijono75130572008-07-17 13:53:41 +00001115 case PJSIP_EVSUB_STATE_ACCEPTED:
Benny Prijono834aee32006-02-19 01:38:06 +00001116 case PJSIP_EVSUB_STATE_PENDING:
1117 sub_state->sub_state = STR_PENDING;
1118 sub_state->expires_param = delay.sec;
1119 break;
1120
1121 case PJSIP_EVSUB_STATE_ACTIVE:
1122 sub_state->sub_state = STR_ACTIVE;
1123 sub_state->expires_param = delay.sec;
1124 break;
1125
1126 case PJSIP_EVSUB_STATE_TERMINATED:
1127 sub_state->sub_state = STR_TERMINATED;
1128 if (reason != NULL)
1129 pj_strdup(pool, &sub_state->reason_param, reason);
1130 break;
1131
1132 case PJSIP_EVSUB_STATE_UNKNOWN:
1133 pj_assert(state_str != NULL);
1134 pj_strdup(pool, &sub_state->sub_state, state_str);
1135 break;
1136 }
1137
1138 return sub_state;
1139}
1140
1141/*
1142 * Create and send NOTIFY request.
1143 */
1144PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1145 pjsip_evsub_state state,
1146 const pj_str_t *state_str,
1147 const pj_str_t *reason,
1148 pjsip_tx_data **p_tdata)
1149{
1150 pjsip_tx_data *tdata;
1151 pjsip_sub_state_hdr *sub_state;
1152 pj_status_t status;
1153
1154 /* Check arguments. */
1155 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1156
1157 /* Lock dialog. */
1158 pjsip_dlg_inc_lock(sub->dlg);
1159
1160 /* Create NOTIFY request */
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001161 status = pjsip_dlg_create_request( sub->dlg, pjsip_get_notify_method(),
1162 -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001163 if (status != PJ_SUCCESS)
1164 goto on_return;
1165
1166 /* Add Event header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001167 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001168 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1169
1170 /* Add Subscription-State header */
1171 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1172 reason);
1173 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1174
1175 /* Add Allow-Events header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001176 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001177 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1178
1179 /* Add Authentication headers. */
1180 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1181
Benny Prijono75130572008-07-17 13:53:41 +00001182 /* Update reason */
1183 if (reason)
1184 pj_strdup(sub->dlg->pool, &sub->term_reason, reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001185
1186 /* Save destination state. */
1187 sub->dst_state = state;
1188 if (state_str)
1189 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1190 else
1191 sub->dst_state_str.slen = 0;
1192
1193
1194 *p_tdata = tdata;
1195
1196on_return:
1197 /* Unlock dialog */
1198 pjsip_dlg_dec_lock(sub->dlg);
1199 return status;
1200}
1201
1202
1203/*
1204 * Create NOTIFY to reflect current status.
1205 */
1206PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1207 pjsip_tx_data **p_tdata )
1208{
1209 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1210 NULL, p_tdata );
1211}
1212
1213
1214/*
1215 * Send request.
1216 */
1217PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1218 pjsip_tx_data *tdata)
1219{
1220 pj_status_t status;
1221
1222 /* Must be request message. */
1223 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1224 PJSIP_ENOTREQUESTMSG);
1225
1226 /* Lock */
1227 pjsip_dlg_inc_lock(sub->dlg);
1228
1229 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001230 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001231 if (status != PJ_SUCCESS)
1232 goto on_return;
1233
1234
1235 /* Special case for NOTIFY:
1236 * The new state was set in pjsip_evsub_notify(), but we apply the
1237 * new state now, when the request was actually sent.
1238 */
1239 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1240 &pjsip_notify_method)==0)
1241 {
1242 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1243 {goto on_return;});
1244
1245 set_state(sub, sub->dst_state,
1246 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
Benny Prijono75130572008-07-17 13:53:41 +00001247 NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001248
1249 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1250 sub->dst_state_str.slen = 0;
1251
1252 }
1253
1254
1255on_return:
1256 pjsip_dlg_dec_lock(sub->dlg);
1257 return status;
1258}
1259
1260
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001261/* Callback to be called to terminate transaction. */
1262static void terminate_timer_cb(pj_timer_heap_t *timer_heap,
1263 struct pj_timer_entry *entry)
1264{
1265 pj_str_t *key;
1266 pjsip_transaction *tsx;
1267
1268 PJ_UNUSED_ARG(timer_heap);
1269
1270 key = (pj_str_t*)entry->user_data;
1271 tsx = pjsip_tsx_layer_find_tsx(key, PJ_FALSE);
1272 /* Chance of race condition here */
1273 if (tsx) {
1274 pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_UPDATED);
1275 }
1276}
1277
Benny Prijono834aee32006-02-19 01:38:06 +00001278
1279/*
1280 * Attach subscription session to newly created transaction, if appropriate.
1281 */
1282static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1283 pjsip_event *event)
1284{
1285 /*
1286 * Newly created transaction will not have subscription session
1287 * attached to it. Find the subscription session from the dialog,
1288 * by matching the Event header.
1289 */
1290 pjsip_dialog *dlg;
1291 pjsip_event_hdr *event_hdr;
1292 pjsip_msg *msg;
1293 struct dlgsub *dlgsub_head, *dlgsub;
1294 pjsip_evsub *sub;
1295
1296 dlg = pjsip_tsx_get_dlg(tsx);
1297 if (!dlg) {
1298 pj_assert(!"Transaction should have a dialog instance!");
1299 return NULL;
1300 }
1301
Benny Prijono26ff9062006-02-21 23:47:00 +00001302
Benny Prijono834aee32006-02-19 01:38:06 +00001303 switch (event->body.tsx_state.type) {
1304 case PJSIP_EVENT_RX_MSG:
1305 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1306 break;
1307 case PJSIP_EVENT_TX_MSG:
1308 msg = event->body.tsx_state.src.tdata->msg;
1309 break;
1310 default:
1311 if (tsx->role == PJSIP_ROLE_UAC)
1312 msg = tsx->last_tx->msg;
1313 else
1314 msg = NULL;
1315 break;
1316 }
1317
1318 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001319 //Note:
1320 // this transaction can be other transaction in the dialog.
1321 // The assertion below probably only valid for dialog that
1322 // only has one event subscription usage.
1323 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001324 return NULL;
1325 }
1326
Benny Prijono9d4469d2007-05-02 05:14:29 +00001327 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001328 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1329 &STR_EVENT_S, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001330 if (!event_hdr) {
1331 /* Not subscription related message */
1332 return NULL;
1333 }
1334
1335 /* Find the subscription in the dialog, based on the content
1336 * of Event header:
1337 */
1338
Benny Prijono9d4469d2007-05-02 05:14:29 +00001339 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +00001340 if (dlgsub_head == NULL) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001341 dlgsub_head = PJ_POOL_ALLOC_T(dlg->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +00001342 pj_list_init(dlgsub_head);
1343 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1344 }
1345 dlgsub = dlgsub_head->next;
1346
1347 while (dlgsub != dlgsub_head) {
1348
Benny Prijono26ff9062006-02-21 23:47:00 +00001349 if (pj_stricmp(&dlgsub->sub->event->event_type,
1350 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001351 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001352 /* Event type matched.
1353 * Check if event ID matched too.
1354 */
1355 if (pj_strcmp(&dlgsub->sub->event->id_param,
1356 &event_hdr->id_param)==0)
1357 {
1358
1359 break;
1360
1361 }
1362 /*
1363 * Otherwise if it is an UAC subscription, AND
1364 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1365 * the session's event id is NULL, AND
1366 * the incoming request is NOTIFY with event ID, then
1367 * we consider it as a match, and update the
1368 * session's event id.
1369 */
1370 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1371 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1372 dlgsub->sub->event->id_param.slen==0 &&
1373 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1374 {
1375 /* Update session's event id. */
1376 pj_strdup(dlgsub->sub->pool,
1377 &dlgsub->sub->event->id_param,
1378 &event_hdr->id_param);
1379
1380 break;
1381 }
Benny Prijono834aee32006-02-19 01:38:06 +00001382 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001383
1384
1385
Benny Prijono834aee32006-02-19 01:38:06 +00001386 dlgsub = dlgsub->next;
1387 }
1388
Benny Prijonoce00fa02009-06-30 13:47:44 +00001389 /* Note:
1390 * the second condition is for http://trac.pjsip.org/repos/ticket/911
1391 */
1392 if (dlgsub == dlgsub_head ||
1393 (dlgsub->sub &&
1394 pjsip_evsub_get_state(dlgsub->sub)==PJSIP_EVSUB_STATE_TERMINATED))
1395 {
1396 const char *reason_msg =
1397 (dlgsub == dlgsub_head ? "Subscription Does Not Exist" :
1398 "Subscription already terminated");
1399
Benny Prijono834aee32006-02-19 01:38:06 +00001400 /* This could be incoming request to create new subscription */
1401 PJ_LOG(4,(THIS_FILE,
Benny Prijonoce00fa02009-06-30 13:47:44 +00001402 "%s for %.*s, event=%.*s;id=%.*s",
1403 reason_msg,
Benny Prijono26ff9062006-02-21 23:47:00 +00001404 (int)tsx->method.name.slen,
1405 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001406 (int)event_hdr->event_type.slen,
1407 event_hdr->event_type.ptr,
1408 (int)event_hdr->id_param.slen,
1409 event_hdr->id_param.ptr));
1410
1411 /* If this is an incoming NOTIFY, reject with 481 */
1412 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1413 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1414 {
Benny Prijonoce00fa02009-06-30 13:47:44 +00001415 pj_str_t reason;
Benny Prijono834aee32006-02-19 01:38:06 +00001416 pjsip_tx_data *tdata;
1417 pj_status_t status;
1418
Benny Prijonoce00fa02009-06-30 13:47:44 +00001419 pj_cstr(&reason, reason_msg);
Benny Prijono834aee32006-02-19 01:38:06 +00001420 status = pjsip_dlg_create_response(dlg,
1421 event->body.tsx_state.src.rdata,
1422 481, &reason,
1423 &tdata);
1424 if (status == PJ_SUCCESS) {
1425 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1426 }
1427 }
1428 return NULL;
1429 }
1430
1431 /* Found! */
1432 sub = dlgsub->sub;
1433
1434 /* Attach session to the transaction */
1435 tsx->mod_data[mod_evsub.mod.id] = sub;
1436 sub->pending_tsx++;
1437
Benny Prijono69b98ab2006-03-03 10:23:35 +00001438 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1439 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1440 * transaction is started while previous one still alive, terminate
1441 * the older one.
1442 *
1443 * Sample scenario:
1444 * - subscribe sent to destination that doesn't exist, transaction
1445 * is still retransmitting request, then unsubscribe is sent.
1446 */
1447 if (tsx->role == PJSIP_ROLE_UAC &&
1448 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001449 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1450 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001451 {
1452
1453 if (sub->pending_sub &&
1454 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1455 {
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001456 pj_timer_entry *timer;
1457 pj_str_t *key;
1458 pj_time_val timeout = {0, 0};
1459
Benny Prijono69b98ab2006-03-03 10:23:35 +00001460 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001461 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001462
1463 /* By convention, we use 490 (Request Updated) status code.
1464 * When transaction handler (below) see this status code, it
1465 * will ignore the transaction.
1466 */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001467 /* This unfortunately may cause deadlock, because at the moment
1468 * we are holding dialog's mutex. If a response to this
1469 * transaction is in progress in another thread, that thread
1470 * will deadlock when trying to acquire dialog mutex, because
1471 * it is holding the transaction mutex.
1472 *
1473 * So the solution is to register timer to kill this transaction.
1474 */
1475 //pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1476 timer = PJ_POOL_ZALLOC_T(dlg->pool, pj_timer_entry);
1477 key = PJ_POOL_ALLOC_T(dlg->pool, pj_str_t);
1478 pj_strdup(dlg->pool, key, &sub->pending_sub->transaction_key);
1479 timer->cb = &terminate_timer_cb;
1480 timer->user_data = key;
1481
1482 pjsip_endpt_schedule_timer(dlg->endpt, timer, &timeout);
Benny Prijono69b98ab2006-03-03 10:23:35 +00001483 }
1484
1485 sub->pending_sub = tsx;
1486
Benny Prijono69b98ab2006-03-03 10:23:35 +00001487 }
1488
Benny Prijono834aee32006-02-19 01:38:06 +00001489 return sub;
1490}
1491
1492
1493/*
1494 * Create response, adding custome headers and msg body.
1495 */
1496static pj_status_t create_response( pjsip_evsub *sub,
1497 pjsip_rx_data *rdata,
1498 int st_code,
1499 const pj_str_t *st_text,
1500 const pjsip_hdr *res_hdr,
1501 const pjsip_msg_body *body,
1502 pjsip_tx_data **p_tdata)
1503{
1504 pjsip_tx_data *tdata;
1505 pjsip_hdr *hdr;
1506 pj_status_t status;
1507
1508 status = pjsip_dlg_create_response(sub->dlg, rdata,
1509 st_code, st_text, &tdata);
1510 if (status != PJ_SUCCESS)
1511 return status;
1512
1513 *p_tdata = tdata;
1514
1515 /* Add response headers. */
1516 hdr = res_hdr->next;
1517 while (hdr != res_hdr) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001518 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001519 pjsip_hdr_clone(tdata->pool, hdr));
1520 hdr = hdr->next;
1521 }
1522
1523 /* Add msg body, if any */
1524 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001525 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1526 if (tdata->msg->body == NULL) {
1527
1528 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1529
Benny Prijono834aee32006-02-19 01:38:06 +00001530 /* Ignore */
1531 return PJ_SUCCESS;
1532 }
1533 }
1534
1535 return PJ_SUCCESS;
1536}
1537
1538/*
1539 * Get subscription state from the value of Subscription-State header.
1540 */
1541static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1542 pjsip_evsub_state *state,
1543 pj_str_t **state_str )
1544{
1545 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1546
1547 *state = PJSIP_EVSUB_STATE_TERMINATED;
1548 *state_str = NULL;
1549
1550 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1551
1552 *state = PJSIP_EVSUB_STATE_ACTIVE;
1553 *state_str = NULL;
1554
1555 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1556
1557 *state = PJSIP_EVSUB_STATE_PENDING;
1558 *state_str = NULL;
1559
1560 } else {
1561
1562 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1563 *state_str = &sub_state->sub_state;
1564
1565 }
1566}
1567
1568/*
1569 * Transaction event processing by UAC, after subscription is sent.
1570 */
1571static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1572 pjsip_event *event )
1573{
1574
Benny Prijono736d0f72006-09-13 22:45:38 +00001575 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1576 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1577 {
Benny Prijono834aee32006-02-19 01:38:06 +00001578
1579 /* Received response to outgoing request that establishes/refresh
1580 * subscription.
1581 */
1582
1583 /* First time initial request is sent. */
1584 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1585 tsx->state == PJSIP_TSX_STATE_CALLING)
1586 {
Benny Prijono75130572008-07-17 13:53:41 +00001587 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001588 return;
1589 }
1590
1591 /* Only interested in final response */
1592 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1593 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1594 {
1595 return;
1596 }
1597
Benny Prijono1d8d6082006-04-29 12:38:25 +00001598 /* Clear pending subscription */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001599 if (tsx == sub->pending_sub) {
Benny Prijono1d8d6082006-04-29 12:38:25 +00001600 sub->pending_sub = NULL;
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001601 } else if (sub->pending_sub != NULL) {
1602 /* This SUBSCRIBE transaction has been "renewed" with another
1603 * SUBSCRIBE, so we can just ignore this. For example, user
1604 * sent SUBSCRIBE followed immediately with UN-SUBSCRIBE.
1605 */
1606 return;
1607 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001608
Benny Prijono834aee32006-02-19 01:38:06 +00001609 /* Handle authentication. */
1610 if (tsx->status_code==401 || tsx->status_code==407) {
1611 pjsip_tx_data *tdata;
1612 pj_status_t status;
1613
1614 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1615 /* Previously failed transaction has terminated */
1616 return;
1617 }
1618
1619 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1620 event->body.tsx_state.src.rdata,
1621 tsx->last_tx, &tdata);
1622 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001623 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001624
1625 if (status != PJ_SUCCESS) {
1626 /* Authentication failed! */
1627 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001628 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001629 return;
1630 }
1631
1632 return;
1633 }
1634
1635 if (tsx->status_code/100 == 2) {
1636
1637 /* Successfull SUBSCRIBE request!
1638 * This could be:
1639 * - response to initial SUBSCRIBE request
1640 * - response to subsequent refresh
1641 * - response to unsubscription
1642 */
1643
1644 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1645 /* Ignore; this transaction has been processed before */
1646 return;
1647 }
1648
1649 /* Update UAC refresh time, if response contains Expires header,
1650 * only when we're not unsubscribing.
1651 */
1652 if (sub->expires->ivalue != 0) {
1653 pjsip_msg *msg;
1654 pjsip_expires_hdr *expires;
1655
1656 msg = event->body.tsx_state.src.rdata->msg_info.msg;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001657 expires = (pjsip_expires_hdr*)
1658 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001659 if (expires) {
1660 sub->expires->ivalue = expires->ivalue;
1661 }
1662 }
1663
1664 /* Update time */
1665 update_expires(sub, sub->expires->ivalue);
1666
1667 /* Start UAC refresh timer, only when we're not unsubscribing */
1668 if (sub->expires->ivalue != 0) {
1669 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1670 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1671
1672 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1673 timeout));
1674 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1675
1676 } else {
1677 /* Otherwise set timer to terminate client subscription when
1678 * NOTIFY to end subscription is not received.
1679 */
1680 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1681 }
1682
1683 /* Set state, if necessary */
1684 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1685 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
Benny Prijono75130572008-07-17 13:53:41 +00001686 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001687 }
1688
1689 } else {
1690
1691 /* Failed SUBSCRIBE request!
1692 *
1693 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1694 * other than 481, the subscription is still considered valid for
1695 * the duration of the last Expires.
1696 *
1697 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1698 * expiration, theoritically the expiration is still valid for the
1699 * next 5 seconds even when we receive non-481 failed response.
1700 *
1701 * Ah, what the heck!
1702 *
1703 * Just terminate now!
1704 *
1705 */
1706
1707 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1708 /* Ignore, has been handled before */
1709 return;
1710 }
1711
Benny Prijono69b98ab2006-03-03 10:23:35 +00001712 /* Ignore 490 (Request Updated) status.
1713 * This happens when application sends SUBSCRIBE/REFER while
1714 * another one is still in progress.
1715 */
1716 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1717 return;
1718 }
1719
Benny Prijono834aee32006-02-19 01:38:06 +00001720 /* Kill any timer. */
1721 set_timer(sub, TIMER_TYPE_NONE, 0);
1722
1723 /* Set state to TERMINATED */
1724 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
Benny Prijono75130572008-07-17 13:53:41 +00001725 NULL, event, &tsx->status_text);
Benny Prijono834aee32006-02-19 01:38:06 +00001726
1727 }
1728
1729 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1730
1731 /* Incoming NOTIFY.
1732 * This can be the result of:
1733 * - Initial subscription response
1734 * - UAS updating the resource info.
1735 * - Unsubscription response.
1736 */
1737 int st_code = 200;
1738 pj_str_t *st_text = NULL;
1739 pjsip_hdr res_hdr;
1740 pjsip_msg_body *body = NULL;
1741
1742 pjsip_rx_data *rdata;
1743 pjsip_msg *msg;
1744 pjsip_sub_state_hdr *sub_state;
1745
1746 pjsip_evsub_state new_state;
1747 pj_str_t *new_state_str;
1748
1749 pjsip_tx_data *tdata;
1750 pj_status_t status;
1751 int next_refresh;
1752
1753 /* Only want to handle initial NOTIFY receive event. */
1754 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1755 return;
1756
1757
1758 rdata = event->body.tsx_state.src.rdata;
1759 msg = rdata->msg_info.msg;
1760
1761 pj_list_init(&res_hdr);
1762
1763 /* Get subscription state header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001764 sub_state = (pjsip_sub_state_hdr*)
1765 pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001766 if (sub_state == NULL) {
1767
1768 pjsip_warning_hdr *warn_hdr;
1769 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1770
1771 /* Bad request! Add warning header. */
1772 st_code = PJSIP_SC_BAD_REQUEST;
1773 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1774 pjsip_endpt_name(sub->endpt),
1775 &warn_text);
1776 pj_list_push_back(&res_hdr, warn_hdr);
1777 }
1778
1779 /* Call application registered callback to handle incoming NOTIFY,
1780 * if any.
1781 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001782 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001783 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1784 &res_hdr, &body);
1785
1786 /* Application MUST specify final response! */
1787 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1788
1789 /* Must be a valid status code */
1790 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1791 }
1792
1793
1794 /* If non-2xx should be returned, then send the response.
1795 * No need to update server subscription state.
1796 */
1797 if (st_code >= 300) {
1798 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1799 body, &tdata);
1800 if (status == PJ_SUCCESS) {
1801 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1802 }
1803
1804 /* Start timer to terminate subscription, just in case server
1805 * is not able to generate NOTIFY to our response.
1806 */
1807 if (status == PJ_SUCCESS) {
1808 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1809 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1810 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001811 char errmsg[PJ_ERR_MSG_SIZE];
1812 pj_str_t reason;
1813
1814 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1815 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
1816 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001817 }
1818
1819 return;
1820 }
1821
1822 /* Update expiration from the value of expires param in
1823 * Subscription-State header, but ONLY when subscription state
1824 * is "active" or "pending", AND the header contains expires param.
1825 */
1826 if (sub->expires->ivalue != 0 &&
1827 sub_state->expires_param >= 0 &&
1828 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1829 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1830 {
1831 next_refresh = sub_state->expires_param;
1832
1833 } else {
1834 next_refresh = sub->expires->ivalue;
1835 }
1836
1837 /* Update time */
1838 update_expires(sub, next_refresh);
1839
1840 /* Start UAC refresh timer, only when we're not unsubscribing */
1841 if (sub->expires->ivalue != 0) {
1842 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1843 next_refresh - TIME_UAC_REFRESH : next_refresh;
1844
1845 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1846 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1847 }
1848
1849 /* Find out the state */
1850 get_hdr_state(sub_state, &new_state, &new_state_str);
1851
1852 /* Send response. */
1853 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1854 body, &tdata);
1855 if (status == PJ_SUCCESS)
1856 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1857
1858 /* Set the state */
1859 if (status == PJ_SUCCESS) {
Benny Prijono75130572008-07-17 13:53:41 +00001860 set_state(sub, new_state, new_state_str, event,
1861 &sub_state->reason_param);
Benny Prijono834aee32006-02-19 01:38:06 +00001862 } else {
Benny Prijono75130572008-07-17 13:53:41 +00001863 char errmsg[PJ_ERR_MSG_SIZE];
1864 pj_str_t reason;
1865
1866 reason = pj_strerror(status, errmsg, sizeof(errmsg));
1867 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
1868 &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001869 }
1870
1871
1872 } else {
1873
1874 /*
1875 * Unexpected method!
1876 */
1877 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1878 (int)tsx->method.name.slen, tsx->method.name.ptr));
1879 }
1880}
1881
1882
1883/*
1884 * Transaction event processing by UAS, after subscription is accepted.
1885 */
1886static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1887 pjsip_event *event)
1888{
1889
Benny Prijono736d0f72006-09-13 22:45:38 +00001890 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1891 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1892 {
Benny Prijono834aee32006-02-19 01:38:06 +00001893
1894 /*
1895 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1896 *
1897 */
1898 pjsip_rx_data *rdata;
1899 pjsip_event_hdr *event_hdr;
1900 pjsip_expires_hdr *expires;
1901 pjsip_msg *msg;
1902 pjsip_tx_data *tdata;
1903 int st_code = 200;
1904 pj_str_t *st_text = NULL;
1905 pjsip_hdr res_hdr;
1906 pjsip_msg_body *body = NULL;
1907 pjsip_evsub_state old_state;
1908 pj_str_t old_state_str;
Benny Prijono75130572008-07-17 13:53:41 +00001909 pj_str_t reason = { NULL, 0 };
Benny Prijono834aee32006-02-19 01:38:06 +00001910 pj_status_t status;
1911
1912
1913 /* Only wants to handle the first event when the request is
1914 * received.
1915 */
1916 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1917 return;
1918
1919 rdata = event->body.tsx_state.src.rdata;
1920 msg = rdata->msg_info.msg;
1921
1922 /* Set expiration time based on client request (in Expires header),
1923 * or package default expiration time.
1924 */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001925 event_hdr = (pjsip_event_hdr*)
Benny Prijono0c13f3d2008-07-16 22:39:45 +00001926 pjsip_msg_find_hdr_by_names(msg, &STR_EVENT,
1927 &STR_EVENT, NULL);
Benny Prijono9d4469d2007-05-02 05:14:29 +00001928 expires = (pjsip_expires_hdr*)
1929 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001930 if (event_hdr && expires) {
1931 struct evpkg *evpkg;
1932
1933 evpkg = find_pkg(&event_hdr->event_type);
1934 if (evpkg) {
1935 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1936 sub->expires->ivalue = expires->ivalue;
1937 else
1938 sub->expires->ivalue = evpkg->pkg_expires;
1939 }
1940 }
1941
1942 /* Update time (before calling on_rx_refresh, since application
1943 * will send NOTIFY.
1944 */
1945 update_expires(sub, sub->expires->ivalue);
1946
1947
1948 /* Save old state.
1949 * If application respond with non-2xx, revert to old state.
1950 */
1951 old_state = sub->state;
1952 old_state_str = sub->state_str;
1953
1954 if (sub->expires->ivalue == 0) {
1955 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1956 sub->state_str = evsub_state_names[sub->state];
1957 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1958 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1959 sub->state_str = evsub_state_names[sub->state];
1960 }
1961
1962 /* Call application's on_rx_refresh, just in case it wants to send
1963 * response other than 200 (OK)
1964 */
1965 pj_list_init(&res_hdr);
1966
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001967 if (sub->user.on_rx_refresh && sub->call_cb) {
1968 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1969 &res_hdr, &body);
1970 }
Benny Prijono834aee32006-02-19 01:38:06 +00001971
1972 /* Application MUST specify final response! */
1973 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1974
1975 /* Must be a valid status code */
1976 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1977
1978
1979 /* Create and send response */
1980 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1981 body, &tdata);
1982 if (status == PJ_SUCCESS) {
1983 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001984 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001985 pjsip_hdr_shallow_clone(tdata->pool,
1986 sub->expires));
1987
1988 /* Send */
1989 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1990 }
1991
1992 /* Update state or revert state */
1993 if (st_code/100==2) {
1994
1995 if (sub->expires->ivalue == 0) {
Benny Prijono75130572008-07-17 13:53:41 +00001996 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001997 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
Benny Prijono75130572008-07-17 13:53:41 +00001998 set_state(sub, sub->state, NULL, event, &reason);
Benny Prijono834aee32006-02-19 01:38:06 +00001999 }
2000
2001 /* Set UAS timeout timer, when state is not terminated. */
2002 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
2003 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
2004 sub->expires->ivalue));
2005 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
2006 sub->expires->ivalue);
2007 }
2008
2009 } else {
2010 sub->state = old_state;
2011 sub->state_str = old_state_str;
2012 }
2013
2014
2015 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
2016
2017 /* Handle authentication */
2018 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
2019 (tsx->status_code==401 || tsx->status_code==407))
2020 {
2021 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
2022 pjsip_tx_data *tdata;
2023 pj_status_t status;
2024
2025 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
2026 tsx->last_tx, &tdata);
2027 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00002028 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00002029
2030 if (status != PJ_SUCCESS) {
2031 /* Can't authenticate. Terminate session (?) */
Benny Prijono75130572008-07-17 13:53:41 +00002032 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL,
2033 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002034 return;
Benny Prijono834aee32006-02-19 01:38:06 +00002035 }
Benny Prijono26ff9062006-02-21 23:47:00 +00002036
2037 }
2038 /*
2039 * Terminate event usage if we receive 481, 408, and 7 class
2040 * responses.
2041 */
2042 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
2043 (tsx->status_code==481 || tsx->status_code==408 ||
2044 tsx->status_code/100 == 7))
2045 {
Benny Prijono75130572008-07-17 13:53:41 +00002046 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event,
2047 &tsx->status_text);
Benny Prijono441ce002006-03-07 15:15:01 +00002048 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00002049 }
Benny Prijono834aee32006-02-19 01:38:06 +00002050
2051 } else {
2052
2053 /*
2054 * Unexpected method!
2055 */
2056 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
2057 (int)tsx->method.name.slen, tsx->method.name.ptr));
2058
2059 }
2060}
2061
2062
2063/*
2064 * Notification when transaction state has changed!
2065 */
2066static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
2067{
2068 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
2069
2070 if (sub == NULL) {
2071 sub = on_new_transaction(tsx, event);
2072 if (sub == NULL)
2073 return;
2074 }
2075
2076
2077 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002078 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00002079 (*sub->user.on_tsx_state)(sub, tsx, event);
2080
2081
2082 /* Process the event: */
2083
2084 if (sub->role == PJSIP_ROLE_UAC) {
2085 on_tsx_state_uac(sub, tsx, event);
2086 } else {
2087 on_tsx_state_uas(sub, tsx, event);
2088 }
2089
2090
2091 /* Check transaction TERMINATE event */
2092 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
2093
2094 --sub->pending_tsx;
2095
2096 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
2097 sub->pending_tsx == 0)
2098 {
2099 evsub_destroy(sub);
2100 }
2101
2102 }
2103}
2104
2105