Asterisk - The Open Source Telephony Project  18.5.0
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 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_progress
 REFER Progress structure. More...
 
struct  refer_progress_notification
 REFER Progress notification structure. More...
 

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. More...
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static int defer_termination_cancel_task (void *data)
 
static int dlg_releaser_task (void *data)
 
static int invite_replaces (void *data)
 Task for invite replaces. More...
 
static int load_module (void)
 
static struct refer_attendedrefer_attended_alloc (struct ast_sip_session *transferer, struct ast_sip_session *transferer_second, struct refer_progress *progress)
 Allocator for attended transfer task. More...
 
static void refer_attended_destroy (void *obj)
 Destructor for attended transfer task. More...
 
static int refer_attended_task (void *data)
 Task for attended transfer executed by attended->transferer_second serializer. More...
 
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. More...
 
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 void refer_outgoing_request (struct ast_sip_session *session, struct pjsip_tx_data *tdata)
 
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. More...
 
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. More...
 
static struct ast_framerefer_progress_framehook (struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
 Progress monitoring frame hook - examines frames to determine state of transfer. More...
 
static void refer_progress_framehook_destroy (void *data)
 Destroy callback for monitoring framehook. More...
 
static struct refer_progress_notificationrefer_progress_notification_alloc (struct refer_progress *progress, int response, pjsip_evsub_state state)
 Allocator for REFER Progress notification structure. More...
 
static void refer_progress_notification_destroy (void *obj)
 Destructor for REFER Progress notification structure. More...
 
static int refer_progress_notify (void *data)
 Serialized callback for subscription notification. More...
 
static void refer_progress_on_evsub_state (pjsip_evsub *sub, pjsip_event *event)
 Callback for REFER subscription state changes. More...
 
static int session_end_if_deferred_task (void *data)
 
static int unload_module (void)
 
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 = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .requires = "res_pjsip,res_pjsip_session,res_pjsip_pubsub", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static pjsip_evsub_user refer_progress_evsub_cb
 Callback structure for subscription. More...
 
static pjsip_module refer_progress_module
 REFER Progress module, used to attach REFER progress structure to subscriptions. More...
 
static struct ast_sip_session_supplement refer_supplement
 

Macro Definition Documentation

◆ DETERMINE_TRANSFER_CONTEXT

#define DETERMINE_TRANSFER_CONTEXT (   context,
  session 
)

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1295 of file res_pjsip_refer.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

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

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

Referenced by refer_outgoing_request().

1224 {
1225  const char *var_value;
1226  pj_str_t pj_header_name;
1227  pjsip_hdr *header;
1228 
1229  var_value = pbx_builtin_getvar_helper(chan, var_name);
1230  if (ast_strlen_zero(var_value)) {
1231  return;
1232  }
1233 
1234  pj_cstr(&pj_header_name, header_name);
1235  header = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_header_name, NULL);
1236  if (header) {
1237  return;
1238  }
1239  ast_sip_add_header(tdata, header_name, var_value);
1240 }
#define NULL
Definition: resample.c:96
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:5063
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define ast_strlen_zero(foo)
Definition: strings.h:52

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 1295 of file res_pjsip_refer.c.

◆ defer_termination_cancel_task()

static int defer_termination_cancel_task ( void *  data)
static

Definition at line 511 of file res_pjsip_refer.c.

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

Referenced by refer_attended_task().

512 {
513  struct ast_sip_session *session = data;
514 
517  ao2_ref(session, -1);
518  return 0;
519 }
A structure describing a SIP session.
static struct ast_mansession session
void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
Cancel a pending deferred termination.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
End the session if it had been previously deferred.

◆ dlg_releaser_task()

static int dlg_releaser_task ( void *  data)
static

Definition at line 343 of file res_pjsip_refer.c.

References refer_progress_module.

Referenced by refer_progress_destroy().

343  {
344  pjsip_dlg_dec_session((pjsip_dialog *)data, &refer_progress_module);
345  return 0;
346 }
static pjsip_module refer_progress_module
REFER Progress module, used to attach REFER progress structure to subscriptions.

◆ invite_replaces()

static int invite_replaces ( void *  data)
static

Task for invite replaces.

Definition at line 954 of file res_pjsip_refer.c.

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

955 {
956  struct invite_replaces *invite = data;
957 
958  if (!invite->session->channel) {
959  return -1;
960  }
961 
962  ast_channel_ref(invite->session->channel);
963  invite->channel = invite->session->channel;
964 
966  return 0;
967 }
Structure used to retrieve channel from another session.
struct ast_bridge * bridge
Bridge the channel is in.
struct ast_bridge * ast_bridge_transfer_acquire_bridge(struct ast_channel *chan)
Acquire the channel&#39;s bridge for transfer purposes.
Definition: bridge.c:4460
struct ast_channel * channel
struct ast_sip_session * session
Session we want the channel from.
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2970
struct ast_channel * channel
Channel from the session (with reference)

◆ load_module()

static int load_module ( void  )
static

Definition at line 1262 of file res_pjsip_refer.c.

References AST_MODULE_LOAD_SUCCESS, ast_module_shutdown_ref, ast_sip_get_norefersub(), ast_sip_get_pjsip_endpoint(), ast_sip_register_service(), ast_sip_session_register_supplement, NULL, refer_progress_module, and ast_module_info::self.

Referenced by unload_module().

1263 {
1264  const pj_str_t str_norefersub = { "norefersub", 10 };
1265 
1266  pjsip_replaces_init_module(ast_sip_get_pjsip_endpoint());
1267  pjsip_xfer_init_module(ast_sip_get_pjsip_endpoint());
1268 
1269  if (ast_sip_get_norefersub()) {
1270  pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_norefersub);
1271  }
1272 
1275 
1277 
1278  return AST_MODULE_LOAD_SUCCESS;
1279 }
static struct ast_sip_session_supplement refer_supplement
unsigned int ast_sip_get_norefersub(void)
Retrieve the global setting &#39;norefersub&#39;.
#define NULL
Definition: resample.c:96
static pjsip_module refer_progress_module
REFER Progress module, used to attach REFER progress structure to subscriptions.
struct ast_module * self
Definition: module.h:342
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition: res_pjsip.c:3315
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:464
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:3718
#define ast_sip_session_register_supplement(supplement)

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

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().

