Asterisk - The Open Source Telephony Project GIT-master-266378a
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_module * AST_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_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-only mode.
 
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.
 
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_data * refer_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_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_framehook_destroy (void *data)
 Destroy callback for monitoring framehook.
 
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 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_info * ast_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_taskprocessor * refer_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 1027 of file res_pjsip_refer.c.

1028 { \
1029 ast_channel_lock((session)->channel); \
1030 context = pbx_builtin_getvar_helper((session)->channel, "TRANSFER_CONTEXT"); \
1031 if (ast_strlen_zero(context)) { \
1032 context = (session)->endpoint->context; \
1033 } else { \
1034 context = ast_strdupa(context); \
1035 } \
1036 ast_channel_unlock((session)->channel); \
1037 } while (0) \
1038
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 2228 of file res_pjsip_refer.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 2228 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 2143 of file res_pjsip_refer.c.

2144{
2145 const char *var_value;
2146 pj_str_t pj_header_name;
2147 pjsip_hdr *header;
2148
2149 var_value = pbx_builtin_getvar_helper(chan, var_name);
2150 if (ast_strlen_zero(var_value)) {
2151 return;
2152 }
2153
2154 pj_cstr(&pj_header_name, header_name);
2155 header = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_header_name, NULL);
2156 if (header) {
2157 return;
2158 }
2159 ast_sip_add_header(tdata, header_name, var_value);
2160}
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:2051
#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 1543 of file res_pjsip_refer.c.

1544{
1545 struct ast_refer_param param;
1546
1547 param.param_name = ast_strdup(key);
1548 if (!param.param_name) {
1549 return 0;
1550 }
1551
1552 param.param_value = copy_string(str);
1553 if (!param.param_value) {
1554 ast_free((char *) param.param_name);
1555 return 0;
1556 }
1557
1558 if (AST_VECTOR_APPEND(params, param) != 0) {
1559 ast_free((char *) param.param_name);
1560 ast_free((char *) param.param_value);
1561 return 0;
1562 }
1563 return 1;
1564}
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 148 of file res_pjsip_refer.c.

149{
150 int res = 0;
151 SCOPE_ENTER(3, "%s: state: %s\n", ast_channel_name(state->transferer_chan),
152 ast_channel_transfer_state2str(state->last_response));
153
154 res = ast_refer_notify_transfer_request(state->transferer_chan, state->referred_by,
155 state->exten, state->protocol_id,
156 state->other_session ? state->other_session->channel : NULL,
157 state->params, state->last_response);
158 SCOPE_EXIT_RTN_VALUE(res, "%s: done: %d\n", ast_channel_name(state->transferer_chan), res);
159}
const char * ast_channel_name(const struct ast_channel *chan)
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_ENTER(level,...)
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
const char * ast_channel_transfer_state2str(enum ast_control_transfer state)

References ast_channel_name(), ast_channel_transfer_state2str(), ast_refer_notify_transfer_request(), NULL, SCOPE_ENTER, and SCOPE_EXIT_RTN_VALUE.

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 2228 of file res_pjsip_refer.c.

◆ copy_string()

static char * copy_string ( struct pj_str_t *  str)
static

Definition at line 1532 of file res_pjsip_refer.c.

1533{
1534 int len = pj_strlen(str) + 1;
1535 char *dst = ast_malloc(len);
1536 if (!dst) {
1537 return NULL;
1538 }
1539 ast_copy_pj_str(dst, str, len);
1540 return dst;
1541}
#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:2221

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 696 of file res_pjsip_refer.c.

697{
698 struct ast_sip_session *session = data;
699
702 ao2_ref(session, -1);
703 return 0;
704}
#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 515 of file res_pjsip_refer.c.

515 {
516 pjsip_dlg_dec_session((pjsip_dialog *)data, &refer_progress_module);
517 return 0;
518}
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 1190 of file res_pjsip_refer.c.

1191{
1192 const pj_str_t REFER_TO = { "Refer-To", 8 };
1193 pjsip_generic_string_hdr *refer_to;
1194 pjsip_uri *parsed_uri;
1195
1196 if (!(refer_to = pjsip_msg_find_hdr_by_name(tdata->msg, &REFER_TO, NULL))
1197 || !(parsed_uri = pjsip_parse_uri(tdata->pool, refer_to->hvalue.ptr, refer_to->hvalue.slen, 0))
1198 || (!PJSIP_URI_SCHEME_IS_SIP(parsed_uri) && !PJSIP_URI_SCHEME_IS_SIPS(parsed_uri))) {
1199 return NULL;
1200 }
1201
1202 return parsed_uri;
1203}

References NULL.

Referenced by refer_on_tx_request().

◆ invite_replaces()

static int invite_replaces ( void *  data)
static

Task for invite replaces.

Definition at line 1858 of file res_pjsip_refer.c.

1859{
1860 struct invite_replaces *invite = data;
1861
1862 if (!invite->session->channel) {
1863 return -1;
1864 }
1865
1867 invite->channel = invite->session->channel;
1868
1870 return 0;
1871}
struct ast_bridge * ast_bridge_transfer_acquire_bridge(struct ast_channel *chan)
Acquire the channel's bridge for transfer purposes.
Definition bridge.c:4617
#define ast_channel_ref(c)
Increase channel reference count.
Definition channel.h:3008
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 1122 of file res_pjsip_refer.c.

1123{
1124 int i;
1125
1126 /* Don't block the Max-Forwards header because the user can override it */
1127 static const char *hdr[] = {
1128 "To",
1129 "From",
1130 "Via",
1131 "Route",
1132 "Contact",
1133 "Call-ID",
1134 "CSeq",
1135 "Allow",
1136 "Content-Length",
1137 "Content-Type",
1138 "Request-URI",
1139 };
1140
1141 for (i = 0; i < ARRAY_LEN(hdr); ++i) {
1142 if (!strcasecmp(name, hdr[i])) {
1143 /* Block addition of this header. */
1144 return 1;
1145 }
1146 }
1147 return 0;
1148}
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 2182 of file res_pjsip_refer.c.

2183{
2184 const pj_str_t str_norefersub = { "norefersub", 10 };
2185
2186 pjsip_replaces_init_module(ast_sip_get_pjsip_endpoint());
2187 pjsip_xfer_init_module(ast_sip_get_pjsip_endpoint());
2188
2189 if (ast_sip_get_norefersub()) {
2190 pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_norefersub);
2191 }
2192
2195 }
2196
2198 if (!refer_serializer) {
2201 }
2202
2206
2208
2210}
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition res_pjsip.c:2137
#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:518
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 391 of file res_pjsip_refer.c.

392{
393 struct refer_progress *progress = data;
394 struct refer_progress_notification *notification = NULL;
395 SCOPE_ENTER(3, "%s\n", ast_channel_name(chan));
396
397 /* We only care about frames *to* the channel */
398 if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
399 SCOPE_EXIT_RTN_VALUE(f, "%s: Not EVENT_WRITE\n", ast_channel_name(chan));
400 }
401
402 /* Determine the state of the REFER based on the control frames (or voice frames) passing */
405 && f->datalen >= sizeof(enum ast_control_transfer)) {
407 ast_trace(-1, "%s message: %d\n", ast_channel_name(chan), *message);
408 switch (*message) {
410 notification = refer_progress_notification_alloc(progress, 603, PJSIP_EVSUB_STATE_TERMINATED);
411 break;
413 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
414 break;
416 notification = refer_progress_notification_alloc(progress, 100, PJSIP_EVSUB_STATE_ACTIVE);
417 break;
419 notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
420 break;
422 break;
423 }
424 progress->ari_state->last_response = *message;
425 }
426
427 /* If a notification is due to be sent push it to the taskpool */
428 if (notification) {
429 /* If the subscription is being terminated we don't need the frame hook any longer */
430 if (notification->state == PJSIP_EVSUB_STATE_TERMINATED) {
431 ast_trace(-1, "Detaching REFER progress monitoring hook from '%s' as subscription is being terminated\n",
432 ast_channel_name(chan));
433 ast_framehook_detach(chan, progress->framehook);
434 }
435
436 /*
437 * If the referer specified "Refer-Sub: false", we can call refer_progress_notify()
438 * directly without pushing a task because it won't be doing any pjproject wotk.
439 * All it will do is send an ARI event.
440 */
441 if (notification->progress->no_refer_sub) {
442 refer_progress_notify(notification);
443 SCOPE_EXIT_RTN_VALUE(f, "%s: done ari no refer sub\n",
444 progress->ari_state ? ast_channel_name(progress->ari_state->transferer_chan) : "????");
445 }
446
447 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
448 ao2_cleanup(notification);
449 }
450 } else {
451 SCOPE_EXIT_RTN_VALUE(f, "%s: Not a transfer event\n", ast_channel_name(chan));
452 }
453
454 SCOPE_EXIT_RTN_VALUE(f, "%s: done\n", ast_channel_name(chan));
455}
#define ao2_cleanup(obj)
Definition astobj2.h:1934
@ 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_trace(level,...)
#define ast_sip_push_task(serializer, sip_task, task_data)
Definition res_pjsip.h:2126
@ AST_TRANSFER_FAILED
@ AST_TRANSFER_UNAVAILABLE
@ AST_TRANSFER_SUCCESS
@ AST_TRANSFER_PROGRESS
@ AST_TRANSFER_INVALID
@ AST_FRAME_CONTROL
@ AST_CONTROL_TRANSFER
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::@237 data
REFER Progress notification structure.
pjsip_evsub_state state
Subscription state.
struct refer_progress * progress
Refer progress structure to send notification on.
REFER Progress structure.
int no_refer_sub
Refer-Sub: false was specified on the REFER.

