Asterisk - The Open Source Telephony Project GIT-master-4f2b068
Loading...
Searching...
No Matches
Data Structures | Macros | Functions | Variables
res_pjsip_refer.c File Reference
#include "asterisk.h"
#include <pjsip.h>
#include <pjsip_ua.h>
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/bridge.h"
#include "asterisk/framehook.h"
#include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/causes.h"
#include "asterisk/refer.h"
Include dependency graph for res_pjsip_refer.c:

Go to the source code of this file.

Data Structures

struct  invite_replaces
 Structure used to retrieve channel from another session. More...
 
struct  refer_attended
 Structure for attended transfer task. More...
 
struct  refer_blind
 Structure for blind transfer callback details. More...
 
struct  refer_data
 
struct  refer_out_of_dialog
 
struct  refer_progress
 REFER Progress structure. More...
 
struct  refer_progress_notification
 REFER Progress notification structure. More...
 
struct  transfer_ari_state
 

Macros

#define DETERMINE_TRANSFER_CONTEXT(context, session)
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static void add_header_from_channel_var (struct ast_channel *chan, const char *var_name, const char *header_name, pjsip_tx_data *tdata)
 Use the value of a channel variable as the value of a SIP header.
 
static int add_refer_param (struct ast_refer_params *params, const char *key, struct pj_str_t *str)
 
static int ari_notify (struct transfer_ari_state *state)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static char * copy_string (struct pj_str_t *str)
 
static int defer_termination_cancel_task (void *data)
 
static int dlg_releaser_task (void *data)
 
static pjsip_uri * get_refer_to_uri (pjsip_tx_data *tdata)
 Helper function which returns the name-addr of the Refer-To header or NULL.
 
static int invite_replaces (void *data)
 Task for invite replaces.
 
static int is_refer_var_blocked (const char *name)
 
static int load_module (void)
 
static struct ast_framerefer_ari_progress_framehook (struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
 Progress monitoring frame hook - examines frames to determine state of transfer. Used for the ari-only mode.
 
static struct refer_attendedrefer_attended_alloc (struct ast_sip_session *transferer, struct ast_sip_session *transferer_second, struct refer_progress *progress)
 Allocator for attended transfer task.
 
static void refer_attended_destroy (void *obj)
 Destructor for attended transfer task.
 
static int refer_attended_task (void *data)
 Task for attended transfer executed by attended->transferer_second serializer.
 
static void refer_blind_callback (struct ast_channel *chan, struct transfer_channel_data *user_data_wrapper, enum ast_transfer_type transfer_type)
 Blind transfer callback function.
 
static void refer_client_on_evsub_state (pjsip_evsub *sub, pjsip_event *event)
 
static struct refer_datarefer_data_create (const struct ast_refer *refer)
 
static void refer_data_destroy (void *obj)
 
static int refer_incoming_ari_request (struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target, pjsip_param *replaces_param, struct refer_progress *progress)
 
static int refer_incoming_attended_request (struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target_uri, pjsip_param *replaces_param, struct refer_progress *progress)
 
static int refer_incoming_blind_request (struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target, struct refer_progress *progress)
 
static int refer_incoming_invite_request (struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 
static int refer_incoming_refer_request (struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 
static int refer_incoming_request (struct ast_sip_session *session, pjsip_rx_data *rdata)
 
static pj_status_t refer_on_tx_request (pjsip_tx_data *tdata)
 
static void refer_out_of_dialog_destroy (void *obj)
 Destructor for REFER out of dialog structure.
 
static void refer_outgoing_request (struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 
static void refer_params_destroy (void *obj)
 
static int refer_progress_alloc (struct ast_sip_session *session, pjsip_rx_data *rdata, struct refer_progress **progress)
 Internal helper function which sets up a refer progress structure if needed.
 
static void refer_progress_bridge (void *data, struct stasis_subscription *sub, struct stasis_message *message)
 
static void refer_progress_destroy (void *obj)
 Destructor for REFER progress sutrcture.
 
static struct ast_framerefer_progress_framehook (struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
 Progress monitoring frame hook - examines frames to determine state of transfer.
 
static void refer_progress_framehook_destroy (void *data)
 Destroy callback for monitoring framehook.
 
static struct refer_progress_notificationrefer_progress_notification_alloc (struct refer_progress *progress, int response, pjsip_evsub_state state)
 Allocator for REFER Progress notification structure.
 
static void refer_progress_notification_destroy (void *obj)
 Destructor for REFER Progress notification structure.
 
static int refer_progress_notify (void *data)
 Serialized callback for subscription notification.
 
static void refer_progress_on_evsub_state (pjsip_evsub *sub, pjsip_event *event)
 Callback for REFER subscription state changes.
 
static int refer_send (void *data)
 
static int refer_unreference_dialog (void *obj)
 
static int session_end_if_deferred_task (void *data)
 
static int sip_refer_send (const struct ast_refer *refer)
 
static void transfer_ari_state_destroy (void *obj)
 Destructor of the state used for the ARI transfer.
 
static int unload_module (void)
 
static enum pjsip_status_code vars_to_headers (const struct ast_refer *refer, pjsip_tx_data *tdata)
 
static int xfer_response_code2sip (enum ast_transfer_result xfer_code)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP Blind and Attended Transfer Support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .requires = "res_pjsip,res_pjsip_session,res_pjsip_pubsub", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static pjsip_module refer_out_of_dialog_module
 REFER Out-of-dialog module, used to attach session data structure to subscription.
 
static pjsip_evsub_user refer_progress_evsub_cb
 Callback structure for subscription.
 
static pjsip_module refer_progress_module
 REFER Progress module, used to attach REFER progress structure to subscriptions.
 
static struct ast_taskprocessorrefer_serializer
 
static struct ast_sip_session_supplement refer_supplement
 
static const struct ast_refer_tech refer_tech
 

Macro Definition Documentation

◆ DETERMINE_TRANSFER_CONTEXT

#define DETERMINE_TRANSFER_CONTEXT (   context,
  session 
)

Definition at line 946 of file res_pjsip_refer.c.

947 { \
948 ast_channel_lock((session)->channel); \
949 context = pbx_builtin_getvar_helper((session)->channel, "TRANSFER_CONTEXT"); \
950 if (ast_strlen_zero(context)) { \
951 context = (session)->endpoint->context; \
952 } else { \
953 context = ast_strdupa(context); \
954 } \
955 ast_channel_unlock((session)->channel); \
956 } while (0) \
957
static struct ast_mansession session
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 2136 of file res_pjsip_refer.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 2136 of file res_pjsip_refer.c.

◆ add_header_from_channel_var()

static void add_header_from_channel_var ( struct ast_channel chan,
const char *  var_name,
const char *  header_name,
pjsip_tx_data *  tdata 
)
static

Use the value of a channel variable as the value of a SIP header.

This looks up a variable name on a channel, then takes that value and adds it to an outgoing SIP request. If the header already exists on the message, then no action is taken.

Precondition
chan is locked.
Parameters
chanThe channel on which to find the variable.
var_nameThe name of the channel variable to use.
header_nameThe name of the SIP header to add to the outgoing message.
tdataThe outgoing SIP message on which to add the header

Definition at line 2051 of file res_pjsip_refer.c.

2052{
2053 const char *var_value;
2054 pj_str_t pj_header_name;
2055 pjsip_hdr *header;
2056
2057 var_value = pbx_builtin_getvar_helper(chan, var_name);
2058 if (ast_strlen_zero(var_value)) {
2059 return;
2060 }
2061
2062 pj_cstr(&pj_header_name, header_name);
2063 header = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_header_name, NULL);
2064 if (header) {
2065 return;
2066 }
2067 ast_sip_add_header(tdata, header_name, var_value);
2068}
int ast_sip_add_header(pjsip_tx_data *tdata, const char *name, const char *value)
Add a header to an outbound SIP message.
Definition res_pjsip.c:2002
#define NULL
Definition resample.c:96

References ast_sip_add_header(), ast_strlen_zero(), NULL, and pbx_builtin_getvar_helper().

Referenced by refer_outgoing_request().

◆ add_refer_param()

static int add_refer_param ( struct ast_refer_params params,
const char *  key,
struct pj_str_t *  str 
)
static

Definition at line 1462 of file res_pjsip_refer.c.

1463{
1464 struct ast_refer_param param;
1465
1466 param.param_name = ast_strdup(key);
1467 if (!param.param_name) {
1468 return 0;
1469 }
1470
1471 param.param_value = copy_string(str);
1472 if (!param.param_value) {
1473 ast_free((char *) param.param_name);
1474 return 0;
1475 }
1476
1477 if (AST_VECTOR_APPEND(params, param) != 0) {
1478 ast_free((char *) param.param_name);
1479 ast_free((char *) param.param_value);
1480 return 0;
1481 }
1482 return 1;
1483}
const char * str
Definition app_jack.c:150
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
static char * copy_string(struct pj_str_t *str)
const char * param_name
Definition refer.h:82
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267

References ast_free, ast_strdup, AST_VECTOR_APPEND, copy_string(), ast_refer_param::param_name, ast_refer_param::param_value, and str.

Referenced by refer_incoming_ari_request().

◆ ari_notify()

static int ari_notify ( struct transfer_ari_state state)
static

Definition at line 140 of file res_pjsip_refer.c.

141{
142 return ast_refer_notify_transfer_request(state->transferer_chan, state->referred_by,
143 state->exten, state->protocol_id,
144 state->other_session ? state->other_session->channel : NULL,
145 state->params, state->last_response);
146}
int ast_refer_notify_transfer_request(struct ast_channel *originating_chan, const char *referred_by, const char *exten, const char *protocol_id, struct ast_channel *dest, struct ast_refer_params *params, enum ast_control_transfer state)
Notify a transfer request.
Definition refer.c:541

References ast_refer_notify_transfer_request(), and NULL.

Referenced by refer_incoming_ari_request(), and refer_progress_notify().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 2136 of file res_pjsip_refer.c.

◆ copy_string()

static char * copy_string ( struct pj_str_t *  str)
static

Definition at line 1451 of file res_pjsip_refer.c.

1452{
1453 int len = pj_strlen(str) + 1;
1454 char *dst = ast_malloc(len);
1455 if (!dst) {
1456 return NULL;
1457 }
1458 ast_copy_pj_str(dst, str, len);
1459 return dst;
1460}
#define ast_malloc(len)
A wrapper for malloc()
Definition astmm.h:191
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition res_pjsip.c:2172

References ast_copy_pj_str(), ast_malloc, len(), NULL, and str.

Referenced by add_refer_param(), and refer_incoming_ari_request().

◆ defer_termination_cancel_task()

static int defer_termination_cancel_task ( void *  data)
static

Definition at line 625 of file res_pjsip_refer.c.

626{
627 struct ast_sip_session *session = data;
628
631 ao2_ref(session, -1);
632 return 0;
633}
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
Cancel a pending deferred termination.
void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
End the session if it had been previously deferred.
A structure describing a SIP session.

References ao2_ref, ast_sip_session_defer_termination_cancel(), ast_sip_session_end_if_deferred(), and session.

Referenced by refer_attended_task().

◆ dlg_releaser_task()

static int dlg_releaser_task ( void *  data)
static

Definition at line 456 of file res_pjsip_refer.c.

456 {
457 pjsip_dlg_dec_session((pjsip_dialog *)data, &refer_progress_module);
458 return 0;
459}
static pjsip_module refer_progress_module
REFER Progress module, used to attach REFER progress structure to subscriptions.

References refer_progress_module.

Referenced by refer_progress_destroy().

◆ get_refer_to_uri()

static pjsip_uri * get_refer_to_uri ( pjsip_tx_data *  tdata)
static

Helper function which returns the name-addr of the Refer-To header or NULL.

Definition at line 1109 of file res_pjsip_refer.c.

1110{
1111 const pj_str_t REFER_TO = { "Refer-To", 8 };
1112 pjsip_generic_string_hdr *refer_to;
1113 pjsip_uri *parsed_uri;
1114
1115 if (!(refer_to = pjsip_msg_find_hdr_by_name(tdata->msg, &REFER_TO, NULL))
1116 || !(parsed_uri = pjsip_parse_uri(tdata->pool, refer_to->hvalue.ptr, refer_to->hvalue.slen, 0))
1117 || (!PJSIP_URI_SCHEME_IS_SIP(parsed_uri) && !PJSIP_URI_SCHEME_IS_SIPS(parsed_uri))) {
1118 return NULL;
1119 }
1120
1121 return parsed_uri;
1122}

References NULL.

Referenced by refer_on_tx_request().

◆ invite_replaces()

static int invite_replaces ( void *  data)
static

Task for invite replaces.

Definition at line 1772 of file res_pjsip_refer.c.

1773{
1774 struct invite_replaces *invite = data;
1775
1776 if (!invite->session->channel) {
1777 return -1;
1778 }
1779
1781 invite->channel = invite->session->channel;
1782
1784 return 0;
1785}
struct ast_bridge * ast_bridge_transfer_acquire_bridge(struct ast_channel *chan)
Acquire the channel's bridge for transfer purposes.
Definition bridge.c:4487
#define ast_channel_ref(c)
Increase channel reference count.
Definition channel.h:3007
struct ast_channel * channel
Structure used to retrieve channel from another session.
struct ast_sip_session * session
Session we want the channel from.
struct ast_channel * channel
Channel from the session (with reference)
struct ast_bridge * bridge
Bridge the channel is in.

References ast_bridge_transfer_acquire_bridge(), ast_channel_ref, invite_replaces::bridge, ast_sip_session::channel, invite_replaces::channel, and invite_replaces::session.

◆ is_refer_var_blocked()

static int is_refer_var_blocked ( const char *  name)
static

Definition at line 1041 of file res_pjsip_refer.c.

1042{
1043 int i;
1044
1045 /* Don't block the Max-Forwards header because the user can override it */
1046 static const char *hdr[] = {
1047 "To",
1048 "From",
1049 "Via",
1050 "Route",
1051 "Contact",
1052 "Call-ID",
1053 "CSeq",
1054 "Allow",
1055 "Content-Length",
1056 "Content-Type",
1057 "Request-URI",
1058 };
1059
1060 for (i = 0; i < ARRAY_LEN(hdr); ++i) {
1061 if (!strcasecmp(name, hdr[i])) {
1062 /* Block addition of this header. */
1063 return 1;
1064 }
1065 }
1066 return 0;
1067}
static const char name[]
Definition format_mp3.c:68
#define ARRAY_LEN(a)
Definition utils.h:706

References ARRAY_LEN, and name.

Referenced by vars_to_headers().

◆ load_module()

static int load_module ( void  )
static

Definition at line 2090 of file res_pjsip_refer.c.

2091{
2092 const pj_str_t str_norefersub = { "norefersub", 10 };
2093
2094 pjsip_replaces_init_module(ast_sip_get_pjsip_endpoint());
2095 pjsip_xfer_init_module(ast_sip_get_pjsip_endpoint());
2096
2097 if (ast_sip_get_norefersub()) {
2098 pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_norefersub);
2099 }
2100
2103 }
2104
2106 if (!refer_serializer) {
2109 }
2110
2114
2116
2118}
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition res_pjsip.c:2088
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition module.h:478
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
int ast_refer_tech_unregister(const struct ast_refer_tech *tech)
Unregister a refer technology.
Definition refer.c:492
int ast_refer_tech_register(const struct ast_refer_tech *tech)
Register a refer technology.
Definition refer.c:448
unsigned int ast_sip_get_norefersub(void)
Retrieve the global setting 'norefersub'.
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition res_pjsip.c:111
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition res_pjsip.c:514
static pjsip_module refer_out_of_dialog_module
REFER Out-of-dialog module, used to attach session data structure to subscription.
static struct ast_taskprocessor * refer_serializer
static struct ast_sip_session_supplement refer_supplement
static const struct ast_refer_tech refer_tech
#define ast_sip_session_register_supplement(supplement)
struct ast_module * self
Definition module.h:356

References AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_module_shutdown_ref, ast_refer_tech_register(), ast_refer_tech_unregister(), ast_sip_create_serializer(), ast_sip_get_norefersub(), ast_sip_get_pjsip_endpoint(), ast_sip_register_service(), ast_sip_session_register_supplement, NULL, refer_out_of_dialog_module, refer_progress_module, refer_serializer, refer_supplement, refer_tech, and ast_module_info::self.

◆ refer_ari_progress_framehook()

static struct ast_frame * refer_ari_progress_framehook ( struct ast_channel chan,
struct ast_frame f,
enum ast_framehook_event  event,
void *  data 
)
static

Progress monitoring frame hook - examines frames to determine state of transfer. Used for the ari-only mode.

Definition at line 358 of file res_pjsip_refer.c.

359{
360 struct refer_progress *progress = data;
361 struct refer_progress_notification *notification = NULL;
362
363 /* We only care about frames *to* the channel */
364 if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
365 return f;
366 }
367
368 /* Determine the state of the REFER based on the control frames (or voice frames) passing */
371 && f->datalen >= sizeof(enum ast_control_transfer)) {
373 switch (*message) {
375 notification = refer_progress_notification_alloc(progress, 603, PJSIP_EVSUB_STATE_TERMINATED);
376 break;
378 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
379 break;
381 notification = refer_progress_notification_alloc(progress, 100, PJSIP_EVSUB_STATE_ACTIVE);
382 break;
384 notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
385 break;
387 break;
388 }
389 progress->ari_state->last_response = *message;
390 }
391
392 /* If a notification is due to be sent push it to the taskpool */
393 if (notification) {
394 /* If the subscription is being terminated we don't need the frame hook any longer */
395 if (notification->state == PJSIP_EVSUB_STATE_TERMINATED) {
396 ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as subscription is being terminated\n",
397 ast_channel_name(chan));
398 ast_framehook_detach(chan, progress->framehook);
399 }
400
401 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
402 ao2_cleanup(notification);
403 }
404 }
405
406 return f;
407}
#define ao2_cleanup(obj)
Definition astobj2.h:1934
const char * ast_channel_name(const struct ast_channel *chan)
@ AST_FRAMEHOOK_EVENT_WRITE
Definition framehook.h:153
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition framehook.c:177
#define ast_sip_push_task(serializer, sip_task, task_data)
Definition res_pjsip.h:2094
@ AST_TRANSFER_FAILED
@ AST_TRANSFER_UNAVAILABLE
@ AST_TRANSFER_SUCCESS
@ AST_TRANSFER_PROGRESS
@ AST_TRANSFER_INVALID
@ AST_FRAME_CONTROL
@ AST_CONTROL_TRANSFER
#define ast_debug(level,...)
Log a DEBUG message.
static struct refer_progress_notification * refer_progress_notification_alloc(struct refer_progress *progress, int response, pjsip_evsub_state state)
Allocator for REFER Progress notification structure.
static int refer_progress_notify(void *data)
Serialized callback for subscription notification.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@239 data
REFER Progress notification structure.
pjsip_evsub_state state
Subscription state.
REFER Progress structure.