478 {
479  struct refer_attended *attended;
480 
481  attended = ao2_alloc_options(sizeof(*attended), refer_attended_destroy,
483  if (!attended) {
484  return NULL;
485  }
486 
487  ao2_ref(transferer, +1);
488  attended->transferer = transferer;
489  ast_channel_ref(transferer->channel);
490  attended->transferer_chan = transferer->channel;
491  ao2_ref(transferer_second, +1);
493 
494  if (progress) {
495  ao2_ref(progress, +1);
496  attended->progress = progress;
497  }
498 
499  return attended;
500 }
struct refer_progress * progress
Optional refer progress structure.
struct ast_sip_session * transferer
Transferer session.
struct ast_sip_session * transferer_second
Second transferer session.
Structure for attended transfer task.
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:406
static void refer_attended_destroy(void *obj)
Destructor for attended transfer task.
#define NULL
Definition: resample.c:96
#define ao2_ref(o, delta)
Definition: astobj2.h:464
struct ast_channel * channel
#define ast_channel_ref(c)
Increase channel reference count.
Definition: channel.h:2970
struct ast_channel * transferer_chan
Transferer channel.

◆ refer_attended_destroy()

static void refer_attended_destroy ( void *  obj)
static

Destructor for attended transfer task.

Definition at line 464 of file res_pjsip_refer.c.

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().

465 {
466  struct refer_attended *attended = obj;
467 
468  ao2_cleanup(attended->transferer);
470  ao2_cleanup(attended->transferer_second);
471  ao2_cleanup(attended->progress);
472 }
struct refer_progress * progress
Optional refer progress structure.
struct ast_sip_session * transferer
Transferer session.
struct ast_sip_session * transferer_second
Second transferer session.
Structure for attended transfer task.
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2992
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct ast_channel * transferer_chan
Transferer channel.

◆ refer_attended_task()

static int refer_attended_task ( void *  data)
static

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

Definition at line 553 of file res_pjsip_refer.c.

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::serializer, ast_sip_session::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().

554 {
555  struct refer_attended *attended = data;
556  int response;
557  int (*task_cb)(void *data);
558 
559  if (attended->transferer_second->channel) {
560  ast_debug(3, "Performing a REFER attended transfer - Transferer #1: %s Transferer #2: %s\n",
563 
565  attended->transferer_chan,
566  attended->transferer_second->channel));
567 
568  ast_debug(3, "Final response for REFER attended transfer - Transferer #1: %s Transferer #2: %s is '%d'\n",
571  response);
572  } else {
573  ast_debug(3, "Received REFER request on channel '%s' but other channel has gone.\n",
574  ast_channel_name(attended->transferer_chan));
575  response = 603;
576  }
577 
578  if (attended->progress) {
579  struct refer_progress_notification *notification;
580 
581  notification = refer_progress_notification_alloc(attended->progress, response,
582  PJSIP_EVSUB_STATE_TERMINATED);
583  if (notification) {
584  if (ast_sip_push_task(attended->progress->serializer, refer_progress_notify, notification)) {
585  ao2_cleanup(notification);
586  }
587  }
588  }
589 
590  if (response == 200) {
592  } else {
594  }
595  if (!ast_sip_push_task(attended->transferer->serializer,
596  task_cb, attended->transferer)) {
597  /* Gave the ref to the pushed task. */
598  attended->transferer = NULL;
599  } else {
600  /* Do this anyway even though it is the wrong serializer. */
602  }
603 
604  ao2_ref(attended, -1);
605  return 0;
606 }
enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee, struct ast_channel *to_transfer_target)
Attended transfer.
Definition: bridge.c:4729
struct refer_progress * progress
Optional refer progress structure.
struct ast_sip_session * transferer
Transferer session.
struct ast_sip_session * transferer_second
Second transferer session.
struct ast_taskprocessor * serializer
Serializer for notifications.
Structure for attended transfer task.
REFER Progress notification structure.
static int defer_termination_cancel_task(void *data)
#define NULL
Definition: resample.c:96
static int xfer_response_code2sip(enum ast_transfer_result xfer_code)
static int session_end_if_deferred_task(void *data)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ao2_ref(o, delta)
Definition: astobj2.h:464
struct ast_channel * channel
struct ast_taskprocessor * serializer
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
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.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
End the session if it had been previously deferred.
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_channel * transferer_chan
Transferer channel.

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

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

Referenced by refer_incoming_attended_request(), and refer_incoming_blind_request().

627 {
628  struct refer_blind *refer = user_data_wrapper->data;
629  pjsip_generic_string_hdr *referred_by;
630 
631  static const pj_str_t str_referred_by = { "Referred-By", 11 };
632  static const pj_str_t str_referred_by_s = { "b", 1 };
633 
634  pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes");
635 
636  if (refer->progress && !refer->attended && !refer->progress->refer_blind_progress) {
637  /* If blind transfer and endpoint doesn't want to receive all the progress details */
639  PJSIP_EVSUB_STATE_TERMINATED);
640 
641  if (notification) {
642  if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
643  ao2_cleanup(notification);
644  }
645  }
646  } else if (refer->progress) {
647  /* If attended transfer and progress monitoring is being done attach a frame hook so we can monitor it */
648  struct ast_framehook_interface hook = {
650  .event_cb = refer_progress_framehook,
651  .destroy_cb = refer_progress_framehook_destroy,
652  .data = refer->progress,
653  .disable_inheritance = 1,
654  };
655 
657  if (!refer->progress->transferee) {
659  PJSIP_EVSUB_STATE_TERMINATED);
660 
661  ast_log(LOG_WARNING, "Could not copy channel name '%s' during transfer - assuming success\n",
662  ast_channel_name(chan));
663 
664  if (notification) {
665  if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
666  ao2_cleanup(notification);
667  }
668  }
669  }
670 
671  /* Progress needs a reference to the transfer_channel_data so that it can track the completed status of the transfer */
672  ao2_ref(user_data_wrapper, +1);
673  refer->progress->transfer_data = user_data_wrapper;
674 
675  /* We need to bump the reference count up on the progress structure since it is in the frame hook now */
676  ao2_ref(refer->progress, +1);
677 
678  /* If we can't attach a frame hook for whatever reason send a notification of success immediately */
679  ast_channel_lock(chan);
680  refer->progress->framehook = ast_framehook_attach(chan, &hook);
681  ast_channel_unlock(chan);
682  if (refer->progress->framehook < 0) {
684  PJSIP_EVSUB_STATE_TERMINATED);
685 
686  ast_log(LOG_WARNING, "Could not attach REFER transfer progress monitoring hook to channel '%s' - assuming success\n",
687  ast_channel_name(chan));
688 
689  if (notification) {
690  if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
691  ao2_cleanup(notification);
692  }
693  }
694 
695  ao2_cleanup(refer->progress);
696  }
697 
698  /* We need to bump the reference count for the stasis subscription */
699  ao2_ref(refer->progress, +1);
700  /* We also will need to detect if the transferee enters a bridge. This is currently the only reliable way to
701  * detect if the transfer target has answered the call
702  */
704  if (!refer->progress->bridge_sub) {
706  PJSIP_EVSUB_STATE_TERMINATED);
707 
708  ast_log(LOG_WARNING, "Could not create bridge stasis subscription for monitoring progress on transfer of channel '%s' - assuming success\n",
709  ast_channel_name(chan));
710 
711  if (notification) {
712  if (ast_sip_push_task(refer->progress->serializer, refer_progress_notify, notification)) {
713  ao2_cleanup(notification);
714  }
715  }
716 
717  ast_channel_lock(chan);
718  ast_framehook_detach(chan, refer->progress->framehook);
719  ast_channel_unlock(chan);
720 
721  ao2_cleanup(refer->progress);
722  } else {
726  }
727  }
728 
729  pbx_builtin_setvar_helper(chan, "SIPREFERRINGCONTEXT", S_OR(refer->context, NULL));
730 
731  referred_by = pjsip_msg_find_hdr_by_names(refer->rdata->msg_info.msg,
732  &str_referred_by, &str_referred_by_s, NULL);
733  if (referred_by) {
734  size_t uri_size = pj_strlen(&referred_by->hvalue) + 1;
735  char *uri = ast_alloca(uri_size);
736 
737  ast_copy_pj_str(uri, &referred_by->hvalue, uri_size);
738  pbx_builtin_setvar_helper(chan, "__SIPREFERREDBYHDR", S_OR(uri, NULL));
739  } else {
740  pbx_builtin_setvar_helper(chan, "SIPREFERREDBYHDR", NULL);
741  }
742 
743  if (refer->replaces) {
744  char replaces[512];
745  char *replaces_val = NULL;
746  int len;
747 
748  len = pjsip_hdr_print_on(refer->replaces, replaces, sizeof(replaces) - 1);
749  if (len != -1) {
750  /* pjsip_hdr_print_on does not NULL terminate the buffer */
751  replaces[len] = '\0';
752  replaces_val = replaces + sizeof("Replaces:");
753  }
754  pbx_builtin_setvar_helper(chan, "__SIPREPLACESHDR", replaces_val);
755  } else {
756  pbx_builtin_setvar_helper(chan, "SIPREPLACESHDR", NULL);
757  }
758 
759  if (refer->refer_to) {
760  char refer_to[PJSIP_MAX_URL_SIZE];
761 
762  pjsip_uri_print(PJSIP_URI_IN_REQ_URI, refer->refer_to, refer_to, sizeof(refer_to));
763  pbx_builtin_setvar_helper(chan, "SIPREFERTOHDR", S_OR(refer_to, NULL));
764  } else {
765  pbx_builtin_setvar_helper(chan, "SIPREFERTOHDR", NULL);
766  }
767 }
#define ast_channel_lock(chan)
Definition: channel.h:2945
struct stasis_message_type * ast_channel_entered_bridge_type(void)
Message type for channel enter bridge blob messages.
struct ast_taskprocessor * serializer
Serializer for notifications.
struct stasis_subscription * bridge_sub
Stasis subscription for bridge events.
#define LOG_WARNING
Definition: logger.h:274
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.
struct transfer_channel_data * transfer_data
Reference to transfer_channel_data related to the refer.
Structure for blind transfer callback details.
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
static void refer_progress_bridge(void *data, struct stasis_subscription *sub, struct stasis_message *message)
int framehook
Frame hook for monitoring REFER progress.
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:1079
REFER Progress notification structure.
unsigned int refer_blind_progress
Whether to notifies all the progress details on blind transfer.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
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:5240
#define NULL
Definition: resample.c:96
struct refer_progress * progress
Optional progress structure.
unsigned int attended
Attended transfer flag.
char * transferee
Uniqueid of transferee channel.
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_log
Definition: astobj2.c:42
const char * context
Context being used for transfer.
pjsip_sip_uri * refer_to
Optional Refer-To header.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char * ast_channel_uniqueid(const struct ast_channel *chan)
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
#define AST_FRAMEHOOK_INTERFACE_VERSION
Definition: framehook.h:227
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
pjsip_replaces_hdr * replaces
Optional Replaces header.
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:682
#define ast_channel_unlock(chan)
Definition: channel.h:2946
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.
struct stasis_topic * ast_bridge_topic_all(void)
A topic which publishes the events for all bridges.
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.
pjsip_rx_data * rdata
REFER message.
static int refer_progress_notify(void *data)
Serialized callback for subscription notification.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
const char * ast_channel_name(const struct ast_channel *chan)
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:1025
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.

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

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, ast_sip_session::channel, context, refer_blind::context, DETERMINE_TRANSFER_CONTEXT, refer_progress::dlg, ast_sip_session::endpoint, LOG_ERROR, NULL, refer_blind::progress, RAII_VAR, refer_progress::rdata, refer_blind::rdata, refer_attended_alloc(), refer_attended_task(), refer_blind_callback(), refer_blind::refer_to, refer_blind::replaces, and xfer_response_code2sip().