References ao2_cleanup, ast_channel_name(), AST_CONTROL_TRANSFER, AST_FRAME_CONTROL, ast_framehook_detach(), AST_FRAMEHOOK_EVENT_WRITE, ast_sip_push_task, ast_trace, 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, refer_progress::no_refer_sub, NULL, refer_progress_notification::progress, ast_frame::ptr, refer_progress_notification_alloc(), refer_progress_notify(), SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, 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 660 of file res_pjsip_refer.c.

663{
664 struct refer_attended *attended;
665
666 attended = ao2_alloc_options(sizeof(*attended), refer_attended_destroy,
668 if (!attended) {
669 return NULL;
670 }
671
672 ao2_ref(transferer, +1);
673 attended->transferer = transferer;
678
679 if (progress) {
680 ao2_ref(progress, +1);
681 attended->progress = progress;
682 }
683
684 return attended;
685}
@ 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 649 of file res_pjsip_refer.c.

650{
651 struct refer_attended *attended = obj;
652
653 ao2_cleanup(attended->transferer);
656 ao2_cleanup(attended->progress);
657}
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition channel.h:3030

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 738 of file res_pjsip_refer.c.

739{
740 struct refer_attended *attended = data;
741 int response;
742 int (*task_cb)(void *data);
743
744 if (attended->transferer_second->channel) {
745 ast_debug(3, "Performing a REFER attended transfer - Transferer #1: %s Transferer #2: %s\n",
748
750 attended->transferer_chan,
751 attended->transferer_second->channel));
752
753 ast_debug(3, "Final response for REFER attended transfer - Transferer #1: %s Transferer #2: %s is '%d'\n",
756 response);
757 } else {
758 ast_debug(3, "Received REFER request on channel '%s' but other channel has gone.\n",
760 response = 603;
761 }
762
763 if (attended->progress) {
764 struct refer_progress_notification *notification;
765
766 notification = refer_progress_notification_alloc(attended->progress, response,
767 PJSIP_EVSUB_STATE_TERMINATED);
768 if (notification) {
769 if (ast_sip_push_task(attended->progress->serializer, refer_progress_notify, notification)) {
770 ao2_cleanup(notification);
771 }
772 }
773 }
774
775 if (response == 200) {
777 } else {
779 }
781 task_cb, attended->transferer)) {
782 /* Gave the ref to the pushed task. */
783 attended->transferer = NULL;
784 } else {
785 /* Do this anyway even though it is the wrong serializer. */
787 }
788
789 ao2_ref(attended, -1);
790 return 0;
791}
enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee, struct ast_channel *to_transfer_target)
Attended transfer.
Definition bridge.c:4886
#define ast_debug(level,...)
Log a DEBUG message.
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 810 of file res_pjsip_refer.c.