References ao2_cleanup, ast_channel_name(), AST_CONTROL_TRANSFER, ast_debug, AST_FRAME_CONTROL, ast_framehook_detach(), AST_FRAMEHOOK_EVENT_WRITE, ast_sip_push_task, AST_TRANSFER_FAILED, AST_TRANSFER_INVALID, AST_TRANSFER_PROGRESS, AST_TRANSFER_SUCCESS, AST_TRANSFER_UNAVAILABLE, ast_frame::data, ast_frame::datalen, ast_frame::frametype, ast_frame_subclass::integer, NULL, ast_frame::ptr, refer_progress_notification_alloc(), refer_progress_notify(), refer_progress_notification::state, and ast_frame::subclass.

Referenced by refer_incoming_ari_request().

◆ refer_attended_alloc()

static struct refer_attended * refer_attended_alloc ( struct ast_sip_session transferer,
struct ast_sip_session transferer_second,
struct refer_progress progress 
)
static

Allocator for attended transfer task.

Definition at line 589 of file res_pjsip_refer.c.

592{
593 struct refer_attended *attended;
594
595 attended = ao2_alloc_options(sizeof(*attended), refer_attended_destroy,
597 if (!attended) {
598 return NULL;
599 }
600
601 ao2_ref(transferer, +1);
602 attended->transferer = transferer;
607
608 if (progress) {
609 ao2_ref(progress, +1);
610 attended->progress = progress;
611 }
612
613 return attended;
614}
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition astobj2.h:404
static void refer_attended_destroy(void *obj)
Destructor for attended transfer task.
Structure for attended transfer task.
struct ast_sip_session * transferer
Transferer session.
struct ast_sip_session * transferer_second
Second transferer session.
struct refer_progress * progress
Optional refer progress structure.
struct ast_channel * transferer_chan
Transferer channel.

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_ref, ast_channel_ref, ast_sip_session::channel, NULL, refer_attended::progress, refer_attended_destroy(), refer_attended::transferer, refer_attended::transferer_chan, and refer_attended::transferer_second.

Referenced by refer_incoming_attended_request().

◆ refer_attended_destroy()

static void refer_attended_destroy ( void *  obj)
static

Destructor for attended transfer task.

Definition at line 578 of file res_pjsip_refer.c.

579{
580 struct refer_attended *attended = obj;
581
582 ao2_cleanup(attended->transferer);
585 ao2_cleanup(attended->progress);
586}
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition channel.h:3029

References ao2_cleanup, ast_channel_cleanup, refer_attended::progress, refer_attended::transferer, refer_attended::transferer_chan, and refer_attended::transferer_second.

Referenced by refer_attended_alloc().

◆ refer_attended_task()

static int refer_attended_task ( void *  data)
static

Task for attended transfer executed by attended->transferer_second serializer.

Definition at line 667 of file res_pjsip_refer.c.

668{
669 struct refer_attended *attended = data;
670 int response;
671 int (*task_cb)(void *data);
672
673 if (attended->transferer_second->channel) {
674 ast_debug(3, "Performing a REFER attended transfer - Transferer #1: %s Transferer #2: %s\n",
677
679 attended->transferer_chan,
680 attended->transferer_second->channel));
681
682 ast_debug(3, "Final response for REFER attended transfer - Transferer #1: %s Transferer #2: %s is '%d'\n",
685 response);
686 } else {
687 ast_debug(3, "Received REFER request on channel '%s' but other channel has gone.\n",
689 response = 603;
690 }
691
692 if (attended->progress) {
693 struct refer_progress_notification *notification;
694
695 notification = refer_progress_notification_alloc(attended->progress, response,
696 PJSIP_EVSUB_STATE_TERMINATED);
697 if (notification) {
698 if (ast_sip_push_task(attended->progress->serializer, refer_progress_notify, notification)) {
699 ao2_cleanup(notification);
700 }
701 }
702 }
703
704 if (response == 200) {
706 } else {
708 }
710 task_cb, attended->transferer)) {
711 /* Gave the ref to the pushed task. */
712 attended->transferer = NULL;
713 } else {
714 /* Do this anyway even though it is the wrong serializer. */
716 }
717
718 ao2_ref(attended, -1);
719 return 0;
720}
enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee, struct ast_channel *to_transfer_target)
Attended transfer.
Definition bridge.c:4756
static int xfer_response_code2sip(enum ast_transfer_result xfer_code)
static int defer_termination_cancel_task(void *data)
static int session_end_if_deferred_task(void *data)
struct ast_taskprocessor * serializer
int response
SIP response code to send.
struct ast_taskprocessor * serializer
Serializer for notifications.

References ao2_cleanup, ao2_ref, ast_bridge_transfer_attended(), ast_channel_name(), ast_debug, ast_sip_push_task, ast_sip_session_end_if_deferred(), ast_sip_session::channel, defer_termination_cancel_task(), NULL, refer_attended::progress, refer_progress_notification_alloc(), refer_progress_notify(), refer_progress_notification::response, ast_sip_session::serializer, refer_progress::serializer, session_end_if_deferred_task(), refer_attended::transferer, refer_attended::transferer_chan, refer_attended::transferer_second, and xfer_response_code2sip().

Referenced by refer_incoming_attended_request().

◆ refer_blind_callback()

static void refer_blind_callback ( struct ast_channel chan,
struct transfer_channel_data user_data_wrapper,
enum ast_transfer_type  transfer_type 
)
static

Blind transfer callback function.

Definition at line 739 of file res_pjsip_refer.c.