Referenced by refer_incoming_refer_request().

791 {
792  const pj_str_t str_replaces = { "Replaces", 8 };
793  pj_str_t replaces_content;
794  pjsip_replaces_hdr *replaces;
795  int parsed_len;
796  pjsip_dialog *dlg;
797 
798  pj_strdup_with_null(rdata->tp_info.pool, &replaces_content, &replaces_param->value);
799 
800  /* Parsing the parameter as a Replaces header easily grabs the needed information */
801  if (!(replaces = pjsip_parse_hdr(rdata->tp_info.pool, &str_replaces, replaces_content.ptr,
802  pj_strlen(&replaces_content), &parsed_len))) {
803  ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' with invalid Replaces header, rejecting\n",
805  return 400;
806  }
807 
808  /* See if the dialog is local, or remote */
809  if ((dlg = pjsip_ua_find_dialog(&replaces->call_id, &replaces->to_tag, &replaces->from_tag, PJ_TRUE))) {
810  RAII_VAR(struct ast_sip_session *, other_session, ast_sip_dialog_get_session(dlg), ao2_cleanup);
811  struct refer_attended *attended;
812 
813  pjsip_dlg_dec_lock(dlg);
814 
815  if (!other_session) {
816  ast_debug(3, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but no session exists on it\n",
818  return 603;
819  }
820 
821  /* We defer actually doing the attended transfer to the other session so no deadlock can occur */
822  if (!(attended = refer_attended_alloc(session, other_session, progress))) {
823  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",
825  return 500;
826  }
827 
828  if (ast_sip_session_defer_termination(session)) {
829  ast_log(LOG_ERROR, "Received REFER request on channel '%s' from endpoint '%s' for local dialog but could not defer termination, rejecting\n",
831  ao2_cleanup(attended);
832  return 500;
833  }
834 
835  /* Push it to the other session, which will have both channels with minimal locking */
836  if (ast_sip_push_task(other_session->serializer, refer_attended_task, attended)) {
839  ao2_cleanup(attended);
840  return 500;
841  }
842 
843  ast_debug(3, "Attended transfer from '%s' pushed to second channel serializer\n",
844  ast_channel_name(session->channel));
845 
846  return 200;
847  } else {
848  const char *context;
849  struct refer_blind refer = { 0, };
850  int response;
851 
852  DETERMINE_TRANSFER_CONTEXT(context, session);
853 
854  if (!ast_exists_extension(NULL, context, "external_replaces", 1, NULL)) {
855  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",
856  ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), context);
857  return 404;
858  }
859 
860  refer.context = context;
861  refer.progress = progress;
862  refer.rdata = rdata;
863  refer.replaces = replaces;
864  refer.refer_to = target_uri;
865  refer.attended = 1;
866 
867  if (ast_sip_session_defer_termination(session)) {
868  ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but could not defer termination, rejecting\n",
869  ast_channel_name(session->channel),
871  return 500;
872  }
873 
875  "external_replaces", context, refer_blind_callback, &refer));
876 
878  if (response != 200) {
880  }
881 
882  return response;
883  }
884 }
struct ast_sip_endpoint * endpoint
Structure for blind transfer callback details.
Structure for attended transfer task.
#define NULL
Definition: resample.c:96
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.
A structure describing a SIP session.
struct refer_progress * progress
Optional progress structure.
unsigned int attended
Attended transfer flag.
static int xfer_response_code2sip(enum ast_transfer_result xfer_code)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
const char * context
Context being used for transfer.
pjsip_sip_uri * refer_to
Optional Refer-To header.
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:4477
#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:911
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.
void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
Cancel a pending deferred termination.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ast_channel * channel
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:4179
#define DETERMINE_TRANSFER_CONTEXT(context, session)
#define LOG_ERROR
Definition: logger.h:285
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
pjsip_replaces_hdr * replaces
Optional Replaces header.
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...
pjsip_rx_data * rdata
REFER message.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
End the session if it had been previously deferred.
const char * ast_channel_name(const struct ast_channel *chan)
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
struct ast_sip_session * ast_sip_dialog_get_session(pjsip_dialog *dlg)
Retrieves a session from a dialog.

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

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, ast_sip_session::channel, context, refer_blind::context, DETERMINE_TRANSFER_CONTEXT, ast_sip_session::endpoint, exten, LOG_ERROR, NULL, refer_blind::progress, refer_progress::rdata, refer_blind::rdata, refer_blind_callback(), refer_blind::refer_to, and xfer_response_code2sip().

