summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins2 <jenkins2@gerrit.asterisk.org>2018-04-27 10:06:30 -0500
committerGerrit Code Review <gerrit2@gerrit.digium.api>2018-04-27 10:06:30 -0500
commit9c430569d4ccfb035970554641eacde88fc0c295 (patch)
tree00204a3a02b9917963e1eb3ff13c67582e9bd776
parent2cef65dc777a24da6999c6bed21f896e8ab79507 (diff)
parent4fb7967c7327fd73a93c587f3eca0564201be049 (diff)
Merge "bridge_softmix: Forward TEXT frames"
-rw-r--r--CHANGES13
-rw-r--r--bridges/bridge_softmix.c41
-rw-r--r--channels/chan_pjsip.c112
-rw-r--r--funcs/func_frame_trace.c5
-rw-r--r--include/asterisk/channel.h32
-rw-r--r--include/asterisk/frame.h3
-rw-r--r--include/asterisk/message.h123
-rw-r--r--main/bridge_channel.c31
-rw-r--r--main/channel.c76
-rw-r--r--main/frame.c3
-rw-r--r--main/message.c142
-rw-r--r--res/res_pjsip_messaging.c115
12 files changed, 651 insertions, 45 deletions
diff --git a/CHANGES b/CHANGES
index 3a81ca7ba..7fdae7066 100644
--- a/CHANGES
+++ b/CHANGES
@@ -105,6 +105,19 @@ Core:
by the system.
------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 15.4.0 to Asterisk 15.5.0 ------------
+------------------------------------------------------------------------------
+
+Core
+------------------
+ * Core bridging and, more specifically, bridge_softmix have been enhanced to
+ relay received frames of type TEXT or TEXT_DATA to all participants in a
+ softmix bridge. res_pjsip_messaging and chan_pjsip have been enhanced to
+ take advantage of this so when res_pjsip_messaging receives an in-dialog
+ MESSAGE message from a user in a conference call, it's relayed to all
+ other participants in the call.
+
+------------------------------------------------------------------------------
--- Functionality changes from Asterisk 15.3.0 to Asterisk 15.4.0 ------------
------------------------------------------------------------------------------
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index ed88b7cd5..e3513a788 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -36,6 +36,7 @@
#include "asterisk/stream.h"
#include "asterisk/test.h"
#include "asterisk/vector.h"
+#include "asterisk/message.h"
#include "bridge_softmix/include/bridge_softmix_internal.h"
/*! The minimum sample rate of the bridge. */
@@ -1121,6 +1122,42 @@ cleanup:
/*!
* \internal
+ * \brief Determine what to do with a text frame.
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param bridge Which bridge is getting the frame
+ * \param bridge_channel Which channel is writing the frame.
+ * \param frame What is being written.
+ *
+ * \return Nothing
+ */
+static void softmix_bridge_write_text(struct ast_bridge *bridge,
+ struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ if (DEBUG_ATLEAST(1)) {
+ struct ast_msg_data *msg = frame->data.ptr;
+ char frame_type[64];
+
+ ast_frame_type2str(frame->frametype, frame_type, sizeof(frame_type));
+
+ if (frame->frametype == AST_FRAME_TEXT_DATA) {
+ ast_log(LOG_DEBUG, "Received %s frame from '%s:%s': %s\n", frame_type,
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_FROM),
+ ast_channel_name(bridge_channel->chan),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY));
+ } else {
+ ast_log(LOG_DEBUG, "Received %s frame from '%s': %.*s\n", frame_type,
+ ast_channel_name(bridge_channel->chan), frame->datalen,
+ (char *)frame->data.ptr);
+ }
+ }
+
+ ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
+}
+
+/*!
+ * \internal
* \brief Determine what to do with a control frame.
* \since 12.0.0
*
@@ -1247,6 +1284,10 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
case AST_FRAME_VIDEO:
softmix_bridge_write_video(bridge, bridge_channel, frame);
break;
+ case AST_FRAME_TEXT:
+ case AST_FRAME_TEXT_DATA:
+ softmix_bridge_write_text(bridge, bridge_channel, frame);
+ break;
case AST_FRAME_CONTROL:
res = softmix_bridge_write_control(bridge, bridge_channel, frame);
break;
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index dde7416c3..b4eb711f7 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -61,6 +61,7 @@
#include "asterisk/features_config.h"
#include "asterisk/pickup.h"
#include "asterisk/test.h"
+#include "asterisk/message.h"
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
@@ -86,6 +87,7 @@ static struct ast_channel *chan_pjsip_request(const char *type, struct ast_forma
static struct ast_channel *chan_pjsip_request_with_stream_topology(const char *type,
struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids,
const struct ast_channel *requestor, const char *data, int *cause);
+static int chan_pjsip_sendtext_data(struct ast_channel *ast, struct ast_msg_data *msg);
static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text);
static int chan_pjsip_digit_begin(struct ast_channel *ast, char digit);
static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
@@ -109,6 +111,7 @@ struct ast_channel_tech chan_pjsip_tech = {
.requester = chan_pjsip_request,
.requester_with_stream_topology = chan_pjsip_request_with_stream_topology,
.send_text = chan_pjsip_sendtext,
+ .send_text_data = chan_pjsip_sendtext_data,
.send_digit_begin = chan_pjsip_digit_begin,
.send_digit_end = chan_pjsip_digit_end,
.call = chan_pjsip_call,
@@ -125,7 +128,7 @@ struct ast_channel_tech chan_pjsip_tech = {
.queryoption = chan_pjsip_queryoption,
.func_channel_read = pjsip_acf_channel_read,
.get_pvt_uniqueid = chan_pjsip_get_uniqueid,
- .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
+ .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER | AST_CHAN_TP_SEND_TEXT_DATA
};
/*! \brief SIP session interaction functions */
@@ -2539,50 +2542,99 @@ static struct ast_channel *chan_pjsip_request(const char *type, struct ast_forma
struct sendtext_data {
struct ast_sip_session *session;
- char text[0];
+ struct ast_msg_data *msg;
};
static void sendtext_data_destroy(void *obj)
{
struct sendtext_data *data = obj;
- ao2_ref(data->session, -1);
+ ao2_cleanup(data->session);
+ ast_free(data->msg);
}
-static struct sendtext_data* sendtext_data_create(struct ast_sip_session *session, const char *text)
+static struct sendtext_data* sendtext_data_create(struct ast_channel *chan,
+ struct ast_msg_data *msg)
{
- int size = strlen(text) + 1;
- struct sendtext_data *data = ao2_alloc(sizeof(*data)+size, sendtext_data_destroy);
+ struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
+ struct sendtext_data *data = ao2_alloc(sizeof(*data), sendtext_data_destroy);
if (!data) {
return NULL;
}
- data->session = session;
+ data->msg = ast_msg_data_dup(msg);
+ if (!data->msg) {
+ ao2_cleanup(data);
+ return NULL;
+ }
+ data->session = channel->session;
ao2_ref(data->session, +1);
- ast_copy_string(data->text, text, size);
+
return data;
}
static int sendtext(void *obj)
{
- RAII_VAR(struct sendtext_data *, data, obj, ao2_cleanup);
+ struct sendtext_data *data = obj;
pjsip_tx_data *tdata;
-
- const struct ast_sip_body body = {
+ const char *body_text = ast_msg_data_get_attribute(data->msg, AST_MSG_DATA_ATTR_BODY);
+ const char *content_type = ast_msg_data_get_attribute(data->msg, AST_MSG_DATA_ATTR_CONTENT_TYPE);
+ char *sep;
+ struct ast_sip_body body = {
.type = "text",
.subtype = "plain",
- .body_text = data->text
+ .body_text = body_text,
};
+ if (!ast_strlen_zero(content_type)) {
+ sep = strchr(content_type, '/');
+ if (sep) {
+ *sep = '\0';
+ body.type = content_type;
+ body.subtype = ++sep;
+ }
+ }
+
if (data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
data->session->inv_session->cause,
pjsip_get_status_text(data->session->inv_session->cause)->ptr);
} else {
- ast_debug(3, "Sending in dialog SIP message\n");
+ pjsip_from_hdr *hdr;
+ pjsip_name_addr *name_addr;
+ const char *from = ast_msg_data_get_attribute(data->msg, AST_MSG_DATA_ATTR_FROM);
+ const char *to = ast_msg_data_get_attribute(data->msg, AST_MSG_DATA_ATTR_TO);
+ int invalidate_tdata = 0;
ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
ast_sip_add_body(tdata, &body);
+
+ /*
+ * If we have a 'from' in the msg, set the display name in the From
+ * header to it.
+ */
+ if (!ast_strlen_zero(from)) {
+ hdr = PJSIP_MSG_FROM_HDR(tdata->msg);
+ name_addr = (pjsip_name_addr *) hdr->uri;
+ pj_strdup2(tdata->pool, &name_addr->display, from);
+ invalidate_tdata = 1;
+ }
+
+ /*
+ * If we have a 'to' in the msg, set the display name in the To
+ * header to it.
+ */
+ if (!ast_strlen_zero(to)) {
+ hdr = PJSIP_MSG_TO_HDR(tdata->msg);
+ name_addr = (pjsip_name_addr *) hdr->uri;
+ pj_strdup2(tdata->pool, &name_addr->display, to);
+ invalidate_tdata = 1;
+ }
+
+ if (invalidate_tdata) {
+ pjsip_tx_data_invalidate_msg(tdata);
+ }
+
ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint, NULL, NULL);
}
@@ -2590,14 +2642,22 @@ static int sendtext(void *obj)
pjsip_inv_dec_ref(data->session->inv_session);
#endif
+ ao2_cleanup(data);
+
return 0;
}
/*! \brief Function called by core to send text on PJSIP session */
-static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text)
+static int chan_pjsip_sendtext_data(struct ast_channel *ast, struct ast_msg_data *msg)
{
struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
- struct sendtext_data *data = sendtext_data_create(channel->session, text);
+ struct sendtext_data *data = sendtext_data_create(ast, msg);
+
+ ast_debug(1, "Sending MESSAGE from '%s' to '%s:%s': %s\n",
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_FROM),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_TO),
+ ast_channel_name(ast),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY));
if (!data) {
return -1;
@@ -2621,6 +2681,28 @@ static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text)
return 0;
}
+static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text)
+{
+ struct ast_msg_data *msg;
+ int rc;
+ struct ast_msg_data_attribute attrs[] =
+ {
+ {
+ .type = AST_MSG_DATA_ATTR_BODY,
+ .value = (char *)text,
+ }
+ };
+
+ msg = ast_msg_data_alloc(AST_MSG_DATA_SOURCE_TYPE_UNKNOWN, attrs, ARRAY_LEN(attrs));
+ if (!msg) {
+ return -1;
+ }
+ rc = chan_pjsip_sendtext_data(ast, msg);
+ ast_free(msg);
+
+ return rc;
+}
+
/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
static int hangup_sip2cause(int cause)
{
diff --git a/funcs/func_frame_trace.c b/funcs/func_frame_trace.c
index 59c8a1461..b62bae9de 100644
--- a/funcs/func_frame_trace.c
+++ b/funcs/func_frame_trace.c
@@ -59,6 +59,7 @@
<enum name = "NULL" />
<enum name = "IAX" />
<enum name = "TEXT" />
+ <enum name = "TEXT_DATA" />
<enum name = "IMAGE" />
<enum name = "HTML" />
<enum name = "CNG" />
@@ -88,6 +89,7 @@ static struct {
{ AST_FRAME_NULL, "NULL" },
{ AST_FRAME_IAX, "IAX" },
{ AST_FRAME_TEXT, "TEXT" },
+ { AST_FRAME_TEXT_DATA, "TEXT_DATA" },
{ AST_FRAME_IMAGE, "IMAGE" },
{ AST_FRAME_HTML, "HTML" },
{ AST_FRAME_CNG, "CNG" },
@@ -391,6 +393,9 @@ static void print_frame(struct ast_frame *frame)
case AST_FRAME_TEXT:
ast_verbose("FrameType: TXT\n");
break;
+ case AST_FRAME_TEXT_DATA:
+ ast_verbose("FrameType: TXT_DATA\n");
+ break;
case AST_FRAME_IMAGE:
ast_verbose("FrameType: IMAGE\n");
break;
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 16f9aa8ae..c865a8a32 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -593,6 +593,11 @@ struct ast_assigned_ids {
};
/*!
+ * \brief Forward declaration
+ */
+struct ast_msg_data;
+
+/*!
* \brief
* Structure to describe a channel "technology", ie a channel driver
* See for examples:
@@ -807,6 +812,9 @@ struct ast_channel_tech {
* \retval -1 on error.
*/
int (*pre_call)(struct ast_channel *chan, const char *sub_args);
+
+ /*! \brief Display or transmit text with data*/
+ int (* const send_text_data)(struct ast_channel *chan, struct ast_msg_data *data);
};
/*! Kill the channel channel driver technology descriptor. */
@@ -934,6 +942,10 @@ enum {
* world
*/
AST_CHAN_TP_INTERNAL = (1 << 2),
+ /*!
+ * \brief Channels have this property if they implement send_text_data
+ */
+ AST_CHAN_TP_SEND_TEXT_DATA = (1 << 3),
};
/*! \brief ast_channel flags */
@@ -2134,6 +2146,26 @@ int ast_set_write_format_interleaved_stereo(struct ast_channel *chan, struct ast
int ast_sendtext(struct ast_channel *chan, const char *text);
/*!
+ * \brief Sends text to a channel in an ast_msg_data structure wrapper with ast_sendtext as fallback
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param chan channel to act upon
+ * \param msg ast_msg_data structure
+ *
+ * \details
+ * Write text to a display on a channel. If the channel driver doesn't support the
+ * send_text_data callback. then the original send_text callback will be used if
+ * available.
+ *
+ * \note The channel does not need to be locked before calling this function.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_sendtext_data(struct ast_channel *chan, struct ast_msg_data *msg);
+
+/*!
* \brief Receives a text character from a channel
* \param chan channel to act upon
* \param timeout timeout in milliseconds (0 for infinite wait)
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index c3c0f8817..542407ecc 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -48,6 +48,7 @@ extern "C" {
* \arg \b DTMF: A DTMF digit, subclass is the digit
* \arg \b IMAGE: Image transport, mostly used in IAX
* \arg \b TEXT: Text messages and character by character (real time text)
+ * \arg \b TEXT_DATA: Text messages in an ast_msg_data structure
* \arg \b HTML: URL's and web pages
* \arg \b MODEM: Modulated data encodings, such as T.38 and V.150
* \arg \b IAX: Private frame type for the IAX protocol
@@ -129,6 +130,8 @@ enum ast_frame_type {
AST_FRAME_BRIDGE_ACTION_SYNC,
/*! RTCP feedback (the subclass will contain the payload type) */
AST_FRAME_RTCP,
+ /*! Text message in an ast_msg_data structure */
+ AST_FRAME_TEXT_DATA,
};
#define AST_FRAME_DTMF AST_FRAME_DTMF_END
diff --git a/include/asterisk/message.h b/include/asterisk/message.h
index ae6533c46..826fa0ac3 100644
--- a/include/asterisk/message.h
+++ b/include/asterisk/message.h
@@ -407,6 +407,129 @@ void ast_msg_var_iterator_destroy(struct ast_msg_var_iterator *iter);
*/
void ast_msg_var_unref_current(struct ast_msg_var_iterator *iter);
+
+/*! \defgroup ast_msg_data Enhanced Messaging
+ * @{
+ * \page Messaging Enhanced Messaging
+ *
+ * The basic messaging framework has a basic drawback... It can only pass
+ * a text string through the core. This causes several issues:
+ * \li Only a content type of text/plain can be passed.
+ * \li If a softmix bridge is used, the original sender identity is lost.
+ *
+ * The Enhanced Messaging framework allows attributes, such as "From", "To"
+ * and "Content-Type" to be attached to the message by the incoming channel
+ * tech which can then be used by the outgoing channel tech to construct
+ * the appropriate technology-specific outgoing message.
+ */
+
+/*!
+ * \brief Structure used to transport an enhanced message through the frame core
+ * \since 13.22.0
+ * \since 15.5.0
+ */
+struct ast_msg_data;
+
+enum ast_msg_data_source_type {
+ AST_MSG_DATA_SOURCE_TYPE_UNKNOWN = 0,
+ AST_MSG_DATA_SOURCE_TYPE_T140,
+ AST_MSG_DATA_SOURCE_TYPE_IN_DIALOG,
+ AST_MSG_DATA_SOURCE_TYPE_OUT_OF_DIALOG,
+ __AST_MSG_DATA_SOURCE_TYPE_LAST,
+};
+
+enum ast_msg_data_attribute_type {
+ AST_MSG_DATA_ATTR_TO = 0,
+ AST_MSG_DATA_ATTR_FROM,
+ AST_MSG_DATA_ATTR_CONTENT_TYPE,
+ AST_MSG_DATA_ATTR_BODY,
+ __AST_MSG_DATA_ATTR_LAST,
+};
+
+struct ast_msg_data_attribute {
+ enum ast_msg_data_attribute_type type;
+ char *value;
+};
+
+/*!
+ * \brief Allocates an ast_msg_data structure.
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param source The source type of the message
+ * \param attributes A pointer to an array of ast_msg_data_attribute structures
+ * \param count The number of elements in the array
+ *
+ * \return Pointer to msg structure or NULL on allocation failure.
+ * Caller must call ast_free when done.
+ */
+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 Clone an ast_msg_data structure
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param msg The message to clone
+ *
+ * \return New message structure or NULL if there was an allocation failure.
+ * Caller must call ast_free when done.
+ */
+struct ast_msg_data *ast_msg_data_dup(struct ast_msg_data *msg);
+
+/*!
+ * \brief Get length of the structure
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param msg Pointer to ast_msg_data structure
+ *
+ * \return The length of the structure itself plus the dynamically allocated attribute buffer.
+ */
+size_t ast_msg_data_get_length(struct ast_msg_data *msg);
+
+/*!
+ * \brief Get "source type" from ast_msg_data
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param msg Pointer to ast_msg_data structure
+ *
+ * \return The source type field.
+ */
+enum ast_msg_data_source_type ast_msg_data_get_source_type(struct ast_msg_data *msg);
+
+/*!
+ * \brief Get attribute from ast_msg_data
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param msg Pointer to ast_msg_data structure
+ * \param attribute_type One of ast_msg_data_attribute_type
+ *
+ * \return The attribute or an empty string ("") if the attribute wasn't set.
+ */
+const char *ast_msg_data_get_attribute(struct ast_msg_data *msg,
+ enum ast_msg_data_attribute_type attribute_type);
+
+/*!
+ * \brief Queue an AST_FRAME_TEXT_DATA frame containing an ast_msg_data structure
+ * \since 13.22.0
+ * \since 15.5.0
+ *
+ * \param channel The channel on which to queue the frame
+ * \param msg Pointer to ast_msg_data structure
+ *
+ * \retval -1 Error
+ * \retval 0 Success
+ */
+int ast_msg_data_queue_frame(struct ast_channel *channel, struct ast_msg_data *msg);
+
+/*!
+ * @}
+ */
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 3aac5eb25..eb4b9ad0e 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -56,6 +56,7 @@
#include "asterisk/test.h"
#include "asterisk/sem.h"
#include "asterisk/stream.h"
+#include "asterisk/message.h"
/*!
* \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
@@ -1055,6 +1056,20 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st
return 0;
}
+ if (DEBUG_ATLEAST(1)) {
+ if (fr->frametype == AST_FRAME_TEXT) {
+ ast_log(LOG_DEBUG, "Queuing TEXT frame to '%s': %*.s\n", ast_channel_name(bridge_channel->chan),
+ fr->datalen, (char *)fr->data.ptr);
+ } else if (fr->frametype == AST_FRAME_TEXT_DATA) {
+ struct ast_msg_data *msg = fr->data.ptr;
+ ast_log(LOG_DEBUG, "Queueing TEXT_DATA frame from '%s' to '%s:%s': %s\n",
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_FROM),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_TO),
+ ast_channel_name(bridge_channel->chan),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY));
+ }
+ }
+
AST_LIST_INSERT_TAIL(&bridge_channel->wr_queue, dup, frame_list);
if (ast_alertpipe_write(bridge_channel->alert_pipe)) {
ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n",
@@ -2349,6 +2364,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
struct ast_frame *fr;
struct sync_payload *sync_payload;
int num;
+ struct ast_msg_data *msg;
ast_bridge_channel_lock(bridge_channel);
@@ -2381,6 +2397,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
AST_LIST_TRAVERSE_SAFE_END;
ast_bridge_channel_unlock(bridge_channel);
+
if (!fr) {
/*
* Wait some to reduce CPU usage from a tight loop
@@ -2404,6 +2421,20 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
break;
case AST_FRAME_NULL:
break;
+ case AST_FRAME_TEXT:
+ ast_debug(1, "Sending TEXT frame to '%s': %*.s\n",
+ ast_channel_name(bridge_channel->chan), fr->datalen, (char *)fr->data.ptr);
+ ast_sendtext(bridge_channel->chan, fr->data.ptr);
+ break;
+ case AST_FRAME_TEXT_DATA:
+ msg = (struct ast_msg_data *)fr->data.ptr;
+ ast_debug(1, "Sending TEXT_DATA frame from '%s' to '%s:%s': %s\n",
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_FROM),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_TO),
+ ast_channel_name(bridge_channel->chan),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY));
+ ast_sendtext_data(bridge_channel->chan, msg);
+ break;
default:
/* Assume that there is no mapped stream for this */
num = -1;
diff --git a/main/channel.c b/main/channel.c
index 815d5dbfe..a23dfa10c 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -73,6 +73,7 @@
#include "asterisk/stasis_channels.h"
#include "asterisk/max_forwards.h"
#include "asterisk/stream.h"
+#include "asterisk/message.h"
/*** DOCUMENTATION
***/
@@ -1492,6 +1493,7 @@ int ast_is_deferrable_frame(const struct ast_frame *frame)
case AST_FRAME_BRIDGE_ACTION_SYNC:
case AST_FRAME_CONTROL:
case AST_FRAME_TEXT:
+ case AST_FRAME_TEXT_DATA:
case AST_FRAME_IMAGE:
case AST_FRAME_HTML:
return 1;
@@ -2767,6 +2769,7 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay)
case AST_FRAME_VOICE:
case AST_FRAME_VIDEO:
case AST_FRAME_TEXT:
+ case AST_FRAME_TEXT_DATA:
case AST_FRAME_DTMF_BEGIN:
case AST_FRAME_DTMF_END:
case AST_FRAME_IMAGE:
@@ -4652,9 +4655,11 @@ char *ast_recvtext(struct ast_channel *chan, int timeout)
return buf;
}
-int ast_sendtext(struct ast_channel *chan, const char *text)
+int ast_sendtext_data(struct ast_channel *chan, struct ast_msg_data *msg)
{
int res = 0;
+ const char *body = ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY);
+ const char *content_type = ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_CONTENT_TYPE);
ast_channel_lock(chan);
/* Stop if we're a zombie or need a soft hangup */
@@ -4663,35 +4668,76 @@ int ast_sendtext(struct ast_channel *chan, const char *text)
return -1;
}
- if (ast_strlen_zero(text)) {
- ast_channel_unlock(chan);
- return 0;
- }
-
CHECK_BLOCKING(chan);
- if (ast_channel_tech(chan)->write_text && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_TEXT))) {
+ if (ast_channel_tech(chan)->write_text
+ && (ast_strlen_zero(content_type) || strcasecmp(content_type, "text/plain") == 0)
+ && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_TEXT))) {
struct ast_frame f;
+ size_t body_len = strlen(body) + 1;
+ /* Process as T.140 text (moved here from ast_sendtext() */
memset(&f, 0, sizeof(f));
- f.frametype = AST_FRAME_TEXT;
f.src = "DIALPLAN";
- f.mallocd = AST_MALLOCD_DATA;
- f.datalen = strlen(text);
- f.data.ptr = ast_strdup(text);
f.subclass.format = ast_format_t140;
-
+ f.frametype = AST_FRAME_TEXT;
+ f.datalen = body_len;
+ f.mallocd = AST_MALLOCD_DATA;
+ f.data.ptr = ast_strdup(body);
if (f.data.ptr) {
res = ast_channel_tech(chan)->write_text(chan, &f);
- ast_frfree(&f);
+ } else {
+ res = -1;
}
- } else if (ast_channel_tech(chan)->send_text) {
- res = ast_channel_tech(chan)->send_text(chan, text);
+ ast_frfree(&f);
+ } else if ((ast_channel_tech(chan)->properties & AST_CHAN_TP_SEND_TEXT_DATA)
+ && ast_channel_tech(chan)->send_text_data) {
+ /* Send enhanced message to a channel driver that supports it */
+ ast_debug(1, "Sending TEXT_DATA from '%s' to %s:%s %s\n",
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_FROM),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_TO),
+ ast_channel_name(chan), body);
+ res = ast_channel_tech(chan)->send_text_data(chan, msg);
+ } else if (ast_channel_tech(chan)->send_text
+ && (ast_strlen_zero(content_type) || strcasecmp(content_type, "text/plain") == 0)) {
+ /* Send the body of an enhanced message to a channel driver that supports only a char str */
+ ast_debug(1, "Sending TEXT to %s: %s\n", ast_channel_name(chan), body);
+ res = ast_channel_tech(chan)->send_text(chan, body);
+ } else {
+ ast_debug(1, "Channel technology does not support sending text on channel '%s'\n",
+ ast_channel_name(chan));
+ res = -1;
}
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING);
ast_channel_unlock(chan);
return res;
}
+int ast_sendtext(struct ast_channel *chan, const char *text)
+{
+ struct ast_msg_data *msg;
+ int rc;
+ struct ast_msg_data_attribute attrs[] =
+ {
+ {
+ .type = AST_MSG_DATA_ATTR_BODY,
+ .value = (char *)text,
+ }
+ };
+
+ if (ast_strlen_zero(text)) {
+ return 0;
+ }
+
+ msg = ast_msg_data_alloc(AST_MSG_DATA_SOURCE_TYPE_UNKNOWN, attrs, ARRAY_LEN(attrs));
+ if (!msg) {
+ return -1;
+ }
+ rc = ast_sendtext_data(chan, msg);
+ ast_free(msg);
+
+ return rc;
+}
+
int ast_senddigit_begin(struct ast_channel *chan, char digit)
{
/* Device does not support DTMF tones, lets fake
diff --git a/main/frame.c b/main/frame.c
index dd47f42d0..383571f65 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -593,6 +593,9 @@ void ast_frame_type2str(enum ast_frame_type frame_type, char *ftype, size_t len)
case AST_FRAME_TEXT:
ast_copy_string(ftype, "Text", len);
break;
+ case AST_FRAME_TEXT_DATA:
+ ast_copy_string(ftype, "Text Data", len);
+ break;
case AST_FRAME_IMAGE:
ast_copy_string(ftype, "Image", len);
break;
diff --git a/main/message.c b/main/message.c
index ac7965ea7..b7d14f1e8 100644
--- a/main/message.c
+++ b/main/message.c
@@ -1348,6 +1348,148 @@ int ast_msg_send(struct ast_msg *msg, const char *to, const char *from)
return res;
}
+/*!
+ * \brief Structure used to transport a message through the frame core
+ * \since 13.22.0
+ * \since 15.5.0
+ */
+struct ast_msg_data {
+ /*! The length of this structure plus the actual length of the allocated buffer */
+ size_t length;
+ enum ast_msg_data_source_type source;
+ /*! These are indices into the buffer where teh attribute starts */
+ int attribute_value_offsets[__AST_MSG_DATA_ATTR_LAST];
+ /*! The buffer containing the NULL separated attributes */
+ char buf[0];
+};
+
+#define ATTRIBUTE_UNSET -1
+
+struct ast_msg_data *ast_msg_data_alloc(enum ast_msg_data_source_type source,
+ struct ast_msg_data_attribute attributes[], size_t count)
+{
+ struct ast_msg_data *msg;
+ size_t len = sizeof(*msg);
+ size_t i;
+ size_t current_offset = 0;
+ enum ast_msg_data_attribute_type attr_type;
+
+ if (!attributes) {
+ ast_assert(attributes != NULL);
+ return NULL;
+ }
+
+ if (!count) {
+ ast_assert(count > 0);
+ return NULL;
+ }
+
+ /* Calculate the length required for the buffer */
+ for (i=0; i < count; i++) {
+ if (!attributes[i].value) {
+ ast_assert(attributes[i].value != NULL);
+ return NULL;
+ }
+ len += (strlen(attributes[i].value) + 1);
+ }
+
+ msg = ast_calloc(1, len);
+ if (!msg) {
+ return NULL;
+ }
+ msg->source = source;
+ msg->length = len;
+
+ /* Mark all of the attributes as unset */
+ for (attr_type = 0; attr_type < __AST_MSG_DATA_ATTR_LAST; attr_type++) {
+ msg->attribute_value_offsets[attr_type] = ATTRIBUTE_UNSET;
+ }
+
+ /* Set the ones we have and increment the offset */
+ for (i=0; i < count; i++) {
+ len = (strlen(attributes[i].value) + 1);
+ strcpy(msg->buf + current_offset, attributes[i].value); /* Safe */
+ msg->attribute_value_offsets[attributes[i].type] = current_offset;
+ current_offset += len;
+ }
+
+ return msg;
+}
+
+struct ast_msg_data *ast_msg_data_dup(struct ast_msg_data *msg)
+{
+ struct ast_msg_data *dest;
+
+ if (!msg) {
+ ast_assert(msg != NULL);
+ return NULL;
+ }
+
+ dest = ast_malloc(msg->length);
+ if (!dest) {
+ return NULL;
+ }
+ memcpy(dest, msg, msg->length);
+
+ return dest;
+}
+
+size_t ast_msg_data_get_length(struct ast_msg_data *msg)
+{
+ if (!msg) {
+ ast_assert(msg != NULL);
+ return 0;
+ }
+
+ return msg->length;
+}
+
+enum ast_msg_data_source_type ast_msg_data_get_source_type(struct ast_msg_data *msg)
+{
+ if (!msg) {
+ ast_assert(msg != NULL);
+ return AST_MSG_DATA_SOURCE_TYPE_UNKNOWN;
+ }
+
+ return msg->source;
+}
+
+const char *ast_msg_data_get_attribute(struct ast_msg_data *msg,
+ enum ast_msg_data_attribute_type attribute_type)
+{
+ if (!msg) {
+ ast_assert(msg != NULL);
+ return "";
+ }
+
+ if (msg->attribute_value_offsets[attribute_type] > ATTRIBUTE_UNSET) {
+ return msg->buf + msg->attribute_value_offsets[attribute_type];
+ }
+
+ return "";
+}
+
+int ast_msg_data_queue_frame(struct ast_channel *channel, struct ast_msg_data *msg)
+{
+ struct ast_frame f;
+
+ if (!channel) {
+ ast_assert(channel != NULL);
+ return -1;
+ }
+
+ if (!msg) {
+ ast_assert(msg != NULL);
+ return -1;
+ }
+
+ memset(&f, 0, sizeof(f));
+ f.frametype = AST_FRAME_TEXT_DATA;
+ f.data.ptr = msg;
+ f.datalen = msg->length;
+ return ast_queue_frame(channel, &f);
+}
+
int ast_msg_tech_register(const struct ast_msg_tech *tech)
{
const struct ast_msg_tech *match;
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index cbaed83fb..d8628f5bd 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -77,6 +77,32 @@ static enum pjsip_status_code check_content_type(const pjsip_rx_data *rdata)
/*!
* \internal
+ * \brief Checks to make sure the request has the correct content type.
+ *
+ * \details This module supports the following media types: "text/\*".
+ * Return unsupported otherwise.
+ *
+ * \param rdata The SIP request
+ */
+static enum pjsip_status_code check_content_type_any_text(const pjsip_rx_data *rdata)
+{
+ int res = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
+ pj_str_t text = { "text", 4};
+
+ /* We'll accept any text/ content type */
+ if (rdata->msg_info.msg->body && rdata->msg_info.msg->body->len
+ && pj_stricmp(&rdata->msg_info.msg->body->content_type.type, &text) == 0) {
+ res = PJSIP_SC_OK;
+ } else if (rdata->msg_info.ctype
+ && pj_stricmp(&rdata->msg_info.ctype->media.type, &text) == 0) {
+ res = PJSIP_SC_OK;
+ }
+
+ return res;
+}
+
+/*!
+ * \internal
* \brief Puts pointer past 'sip[s]:' string that should be at the
* front of the given 'fromto' parameter
*
@@ -755,39 +781,98 @@ static pj_bool_t module_on_rx_request(pjsip_rx_data *rdata)
static int incoming_in_dialog_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
{
- char buf[MAX_BODY_SIZE];
enum pjsip_status_code code;
- struct ast_frame f;
+ int rc;
pjsip_dialog *dlg = session->inv_session->dlg;
pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
+ struct ast_msg_data *msg;
+ struct ast_party_caller *caller;
+ pjsip_name_addr *name_addr;
+ size_t from_len;
+ size_t to_len;
+ struct ast_msg_data_attribute attrs[4];
+ int pos = 0;
+ int body_pos;
if (!session->channel) {
send_response(rdata, PJSIP_SC_NOT_FOUND, dlg, tsx);
return 0;
}
- if ((code = check_content_type(rdata)) != PJSIP_SC_OK) {
+ code = check_content_type_any_text(rdata);
+ if (code != PJSIP_SC_OK) {
send_response(rdata, code, dlg, tsx);
return 0;
}
- if (print_body(rdata, buf, sizeof(buf)-1) < 1) {
- /* invalid body size */
- send_response(rdata, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE, dlg, tsx);
+ caller = ast_channel_caller(session->channel);
+
+ name_addr = (pjsip_name_addr *) rdata->msg_info.from->uri;
+ from_len = pj_strlen(&name_addr->display);
+ if (from_len) {
+ attrs[pos].type = AST_MSG_DATA_ATTR_FROM;
+ from_len++;
+ attrs[pos].value = ast_alloca(from_len);
+ ast_copy_pj_str(attrs[pos].value, &name_addr->display, from_len);
+ pos++;
+ } else if (caller->id.name.valid && !ast_strlen_zero(caller->id.name.str)) {
+ attrs[pos].type = AST_MSG_DATA_ATTR_FROM;
+ attrs[pos].value = caller->id.name.str;
+ pos++;
+ }
+
+ name_addr = (pjsip_name_addr *) rdata->msg_info.to->uri;
+ to_len = pj_strlen(&name_addr->display);
+ if (to_len) {
+ attrs[pos].type = AST_MSG_DATA_ATTR_TO;
+ to_len++;
+ attrs[pos].value = ast_alloca(to_len);
+ ast_copy_pj_str(attrs[pos].value, &name_addr->display, to_len);
+ pos++;
+ }
+
+ attrs[pos].type = AST_MSG_DATA_ATTR_CONTENT_TYPE;
+ attrs[pos].value = ast_alloca(rdata->msg_info.msg->body->content_type.type.slen
+ + rdata->msg_info.msg->body->content_type.subtype.slen + 2);
+ sprintf(attrs[pos].value, "%.*s/%.*s",
+ (int)rdata->msg_info.msg->body->content_type.type.slen,
+ rdata->msg_info.msg->body->content_type.type.ptr,
+ (int)rdata->msg_info.msg->body->content_type.subtype.slen,
+ rdata->msg_info.msg->body->content_type.subtype.ptr);
+ pos++;
+
+ body_pos = pos;
+ attrs[pos].type = AST_MSG_DATA_ATTR_BODY;
+ attrs[pos].value = ast_malloc(rdata->msg_info.msg->body->len + 1);
+ if (!attrs[pos].value) {
+ send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, dlg, tsx);
return 0;
}
+ ast_copy_string(attrs[pos].value, rdata->msg_info.msg->body->data, rdata->msg_info.msg->body->len);
+ pos++;
- ast_debug(3, "Received in dialog SIP message\n");
+ msg = ast_msg_data_alloc(AST_MSG_DATA_SOURCE_TYPE_IN_DIALOG, attrs, pos);
+ if (!msg) {
+ ast_free(attrs[body_pos].value);
+ send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, dlg, tsx);
+ return 0;
+ }
- memset(&f, 0, sizeof(f));
- f.frametype = AST_FRAME_TEXT;
- f.subclass.integer = 0;
- f.offset = 0;
- f.data.ptr = buf;
- f.datalen = strlen(buf) + 1;
- ast_queue_frame(session->channel, &f);
+ ast_debug(1, "Received in-dialog MESSAGE from '%s:%s': %s %s\n",
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_FROM),
+ ast_channel_name(session->channel),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_TO),
+ ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY));
+
+ rc = ast_msg_data_queue_frame(session->channel, msg);
+ ast_free(attrs[body_pos].value);
+ ast_free(msg);
+ if (rc != 0) {
+ send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, dlg, tsx);
+ } else {
+ send_response(rdata, PJSIP_SC_ACCEPTED, dlg, tsx);
+ }
- send_response(rdata, PJSIP_SC_ACCEPTED, dlg, tsx);
return 0;
}