741{
742 struct refer_blind *refer = user_data_wrapper->data;
743 pjsip_generic_string_hdr *referred_by;
744
745 static const pj_str_t str_referred_by = { "Referred-By", 11 };
746 static const pj_str_t str_referred_by_s = { "b", 1 };
747 const char *get_xfrdata;
748
749 pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes");
750
751 if (refer->progress && !refer->attended && !refer->progress->refer_blind_progress) {
752 /* If blind transfer and endpoint doesn't want to receive all the progress details */
754 PJSIP_EVSUB_STATE_TERMINATED);
755
756 if (notification) {
757 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
758 ao2_cleanup(notification);
759 }
760 }
761 } else if (refer->progress) {
762 /* If attended transfer and progress monitoring is being done attach a frame hook so we can monitor it */
763 struct ast_framehook_interface hook = {
765 .event_cb = refer_progress_framehook,
767 .data = refer->progress,
768 .disable_inheritance = 1,
769 };
770
772 if (!refer->progress->transferee) {
774 PJSIP_EVSUB_STATE_TERMINATED);
775
776 ast_log(LOG_WARNING, "Could not copy channel name '%s' during transfer - assuming success\n",
777 ast_channel_name(chan));
778
779 if (notification) {
780 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
781 ao2_cleanup(notification);
782 }
783 }
784 }
785
786 /* Progress needs a reference to the transfer_channel_data so that it can track the completed status of the transfer */
787 ao2_ref(user_data_wrapper, +1);
788 refer->progress->transfer_data = user_data_wrapper;
789
790 /* We need to bump the reference count up on the progress structure since it is in the frame hook now */
791 ao2_ref(refer->progress, +1);
792
793 /* If we can't attach a frame hook for whatever reason send a notification of success immediately */
794 ast_channel_lock(chan);
795 refer->progress->framehook = ast_framehook_attach(chan, &hook);
796 ast_channel_unlock(chan);
797 if (refer->progress->framehook < 0) {
799 PJSIP_EVSUB_STATE_TERMINATED);
800
801 ast_log(LOG_WARNING, "Could not attach REFER transfer progress monitoring hook to channel '%s' - assuming success\n",
802 ast_channel_name(chan));
803
804 if (notification) {
805 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
806 ao2_cleanup(notification);
807 }
808 }
809
810 ao2_cleanup(refer->progress);
811 }
812
813 /* We need to bump the reference count for the stasis subscription */
814 ao2_ref(refer->progress, +1);
815 /* We also will need to detect if the transferee enters a bridge. This is currently the only reliable way to
816 * detect if the transfer target has answered the call
817 */
819 if (!refer->progress->bridge_sub) {
821 PJSIP_EVSUB_STATE_TERMINATED);
822
823 ast_log(LOG_WARNING, "Could not create bridge stasis subscription for monitoring progress on transfer of channel '%s' - assuming success\n",
824 ast_channel_name(chan));
825
826 if (notification) {
827 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
828 ao2_cleanup(notification);
829 }
830 }
831
832 ast_channel_lock(chan);
834 ast_channel_unlock(chan);
835
836 ao2_cleanup(refer->progress);
837 } else {
841 }
842 }
843
844 pbx_builtin_setvar_helper(chan, "SIPREFERRINGCONTEXT", S_OR(refer->context, NULL));
845
846 ast_channel_lock(chan);
847 if ((get_xfrdata = pbx_builtin_getvar_helper(chan, "GET_TRANSFERRER_DATA"))) {
848 get_xfrdata = ast_strdupa(get_xfrdata);
849 }
850 ast_channel_unlock(chan);
851 if (!ast_strlen_zero(get_xfrdata)) {
852 const pjsip_msg * msg = refer->rdata->msg_info.msg;
853 const struct pjsip_hdr *end = &msg->hdr;
854 struct pjsip_hdr *hdr = end->next;
855 struct ast_str *pbxvar = ast_str_create(64); /* initial buffer for variable name, extended on demand */
856 char buf[4096]; /* should be enough for one header value */
857 const char *prefix = get_xfrdata;
858
859 /* The '*' alone matches all headers. */
860 if (!strcmp(prefix, "*")) {
861 prefix = "";
862 }
863 if (pbxvar) {
864 for (; hdr != end; hdr = hdr->next) {
865 if (!pj_strnicmp2(&hdr->name, prefix, strlen(prefix))) {
866 const int hdr_name_strlen = pj_strlen(&hdr->name);
867 const int hdr_name_bytes = hdr_name_strlen + 1; /* +1 for string NULL terminator */
868 char hdr_name[hdr_name_bytes];
869 int len;
870 char *value_str;
871
872 ast_copy_pj_str(hdr_name, &hdr->name, hdr_name_bytes);
873 len = pjsip_hdr_print_on(hdr, buf, sizeof(buf) - 1);
874 if (len < 0) {
875 /* ignore too long headers */
876 ast_log(LOG_WARNING, "Could not store header '%s' from transfer on channel '%s' - too long text\n", hdr_name, ast_channel_name(chan));
877 continue;
878 }
879 buf[len] = '\0';
880
881 /* Get value - remove header name (before ':') from buf and trim blanks. */
882 value_str = strchr(buf, ':');
883 if (!value_str) {
884 /* Ignore header without value */
885 continue;
886 }
887 value_str = ast_strip(value_str + 1); /* +1 to get string right after the ':' delimiter (i.e. header value) */
888
889 ast_str_set(&pbxvar, -1, "~HASH~TRANSFER_DATA~%.*s~", hdr_name_strlen, hdr_name);
890 pbx_builtin_setvar_helper(chan, ast_str_buffer(pbxvar), value_str);
891 ast_debug(5, "On channel '%s' set TRANSFER_DATA variable '%s' to value '%s' \n", ast_channel_name(chan), hdr_name, value_str);
892 }
893 }
894 ast_free(pbxvar);
895 } else {
896 ast_log(LOG_ERROR, "Channel '%s' failed to allocate buffer for TRANSFER_DATA variable\n", ast_channel_name(chan));
897 }
898 }
899
900 referred_by = pjsip_msg_find_hdr_by_names(refer->rdata->msg_info.msg,
901 &str_referred_by, &str_referred_by_s, NULL);
902 if (referred_by) {
903 size_t uri_size = pj_strlen(&referred_by->hvalue) + 1;
904 char *uri = ast_alloca(uri_size);
905
906 ast_copy_pj_str(uri, &referred_by->hvalue, uri_size);
907 pbx_builtin_setvar_helper(chan, "__SIPREFERREDBYHDR", S_OR(uri, NULL));
908 } else {
909 pbx_builtin_setvar_helper(chan, "SIPREFERREDBYHDR", NULL);
910 }
911
912 if (refer->replaces) {
913 char replaces[512];
914 char *replaces_val = NULL;
915 int len;
916
917 len = pjsip_hdr_print_on(refer->replaces, replaces, sizeof(replaces) - 1);
918 if (len != -1) {
919 /* pjsip_hdr_print_on does not NULL terminate the buffer */
920 replaces[len] = '\0';
921 replaces_val = replaces + sizeof("Replaces:");
922 }
923 pbx_builtin_setvar_helper(chan, "__SIPREPLACESHDR", replaces_val);
924 } else {
925 pbx_builtin_setvar_helper(chan, "SIPREPLACESHDR", NULL);
926 }
927
928 if (refer->refer_to) {
929 char refer_to[PJSIP_MAX_URL_SIZE];
930
931 pjsip_uri_print(PJSIP_URI_IN_REQ_URI, refer->refer_to, refer_to, sizeof(refer_to));
932 pbx_builtin_setvar_helper(chan, "SIPREFERTOHDR", S_OR(refer_to, NULL));
933 } else {
934 pbx_builtin_setvar_helper(chan, "SIPREFERTOHDR", NULL);
935 }
936}
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_log
Definition astobj2.c:42
#define ast_channel_lock(chan)
Definition channel.h:2982
const char * ast_channel_uniqueid(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition channel.h:2983
char * end
Definition eagi_proxy.c:73
char buf[BUFSIZE]
Definition eagi_proxy.c:66
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
Definition framehook.c:132
#define AST_FRAMEHOOK_INTERFACE_VERSION
Definition framehook.h:227
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
static char prefix[MAX_PREFIX]
Definition http.c:144
#define LOG_ERROR
#define LOG_WARNING
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
static void refer_progress_framehook_destroy(void *data)
Destroy callback for monitoring framehook.
static struct ast_frame * refer_progress_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
Progress monitoring frame hook - examines frames to determine state of transfer.
static void refer_progress_bridge(void *data, struct stasis_subscription *sub, struct stasis_message *message)
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition stasis.h:297
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition stasis.c:1090
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition stasis.c:1144
#define stasis_subscribe_pool(topic, callback, data)
Definition stasis.h:680
struct stasis_message_type * ast_channel_entered_bridge_type(void)
Message type for ast_channel enter bridge blob messages.
struct stasis_topic * ast_bridge_topic_all(void)
A topic which publishes the events for all bridges.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition strings.h:1113
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition strings.h:223
Support for dynamic strings.
Definition strings.h:623
Structure for blind transfer callback details.
pjsip_replaces_hdr * replaces
Optional Replaces header.
pjsip_rx_data * rdata
REFER message.
struct refer_progress * progress
Optional progress structure.
unsigned int attended
Attended transfer flag.
const char * context
Context being used for transfer.
pjsip_sip_uri * refer_to
Optional Refer-To header.
struct transfer_channel_data * transfer_data
Reference to transfer_channel_data related to the refer.
unsigned int refer_blind_progress
Whether to notifies all the progress details on blind transfer.
struct stasis_subscription * bridge_sub
Stasis subscription for bridge events.
char * transferee
Uniqueid of transferee channel.
int framehook
Frame hook for monitoring REFER progress.

References ao2_cleanup, ao2_ref, ast_alloca, ast_bridge_topic_all(), ast_channel_entered_bridge_type(), ast_channel_lock, ast_channel_name(), ast_channel_uniqueid(), ast_channel_unlock, ast_copy_pj_str(), ast_debug, ast_framehook_attach(), ast_framehook_detach(), AST_FRAMEHOOK_INTERFACE_VERSION, ast_free, ast_log, ast_sip_push_task, ast_str_buffer(), ast_str_create, ast_str_set(), ast_strdup, ast_strdupa, ast_strip(), ast_strlen_zero(), refer_blind::attended, refer_progress::bridge_sub, buf, refer_blind::context, transfer_channel_data::data, ast_frame::data, end, refer_progress::framehook, len(), LOG_ERROR, LOG_WARNING, NULL, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), prefix, refer_blind::progress, refer_blind::rdata, refer_progress::refer_blind_progress, refer_progress_bridge(), refer_progress_framehook(), refer_progress_framehook_destroy(), refer_progress_notification_alloc(), refer_progress_notify(), refer_blind::refer_to, refer_blind::replaces, S_OR, refer_progress::serializer, stasis_subscribe_pool, stasis_subscription_accept_message_type(), stasis_subscription_change_type(), STASIS_SUBSCRIPTION_FILTER_SELECTIVE, stasis_subscription_set_filter(), refer_progress::transfer_data, refer_progress::transferee, and ast_framehook_interface::version.

Referenced by refer_incoming_attended_request(), and refer_incoming_blind_request().

◆ refer_client_on_evsub_state()

static void refer_client_on_evsub_state ( pjsip_evsub *  sub,
pjsip_event *  event 
)
static

Definition at line 1195 of file res_pjsip_refer.c.

1196{
1197 pjsip_tx_data *tdata;
1198 RAII_VAR(struct ast_sip_endpoint *, endpt, NULL, ao2_cleanup);
1200 int refer_success;
1201 int res = 0;
1202
1203 if (!event) {
1204 return;
1205 }
1206
1207 refer_data = pjsip_evsub_get_mod_data(sub, refer_out_of_dialog_module.id);
1208 if (!refer_data || !refer_data->dlg) {
1209 return;
1210 }
1211
1213
1214 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
1215 /* Check if subscription is suppressed and terminate and send completion code, if so. */
1216 pjsip_rx_data *rdata;
1217 pjsip_generic_string_hdr *refer_sub;
1218 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
1219
1220 ast_debug(3, "Refer accepted by %s\n", endpt ? ast_sorcery_object_get_id(endpt) : "(unknown endpoint)");
1221
1222 /* Check if response message */
1223 if (event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1224 rdata = event->body.tsx_state.src.rdata;
1225
1226 /* Find Refer-Sub header */
1227 refer_sub = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &REFER_SUB, NULL);
1228
1229 /* Check if subscription is suppressed. If it is, the far end will not terminate it,
1230 * and the subscription will remain active until it times out. Terminating it here
1231 * eliminates the unnecessary timeout.
1232 */
1233 if (refer_sub && !pj_stricmp2(&refer_sub->hvalue, "false")) {
1234 /* Since no subscription is desired, assume that call has been referred successfully
1235 * and terminate subscription.
1236 */
1237 pjsip_evsub_set_mod_data(sub, refer_out_of_dialog_module.id, NULL);
1238 pjsip_evsub_terminate(sub, PJ_TRUE);
1239 res = -1;
1240 }
1241 }
1242 } else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
1243 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1244 /* Check for NOTIFY complete or error. */
1245 pjsip_msg *msg;
1246 pjsip_msg_body *body;
1247 pjsip_status_line status_line = { .code = 0 };
1248 pj_bool_t is_last;
1249 pj_status_t status;
1250
1251 if (event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1252 pjsip_rx_data *rdata;
1253 pj_str_t refer_str;
1254 pj_cstr(&refer_str, "REFER");
1255
1256 rdata = event->body.tsx_state.src.rdata;
1257 msg = rdata->msg_info.msg;
1258
1259 if (msg->type == PJSIP_RESPONSE_MSG
1260 && (event->body.tsx_state.tsx->status_code == 401
1261 || event->body.tsx_state.tsx->status_code == 407)
1262 && pj_stristr(&refer_str, &event->body.tsx_state.tsx->method.name)
1263 && ++refer_data->authentication_challenge_count < MAX_RX_CHALLENGES
1264 && endpt) {
1265
1266 if (!ast_sip_create_request_with_auth(&endpt->outbound_auths,
1267 event->body.tsx_state.src.rdata, event->body.tsx_state.tsx->last_tx, &tdata)) {
1268 /* Send authed REFER */
1270 goto out;
1271 }
1272 }
1273
1274 if (msg->type == PJSIP_REQUEST_MSG) {
1275 if (!pjsip_method_cmp(&msg->line.req.method, pjsip_get_notify_method())) {
1276 body = msg->body;
1277 if (body && !pj_stricmp2(&body->content_type.type, "message")
1278 && !pj_stricmp2(&body->content_type.subtype, "sipfrag")) {
1279 pjsip_parse_status_line((char *)body->data, body->len, &status_line);
1280 }
1281 }
1282 } else {
1283 status_line.code = msg->line.status.code;
1284 status_line.reason = msg->line.status.reason;
1285 }
1286 } else {
1287 status_line.code = 500;
1288 status_line.reason = *pjsip_get_status_text(500);
1289 }
1290
1291 is_last = (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED);
1292 /* If the status code is >= 200, the subscription is finished. */
1293 if (status_line.code >= 200 || is_last) {
1294 res = -1;
1295
1296 refer_success = status_line.code >= 200 && status_line.code < 300;
1297
1298 /* If subscription not terminated and subscription is finished (status code >= 200)
1299 * terminate it */
1300 if (!is_last) {
1301 pjsip_tx_data *tdata;
1302
1303 status = pjsip_evsub_initiate(sub, pjsip_get_subscribe_method(), 0, &tdata);
1304 if (status == PJ_SUCCESS) {
1305 pjsip_evsub_send_request(sub, tdata);
1306 }
1307 }
1308 ast_debug(3, "Refer completed: %d %.*s (%s)\n",
1309 status_line.code,
1310 (int)status_line.reason.slen, status_line.reason.ptr,
1311 refer_success ? "Success" : "Failure");
1312 }
1313 }
1314
1315out:
1316 if (res) {
1318 }
1319}
jack_status_t status
Definition app_jack.c:149
struct ast_sip_endpoint * ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
Get the endpoint associated with this dialog.
static struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
int ast_sip_create_request_with_auth(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge, pjsip_tx_data *tdata, pjsip_tx_data **new_request)
Create a response to an authentication challenge.
Definition res_pjsip.c:208
int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint, void *token, void(*callback)(void *token, pjsip_event *e))
General purpose method for sending a SIP request.
Definition res_pjsip.c:1973
#define MAX_RX_CHALLENGES
Definition res_pjsip.h:108
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition sorcery.c:2381
An entity with which Asterisk communicates.
Definition res_pjsip.h:1061
FILE * out
Definition utils/frame.c:33
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981