812{
813 struct refer_blind *refer = user_data_wrapper->data;
814 pjsip_generic_string_hdr *referred_by;
815
816 static const pj_str_t str_referred_by = { "Referred-By", 11 };
817 static const pj_str_t str_referred_by_s = { "b", 1 };
818 const char *get_xfrdata;
819
820 pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes");
821
822 if (refer->progress && !refer->attended && !refer->progress->refer_blind_progress) {
823 /* If blind transfer and endpoint doesn't want to receive all the progress details */
825 PJSIP_EVSUB_STATE_TERMINATED);
826
827 if (notification) {
828 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
829 ao2_cleanup(notification);
830 }
831 }
832 } else if (refer->progress) {
833 /* If attended transfer and progress monitoring is being done attach a frame hook so we can monitor it */
834 struct ast_framehook_interface hook = {
836 .event_cb = refer_progress_framehook,
838 .data = refer->progress,
839 .disable_inheritance = 1,
840 };
841 int subscribed;
842
844 if (!refer->progress->transferee) {
846 PJSIP_EVSUB_STATE_TERMINATED);
847
848 ast_log(LOG_WARNING, "Could not copy channel name '%s' during transfer - assuming success\n",
849 ast_channel_name(chan));
850
851 if (notification) {
852 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
853 ao2_cleanup(notification);
854 }
855 }
856 }
857
858 /* Progress needs a reference to the transfer_channel_data so that it can track the completed status of the transfer */
859 ao2_ref(user_data_wrapper, +1);
860 refer->progress->transfer_data = user_data_wrapper;
861
862 /* We need to bump the reference count up on the progress structure since it is in the frame hook now */
863 ao2_ref(refer->progress, +1);
864
865 /* If we can't attach a frame hook for whatever reason send a notification of success immediately */
866 ast_channel_lock(chan);
867 refer->progress->framehook = ast_framehook_attach(chan, &hook);
868 ast_channel_unlock(chan);
869 if (refer->progress->framehook < 0) {
871 PJSIP_EVSUB_STATE_TERMINATED);
872
873 ast_log(LOG_WARNING, "Could not attach REFER transfer progress monitoring hook to channel '%s' - assuming success\n",
874 ast_channel_name(chan));
875
876 if (notification) {
877 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
878 ao2_cleanup(notification);
879 }
880 }
881
882 ao2_cleanup(refer->progress);
883 }
884
885 /* We need to bump the reference count for the stasis subscription */
886 ao2_ref(refer->progress, +1);
887 /* We also will need to detect if the transferee enters a bridge. This is currently the only reliable way to
888 * detect if the transfer target has answered the call
889 *
890 * The subscription is live as soon as it is created and another thread may unsubscribe
891 * it while the filters below are applied, so hold the progress lock across both. The
892 * unsubscribe sites take the same lock.
893 */
894 ao2_lock(refer->progress);
896 subscribed = refer->progress->bridge_sub != NULL;
897 if (subscribed) {
901 }
902 ao2_unlock(refer->progress);
903
904 if (!subscribed) {
906 PJSIP_EVSUB_STATE_TERMINATED);
907
908 ast_log(LOG_WARNING, "Could not create bridge stasis subscription for monitoring progress on transfer of channel '%s' - assuming success\n",
909 ast_channel_name(chan));
910
911 if (notification) {
912 if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
913 ao2_cleanup(notification);
914 }
915 }
916
917 ast_channel_lock(chan);
919 ast_channel_unlock(chan);
920
921 ao2_cleanup(refer->progress);
922 }
923 }
924
925 pbx_builtin_setvar_helper(chan, "SIPREFERRINGCONTEXT", S_OR(refer->context, NULL));
926
927 ast_channel_lock(chan);
928 if ((get_xfrdata = pbx_builtin_getvar_helper(chan, "GET_TRANSFERRER_DATA"))) {
929 get_xfrdata = ast_strdupa(get_xfrdata);
930 }
931 ast_channel_unlock(chan);
932 if (!ast_strlen_zero(get_xfrdata)) {
933 const pjsip_msg * msg = refer->rdata->msg_info.msg;
934 const struct pjsip_hdr *end = &msg->hdr;
935 struct pjsip_hdr *hdr = end->next;
936 struct ast_str *pbxvar = ast_str_create(64); /* initial buffer for variable name, extended on demand */
937 char buf[4096]; /* should be enough for one header value */
938 const char *prefix = get_xfrdata;
939
940 /* The '*' alone matches all headers. */
941 if (!strcmp(prefix, "*")) {
942 prefix = "";
943 }
944 if (pbxvar) {
945 for (; hdr != end; hdr = hdr->next) {
946 if (!pj_strnicmp2(&hdr->name, prefix, strlen(prefix))) {
947 const int hdr_name_strlen = pj_strlen(&hdr->name);
948 const int hdr_name_bytes = hdr_name_strlen + 1; /* +1 for string NULL terminator */
949 char hdr_name[hdr_name_bytes];
950 int len;
951 char *value_str;
952
953 ast_copy_pj_str(hdr_name, &hdr->name, hdr_name_bytes);
954 len = pjsip_hdr_print_on(hdr, buf, sizeof(buf) - 1);
955 if (len < 0) {
956 /* ignore too long headers */
957 ast_log(LOG_WARNING, "Could not store header '%s' from transfer on channel '%s' - too long text\n", hdr_name, ast_channel_name(chan));
958 continue;
959 }
960 buf[len] = '\0';
961
962 /* Get value - remove header name (before ':') from buf and trim blanks. */
963 value_str = strchr(buf, ':');
964 if (!value_str) {
965 /* Ignore header without value */
966 continue;
967 }
968 value_str = ast_strip(value_str + 1); /* +1 to get string right after the ':' delimiter (i.e. header value) */
969
970 ast_str_set(&pbxvar, -1, "~HASH~TRANSFER_DATA~%.*s~", hdr_name_strlen, hdr_name);
971 pbx_builtin_setvar_helper(chan, ast_str_buffer(pbxvar), value_str);
972 ast_debug(5, "On channel '%s' set TRANSFER_DATA variable '%s' to value '%s' \n", ast_channel_name(chan), hdr_name, value_str);
973 }
974 }
975 ast_free(pbxvar);
976 } else {
977 ast_log(LOG_ERROR, "Channel '%s' failed to allocate buffer for TRANSFER_DATA variable\n", ast_channel_name(chan));
978 }
979 }
980
981 referred_by = pjsip_msg_find_hdr_by_names(refer->rdata->msg_info.msg,
982 &str_referred_by, &str_referred_by_s, NULL);
983 if (referred_by) {
984 size_t uri_size = pj_strlen(&referred_by->hvalue) + 1;
985 char *uri = ast_alloca(uri_size);
986
987 ast_copy_pj_str(uri, &referred_by->hvalue, uri_size);
988 pbx_builtin_setvar_helper(chan, "__SIPREFERREDBYHDR", S_OR(uri, NULL));
989 } else {
990 pbx_builtin_setvar_helper(chan, "SIPREFERREDBYHDR", NULL);
991 }
992
993 if (refer->replaces) {
994 char replaces[512];
995 char *replaces_val = NULL;
996 int len;
997
998 len = pjsip_hdr_print_on(refer->replaces, replaces, sizeof(replaces) - 1);
999 if (len != -1) {
1000 /* pjsip_hdr_print_on does not NULL terminate the buffer */
1001 replaces[len] = '\0';
1002 replaces_val = replaces + sizeof("Replaces:");
1003 }
1004 pbx_builtin_setvar_helper(chan, "__SIPREPLACESHDR", replaces_val);
1005 } else {
1006 pbx_builtin_setvar_helper(chan, "SIPREPLACESHDR", NULL);
1007 }
1008
1009 if (refer->refer_to) {
1010 char refer_to[PJSIP_MAX_URL_SIZE];
1011
1012 pjsip_uri_print(PJSIP_URI_IN_REQ_URI, refer->refer_to, refer_to, sizeof(refer_to));
1013 pbx_builtin_setvar_helper(chan, "SIPREFERTOHDR", S_OR(refer_to, NULL));
1014 } else {
1015 pbx_builtin_setvar_helper(chan, "SIPREFERTOHDR", NULL);
1016 }
1017}
#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 ao2_unlock(a)
Definition astobj2.h:729
#define ao2_lock(a)
Definition astobj2.h:717
#define ast_channel_lock(chan)
Definition channel.h:2983
const char * ast_channel_uniqueid(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition channel.h:2984
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
static int subscribed
Definition manager.c:155
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:145
#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:1101
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:1155
#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_lock, ao2_ref, ao2_unlock, 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(), subscribed, 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 1276 of file res_pjsip_refer.c.

1277{
1278 pjsip_tx_data *tdata;
1279 RAII_VAR(struct ast_sip_endpoint *, endpt, NULL, ao2_cleanup);
1281 int refer_success;
1282 int res = 0;
1283
1284 if (!event) {
1285 return;
1286 }
1287
1288 refer_data = pjsip_evsub_get_mod_data(sub, refer_out_of_dialog_module.id);
1289 if (!refer_data || !refer_data->dlg) {
1290 return;
1291 }
1292
1294
1295 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
1296 /* Check if subscription is suppressed and terminate and send completion code, if so. */
1297 pjsip_rx_data *rdata;
1298 pjsip_generic_string_hdr *refer_sub;
1299 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
1300
1301 ast_debug(3, "Refer accepted by %s\n", endpt ? ast_sorcery_object_get_id(endpt) : "(unknown endpoint)");
1302
1303 /* Check if response message */
1304 if (event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1305 rdata = event->body.tsx_state.src.rdata;
1306
1307 /* Find Refer-Sub header */
1308 refer_sub = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &REFER_SUB, NULL);
1309
1310 /* Check if subscription is suppressed. If it is, the far end will not terminate it,
1311 * and the subscription will remain active until it times out. Terminating it here
1312 * eliminates the unnecessary timeout.
1313 */
1314 if (refer_sub && !pj_stricmp2(&refer_sub->hvalue, "false")) {
1315 /* Since no subscription is desired, assume that call has been referred successfully
1316 * and terminate subscription.
1317 */
1318 pjsip_evsub_set_mod_data(sub, refer_out_of_dialog_module.id, NULL);
1319 pjsip_evsub_terminate(sub, PJ_TRUE);
1320 res = -1;
1321 }
1322 }
1323 } else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
1324 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
1325 /* Check for NOTIFY complete or error. */
1326 pjsip_msg *msg;
1327 pjsip_msg_body *body;
1328 pjsip_status_line status_line = { .code = 0 };
1329 pj_bool_t is_last;
1330 pj_status_t status;
1331
1332 if (event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1333 pjsip_rx_data *rdata;
1334 pj_str_t refer_str;
1335 pj_cstr(&refer_str, "REFER");
1336
1337 rdata = event->body.tsx_state.src.rdata;
1338 msg = rdata->msg_info.msg;
1339
1340 if (msg->type == PJSIP_RESPONSE_MSG
1341 && (event->body.tsx_state.tsx->status_code == 401
1342 || event->body.tsx_state.tsx->status_code == 407)
1343 && pj_stristr(&refer_str, &event->body.tsx_state.tsx->method.name)
1344 && ++refer_data->authentication_challenge_count < MAX_RX_CHALLENGES
1345 && endpt) {
1346
1347 if (!ast_sip_create_request_with_auth(&endpt->outbound_auths,
1348 event->body.tsx_state.src.rdata, event->body.tsx_state.tsx->last_tx, &tdata)) {
1349 /* Send authed REFER */
1351 goto out;
1352 }
1353 }
1354
1355 if (msg->type == PJSIP_REQUEST_MSG) {
1356 if (!pjsip_method_cmp(&msg->line.req.method, pjsip_get_notify_method())) {
1357 body = msg->body;
1358 if (body && !pj_stricmp2(&body->content_type.type, "message")
1359 && !pj_stricmp2(&body->content_type.subtype, "sipfrag")) {
1360 pjsip_parse_status_line((char *)body->data, body->len, &status_line);
1361 }
1362 }
1363 } else {
1364 status_line.code = msg->line.status.code;
1365 status_line.reason = msg->line.status.reason;
1366 }
1367 } else {
1368 status_line.code = 500;
1369 status_line.reason = *pjsip_get_status_text(500);
1370 }
1371
1372 is_last = (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED);
1373 /* If the status code is >= 200, the subscription is finished. */
1374 if (status_line.code >= 200 || is_last) {
1375 res = -1;
1376
1377 refer_success = status_line.code >= 200 && status_line.code < 300;
1378
1379 /* If subscription not terminated and subscription is finished (status code >= 200)
1380 * terminate it */
1381 if (!is_last) {
1382 pjsip_tx_data *tdata;
1383
1384 status = pjsip_evsub_initiate(sub, pjsip_get_subscribe_method(), 0, &tdata);
1385 if (status == PJ_SUCCESS) {
1386 pjsip_evsub_send_request(sub, tdata);
1387 }
1388 }
1389 ast_debug(3, "Refer completed: %d %.*s (%s)\n",
1390 status_line.code,
1391 (int)status_line.reason.slen, status_line.reason.ptr,
1392 refer_success ? "Success" : "Failure");
1393 }
1394 }
1395
1396out:
1397 if (res) {
1399 }
1400}
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:2022
#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:1067
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 1058 of file res_pjsip_refer.c.

1059{
1060 char *uri_params;
1061 const char *destination;
1063
1064 if (!rdata) {
1065 return NULL;
1066 }
1067
1068 /* typecast to suppress const warning */
1069 rdata->refer = ast_refer_ref((struct ast_refer *) refer);
1071
1072 /* To starts with 'pjsip:' which needs to be removed. */
1073 if (!(destination = strchr(destination, ':'))) {
1074 goto failure;
1075 }
1076 ++destination;/* Now skip the ':' */
1077
1079 if (!rdata->destination) {
1080 goto failure;
1081 }
1082
1084 if (!rdata->from) {
1085 goto failure;
1086 }
1087
1089 if (!rdata->refer_to) {
1090 goto failure;
1091 }
1093
1094 /*
1095 * Sometimes from URI can contain URI parameters, so remove them.
1096 *
1097 * sip:user;user-options@domain;uri-parameters
1098 */
1099 uri_params = strchr(rdata->from, '@');
1100 if (uri_params && (uri_params = strchr(uri_params, ';'))) {
1101 *uri_params = '\0';
1102 }
1103 return rdata;
1104
1105failure:
1106 ao2_cleanup(rdata);
1107 return NULL;
1108}
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 1047 of file res_pjsip_refer.c.

1048{
1049 struct refer_data *rdata = obj;
1050
1051 ast_free(rdata->destination);
1052 ast_free(rdata->from);
1053 ast_free(rdata->refer_to);
1054
1055 ast_refer_destroy(rdata->refer);
1056}
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 1567 of file res_pjsip_refer.c.

