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