References ao2_cleanup, ast_debug, ast_sip_create_request_with_auth(), ast_sip_dialog_get_endpoint(), ast_sip_send_request(), ast_sorcery_object_get_id(), MAX_RX_CHALLENGES, NULL, out, RAII_VAR, refer_out_of_dialog_module, status, and sub.

Referenced by refer_send().

◆ refer_data_create()

static struct refer_data * refer_data_create ( const struct ast_refer refer)
static

Definition at line 977 of file res_pjsip_refer.c.

978{
979 char *uri_params;
980 const char *destination;
982
983 if (!rdata) {
984 return NULL;
985 }
986
987 /* typecast to suppress const warning */
988 rdata->refer = ast_refer_ref((struct ast_refer *) refer);
990
991 /* To starts with 'pjsip:' which needs to be removed. */
992 if (!(destination = strchr(destination, ':'))) {
993 goto failure;
994 }
995 ++destination;/* Now skip the ':' */
996
998 if (!rdata->destination) {
999 goto failure;
1000 }
1001
1003 if (!rdata->from) {
1004 goto failure;
1005 }
1006
1008 if (!rdata->refer_to) {
1009 goto failure;
1010 }
1012
1013 /*
1014 * Sometimes from URI can contain URI parameters, so remove them.
1015 *
1016 * sip:user;user-options@domain;uri-parameters
1017 */
1018 uri_params = strchr(rdata->from, '@');
1019 if (uri_params && (uri_params = strchr(uri_params, ';'))) {
1020 *uri_params = '\0';
1021 }
1022 return rdata;
1023
1024failure:
1025 ao2_cleanup(rdata);
1026 return NULL;
1027}
const char * ast_refer_get_to(const struct ast_refer *refer)
Retrieve the destination of this refer.
Definition refer.c:231
const char * ast_refer_get_from(const struct ast_refer *refer)
Retrieve the source of this refer.
Definition refer.c:226
const char * ast_refer_get_refer_to(const struct ast_refer *refer)
Get the "refer-to" value of a refer.
Definition refer.c:221
struct ast_refer * ast_refer_ref(struct ast_refer *refer)
Bump a refer's ref count.
Definition refer.c:148
int ast_refer_get_to_self(const struct ast_refer *refer)
Retrieve the "to_self" value of this refer.
Definition refer.c:236
static void refer_data_destroy(void *obj)
A refer.
Definition refer.c:59
struct ast_refer * refer

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ao2_cleanup, ast_refer_get_from(), ast_refer_get_refer_to(), ast_refer_get_to(), ast_refer_get_to_self(), ast_refer_ref(), ast_strdup, refer_data::destination, refer_data::from, NULL, refer_data::refer, refer_data_destroy(), refer_data::refer_to, and refer_data::to_self.

Referenced by sip_refer_send().

◆ refer_data_destroy()

static void refer_data_destroy ( void *  obj)
static

Definition at line 966 of file res_pjsip_refer.c.

967{
968 struct refer_data *rdata = obj;
969
970 ast_free(rdata->destination);
971 ast_free(rdata->from);
972 ast_free(rdata->refer_to);
973
974 ast_refer_destroy(rdata->refer);
975}
struct ast_refer * ast_refer_destroy(struct ast_refer *refer)
Destroy an ast_refer.
Definition refer.c:154

References ast_free, ast_refer_destroy(), refer_data::destination, refer_data::from, refer_data::refer, and refer_data::refer_to.

Referenced by refer_data_create().

◆ refer_incoming_ari_request()

static int refer_incoming_ari_request ( struct ast_sip_session session,
pjsip_rx_data *  rdata,
pjsip_sip_uri *  target,
pjsip_param *  replaces_param,
struct refer_progress progress 
)
static

Definition at line 1486 of file res_pjsip_refer.c.

1488{
1489 int parsed_len;
1490 pjsip_replaces_hdr *replaces;
1491 pjsip_generic_string_hdr *referred_hdr;
1492
1493
1495
1496 struct ast_framehook_interface hook = {
1498 .event_cb = refer_ari_progress_framehook,
1500 .data = progress,
1501 .disable_inheritance = 1,
1502 };
1503
1504 static const pj_str_t str_referred_by = { "Referred-By", 11 };
1505 static const pj_str_t str_referred_by_s = { "b", 1 };
1506 static const pj_str_t str_replaces = { "Replaces", 8 };
1507
1508
1510 if (!state) {
1511 return 500;
1512 }
1513
1514 state->last_response = AST_TRANSFER_INVALID;
1515
1516 state->params = ao2_alloc(sizeof(struct ast_refer_params), refer_params_destroy);
1517 if (!state->params) {
1518 return 500;
1519 }
1520 AST_VECTOR_INIT(state->params, 0);
1521
1522
1523 ast_channel_ref(session->channel);
1524 state->transferer_chan = session->channel;
1525
1526 /* Using the user portion of the target URI see if it exists as a valid extension in their context */
1527 ast_copy_pj_str(state->exten, &target->user, sizeof(state->exten));
1528
1529 /*
1530 * We may want to match in the dialplan without any user
1531 * options getting in the way.
1532 */
1534
1535 referred_hdr = pjsip_msg_find_hdr_by_names(rdata->msg_info.msg,
1536 &str_referred_by, &str_referred_by_s, NULL);
1537 if (referred_hdr) {
1538 state->referred_by = copy_string(&referred_hdr->hvalue);
1539 if (!state->referred_by) {
1540 return 500;
1541 }
1542 }
1543
1544 if (replaces_param) {
1545 pjsip_dialog *dlg;
1546 pj_str_t replaces_content = { 0, };
1547 pj_strdup_with_null(rdata->tp_info.pool, &replaces_content, &replaces_param->value);
1548
1549 /* Parsing the parameter as a Replaces header easily grabs the needed information */
1550 if (!(replaces = pjsip_parse_hdr(rdata->tp_info.pool, &str_replaces, replaces_content.ptr,
1551 pj_strlen(&replaces_content), &parsed_len))) {
1552 ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' with invalid Replaces header, rejecting\n",
1554 return 400;
1555 }
1556
1557 dlg = pjsip_ua_find_dialog(&replaces->call_id, &replaces->to_tag, &replaces->from_tag, PJ_TRUE);
1558 if (dlg) {
1559 state->other_session = ast_sip_dialog_get_session(dlg);
1560 pjsip_dlg_dec_lock(dlg);
1561 }
1562
1563 state->protocol_id = copy_string(&replaces->call_id);
1564 if (!state->protocol_id) {
1565 return 500;
1566 }
1567
1568 if (!add_refer_param(state->params, "from", &replaces->from_tag)) {
1569 return 500;
1570 }
1571
1572 if (!add_refer_param(state->params, "to", &replaces->to_tag)) {
1573 return 500;
1574 }
1575 }
1576
1577 ao2_ref(session, +1);
1578 state->transferer = session;
1579
1580
1581 /* We need to bump the reference count up on the progress structure since it is in the frame hook now */
1582 ao2_ref(progress, +1);
1583 ast_channel_lock(session->channel);
1584 progress->framehook = ast_framehook_attach(session->channel, &hook);
1585 ast_channel_unlock(session->channel);
1586
1587 if (progress->framehook < 0) {
1589 return 500;
1590 }
1591
1592 if (ari_notify(state)) {
1593 ast_channel_lock(session->channel);
1594 ast_framehook_detach(session->channel, progress->framehook);
1595 progress->framehook = -1;
1597 ast_channel_unlock(session->channel);
1598 return 500;
1599 }
1600
1601 /* Transfer ownership to the progress */
1602 progress->ari_state = state;
1603 state = NULL;
1604 return 200;
1605}
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
#define AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(str)
Truncate the URI user field options string if enabled.
Definition res_pjsip.h:3529
static void transfer_ari_state_destroy(void *obj)
Destructor of the state used for the ARI transfer.
static void refer_params_destroy(void *obj)
static int ari_notify(struct transfer_ari_state *state)
static struct ast_frame * refer_ari_progress_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
Progress monitoring frame hook - examines frames to determine state of transfer. Used for the ari-onl...
static int add_refer_param(struct ast_refer_params *params, const char *key, struct pj_str_t *str)
struct ast_sip_session * ast_sip_dialog_get_session(pjsip_dialog *dlg)
Retrieves a session from a dialog.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124

References add_refer_param(), ao2_alloc, ao2_cleanup, ao2_ref, ari_notify(), ast_channel_lock, ast_channel_name(), ast_channel_ref, ast_channel_unlock, ast_copy_pj_str(), ast_framehook_attach(), ast_framehook_detach(), AST_FRAMEHOOK_INTERFACE_VERSION, ast_log, ast_sip_dialog_get_session(), AST_SIP_USER_OPTIONS_TRUNCATE_CHECK, ast_sorcery_object_get_id(), AST_TRANSFER_INVALID, AST_VECTOR_INIT, copy_string(), ast_frame::data, LOG_ERROR, NULL, RAII_VAR, refer_ari_progress_framehook(), refer_params_destroy(), refer_progress_framehook_destroy(), session, transfer_ari_state_destroy(), and ast_framehook_interface::version.

Referenced by refer_incoming_refer_request().

◆ refer_incoming_attended_request()

static int refer_incoming_attended_request ( struct ast_sip_session session,
pjsip_rx_data *  rdata,
pjsip_sip_uri *  target_uri,
pjsip_param *  replaces_param,
struct refer_progress progress 
)
static

Definition at line 1607 of file res_pjsip_refer.c.

1609{
1610 const pj_str_t str_replaces = { "Replaces", 8 };
1611 pj_str_t replaces_content;
1612 pjsip_replaces_hdr *replaces;
1613 int parsed_len;
1614 pjsip_dialog *dlg;
1615
1616 pj_strdup_with_null(rdata->tp_info.pool, &replaces_content, &replaces_param->value);
1617
1618 /* Parsing the parameter as a Replaces header easily grabs the needed information */
1619 if (!(replaces = pjsip_parse_hdr(rdata->tp_info.pool, &str_replaces, replaces_content.ptr,
1620 pj_strlen(&replaces_content), &parsed_len))) {
1621 ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' with invalid Replaces header, rejecting\n",
1623 return 400;
1624 }
1625
1626 /* See if the dialog is local, or remote */
1627 if ((dlg = pjsip_ua_find_dialog(&replaces->call_id, &replaces->to_tag, &replaces->from_tag, PJ_TRUE))) {
1628 RAII_VAR(struct ast_sip_session *, other_session, ast_sip_dialog_get_session(dlg), ao2_cleanup);
1629 struct refer_attended *attended;
1630
1631 pjsip_dlg_dec_lock(dlg);
1632
1633 if (!other_session) {
1634 ast_debug(3, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but no session exists on it\n",
1636 return 603;
1637 }
1638
1639 /* We defer actually doing the attended transfer to the other session so no deadlock can occur */
1640 if (!(attended = refer_attended_alloc(session, other_session, progress))) {
1641 ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but could not allocate structure to complete, rejecting\n",
1643 return 500;
1644 }
1645
1647 ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but could not defer termination, rejecting\n",
1649 ao2_cleanup(attended);
1650 return 500;
1651 }
1652
1653 /* Push it to the other session, which will have both channels with minimal locking */
1654 if (ast_sip_push_task(other_session->serializer, refer_attended_task, attended)) {
1657 ao2_cleanup(attended);
1658 return 500;
1659 }
1660
1661 ast_debug(3, "Attended transfer from '%s' pushed to second channel serializer\n",
1662 ast_channel_name(session->channel));
1663
1664 return 200;
1665 } else {
1666 const char *context;
1667 struct refer_blind refer = { 0, };
1668 int response;
1669
1671
1672 if (!ast_exists_extension(NULL, context, "external_replaces", 1, NULL)) {
1673 ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but 'external_replaces' extension not found in context %s\n",
1675 return 404;
1676 }
1677
1678 refer.context = context;
1679 refer.progress = progress;
1680 refer.rdata = rdata;
1681 refer.replaces = replaces;
1682 refer.refer_to = target_uri;
1683 refer.attended = 1;
1684
1686 ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but could not defer termination, rejecting\n",
1687 ast_channel_name(session->channel),
1689 return 500;
1690 }
1691
1693 "external_replaces", context, refer_blind_callback, &refer));
1694
1696 if (response != 200) {
1698 }
1699
1700 return response;
1701 }
1702}
enum ast_transfer_result ast_bridge_transfer_blind(int is_external, struct ast_channel *transferer, const char *exten, const char *context, transfer_channel_cb new_channel_cb, void *user_data)
Blind transfer target to the extension and context provided.
Definition bridge.c:4504
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition pbx.c:4196
#define DETERMINE_TRANSFER_CONTEXT(context, session)
static void refer_blind_callback(struct ast_channel *chan, struct transfer_channel_data *user_data_wrapper, enum ast_transfer_type transfer_type)
Blind transfer callback function.
static int refer_attended_task(void *data)
Task for attended transfer executed by attended->transferer_second serializer.
static struct refer_attended * refer_attended_alloc(struct ast_sip_session *transferer, struct ast_sip_session *transferer_second, struct refer_progress *progress)
Allocator for attended transfer task.
int ast_sip_session_defer_termination(struct ast_sip_session *session)
Defer local termination of a session until remote side terminates, or an amount of time passes.

