diff --git a/doc/CHANGES-staging/ami_sendtext_content_type.txt b/doc/CHANGES-staging/ami_sendtext_content_type.txt new file mode 100644 index 0000000000000000000000000000000000000000..45037ff7a0a9dd36fcaf94f01838da385f572a8d --- /dev/null +++ b/doc/CHANGES-staging/ami_sendtext_content_type.txt @@ -0,0 +1,4 @@ +Subject: AMI + +You can now specify an optional 'Content-Type' as an argument for the Asterisk +SendText manager action. diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 0ead3b81c5dff8d7f1d7f3cb8722d6ced6b1beed..9c1822771e8f6c6ad533d53fc754ccfe6f49191b 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -335,9 +335,16 @@ enum ast_control_frame_type { AST_CONTROL_RECORD_MUTE = 1103, /*!< Indicated to a channel in record to mute/unmute (i.e. write silence) recording */ }; +/*! + * \brief Actions to indicate to, and be handled on channel read + * + * The subtype to specify for an AST_CONTROL_READ_ACTION frame. These + * frames are then to be enacted on within a channel's read thread. + */ enum ast_frame_read_action { AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO, AST_FRAME_READ_ACTION_SEND_TEXT, + AST_FRAME_READ_ACTION_SEND_TEXT_DATA, }; struct ast_control_read_action_payload { diff --git a/include/asterisk/message.h b/include/asterisk/message.h index f5b7a7528da6d014e85ae39cdda6d274c34ab2fa..94b5e916f0b04b7f77f0ad10a54874154e3c2c6e 100644 --- a/include/asterisk/message.h +++ b/include/asterisk/message.h @@ -479,6 +479,24 @@ struct ast_msg_data_attribute { struct ast_msg_data *ast_msg_data_alloc(enum ast_msg_data_source_type source, struct ast_msg_data_attribute attributes[], size_t count); +/*! + * \brief Allocates an ast_msg_data structure. + * \since 13.35.0 + * \since 16.12.0 + * \since 17.6.0 + * + * \param source The source type of the message + * \param to Where the message is sent to + * \param from Where the message is sent from + * \param content_type Content type of the body + * \param body The message body + * + * \return Pointer to msg structure or NULL on allocation failure. + * Caller must call ast_free when done. + */ +struct ast_msg_data *ast_msg_data_alloc2(enum ast_msg_data_source_type source_type, + const char *to, const char *from, const char *content_type, const char *body); + /*! * \brief Clone an ast_msg_data structure * \since 13.22.0 diff --git a/main/channel.c b/main/channel.c index 40d77248f613865c33c7c9542f88cccd29b23d3e..2f3aad1b508372606f7c46fa27531ad958f8a272 100644 --- a/main/channel.c +++ b/main/channel.c @@ -3812,7 +3812,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int break; case AST_FRAME_READ_ACTION_SEND_TEXT: ast_channel_unlock(chan); - ast_sendtext(chan, (const char *) read_action_payload->payload); + ast_sendtext(chan, (const char *)read_action_payload->payload); + ast_channel_lock(chan); + break; + case AST_FRAME_READ_ACTION_SEND_TEXT_DATA: + ast_channel_unlock(chan); + ast_sendtext_data(chan, (struct ast_msg_data *)read_action_payload->payload); ast_channel_lock(chan); break; } diff --git a/main/manager.c b/main/manager.c index e7a7f0f022ac9064a9fe544d50401d8b0203854b..562a7ea088f0c6c129722f46f2363a92c8c60e6a 100644 --- a/main/manager.c +++ b/main/manager.c @@ -100,6 +100,7 @@ #include "asterisk/format_cache.h" #include "asterisk/translate.h" #include "asterisk/taskprocessor.h" +#include "asterisk/message.h" /*** DOCUMENTATION <manager name="Ping" language="en_US"> @@ -873,7 +874,8 @@ </manager> <manager name="SendText" language="en_US"> <synopsis> - Send text message to channel. + Sends a text message to channel. A content type can be optionally specified. If not set + it is set to an empty string allowing a custom handler to default it as it sees fit. </synopsis> <syntax> <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /> @@ -883,10 +885,16 @@ <parameter name="Message" required="true"> <para>Message to send.</para> </parameter> + <parameter name="Content-Type" required="false" default=""> + <para>The type of content in the message</para> + </parameter> </syntax> <description> <para>Sends A Text Message to a channel while in a call.</para> </description> + <see-also> + <ref type="application">SendText</ref> + </see-also> </manager> <manager name="UserEvent" language="en_US"> <synopsis> @@ -4839,14 +4847,92 @@ static int action_status(struct mansession *s, const struct message *m) return 0; } +/*! + * \brief Queue a given read action containing a payload onto a channel + * + * This queues a READ_ACTION control frame that contains a given "payload", or + * data to be triggered and handled on the channel's read side. This ensures + * the "action" is handled by the channel's media reading thread. + * + * \param chan The channel to queue the action on + * \param payload The read action's payload + * \param payload_size The size of the given payload + * \param action The type of read action to queue + * + * \return -1 on error, 0 on success + */ +static int queue_read_action_payload(struct ast_channel *chan, const unsigned char *payload, + size_t payload_size, enum ast_frame_read_action action) +{ + struct ast_control_read_action_payload *obj; + size_t obj_size; + int res; + + obj_size = payload_size + sizeof(*obj); + + obj = ast_malloc(obj_size); + if (!obj) { + return -1; + } + + obj->action = action; + obj->payload_size = payload_size; + memcpy(obj->payload, payload, payload_size); + + res = ast_queue_control_data(chan, AST_CONTROL_READ_ACTION, obj, obj_size); + + ast_free(obj); + return res; +} + +/*! + * \brief Queue a read action to send a text message + * + * \param chan The channel to queue the action on + * \param body The body of the message + * + * \return -1 on error, 0 on success + */ +static int queue_sendtext(struct ast_channel *chan, const char *body) +{ + return queue_read_action_payload(chan, (const unsigned char *)body, + strlen(body) + 1, AST_FRAME_READ_ACTION_SEND_TEXT); +} + +/*! + * \brief Queue a read action to send a text data message + * + * \param chan The channel to queue the action on + * \param body The body of the message + * \param content_type The message's content type + * + * \return -1 on error, 0 on success + */ +static int queue_sendtext_data(struct ast_channel *chan, const char *body, + const char *content_type) +{ + int res; + struct ast_msg_data *obj; + + obj = ast_msg_data_alloc2(AST_MSG_DATA_SOURCE_TYPE_UNKNOWN, + NULL, NULL, content_type, body); + if (!obj) { + return -1; + } + + res = queue_read_action_payload(chan, (const unsigned char *)obj, + ast_msg_data_get_length(obj), AST_FRAME_READ_ACTION_SEND_TEXT_DATA); + + ast_free(obj); + return res; +} + static int action_sendtext(struct mansession *s, const struct message *m) { struct ast_channel *c; const char *name = astman_get_header(m, "Channel"); const char *textmsg = astman_get_header(m, "Message"); - struct ast_control_read_action_payload *frame_payload; - int payload_size; - int frame_size; + const char *content_type = astman_get_header(m, "Content-Type"); int res; if (ast_strlen_zero(name)) { @@ -4865,22 +4951,13 @@ static int action_sendtext(struct mansession *s, const struct message *m) return 0; } - payload_size = strlen(textmsg) + 1; - frame_size = payload_size + sizeof(*frame_payload); - - frame_payload = ast_malloc(frame_size); - if (!frame_payload) { - ast_channel_unref(c); - astman_send_error(s, m, "Failure"); - return 0; - } - - frame_payload->action = AST_FRAME_READ_ACTION_SEND_TEXT; - frame_payload->payload_size = payload_size; - memcpy(frame_payload->payload, textmsg, payload_size); - res = ast_queue_control_data(c, AST_CONTROL_READ_ACTION, frame_payload, frame_size); + /* + * If the "extra" data is not available, then send using "string" only. + * Doing such maintains backward compatibilities. + */ + res = ast_strlen_zero(content_type) ? queue_sendtext(c, textmsg) : + queue_sendtext_data(c, textmsg, content_type); - ast_free(frame_payload); ast_channel_unref(c); if (res >= 0) { diff --git a/main/message.c b/main/message.c index 66d051c29754e86cbd1762fb49a553e844c4d7e0..b6f254d166cf6d396c6543a0e7bc927155a7de2f 100644 --- a/main/message.c +++ b/main/message.c @@ -1429,6 +1429,32 @@ struct ast_msg_data *ast_msg_data_alloc(enum ast_msg_data_source_type source, return msg; } +struct ast_msg_data *ast_msg_data_alloc2(enum ast_msg_data_source_type source_type, + const char *to, const char *from, const char *content_type, const char *body) +{ + struct ast_msg_data_attribute attrs[] = + { + { + .type = AST_MSG_DATA_ATTR_TO, + .value = (char *)S_OR(to, ""), + }, + { + .type = AST_MSG_DATA_ATTR_FROM, + .value = (char *)S_OR(from, ""), + }, + { + .type = AST_MSG_DATA_ATTR_CONTENT_TYPE, + .value = (char *)S_OR(content_type, ""), + }, + { + .type = AST_MSG_DATA_ATTR_BODY, + .value = (char *)S_OR(body, ""), + }, + }; + + return ast_msg_data_alloc(source_type, attrs, ARRAY_LEN(attrs)); +} + struct ast_msg_data *ast_msg_data_dup(struct ast_msg_data *msg) { struct ast_msg_data *dest;