1569{
1570 int parsed_len;
1571 pjsip_replaces_hdr *replaces;
1572 pjsip_generic_string_hdr *referred_hdr;
1573
1574
1576
1577 struct ast_framehook_interface hook = {
1579 .event_cb = refer_ari_progress_framehook,
1581 .data = progress,
1582 .disable_inheritance = 1,
1583 };
1584
1585 static const pj_str_t str_referred_by = { "Referred-By", 11 };
1586 static const pj_str_t str_referred_by_s = { "b", 1 };
1587 static const pj_str_t str_replaces = { "Replaces", 8 };
1588 SCOPE_ENTER(3, "%s: progress: %p\n", ast_channel_name(session->channel), progress);
1589
1590 if (!progress) {
1591 SCOPE_EXIT_RTN_VALUE(204, "%s: done: %d\n", ast_channel_name(session->channel), 204);
1592 }
1593
1595 if (!state) {
1596 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1597 }
1598
1599 state->last_response = AST_TRANSFER_INVALID;
1600
1601 state->params = ao2_alloc(sizeof(struct ast_refer_params), refer_params_destroy);
1602 if (!state->params) {
1603 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1604 }
1605 AST_VECTOR_INIT(state->params, 0);
1606
1607
1608 ast_channel_ref(session->channel);
1609 state->transferer_chan = session->channel;
1610
1611 /* Using the user portion of the target URI see if it exists as a valid extension in their context */
1612 ast_copy_pj_str(state->exten, &target->user, sizeof(state->exten));
1613
1614 /*
1615 * We may want to match in the dialplan without any user
1616 * options getting in the way.
1617 */
1619
1620 referred_hdr = pjsip_msg_find_hdr_by_names(rdata->msg_info.msg,
1621 &str_referred_by, &str_referred_by_s, NULL);
1622 if (referred_hdr) {
1623 state->referred_by = copy_string(&referred_hdr->hvalue);
1624 if (!state->referred_by) {
1625 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1626 }
1627 }
1628
1629 if (replaces_param) {
1630 pjsip_dialog *dlg;
1631 pj_str_t replaces_content = { 0, };
1632 pj_strdup_with_null(rdata->tp_info.pool, &replaces_content, &replaces_param->value);
1633
1634 /* Parsing the parameter as a Replaces header easily grabs the needed information */
1635 if (!(replaces = pjsip_parse_hdr(rdata->tp_info.pool, &str_replaces, replaces_content.ptr,
1636 pj_strlen(&replaces_content), &parsed_len))) {
1637 ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' with invalid Replaces header, rejecting\n",
1639 SCOPE_EXIT_RTN_VALUE(400, "%s: done: %d\n", ast_channel_name(session->channel), 400);
1640 }
1641
1642 dlg = pjsip_ua_find_dialog(&replaces->call_id, &replaces->to_tag, &replaces->from_tag, PJ_TRUE);
1643 if (dlg) {
1644 state->other_session = ast_sip_dialog_get_session(dlg);
1645 pjsip_dlg_dec_lock(dlg);
1646 }
1647
1648 state->protocol_id = copy_string(&replaces->call_id);
1649 if (!state->protocol_id) {
1650 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1651 }
1652
1653 if (!add_refer_param(state->params, "from", &replaces->from_tag)) {
1654 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1655 }
1656
1657 if (!add_refer_param(state->params, "to", &replaces->to_tag)) {
1658 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1659 }
1660 }
1661
1662 ao2_ref(session, +1);
1663 state->transferer = session;
1664
1665
1666 /* We need to bump the reference count up on the progress structure since it is in the frame hook now */
1667 ao2_ref(progress, +1);
1668 ast_channel_lock(session->channel);
1669 progress->framehook = ast_framehook_attach(session->channel, &hook);
1670 ast_channel_unlock(session->channel);
1671
1672 if (progress->framehook < 0) {
1674 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1675 }
1676
1677 if (ari_notify(state)) {
1678 ast_channel_lock(session->channel);
1679 ast_framehook_detach(session->channel, progress->framehook);
1680 progress->framehook = -1;
1682 ast_channel_unlock(session->channel);
1683 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1684 }
1685
1686 /* Transfer ownership to the progress */
1687 progress->ari_state = state;
1688 state = NULL;
1689 SCOPE_EXIT_RTN_VALUE(200, "%s: done: %d\n", ast_channel_name(session->channel), 200);
1690}
#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:3578
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(), SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, 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 1692 of file res_pjsip_refer.c.

1694{
1695 const pj_str_t str_replaces = { "Replaces", 8 };
1696 pj_str_t replaces_content;
1697 pjsip_replaces_hdr *replaces;
1698 int parsed_len;
1699 pjsip_dialog *dlg;
1700 SCOPE_ENTER(3, "%s", ast_channel_name(session->channel));
1701
1702 pj_strdup_with_null(rdata->tp_info.pool, &replaces_content, &replaces_param->value);
1703
1704 /* Parsing the parameter as a Replaces header easily grabs the needed information */
1705 if (!(replaces = pjsip_parse_hdr(rdata->tp_info.pool, &str_replaces, replaces_content.ptr,
1706 pj_strlen(&replaces_content), &parsed_len))) {
1707 ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' with invalid Replaces header, rejecting\n",
1709 SCOPE_EXIT_RTN_VALUE(400, "%s: done: %d\n", ast_channel_name(session->channel), 400);
1710 }
1711
1712 /* See if the dialog is local, or remote */
1713 if ((dlg = pjsip_ua_find_dialog(&replaces->call_id, &replaces->to_tag, &replaces->from_tag, PJ_TRUE))) {
1714 RAII_VAR(struct ast_sip_session *, other_session, ast_sip_dialog_get_session(dlg), ao2_cleanup);
1715 struct refer_attended *attended;
1716
1717 pjsip_dlg_dec_lock(dlg);
1718
1719 if (!other_session) {
1720 ast_debug(3, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but no session exists on it\n",
1722 SCOPE_EXIT_RTN_VALUE(503, "%s: done: %d\n", ast_channel_name(session->channel), 603);
1723 }
1724
1725 /* We defer actually doing the attended transfer to the other session so no deadlock can occur */
1726 if (!(attended = refer_attended_alloc(session, other_session, progress))) {
1727 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",
1729 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1730 }
1731
1733 ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but could not defer termination, rejecting\n",
1735 ao2_cleanup(attended);
1736 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1737 }
1738
1739 /* Push it to the other session, which will have both channels with minimal locking */
1740 if (ast_sip_push_task(other_session->serializer, refer_attended_task, attended)) {
1743 ao2_cleanup(attended);
1744 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1745 }
1746
1747 ast_debug(3, "Attended transfer from '%s' pushed to second channel serializer\n",
1748 ast_channel_name(session->channel));
1749
1750 SCOPE_EXIT_RTN_VALUE(200, "%s: done: %d\n", ast_channel_name(session->channel), 200);
1751 } else {
1752 const char *context;
1753 struct refer_blind refer = { 0, };
1754 int response;
1755
1757
1758 if (!ast_exists_extension(NULL, context, "external_replaces", 1, NULL)) {
1759 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",
1761 SCOPE_EXIT_RTN_VALUE(404, "%s: done: %d\n", ast_channel_name(session->channel), 404);
1762 }
1763
1764 refer.context = context;
1765 refer.progress = progress;
1766 refer.rdata = rdata;
1767 refer.replaces = replaces;
1768 refer.refer_to = target_uri;
1769 refer.attended = 1;
1770
1772 ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but could not defer termination, rejecting\n",
1773 ast_channel_name(session->channel),
1775 SCOPE_EXIT_RTN_VALUE(500, "%s: done: %d\n", ast_channel_name(session->channel), 500);
1776 }
1777
1779 "external_replaces", context, refer_blind_callback, &refer));
1780
1782 if (response != 200) {
1784 }
1785
1786 SCOPE_EXIT_RTN_VALUE(response, "%s: done: %d\n", ast_channel_name(session->channel), response);
1787 }
1788}
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:4634
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:2735
#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, SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, 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 1790 of file res_pjsip_refer.c.

1792{
1793 const char *context;
1794 char exten[AST_MAX_EXTENSION];
1795 struct refer_blind refer = { 0, };
1796 int response;
1797
1798 /* If no explicit transfer context has been provided use their configured context */
1800
1801 /* Using the user portion of the target URI see if it exists as a valid extension in their context */
1802 ast_copy_pj_str(exten, &target->user, sizeof(exten));
1803
1804 /*
1805 * We may want to match in the dialplan without any user
1806 * options getting in the way.
1807 */
1809
1810 /* Uri without exten */
1811 if (ast_strlen_zero(exten)) {
1812 ast_copy_string(exten, "s", sizeof(exten));
1813 ast_debug(3, "Channel '%s' from endpoint '%s' attempted blind transfer to a target without extension. Target was set to 's@%s'\n",
1815 }
1816
1817 if (!ast_exists_extension(NULL, context, exten, 1, NULL)) {
1818 ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer to '%s@%s' but target does not exist\n",
1819 ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), exten, context);
1820 return 404;
1821 }
1822
1823 refer.context = context;
1824 refer.progress = progress;
1825 refer.rdata = rdata;
1826 refer.refer_to = target;
1827 refer.attended = 0;
1828
1830 ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer but could not defer termination, rejecting\n",
1831 ast_channel_name(session->channel),
1833 return 500;
1834 }
1835
1837 exten, context, refer_blind_callback, &refer));
1838
1840 if (response != 200) {
1842 }
1843
1844 return response;
1845}
#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 1873 of file res_pjsip_refer.c.