References ao2_cleanup, ast_bridge_transfer_blind(), ast_channel_name(), ast_debug, ast_exists_extension(), ast_log, ast_sip_dialog_get_session(), ast_sip_push_task, ast_sip_session_defer_termination(), ast_sip_session_defer_termination_cancel(), ast_sip_session_end_if_deferred(), ast_sorcery_object_get_id(), refer_blind::attended, refer_blind::context, DETERMINE_TRANSFER_CONTEXT, LOG_ERROR, NULL, refer_blind::progress, RAII_VAR, refer_blind::rdata, refer_attended_alloc(), refer_attended_task(), refer_blind_callback(), refer_blind::refer_to, refer_blind::replaces, session, and xfer_response_code2sip().

Referenced by refer_incoming_refer_request().

◆ refer_incoming_blind_request()

static int refer_incoming_blind_request ( struct ast_sip_session session,
pjsip_rx_data *  rdata,
pjsip_sip_uri *  target,
struct refer_progress progress 
)
static

Definition at line 1704 of file res_pjsip_refer.c.

1706{
1707 const char *context;
1708 char exten[AST_MAX_EXTENSION];
1709 struct refer_blind refer = { 0, };
1710 int response;
1711
1712 /* If no explicit transfer context has been provided use their configured context */
1714
1715 /* Using the user portion of the target URI see if it exists as a valid extension in their context */
1716 ast_copy_pj_str(exten, &target->user, sizeof(exten));
1717
1718 /*
1719 * We may want to match in the dialplan without any user
1720 * options getting in the way.
1721 */
1723
1724 /* Uri without exten */
1725 if (ast_strlen_zero(exten)) {
1726 ast_copy_string(exten, "s", sizeof(exten));
1727 ast_debug(3, "Channel '%s' from endpoint '%s' attempted blind transfer to a target without extension. Target was set to 's@%s'\n",
1729 }
1730
1731 if (!ast_exists_extension(NULL, context, exten, 1, NULL)) {
1732 ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer to '%s@%s' but target does not exist\n",
1733 ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), exten, context);
1734 return 404;
1735 }
1736
1737 refer.context = context;
1738 refer.progress = progress;
1739 refer.rdata = rdata;
1740 refer.refer_to = target;
1741 refer.attended = 0;
1742
1744 ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer but could not defer termination, rejecting\n",
1745 ast_channel_name(session->channel),
1747 return 500;
1748 }
1749
1751 exten, context, refer_blind_callback, &refer));
1752
1754 if (response != 200) {
1756 }
1757
1758 return response;
1759}
#define AST_MAX_EXTENSION
Definition channel.h:134
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425

References ast_bridge_transfer_blind(), ast_channel_name(), ast_copy_pj_str(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_log, AST_MAX_EXTENSION, ast_sip_session_defer_termination(), ast_sip_session_defer_termination_cancel(), ast_sip_session_end_if_deferred(), AST_SIP_USER_OPTIONS_TRUNCATE_CHECK, ast_sorcery_object_get_id(), ast_strlen_zero(), refer_blind::attended, refer_blind::context, DETERMINE_TRANSFER_CONTEXT, LOG_ERROR, NULL, refer_blind::progress, refer_blind::rdata, refer_blind_callback(), refer_blind::refer_to, session, and xfer_response_code2sip().

Referenced by refer_incoming_refer_request().

◆ refer_incoming_invite_request()

static int refer_incoming_invite_request ( struct ast_sip_session session,
struct pjsip_rx_data *  rdata 
)
static

Definition at line 1787 of file res_pjsip_refer.c.

1788{
1789 pjsip_dialog *other_dlg = NULL;
1790 pjsip_tx_data *packet;
1791 int response = 0;
1792 RAII_VAR(struct ast_sip_session *, other_session, NULL, ao2_cleanup);
1793 struct invite_replaces invite;
1794
1795 /* If a Replaces header is present make sure it is valid */
1796 if (pjsip_replaces_verify_request(rdata, &other_dlg, PJ_TRUE, &packet) != PJ_SUCCESS) {
1797 response = packet->msg->line.status.code;
1798 ast_assert(response != 0);
1799 pjsip_tx_data_dec_ref(packet);
1800 goto inv_replace_failed;
1801 }
1802
1803 /* If no other dialog exists then this INVITE request does not have a Replaces header */
1804 if (!other_dlg) {
1805 return 0;
1806 }
1807
1808 other_session = ast_sip_dialog_get_session(other_dlg);
1809 pjsip_dlg_dec_lock(other_dlg);
1810
1811 /* Don't accept an in-dialog INVITE with Replaces as it does not make much sense */
1812 if (session->inv_session->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
1813 response = 488;
1814 goto inv_replace_failed;
1815 }
1816
1817 if (!other_session) {
1818 ast_debug(3, "INVITE with Replaces received on channel '%s' from endpoint '%s', but requested session does not exist\n",
1820 response = 481;
1821 goto inv_replace_failed;
1822 }
1823
1824 invite.session = other_session;
1825
1826 if (ast_sip_push_task_wait_serializer(other_session->serializer, invite_replaces,
1827 &invite)) {
1828 response = 481;
1829 goto inv_replace_failed;
1830 }
1831
1832 ast_channel_lock(session->channel);
1834 ast_channel_unlock(session->channel);
1835 ast_raw_answer(session->channel);
1836
1837 ast_debug(3, "INVITE with Replaces being attempted. '%s' --> '%s'\n",
1838 ast_channel_name(session->channel), ast_channel_name(invite.channel));
1839
1840 /* Unhold the channel now, as later we are not having access to it anymore */
1841 ast_queue_unhold(session->channel);
1843
1844 if (!invite.bridge) {
1845 struct ast_channel *chan = session->channel;
1846
1847 /*
1848 * This will use a synchronous task but we aren't operating in
1849 * the serializer at this point in time, so it won't deadlock.
1850 */
1851 if (!ast_channel_move(invite.channel, chan)) {
1852 /*
1853 * We can't directly use session->channel because ast_channel_move()
1854 * does a masquerade which changes session->channel to a different
1855 * channel. To ensure we work on the right channel we store a
1856 * pointer locally before we begin so it remains valid.
1857 */
1858 ast_hangup(chan);
1859 } else {
1860 response = AST_CAUSE_FAILURE;
1861 }
1862 } else {
1863 if (ast_bridge_impart(invite.bridge, session->channel, invite.channel, NULL,
1865 response = AST_CAUSE_FAILURE;
1866 }
1867 }
1868
1869 ast_channel_unref(invite.channel);
1870 ao2_cleanup(invite.bridge);
1871
1872 if (!response) {
1873 /*
1874 * On success we cannot use session->channel in the debug message.
1875 * This thread either no longer has a ref to session->channel or
1876 * session->channel is no longer the original channel.
1877 */
1878 ast_debug(3, "INVITE with Replaces successfully completed.\n");
1879 } else {
1880 ast_debug(3, "INVITE with Replaces failed on channel '%s', hanging up with cause '%d'\n",
1881 ast_channel_name(session->channel), response);
1882 ast_channel_lock(session->channel);
1883 ast_channel_hangupcause_set(session->channel, response);
1884 ast_channel_unlock(session->channel);
1885 ast_hangup(session->channel);
1886 }
1887
1888 return 1;
1889
1890inv_replace_failed:
1891 if (session->inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
1892 ast_debug(3, "INVITE with Replaces failed on channel '%s', sending response of '%d'\n",
1893 ast_channel_name(session->channel), response);
1894 session->defer_terminate = 1;
1895 ast_hangup(session->channel);
1896
1897 if (pjsip_inv_end_session(session->inv_session, response, NULL, &packet) == PJ_SUCCESS
1898 && packet) {
1900 }
1901 } else {
1902 ast_debug(3, "INVITE with Replaces in-dialog on channel '%s', hanging up\n",
1903 ast_channel_name(session->channel));
1904 ast_queue_hangup(session->channel);
1905 }
1906
1907 return 1;
1908}
int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, enum ast_bridge_impart_flags flags) attribute_warn_unused_result
Impart a channel to a bridge (non-blocking)
Definition bridge.c:1947
@ AST_BRIDGE_IMPART_CHAN_INDEPENDENT
Definition bridge.h:594
#define AST_CAUSE_FAILURE
Definition causes.h:150
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition channel.c:2538
int ast_queue_hangup(struct ast_channel *chan)
Queue a hangup frame.
Definition channel.c:1181
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f)
Queue one or more frames to a channel's frame queue.
Definition channel.c:1170
int ast_channel_move(struct ast_channel *dest, struct ast_channel *source)
Move a channel from its current location to a new location.
Definition channel.c:10714
int ast_queue_unhold(struct ast_channel *chan)
Queue an unhold frame.
Definition channel.c:1273
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3018
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
int ast_raw_answer(struct ast_channel *chan)
Answer a channel.
Definition channel.c:2688
@ AST_STATE_RING
int ast_setstate(struct ast_channel *chan, enum ast_channel_state)
Change the state of a channel.
Definition channel.c:7398
#define ast_sip_push_task_wait_serializer(serializer, sip_task, task_data)
Definition res_pjsip.h:2189
struct ast_frame ast_null_frame
Definition main/frame.c:79
void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
Send a SIP response.
Main Channel structure associated with a channel.
#define ast_assert(a)
Definition utils.h:779

References ao2_cleanup, ast_assert, ast_bridge_impart(), AST_BRIDGE_IMPART_CHAN_INDEPENDENT, AST_CAUSE_FAILURE, ast_channel_hangupcause_set(), ast_channel_lock, ast_channel_move(), ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_debug, ast_hangup(), ast_null_frame, ast_queue_frame(), ast_queue_hangup(), ast_queue_unhold(), ast_raw_answer(), ast_setstate(), ast_sip_dialog_get_session(), ast_sip_push_task_wait_serializer, ast_sip_session_send_response(), ast_sorcery_object_get_id(), AST_STATE_RING, invite_replaces::bridge, invite_replaces::channel, NULL, RAII_VAR, invite_replaces::session, and session.

Referenced by refer_incoming_request().

◆ refer_incoming_refer_request()

static int refer_incoming_refer_request ( struct ast_sip_session session,
struct pjsip_rx_data *  rdata 
)
static

Definition at line 1910 of file res_pjsip_refer.c.

1911{
1912 pjsip_generic_string_hdr *refer_to;
1913 char *uri;
1914 size_t uri_size;
1915 pjsip_uri *target;
1916 pjsip_sip_uri *target_uri;
1918 pjsip_param *replaces;
1919 int response;
1920
1921 static const pj_str_t str_refer_to = { "Refer-To", 8 };
1922 static const pj_str_t str_refer_to_s = { "r", 1 };
1923 static const pj_str_t str_replaces = { "Replaces", 8 };
1924
1925 if (!session->channel) {
1926 /* No channel to refer. Likely because the call was just hung up. */
1927 pjsip_dlg_respond(session->inv_session->dlg, rdata, 404, NULL, NULL, NULL);
1928 ast_debug(3, "Received a REFER on a session with no channel from endpoint '%s'.\n",
1930 return 0;
1931 }
1932
1933 if (!session->endpoint->allowtransfer) {
1934 pjsip_dlg_respond(session->inv_session->dlg, rdata, 603, NULL, NULL, NULL);
1935 ast_log(LOG_WARNING, "Endpoint %s transfer attempt blocked due to configuration\n",
1937 return 0;
1938 }
1939
1940 /* A Refer-To header is required */
1941 refer_to = pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &str_refer_to, &str_refer_to_s, NULL);
1942 if (!refer_to) {
1943 pjsip_dlg_respond(session->inv_session->dlg, rdata, 400, NULL, NULL, NULL);
1944 ast_debug(3, "Received a REFER without Refer-To on channel '%s' from endpoint '%s'\n",
1946 return 0;
1947 }
1948
1949 /* The ast_copy_pj_str to uri is needed because it puts the NULL terminator to the uri
1950 * as pjsip_parse_uri require a NULL terminated uri
1951 */
1952
1953 uri_size = pj_strlen(&refer_to->hvalue) + 1;
1954 uri = ast_alloca(uri_size);
1955 ast_copy_pj_str(uri, &refer_to->hvalue, uri_size);
1956
1957 target = pjsip_parse_uri(rdata->tp_info.pool, uri, uri_size - 1, 0);
1958
1959 if (!target
1960 || (!PJSIP_URI_SCHEME_IS_SIP(target)
1961 && !PJSIP_URI_SCHEME_IS_SIPS(target))) {
1962
1963 pjsip_dlg_respond(session->inv_session->dlg, rdata, 400, NULL, NULL, NULL);
1964 ast_debug(3, "Received a REFER without a parseable Refer-To ('%s') on channel '%s' from endpoint '%s'\n",
1965 uri, ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint));
1966 return 0;
1967 }
1968 target_uri = pjsip_uri_get_uri(target);
1969
1970 /* Set up REFER progress subscription if requested/possible */
1971 if (refer_progress_alloc(session, rdata, &progress)) {
1972 pjsip_dlg_respond(session->inv_session->dlg, rdata, 500, NULL, NULL, NULL);
1973 ast_debug(3, "Could not set up subscription for REFER on channel '%s' from endpoint '%s'\n",
1975 return 0;
1976 }
1977
1978 replaces = pjsip_param_find(&target_uri->header_param, &str_replaces);
1979 if (!replaces) {
1980 replaces = pjsip_param_find(&target_uri->other_param, &str_replaces);
1981 }
1982
1983 /* Determine if this is handled externally or an attended or blind transfer */
1984 if (session->transferhandling_ari) {
1985 response = refer_incoming_ari_request(session, rdata, target_uri, replaces, progress);
1986 } else if (replaces) {
1987 response = refer_incoming_attended_request(session, rdata, target_uri, replaces, progress);
1988 } else {
1989 response = refer_incoming_blind_request(session, rdata, target_uri, progress);
1990 }
1991
1992 if (!progress) {
1993 /* The transferer has requested no subscription, so send a final response immediately */
1994 pjsip_tx_data *tdata;
1995 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
1996 const pj_str_t str_false = { "false", 5 };
1997 pjsip_hdr *hdr;
1998
1999 ast_debug(3, "Progress monitoring not requested for REFER on channel '%s' from endpoint '%s', sending immediate response of '%d'\n",
2000 ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), response);
2001
2002 if (pjsip_dlg_create_response(session->inv_session->dlg, rdata, response, NULL, &tdata) != PJ_SUCCESS) {
2003 pjsip_dlg_respond(session->inv_session->dlg, rdata, response, NULL, NULL, NULL);
2004 return 0;
2005 }
2006
2007 hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, &str_false);
2008 pjsip_msg_add_hdr(tdata->msg, hdr);
2009
2010 pjsip_dlg_send_response(session->inv_session->dlg, pjsip_rdata_get_tsx(rdata), tdata);
2011 } else if (response != 200) {
2012 /* Since this failed we can send a final NOTIFY now and terminate the subscription */
2013 struct refer_progress_notification *notification = refer_progress_notification_alloc(progress, response, PJSIP_EVSUB_STATE_TERMINATED);
2014
2015 if (notification) {
2016 /* The refer_progress_notify function will call ao2_cleanup on this for us */
2017 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
2018 ao2_cleanup(notification);
2019 }
2020 }
2021 }
2022
2023 return 0;
2024}
static int refer_incoming_ari_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target, pjsip_param *replaces_param, struct refer_progress *progress)
static int refer_incoming_attended_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target_uri, pjsip_param *replaces_param, struct refer_progress *progress)
static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target, struct refer_progress *progress)
static int refer_progress_alloc(struct ast_sip_session *session, pjsip_rx_data *rdata, struct refer_progress **progress)
Internal helper function which sets up a refer progress structure if needed.

