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