Merge "bridge_softmix: Forward TEXT frames" into 13

This commit is contained in:
George Joseph
2018-04-27 13:17:27 -05:00
committed by Gerrit Code Review
12 changed files with 658 additions and 45 deletions

13
CHANGES
View File

@@ -8,6 +8,19 @@
===
==============================================================================
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.21.0 to Asterisk 13.22.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 13.20.0 to Asterisk 13.21.0 ----------
------------------------------------------------------------------------------

View File

@@ -54,6 +54,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/translate.h"
#include "asterisk/message.h"
#define MAX_DATALEN 8096
@@ -719,6 +720,42 @@ static void softmix_bridge_check_voice(struct ast_bridge *bridge, struct ast_bri
}
}
/*!
* \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.
@@ -803,6 +840,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;

View File

@@ -63,6 +63,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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"
@@ -91,6 +92,7 @@ static void chan_pjsip_pvt_dtor(void *obj)
/* \brief Asterisk core interaction functions */
static struct ast_channel *chan_pjsip_request(const char *type, struct ast_format_cap *cap, 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);
@@ -112,6 +114,7 @@ struct ast_channel_tech chan_pjsip_tech = {
.description = "PJSIP Channel Driver",
.requester = chan_pjsip_request,
.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,
@@ -128,7 +131,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 */
@@ -2261,50 +2264,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);
}
@@ -2312,14 +2364,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;
@@ -2343,6 +2403,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)
{

View File

@@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name = "NULL" />
<enum name = "IAX" />
<enum name = "TEXT" />
<enum name = "TEXT_DATA" />
<enum name = "IMAGE" />
<enum name = "HTML" />
<enum name = "CNG" />
@@ -90,6 +91,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" },
@@ -372,6 +374,9 @@ static void print_frame(struct ast_frame *frame)
}
ast_verbose("Bytes: %d\n", frame->datalen);
break;
case AST_FRAME_RTCP:
ast_verbose("FrameType: RTCP\n");
break;
case AST_FRAME_NULL:
ast_verbose("FrameType: NULL\n");
break;
@@ -381,6 +386,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;

View File

@@ -589,6 +589,11 @@ struct ast_assigned_ids {
const char *uniqueid2;
};
/*!
* \brief Forward declaration
*/
struct ast_msg_data;
/*!
* \brief
* Structure to describe a channel "technology", ie a channel driver
@@ -756,6 +761,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. */
@@ -883,6 +891,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 */
@@ -2067,6 +2079,26 @@ int ast_set_write_format(struct ast_channel *chan, struct ast_format *format);
*/
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

View File

@@ -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
@@ -127,6 +128,10 @@ enum ast_frame_type {
* directly into bridges.
*/
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

View File

@@ -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

View File

@@ -57,6 +57,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/causes.h"
#include "asterisk/test.h"
#include "asterisk/sem.h"
#include "asterisk/message.h"
/*!
* \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
@@ -999,6 +1000,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",
@@ -2289,6 +2304,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
{
struct ast_frame *fr;
struct sync_payload *sync_payload;
struct ast_msg_data *msg;
ast_bridge_channel_lock(bridge_channel);
@@ -2321,6 +2337,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
@@ -2344,6 +2361,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:
/* Write the frame to the channel. */
bridge_channel->activity = BRIDGE_CHANNEL_THREAD_SIMPLE;

View File

@@ -75,6 +75,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/test.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/max_forwards.h"
#include "asterisk/message.h"
/*** DOCUMENTATION
***/
@@ -1513,6 +1514,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;
@@ -1525,6 +1527,7 @@ int ast_is_deferrable_frame(const struct ast_frame *frame)
case AST_FRAME_IAX:
case AST_FRAME_CNG:
case AST_FRAME_MODEM:
case AST_FRAME_RTCP:
return 0;
}
return 0;
@@ -2883,11 +2886,13 @@ 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:
case AST_FRAME_HTML:
case AST_FRAME_MODEM:
case AST_FRAME_RTCP:
done = 1;
break;
case AST_FRAME_CONTROL:
@@ -4869,9 +4874,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 */
@@ -4880,35 +4887,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

View File

@@ -591,6 +591,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;

View File

@@ -1350,6 +1350,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;

View File

@@ -76,6 +76,32 @@ static enum pjsip_status_code check_content_type(const pjsip_rx_data *rdata)
return res ? PJSIP_SC_OK : PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
}
/*!
* \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
@@ -756,39 +782,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++;
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;
}
ast_debug(3, "Received in dialog SIP message\n");
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));
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);
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;
}