1874{
1875 pjsip_dialog *other_dlg = NULL;
1876 pjsip_tx_data *packet;
1877 int response = 0;
1878 RAII_VAR(struct ast_sip_session *, other_session, NULL, ao2_cleanup);
1879 struct invite_replaces invite;
1880
1881 /* If a Replaces header is present make sure it is valid */
1882 if (pjsip_replaces_verify_request(rdata, &other_dlg, PJ_TRUE, &packet) != PJ_SUCCESS) {
1883 response = packet->msg->line.status.code;
1884 ast_assert(response != 0);
1885 pjsip_tx_data_dec_ref(packet);
1886 goto inv_replace_failed;
1887 }
1888
1889 /* If no other dialog exists then this INVITE request does not have a Replaces header */
1890 if (!other_dlg) {
1891 return 0;
1892 }
1893
1894 other_session = ast_sip_dialog_get_session(other_dlg);
1895 pjsip_dlg_dec_lock(other_dlg);
1896
1897 /* Don't accept an in-dialog INVITE with Replaces as it does not make much sense */
1898 if (session->inv_session->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
1899 response = 488;
1900 goto inv_replace_failed;
1901 }
1902
1903 if (!other_session) {
1904 ast_debug(3, "INVITE with Replaces received on channel '%s' from endpoint '%s', but requested session does not exist\n",
1906 response = 481;
1907 goto inv_replace_failed;
1908 }
1909
1910 invite.session = other_session;
1911
1912 if (ast_sip_push_task_wait_serializer(other_session->serializer, invite_replaces,
1913 &invite)) {
1914 response = 481;
1915 goto inv_replace_failed;
1916 }
1917
1918 ast_channel_lock(session->channel);
1920 ast_channel_unlock(session->channel);
1921 ast_raw_answer(session->channel);
1922
1923 ast_debug(3, "INVITE with Replaces being attempted. '%s' --> '%s'\n",
1924 ast_channel_name(session->channel), ast_channel_name(invite.channel));
1925
1926 /* Unhold the channel now, as later we are not having access to it anymore */
1927 ast_queue_unhold(session->channel);
1929
1930 if (!invite.bridge) {
1931 struct ast_channel *chan = session->channel;
1932
1933 /*
1934 * This will use a synchronous task but we aren't operating in
1935 * the serializer at this point in time, so it won't deadlock.
1936 */
1937 if (!ast_channel_move(invite.channel, chan)) {
1938 /*
1939 * We can't directly use session->channel because ast_channel_move()
1940 * does a masquerade which changes session->channel to a different
1941 * channel. To ensure we work on the right channel we store a
1942 * pointer locally before we begin so it remains valid.
1943 */
1944 ast_hangup(chan);
1945 } else {
1946 response = AST_CAUSE_FAILURE;
1947 }
1948 } else {
1949 if (ast_bridge_impart(invite.bridge, session->channel, invite.channel, NULL,
1951 response = AST_CAUSE_FAILURE;
1952 }
1953 }
1954
1955 ast_channel_unref(invite.channel);
1956 ao2_cleanup(invite.bridge);
1957
1958 if (!response) {
1959 /*
1960 * On success we cannot use session->channel in the debug message.
1961 * This thread either no longer has a ref to session->channel or
1962 * session->channel is no longer the original channel.
1963 */
1964 ast_debug(3, "INVITE with Replaces successfully completed.\n");
1965 } else {
1966 ast_debug(3, "INVITE with Replaces failed on channel '%s', hanging up with cause '%d'\n",
1967 ast_channel_name(session->channel), response);
1968 ast_channel_lock(session->channel);
1969 ast_channel_hangupcause_set(session->channel, response);
1970 ast_channel_unlock(session->channel);
1971 ast_hangup(session->channel);
1972 }
1973
1974 return 1;
1975
1976inv_replace_failed:
1977 if (session->inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
1978 ast_debug(3, "INVITE with Replaces failed on channel '%s', sending response of '%d'\n",
1979 ast_channel_name(session->channel), response);
1980 session->defer_terminate = 1;
1981 ast_hangup(session->channel);
1982
1983 if (pjsip_inv_end_session(session->inv_session, response, NULL, &packet) == PJ_SUCCESS
1984 && packet) {
1986 }
1987 } else {
1988 ast_debug(3, "INVITE with Replaces in-dialog on channel '%s', hanging up\n",
1989 ast_channel_name(session->channel));
1990 ast_queue_hangup(session->channel);
1991 }
1992
1993 return 1;
1994}
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:2077
@ AST_BRIDGE_IMPART_CHAN_INDEPENDENT
Definition bridge.h:600
#define AST_CAUSE_FAILURE
Definition causes.h:150
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition channel.c:2540
int ast_queue_hangup(struct ast_channel *chan)
Queue a hangup frame.
Definition channel.c:1182
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:1171
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:10846
int ast_queue_unhold(struct ast_channel *chan)
Queue an unhold frame.
Definition channel.c:1274
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3019
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:2690
@ AST_STATE_RING
int ast_setstate(struct ast_channel *chan, enum ast_channel_state)
Change the state of a channel.
Definition channel.c:7422
#define ast_sip_push_task_wait_serializer(serializer, sip_task, task_data)
Definition res_pjsip.h:2221
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 1996 of file res_pjsip_refer.c.

1997{
1998 pjsip_generic_string_hdr *refer_to;
1999 char *uri;
2000 size_t uri_size;
2001 pjsip_uri *target;
2002 pjsip_sip_uri *target_uri;
2004 pjsip_param *replaces;
2005 int response;
2006
2007 static const pj_str_t str_refer_to = { "Refer-To", 8 };
2008 static const pj_str_t str_refer_to_s = { "r", 1 };
2009 static const pj_str_t str_replaces = { "Replaces", 8 };
2010
2011 SCOPE_ENTER(3, "%s\n", session->channel ? ast_channel_name(session->channel) : "unknown");
2012
2013 if (!session->channel) {
2014 /* No channel to refer. Likely because the call was just hung up. */
2015 pjsip_dlg_respond(session->inv_session->dlg, rdata, 404, NULL, NULL, NULL);
2016 ast_debug(3, "Received a REFER on a session with no channel from endpoint '%s'.\n",
2018 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
2019 }
2020
2021 if (!session->endpoint->allowtransfer) {
2022 pjsip_dlg_respond(session->inv_session->dlg, rdata, 603, NULL, NULL, NULL);
2023 ast_log(LOG_WARNING, "Endpoint %s transfer attempt blocked due to configuration\n",
2025 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
2026 }
2027
2028 /* A Refer-To header is required */
2029 refer_to = pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &str_refer_to, &str_refer_to_s, NULL);
2030 if (!refer_to) {
2031 pjsip_dlg_respond(session->inv_session->dlg, rdata, 400, NULL, NULL, NULL);
2032 ast_debug(3, "Received a REFER without Refer-To on channel '%s' from endpoint '%s'\n",
2034 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
2035 }
2036
2037 /* The ast_copy_pj_str to uri is needed because it puts the NULL terminator to the uri
2038 * as pjsip_parse_uri require a NULL terminated uri
2039 */
2040
2041 uri_size = pj_strlen(&refer_to->hvalue) + 1;
2042 uri = ast_alloca(uri_size);
2043 ast_copy_pj_str(uri, &refer_to->hvalue, uri_size);
2044
2045 target = pjsip_parse_uri(rdata->tp_info.pool, uri, uri_size - 1, 0);
2046
2047 if (!target
2048 || (!PJSIP_URI_SCHEME_IS_SIP(target)
2049 && !PJSIP_URI_SCHEME_IS_SIPS(target))) {
2050
2051 pjsip_dlg_respond(session->inv_session->dlg, rdata, 400, NULL, NULL, NULL);
2052 ast_debug(3, "Received a REFER without a parseable Refer-To ('%s') on channel '%s' from endpoint '%s'\n",
2053 uri, ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint));
2054 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
2055 }
2056 target_uri = pjsip_uri_get_uri(target);
2057
2058 /* Set up REFER progress subscription if requested/possible */
2059 if (refer_progress_alloc(session, rdata, &progress)) {
2060 pjsip_dlg_respond(session->inv_session->dlg, rdata, 500, NULL, NULL, NULL);
2061 ast_debug(3, "Could not set up subscription for REFER on channel '%s' from endpoint '%s'\n",
2063 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
2064 }
2065
2066 replaces = pjsip_param_find(&target_uri->header_param, &str_replaces);
2067 if (!replaces) {
2068 replaces = pjsip_param_find(&target_uri->other_param, &str_replaces);
2069 }
2070
2071 /* Determine if this is handled externally or an attended or blind transfer */
2072 if (session->transferhandling_ari) {
2073 ast_trace(-1, "%s: incoming ari\n", session->channel ? ast_channel_name(session->channel) : "unknown");
2074 response = refer_incoming_ari_request(session, rdata, target_uri, replaces, progress);
2075 } else if (replaces) {
2076 ast_trace(-1, "%s: incoming attended\n", session->channel ? ast_channel_name(session->channel) : "unknown");
2077 response = refer_incoming_attended_request(session, rdata, target_uri, replaces, progress);
2078 } else {
2079 ast_trace(-1, "%s: incoming blind\n", session->channel ? ast_channel_name(session->channel) : "unknown");
2080 response = refer_incoming_blind_request(session, rdata, target_uri, progress);
2081 }
2082
2083 if (progress->no_refer_sub) {
2084 /* The transferer has requested no subscription, so send a final response immediately */
2085 pjsip_tx_data *tdata;
2086 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
2087 const pj_str_t str_false = { "false", 5 };
2088 pjsip_hdr *hdr;
2089
2090 ast_trace(-1, "%s: No refer sub from endpoint '%s'. Sending reponse %d\n", ast_channel_name(session->channel),
2091 ast_sorcery_object_get_id(session->endpoint), response);
2092
2093 if (pjsip_dlg_create_response(session->inv_session->dlg, rdata, response, NULL, &tdata) != PJ_SUCCESS) {
2094 pjsip_dlg_respond(session->inv_session->dlg, rdata, response, NULL, NULL, NULL);
2095 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
2096 }
2097
2098 hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, &str_false);
2099 pjsip_msg_add_hdr(tdata->msg, hdr);
2100
2101 pjsip_dlg_send_response(session->inv_session->dlg, pjsip_rdata_get_tsx(rdata), tdata);
2102
2103 } else if (response != 200) {
2104 /* Since this failed we can send a final NOTIFY now and terminate the subscription */
2105 struct refer_progress_notification *notification = refer_progress_notification_alloc(progress, response, PJSIP_EVSUB_STATE_TERMINATED);
2106
2107 if (notification) {
2108 /* The refer_progress_notify function will call ao2_cleanup on this for us */
2109 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
2110 ao2_cleanup(notification);
2111 }
2112 }
2113 }
2114
2115 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
2116}
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(), ast_trace, 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, SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, 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 2118 of file res_pjsip_refer.c.

2119{
2120 if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_refer_method())) {
2122 } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_invite_method)) {
2124 } else {
2125 return 0;
2126 }
2127}
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 1205 of file res_pjsip_refer.c.