Referenced by refer_incoming_refer_request().

888 {
889  const char *context;
890  char exten[AST_MAX_EXTENSION];
891  struct refer_blind refer = { 0, };
892  int response;
893 
894  /* If no explicit transfer context has been provided use their configured context */
895  DETERMINE_TRANSFER_CONTEXT(context, session);
896 
897  /* Using the user portion of the target URI see if it exists as a valid extension in their context */
898  ast_copy_pj_str(exten, &target->user, sizeof(exten));
899 
900  /*
901  * We may want to match in the dialplan without any user
902  * options getting in the way.
903  */
905 
906  /* Uri without exten */
907  if (ast_strlen_zero(exten)) {
908  ast_copy_string(exten, "s", sizeof(exten));
909  ast_debug(3, "Channel '%s' from endpoint '%s' attempted blind transfer to a target without extension. Target was set to 's@%s'\n",
910  ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), context);
911  }
912 
913  if (!ast_exists_extension(NULL, context, exten, 1, NULL)) {
914  ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer to '%s@%s' but target does not exist\n",
915  ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), exten, context);
916  return 404;
917  }
918 
919  refer.context = context;
920  refer.progress = progress;
921  refer.rdata = rdata;
922  refer.refer_to = target;
923  refer.attended = 0;
924 
925  if (ast_sip_session_defer_termination(session)) {
926  ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer but could not defer termination, rejecting\n",
927  ast_channel_name(session->channel),
929  return 500;
930  }
931 
933  exten, context, refer_blind_callback, &refer));
934 
936  if (response != 200) {
938  }
939 
940  return response;
941 }
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:118
struct ast_sip_endpoint * endpoint
#define AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(str)
Truncate the URI user field options string if enabled.
Definition: res_pjsip.h:3036
Structure for blind transfer callback details.
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:5240
#define NULL
Definition: resample.c:96
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.
struct refer_progress * progress
Optional progress structure.
unsigned int attended
Attended transfer flag.
#define ast_strlen_zero(foo)
Definition: strings.h:52
static int xfer_response_code2sip(enum ast_transfer_result xfer_code)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
const char * context
Context being used for transfer.
pjsip_sip_uri * refer_to
Optional Refer-To header.
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:4477
#define AST_MAX_EXTENSION
Definition: channel.h:135
void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
Cancel a pending deferred termination.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ast_channel * channel
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:4179
#define DETERMINE_TRANSFER_CONTEXT(context, session)
#define LOG_ERROR
Definition: logger.h:285
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...
pjsip_rx_data * rdata
REFER message.
void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
End the session if it had been previously deferred.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
const char * ast_channel_name(const struct ast_channel *chan)
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116

◆ refer_incoming_invite_request()

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

Definition at line 969 of file res_pjsip_refer.c.

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_queue_hangup(), 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, ast_sip_session::channel, invite_replaces::channel, ast_sip_session::defer_terminate, ast_sip_session::endpoint, ast_sip_session::inv_session, NULL, RAII_VAR, and invite_replaces::session.

Referenced by refer_incoming_request().

