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