blob: 3b005b5e80d1e10a2c52fcf4d32b87b57f8e7e4e [file] [log] [blame]
Benny Prijono834aee32006-02-19 01:38:06 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono834aee32006-02-19 01:38:06 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsip-simple/evsub.h>
20#include <pjsip-simple/evsub_msg.h>
21#include <pjsip-simple/errno.h>
22#include <pjsip/sip_errno.h>
23#include <pjsip/sip_module.h>
24#include <pjsip/sip_endpoint.h>
25#include <pjsip/sip_dialog.h>
26#include <pjsip/sip_auth.h>
27#include <pjsip/sip_transaction.h>
28#include <pjsip/sip_event.h>
29#include <pj/assert.h>
30#include <pj/guid.h>
31#include <pj/log.h>
32#include <pj/os.h>
33#include <pj/pool.h>
34#include <pj/string.h>
35
36
37#define THIS_FILE "evsub.c"
38
39/*
40 * Global constant
41 */
42
43/* Let's define this enum, so that it'll trigger compilation error
44 * when somebody define the same enum in sip_msg.h
45 */
46enum
47{
48 PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD,
49 PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD
50};
51
Benny Prijono1f61a8f2007-08-16 10:11:44 +000052PJ_DEF_DATA(const pjsip_method) pjsip_subscribe_method =
Benny Prijono834aee32006-02-19 01:38:06 +000053{
Benny Prijono9d4469d2007-05-02 05:14:29 +000054 (pjsip_method_e) PJSIP_SUBSCRIBE_METHOD,
Benny Prijono834aee32006-02-19 01:38:06 +000055 { "SUBSCRIBE", 9 }
56};
57
Benny Prijono1f61a8f2007-08-16 10:11:44 +000058PJ_DEF_DATA(const pjsip_method) pjsip_notify_method =
Benny Prijono834aee32006-02-19 01:38:06 +000059{
Benny Prijono9d4469d2007-05-02 05:14:29 +000060 (pjsip_method_e) PJSIP_NOTIFY_METHOD,
Benny Prijono834aee32006-02-19 01:38:06 +000061 { "NOTIFY", 6 }
62};
63
Benny Prijono1f61a8f2007-08-16 10:11:44 +000064/**
65 * SUBSCRIBE method constant.
66 */
67PJ_DEF(const pjsip_method*) pjsip_get_subscribe_method()
68{
69 return &pjsip_subscribe_method;
70}
71
72/**
73 * NOTIFY method constant.
74 */
75PJ_DEF(const pjsip_method*) pjsip_get_notify_method()
76{
77 return &pjsip_notify_method;
78}
79
80
Benny Prijono834aee32006-02-19 01:38:06 +000081/*
82 * Static prototypes.
83 */
84static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*);
85static pj_status_t mod_evsub_unload(void);
86
87
88/*
89 * State names.
90 */
91static pj_str_t evsub_state_names[] =
92{
93 { "NULL", 4},
94 { "SENT", 4},
95 { "ACCEPTED", 8},
96 { "PENDING", 7},
97 { "ACTIVE", 6},
98 { "TERMINATED", 10},
99 { "UNKNOWN", 7}
100};
101
102/*
103 * Timer constants.
104 */
105
106/* Number of seconds to send SUBSCRIBE before the actual expiration */
107#define TIME_UAC_REFRESH 5
108
109/* Time to wait for the final NOTIFY after sending unsubscription */
110#define TIME_UAC_TERMINATE 5
111
112/* If client responds NOTIFY with non-2xx final response (such as 401),
113 * wait for this seconds for further NOTIFY, otherwise client will
114 * unsubscribe
115 */
116#define TIME_UAC_WAIT_NOTIFY 5
117
118
119/*
120 * Timer id
121 */
122enum timer_id
123{
124 /* No timer. */
125 TIMER_TYPE_NONE,
126
127 /* Time to refresh client subscription.
128 * The action is to call on_client_refresh() callback.
129 */
130 TIMER_TYPE_UAC_REFRESH,
131
132 /* UAS timeout after to subscription refresh.
133 * The action is to call on_server_timeout() callback.
134 */
135 TIMER_TYPE_UAS_TIMEOUT,
136
137 /* UAC waiting for final NOTIFY after unsubscribing
138 * The action is to terminate.
139 */
140 TIMER_TYPE_UAC_TERMINATE,
141
142 /* UAC waiting for further NOTIFY after sending non-2xx response to
143 * NOTIFY. The action is to unsubscribe.
144 */
145 TIMER_TYPE_UAC_WAIT_NOTIFY,
146
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000147 /* Max nb of timer types. */
148 TIMER_TYPE_MAX
Benny Prijono834aee32006-02-19 01:38:06 +0000149};
150
151static const char *timer_names[] =
152{
153 "None",
154 "UAC_REFRESH",
155 "UAS_TIMEOUT"
156 "UAC_TERMINATE",
157 "UAC_WAIT_NOTIFY",
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000158 "INVALID_TIMER"
Benny Prijono834aee32006-02-19 01:38:06 +0000159};
160
161/*
162 * Definition of event package.
163 */
164struct evpkg
165{
166 PJ_DECL_LIST_MEMBER(struct evpkg);
167
168 pj_str_t pkg_name;
169 pjsip_module *pkg_mod;
170 unsigned pkg_expires;
171 pjsip_accept_hdr *pkg_accept;
172};
173
174
175/*
176 * Event subscription module (mod-evsub).
177 */
178static struct mod_evsub
179{
180 pjsip_module mod;
181 pj_pool_t *pool;
182 pjsip_endpoint *endpt;
183 struct evpkg pkg_list;
184 pjsip_allow_events_hdr *allow_events_hdr;
185
186} mod_evsub =
187{
188 {
Benny Prijono2f8992b2006-02-25 21:16:36 +0000189 NULL, NULL, /* prev, next. */
190 { "mod-evsub", 9 }, /* Name. */
191 -1, /* Id */
192 PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */
193 NULL, /* load() */
194 NULL, /* start() */
195 NULL, /* stop() */
196 &mod_evsub_unload, /* unload() */
197 NULL, /* on_rx_request() */
198 NULL, /* on_rx_response() */
199 NULL, /* on_tx_request. */
200 NULL, /* on_tx_response() */
201 &mod_evsub_on_tsx_state, /* on_tsx_state() */
Benny Prijono834aee32006-02-19 01:38:06 +0000202 }
203};
204
205
206/*
207 * Event subscription session.
208 */
209struct pjsip_evsub
210{
211 char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
212 pj_pool_t *pool; /**< Pool. */
213 pjsip_endpoint *endpt; /**< Endpoint instance. */
214 pjsip_dialog *dlg; /**< Underlying dialog. */
215 struct evpkg *pkg; /**< The event package. */
Benny Prijono26ff9062006-02-21 23:47:00 +0000216 unsigned option; /**< Options. */
Benny Prijono834aee32006-02-19 01:38:06 +0000217 pjsip_evsub_user user; /**< Callback. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000218 pj_bool_t call_cb; /**< Notify callback? */
Benny Prijono834aee32006-02-19 01:38:06 +0000219 pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */
220 pjsip_evsub_state state; /**< Subscription state. */
221 pj_str_t state_str; /**< String describing the state. */
222 pjsip_evsub_state dst_state; /**< Pending state to be set. */
223 pj_str_t dst_state_str;/**< Pending state to be set. */
224 pjsip_method method; /**< Method that established subscr.*/
225 pjsip_event_hdr *event; /**< Event description. */
226 pjsip_expires_hdr *expires; /**< Expires header */
227 pjsip_accept_hdr *accept; /**< Local Accept header. */
228
229 pj_time_val refresh_time; /**< Time to refresh. */
230 pj_timer_entry timer; /**< Internal timer. */
231 int pending_tsx; /**< Number of pending transactions.*/
Benny Prijono69b98ab2006-03-03 10:23:35 +0000232 pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */
Benny Prijono834aee32006-02-19 01:38:06 +0000233
234 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
235};
236
237
238/*
239 * This is the structure that will be "attached" to dialog.
240 * The purpose is to allow multiple subscriptions inside a dialog.
241 */
242struct dlgsub
243{
244 PJ_DECL_LIST_MEMBER(struct dlgsub);
245 pjsip_evsub *sub;
246};
247
248
249/* Static vars. */
250static const pj_str_t STR_EVENT = { "Event", 5 };
251static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
252static const pj_str_t STR_TERMINATED = { "terminated", 10 };
253static const pj_str_t STR_ACTIVE = { "active", 6 };
254static const pj_str_t STR_PENDING = { "pending", 7 };
255static const pj_str_t STR_TIMEOUT = { "timeout", 7};
256
Benny Prijono26ff9062006-02-21 23:47:00 +0000257
Benny Prijono834aee32006-02-19 01:38:06 +0000258/*
259 * On unload module.
260 */
261static pj_status_t mod_evsub_unload(void)
262{
263 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
264 mod_evsub.pool = NULL;
265
266 return PJ_SUCCESS;
267}
268
Benny Prijono26ff9062006-02-21 23:47:00 +0000269/* Proto for pjsipsimple_strerror().
270 * Defined in errno.c
271 */
272PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode,
273 char *buf, pj_size_t bufsize );
274
Benny Prijono834aee32006-02-19 01:38:06 +0000275/*
276 * Init and register module.
277 */
278PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
279{
280 pj_status_t status;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000281 pj_str_t method_tags[] = {
282 { "SUBSCRIBE", 9},
283 { "NOTIFY", 6}
284 };
Benny Prijono834aee32006-02-19 01:38:06 +0000285
Benny Prijono26ff9062006-02-21 23:47:00 +0000286 pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
287 &pjsipsimple_strerror);
288
Benny Prijono834aee32006-02-19 01:38:06 +0000289 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
290 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
291
292 /* Keep endpoint for future reference: */
293 mod_evsub.endpt = endpt;
294
295 /* Init event package list: */
296 pj_list_init(&mod_evsub.pkg_list);
297
298 /* Create pool: */
299 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 4000, 4000);
300 if (!mod_evsub.pool)
301 return PJ_ENOMEM;
302
303 /* Register module: */
304 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
305 if (status != PJ_SUCCESS)
306 goto on_error;
307
308 /* Create Allow-Events header: */
309 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
310
311 /* Register SIP-event specific headers parser: */
312 pjsip_evsub_init_parser();
313
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000314 /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */
315 pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL,
316 2, method_tags);
317
318 /* Done. */
Benny Prijono834aee32006-02-19 01:38:06 +0000319 return PJ_SUCCESS;
320
321on_error:
322 if (mod_evsub.pool) {
323 pjsip_endpt_release_pool(endpt, mod_evsub.pool);
324 mod_evsub.pool = NULL;
325 }
326 mod_evsub.endpt = NULL;
327 return status;
328}
329
330
331/*
332 * Get the instance of the module.
333 */
334PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
335{
336 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
337
338 return &mod_evsub.mod;
339}
340
341
342/*
343 * Get the event subscription instance in the transaction.
344 */
345PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
346{
Benny Prijono9d4469d2007-05-02 05:14:29 +0000347 return (pjsip_evsub*) tsx->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000348}
349
350
351/*
352 * Set event subscription's module data.
353 */
354PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
355 void *data )
356{
357 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
358 sub->mod_data[mod_id] = data;
359}
360
361
362/*
363 * Get event subscription's module data.
364 */
365PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
366{
367 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
368 return sub->mod_data[mod_id];
369}
370
371
372/*
373 * Find registered event package with matching name.
374 */
375static struct evpkg* find_pkg(const pj_str_t *event_name)
376{
377 struct evpkg *pkg;
378
379 pkg = mod_evsub.pkg_list.next;
380 while (pkg != &mod_evsub.pkg_list) {
381
382 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
383 return pkg;
384 }
385
386 pkg = pkg->next;
387 }
388
389 return NULL;
390}
391
392/*
393 * Register an event package
394 */
395PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
396 const pj_str_t *event_name,
397 unsigned expires,
398 unsigned accept_cnt,
399 const pj_str_t accept[])
400{
401 struct evpkg *pkg;
402 unsigned i;
403
404 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
405 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
406 PJ_ETOOMANY);
407
Benny Prijonoc2456cc2007-10-25 03:19:58 +0000408 /* Make sure evsub module has been initialized */
409 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, PJ_EINVALIDOP);
410
Benny Prijono834aee32006-02-19 01:38:06 +0000411 /* Make sure no module with the specified name already registered: */
412
413 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
414
415
416 /* Create new event package: */
417
Benny Prijono9d4469d2007-05-02 05:14:29 +0000418 pkg = PJ_POOL_ALLOC_T(mod_evsub.pool, struct evpkg);
Benny Prijono834aee32006-02-19 01:38:06 +0000419 pkg->pkg_mod = pkg_mod;
420 pkg->pkg_expires = expires;
421 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
422
423 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
424 pkg->pkg_accept->count = accept_cnt;
425 for (i=0; i<accept_cnt; ++i) {
426 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
427 }
428
429 /* Add to package list: */
430
431 pj_list_push_back(&mod_evsub.pkg_list, pkg);
432
433 /* Add to Allow-Events header: */
434
435 if (mod_evsub.allow_events_hdr->count !=
436 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
437 {
438 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
439 pkg->pkg_name;
440 ++mod_evsub.allow_events_hdr->count;
441 }
442
Benny Prijono56315612006-07-18 14:39:40 +0000443 /* Add to endpoint's Accept header */
444 pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod,
445 PJSIP_H_ACCEPT, NULL,
446 pkg->pkg_accept->count,
447 pkg->pkg_accept->values);
448
Benny Prijono834aee32006-02-19 01:38:06 +0000449
450 /* Done */
451
452 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
453 (int)event_name->slen, event_name->ptr,
454 (int)pkg_mod->name.slen, pkg_mod->name.ptr));
455
456 return PJ_SUCCESS;
457}
458
459
Benny Prijono56315612006-07-18 14:39:40 +0000460/*
461 * Retrieve Allow-Events header
462 */
463PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m)
464{
465 struct mod_evsub *mod;
466
467 if (m == NULL)
468 m = pjsip_evsub_instance();
469
470 mod = (struct mod_evsub*)m;
471
472 return (pjsip_hdr*) mod->allow_events_hdr;
473}
474
Benny Prijono834aee32006-02-19 01:38:06 +0000475
476/*
477 * Update expiration time.
478 */
479static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
480{
481 pj_gettimeofday(&sub->refresh_time);
482 sub->refresh_time.sec += interval;
483}
484
485
486/*
487 * Schedule timer.
488 */
489static void set_timer( pjsip_evsub *sub, int timer_id,
490 pj_int32_t seconds)
491{
492 if (sub->timer.id != TIMER_TYPE_NONE) {
493 PJ_LOG(5,(sub->obj_name, "%s %s timer",
494 (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
495 timer_names[sub->timer.id]));
496 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
497 sub->timer.id = TIMER_TYPE_NONE;
498 }
499
500 if (timer_id != TIMER_TYPE_NONE) {
501 pj_time_val timeout;
502
503 PJ_ASSERT_ON_FAIL(seconds > 0, return);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000504 PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX,
505 return);
Benny Prijono834aee32006-02-19 01:38:06 +0000506
507 timeout.sec = seconds;
508 timeout.msec = 0;
509 sub->timer.id = timer_id;
510
511 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
512
513 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
514 timer_names[sub->timer.id], timeout.sec));
515 }
516}
517
518
519/*
520 * Destroy session.
521 */
522static void evsub_destroy( pjsip_evsub *sub )
523{
524 struct dlgsub *dlgsub_head, *dlgsub;
525
526 PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
527
528 /* Kill timer */
529 set_timer(sub, TIMER_TYPE_NONE, 0);
530
Benny Prijono9d4469d2007-05-02 05:14:29 +0000531 /* Remove this session from dialog's list of subscription */
532 dlgsub_head = (struct dlgsub *) sub->dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +0000533 dlgsub = dlgsub_head->next;
534 while (dlgsub != dlgsub_head) {
535
536 if (dlgsub->sub == sub) {
537 pj_list_erase(dlgsub);
538 break;
539 }
540
541 dlgsub = dlgsub->next;
542 }
543
544 /* Decrement dialog's session */
545 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
546}
547
548/*
549 * Set subscription session state.
550 */
551static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
552 const pj_str_t *state_str, pjsip_event *event)
553{
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000554 pjsip_evsub_state prev_state = sub->state;
Benny Prijono834aee32006-02-19 01:38:06 +0000555 pj_str_t old_state_str = sub->state_str;
556
557 sub->state = state;
558
559 if (state_str && state_str->slen)
560 pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
561 else
562 sub->state_str = evsub_state_names[state];
563
564 PJ_LOG(4,(sub->obj_name,
565 "Subscription state changed %.*s --> %.*s",
566 (int)old_state_str.slen,
567 old_state_str.ptr,
568 (int)sub->state_str.slen,
569 sub->state_str.ptr));
570
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000571 if (sub->user.on_evsub_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +0000572 (*sub->user.on_evsub_state)(sub, event);
573
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000574 if (state == PJSIP_EVSUB_STATE_TERMINATED &&
575 prev_state != PJSIP_EVSUB_STATE_TERMINATED)
576 {
Benny Prijono834aee32006-02-19 01:38:06 +0000577 if (sub->pending_tsx == 0) {
578 evsub_destroy(sub);
579 }
580 }
581}
582
583
584/*
585 * Timer callback.
586 */
587static void on_timer( pj_timer_heap_t *timer_heap,
588 struct pj_timer_entry *entry)
589{
590 pjsip_evsub *sub;
591 int timer_id;
592
593 PJ_UNUSED_ARG(timer_heap);
594
Benny Prijono9d4469d2007-05-02 05:14:29 +0000595 sub = (pjsip_evsub*) entry->user_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000596
597 pjsip_dlg_inc_lock(sub->dlg);
598
599 timer_id = entry->id;
600 entry->id = TIMER_TYPE_NONE;
601
602 switch (timer_id) {
603
604 case TIMER_TYPE_UAC_REFRESH:
605 /* Time for UAC to refresh subscription */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000606 if (sub->user.on_client_refresh && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000607 (*sub->user.on_client_refresh)(sub);
608 } else {
609 pjsip_tx_data *tdata;
610 pj_status_t status;
611
612 PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000613 status = pjsip_evsub_initiate(sub, NULL,
Benny Prijono834aee32006-02-19 01:38:06 +0000614 sub->expires->ivalue,
615 &tdata);
616 if (status == PJ_SUCCESS)
617 pjsip_evsub_send_request(sub, tdata);
618 }
619 break;
620
621 case TIMER_TYPE_UAS_TIMEOUT:
622 /* Refresh from UAC has not been received */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000623 if (sub->user.on_server_timeout && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +0000624 (*sub->user.on_server_timeout)(sub);
625 } else {
626 pjsip_tx_data *tdata;
627 pj_status_t status;
628
629 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
630 "Sending NOTIFY to terminate."));
631 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
632 NULL, &STR_TIMEOUT, &tdata);
633 if (status == PJ_SUCCESS)
634 pjsip_evsub_send_request(sub, tdata);
635 }
636 break;
637
638 case TIMER_TYPE_UAC_TERMINATE:
639 {
Benny Prijono834aee32006-02-19 01:38:06 +0000640 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
641 "Terminating.."));
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000642 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +0000643 }
644 break;
645
646 case TIMER_TYPE_UAC_WAIT_NOTIFY:
647 {
648 pjsip_tx_data *tdata;
649 pj_status_t status;
650
651 PJ_LOG(5,(sub->obj_name,
652 "Timeout waiting for subsequent NOTIFY (we did "
653 "send non-2xx response for previous NOTIFY). "
654 "Unsubscribing.."));
Benny Prijono736d0f72006-09-13 22:45:38 +0000655 status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +0000656 if (status == PJ_SUCCESS)
657 pjsip_evsub_send_request(sub, tdata);
658 }
659 break;
660
661 default:
662 pj_assert(!"Invalid timer id");
663 }
664
665 pjsip_dlg_dec_lock(sub->dlg);
666}
667
668
669/*
670 * Create subscription session, used for both client and notifier.
671 */
672static pj_status_t evsub_create( pjsip_dialog *dlg,
673 pjsip_role_e role,
674 const pjsip_evsub_user *user_cb,
675 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000676 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000677 pjsip_evsub **p_evsub )
678{
679 pjsip_evsub *sub;
680 struct evpkg *pkg;
681 struct dlgsub *dlgsub_head, *dlgsub;
682 pj_status_t status;
683
684 /* Make sure there's package register for the event name: */
685
686 pkg = find_pkg(event);
687 if (pkg == NULL)
688 return PJSIP_SIMPLE_ENOPKG;
689
690
Benny Prijono8eae8382006-08-10 21:44:26 +0000691 /* Must lock dialog before using pool etc. */
692 pjsip_dlg_inc_lock(dlg);
693
Benny Prijono834aee32006-02-19 01:38:06 +0000694 /* Init attributes: */
695
Benny Prijono9d4469d2007-05-02 05:14:29 +0000696 sub = PJ_POOL_ZALLOC_T(dlg->pool, struct pjsip_evsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000697 sub->pool = dlg->pool;
698 sub->endpt = dlg->endpt;
699 sub->dlg = dlg;
700 sub->pkg = pkg;
701 sub->role = role;
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000702 sub->call_cb = PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000703 sub->option = option;
Benny Prijono834aee32006-02-19 01:38:06 +0000704 sub->state = PJSIP_EVSUB_STATE_NULL;
705 sub->state_str = evsub_state_names[sub->state];
706 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
Benny Prijono9d4469d2007-05-02 05:14:29 +0000707 sub->accept = (pjsip_accept_hdr*)
708 pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
Benny Prijono834aee32006-02-19 01:38:06 +0000709
710 sub->timer.user_data = sub;
711 sub->timer.cb = &on_timer;
712
713 /* Set name. */
Benny Prijonoed811d72006-03-10 12:57:12 +0000714 pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
715 "evsub%p", sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000716
717
718 /* Copy callback, if any: */
719 if (user_cb)
720 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
721
722
723 /* Create Event header: */
724 sub->event = pjsip_event_hdr_create(sub->pool);
725 pj_strdup(sub->pool, &sub->event->event_type, event);
726
727
728 /* Create subcription list: */
729
Benny Prijono9d4469d2007-05-02 05:14:29 +0000730 dlgsub_head = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
731 dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +0000732 dlgsub->sub = sub;
733
734 pj_list_init(dlgsub_head);
735 pj_list_push_back(dlgsub_head, dlgsub);
736
737
738 /* Register as dialog usage: */
739
740 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
Benny Prijono8eae8382006-08-10 21:44:26 +0000741 if (status != PJ_SUCCESS) {
742 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000743 return status;
Benny Prijono8eae8382006-08-10 21:44:26 +0000744 }
Benny Prijono834aee32006-02-19 01:38:06 +0000745
746
747 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
748 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
749 dlg->obj_name));
750
751 *p_evsub = sub;
Benny Prijono8eae8382006-08-10 21:44:26 +0000752 pjsip_dlg_dec_lock(dlg);
Benny Prijono834aee32006-02-19 01:38:06 +0000753
754 return PJ_SUCCESS;
755}
756
757
758
759/*
760 * Create client subscription session.
761 */
762PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
763 const pjsip_evsub_user *user_cb,
764 const pj_str_t *event,
Benny Prijono26ff9062006-02-21 23:47:00 +0000765 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000766 pjsip_evsub **p_evsub)
767{
768 pjsip_evsub *sub;
769 pj_status_t status;
770
771 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
772
773 pjsip_dlg_inc_lock(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000774 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000775 if (status != PJ_SUCCESS)
776 goto on_return;
777
Benny Prijono26ff9062006-02-21 23:47:00 +0000778 /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
779 * is not specified.
780 */
781 if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
782 pj_create_unique_string(sub->pool, &sub->event->id_param);
783 }
Benny Prijono834aee32006-02-19 01:38:06 +0000784
785 /* Increment dlg session. */
786 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
787
788 /* Done */
789 *p_evsub = sub;
790
791on_return:
792 pjsip_dlg_dec_lock(dlg);
793 return status;
794}
795
796
797/*
798 * Create server subscription session from incoming request.
799 */
800PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
801 const pjsip_evsub_user *user_cb,
802 pjsip_rx_data *rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000803 unsigned option,
Benny Prijono834aee32006-02-19 01:38:06 +0000804 pjsip_evsub **p_evsub)
805{
806 pjsip_evsub *sub;
807 pjsip_transaction *tsx;
808 pjsip_accept_hdr *accept_hdr;
809 pjsip_event_hdr *event_hdr;
810 pjsip_expires_hdr *expires_hdr;
811 pj_status_t status;
812
813 /* Check arguments: */
814 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
815
816 /* MUST be request message: */
817 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
818 PJSIP_ENOTREQUESTMSG);
819
820 /* Transaction MUST have been created (in the dialog) */
821 tsx = pjsip_rdata_get_tsx(rdata);
822 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
823
824 /* No subscription must have been attached to transaction */
825 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
826 PJSIP_ETYPEEXISTS);
827
828 /* Package MUST implement on_rx_refresh */
829 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
830
Benny Prijono26ff9062006-02-21 23:47:00 +0000831 /* Request MUST have "Event" header. We need the Event header to get
832 * the package name (don't want to add more arguments in the function).
833 */
Benny Prijono834aee32006-02-19 01:38:06 +0000834 event_hdr = (pjsip_event_hdr*)
835 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
836 if (event_hdr == NULL) {
837 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
838 }
839
840 /* Start locking the mutex: */
841
842 pjsip_dlg_inc_lock(dlg);
843
844 /* Create the session: */
845
846 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
Benny Prijono26ff9062006-02-21 23:47:00 +0000847 &event_hdr->event_type, option, &sub);
Benny Prijono834aee32006-02-19 01:38:06 +0000848 if (status != PJ_SUCCESS)
849 goto on_return;
850
851 /* Just duplicate Event header from the request */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000852 sub->event = (pjsip_event_hdr*) pjsip_hdr_clone(sub->pool, event_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000853
854 /* Set the method: */
855 pjsip_method_copy(sub->pool, &sub->method,
856 &rdata->msg_info.msg->line.req.method);
857
858 /* Update expiration time according to client request: */
859
860 expires_hdr = (pjsip_expires_hdr*)
861 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
862 if (expires_hdr) {
863 sub->expires->ivalue = expires_hdr->ivalue;
864 }
865
866 /* Update time. */
867 update_expires(sub, sub->expires->ivalue);
868
869 /* Update Accept header: */
870
871 accept_hdr = (pjsip_accept_hdr*)
872 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
873 if (accept_hdr)
Benny Prijono9d4469d2007-05-02 05:14:29 +0000874 sub->accept = (pjsip_accept_hdr*)pjsip_hdr_clone(sub->pool,accept_hdr);
Benny Prijono834aee32006-02-19 01:38:06 +0000875
876 /* We can start the session: */
877
878 pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
879 sub->pending_tsx++;
880 tsx->mod_data[mod_evsub.mod.id] = sub;
881
882
883 /* Done. */
884 *p_evsub = sub;
885
886
887on_return:
888 pjsip_dlg_dec_lock(dlg);
889 return status;
890}
891
892
893/*
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000894 * Forcefully destroy subscription.
895 */
896PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
897 pj_bool_t notify )
898{
899 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
900
901 pjsip_dlg_inc_lock(sub->dlg);
902
Benny Prijonod524e822006-09-22 12:48:18 +0000903 /* I think it's pretty safe to disable this check.
904
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000905 if (sub->pending_tsx) {
906 pj_assert(!"Unable to terminate when there's pending tsx");
907 pjsip_dlg_dec_lock(sub->dlg);
908 return PJ_EINVALIDOP;
909 }
Benny Prijonod524e822006-09-22 12:48:18 +0000910 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +0000911
912 sub->call_cb = notify;
913 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
914
915 pjsip_dlg_dec_lock(sub->dlg);
916 return PJ_SUCCESS;
917}
918
919/*
Benny Prijono834aee32006-02-19 01:38:06 +0000920 * Get subscription state.
921 */
922PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
923{
924 return sub->state;
925}
926
927/*
928 * Get state name.
929 */
930PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
931{
932 return sub->state_str.ptr;
933}
934
935
936/*
937 * Initiate client subscription
938 */
939PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
940 const pjsip_method *method,
941 pj_int32_t expires,
942 pjsip_tx_data **p_tdata)
943{
944 pjsip_tx_data *tdata;
945 pj_status_t status;
946
947 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
948
949 /* Use SUBSCRIBE if method is not specified */
950 if (method == NULL)
951 method = &pjsip_subscribe_method;
952
953 pjsip_dlg_inc_lock(sub->dlg);
954
955 /* Update method: */
956 if (sub->state == PJSIP_EVSUB_STATE_NULL)
957 pjsip_method_copy(sub->pool, &sub->method, method);
958
959 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
960 if (status != PJ_SUCCESS)
961 goto on_return;
962
963
964 /* Add Event header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000965 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000966 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
967
968 /* Update and add expires header: */
969 if (expires >= 0)
970 sub->expires->ivalue = expires;
Benny Prijono9d4469d2007-05-02 05:14:29 +0000971 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000972 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
973
974 /* Add Accept header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000975 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000976 pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
977
978
979 /* Add Allow-Events header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +0000980 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +0000981 pjsip_hdr_shallow_clone(tdata->pool,
982 mod_evsub.allow_events_hdr));
983
984
985 *p_tdata = tdata;
986
987
988on_return:
989
990 pjsip_dlg_dec_lock(sub->dlg);
991 return status;
992}
993
994
995/*
996 * Accept incoming subscription request.
997 */
998PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
999 pjsip_rx_data *rdata,
1000 int st_code,
1001 const pjsip_hdr *hdr_list )
1002{
1003 pjsip_tx_data *tdata;
1004 pjsip_transaction *tsx;
1005 pj_status_t status;
1006
1007 /* Check arguments */
1008 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
1009
1010 /* Can only be for server subscription: */
1011 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
1012
1013 /* Only expect 2xx status code (for now) */
1014 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
1015
1016 /* Subscription MUST have been attached to the transaction.
1017 * Initial subscription request will be attached on evsub_create_uas(),
1018 * while subsequent requests will be attached in tsx_state()
1019 */
1020 tsx = pjsip_rdata_get_tsx(rdata);
1021 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
1022 PJ_EINVALIDOP);
1023
1024 /* Lock dialog */
1025 pjsip_dlg_inc_lock(sub->dlg);
1026
1027 /* Create response: */
1028 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
1029 &tdata);
1030 if (status != PJ_SUCCESS)
1031 goto on_return;
1032
1033
1034 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001035 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001036 pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
1037
Benny Prijonob0808372006-03-02 21:18:58 +00001038 /* Add additional header, if any. */
1039 if (hdr_list) {
1040 const pjsip_hdr *hdr = hdr_list->next;
1041 while (hdr != hdr_list) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001042 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonob0808372006-03-02 21:18:58 +00001043 pjsip_hdr_clone(tdata->pool, hdr));
1044 hdr = hdr->next;
1045 }
1046 }
Benny Prijono834aee32006-02-19 01:38:06 +00001047
1048 /* Send the response: */
1049 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
1050 if (status != PJ_SUCCESS)
1051 goto on_return;
1052
1053
1054on_return:
1055
1056 pjsip_dlg_dec_lock(sub->dlg);
1057 return status;
1058}
1059
1060
1061/*
1062 * Create Subscription-State header based on current server subscription
1063 * state.
1064 */
1065static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
1066 pjsip_evsub *sub,
1067 pjsip_evsub_state state,
1068 const pj_str_t *state_str,
1069 const pj_str_t *reason )
1070{
1071 pjsip_sub_state_hdr *sub_state;
1072 pj_time_val now, delay;
1073
1074 /* Get the remaining time before refresh is required */
1075 pj_gettimeofday(&now);
1076 delay = sub->refresh_time;
1077 PJ_TIME_VAL_SUB(delay, now);
1078
1079 /* Create the Subscription-State header */
1080 sub_state = pjsip_sub_state_hdr_create(pool);
1081
1082 /* Fill up the header */
1083 switch (state) {
Benny Prijonof80b1bf2006-02-19 02:24:27 +00001084 case PJSIP_EVSUB_STATE_NULL:
Benny Prijono834aee32006-02-19 01:38:06 +00001085 case PJSIP_EVSUB_STATE_SENT:
1086 case PJSIP_EVSUB_STATE_ACCEPTED:
1087 pj_assert(!"Invalid state!");
1088 /* Treat as pending */
1089
1090 case PJSIP_EVSUB_STATE_PENDING:
1091 sub_state->sub_state = STR_PENDING;
1092 sub_state->expires_param = delay.sec;
1093 break;
1094
1095 case PJSIP_EVSUB_STATE_ACTIVE:
1096 sub_state->sub_state = STR_ACTIVE;
1097 sub_state->expires_param = delay.sec;
1098 break;
1099
1100 case PJSIP_EVSUB_STATE_TERMINATED:
1101 sub_state->sub_state = STR_TERMINATED;
1102 if (reason != NULL)
1103 pj_strdup(pool, &sub_state->reason_param, reason);
1104 break;
1105
1106 case PJSIP_EVSUB_STATE_UNKNOWN:
1107 pj_assert(state_str != NULL);
1108 pj_strdup(pool, &sub_state->sub_state, state_str);
1109 break;
1110 }
1111
1112 return sub_state;
1113}
1114
1115/*
1116 * Create and send NOTIFY request.
1117 */
1118PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
1119 pjsip_evsub_state state,
1120 const pj_str_t *state_str,
1121 const pj_str_t *reason,
1122 pjsip_tx_data **p_tdata)
1123{
1124 pjsip_tx_data *tdata;
1125 pjsip_sub_state_hdr *sub_state;
1126 pj_status_t status;
1127
1128 /* Check arguments. */
1129 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
1130
1131 /* Lock dialog. */
1132 pjsip_dlg_inc_lock(sub->dlg);
1133
1134 /* Create NOTIFY request */
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001135 status = pjsip_dlg_create_request( sub->dlg, pjsip_get_notify_method(),
1136 -1, &tdata);
Benny Prijono834aee32006-02-19 01:38:06 +00001137 if (status != PJ_SUCCESS)
1138 goto on_return;
1139
1140 /* Add Event header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001141 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001142 pjsip_hdr_shallow_clone(tdata->pool, sub->event));
1143
1144 /* Add Subscription-State header */
1145 sub_state = sub_state_create(tdata->pool, sub, state, state_str,
1146 reason);
1147 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
1148
1149 /* Add Allow-Events header */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001150 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001151 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
1152
1153 /* Add Authentication headers. */
1154 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
1155
1156
1157
1158 /* Save destination state. */
1159 sub->dst_state = state;
1160 if (state_str)
1161 pj_strdup(sub->pool, &sub->dst_state_str, state_str);
1162 else
1163 sub->dst_state_str.slen = 0;
1164
1165
1166 *p_tdata = tdata;
1167
1168on_return:
1169 /* Unlock dialog */
1170 pjsip_dlg_dec_lock(sub->dlg);
1171 return status;
1172}
1173
1174
1175/*
1176 * Create NOTIFY to reflect current status.
1177 */
1178PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
1179 pjsip_tx_data **p_tdata )
1180{
1181 return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
1182 NULL, p_tdata );
1183}
1184
1185
1186/*
1187 * Send request.
1188 */
1189PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
1190 pjsip_tx_data *tdata)
1191{
1192 pj_status_t status;
1193
1194 /* Must be request message. */
1195 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
1196 PJSIP_ENOTREQUESTMSG);
1197
1198 /* Lock */
1199 pjsip_dlg_inc_lock(sub->dlg);
1200
1201 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001202 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001203 if (status != PJ_SUCCESS)
1204 goto on_return;
1205
1206
1207 /* Special case for NOTIFY:
1208 * The new state was set in pjsip_evsub_notify(), but we apply the
1209 * new state now, when the request was actually sent.
1210 */
1211 if (pjsip_method_cmp(&tdata->msg->line.req.method,
1212 &pjsip_notify_method)==0)
1213 {
1214 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
1215 {goto on_return;});
1216
1217 set_state(sub, sub->dst_state,
1218 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
1219 NULL);
1220
1221 sub->dst_state = PJSIP_EVSUB_STATE_NULL;
1222 sub->dst_state_str.slen = 0;
1223
1224 }
1225
1226
1227on_return:
1228 pjsip_dlg_dec_lock(sub->dlg);
1229 return status;
1230}
1231
1232
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001233/* Callback to be called to terminate transaction. */
1234static void terminate_timer_cb(pj_timer_heap_t *timer_heap,
1235 struct pj_timer_entry *entry)
1236{
1237 pj_str_t *key;
1238 pjsip_transaction *tsx;
1239
1240 PJ_UNUSED_ARG(timer_heap);
1241
1242 key = (pj_str_t*)entry->user_data;
1243 tsx = pjsip_tsx_layer_find_tsx(key, PJ_FALSE);
1244 /* Chance of race condition here */
1245 if (tsx) {
1246 pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_UPDATED);
1247 }
1248}
1249
Benny Prijono834aee32006-02-19 01:38:06 +00001250
1251/*
1252 * Attach subscription session to newly created transaction, if appropriate.
1253 */
1254static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
1255 pjsip_event *event)
1256{
1257 /*
1258 * Newly created transaction will not have subscription session
1259 * attached to it. Find the subscription session from the dialog,
1260 * by matching the Event header.
1261 */
1262 pjsip_dialog *dlg;
1263 pjsip_event_hdr *event_hdr;
1264 pjsip_msg *msg;
1265 struct dlgsub *dlgsub_head, *dlgsub;
1266 pjsip_evsub *sub;
1267
1268 dlg = pjsip_tsx_get_dlg(tsx);
1269 if (!dlg) {
1270 pj_assert(!"Transaction should have a dialog instance!");
1271 return NULL;
1272 }
1273
Benny Prijono26ff9062006-02-21 23:47:00 +00001274
Benny Prijono834aee32006-02-19 01:38:06 +00001275 switch (event->body.tsx_state.type) {
1276 case PJSIP_EVENT_RX_MSG:
1277 msg = event->body.tsx_state.src.rdata->msg_info.msg;
1278 break;
1279 case PJSIP_EVENT_TX_MSG:
1280 msg = event->body.tsx_state.src.tdata->msg;
1281 break;
1282 default:
1283 if (tsx->role == PJSIP_ROLE_UAC)
1284 msg = tsx->last_tx->msg;
1285 else
1286 msg = NULL;
1287 break;
1288 }
1289
1290 if (!msg) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001291 //Note:
1292 // this transaction can be other transaction in the dialog.
1293 // The assertion below probably only valid for dialog that
1294 // only has one event subscription usage.
1295 //pj_assert(!"First transaction event is not TX or RX!");
Benny Prijono834aee32006-02-19 01:38:06 +00001296 return NULL;
1297 }
1298
Benny Prijono9d4469d2007-05-02 05:14:29 +00001299 event_hdr = (pjsip_event_hdr*)
1300 pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001301 if (!event_hdr) {
1302 /* Not subscription related message */
1303 return NULL;
1304 }
1305
1306 /* Find the subscription in the dialog, based on the content
1307 * of Event header:
1308 */
1309
Benny Prijono9d4469d2007-05-02 05:14:29 +00001310 dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id];
Benny Prijono834aee32006-02-19 01:38:06 +00001311 if (dlgsub_head == NULL) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001312 dlgsub_head = PJ_POOL_ALLOC_T(dlg->pool, struct dlgsub);
Benny Prijono834aee32006-02-19 01:38:06 +00001313 pj_list_init(dlgsub_head);
1314 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
1315 }
1316 dlgsub = dlgsub_head->next;
1317
1318 while (dlgsub != dlgsub_head) {
1319
Benny Prijono26ff9062006-02-21 23:47:00 +00001320 if (pj_stricmp(&dlgsub->sub->event->event_type,
1321 &event_hdr->event_type)==0)
Benny Prijono834aee32006-02-19 01:38:06 +00001322 {
Benny Prijono26ff9062006-02-21 23:47:00 +00001323 /* Event type matched.
1324 * Check if event ID matched too.
1325 */
1326 if (pj_strcmp(&dlgsub->sub->event->id_param,
1327 &event_hdr->id_param)==0)
1328 {
1329
1330 break;
1331
1332 }
1333 /*
1334 * Otherwise if it is an UAC subscription, AND
1335 * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
1336 * the session's event id is NULL, AND
1337 * the incoming request is NOTIFY with event ID, then
1338 * we consider it as a match, and update the
1339 * session's event id.
1340 */
1341 else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
1342 (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
1343 dlgsub->sub->event->id_param.slen==0 &&
1344 !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
1345 {
1346 /* Update session's event id. */
1347 pj_strdup(dlgsub->sub->pool,
1348 &dlgsub->sub->event->id_param,
1349 &event_hdr->id_param);
1350
1351 break;
1352 }
Benny Prijono834aee32006-02-19 01:38:06 +00001353 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001354
1355
1356
Benny Prijono834aee32006-02-19 01:38:06 +00001357 dlgsub = dlgsub->next;
1358 }
1359
1360 if (dlgsub == dlgsub_head) {
1361 /* This could be incoming request to create new subscription */
1362 PJ_LOG(4,(THIS_FILE,
1363 "Subscription not found for %.*s, event=%.*s;id=%.*s",
Benny Prijono26ff9062006-02-21 23:47:00 +00001364 (int)tsx->method.name.slen,
1365 tsx->method.name.ptr,
Benny Prijono834aee32006-02-19 01:38:06 +00001366 (int)event_hdr->event_type.slen,
1367 event_hdr->event_type.ptr,
1368 (int)event_hdr->id_param.slen,
1369 event_hdr->id_param.ptr));
1370
1371 /* If this is an incoming NOTIFY, reject with 481 */
1372 if (tsx->state == PJSIP_TSX_STATE_TRYING &&
1373 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
1374 {
1375 pj_str_t reason = pj_str("Subscription Does Not Exist");
1376 pjsip_tx_data *tdata;
1377 pj_status_t status;
1378
1379 status = pjsip_dlg_create_response(dlg,
1380 event->body.tsx_state.src.rdata,
1381 481, &reason,
1382 &tdata);
1383 if (status == PJ_SUCCESS) {
1384 status = pjsip_dlg_send_response(dlg, tsx, tdata);
1385 }
1386 }
1387 return NULL;
1388 }
1389
1390 /* Found! */
1391 sub = dlgsub->sub;
1392
1393 /* Attach session to the transaction */
1394 tsx->mod_data[mod_evsub.mod.id] = sub;
1395 sub->pending_tsx++;
1396
Benny Prijono69b98ab2006-03-03 10:23:35 +00001397 /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction.
1398 * We can only have one pending UAC SUBSCRIBE/REFER, so if another
1399 * transaction is started while previous one still alive, terminate
1400 * the older one.
1401 *
1402 * Sample scenario:
1403 * - subscribe sent to destination that doesn't exist, transaction
1404 * is still retransmitting request, then unsubscribe is sent.
1405 */
1406 if (tsx->role == PJSIP_ROLE_UAC &&
1407 tsx->state == PJSIP_TSX_STATE_CALLING &&
Benny Prijono736d0f72006-09-13 22:45:38 +00001408 (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1409 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
Benny Prijono69b98ab2006-03-03 10:23:35 +00001410 {
1411
1412 if (sub->pending_sub &&
1413 sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED)
1414 {
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001415 pj_timer_entry *timer;
1416 pj_str_t *key;
1417 pj_time_val timeout = {0, 0};
1418
Benny Prijono69b98ab2006-03-03 10:23:35 +00001419 PJ_LOG(4,(sub->obj_name,
Benny Prijono736d0f72006-09-13 22:45:38 +00001420 "Cancelling pending subscription request"));
Benny Prijono69b98ab2006-03-03 10:23:35 +00001421
1422 /* By convention, we use 490 (Request Updated) status code.
1423 * When transaction handler (below) see this status code, it
1424 * will ignore the transaction.
1425 */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001426 /* This unfortunately may cause deadlock, because at the moment
1427 * we are holding dialog's mutex. If a response to this
1428 * transaction is in progress in another thread, that thread
1429 * will deadlock when trying to acquire dialog mutex, because
1430 * it is holding the transaction mutex.
1431 *
1432 * So the solution is to register timer to kill this transaction.
1433 */
1434 //pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
1435 timer = PJ_POOL_ZALLOC_T(dlg->pool, pj_timer_entry);
1436 key = PJ_POOL_ALLOC_T(dlg->pool, pj_str_t);
1437 pj_strdup(dlg->pool, key, &sub->pending_sub->transaction_key);
1438 timer->cb = &terminate_timer_cb;
1439 timer->user_data = key;
1440
1441 pjsip_endpt_schedule_timer(dlg->endpt, timer, &timeout);
Benny Prijono69b98ab2006-03-03 10:23:35 +00001442 }
1443
1444 sub->pending_sub = tsx;
1445
Benny Prijono69b98ab2006-03-03 10:23:35 +00001446 }
1447
Benny Prijono834aee32006-02-19 01:38:06 +00001448 return sub;
1449}
1450
1451
1452/*
1453 * Create response, adding custome headers and msg body.
1454 */
1455static pj_status_t create_response( pjsip_evsub *sub,
1456 pjsip_rx_data *rdata,
1457 int st_code,
1458 const pj_str_t *st_text,
1459 const pjsip_hdr *res_hdr,
1460 const pjsip_msg_body *body,
1461 pjsip_tx_data **p_tdata)
1462{
1463 pjsip_tx_data *tdata;
1464 pjsip_hdr *hdr;
1465 pj_status_t status;
1466
1467 status = pjsip_dlg_create_response(sub->dlg, rdata,
1468 st_code, st_text, &tdata);
1469 if (status != PJ_SUCCESS)
1470 return status;
1471
1472 *p_tdata = tdata;
1473
1474 /* Add response headers. */
1475 hdr = res_hdr->next;
1476 while (hdr != res_hdr) {
Benny Prijono9d4469d2007-05-02 05:14:29 +00001477 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001478 pjsip_hdr_clone(tdata->pool, hdr));
1479 hdr = hdr->next;
1480 }
1481
1482 /* Add msg body, if any */
1483 if (body) {
Benny Prijonob0808372006-03-02 21:18:58 +00001484 tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
1485 if (tdata->msg->body == NULL) {
1486
1487 PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));
1488
Benny Prijono834aee32006-02-19 01:38:06 +00001489 /* Ignore */
1490 return PJ_SUCCESS;
1491 }
1492 }
1493
1494 return PJ_SUCCESS;
1495}
1496
1497/*
1498 * Get subscription state from the value of Subscription-State header.
1499 */
1500static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
1501 pjsip_evsub_state *state,
1502 pj_str_t **state_str )
1503{
1504 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
1505
1506 *state = PJSIP_EVSUB_STATE_TERMINATED;
1507 *state_str = NULL;
1508
1509 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
1510
1511 *state = PJSIP_EVSUB_STATE_ACTIVE;
1512 *state_str = NULL;
1513
1514 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
1515
1516 *state = PJSIP_EVSUB_STATE_PENDING;
1517 *state_str = NULL;
1518
1519 } else {
1520
1521 *state = PJSIP_EVSUB_STATE_UNKNOWN;
1522 *state_str = &sub_state->sub_state;
1523
1524 }
1525}
1526
1527/*
1528 * Transaction event processing by UAC, after subscription is sent.
1529 */
1530static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
1531 pjsip_event *event )
1532{
1533
Benny Prijono736d0f72006-09-13 22:45:38 +00001534 if (pjsip_method_cmp(&tsx->method, &sub->method)==0 ||
1535 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0)
1536 {
Benny Prijono834aee32006-02-19 01:38:06 +00001537
1538 /* Received response to outgoing request that establishes/refresh
1539 * subscription.
1540 */
1541
1542 /* First time initial request is sent. */
1543 if (sub->state == PJSIP_EVSUB_STATE_NULL &&
1544 tsx->state == PJSIP_TSX_STATE_CALLING)
1545 {
1546 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
1547 return;
1548 }
1549
1550 /* Only interested in final response */
1551 if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
1552 tsx->state != PJSIP_TSX_STATE_TERMINATED)
1553 {
1554 return;
1555 }
1556
Benny Prijono1d8d6082006-04-29 12:38:25 +00001557 /* Clear pending subscription */
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001558 if (tsx == sub->pending_sub) {
Benny Prijono1d8d6082006-04-29 12:38:25 +00001559 sub->pending_sub = NULL;
Benny Prijonoefb9b6b2007-06-27 12:52:35 +00001560 } else if (sub->pending_sub != NULL) {
1561 /* This SUBSCRIBE transaction has been "renewed" with another
1562 * SUBSCRIBE, so we can just ignore this. For example, user
1563 * sent SUBSCRIBE followed immediately with UN-SUBSCRIBE.
1564 */
1565 return;
1566 }
Benny Prijono1d8d6082006-04-29 12:38:25 +00001567
Benny Prijono834aee32006-02-19 01:38:06 +00001568 /* Handle authentication. */
1569 if (tsx->status_code==401 || tsx->status_code==407) {
1570 pjsip_tx_data *tdata;
1571 pj_status_t status;
1572
1573 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1574 /* Previously failed transaction has terminated */
1575 return;
1576 }
1577
1578 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
1579 event->body.tsx_state.src.rdata,
1580 tsx->last_tx, &tdata);
1581 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001582 status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001583
1584 if (status != PJ_SUCCESS) {
1585 /* Authentication failed! */
1586 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1587 NULL,
1588 event);
1589 return;
1590 }
1591
1592 return;
1593 }
1594
1595 if (tsx->status_code/100 == 2) {
1596
1597 /* Successfull SUBSCRIBE request!
1598 * This could be:
1599 * - response to initial SUBSCRIBE request
1600 * - response to subsequent refresh
1601 * - response to unsubscription
1602 */
1603
1604 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
1605 /* Ignore; this transaction has been processed before */
1606 return;
1607 }
1608
1609 /* Update UAC refresh time, if response contains Expires header,
1610 * only when we're not unsubscribing.
1611 */
1612 if (sub->expires->ivalue != 0) {
1613 pjsip_msg *msg;
1614 pjsip_expires_hdr *expires;
1615
1616 msg = event->body.tsx_state.src.rdata->msg_info.msg;
Benny Prijono9d4469d2007-05-02 05:14:29 +00001617 expires = (pjsip_expires_hdr*)
1618 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001619 if (expires) {
1620 sub->expires->ivalue = expires->ivalue;
1621 }
1622 }
1623
1624 /* Update time */
1625 update_expires(sub, sub->expires->ivalue);
1626
1627 /* Start UAC refresh timer, only when we're not unsubscribing */
1628 if (sub->expires->ivalue != 0) {
1629 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
1630 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
1631
1632 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
1633 timeout));
1634 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1635
1636 } else {
1637 /* Otherwise set timer to terminate client subscription when
1638 * NOTIFY to end subscription is not received.
1639 */
1640 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
1641 }
1642
1643 /* Set state, if necessary */
1644 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
1645 if (sub->state == PJSIP_EVSUB_STATE_SENT) {
1646 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
1647 }
1648
1649 } else {
1650
1651 /* Failed SUBSCRIBE request!
1652 *
1653 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
1654 * other than 481, the subscription is still considered valid for
1655 * the duration of the last Expires.
1656 *
1657 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
1658 * expiration, theoritically the expiration is still valid for the
1659 * next 5 seconds even when we receive non-481 failed response.
1660 *
1661 * Ah, what the heck!
1662 *
1663 * Just terminate now!
1664 *
1665 */
1666
1667 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
1668 /* Ignore, has been handled before */
1669 return;
1670 }
1671
Benny Prijono69b98ab2006-03-03 10:23:35 +00001672 /* Ignore 490 (Request Updated) status.
1673 * This happens when application sends SUBSCRIBE/REFER while
1674 * another one is still in progress.
1675 */
1676 if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
1677 return;
1678 }
1679
Benny Prijono834aee32006-02-19 01:38:06 +00001680 /* Kill any timer. */
1681 set_timer(sub, TIMER_TYPE_NONE, 0);
1682
1683 /* Set state to TERMINATED */
1684 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
1685 NULL, event);
1686
1687 }
1688
1689 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
1690
1691 /* Incoming NOTIFY.
1692 * This can be the result of:
1693 * - Initial subscription response
1694 * - UAS updating the resource info.
1695 * - Unsubscription response.
1696 */
1697 int st_code = 200;
1698 pj_str_t *st_text = NULL;
1699 pjsip_hdr res_hdr;
1700 pjsip_msg_body *body = NULL;
1701
1702 pjsip_rx_data *rdata;
1703 pjsip_msg *msg;
1704 pjsip_sub_state_hdr *sub_state;
1705
1706 pjsip_evsub_state new_state;
1707 pj_str_t *new_state_str;
1708
1709 pjsip_tx_data *tdata;
1710 pj_status_t status;
1711 int next_refresh;
1712
1713 /* Only want to handle initial NOTIFY receive event. */
1714 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1715 return;
1716
1717
1718 rdata = event->body.tsx_state.src.rdata;
1719 msg = rdata->msg_info.msg;
1720
1721 pj_list_init(&res_hdr);
1722
1723 /* Get subscription state header. */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001724 sub_state = (pjsip_sub_state_hdr*)
1725 pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001726 if (sub_state == NULL) {
1727
1728 pjsip_warning_hdr *warn_hdr;
1729 pj_str_t warn_text = { "Missing Subscription-State header", 33};
1730
1731 /* Bad request! Add warning header. */
1732 st_code = PJSIP_SC_BAD_REQUEST;
1733 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
1734 pjsip_endpt_name(sub->endpt),
1735 &warn_text);
1736 pj_list_push_back(&res_hdr, warn_hdr);
1737 }
1738
1739 /* Call application registered callback to handle incoming NOTIFY,
1740 * if any.
1741 */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001742 if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
Benny Prijono834aee32006-02-19 01:38:06 +00001743 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
1744 &res_hdr, &body);
1745
1746 /* Application MUST specify final response! */
1747 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1748
1749 /* Must be a valid status code */
1750 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1751 }
1752
1753
1754 /* If non-2xx should be returned, then send the response.
1755 * No need to update server subscription state.
1756 */
1757 if (st_code >= 300) {
1758 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1759 body, &tdata);
1760 if (status == PJ_SUCCESS) {
1761 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1762 }
1763
1764 /* Start timer to terminate subscription, just in case server
1765 * is not able to generate NOTIFY to our response.
1766 */
1767 if (status == PJ_SUCCESS) {
1768 unsigned timeout = TIME_UAC_WAIT_NOTIFY;
1769 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
1770 } else {
1771 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
1772 }
1773
1774 return;
1775 }
1776
1777 /* Update expiration from the value of expires param in
1778 * Subscription-State header, but ONLY when subscription state
1779 * is "active" or "pending", AND the header contains expires param.
1780 */
1781 if (sub->expires->ivalue != 0 &&
1782 sub_state->expires_param >= 0 &&
1783 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
1784 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
1785 {
1786 next_refresh = sub_state->expires_param;
1787
1788 } else {
1789 next_refresh = sub->expires->ivalue;
1790 }
1791
1792 /* Update time */
1793 update_expires(sub, next_refresh);
1794
1795 /* Start UAC refresh timer, only when we're not unsubscribing */
1796 if (sub->expires->ivalue != 0) {
1797 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
1798 next_refresh - TIME_UAC_REFRESH : next_refresh;
1799
1800 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
1801 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
1802 }
1803
1804 /* Find out the state */
1805 get_hdr_state(sub_state, &new_state, &new_state_str);
1806
1807 /* Send response. */
1808 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1809 body, &tdata);
1810 if (status == PJ_SUCCESS)
1811 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1812
1813 /* Set the state */
1814 if (status == PJ_SUCCESS) {
1815 set_state(sub, new_state, new_state_str, event);
1816 } else {
1817 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
1818 }
1819
1820
1821 } else {
1822
1823 /*
1824 * Unexpected method!
1825 */
1826 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
1827 (int)tsx->method.name.slen, tsx->method.name.ptr));
1828 }
1829}
1830
1831
1832/*
1833 * Transaction event processing by UAS, after subscription is accepted.
1834 */
1835static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
1836 pjsip_event *event)
1837{
1838
Benny Prijono736d0f72006-09-13 22:45:38 +00001839 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 ||
1840 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)
1841 {
Benny Prijono834aee32006-02-19 01:38:06 +00001842
1843 /*
1844 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
1845 *
1846 */
1847 pjsip_rx_data *rdata;
1848 pjsip_event_hdr *event_hdr;
1849 pjsip_expires_hdr *expires;
1850 pjsip_msg *msg;
1851 pjsip_tx_data *tdata;
1852 int st_code = 200;
1853 pj_str_t *st_text = NULL;
1854 pjsip_hdr res_hdr;
1855 pjsip_msg_body *body = NULL;
1856 pjsip_evsub_state old_state;
1857 pj_str_t old_state_str;
1858 pj_status_t status;
1859
1860
1861 /* Only wants to handle the first event when the request is
1862 * received.
1863 */
1864 if (tsx->state != PJSIP_TSX_STATE_TRYING)
1865 return;
1866
1867 rdata = event->body.tsx_state.src.rdata;
1868 msg = rdata->msg_info.msg;
1869
1870 /* Set expiration time based on client request (in Expires header),
1871 * or package default expiration time.
1872 */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001873 event_hdr = (pjsip_event_hdr*)
1874 pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
1875 expires = (pjsip_expires_hdr*)
1876 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono834aee32006-02-19 01:38:06 +00001877 if (event_hdr && expires) {
1878 struct evpkg *evpkg;
1879
1880 evpkg = find_pkg(&event_hdr->event_type);
1881 if (evpkg) {
1882 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
1883 sub->expires->ivalue = expires->ivalue;
1884 else
1885 sub->expires->ivalue = evpkg->pkg_expires;
1886 }
1887 }
1888
1889 /* Update time (before calling on_rx_refresh, since application
1890 * will send NOTIFY.
1891 */
1892 update_expires(sub, sub->expires->ivalue);
1893
1894
1895 /* Save old state.
1896 * If application respond with non-2xx, revert to old state.
1897 */
1898 old_state = sub->state;
1899 old_state_str = sub->state_str;
1900
1901 if (sub->expires->ivalue == 0) {
1902 sub->state = PJSIP_EVSUB_STATE_TERMINATED;
1903 sub->state_str = evsub_state_names[sub->state];
1904 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1905 sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
1906 sub->state_str = evsub_state_names[sub->state];
1907 }
1908
1909 /* Call application's on_rx_refresh, just in case it wants to send
1910 * response other than 200 (OK)
1911 */
1912 pj_list_init(&res_hdr);
1913
Benny Prijonod4e0abd2006-03-05 11:53:36 +00001914 if (sub->user.on_rx_refresh && sub->call_cb) {
1915 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
1916 &res_hdr, &body);
1917 }
Benny Prijono834aee32006-02-19 01:38:06 +00001918
1919 /* Application MUST specify final response! */
1920 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
1921
1922 /* Must be a valid status code */
1923 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
1924
1925
1926 /* Create and send response */
1927 status = create_response(sub, rdata, st_code, st_text, &res_hdr,
1928 body, &tdata);
1929 if (status == PJ_SUCCESS) {
1930 /* Add expires header: */
Benny Prijono9d4469d2007-05-02 05:14:29 +00001931 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijono834aee32006-02-19 01:38:06 +00001932 pjsip_hdr_shallow_clone(tdata->pool,
1933 sub->expires));
1934
1935 /* Send */
1936 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
1937 }
1938
1939 /* Update state or revert state */
1940 if (st_code/100==2) {
1941
1942 if (sub->expires->ivalue == 0) {
1943 set_state(sub, sub->state, NULL, event);
1944 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
1945 set_state(sub, sub->state, NULL, event);
1946 }
1947
1948 /* Set UAS timeout timer, when state is not terminated. */
1949 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
1950 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
1951 sub->expires->ivalue));
1952 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
1953 sub->expires->ivalue);
1954 }
1955
1956 } else {
1957 sub->state = old_state;
1958 sub->state_str = old_state_str;
1959 }
1960
1961
1962 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
1963
1964 /* Handle authentication */
1965 if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
1966 (tsx->status_code==401 || tsx->status_code==407))
1967 {
1968 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1969 pjsip_tx_data *tdata;
1970 pj_status_t status;
1971
1972 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
1973 tsx->last_tx, &tdata);
1974 if (status == PJ_SUCCESS)
Benny Prijono64158af2006-04-04 11:06:34 +00001975 status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
Benny Prijono834aee32006-02-19 01:38:06 +00001976
1977 if (status != PJ_SUCCESS) {
1978 /* Can't authenticate. Terminate session (?) */
1979 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
Benny Prijono441ce002006-03-07 15:15:01 +00001980 return;
Benny Prijono834aee32006-02-19 01:38:06 +00001981 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001982
1983 }
1984 /*
1985 * Terminate event usage if we receive 481, 408, and 7 class
1986 * responses.
1987 */
1988 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
1989 (tsx->status_code==481 || tsx->status_code==408 ||
1990 tsx->status_code/100 == 7))
1991 {
1992 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
Benny Prijono441ce002006-03-07 15:15:01 +00001993 return;
Benny Prijono26ff9062006-02-21 23:47:00 +00001994 }
Benny Prijono834aee32006-02-19 01:38:06 +00001995
1996 } else {
1997
1998 /*
1999 * Unexpected method!
2000 */
2001 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
2002 (int)tsx->method.name.slen, tsx->method.name.ptr));
2003
2004 }
2005}
2006
2007
2008/*
2009 * Notification when transaction state has changed!
2010 */
2011static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
2012{
2013 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
2014
2015 if (sub == NULL) {
2016 sub = on_new_transaction(tsx, event);
2017 if (sub == NULL)
2018 return;
2019 }
2020
2021
2022 /* Call on_tsx_state callback, if any. */
Benny Prijonod4e0abd2006-03-05 11:53:36 +00002023 if (sub->user.on_tsx_state && sub->call_cb)
Benny Prijono834aee32006-02-19 01:38:06 +00002024 (*sub->user.on_tsx_state)(sub, tsx, event);
2025
2026
2027 /* Process the event: */
2028
2029 if (sub->role == PJSIP_ROLE_UAC) {
2030 on_tsx_state_uac(sub, tsx, event);
2031 } else {
2032 on_tsx_state_uas(sub, tsx, event);
2033 }
2034
2035
2036 /* Check transaction TERMINATE event */
2037 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
2038
2039 --sub->pending_tsx;
2040
2041 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
2042 sub->pending_tsx == 0)
2043 {
2044 evsub_destroy(sub);
2045 }
2046
2047 }
2048}
2049
2050