970 {
971  pjsip_dialog *other_dlg = NULL;
972  pjsip_tx_data *packet;
973  int response = 0;
974  RAII_VAR(struct ast_sip_session *, other_session, NULL, ao2_cleanup);
975  struct invite_replaces invite;
976 
977  /* If a Replaces header is present make sure it is valid */
978  if (pjsip_replaces_verify_request(rdata, &other_dlg, PJ_TRUE, &packet) != PJ_SUCCESS) {
979  response = packet->msg->line.status.code;
980  ast_assert(response != 0);
981  pjsip_tx_data_dec_ref(packet);
982  goto inv_replace_failed;
983  }
984 
985  /* If no other dialog exists then this INVITE request does not have a Replaces header */
986  if (!other_dlg) {
987  return 0;
988  }
989 
990  other_session = ast_sip_dialog_get_session(other_dlg);
991  pjsip_dlg_dec_lock(other_dlg);
992 
993  /* Don't accept an in-dialog INVITE with Replaces as it does not make much sense */
994  if (session->inv_session->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
995  response = 488;
996  goto inv_replace_failed;
997  }
998 
999  if (!other_session) {
1000  ast_debug(3, "INVITE with Replaces received on channel '%s' from endpoint '%s', but requested session does not exist\n",
1002  response = 481;
1003  goto inv_replace_failed;
1004  }
1005 
1006  invite.session = other_session;
1007 
1008  if (ast_sip_push_task_wait_serializer(other_session->serializer, invite_replaces,
1009  &invite)) {
1010  response = 481;
1011  goto inv_replace_failed;
1012  }
1013 
1014  ast_channel_lock(session->channel);
1015  ast_setstate(session->channel, AST_STATE_RING);
1016  ast_channel_unlock(session->channel);
1017  ast_raw_answer(session->channel);
1018 
1019  ast_debug(3, "INVITE with Replaces being attempted. '%s' --> '%s'\n",
1020  ast_channel_name(session->channel), ast_channel_name(invite.channel));
1021 
1022  if (!invite.bridge) {
1023  struct ast_channel *chan = session->channel;
1024 
1025  /*
1026  * This will use a synchronous task but we aren't operating in
1027  * the serializer at this point in time, so it won't deadlock.
1028  */
1029  if (!ast_channel_move(invite.channel, chan)) {
1030  /*
1031  * We can't directly use session->channel because ast_channel_move()
1032  * does a masquerade which changes session->channel to a different
1033  * channel. To ensure we work on the right channel we store a
1034  * pointer locally before we begin so it remains valid.
1035  */
1036  ast_hangup(chan);
1037  } else {
1038  response = AST_CAUSE_FAILURE;
1039  }
1040  } else {
1041  if (ast_bridge_impart(invite.bridge, session->channel, invite.channel, NULL,
1043  response = AST_CAUSE_FAILURE;
1044  }
1045  }
1046 
1047  ast_channel_unref(invite.channel);
1048  ao2_cleanup(invite.bridge);
1049 
1050  if (!response) {
1051  /*
1052  * On success we cannot use session->channel in the debug message.
1053  * This thread either no longer has a ref to session->channel or
1054  * session->channel is no longer the original channel.
1055  */
1056  ast_debug(3, "INVITE with Replaces successfully completed.\n");
1057  } else {
1058  ast_debug(3, "INVITE with Replaces failed on channel '%s', hanging up with cause '%d'\n",
1059  ast_channel_name(session->channel), response);
1060  ast_channel_lock(session->channel);
1061  ast_channel_hangupcause_set(session->channel, response);
1062  ast_channel_unlock(session->channel);
1063  ast_hangup(session->channel);
1064  }
1065 
1066  return 1;
1067 
1068 inv_replace_failed:
1069  if (session->inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
1070  ast_debug(3, "INVITE with Replaces failed on channel '%s', sending response of '%d'\n",
1071  ast_channel_name(session->channel), response);
1072  session->defer_terminate = 1;
1073  ast_hangup(session->channel);
1074 
1075  if (pjsip_inv_end_session(session->inv_session, response, NULL, &packet) == PJ_SUCCESS
1076  && packet) {
1077  ast_sip_session_send_response(session, packet);
1078  }
1079  } else {
1080  ast_debug(3, "INVITE with Replaces in-dialog on channel '%s', hanging up\n",
1081  ast_channel_name(session->channel));
1082  ast_queue_hangup(session->channel);
1083  }
1084 
1085  return 1;
1086 }
int ast_queue_hangup(struct ast_channel *chan)
Queue a hangup frame.
Definition: channel.c:1150
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
struct ast_sip_endpoint * endpoint
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2981
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
unsigned int defer_terminate
Structure used to retrieve channel from another session.
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
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:10867
struct pjsip_inv_session * inv_session
A structure describing a SIP session.
int ast_raw_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2699
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
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:1924
#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:911
int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to the serializer and wait for it to complete.
Definition: res_pjsip.c:5218
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ast_channel * channel
void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
Send a SIP response.
#define AST_CAUSE_FAILURE
Definition: causes.h:149
#define ast_channel_unlock(chan)
Definition: channel.h:2946
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2548
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
int ast_setstate(struct ast_channel *chan, enum ast_channel_state)
Change the state of a channel.
Definition: channel.c:7486
struct ast_sip_session * ast_sip_dialog_get_session(pjsip_dialog *dlg)
Retrieves a session from a dialog.

◆ refer_incoming_refer_request()

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

Definition at line 1088 of file res_pjsip_refer.c.

References ast_sip_endpoint::allowtransfer, 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_sip_session::channel, ast_sip_session::endpoint, ast_sip_session::inv_session, LOG_WARNING, NULL, RAII_VAR, refer_incoming_attended_request(), refer_incoming_blind_request(), refer_progress_alloc(), refer_progress_notification_alloc(), and refer_progress_notify().

Referenced by refer_incoming_request().

1089 {
1090  pjsip_generic_string_hdr *refer_to;
1091  char *uri;
1092  size_t uri_size;
1093  pjsip_uri *target;
1094  pjsip_sip_uri *target_uri;
1096  pjsip_param *replaces;
1097  int response;
1098 
1099  static const pj_str_t str_refer_to = { "Refer-To", 8 };
1100  static const pj_str_t str_refer_to_s = { "r", 1 };
1101  static const pj_str_t str_replaces = { "Replaces", 8 };
1102 
1103  if (!session->channel) {
1104  /* No channel to refer. Likely because the call was just hung up. */
1105  pjsip_dlg_respond(session->inv_session->dlg, rdata, 404, NULL, NULL, NULL);
1106  ast_debug(3, "Received a REFER on a session with no channel from endpoint '%s'.\n",
1108  return 0;
1109  }
1110 
1111  if (!session->endpoint->allowtransfer) {
1112  pjsip_dlg_respond(session->inv_session->dlg, rdata, 603, NULL, NULL, NULL);
1113  ast_log(LOG_WARNING, "Endpoint %s transfer attempt blocked due to configuration\n",
1115  return 0;
1116  }
1117 
1118  /* A Refer-To header is required */
1119  refer_to = pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &str_refer_to, &str_refer_to_s, NULL);
1120  if (!refer_to) {
1121  pjsip_dlg_respond(session->inv_session->dlg, rdata, 400, NULL, NULL, NULL);
1122  ast_debug(3, "Received a REFER without Refer-To on channel '%s' from endpoint '%s'\n",
1124  return 0;
1125  }
1126 
1127  /* The ast_copy_pj_str to uri is needed because it puts the NULL terminator to the uri
1128  * as pjsip_parse_uri require a NULL terminated uri
1129  */
1130 
1131  uri_size = pj_strlen(&refer_to->hvalue) + 1;
1132  uri = ast_alloca(uri_size);
1133  ast_copy_pj_str(uri, &refer_to->hvalue, uri_size);
1134 
1135  target = pjsip_parse_uri(rdata->tp_info.pool, uri, uri_size - 1, 0);
1136 
1137  if (!target
1138  || (!PJSIP_URI_SCHEME_IS_SIP(target)
1139  && !PJSIP_URI_SCHEME_IS_SIPS(target))) {
1140 
1141  pjsip_dlg_respond(session->inv_session->dlg, rdata, 400, NULL, NULL, NULL);
1142  ast_debug(3, "Received a REFER without a parseable Refer-To ('%s') on channel '%s' from endpoint '%s'\n",
1143  uri, ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint));
1144  return 0;
1145  }
1146  target_uri = pjsip_uri_get_uri(target);
1147 
1148  /* Set up REFER progress subscription if requested/possible */
1149  if (refer_progress_alloc(session, rdata, &progress)) {
1150  pjsip_dlg_respond(session->inv_session->dlg, rdata, 500, NULL, NULL, NULL);
1151  ast_debug(3, "Could not set up subscription for REFER on channel '%s' from endpoint '%s'\n",
1153  return 0;
1154  }
1155 
1156  /* Determine if this is an attended or blind transfer */
1157  if ((replaces = pjsip_param_find(&target_uri->header_param, &str_replaces)) ||
1158  (replaces = pjsip_param_find(&target_uri->other_param, &str_replaces))) {
1159  response = refer_incoming_attended_request(session, rdata, target_uri, replaces, progress);
1160  } else {
1161  response = refer_incoming_blind_request(session, rdata, target_uri, progress);
1162  }
1163 
1164  if (!progress) {
1165  /* The transferer has requested no subscription, so send a final response immediately */
1166  pjsip_tx_data *tdata;
1167  const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
1168  const pj_str_t str_false = { "false", 5 };
1169  pjsip_hdr *hdr;
1170 
1171  ast_debug(3, "Progress monitoring not requested for REFER on channel '%s' from endpoint '%s', sending immediate response of '%d'\n",
1172  ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), response);
1173 
1174  if (pjsip_dlg_create_response(session->inv_session->dlg, rdata, response, NULL, &tdata) != PJ_SUCCESS) {
1175  pjsip_dlg_respond(session->inv_session->dlg, rdata, response, NULL, NULL, NULL);
1176  return 0;
1177  }
1178 
1179  hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, &str_false);
1180  pjsip_msg_add_hdr(tdata->msg, hdr);
1181 
1182  pjsip_dlg_send_response(session->inv_session->dlg, pjsip_rdata_get_tsx(rdata), tdata);
1183  } else if (response != 200) {
1184  /* Since this failed we can send a final NOTIFY now and terminate the subscription */
1185  struct refer_progress_notification *notification = refer_progress_notification_alloc(progress, response, PJSIP_EVSUB_STATE_TERMINATED);
1186 
1187  if (notification) {
1188  /* The refer_progress_notify function will call ao2_cleanup on this for us */
1189  if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
1190  ao2_cleanup(notification);
1191  }
1192  }
1193  }
1194 
1195  return 0;
1196 }
struct ast_sip_endpoint * endpoint
#define LOG_WARNING
Definition: logger.h:274
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)
REFER Progress notification structure.
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:5240
#define NULL
Definition: resample.c:96
Definition: dsp.c:117
struct pjsip_inv_session * inv_session
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#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:911
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ast_channel * channel
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
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.
unsigned int allowtransfer
Definition: res_pjsip.h:871
static int refer_progress_notify(void *data)
Serialized callback for subscription notification.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_rx_data *rdata, pjsip_sip_uri *target, struct refer_progress *progress)
REFER Progress structure.
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.

