blob: aadcd3f26c2c8f01c4bae453590001b7a0f1fad3 [file] [log] [blame]
Benny Prijono26ff9062006-02-21 23:47:00 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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-ua/sip_xfer.h>
20#include <pjsip-simple/evsub_msg.h>
21#include <pjsip/sip_dialog.h>
22#include <pjsip/sip_errno.h>
23#include <pjsip/sip_endpoint.h>
24#include <pjsip/sip_module.h>
25#include <pjsip/sip_transport.h>
26#include <pj/assert.h>
27#include <pj/pool.h>
28#include <pj/string.h>
29
30
31/*
32 * Refer module (mod-refer)
33 */
34static struct pjsip_module mod_xfer =
35{
36 NULL, NULL, /* prev, next. */
37 { "mod-refer", 9 }, /* Name. */
38 -1, /* Id */
39 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */
40 NULL, /* User data. */
41 NULL, /* load() */
42 NULL, /* start() */
43 NULL, /* stop() */
44 NULL, /* unload() */
45 NULL, /* on_rx_request() */
46 NULL, /* on_rx_response() */
47 NULL, /* on_tx_request. */
48 NULL, /* on_tx_response() */
49 NULL, /* on_tsx_state() */
50};
51
52
53/* Declare PJSIP_REFER_METHOD, so that if somebody declares this in
54 * sip_msg.h we can catch the error here.
55 */
56enum
57{
58 PJSIP_REFER_METHOD = PJSIP_OTHER_METHOD
59};
60
61const pjsip_method pjsip_refer_method = {
62 PJSIP_REFER_METHOD,
63 { "REFER", 5}
64};
65
66
67/*
68 * String constants
69 */
70const pj_str_t STR_REFER = { "refer", 5 };
71const pj_str_t STR_MESSAGE = { "message", 7 };
72const pj_str_t STR_SIPFRAG = { "sipfrag", 7 };
73const pj_str_t STR_SIPFRAG_VERSION = {";version=2.0", 12 };
74
75
76/*
77 * Transfer struct.
78 */
79struct pjsip_xfer
80{
81 pjsip_evsub *sub; /**< Event subscribtion record. */
82 pjsip_dialog *dlg; /**< The dialog. */
83 pjsip_evsub_user user_cb; /**< The user callback. */
84 pj_str_t refer_to_uri; /**< The full Refer-To URI. */
85 int last_st_code; /**< st_code sent in last NOTIFY */
86 pj_str_t last_st_text; /**< st_text sent in last NOTIFY */
87};
88
89
90typedef struct pjsip_xfer pjsip_xfer;
91
92
93
94/*
95 * Forward decl for evsub callback.
96 */
97static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
98static void xfer_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
99 pjsip_event *event);
100static void xfer_on_evsub_rx_refresh( pjsip_evsub *sub,
101 pjsip_rx_data *rdata,
102 int *p_st_code,
103 pj_str_t **p_st_text,
104 pjsip_hdr *res_hdr,
105 pjsip_msg_body **p_body);
106static void xfer_on_evsub_rx_notify( pjsip_evsub *sub,
107 pjsip_rx_data *rdata,
108 int *p_st_code,
109 pj_str_t **p_st_text,
110 pjsip_hdr *res_hdr,
111 pjsip_msg_body **p_body);
112static void xfer_on_evsub_client_refresh(pjsip_evsub *sub);
113static void xfer_on_evsub_server_timeout(pjsip_evsub *sub);
114
115
116/*
117 * Event subscription callback for xference.
118 */
119static pjsip_evsub_user xfer_user =
120{
121 &xfer_on_evsub_state,
122 &xfer_on_evsub_tsx_state,
123 &xfer_on_evsub_rx_refresh,
124 &xfer_on_evsub_rx_notify,
125 &xfer_on_evsub_client_refresh,
126 &xfer_on_evsub_server_timeout,
127};
128
129
130
131
132/*
133 * Initialize the REFER subsystem.
134 */
135PJ_DEF(pj_status_t) pjsip_xfer_init_module(pjsip_endpoint *endpt)
136{
137 const pj_str_t accept = { "message/sipfrag;version=2.0", 27 };
138 pj_status_t status;
139
140 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
141 PJ_ASSERT_RETURN(mod_xfer.id == -1, PJ_EINVALIDOP);
142
143 status = pjsip_endpt_register_module(endpt, &mod_xfer);
144 if (status != PJ_SUCCESS)
145 return status;
146
147 status = pjsip_endpt_add_capability( endpt, &mod_xfer, PJSIP_H_ALLOW,
148 NULL, 1, &pjsip_refer_method.name);
149 if (status != PJ_SUCCESS)
150 return status;
151
152 status = pjsip_evsub_register_pkg( &mod_xfer, &STR_REFER, 300, 1, &accept);
153 if (status != PJ_SUCCESS)
154 return status;
155
156 return PJ_SUCCESS;
157}
158
159
160/*
161 * Create transferer (sender of REFER request).
162 *
163 */
164PJ_DEF(pj_status_t) pjsip_xfer_create_uac( pjsip_dialog *dlg,
165 const pjsip_evsub_user *user_cb,
166 pjsip_evsub **p_evsub )
167{
168 pj_status_t status;
169 pjsip_xfer *xfer;
170 pjsip_evsub *sub;
171
172 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
173
174 pjsip_dlg_inc_lock(dlg);
175
176 /* Create event subscription */
177 status = pjsip_evsub_create_uac( dlg, &xfer_user, &STR_REFER,
178 PJSIP_EVSUB_NO_EVENT_ID, &sub);
179 if (status != PJ_SUCCESS)
180 goto on_return;
181
182 /* Create xfer session */
183 xfer = pj_pool_zalloc(dlg->pool, sizeof(pjsip_xfer));
184 xfer->dlg = dlg;
185 xfer->sub = sub;
186 if (user_cb)
187 pj_memcpy(&xfer->user_cb, user_cb, sizeof(pjsip_evsub_user));
188
189 /* Attach to evsub */
190 pjsip_evsub_set_mod_data(sub, mod_xfer.id, xfer);
191
192 *p_evsub = sub;
193
194on_return:
195 pjsip_dlg_dec_lock(dlg);
196 return status;
197
198}
199
200
201
202
203/*
204 * Create transferee (receiver of REFER request).
205 *
206 */
207PJ_DEF(pj_status_t) pjsip_xfer_create_uas( pjsip_dialog *dlg,
208 const pjsip_evsub_user *user_cb,
209 pjsip_rx_data *rdata,
210 pjsip_evsub **p_evsub )
211{
212 pjsip_evsub *sub;
213 pjsip_xfer *xfer;
214 const pj_str_t STR_EVENT = {"Event", 5 };
215 pjsip_event_hdr *event_hdr;
216 pj_status_t status;
217
218 /* Check arguments */
219 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
220
221 /* Must be request message */
222 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
223 PJSIP_ENOTREQUESTMSG);
224
225 /* Check that request is REFER */
226 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
227 &pjsip_refer_method)==0,
228 PJSIP_ENOTREFER);
229
230 /* Lock dialog */
231 pjsip_dlg_inc_lock(dlg);
232
233 /* The evsub framework expects an Event header in the request,
234 * while a REFER request conveniently doesn't have one (pun intended!).
235 * So create a dummy Event header.
236 */
237 if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
238 &STR_EVENT, NULL)==NULL)
239 {
240 event_hdr = pjsip_event_hdr_create(rdata->tp_info.pool);
241 event_hdr->event_type = STR_REFER;
242 pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr*)event_hdr);
243 }
244
245 /* Create server subscription */
246 status = pjsip_evsub_create_uas( dlg, &xfer_user, rdata,
247 PJSIP_EVSUB_NO_EVENT_ID, &sub);
248 if (status != PJ_SUCCESS)
249 goto on_return;
250
251 /* Create server xfer subscription */
252 xfer = pj_pool_zalloc(dlg->pool, sizeof(pjsip_xfer));
253 xfer->dlg = dlg;
254 xfer->sub = sub;
255 if (user_cb)
256 pj_memcpy(&xfer->user_cb, user_cb, sizeof(pjsip_evsub_user));
257
258 /* Attach to evsub */
259 pjsip_evsub_set_mod_data(sub, mod_xfer.id, xfer);
260
261 /* Done: */
262 *p_evsub = sub;
263
264on_return:
265 pjsip_dlg_dec_lock(dlg);
266 return status;
267}
268
269
270
271/*
272 * Call this function to create request to initiate REFER subscription.
273 *
274 */
275PJ_DEF(pj_status_t) pjsip_xfer_initiate( pjsip_evsub *sub,
276 const pj_str_t *refer_to_uri,
277 pjsip_tx_data **p_tdata)
278{
279 pjsip_xfer *xfer;
280 const pj_str_t refer_to = { "Refer-To", 8};
281 pjsip_tx_data *tdata;
282 pjsip_generic_string_hdr *hdr;
283 pj_status_t status;
284
285 /* sub and p_tdata argument must be valid. */
286 PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL);
287
288
289 /* Get the xfer object. */
290 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
291 PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION);
292
293 /* refer_to_uri argument MAY be NULL for subsequent REFER requests,
294 * but it MUST be specified in the first REFER.
295 */
296 PJ_ASSERT_RETURN((refer_to_uri || xfer->refer_to_uri.slen), PJ_EINVAL);
297
298 /* Lock dialog. */
299 pjsip_dlg_inc_lock(xfer->dlg);
300
301 /* Create basic REFER request */
302 status = pjsip_evsub_initiate(sub, &pjsip_refer_method, -1,
303 &tdata);
304 if (status != PJ_SUCCESS)
305 goto on_return;
306
307 /* Save Refer-To URI. */
308 if (refer_to_uri == NULL) {
309 refer_to_uri = &xfer->refer_to_uri;
310 } else {
311 pj_strdup(xfer->dlg->pool, &xfer->refer_to_uri, refer_to_uri);
312 }
313
314 /* Create and add Refer-To header. */
315 hdr = pjsip_generic_string_hdr_create(tdata->pool, &refer_to,
316 refer_to_uri);
317 if (!hdr) {
318 pjsip_tx_data_dec_ref(tdata);
319 status = PJ_ENOMEM;
320 goto on_return;
321 }
322
323 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
324
325
326 /* Done. */
327 *p_tdata = tdata;
328
329 status = PJ_SUCCESS;
330
331on_return:
332 pjsip_dlg_dec_lock(xfer->dlg);
333 return status;
334}
335
336
337/*
338 * Accept the incoming REFER request by sending 2xx response.
339 *
340 */
341PJ_DEF(pj_status_t) pjsip_xfer_accept( pjsip_evsub *sub,
342 pjsip_rx_data *rdata,
343 int st_code,
344 const pjsip_hdr *hdr_list )
345{
346 /*
347 * Don't need to add custom headers, so just call basic
348 * evsub response.
349 */
350 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
351}
352
353
354/*
355 * For notifier, create NOTIFY request to subscriber, and set the state
356 * of the subscription.
357 */
358PJ_DEF(pj_status_t) pjsip_xfer_notify( pjsip_evsub *sub,
359 pjsip_evsub_state state,
360 int xfer_st_code,
361 const pj_str_t *xfer_st_text,
362 pjsip_tx_data **p_tdata)
363{
364 pjsip_tx_data *tdata;
365 pjsip_xfer *xfer;
366 const pj_str_t reason = { "noresource", 10 };
367 char *body;
368 int bodylen;
369 pjsip_msg_body *msg_body;
370 pj_status_t status;
371
372
373 /* Check arguments. */
374 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
375
376 /* Get the xfer object. */
377 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
378 PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION);
379
380
381 /* Lock object. */
382 pjsip_dlg_inc_lock(xfer->dlg);
383
384 /* Create the NOTIFY request.
385 * Note that reason is only used when state is TERMINATED, and
386 * the defined termination reason for REFER is "noresource".
387 */
388 status = pjsip_evsub_notify( sub, state, NULL, &reason, &tdata);
389 if (status != PJ_SUCCESS)
390 goto on_return;
391
392
393 /* Check status text */
394 if (xfer_st_text==NULL || xfer_st_text->slen==0)
395 xfer_st_text = pjsip_get_status_text(xfer_st_code);
396
397 /* Save st_code and st_text, for current_notify() */
398 xfer->last_st_code = xfer_st_code;
399 pj_strdup(xfer->dlg->pool, &xfer->last_st_text, xfer_st_text);
400
401 /* Create sipfrag content. */
402 body = pj_pool_alloc(tdata->pool, 128);
403 bodylen = pj_ansi_snprintf(body, 128, "SIP/2.0 %u %.*s",
404 xfer_st_code,
405 (int)xfer_st_text->slen,
406 xfer_st_text->ptr);
407 PJ_ASSERT_ON_FAIL(bodylen > 0 && bodylen < 128,
408 {status=PJ_EBUG; pjsip_tx_data_dec_ref(tdata);
409 goto on_return; });
410
411
412 /* Create SIP message body. */
413 msg_body = pj_pool_zalloc(tdata->pool, sizeof(pjsip_msg_body));
414 msg_body->content_type.type = STR_MESSAGE;
415 msg_body->content_type.subtype = STR_SIPFRAG;
416 msg_body->content_type.param = STR_SIPFRAG_VERSION;
417 msg_body->data = body;
418 msg_body->len = bodylen;
419 msg_body->print_body = &pjsip_print_text_body;
420 msg_body->clone_data = &pjsip_clone_text_data;
421
422 /* Attach sipfrag body. */
423 tdata->msg->body = msg_body;
424
425
426 /* Done. */
427 *p_tdata = tdata;
428
429
430on_return:
431 pjsip_dlg_dec_lock(xfer->dlg);
432 return status;
433
434}
435
436
437/*
438 * Send current state and the last sipfrag body.
439 */
440PJ_DEF(pj_status_t) pjsip_xfer_current_notify( pjsip_evsub *sub,
441 pjsip_tx_data **p_tdata )
442{
443 pjsip_xfer *xfer;
444 pj_status_t status;
445
446
447 /* Check arguments. */
448 PJ_ASSERT_RETURN(sub, PJ_EINVAL);
449
450 /* Get the xfer object. */
451 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
452 PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION);
453
454 pjsip_dlg_inc_lock(xfer->dlg);
455
456 status = pjsip_xfer_notify(sub, pjsip_evsub_get_state(sub),
457 xfer->last_st_code, &xfer->last_st_text,
458 p_tdata);
459
460 pjsip_dlg_dec_lock(xfer->dlg);
461
462 return status;
463}
464
465
466/*
467 * Send request message.
468 */
469PJ_DEF(pj_status_t) pjsip_xfer_send_request( pjsip_evsub *sub,
470 pjsip_tx_data *tdata)
471{
472 return pjsip_evsub_send_request(sub, tdata);
473}
474
475
476/*
477 * This callback is called by event subscription when subscription
478 * state has changed.
479 */
480static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
481{
482 pjsip_xfer *xfer;
483
484 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
485 PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;});
486
487 if (xfer->user_cb.on_evsub_state)
488 (*xfer->user_cb.on_evsub_state)(sub, event);
489
490}
491
492/*
493 * Called when transaction state has changed.
494 */
495static void xfer_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
496 pjsip_event *event)
497{
498 pjsip_xfer *xfer;
499
500 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
501 PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;});
502
503 if (xfer->user_cb.on_tsx_state)
504 (*xfer->user_cb.on_tsx_state)(sub, tsx, event);
505}
506
507/*
508 * Called when REFER is received to refresh subscription.
509 */
510static void xfer_on_evsub_rx_refresh( pjsip_evsub *sub,
511 pjsip_rx_data *rdata,
512 int *p_st_code,
513 pj_str_t **p_st_text,
514 pjsip_hdr *res_hdr,
515 pjsip_msg_body **p_body)
516{
517 pjsip_xfer *xfer;
518
519 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
520 PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;});
521
522 if (xfer->user_cb.on_rx_refresh) {
523 (*xfer->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
524 res_hdr, p_body);
525
526 } else {
527 /* Implementors MUST send NOTIFY if it implements on_rx_refresh
528 * (implementor == "us" from evsub point of view.
529 */
530 pjsip_tx_data *tdata;
531 pj_status_t status;
532
533 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
534 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
535 xfer->last_st_code,
536 &xfer->last_st_text,
537 &tdata);
538 } else {
539 status = pjsip_xfer_current_notify(sub, &tdata);
540 }
541
542 if (status == PJ_SUCCESS)
543 pjsip_xfer_send_request(sub, tdata);
544 }
545}
546
547
548/*
549 * Called when NOTIFY is received.
550 */
551static void xfer_on_evsub_rx_notify( pjsip_evsub *sub,
552 pjsip_rx_data *rdata,
553 int *p_st_code,
554 pj_str_t **p_st_text,
555 pjsip_hdr *res_hdr,
556 pjsip_msg_body **p_body)
557{
558 pjsip_xfer *xfer;
559
560 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
561 PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;});
562
563 if (xfer->user_cb.on_rx_notify)
564 (*xfer->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
565 res_hdr, p_body);
566}
567
568/*
569 * Called when it's time to send SUBSCRIBE.
570 */
571static void xfer_on_evsub_client_refresh(pjsip_evsub *sub)
572{
573 pjsip_xfer *xfer;
574
575 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
576 PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;});
577
578 if (xfer->user_cb.on_client_refresh) {
579 (*xfer->user_cb.on_client_refresh)(sub);
580 } else {
581 pj_status_t status;
582 pjsip_tx_data *tdata;
583
584 status = pjsip_xfer_initiate(sub, NULL, &tdata);
585 if (status == PJ_SUCCESS)
586 pjsip_xfer_send_request(sub, tdata);
587 }
588}
589
590
591/*
592 * Called when no refresh is received after the interval.
593 */
594static void xfer_on_evsub_server_timeout(pjsip_evsub *sub)
595{
596 pjsip_xfer *xfer;
597
598 xfer = pjsip_evsub_get_mod_data(sub, mod_xfer.id);
599 PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;});
600
601 if (xfer->user_cb.on_server_timeout) {
602 (*xfer->user_cb.on_server_timeout)(sub);
603 } else {
604 pj_status_t status;
605 pjsip_tx_data *tdata;
606
607 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
608 xfer->last_st_code,
609 &xfer->last_st_text, &tdata);
610 if (status == PJ_SUCCESS)
611 pjsip_xfer_send_request(sub, tdata);
612 }
613}
614