References ao2_cleanup, ast_alloca, ast_channel_name(), ast_copy_pj_str(), ast_debug, ast_log, ast_sip_push_task, ast_sorcery_object_get_id(), LOG_WARNING, NULL, RAII_VAR, refer_incoming_ari_request(), refer_incoming_attended_request(), refer_incoming_blind_request(), refer_progress_alloc(), refer_progress_notification_alloc(), refer_progress_notify(), refer_progress_notification::response, and session.

Referenced by refer_incoming_request().

◆ refer_incoming_request()

static int refer_incoming_request ( struct ast_sip_session session,
pjsip_rx_data *  rdata 
)
static

Definition at line 2026 of file res_pjsip_refer.c.

2027{
2028 if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_refer_method())) {
2030 } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_invite_method)) {
2032 } else {
2033 return 0;
2034 }
2035}
static int refer_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
static int refer_incoming_refer_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)

References refer_incoming_invite_request(), refer_incoming_refer_request(), and session.

◆ refer_on_tx_request()

static pj_status_t refer_on_tx_request ( pjsip_tx_data *  tdata)
static

Definition at line 1124 of file res_pjsip_refer.c.

1124 {
1125 RAII_VAR(struct ast_str *, refer_to_str, ast_str_create(PJSIP_MAX_URL_SIZE), ast_free_ptr);
1126 const pj_str_t REFER_TO = { "Refer-To", 8 };
1127 pjsip_generic_string_hdr *refer_to_hdr;
1128 pjsip_dialog *dlg;
1129 struct refer_data *refer_data;
1130 pjsip_uri *parsed_uri;
1131 pjsip_sip_uri *refer_to_uri;
1132
1133 /*
1134 * If this is a request in response to a 401/407 Unauthorized challenge, the
1135 * Refer-To URI has been rewritten already, so don't attempt to re-write it again.
1136 * Checking for presence of the Authorization header is not an ideal solution. We do this because
1137 * there exists some race condition where this dialog is not the same as the one used
1138 * to send the original request in which case we don't have the correct refer_data.
1139 */
1140 if (!refer_to_str
1141 || pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL)
1142 || !(dlg = pjsip_tdata_get_dlg(tdata))
1143 || !(refer_data = pjsip_dlg_get_mod_data(dlg, refer_out_of_dialog_module.id))
1144 || !refer_data->to_self
1145 || !(parsed_uri = get_refer_to_uri(tdata))) {
1146 goto out;
1147 }
1148 refer_to_uri = pjsip_uri_get_uri(parsed_uri);
1149 ast_sip_rewrite_uri_to_local(refer_to_uri, tdata);
1150
1151 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, parsed_uri, ast_str_buffer(refer_to_str), ast_str_size(refer_to_str));
1152 refer_to_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &REFER_TO, NULL);
1153 pj_strdup2(tdata->pool, &refer_to_hdr->hvalue, ast_str_buffer(refer_to_str));
1154
1155out:
1156 return PJ_SUCCESS;
1157}
void ast_free_ptr(void *ptr)
free() wrapper
Definition astmm.c:1739
int ast_sip_rewrite_uri_to_local(pjsip_sip_uri *uri, pjsip_tx_data *tdata)
Replace domain and port of SIP URI to point to (external) signaling address of this Asterisk instance...
Definition res_pjsip.c:599
static pjsip_uri * get_refer_to_uri(pjsip_tx_data *tdata)
Helper function which returns the name-addr of the Refer-To header or NULL.
size_t attribute_pure ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition strings.h:742

References ast_free_ptr(), ast_sip_rewrite_uri_to_local(), ast_str_buffer(), ast_str_create, ast_str_size(), get_refer_to_uri(), NULL, out, RAII_VAR, refer_out_of_dialog_module, and refer_data::to_self.

◆ refer_out_of_dialog_destroy()

static void refer_out_of_dialog_destroy ( void *  obj)
static

Destructor for REFER out of dialog structure.

Definition at line 1176 of file res_pjsip_refer.c.

1176 {
1177 struct refer_out_of_dialog *data = obj;
1178
1179 if (data->dlg) {
1180 /* ast_sip_push_task_wait_servant should not be called in a destructor,
1181 * however in this case it seems to be fine.
1182 */
1184 }
1185}
#define ast_sip_push_task_wait_servant(serializer, sip_task, task_data)
Definition res_pjsip.h:2133
static int refer_unreference_dialog(void *obj)

References ast_sip_push_task_wait_servant, refer_out_of_dialog::dlg, refer_serializer, and refer_unreference_dialog().

Referenced by refer_send().

◆ refer_outgoing_request()

static void refer_outgoing_request ( struct ast_sip_session session,
struct pjsip_tx_data *  tdata 
)
static

Definition at line 2070 of file res_pjsip_refer.c.

2071{
2072 if (pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_invite_method)
2073 || !session->channel
2074 || session->inv_session->state != PJSIP_INV_STATE_NULL) {
2075 return;
2076 }
2077
2078 ast_channel_lock(session->channel);
2079 add_header_from_channel_var(session->channel, "SIPREPLACESHDR", "Replaces", tdata);
2080 add_header_from_channel_var(session->channel, "SIPREFERREDBYHDR", "Referred-By", tdata);
2081 ast_channel_unlock(session->channel);
2082}
static void add_header_from_channel_var(struct ast_channel *chan, const char *var_name, const char *header_name, pjsip_tx_data *tdata)
Use the value of a channel variable as the value of a SIP header.

References add_header_from_channel_var(), ast_channel_lock, ast_channel_unlock, and session.

◆ refer_params_destroy()

static void refer_params_destroy ( void *  obj)
static

Definition at line 121 of file res_pjsip_refer.c.

122{
123 struct ast_refer_params *params = obj;
124
125 for (int i = 0; i < AST_VECTOR_SIZE(params); ++i) {
126 struct ast_refer_param param = AST_VECTOR_GET(params, i);
127 ast_free((char *) param.param_name);
128 ast_free((char *) param.param_value);
129 }
130}
const char * param_value
Definition refer.h:83
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:620
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:691

References ast_free, AST_VECTOR_GET, AST_VECTOR_SIZE, ast_refer_param::param_name, and ast_refer_param::param_value.

Referenced by refer_incoming_ari_request().

◆ refer_progress_alloc()

static int refer_progress_alloc ( struct ast_sip_session session,
pjsip_rx_data *  rdata,
struct refer_progress **  progress 
)
static

Internal helper function which sets up a refer progress structure if needed.

Definition at line 496 of file res_pjsip_refer.c.

497{
498 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
499 pjsip_generic_string_hdr *refer_sub = NULL;
500 const pj_str_t str_true = { "true", 4 };
501 pjsip_hdr hdr_list;
502 char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
503
504 *progress = NULL;
505
506 /* Grab the optional Refer-Sub header, it can be used to suppress the implicit subscription */
507 refer_sub = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
508 if ((refer_sub && pj_strnicmp(&refer_sub->hvalue, &str_true, 4))) {
509 return 0;
510 }
511
512 if (!(*progress = ao2_alloc(sizeof(struct refer_progress), refer_progress_destroy))) {
513 return -1;
514 }
515
516 ast_debug(3, "Created progress monitor '%p' for transfer occurring from channel '%s' and endpoint '%s'\n",
518
519 (*progress)->refer_blind_progress = session->endpoint->refer_blind_progress;
520
521 (*progress)->framehook = -1;
522
523 /* Create name with seq number appended. */
524 ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/refer/%s",
526
527 if (!((*progress)->serializer = ast_sip_create_serializer(tps_name))) {
528 goto error;
529 }
530
531 /* Create the implicit subscription for monitoring of this transfer */
532 if (pjsip_xfer_create_uas(session->inv_session->dlg, &refer_progress_evsub_cb, rdata, &(*progress)->sub) != PJ_SUCCESS) {
533 goto error;
534 }
535
536 /* To prevent a potential deadlock we need the dialog so we can lock/unlock */
537 (*progress)->dlg = session->inv_session->dlg;
538 /* We also need to make sure it stays around until we're done with it */
539 pjsip_dlg_inc_session((*progress)->dlg, &refer_progress_module);
540
541
542 /* Associate the REFER progress structure with the subscription */
543 ao2_ref(*progress, +1);
544 pjsip_evsub_set_mod_data((*progress)->sub, refer_progress_module.id, *progress);
545
546 pj_list_init(&hdr_list);
547 if (refer_sub) {
548 pjsip_hdr *hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(session->inv_session->dlg->pool, &str_refer_sub, &str_true);
549
550 pj_list_push_back(&hdr_list, hdr);
551 }
552
553 /* Accept the REFER request */
554 ast_debug(3, "Accepting REFER request for progress monitor '%p'\n", *progress);
555 pjsip_xfer_accept((*progress)->sub, rdata, 202, &hdr_list);
556
557 return 0;
558
559error:
561 *progress = NULL;
562 return -1;
563}
static void refer_progress_destroy(void *obj)
Destructor for REFER progress sutrcture.
static pjsip_evsub_user refer_progress_evsub_cb
Callback structure for subscription.
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
int error(const char *format,...)

References ao2_alloc, ao2_cleanup, ao2_ref, ast_channel_name(), ast_debug, ast_sip_create_serializer(), ast_sorcery_object_get_id(), ast_taskprocessor_build_name(), AST_TASKPROCESSOR_MAX_NAME, error(), NULL, refer_progress::rdata, refer_progress_destroy(), refer_progress_evsub_cb, refer_progress_module, and session.

Referenced by refer_incoming_refer_request().

◆ refer_progress_bridge()

static void refer_progress_bridge ( void *  data,
struct stasis_subscription sub,
struct stasis_message message 
)
static

Definition at line 242 of file res_pjsip_refer.c.

244{
245 struct refer_progress *progress = data;
246 struct ast_bridge_blob *enter_blob;
247 struct refer_progress_notification *notification;
248 struct ast_channel *chan;
249
251 ao2_ref(progress, -1);
252 return;
253 }
254
256 /* Don't care */
257 return;
258 }
259
260 enter_blob = stasis_message_data(message);
261 if (strcmp(enter_blob->channel->base->uniqueid, progress->transferee)) {
262 /* Don't care */
263 return;
264 }
265
266 if (!progress->transfer_data->completed) {
267 /* We can't act on this message because the transfer_channel_data doesn't show that
268 * the transfer is ready to progress */
269 return;
270 }
271
272 /* OMG the transferee is joining a bridge. His call got answered! */
273 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
274 if (notification) {
275 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
276 ao2_cleanup(notification);
277 }
278 progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
279 }
280
281 chan = ast_channel_get_by_name(progress->transferee);
282 if (!chan) {
283 /* The channel is already gone */
284 return;
285 }
286
287 ast_channel_lock(chan);
288 ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as it has joined a bridge\n",
289 ast_channel_name(chan));
290 ast_framehook_detach(chan, progress->framehook);
291 ast_channel_unlock(chan);
292
293 ast_channel_unref(chan);
294}
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition channel.c:1416
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
Determine whether a message is the final message to be received on a subscription.
Definition stasis.c:1241
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition stasis.c:1038
Blob of data associated with a bridge.
struct ast_channel_snapshot * channel
const ast_string_field uniqueid
struct ast_channel_snapshot_base * base