◆ refer_incoming_request()

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

Definition at line 1198 of file res_pjsip_refer.c.

References refer_incoming_invite_request(), and refer_incoming_refer_request().

1199 {
1200  if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_refer_method())) {
1201  return refer_incoming_refer_request(session, rdata);
1202  } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_invite_method)) {
1203  return refer_incoming_invite_request(session, rdata);
1204  } else {
1205  return 0;
1206  }
1207 }
static int refer_incoming_refer_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
static int refer_incoming_invite_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)

◆ refer_outgoing_request()

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

Definition at line 1242 of file res_pjsip_refer.c.

References add_header_from_channel_var(), ast_channel_lock, ast_channel_unlock, ast_sip_session::channel, and ast_sip_session::inv_session.

1243 {
1244  if (pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_invite_method)
1245  || !session->channel
1246  || session->inv_session->state != PJSIP_INV_STATE_NULL) {
1247  return;
1248  }
1249 
1250  ast_channel_lock(session->channel);
1251  add_header_from_channel_var(session->channel, "SIPREPLACESHDR", "Replaces", tdata);
1252  add_header_from_channel_var(session->channel, "SIPREFERREDBYHDR", "Referred-By", tdata);
1253  ast_channel_unlock(session->channel);
1254 }
#define ast_channel_lock(chan)
Definition: channel.h:2945
struct pjsip_inv_session * inv_session
struct ast_channel * channel
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.
#define ast_channel_unlock(chan)
Definition: channel.h:2946

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

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, ast_sip_session::channel, ast_sip_session::endpoint, error(), ast_sip_session::inv_session, NULL, ast_sip_endpoint::refer_blind_progress, refer_progress_destroy(), refer_progress_evsub_cb, and refer_progress_module.

Referenced by refer_incoming_refer_request().

383 {
384  const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
385  pjsip_generic_string_hdr *refer_sub = NULL;
386  const pj_str_t str_true = { "true", 4 };
387  pjsip_hdr hdr_list;
388  char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
389 
390  *progress = NULL;
391 
392  /* Grab the optional Refer-Sub header, it can be used to suppress the implicit subscription */
393  refer_sub = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
394  if ((refer_sub && pj_strnicmp(&refer_sub->hvalue, &str_true, 4))) {
395  return 0;
396  }
397 
398  if (!(*progress = ao2_alloc(sizeof(struct refer_progress), refer_progress_destroy))) {
399  return -1;
400  }
401 
402  ast_debug(3, "Created progress monitor '%p' for transfer occurring from channel '%s' and endpoint '%s'\n",
403  progress, ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint));
404 
405  (*progress)->refer_blind_progress = session->endpoint->refer_blind_progress;
406 
407  (*progress)->framehook = -1;
408 
409  /* Create name with seq number appended. */
410  ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/refer/%s",
412 
413  if (!((*progress)->serializer = ast_sip_create_serializer(tps_name))) {
414  goto error;
415  }
416 
417  /* Create the implicit subscription for monitoring of this transfer */
418  if (pjsip_xfer_create_uas(session->inv_session->dlg, &refer_progress_evsub_cb, rdata, &(*progress)->sub) != PJ_SUCCESS) {
419  goto error;
420  }
421 
422  /* To prevent a potential deadlock we need the dialog so we can lock/unlock */
423  (*progress)->dlg = session->inv_session->dlg;
424  /* We also need to make sure it stays around until we're done with it */
425  pjsip_dlg_inc_session((*progress)->dlg, &refer_progress_module);
426 
427 
428  /* Associate the REFER progress structure with the subscription */
429  ao2_ref(*progress, +1);
430  pjsip_evsub_set_mod_data((*progress)->sub, refer_progress_module.id, *progress);
431 
432  pj_list_init(&hdr_list);
433  if (refer_sub) {
434  pjsip_hdr *hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(session->inv_session->dlg->pool, &str_refer_sub, &str_true);
435 
436  pj_list_push_back(&hdr_list, hdr);
437  }
438 
439  /* Accept the REFER request */
440  ast_debug(3, "Accepting REFER request for progress monitor '%p'\n", *progress);
441  pjsip_xfer_accept((*progress)->sub, rdata, 202, &hdr_list);
442 
443  return 0;
444 
445 error:
446  ao2_cleanup(*progress);
447  *progress = NULL;
448  return -1;
449 }
struct ast_sip_endpoint * endpoint
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 NULL
Definition: resample.c:96
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:60
struct pjsip_inv_session * inv_session
static pjsip_module refer_progress_module
REFER Progress module, used to attach REFER progress structure to subscriptions.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ao2_ref(o, delta)
Definition: astobj2.h:464
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ast_channel * channel
static void refer_progress_destroy(void *obj)
Destructor for REFER progress sutrcture.
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition: res_pjsip.c:5133
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
unsigned int refer_blind_progress
Definition: res_pjsip.h:895
static pjsip_evsub_user refer_progress_evsub_cb
Callback structure for subscription.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
int error(const char *format,...)
Definition: utils/frame.c:999
REFER Progress structure.

◆ refer_progress_bridge()

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

Definition at line 181 of file res_pjsip_refer.c.