1205 {
1206 RAII_VAR(struct ast_str *, refer_to_str, ast_str_create(PJSIP_MAX_URL_SIZE), ast_free_ptr);
1207 const pj_str_t REFER_TO = { "Refer-To", 8 };
1208 pjsip_generic_string_hdr *refer_to_hdr;
1209 pjsip_dialog *dlg;
1210 struct refer_data *refer_data;
1211 pjsip_uri *parsed_uri;
1212 pjsip_sip_uri *refer_to_uri;
1213
1214 /*
1215 * If this is a request in response to a 401/407 Unauthorized challenge, the
1216 * Refer-To URI has been rewritten already, so don't attempt to re-write it again.
1217 * Checking for presence of the Authorization header is not an ideal solution. We do this because
1218 * there exists some race condition where this dialog is not the same as the one used
1219 * to send the original request in which case we don't have the correct refer_data.
1220 */
1221 if (!refer_to_str
1222 || pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL)
1223 || !(dlg = pjsip_tdata_get_dlg(tdata))
1224 || !(refer_data = pjsip_dlg_get_mod_data(dlg, refer_out_of_dialog_module.id))
1225 || !refer_data->to_self
1226 || !(parsed_uri = get_refer_to_uri(tdata))) {
1227 goto out;
1228 }
1229 refer_to_uri = pjsip_uri_get_uri(parsed_uri);
1230 ast_sip_rewrite_uri_to_local(refer_to_uri, tdata);
1231
1232 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, parsed_uri, ast_str_buffer(refer_to_str), ast_str_size(refer_to_str));
1233 refer_to_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &REFER_TO, NULL);
1234 pj_strdup2(tdata->pool, &refer_to_hdr->hvalue, ast_str_buffer(refer_to_str));
1235
1236out:
1237 return PJ_SUCCESS;
1238}
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:603
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 1257 of file res_pjsip_refer.c.

1257 {
1258 struct refer_out_of_dialog *data = obj;
1259
1260 if (data->dlg) {
1261 /* ast_sip_push_task_wait_servant should not be called in a destructor,
1262 * however in this case it seems to be fine.
1263 */
1265 }
1266}
#define ast_sip_push_task_wait_servant(serializer, sip_task, task_data)
Definition res_pjsip.h:2165
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 2162 of file res_pjsip_refer.c.

2163{
2164 if (pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_invite_method)
2165 || !session->channel
2166 || session->inv_session->state != PJSIP_INV_STATE_NULL) {
2167 return;
2168 }
2169
2170 ast_channel_lock(session->channel);
2171 add_header_from_channel_var(session->channel, "SIPREPLACESHDR", "Replaces", tdata);
2172 add_header_from_channel_var(session->channel, "SIPREFERREDBYHDR", "Referred-By", tdata);
2173 ast_channel_unlock(session->channel);
2174}
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 127 of file res_pjsip_refer.c.

128{
129 struct ast_refer_params *params = obj;
130
131 for (int i = 0; i < AST_VECTOR_SIZE(params); ++i) {
132 struct ast_refer_param param = AST_VECTOR_GET(params, i);
133 ast_free((char *) param.param_name);
134 ast_free((char *) param.param_value);
135 }
136}
const char * param_value
Definition refer.h:83
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:637
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:708

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 557 of file res_pjsip_refer.c.

558{
559 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
560 pjsip_generic_string_hdr *refer_sub = NULL;
561 const pj_str_t str_true = { "true", 4 };
562 pjsip_hdr hdr_list;
563 char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
564 int no_refer_sub = 0;
565 SCOPE_ENTER(3, "%s\n", ast_channel_name(session->channel));
566
567 *progress = NULL;
568
569 /* Grab the optional Refer-Sub header, it can be used to suppress the implicit subscription */
570 refer_sub = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
571 if ((refer_sub && pj_strnicmp(&refer_sub->hvalue, &str_true, 4))) {
572 no_refer_sub = 1;
573 }
574
575 if (!(*progress = ao2_alloc(sizeof(struct refer_progress), refer_progress_destroy))) {
576 SCOPE_EXIT_RTN_VALUE(-1, "%s: done: %d\n", ast_channel_name(session->channel), -1);
577 }
578
579 (*progress)->no_refer_sub = no_refer_sub;
580
581 ast_debug(3, "Created progress monitor '%p' for transfer occurring from channel '%s' and endpoint '%s'\n",
583
584 (*progress)->refer_blind_progress = session->endpoint->refer_blind_progress;
585
586 (*progress)->framehook = -1;
587
588 /* Create name with seq number appended. */
589 ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/refer/%s",
591
592 if (!((*progress)->serializer = ast_sip_create_serializer(tps_name))) {
593 goto error;
594 }
595
596 /*
597 * If the referer request the subscription be suppressed, we can stop here.
598 */
599 if ((*progress)->no_refer_sub) {
600 SCOPE_EXIT_RTN_VALUE(0, "%s: done no refer sub: %d\n", ast_channel_name(session->channel), 0);
601 }
602
603 /* Create the implicit subscription for monitoring of this transfer */
604 if (pjsip_xfer_create_uas(session->inv_session->dlg, &refer_progress_evsub_cb, rdata, &(*progress)->sub) != PJ_SUCCESS) {
605 goto error;
606 }
607
608 /* To prevent a potential deadlock we need the dialog so we can lock/unlock */
609 (*progress)->dlg = session->inv_session->dlg;
610 /* We also need to make sure it stays around until we're done with it */
611 pjsip_dlg_inc_session((*progress)->dlg, &refer_progress_module);
612
613 /* Associate the REFER progress structure with the subscription */
614 ao2_ref(*progress, +1);
615 pjsip_evsub_set_mod_data((*progress)->sub, refer_progress_module.id, *progress);
616
617 pj_list_init(&hdr_list);
618 if (refer_sub) {
619 pjsip_hdr *hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(session->inv_session->dlg->pool, &str_refer_sub, &str_true);
620
621 pj_list_push_back(&hdr_list, hdr);
622 }
623
624 /* Accept the REFER request */
625 ast_debug(3, "Accepting REFER request for progress monitor '%p'\n", *progress);
626 pjsip_xfer_accept((*progress)->sub, rdata, 202, &hdr_list);
627
628 SCOPE_EXIT_RTN_VALUE(0, "%s: done: %d\n", ast_channel_name(session->channel), 0);
629
630error:
632 *progress = NULL;
633 SCOPE_EXIT_RTN_VALUE(-1, "%s: done: %d\n", ast_channel_name(session->channel), -1);
634}
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(), refer_progress::no_refer_sub, NULL, refer_progress::rdata, refer_progress_destroy(), refer_progress_evsub_cb, refer_progress_module, SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, 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 272 of file res_pjsip_refer.c.

274{
275 struct refer_progress *progress = data;
276 struct ast_bridge_blob *enter_blob;
277 struct refer_progress_notification *notification;
278 struct ast_channel *chan;
279
281 ao2_ref(progress, -1);
282 return;
283 }
284
286 /* Don't care */
287 return;
288 }
289
290 enter_blob = stasis_message_data(message);
291 if (strcmp(enter_blob->channel->base->uniqueid, progress->transferee)) {
292 /* Don't care */
293 return;
294 }
295
296 if (!progress->transfer_data->completed) {
297 /* We can't act on this message because the transfer_channel_data doesn't show that
298 * the transfer is ready to progress */
299 return;
300 }
301
302 /* OMG the transferee is joining a bridge. His call got answered! */
303 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
304 if (notification) {
305 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
306 ao2_cleanup(notification);
307 }
309 progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
311 }
312
313 chan = ast_channel_get_by_name(progress->transferee);
314 if (!chan) {
315 /* The channel is already gone */
316 return;
317 }
318
319 ast_channel_lock(chan);
320 ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as it has joined a bridge\n",
321 ast_channel_name(chan));
322 ast_framehook_detach(chan, progress->framehook);
323 ast_channel_unlock(chan);
324
325 ast_channel_unref(chan);
326}
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition channel.c:1417
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:1252
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition stasis.c:1049
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_lock, ao2_ref, ao2_unlock, 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 521 of file res_pjsip_refer.c.

