diff --git a/CHANGES b/CHANGES index 4d1395fba46d84b9696c035d730b851d245e79c0..63c8c6f7cec8d64a5a2f47fac8a890cd03581a61 100644 --- a/CHANGES +++ b/CHANGES @@ -33,6 +33,10 @@ res_pjsip than one. The removed contact is likely the old contact created by "rewrite_contact" that the device is refreshing. +AMI +------------------ + * Added a new CancelAtxfer action that cancels an attended transfer. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 14 to Asterisk 15 -------------------- ------------------------------------------------------------------------------ diff --git a/include/asterisk/features_config.h b/include/asterisk/features_config.h index baaff183b3061087ced7edd77021d6ed896ba648..1bce50bba23733dcc5803bdf384c0ce4d29c5c0e 100644 --- a/include/asterisk/features_config.h +++ b/include/asterisk/features_config.h @@ -116,6 +116,21 @@ struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_ch */ char *ast_get_chan_features_xferfailsound(struct ast_channel *chan); +/*! + * \brief Get the transfer configuration option atxferabort + * + * \note The channel should be locked before calling this function. + * \note The returned value has to be freed. + * + * If no channel is provided, then option is pulled from the global + * transfer configuration. + * + * \param chan The channel to get configuration options for + * \retval NULL Failed to get configuration + * \retval non-NULL The atxferabort + */ +char *ast_get_chan_features_atxferabort(struct ast_channel *chan); + /*! * \brief Configuration relating to call pickup */ diff --git a/main/features_config.c b/main/features_config.c index 8307b1209186d11ce8d386d835bef00c84e1312b..72cd0404f7a64d14233fe33bb284c85c3055ced2 100644 --- a/main/features_config.c +++ b/main/features_config.c @@ -1173,6 +1173,21 @@ char *ast_get_chan_features_xferfailsound(struct ast_channel *chan) return res; } +char *ast_get_chan_features_atxferabort(struct ast_channel *chan) +{ + char *res; + struct ast_features_xfer_config *cfg = ast_get_chan_features_xfer_config(chan); + + if (!cfg) { + return NULL; + } + + res = ast_strdup(cfg->atxferabort); + ao2_ref(cfg, -1); + + return res; +} + struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan) { RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup); diff --git a/main/manager.c b/main/manager.c index e67efe3f3fc798020be449b69d7877a34f06b6cb..5c0b318b96e756783f1770d874cac92094177c56 100644 --- a/main/manager.c +++ b/main/manager.c @@ -628,6 +628,25 @@ <ref type="managerEvent">AttendedTransfer</ref> </see-also> </manager> + <manager name="CancelAtxfer" language="en_US"> + <synopsis> + Cancel an attended transfer. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /> + <parameter name="Channel" required="true"> + <para>The transferer channel.</para> + </parameter> + </syntax> + <description> + <para>Cancel an attended transfer. Note, this uses the configured cancel attended transfer + feature option (atxferabort) to cancel the transfer. If not available this action will fail. + </para> + </description> + <see-also> + <ref type="managerEvent">AttendedTransfer</ref> + </see-also> + </manager> <manager name="Originate" language="en_US"> <synopsis> Originate a call. @@ -4959,6 +4978,47 @@ static int action_atxfer(struct mansession *s, const struct message *m) return 0; } +static int action_cancel_atxfer(struct mansession *s, const struct message *m) +{ + const char *name = astman_get_header(m, "Channel"); + struct ast_channel *chan = NULL; + char *feature_code; + const char *digit; + + if (ast_strlen_zero(name)) { + astman_send_error(s, m, "No channel specified"); + return 0; + } + + if (!(chan = ast_channel_get_by_name(name))) { + astman_send_error(s, m, "Channel specified does not exist"); + return 0; + } + + ast_channel_lock(chan); + feature_code = ast_get_chan_features_atxferabort(chan); + ast_channel_unlock(chan); + + if (!feature_code) { + astman_send_error(s, m, "No disconnect feature code found"); + ast_channel_unref(chan); + return 0; + } + + for (digit = feature_code; *digit; ++digit) { + struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit }; + ast_queue_frame(chan, &f); + } + ast_free(feature_code); + + chan = ast_channel_unref(chan); + + astman_send_ack(s, m, "CancelAtxfer successfully queued"); + + return 0; +} + + static int check_blacklist(const char *cmd) { char *cmd_copy, *cur_cmd; @@ -8679,6 +8739,7 @@ static void manager_shutdown(void) ast_manager_unregister("ListCategories"); ast_manager_unregister("Redirect"); ast_manager_unregister("Atxfer"); + ast_manager_unregister("CancelAtxfer"); ast_manager_unregister("Originate"); ast_manager_unregister("Command"); ast_manager_unregister("ExtensionState"); @@ -8894,6 +8955,7 @@ static int __init_manager(int reload, int by_external_config) ast_manager_register_xml_core("ListCategories", EVENT_FLAG_CONFIG, action_listcategories); ast_manager_register_xml_core("Redirect", EVENT_FLAG_CALL, action_redirect); ast_manager_register_xml_core("Atxfer", EVENT_FLAG_CALL, action_atxfer); + ast_manager_register_xml_core("CancelAtxfer", EVENT_FLAG_CALL, action_cancel_atxfer); ast_manager_register_xml_core("Originate", EVENT_FLAG_ORIGINATE, action_originate); ast_manager_register_xml_core("Command", EVENT_FLAG_COMMAND, action_command); ast_manager_register_xml_core("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);