References ao2_cleanup, ao2_ref, ast_channel_entered_bridge_type(), ast_channel_get_by_name(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_debug, ast_framehook_detach(), ast_sip_push_task, ast_channel_snapshot::base, ast_bridge_blob::channel, refer_progress_notification_alloc(), refer_progress_notify(), stasis_message_data(), stasis_subscription_final_message(), stasis_unsubscribe(), sub, and ast_channel_snapshot_base::uniqueid.

Referenced by refer_blind_callback().

◆ refer_progress_destroy()

static void refer_progress_destroy ( void *  obj)
static

Destructor for REFER progress sutrcture.

Definition at line 462 of file res_pjsip_refer.c.

463{
464 struct refer_progress *progress = obj;
465
466 if (progress->bridge_sub) {
467 progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
468 }
469
470 if (progress->dlg) {
471 /*
472 * Although the dlg session count was incremented in a pjsip servant
473 * thread, there's no guarantee that the last thread to unref this progress
474 * object was one. Before we decrement, we need to make sure that this
475 * is either a servant thread or that we push the decrement to a
476 * serializer that is one.
477 *
478 * Because pjsip_dlg_dec_session requires the dialog lock, we don't want
479 * to wait on the task to complete if we had to push it to a serializer.
480 */
482 pjsip_dlg_dec_session(progress->dlg, &refer_progress_module);
483 } else {
485 }
486 }
487
488 ao2_cleanup(progress->transfer_data);
489 ao2_cleanup(progress->ari_state);
490
491 ast_free(progress->transferee);
493}
int ast_sip_thread_is_servant(void)
Determine if the current thread is a SIP servant thread.
Definition res_pjsip.c:2281
static int dlg_releaser_task(void *data)
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.

References ao2_cleanup, ast_free, ast_sip_push_task, ast_sip_thread_is_servant(), ast_taskprocessor_unreference(), dlg_releaser_task(), NULL, refer_progress_module, and stasis_unsubscribe().

Referenced by refer_progress_alloc().

◆ refer_progress_framehook()

static struct ast_frame * refer_progress_framehook ( struct ast_channel chan,
struct ast_frame f,
enum ast_framehook_event  event,
void *  data 
)
static

Progress monitoring frame hook - examines frames to determine state of transfer.

Definition at line 297 of file res_pjsip_refer.c.

298{
299 struct refer_progress *progress = data;
300 struct refer_progress_notification *notification = NULL;
301
302 /* We only care about frames *to* the channel */
303 if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
304 return f;
305 }
306
307 /* If the completed flag hasn't been raised, skip this pass. */
308 if (!progress->transfer_data->completed) {
309 return f;
310 }
311
312 /* Determine the state of the REFER based on the control frames (or voice frames) passing */
313 if (f->frametype == AST_FRAME_VOICE && !progress->subclass) {
314 /* Media is passing without progress, this means the call has been answered */
315 progress->subclass = AST_CONTROL_ANSWER;
316 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
317 } else if (f->frametype == AST_FRAME_CONTROL) {
318 /* Based on the control frame being written we can send a NOTIFY advising of the progress */
320 /* Don't set progress->subclass; an ANSWER can still follow */
321 notification = refer_progress_notification_alloc(progress, 180, PJSIP_EVSUB_STATE_ACTIVE);
322 } else if (f->subclass.integer == AST_CONTROL_BUSY) {
323 progress->subclass = f->subclass.integer;
324 notification = refer_progress_notification_alloc(progress, 486, PJSIP_EVSUB_STATE_TERMINATED);
325 } else if (f->subclass.integer == AST_CONTROL_CONGESTION) {
326 progress->subclass = f->subclass.integer;
327 notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
328 } else if (f->subclass.integer == AST_CONTROL_PROGRESS) {
329 /* Don't set progress->subclass; an ANSWER can still follow */
330 notification = refer_progress_notification_alloc(progress, 183, PJSIP_EVSUB_STATE_ACTIVE);
331 } else if (f->subclass.integer == AST_CONTROL_PROCEEDING) {
332 /* Don't set progress->subclass; an ANSWER can still follow */
333 notification = refer_progress_notification_alloc(progress, 100, PJSIP_EVSUB_STATE_ACTIVE);
334 } else if (f->subclass.integer == AST_CONTROL_ANSWER) {
335 progress->subclass = f->subclass.integer;
336 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
337 }
338 }
339
340 /* If a notification is due to be sent push it to the taskpool */
341 if (notification) {
342 /* If the subscription is being terminated we don't need the frame hook any longer */
343 if (notification->state == PJSIP_EVSUB_STATE_TERMINATED) {
344 ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as subscription is being terminated\n",
345 ast_channel_name(chan));
346 ast_framehook_detach(chan, progress->framehook);
347 }
348
349 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
350 ao2_cleanup(notification);
351 }
352 }
353
354 return f;
355}
@ AST_CONTROL_PROGRESS
@ AST_CONTROL_PROCEEDING
@ AST_CONTROL_CONGESTION
@ AST_CONTROL_ANSWER
@ AST_CONTROL_RINGING

References ao2_cleanup, ast_channel_name(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, AST_CONTROL_RING, AST_CONTROL_RINGING, ast_debug, AST_FRAME_CONTROL, AST_FRAME_VOICE, ast_framehook_detach(), AST_FRAMEHOOK_EVENT_WRITE, ast_sip_push_task, ast_frame::frametype, ast_frame_subclass::integer, NULL, refer_progress_notification_alloc(), refer_progress_notify(), refer_progress_notification::state, and ast_frame::subclass.

Referenced by refer_blind_callback().

◆ refer_progress_framehook_destroy()

static void refer_progress_framehook_destroy ( void *  data)
static

Destroy callback for monitoring framehook.

Definition at line 410 of file res_pjsip_refer.c.

411{
412 struct refer_progress *progress = data;
413 struct refer_progress_notification *notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
414
415 if (notification && ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
416 ao2_cleanup(notification);
417 }
418
419 if (progress->bridge_sub) {
420 progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
421 }
422
424}

References ao2_cleanup, ast_sip_push_task, refer_progress_notification_alloc(), refer_progress_notify(), and stasis_unsubscribe().

Referenced by refer_blind_callback(), and refer_incoming_ari_request().

◆ refer_progress_notification_alloc()

static struct refer_progress_notification * refer_progress_notification_alloc ( struct refer_progress progress,
int  response,
pjsip_evsub_state  state 
)
static

Allocator for REFER Progress notification structure.

Definition at line 149 of file res_pjsip_refer.c.

151{
152 struct refer_progress_notification *notification = ao2_alloc(sizeof(*notification), refer_progress_notification_destroy);
153
154 if (!notification) {
155 return NULL;
156 }
157
158 ao2_ref(progress, +1);
159 notification->progress = progress;
160 notification->response = response;
161 notification->state = state;
162
163 return notification;
164}
static void refer_progress_notification_destroy(void *obj)
Destructor for REFER Progress notification structure.
struct refer_progress * progress
Refer progress structure to send notification on.

References ao2_alloc, ao2_ref, NULL, refer_progress_notification::progress, refer_progress_notification_destroy(), refer_progress_notification::response, and refer_progress_notification::state.

Referenced by refer_ari_progress_framehook(), refer_attended_task(), refer_blind_callback(), refer_incoming_refer_request(), refer_progress_bridge(), refer_progress_framehook(), and refer_progress_framehook_destroy().

◆ refer_progress_notification_destroy()

static void refer_progress_notification_destroy ( void *  obj)
static

Destructor for REFER Progress notification structure.

Definition at line 133 of file res_pjsip_refer.c.

134{
135 struct refer_progress_notification *notification = obj;
136
137 ao2_cleanup(notification->progress);
138}

References ao2_cleanup, and refer_progress_notification::progress.

Referenced by refer_progress_notification_alloc().

◆ refer_progress_notify()

static int refer_progress_notify ( void *  data)
static

Serialized callback for subscription notification.

Locking and serialization:

Although refer_progress_notify() always runs in the progress serializer, the pjproject evsub module itself can cause the subscription to be destroyed which then triggers refer_progress_on_evsub_state() to clean it up. In this case, it's possible that refer_progress_notify() could get the subscription pulled out from under it while it's trying to use it.

At one point we tried to have refer_progress_on_evsub_state() push the cleanup to the serializer and wait for its return before returning to pjproject but since pjproject calls its state callbacks with the dialog locked, this required us to unlock the dialog while waiting for the serialized cleanup, then lock it again before returning to pjproject. There were also still some cases where other callers of refer_progress_notify() weren't using the serializer and crashes were resulting.

Although all callers of refer_progress_notify() now use the progress serializer, we decided to simplify the locking so we didn't have to unlock and relock the dialog in refer_progress_on_evsub_state().

Now, refer_progress_notify() holds the dialog lock for all its work rather than just when calling pjsip_evsub_set_mod_data() to clear the module data. Since pjproject also holds the dialog lock while calling refer_progress_on_evsub_state(), there should be no more chances for the subscription to be cleaned up while still being used to send NOTIFYs.

Definition at line 195 of file res_pjsip_refer.c.

196{
197 RAII_VAR(struct refer_progress_notification *, notification, data, ao2_cleanup);
198 pjsip_evsub *sub;
199 pjsip_tx_data *tdata;
200
201 pjsip_dlg_inc_lock(notification->progress->dlg);
202
203 /* If the subscription has already been terminated we can't send a notification */
204 if (!(sub = notification->progress->sub)) {
205 ast_debug(3, "Not sending NOTIFY of response '%d' and state '%u' on progress monitor '%p' as subscription has been terminated\n",
206 notification->response, notification->state, notification->progress);
207 pjsip_dlg_dec_lock(notification->progress->dlg);
208 return 0;
209 }
210
211 /* Send a deferred initial 100 Trying SIP frag NOTIFY if we haven't already. */
212 if (!notification->progress->sent_100) {
213 notification->progress->sent_100 = 1;
214 if (notification->response != 100) {
215 ast_debug(3, "Sending initial 100 Trying NOTIFY for progress monitor '%p'\n",
216 notification->progress);
217 if (pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, 100, NULL, &tdata) == PJ_SUCCESS) {
218 pjsip_xfer_send_request(sub, tdata);
219 }
220 }
221 }
222
223 ast_debug(3, "Sending NOTIFY with response '%d' and state '%u' on subscription '%p' and progress monitor '%p'\n",
224 notification->response, notification->state, sub, notification->progress);
225
226 /* Actually send the notification */
227 if (pjsip_xfer_notify(sub, notification->state, notification->response, NULL, &tdata) == PJ_SUCCESS) {
228 pjsip_xfer_send_request(sub, tdata);
229 }
230
231
232 if (notification->progress->ari_state) {
233 struct transfer_ari_state *ari_state = notification->progress->ari_state;
234 ari_notify(ari_state);
235 }
236
237 pjsip_dlg_dec_lock(notification->progress->dlg);
238
239 return 0;
240}

References ao2_cleanup, ari_notify(), ast_debug, NULL, RAII_VAR, and sub.

Referenced by refer_ari_progress_framehook(), refer_attended_task(), refer_blind_callback(), refer_incoming_refer_request(), refer_progress_bridge(), refer_progress_framehook(), and refer_progress_framehook_destroy().

◆ refer_progress_on_evsub_state()

static void refer_progress_on_evsub_state ( pjsip_evsub *  sub,
pjsip_event *  event 
)
static

Callback for REFER subscription state changes.

See also
refer_progress_notify

The documentation attached to refer_progress_notify has more information about the locking issues with cleaning up the subscription.

Note
pjproject holds the dialog lock while calling this function.

Definition at line 436 of file res_pjsip_refer.c.

437{
438 struct refer_progress *progress = pjsip_evsub_get_mod_data(sub, refer_progress_module.id);
439
440 /*
441 * If being destroyed, remove the progress object from the subscription
442 * and release the reference it had.
443 */
444 if (progress && (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)) {
445 pjsip_evsub_set_mod_data(progress->sub, refer_progress_module.id, NULL);
446 progress->sub = NULL;
448 }
449}

References ao2_cleanup, NULL, refer_progress_module, and sub.

◆ refer_send()

static int refer_send ( void *  data)
static

Definition at line 1329 of file res_pjsip_refer.c.

