diff --git a/configs/samples/features.conf.sample b/configs/samples/features.conf.sample
index afdcf5ae4f..05b514e7b9 100644
--- a/configs/samples/features.conf.sample
+++ b/configs/samples/features.conf.sample
@@ -6,7 +6,11 @@
[general]
;transferdigittimeout => 3 ; Number of seconds to wait between digits when transferring a call
- ; (default is 3 seconds)
+ ; (default is 3 seconds). If the TRANSFER_EXTEN dialplan variable has been set
+ ; on the channel of the user that is invoking the transfer feature, then
+ ; this option is not used as the user is transferred directly to the extension
+ ; specified by TRANSFER_EXTEN (the transfer context remains the context specified
+ ; by TRANSFER_CONTEXT, if set, and otherwise the default context).
;xfersound = beep ; to indicate an attended transfer is complete
;xferfailsound = beeperr ; to indicate a failed transfer
;pickupexten = *8 ; Configure the pickup extension. (default is *8)
@@ -26,12 +30,13 @@
; By default, this is 2.
;transferdialattempts = 3 ; Number of times that a transferer may attempt to dial an extension before
; being kicked back to the original call.
+;transferannouncesound = beep ; Sound to play to a transferer to indicate transfer process has begun. If empty, no sound will be played.
;transferretrysound = beep ; Sound to play when a transferer fails to dial a valid extension.
;transferinvalidsound = beeperr ; Sound to play when a transferer fails to dial a valid extension and is out of retries.
;atxferabort = *1 ; cancel the attended transfer
;atxfercomplete = *2 ; complete the attended transfer, dropping out of the call
;atxferthreeway = *3 ; complete the attended transfer, but stay in the call. This will turn the call into a multi-party bridge
-;atxferswap = *4 ; swap to the other party. Once an attended transfer has begun, this options may be used multiple times
+;atxferswap = *4 ; swap to the other party. Once an attended transfer has begun, this option may be used multiple times
; Note that the DTMF features listed below only work when two channels have answered and are bridged together.
; They can not be used while the remote party is ringing or in progress. If you require this feature you can use
diff --git a/doc/CHANGES-staging/transfer.txt b/doc/CHANGES-staging/transfer.txt
new file mode 100644
index 0000000000..962272fcf7
--- /dev/null
+++ b/doc/CHANGES-staging/transfer.txt
@@ -0,0 +1,14 @@
+Subject: Transfer feature
+
+The following capabilities have been added to the
+transfer feature:
+
+- The transfer initiation announcement prompt can
+now be customized in features.conf.
+
+- The TRANSFER_EXTEN variable now can be set on the
+transferer's channel in order to allow the transfer
+function to automatically attempt to go to the extension
+contained in this variable, if it exists. The transfer
+context behavior is not changed (TRANSFER_CONTEXT is used
+if it exists; otherwise the default context is used).
diff --git a/include/asterisk/features_config.h b/include/asterisk/features_config.h
index 52533693b5..9d626ee0b7 100644
--- a/include/asterisk/features_config.h
+++ b/include/asterisk/features_config.h
@@ -72,6 +72,8 @@ struct ast_features_xfer_config {
AST_STRING_FIELD(transferretrysound);
/*! Sound played when an invalid extension is dialed, and the transferer is being returned to the call. */
AST_STRING_FIELD(transferinvalidsound);
+ /*! Sound to play to announce the transfer process has started. */
+ AST_STRING_FIELD_EXTENDED(transferannouncesound);
);
/*! Seconds allowed between digit presses when dialing transfer destination */
unsigned int transferdigittimeout;
diff --git a/main/bridge_basic.c b/main/bridge_basic.c
index df211957f6..c291fb202d 100644
--- a/main/bridge_basic.c
+++ b/main/bridge_basic.c
@@ -1397,6 +1397,27 @@ static const char *get_transfer_context(struct ast_channel *transferer, const ch
return "default";
}
+/*!
+ * \internal
+ * \brief Determine the transfer extension to use.
+ *
+ * \param transferer Channel initiating the transfer.
+ * \param extension User supplied extension if available. May be NULL.
+ *
+ * \return The extension to use for the transfer.
+ */
+static const char *get_transfer_exten(struct ast_channel *transferer, const char *exten)
+{
+ if (!ast_strlen_zero(exten)) {
+ return exten;
+ }
+ exten = pbx_builtin_getvar_helper(transferer, "TRANSFER_EXTEN");
+ if (!ast_strlen_zero(exten)) {
+ return exten;
+ }
+ return ""; /* empty default, to get transfer extension from user now */
+}
+
/*!
* \brief Allocate and initialize attended transfer properties
*
@@ -3162,10 +3183,25 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
int attempts = 0;
int max_attempts;
struct ast_features_xfer_config *xfer_cfg;
- char *retry_sound;
- char *invalid_sound;
+ char *announce_sound, *retry_sound, *invalid_sound;
+ const char *extenoverride;
ast_channel_lock(chan);
+ extenoverride = get_transfer_exten(chan, NULL);
+
+ if (!ast_strlen_zero(extenoverride)) {
+ int extenres = ast_exists_extension(chan, context, extenoverride, 1,
+ S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)) ? 1 : 0;
+ if (extenres) {
+ ast_copy_string(exten, extenoverride, exten_len);
+ ast_channel_unlock(chan);
+ ast_verb(3, "Transfering call to '%s@%s'", exten, context);
+ return 0;
+ }
+ ast_log(LOG_WARNING, "Override extension '%s' does not exist in context '%s'\n", extenoverride, context);
+ /* since we didn't get a valid extension from the channel, fall back and grab it from the user as usual now */
+ }
+
xfer_cfg = ast_get_chan_features_xfer_config(chan);
if (!xfer_cfg) {
ast_log(LOG_ERROR, "Channel %s: Unable to get transfer configuration\n",
@@ -3175,21 +3211,24 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
}
digit_timeout = xfer_cfg->transferdigittimeout * 1000;
max_attempts = xfer_cfg->transferdialattempts;
+ announce_sound = ast_strdupa(xfer_cfg->transferannouncesound);
retry_sound = ast_strdupa(xfer_cfg->transferretrysound);
invalid_sound = ast_strdupa(xfer_cfg->transferinvalidsound);
ao2_ref(xfer_cfg, -1);
ast_channel_unlock(chan);
/* Play the simple "transfer" prompt out and wait */
- res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
- ast_stopstream(chan);
- if (res < 0) {
- /* Hangup or error */
- return -1;
- }
- if (res) {
- /* Store the DTMF digit that interrupted playback of the file. */
- exten[0] = res;
+ if (!ast_strlen_zero(announce_sound)) {
+ res = ast_stream_and_wait(chan, announce_sound, AST_DIGIT_ANY);
+ ast_stopstream(chan);
+ if (res < 0) {
+ /* Hangup or error */
+ return -1;
+ }
+ if (res) {
+ /* Store the DTMF digit that interrupted playback of the file. */
+ exten[0] = res;
+ }
}
/* Drop to dialtone so they can enter the extension they want to transfer to */
diff --git a/main/features_config.c b/main/features_config.c
index ac0135abe3..ba2f905003 100644
--- a/main/features_config.c
+++ b/main/features_config.c
@@ -143,6 +143,9 @@
Sound that is played when an incorrect extension is dialed and the transferer has no attempts remaining.
+
+ Sound that is played to the transferer when a transfer is initiated. If empty, no sound will be played.
+
DTMF options that can be triggered during bridged calls
@@ -324,6 +327,7 @@
+
@@ -387,6 +391,7 @@
#define DEFAULT_TRANSFER_DIAL_ATTEMPTS 3
#define DEFAULT_TRANSFER_RETRY_SOUND "pbx-invalid"
#define DEFAULT_TRANSFER_INVALID_SOUND "privacy-incorrect"
+#define DEFAULT_TRANSFER_ANNOUNCE_SOUND "pbx-transfer"
/*! Default pickup options */
#define DEFAULT_PICKUPEXTEN "*8"
@@ -910,6 +915,8 @@ static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
ast_string_field_set(xfer, transferretrysound, value);
} else if (!strcasecmp(name, "transferinvalidsound")) {
ast_string_field_set(xfer, transferinvalidsound, value);
+ } else if (!strcasecmp(name, "transferannouncesound")) {
+ ast_string_field_set(xfer, transferannouncesound, value);
} else {
/* Unrecognized option */
res = -1;
@@ -1801,6 +1808,8 @@ static int load_config(void)
DEFAULT_TRANSFER_RETRY_SOUND, xfer_handler, 0);
aco_option_register_custom(&cfg_info, "transferinvalidsound", ACO_EXACT, global_options,
DEFAULT_TRANSFER_INVALID_SOUND, xfer_handler, 0);
+ aco_option_register_custom(&cfg_info, "transferannouncesound", ACO_EXACT, global_options,
+ DEFAULT_TRANSFER_ANNOUNCE_SOUND, xfer_handler, 0);
aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
DEFAULT_PICKUPEXTEN, pickup_handler, 0);