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