References ao2_cleanup, ao2_ref, ast_channel_entered_bridge_type(), ast_channel_get_by_name(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_channel_unref, ast_debug, ast_framehook_detach(), ast_sip_push_task(), ast_channel_snapshot::base, refer_progress::bridge_sub, ast_bridge_blob::channel, transfer_channel_data::completed, refer_progress::framehook, refer_progress_notification_alloc(), refer_progress_notify(), refer_progress::serializer, stasis_message_data(), stasis_message_type(), stasis_subscription_final_message(), stasis_unsubscribe(), refer_progress::transfer_data, refer_progress::transferee, and ast_channel_snapshot_base::uniqueid.

Referenced by refer_blind_callback().

183 {
184  struct refer_progress *progress = data;
185  struct ast_bridge_blob *enter_blob;
186  struct refer_progress_notification *notification;
187  struct ast_channel *chan;
188 
189  if (stasis_subscription_final_message(sub, message)) {
190  ao2_ref(progress, -1);
191  return;
192  }
193 
195  /* Don't care */
196  return;
197  }
198 
199  enter_blob = stasis_message_data(message);
200  if (strcmp(enter_blob->channel->base->uniqueid, progress->transferee)) {
201  /* Don't care */
202  return;
203  }
204 
205  if (!progress->transfer_data->completed) {
206  /* We can't act on this message because the transfer_channel_data doesn't show that
207  * the transfer is ready to progress */
208  return;
209  }
210 
211  /* OMG the transferee is joining a bridge. His call got answered! */
212  notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
213  if (notification) {
214  if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
215  ao2_cleanup(notification);
216  }
217  progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
218  }
219 
220  chan = ast_channel_get_by_name(progress->transferee);
221  if (!chan) {
222  /* The channel is already gone */
223  return;
224  }
225 
226  ast_channel_lock(chan);
227  ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as it has joined a bridge\n",
228  ast_channel_name(chan));
229  ast_framehook_detach(chan, progress->framehook);
230  ast_channel_unlock(chan);
231 
232  ast_channel_unref(chan);
233 }
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
struct ast_channel_snapshot_base * base
struct stasis_message_type * ast_channel_entered_bridge_type(void)
Message type for channel enter bridge blob messages.
struct ast_channel_snapshot * channel
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2981
struct ast_taskprocessor * serializer
Serializer for notifications.
struct stasis_subscription * bridge_sub
Stasis subscription for bridge events.
struct transfer_channel_data * transfer_data
Reference to transfer_channel_data related to the refer.
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
int framehook
Frame hook for monitoring REFER progress.
REFER Progress notification structure.
const ast_string_field uniqueid
Definition: dsp.c:117
char * transferee
Uniqueid of transferee channel.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ao2_ref(o, delta)
Definition: astobj2.h:464
Blob of data associated with a bridge.
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:973
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:1176
#define ast_channel_unlock(chan)
Definition: channel.h:2946
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.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
REFER Progress structure.

◆ refer_progress_destroy()

static void refer_progress_destroy ( void *  obj)
static

Destructor for REFER progress sutrcture.

Definition at line 349 of file res_pjsip_refer.c.

References ao2_cleanup, ast_free, ast_sip_push_task(), ast_sip_thread_is_servant(), ast_taskprocessor_unreference(), refer_progress::bridge_sub, refer_progress::dlg, dlg_releaser_task(), NULL, refer_progress_module, refer_progress::serializer, stasis_unsubscribe(), refer_progress::transfer_data, and refer_progress::transferee.

Referenced by refer_progress_alloc().

350 {
351  struct refer_progress *progress = obj;
352 
353  if (progress->bridge_sub) {
354  progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
355  }
356 
357  if (progress->dlg) {
358  /*
359  * Although the dlg session count was incremented in a pjsip servant
360  * thread, there's no guarantee that the last thread to unref this progress
361  * object was one. Before we decrement, we need to make sure that this
362  * is either a servant thread or that we push the decrement to a
363  * serializer that is one.
364  *
365  * Because pjsip_dlg_dec_session requires the dialog lock, we don't want
366  * to wait on the task to complete if we had to push it to a serializer.
367  */
369  pjsip_dlg_dec_session(progress->dlg, &refer_progress_module);
370  } else {
372  }
373  }
374 
375  ao2_cleanup(progress->transfer_data);
376 
377  ast_free(progress->transferee);
379 }
struct ast_taskprocessor * serializer
Serializer for notifications.
struct stasis_subscription * bridge_sub
Stasis subscription for bridge events.
struct transfer_channel_data * transfer_data
Reference to transfer_channel_data related to the refer.
static int dlg_releaser_task(void *data)
#define NULL
Definition: resample.c:96
Definition: dsp.c:117
char * transferee
Uniqueid of transferee channel.
static pjsip_module refer_progress_module
REFER Progress module, used to attach REFER progress structure to subscriptions.
int ast_sip_thread_is_servant(void)
Determine if the current thread is a SIP servant thread.
Definition: res_pjsip.c:5321
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:973
#define ast_free(a)
Definition: astmm.h:182
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
pjsip_dialog * dlg
Dialog for subscription.
REFER Progress structure.

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

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(), transfer_channel_data::completed, refer_progress::framehook, ast_frame::frametype, ast_frame_subclass::integer, NULL, refer_progress_notification_alloc(), refer_progress_notify(), refer_progress::serializer, refer_progress_notification::state, refer_progress::subclass, ast_frame::subclass, and refer_progress::transfer_data.

Referenced by refer_blind_callback().

237 {
238  struct refer_progress *progress = data;
239  struct refer_progress_notification *notification = NULL;
240 
241  /* We only care about frames *to* the channel */
242  if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
243  return f;
244  }
245 
246  /* If the completed flag hasn't been raised, skip this pass. */
247  if (!progress->transfer_data->completed) {
248  return f;
249  }
250 
251  /* Determine the state of the REFER based on the control frames (or voice frames) passing */
252  if (f->frametype == AST_FRAME_VOICE && !progress->subclass) {
253  /* Media is passing without progress, this means the call has been answered */
254  progress->subclass = AST_CONTROL_ANSWER;
255  notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
256  } else if (f->frametype == AST_FRAME_CONTROL) {
257  /* Based on the control frame being written we can send a NOTIFY advising of the progress */
259  /* Don't set progress->subclass; an ANSWER can still follow */
260  notification = refer_progress_notification_alloc(progress, 180, PJSIP_EVSUB_STATE_ACTIVE);
261  } else if (f->subclass.integer == AST_CONTROL_BUSY) {
262  progress->subclass = f->subclass.integer;
263  notification = refer_progress_notification_alloc(progress, 486, PJSIP_EVSUB_STATE_TERMINATED);
264  } else if (f->subclass.integer == AST_CONTROL_CONGESTION) {
265  progress->subclass = f->subclass.integer;
266  notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
267  } else if (f->subclass.integer == AST_CONTROL_PROGRESS) {
268  /* Don't set progress->subclass; an ANSWER can still follow */
269  notification = refer_progress_notification_alloc(progress, 183, PJSIP_EVSUB_STATE_ACTIVE);
270  } else if (f->subclass.integer == AST_CONTROL_PROCEEDING) {
271  /* Don't set progress->subclass; an ANSWER can still follow */
272  notification = refer_progress_notification_alloc(progress, 100, PJSIP_EVSUB_STATE_ACTIVE);
273  } else if (f->subclass.integer == AST_CONTROL_ANSWER) {
274  progress->subclass = f->subclass.integer;
275  notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
276  }
277  }
278 
279  /* If a notification is due to be sent push it to the thread pool */
280  if (notification) {
281  /* If the subscription is being terminated we don't need the frame hook any longer */
282  if (notification->state == PJSIP_EVSUB_STATE_TERMINATED) {
283  ast_debug(3, "Detaching REFER progress monitoring hook from '%s' as subscription is being terminated\n",
284  ast_channel_name(chan));
285  ast_framehook_detach(chan, progress->framehook);
286  }
287 
288  if (ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
289  ao2_cleanup(notification);
290  }
291  }
292 
293  return f;
294 }
int subclass
Last received subclass in frame hook.
struct ast_taskprocessor * serializer
Serializer for notifications.
struct transfer_channel_data * transfer_data
Reference to transfer_channel_data related to the refer.
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
int framehook
Frame hook for monitoring REFER progress.
REFER Progress notification structure.
Definition: astman.c:222
#define NULL
Definition: resample.c:96
Definition: dsp.c:117
struct ast_frame_subclass subclass
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
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.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
enum ast_frame_type frametype
pjsip_evsub_state state
Subscription state.
REFER Progress structure.