522{
523 struct refer_progress *progress = obj;
524 SCOPE_ENTER(3, "%p\n", progress);
525
526 if (progress->bridge_sub) {
527 progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
528 }
529
530 if (progress->dlg) {
531 /*
532 * Although the dlg session count was incremented in a pjsip servant
533 * thread, there's no guarantee that the last thread to unref this progress
534 * object was one. Before we decrement, we need to make sure that this
535 * is either a servant thread or that we push the decrement to a
536 * serializer that is one.
537 *
538 * Because pjsip_dlg_dec_session requires the dialog lock, we don't want
539 * to wait on the task to complete if we had to push it to a serializer.
540 */
542 pjsip_dlg_dec_session(progress->dlg, &refer_progress_module);
543 } else {
545 }
546 }
547
548 ao2_cleanup(progress->transfer_data);
549 ao2_cleanup(progress->ari_state);
550
551 ast_free(progress->transferee);
553 SCOPE_EXIT_RTN("%p\n", progress);
554}
#define SCOPE_EXIT_RTN(...)
int ast_sip_thread_is_servant(void)
Determine if the current thread is a SIP servant thread.
Definition res_pjsip.c:2330
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, SCOPE_ENTER, SCOPE_EXIT_RTN, 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 329 of file res_pjsip_refer.c.

331{
332 struct refer_progress *progress = data;
333 struct refer_progress_notification *notification = NULL;
334
335 /* We only care about frames *to* the channel */
336 if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
337 return f;
338 }
339
340 /* If the completed flag hasn't been raised, skip this pass. */
341 if (!progress->transfer_data->completed) {
342 return f;
343 }
344
345 /* Determine the state of the REFER based on the control frames (or voice frames) passing */
346 if (f->frametype == AST_FRAME_VOICE && !progress->subclass) {
347 /* Media is passing without progress, this means the call has been answered */
348 progress->subclass = AST_CONTROL_ANSWER;
349 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
350 } else if (f->frametype == AST_FRAME_CONTROL) {
351 /* Based on the control frame being written we can send a NOTIFY advising of the progress */
353 /* Don't set progress->subclass; an ANSWER can still follow */
354 notification = refer_progress_notification_alloc(progress, 180, PJSIP_EVSUB_STATE_ACTIVE);
355 } else if (f->subclass.integer == AST_CONTROL_BUSY) {
356 progress->subclass = f->subclass.integer;
357 notification = refer_progress_notification_alloc(progress, 486, PJSIP_EVSUB_STATE_TERMINATED);
358 } else if (f->subclass.integer == AST_CONTROL_CONGESTION) {
359 progress->subclass = f->subclass.integer;
360 notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
361 } else if (f->subclass.integer == AST_CONTROL_PROGRESS) {
362 /* Don't set progress->subclass; an ANSWER can still follow */
363 notification = refer_progress_notification_alloc(progress, 183, PJSIP_EVSUB_STATE_ACTIVE);
364 } else if (f->subclass.integer == AST_CONTROL_PROCEEDING) {
365 /* Don't set progress->subclass; an ANSWER can still follow */
366 notification = refer_progress_notification_alloc(progress, 100, PJSIP_EVSUB_STATE_ACTIVE);
367 } else if (f->subclass.integer == AST_CONTROL_ANSWER) {
368 progress->subclass = f->subclass.integer;
369 notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
370 }
371 }
372
373 /* If a notification is due to be sent push it to the taskpool */
374 if (notification) {
375 /* If the subscription is being terminated we don't need the frame hook any longer */
376 if (notification->state == PJSIP_EVSUB_STATE_TERMINATED) {
377 ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as subscription is being terminated\n",
378 ast_channel_name(chan));
379 ast_framehook_detach(chan, progress->framehook);
380 }
381
382 if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
383 ao2_cleanup(notification);
384 }
385 }
386
387 return f;
388}
@ 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 458 of file res_pjsip_refer.c.

459{
460 struct refer_progress *progress = data;
461 struct refer_progress_notification *notification = NULL;
462 SCOPE_ENTER(3, "%p\n", progress);
463
464 /*
465 * If the referer didn't request the subscription be suppressed, we need to do
466 * a final notification.
467 */
468 if (!progress->no_refer_sub) {
469 notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
470 if (notification && ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
471 ao2_cleanup(notification);
472 }
473 }
474
476 if (progress->bridge_sub) {
477 progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
478 }
480
482 SCOPE_EXIT_RTN("%p\n", progress);
483}

References ao2_cleanup, ao2_lock, ao2_unlock, ast_sip_push_task, NULL, refer_progress_notification_alloc(), refer_progress_notify(), SCOPE_ENTER, SCOPE_EXIT_RTN, 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 162 of file res_pjsip_refer.c.

164{
165 struct refer_progress_notification *notification = ao2_alloc(sizeof(*notification), refer_progress_notification_destroy);
166
167 if (!notification) {
168 return NULL;
169 }
170
171 ao2_ref(progress, +1);
172 notification->progress = progress;
173 notification->response = response;
174 notification->state = state;
175
176 return notification;
177}
static void refer_progress_notification_destroy(void *obj)
Destructor for REFER Progress notification structure.

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 139 of file res_pjsip_refer.c.

140{
141 struct refer_progress_notification *notification = obj;
142 SCOPE_ENTER(3, "%p\n", obj);
143
144 ao2_cleanup(notification->progress);
145 SCOPE_EXIT_RTN("%p\n", obj);
146}

References ao2_cleanup, refer_progress_notification::progress, SCOPE_ENTER, and SCOPE_EXIT_RTN.

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 208 of file res_pjsip_refer.c.

209{
210 RAII_VAR(struct refer_progress_notification *, notification, data, ao2_cleanup);
211 struct transfer_ari_state *ari_state = notification->progress->ari_state;
212 pjsip_evsub *sub;
213 pjsip_tx_data *tdata;
214 SCOPE_ENTER(3, "%s: no_refer_sub: %s\n",
215 ari_state ? ast_channel_name(ari_state->transferer_chan) : "????",
216 AST_YESNO(notification->progress->no_refer_sub));
217
218 /*
219 * If the referer included a "Refer-Sub: false" header, we don't want to
220 * create the automatic subscription but, if the transfer is being controlled
221 * by ARI, we still want the app to get the progress events.
222 */
223 if (notification->progress->no_refer_sub) {
224 if (ari_state) {
225 ari_notify(ari_state);
226 }
227 SCOPE_EXIT_RTN_VALUE(0, "%s: done\n",
228 ari_state ? ast_channel_name(ari_state->transferer_chan) : "????");
229 }
230
231 pjsip_dlg_inc_lock(notification->progress->dlg);
232
233 /* If the subscription has already been terminated we can't send a notification */
234 if (!(sub = notification->progress->sub)) {
235 ast_debug(3, "Not sending NOTIFY of response '%d' and state '%u' on progress monitor '%p' as subscription has been terminated\n",
236 notification->response, notification->state, notification->progress);
237 pjsip_dlg_dec_lock(notification->progress->dlg);
238 SCOPE_EXIT_RTN_VALUE(0, "???\n");
239 }
240
241 /* Send a deferred initial 100 Trying SIP frag NOTIFY if we haven't already. */
242 if (!notification->progress->sent_100) {
243 notification->progress->sent_100 = 1;
244 if (notification->response != 100) {
245 ast_debug(3, "Sending initial 100 Trying NOTIFY for progress monitor '%p'\n",
246 notification->progress);
247 if (pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, 100, NULL, &tdata) == PJ_SUCCESS) {
248 pjsip_xfer_send_request(sub, tdata);
249 }
250 }
251 }
252
253 ast_debug(3, "Sending NOTIFY with response '%d' and state '%u' on subscription '%p' and progress monitor '%p'\n",
254 notification->response, notification->state, sub, notification->progress);
255
256 /* Actually send the notification */
257 if (pjsip_xfer_notify(sub, notification->state, notification->response, NULL, &tdata) == PJ_SUCCESS) {
258 pjsip_xfer_send_request(sub, tdata);
259 }
260
261
262 if (notification->progress->ari_state) {
263 struct transfer_ari_state *ari_state = notification->progress->ari_state;
264 ari_notify(ari_state);
265 }
266
267 pjsip_dlg_dec_lock(notification->progress->dlg);
268
269 SCOPE_EXIT_RTN_VALUE(0, "???\n");
270}
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition strings.h:143
struct ast_channel * transferer_chan

References ao2_cleanup, ari_notify(), ast_channel_name(), ast_debug, AST_YESNO, NULL, RAII_VAR, SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, sub, and transfer_ari_state::transferer_chan.

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 495 of file res_pjsip_refer.c.

496{
497 struct refer_progress *progress = pjsip_evsub_get_mod_data(sub, refer_progress_module.id);
498
499 /*
500 * If being destroyed, remove the progress object from the subscription
501 * and release the reference it had.
502 */
503 if (progress && (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)) {
504 pjsip_evsub_set_mod_data(progress->sub, refer_progress_module.id, NULL);
505 progress->sub = NULL;
507 }
508}