1330{
1331 struct refer_data *rdata = data; /* The caller holds a reference */
1332 pjsip_tx_data *tdata;
1333 pjsip_evsub *sub;
1334 pj_str_t tmp;
1335 char refer_to_str[PJSIP_MAX_URL_SIZE];
1336 char disp_name_escaped[128];
1337 struct refer_out_of_dialog *refer;
1338 struct pjsip_evsub_user xfer_cb;
1339 RAII_VAR(char *, uri, NULL, ast_free);
1340 RAII_VAR(char *, tmp_str, NULL, ast_free);
1341 RAII_VAR(char *, display_name, NULL, ast_free);
1342 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
1343 RAII_VAR(struct ast_sip_endpoint *, refer_to_endpoint, NULL, ao2_cleanup);
1344
1345 endpoint = ast_sip_get_endpoint(rdata->destination, 1, &uri);
1346 if (!endpoint) {
1348 "PJSIP REFER - Could not find endpoint '%s' and no default outbound endpoint configured\n",
1349 rdata->destination);
1350 return -1;
1351 }
1352 ast_debug(3, "Request URI: %s\n", uri);
1353
1354 refer_to_endpoint = ast_sip_get_endpoint(rdata->refer_to, 0, &tmp_str);
1355 if (!tmp_str) {
1356 ast_log(LOG_WARNING, "PJSIP REFER - Refer to not a valid resource identifier or SIP URI\n");
1357 return -1;
1358 }
1359 if (!(refer = ao2_alloc(sizeof(struct refer_out_of_dialog), refer_out_of_dialog_destroy))) {
1360 ast_log(LOG_ERROR, "PJSIP REFER - Could not allocate resources.\n");
1361 return -1;
1362 }
1363 /* The dialog will be terminated in the subscription event callback
1364 * when the subscription has terminated. */
1366 refer->dlg = ast_sip_create_dialog_uac(endpoint, uri, NULL);
1367 if (!refer->dlg) {
1368 ast_log(LOG_WARNING, "PJSIP REFER - Could not create dialog\n");
1369 ao2_cleanup(refer);
1370 return -1;
1371 }
1372 ast_sip_dialog_set_endpoint(refer->dlg, endpoint);
1373
1374 pj_bzero(&xfer_cb, sizeof(xfer_cb));
1375 xfer_cb.on_evsub_state = &refer_client_on_evsub_state;
1376 if (pjsip_xfer_create_uac(refer->dlg, &xfer_cb, &sub) != PJ_SUCCESS) {
1377 ast_log(LOG_WARNING, "PJSIP REFER - Could not create uac\n");
1378 ao2_cleanup(refer);
1379 return -1;
1380 }
1381
1382 display_name = ast_refer_get_var_and_unlink(rdata->refer, "display_name");
1383 if (display_name) {
1384 ast_escape_quoted(display_name, disp_name_escaped, sizeof(disp_name_escaped));
1385 snprintf(refer_to_str, sizeof(refer_to_str), "\"%s\" <%s>", disp_name_escaped, tmp_str);
1386 } else {
1387 snprintf(refer_to_str, sizeof(refer_to_str), "%s", tmp_str);
1388 }
1389
1390 /* refer_out_of_dialog_module requires a reference to dlg
1391 * which will be released in refer_client_on_evsub_state()
1392 * when the implicit REFER subscription terminates */
1393 pjsip_evsub_set_mod_data(sub, refer_out_of_dialog_module.id, refer);
1394 if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, refer_to_str), &tdata) != PJ_SUCCESS) {
1395 ast_log(LOG_WARNING, "PJSIP REFER - Could not create request\n");
1396 goto failure;
1397 }
1398
1399 if (refer_to_endpoint && rdata->to_self) {
1400 pjsip_dlg_add_usage(refer->dlg, &refer_out_of_dialog_module, rdata);
1401 }
1402
1403 ast_sip_update_to_uri(tdata, uri);
1404 ast_sip_update_from(tdata, rdata->from);
1405
1406 /*
1407 * This copies any headers found in the refer's variables to
1408 * tdata.
1409 */
1410 vars_to_headers(rdata->refer, tdata);
1411 ast_debug(1, "Sending REFER to '%s' (via endpoint %s) from '%s'\n",
1412 rdata->destination, ast_sorcery_object_get_id(endpoint), rdata->from);
1413
1414 if (pjsip_xfer_send_request(sub, tdata) == PJ_SUCCESS) {
1415 return 0;
1416 }
1417
1418failure:
1419 ao2_cleanup(refer);
1420 pjsip_evsub_set_mod_data(sub, refer_out_of_dialog_module.id, NULL);
1421 pjsip_evsub_terminate(sub, PJ_FALSE);
1422 return -1;
1423}
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
Set an endpoint on a SIP dialog so in-dialog requests do not undergo endpoint lookup.
char * ast_refer_get_var_and_unlink(struct ast_refer *refer, const char *name)
Get the specified variable on the refer and unlink it from the container of variables.
Definition refer.c:269
struct ast_sip_endpoint * ast_sip_get_endpoint(const char *to, int get_default_outbound, char **uri)
Retrieves an endpoint and URI from the "to" string.
Definition res_pjsip.c:3220
pjsip_dialog * ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint, const char *aor_name, const char *request_user)
General purpose method for creating a UAC dialog with an endpoint.
Definition res_pjsip.c:958
int ast_sip_update_from(pjsip_tx_data *tdata, char *from)
Overwrite fields in the outbound 'From' header.
Definition res_pjsip.c:3354
int ast_sip_update_to_uri(pjsip_tx_data *tdata, const char *to)
Replace the To URI in the tdata with the supplied one.
Definition res_pjsip.c:3294
static void refer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
static enum pjsip_status_code vars_to_headers(const struct ast_refer *refer, pjsip_tx_data *tdata)
static void refer_out_of_dialog_destroy(void *obj)
Destructor for REFER out of dialog structure.
char * ast_escape_quoted(const char *string, char *outbuf, int buflen)
Escape characters found in a quoted string.
Definition utils.c:817

References ao2_alloc, ao2_cleanup, ast_debug, ast_escape_quoted(), ast_free, ast_log, ast_refer_get_var_and_unlink(), ast_sip_create_dialog_uac(), ast_sip_dialog_set_endpoint(), ast_sip_get_endpoint(), ast_sip_update_from(), ast_sip_update_to_uri(), ast_sorcery_object_get_id(), refer_out_of_dialog::authentication_challenge_count, refer_data::destination, refer_out_of_dialog::dlg, refer_data::from, LOG_ERROR, LOG_WARNING, NULL, RAII_VAR, refer_data::refer, refer_client_on_evsub_state(), refer_out_of_dialog_destroy(), refer_out_of_dialog_module, refer_data::refer_to, sub, refer_data::to_self, and vars_to_headers().

Referenced by sip_refer_send().

◆ refer_unreference_dialog()

static int refer_unreference_dialog ( void *  obj)
static

Definition at line 1159 of file res_pjsip_refer.c.

1160{
1161 struct refer_out_of_dialog *data = obj;
1162
1163 /* This is why we keep the dialog on the subscription. When the subscription
1164 * is destroyed, there is no guarantee that the underlying dialog is ready
1165 * to be destroyed. Furthermore, there's no guarantee in the opposite direction
1166 * either. The dialog could be destroyed before our subscription is. We fix
1167 * this problem by keeping a reference to the dialog until it is time to
1168 * destroy the subscription.
1169 */
1170 pjsip_dlg_dec_session(data->dlg, &refer_out_of_dialog_module);
1171 data->dlg = NULL;
1172
1173 return 0;
1174}

References refer_out_of_dialog::dlg, NULL, and refer_out_of_dialog_module.

Referenced by refer_out_of_dialog_destroy().

◆ session_end_if_deferred_task()

static int session_end_if_deferred_task ( void *  data)
static

Definition at line 616 of file res_pjsip_refer.c.

617{
618 struct ast_sip_session *session = data;
619
621 ao2_ref(session, -1);
622 return 0;
623}

References ao2_ref, ast_sip_session_end_if_deferred(), and session.

Referenced by refer_attended_task().

◆ sip_refer_send()

static int sip_refer_send ( const struct ast_refer refer)
static

Definition at line 1425 of file res_pjsip_refer.c.

1426{
1427 struct refer_data *rdata;
1428 int res;
1429
1431 ast_log(LOG_ERROR, "SIP REFER - a 'To' URI must be specified\n");
1432 return -1;
1433 }
1434
1435 rdata = refer_data_create(refer);
1436 if (!rdata) {
1437 return -1;
1438 }
1439
1441 ao2_ref(rdata, -1);
1442
1443 return res;
1444}
static struct refer_data * refer_data_create(const struct ast_refer *refer)
static int refer_send(void *data)

References ao2_ref, ast_log, ast_refer_get_to(), ast_sip_push_task_wait_serializer, ast_strlen_zero(), LOG_ERROR, refer_data::refer, refer_data_create(), refer_send(), and refer_serializer.

◆ transfer_ari_state_destroy()

static void transfer_ari_state_destroy ( void *  obj)
static

Destructor of the state used for the ARI transfer.

Definition at line 109 of file res_pjsip_refer.c.

110{
111 struct transfer_ari_state *state = obj;
112
113 ao2_cleanup(state->transferer);
114 ao2_cleanup(state->other_session);
115 ast_channel_cleanup(state->transferer_chan);
116 ast_free(state->referred_by);
117 ast_free(state->protocol_id);
118 ao2_cleanup(state->params);
119}

References ao2_cleanup, ast_channel_cleanup, and ast_free.

Referenced by refer_incoming_ari_request().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2120 of file res_pjsip_refer.c.

2121{
2126
2127 return 0;
2128}
void ast_sip_unregister_service(pjsip_module *module)
Definition res_pjsip.c:127
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.

References ast_sip_session_unregister_supplement(), ast_sip_unregister_service(), ast_taskprocessor_unreference(), refer_out_of_dialog_module, refer_progress_module, refer_serializer, and refer_supplement.

◆ vars_to_headers()

static enum pjsip_status_code vars_to_headers ( const struct ast_refer refer,
pjsip_tx_data *  tdata 
)
static

Definition at line 1076 of file res_pjsip_refer.c.

1077{
1078 const char *name;
1079 const char *value;
1081
1082 for (iter = ast_refer_var_iterator_init(refer);
1085 if (!is_refer_var_blocked(name)) {
1086 ast_sip_add_header(tdata, name, value);
1087 }
1088 }
1090
1091 return PJSIP_SC_OK;
1092}
void ast_refer_var_unref_current(struct ast_refer_var_iterator *iter)
Unref a refer var from inside an iterator loop.
Definition refer.c:372
int ast_refer_var_iterator_next(struct ast_refer_var_iterator *iter, const char **name, const char **value)
Get the next variable name and value.
Definition refer.c:351
struct ast_refer_var_iterator * ast_refer_var_iterator_init(const struct ast_refer *refer)
Create a new refer variable iterator.
Definition refer.c:337
void ast_refer_var_iterator_destroy(struct ast_refer_var_iterator *iter)
Destroy a refer variable iterator.
Definition refer.c:378
static int is_refer_var_blocked(const char *name)
struct ao2_iterator iter
Definition refer.c:333
int value
Definition syslog.c:37

References ast_refer_var_iterator_destroy(), ast_refer_var_iterator_init(), ast_refer_var_iterator_next(), ast_refer_var_unref_current(), ast_sip_add_header(), is_refer_var_blocked(), ast_refer_var_iterator::iter, name, and value.

Referenced by refer_send().

◆ xfer_response_code2sip()

static int xfer_response_code2sip ( enum ast_transfer_result  xfer_code)
static

Definition at line 644 of file res_pjsip_refer.c.

645{
646 int response;
647
648 response = 503;
649 switch (xfer_code) {
651 response = 400;
652 break;
654 response = 403;
655 break;
657 response = 500;
658 break;
660 response = 200;
661 break;
662 }
663 return response;
664}
@ AST_BRIDGE_TRANSFER_NOT_PERMITTED
Definition bridge.h:1106
@ AST_BRIDGE_TRANSFER_SUCCESS
Definition bridge.h:1104
@ AST_BRIDGE_TRANSFER_INVALID
Definition bridge.h:1108
@ AST_BRIDGE_TRANSFER_FAIL
Definition bridge.h:1110

References AST_BRIDGE_TRANSFER_FAIL, AST_BRIDGE_TRANSFER_INVALID, AST_BRIDGE_TRANSFER_NOT_PERMITTED, and AST_BRIDGE_TRANSFER_SUCCESS.

Referenced by refer_attended_task(), refer_incoming_attended_request(), and refer_incoming_blind_request().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP Blind and Attended Transfer Support" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .requires = "res_pjsip,res_pjsip_session,res_pjsip_pubsub", }
static

Definition at line 2136 of file res_pjsip_refer.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2136 of file res_pjsip_refer.c.

◆ refer_out_of_dialog_module

pjsip_module refer_out_of_dialog_module
static

REFER Out-of-dialog module, used to attach session data structure to subscription.

Definition at line 1100 of file res_pjsip_refer.c.

1100 {
1101 .name = { "REFER Out-of-dialog Module", 26 },
1102 .id = -1,
1103 .on_tx_request = refer_on_tx_request,
1104 /* Ensure that we are called after res_pjsp_nat module and before transport priority */
1105 .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 4,
1106};
static pj_status_t refer_on_tx_request(pjsip_tx_data *tdata)

Referenced by load_module(), refer_client_on_evsub_state(), refer_on_tx_request(), refer_send(), refer_unreference_dialog(), and unload_module().

◆ refer_progress_evsub_cb

pjsip_evsub_user refer_progress_evsub_cb
static
Initial value:
= {
.on_evsub_state = refer_progress_on_evsub_state,
}
static void refer_progress_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
Callback for REFER subscription state changes.

Callback structure for subscription.

Definition at line 452 of file res_pjsip_refer.c.

452 {
453 .on_evsub_state = refer_progress_on_evsub_state,
454};

Referenced by refer_progress_alloc().

◆ refer_progress_module

pjsip_module refer_progress_module
static
Initial value:
= {
.name = { "REFER Progress", 14 },
.id = -1,
}

REFER Progress module, used to attach REFER progress structure to subscriptions.

Definition at line 103 of file res_pjsip_refer.c.

103 {
104 .name = { "REFER Progress", 14 },
105 .id = -1,
106};

Referenced by dlg_releaser_task(), load_module(), refer_progress_alloc(), refer_progress_destroy(), refer_progress_on_evsub_state(), and unload_module().

◆ refer_serializer

struct ast_taskprocessor* refer_serializer
static

◆ refer_supplement

struct ast_sip_session_supplement refer_supplement
static
Initial value:
= {
.incoming_request = refer_incoming_request,
.outgoing_request = refer_outgoing_request,
}
@ AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL
Definition res_pjsip.h:3364
static void refer_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
static int refer_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)

Definition at line 2084 of file res_pjsip_refer.c.

2084 {
2086 .incoming_request = refer_incoming_request,
2087 .outgoing_request = refer_outgoing_request,
2088};

Referenced by load_module(), and unload_module().

◆ refer_tech

const struct ast_refer_tech refer_tech
static
Initial value:
= {
.name = "pjsip",
.refer_send = sip_refer_send,
}
static int sip_refer_send(const struct ast_refer *refer)

Definition at line 1446 of file res_pjsip_refer.c.

1446 {
1447 .name = "pjsip",
1448 .refer_send = sip_refer_send,
1449};

Referenced by ast_refer_send(), and load_module().