◆ refer_progress_framehook_destroy()

static void refer_progress_framehook_destroy ( void *  data)
static

Destroy callback for monitoring framehook.

Definition at line 297 of file res_pjsip_refer.c.

References ao2_cleanup, ast_sip_push_task(), refer_progress::bridge_sub, refer_progress_notification_alloc(), refer_progress_notify(), refer_progress::serializer, and stasis_unsubscribe().

Referenced by refer_blind_callback().

298 {
299  struct refer_progress *progress = data;
300  struct refer_progress_notification *notification = refer_progress_notification_alloc(progress, 503, PJSIP_EVSUB_STATE_TERMINATED);
301 
302  if (notification && ast_sip_push_task(progress->serializer, refer_progress_notify, notification)) {
303  ao2_cleanup(notification);
304  }
305 
306  if (progress->bridge_sub) {
307  progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
308  }
309 
310  ao2_cleanup(progress);
311 }
struct ast_taskprocessor * serializer
Serializer for notifications.
struct stasis_subscription * bridge_sub
Stasis subscription for bridge events.
REFER Progress notification structure.
Definition: dsp.c:117
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:973
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.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
REFER Progress structure.

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

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

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

96 {
97  struct refer_progress_notification *notification = ao2_alloc(sizeof(*notification), refer_progress_notification_destroy);
98 
99  if (!notification) {
100  return NULL;
101  }
102 
103  ao2_ref(progress, +1);
104  notification->progress = progress;
105  notification->response = response;
106  notification->state = state;
107 
108  return notification;
109 }
enum sip_cc_notify_state state
Definition: chan_sip.c:959
struct refer_progress * progress
Refer progress structure to send notification on.
static void refer_progress_notification_destroy(void *obj)
Destructor for REFER Progress notification structure.
REFER Progress notification structure.
#define NULL
Definition: resample.c:96
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
int response
SIP response code to send.
pjsip_evsub_state state
Subscription state.

◆ refer_progress_notification_destroy()

static void refer_progress_notification_destroy ( void *  obj)
static

Destructor for REFER Progress notification structure.

Definition at line 86 of file res_pjsip_refer.c.

References ao2_cleanup, and refer_progress_notification::progress.

Referenced by refer_progress_notification_alloc().

87 {
88  struct refer_progress_notification *notification = obj;
89 
90  ao2_cleanup(notification->progress);
91 }
struct refer_progress * progress
Refer progress structure to send notification on.
REFER Progress notification structure.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958

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

References ao2_cleanup, ast_debug, NULL, RAII_VAR, and refer_progress::sub.

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

141 {
142  RAII_VAR(struct refer_progress_notification *, notification, data, ao2_cleanup);
143  pjsip_evsub *sub;
144  pjsip_tx_data *tdata;
145 
146  pjsip_dlg_inc_lock(notification->progress->dlg);
147 
148  /* If the subscription has already been terminated we can't send a notification */
149  if (!(sub = notification->progress->sub)) {
150  ast_debug(3, "Not sending NOTIFY of response '%d' and state '%u' on progress monitor '%p' as subscription has been terminated\n",
151  notification->response, notification->state, notification->progress);
152  pjsip_dlg_dec_lock(notification->progress->dlg);
153  return 0;
154  }
155 
156  /* Send a deferred initial 100 Trying SIP frag NOTIFY if we haven't already. */
157  if (!notification->progress->sent_100) {
158  notification->progress->sent_100 = 1;
159  if (notification->response != 100) {
160  ast_debug(3, "Sending initial 100 Trying NOTIFY for progress monitor '%p'\n",
161  notification->progress);
162  if (pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, 100, NULL, &tdata) == PJ_SUCCESS) {
163  pjsip_xfer_send_request(sub, tdata);
164  }
165  }
166  }
167 
168  ast_debug(3, "Sending NOTIFY with response '%d' and state '%u' on subscription '%p' and progress monitor '%p'\n",
169  notification->response, notification->state, sub, notification->progress);
170 
171  /* Actually send the notification */
172  if (pjsip_xfer_notify(sub, notification->state, notification->response, NULL, &tdata) == PJ_SUCCESS) {
173  pjsip_xfer_send_request(sub, tdata);
174  }
175 
176  pjsip_dlg_dec_lock(notification->progress->dlg);
177 
178  return 0;
179 }
REFER Progress notification structure.
#define NULL
Definition: resample.c:96
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#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:911
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct stasis_forward * sub
Definition: res_corosync.c:240

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

References ao2_cleanup, NULL, refer_progress_module, and refer_progress::sub.

324 {
325  struct refer_progress *progress = pjsip_evsub_get_mod_data(sub, refer_progress_module.id);
326 
327  /*
328  * If being destroyed, remove the progress object from the subscription
329  * and release the reference it had.
330  */
331  if (progress && (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)) {
332  pjsip_evsub_set_mod_data(progress->sub, refer_progress_module.id, NULL);
333  progress->sub = NULL;
334  ao2_cleanup(progress);
335  }
336 }
pjsip_evsub * sub
Subscription to provide updates on.
#define NULL
Definition: resample.c:96
Definition: dsp.c:117
static pjsip_module refer_progress_module
REFER Progress module, used to attach REFER progress structure to subscriptions.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct stasis_forward * sub
Definition: res_corosync.c:240
REFER Progress structure.

◆ session_end_if_deferred_task()

static int session_end_if_deferred_task ( void *  data)
static

Definition at line 502 of file res_pjsip_refer.c.

References ao2_ref, ast_sip_session_end_if_deferred(), and session.

Referenced by refer_attended_task().

503 {
504  struct ast_sip_session *session = data;
505 
507  ao2_ref(session, -1);
508  return 0;
509 }
A structure describing a SIP session.
static struct ast_mansession session
#define ao2_ref(o, delta)
Definition: astobj2.h:464
void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
End the session if it had been previously deferred.

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1281 of file res_pjsip_refer.c.

References AST_MODFLAG_LOAD_ORDER, AST_MODPRI_APP_DEPEND, AST_MODULE_INFO(), AST_MODULE_SUPPORT_CORE, ast_sip_session_unregister_supplement(), ast_sip_unregister_service(), ASTERISK_GPL_KEY, load_module(), and refer_progress_module.

1282 {
1285 
1286  return 0;
1287 }
static struct ast_sip_session_supplement refer_supplement
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.
Definition: pjsip_session.c:63
static pjsip_module refer_progress_module
REFER Progress module, used to attach REFER progress structure to subscriptions.
void ast_sip_unregister_service(pjsip_module *module)
Definition: res_pjsip.c:3331

◆ xfer_response_code2sip()

static int xfer_response_code2sip ( enum ast_transfer_result  xfer_code)
static

Definition at line 530 of file res_pjsip_refer.c.

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().

531 {
532  int response;
533 
534  response = 503;
535  switch (xfer_code) {
537  response = 400;
538  break;
540  response = 403;
541  break;
543  response = 500;
544  break;
546  response = 200;
547  break;
548  }
549  return response;
550 }

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 = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .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 1295 of file res_pjsip_refer.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1295 of file res_pjsip_refer.c.

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

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

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

◆ refer_supplement

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

Definition at line 1256 of file res_pjsip_refer.c.