References ao2_cleanup, NULL, refer_progress_module, and sub.

◆ refer_send()

static int refer_send ( void *  data)
static

Definition at line 1410 of file res_pjsip_refer.c.

1411{
1412 struct refer_data *rdata = data; /* The caller holds a reference */
1413 pjsip_tx_data *tdata;
1414 pjsip_evsub *sub;
1415 pj_str_t tmp;
1416 char refer_to_str[PJSIP_MAX_URL_SIZE];
1417 char disp_name_escaped[128];
1418 struct refer_out_of_dialog *refer;
1419 struct pjsip_evsub_user xfer_cb;
1420 RAII_VAR(char *, uri, NULL, ast_free);
1421 RAII_VAR(char *, tmp_str, NULL, ast_free);
1422 RAII_VAR(char *, display_name, NULL, ast_free);
1423 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
1424 RAII_VAR(struct ast_sip_endpoint *, refer_to_endpoint, NULL, ao2_cleanup);
1425
1426 endpoint = ast_sip_get_endpoint(rdata->destination, 1, &uri);
1427 if (!endpoint) {
1429 "PJSIP REFER - Could not find endpoint '%s' and no default outbound endpoint configured\n",
1430 rdata->destination);
1431 return -1;
1432 }
1433 ast_debug(3, "Request URI: %s\n", uri);
1434
1435 refer_to_endpoint = ast_sip_get_endpoint(rdata->refer_to, 0, &tmp_str);
1436 if (!tmp_str) {
1437 ast_log(LOG_WARNING, "PJSIP REFER - Refer to not a valid resource identifier or SIP URI\n");
1438 return -1;
1439 }
1440 if (!(refer = ao2_alloc(sizeof(struct refer_out_of_dialog), refer_out_of_dialog_destroy))) {
1441 ast_log(LOG_ERROR, "PJSIP REFER - Could not allocate resources.\n");
1442 return -1;
1443 }
1444 /* The dialog will be terminated in the subscription event callback
1445 * when the subscription has terminated. */
1447 refer->dlg = ast_sip_create_dialog_uac(endpoint, uri, NULL);
1448 if (!refer->dlg) {
1449 ast_log(LOG_WARNING, "PJSIP REFER - Could not create dialog\n");
1450 ao2_cleanup(refer);
1451 return -1;
1452 }
1453 ast_sip_dialog_set_endpoint(refer->dlg, endpoint);
1454
1455 pj_bzero(&xfer_cb, sizeof(xfer_cb));
1456 xfer_cb.on_evsub_state = &refer_client_on_evsub_state;
1457 if (pjsip_xfer_create_uac(refer->dlg, &xfer_cb, &sub) != PJ_SUCCESS) {
1458 ast_log(LOG_WARNING, "PJSIP REFER - Could not create uac\n");
1459 ao2_cleanup(refer);
1460 return -1;
1461 }
1462
1463 display_name = ast_refer_get_var_and_unlink(rdata->refer, "display_name");
1464 if (display_name) {
1465 ast_escape_quoted(display_name, disp_name_escaped, sizeof(disp_name_escaped));
1466 snprintf(refer_to_str, sizeof(refer_to_str), "\"%s\" <%s>", disp_name_escaped, tmp_str);
1467 } else {
1468 snprintf(refer_to_str, sizeof(refer_to_str), "%s", tmp_str);
1469 }
1470
1471 /* refer_out_of_dialog_module requires a reference to dlg
1472 * which will be released in refer_client_on_evsub_state()
1473 * when the implicit REFER subscription terminates */
1474 pjsip_evsub_set_mod_data(sub, refer_out_of_dialog_module.id, refer);
1475 if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, refer_to_str), &tdata) != PJ_SUCCESS) {
1476 ast_log(LOG_WARNING, "PJSIP REFER - Could not create request\n");
1477 goto failure;
1478 }
1479
1480 if (refer_to_endpoint && rdata->to_self) {
1481 pjsip_dlg_add_usage(refer->dlg, &refer_out_of_dialog_module, rdata);
1482 }
1483
1484 ast_sip_update_to_uri(tdata, uri);
1485 ast_sip_update_from(tdata, rdata->from);
1486
1487 /*
1488 * This copies any headers found in the refer's variables to
1489 * tdata.
1490 */
1491 vars_to_headers(rdata->refer, tdata);
1492 ast_debug(1, "Sending REFER to '%s' (via endpoint %s) from '%s'\n",
1493 rdata->destination, ast_sorcery_object_get_id(endpoint), rdata->from);
1494
1495 if (pjsip_xfer_send_request(sub, tdata) == PJ_SUCCESS) {
1496 return 0;
1497 }
1498
1499failure:
1500 ao2_cleanup(refer);
1501 pjsip_evsub_set_mod_data(sub, refer_out_of_dialog_module.id, NULL);
1502 pjsip_evsub_terminate(sub, PJ_FALSE);
1503 return -1;
1504}
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:3269
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:962
int ast_sip_update_from(pjsip_tx_data *tdata, char *from)
Overwrite fields in the outbound 'From' header.
Definition res_pjsip.c:3403
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:3343
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:815

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 1240 of file res_pjsip_refer.c.

1241{
1242 struct refer_out_of_dialog *data = obj;
1243
1244 /* This is why we keep the dialog on the subscription. When the subscription
1245 * is destroyed, there is no guarantee that the underlying dialog is ready
1246 * to be destroyed. Furthermore, there's no guarantee in the opposite direction
1247 * either. The dialog could be destroyed before our subscription is. We fix
1248 * this problem by keeping a reference to the dialog until it is time to
1249 * destroy the subscription.
1250 */
1251 pjsip_dlg_dec_session(data->dlg, &refer_out_of_dialog_module);
1252 data->dlg = NULL;
1253
1254 return 0;
1255}

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 687 of file res_pjsip_refer.c.

688{
689 struct ast_sip_session *session = data;
690
692 ao2_ref(session, -1);
693 return 0;
694}

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 1506 of file res_pjsip_refer.c.

1507{
1508 struct refer_data *rdata;
1509 int res;
1510
1512 ast_log(LOG_ERROR, "SIP REFER - a 'To' URI must be specified\n");
1513 return -1;
1514 }
1515
1516 rdata = refer_data_create(refer);
1517 if (!rdata) {
1518 return -1;
1519 }
1520
1522 ao2_ref(rdata, -1);
1523
1524 return res;
1525}
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 111 of file res_pjsip_refer.c.

112{
113 struct transfer_ari_state *state = obj;
114 char *chan_name = ast_strdupa(state->transferer_chan ? ast_channel_name(state->transferer_chan) : "unknown");
115 SCOPE_ENTER(3, "%s: state destructor", chan_name);
116
117 ao2_cleanup(state->transferer);
118 ao2_cleanup(state->other_session);
119 ast_channel_cleanup(state->transferer_chan);
120 ast_free(state->referred_by);
121 ast_free(state->protocol_id);
122 ao2_cleanup(state->params);
123
124 SCOPE_EXIT_RTN("%s: done", chan_name);
125}

References ao2_cleanup, ast_channel_cleanup, ast_channel_name(), ast_free, ast_strdupa, SCOPE_ENTER, and SCOPE_EXIT_RTN.

Referenced by refer_incoming_ari_request().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 2212 of file res_pjsip_refer.c.

2213{
2218
2219 return 0;
2220}
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 1157 of file res_pjsip_refer.c.

1158{
1159 const char *name;
1160 const char *value;
1162
1163 for (iter = ast_refer_var_iterator_init(refer);
1166 if (!is_refer_var_blocked(name)) {
1167 ast_sip_add_header(tdata, name, value);
1168 }
1169 }
1171
1172 return PJSIP_SC_OK;
1173}
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 715 of file res_pjsip_refer.c.

716{
717 int response;
718
719 response = 503;
720 switch (xfer_code) {
722 response = 400;
723 break;
725 response = 403;
726 break;
728 response = 500;
729 break;
731 response = 200;
732 break;
733 }
734 return response;
735}
@ AST_BRIDGE_TRANSFER_NOT_PERMITTED
Definition bridge.h:1163
@ AST_BRIDGE_TRANSFER_SUCCESS
Definition bridge.h:1161
@ AST_BRIDGE_TRANSFER_INVALID
Definition bridge.h:1165
@ AST_BRIDGE_TRANSFER_FAIL
Definition bridge.h:1167

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 2228 of file res_pjsip_refer.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 2228 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 1181 of file res_pjsip_refer.c.

1181 {
1182 .name = { "REFER Out-of-dialog Module", 26 },
1183 .id = -1,
1184 .on_tx_request = refer_on_tx_request,
1185 /* Ensure that we are called after res_pjsp_nat module and before transport priority */
1186 .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 4,
1187};
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 511 of file res_pjsip_refer.c.

511 {
512 .on_evsub_state = refer_progress_on_evsub_state,
513};

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 105 of file res_pjsip_refer.c.

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

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:3413
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 2176 of file res_pjsip_refer.c.

2176 {
2178 .incoming_request = refer_incoming_request,
2179 .outgoing_request = refer_outgoing_request,
2180};

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 1527 of file res_pjsip_refer.c.

1527 {
1528 .name = "pjsip",
1529 .refer_send = sip_refer_send,
1530};

Referenced by ast_refer_send(), and load_module().