mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-06 12:36:58 +00:00
From reviewboard: Digium has a commercial customer who has made extensive use of the connected party and redirecting information present in later versions of Asterisk Business Edition and which is to be in the upcoming 1.8 release. Through their use of the feature, new problems and solutions have come about. This patch adds several enhancements to maximize usage of the connected party and redirecting information functionality. First, Asterisk trunk already had connected line interception macros. These macros allow you to manipulate connected line information before it was sent out to its target. This patch adds the same feature except for redirecting information instead. Second, the ast_callerid and ast_party_id structures have been enhanced to provide a "tag." This tag can be set with func_callerid, func_connectedline, func_redirecting, and in the case of DAHDI, mISDN, and SIP channels, can be set in a configuration file. The idea behind the callerid tag is that it can be set to whatever value the administrator likes. Later, when running connected line and redirecting macros, the admin can read the tag off the appropriate structure to determine what action to take. You can think of this sort of like a channel variable, except that instead of having the variable associated with a channel, the variable is associated with a specific identity within Asterisk. Third, app_dial has two new options, s and u. The s option lets a dialplan writer force a specific caller ID tag to be placed on the outgoing channel. The u option allows the dialplan writer to force a specific calling presentation value on the outgoing channel. Fourth, there is a new control frame subclass called AST_CONTROL_READ_ACTION added. This was added to correct a very specific situation. In the case of SIP semi-attended (blond) transfers, the party being transferred would not have the opportunity to run a connected line interception macro to possibly alter the transfer target's connected line information. The issue here was that during a blond transfer, the SIP transfer code has no bridged channel on which to queue the connected line update. The way this was corrected was to add this new control frame subclass. Now, we queue an AST_CONTROL_READ_ACTION frame on the channel on which the connected line interception macro should be run. When ast_read is called to read the frame, ast_read responds by calling a callback function associated with the specific read action the control frame describes. In this case, the action taken is to run the connected line interception macro on the transferee's channel. Review: https://reviewboard.asterisk.org/r/652/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@263541 65c4cc65-6c06-0410-ace0-fbb531ad65f3
488 lines
12 KiB
C
488 lines
12 KiB
C
/*
|
|
* Asterisk -- An open source telephony toolkit.
|
|
*
|
|
* Copyright (C) 2008 Digium, Inc.
|
|
*
|
|
* Richard Mudgett <rmudgett@digium.com>
|
|
*
|
|
* See http://www.asterisk.org for more information about
|
|
* the Asterisk project. Please do not directly contact
|
|
* any of the maintainers of this project for assistance;
|
|
* the project provides a web site, mailing lists and IRC
|
|
* channels for your use.
|
|
*
|
|
* This program is free software, distributed under the terms of
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
* at the top of the source tree.
|
|
*/
|
|
|
|
/*!
|
|
* \file
|
|
* \brief Redirecting data dialplan function
|
|
* \ingroup functions
|
|
*
|
|
* \author Richard Mudgett <rmudgett@digium.com>
|
|
*
|
|
* See Also:
|
|
* \arg \ref AstCREDITS
|
|
*/
|
|
|
|
|
|
#include "asterisk.h"
|
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "asterisk/module.h"
|
|
#include "asterisk/channel.h"
|
|
#include "asterisk/pbx.h"
|
|
#include "asterisk/logger.h"
|
|
#include "asterisk/utils.h"
|
|
#include "asterisk/app.h"
|
|
#include "asterisk/options.h"
|
|
#include "asterisk/callerid.h"
|
|
|
|
/*
|
|
* Do not document the REDIRECTING(pres) datatype.
|
|
* It has turned out that the from-pres and to-pres values must be kept
|
|
* separate. They represent two different parties and there is a case when
|
|
* they are active at the same time. The plain pres option will simply
|
|
* live on as a historical relic.
|
|
*/
|
|
/*** DOCUMENTATION
|
|
<function name="REDIRECTING" language="en_US">
|
|
<synopsis>
|
|
Gets or sets Redirecting data on the channel.
|
|
</synopsis>
|
|
<syntax>
|
|
<parameter name="datatype" required="true">
|
|
<para>The allowable datatypes are:</para>
|
|
<enumlist>
|
|
<enum name = "from-all" />
|
|
<enum name = "from-num" />
|
|
<enum name = "from-name" />
|
|
<enum name = "from-tag" />
|
|
<enum name = "from-ton" />
|
|
<enum name = "from-pres" />
|
|
<enum name = "to-all" />
|
|
<enum name = "to-num" />
|
|
<enum name = "to-name" />
|
|
<enum name = "to-tag" />
|
|
<enum name = "to-ton" />
|
|
<enum name = "to-pres" />
|
|
<enum name = "reason" />
|
|
<enum name = "count" />
|
|
</enumlist>
|
|
</parameter>
|
|
<parameter name="i">
|
|
<para>If set, this will prevent the channel from sending out protocol
|
|
messages because of the value being set</para>
|
|
</parameter>
|
|
</syntax>
|
|
<description>
|
|
<para>Gets or sets Redirecting data on the channel. The allowable values
|
|
for the <replaceable>reason</replaceable> field are the following:</para>
|
|
<enumlist>
|
|
<enum name = "unknown"><para>Unknown</para></enum>
|
|
<enum name = "cfb"><para>Call Forwarding Busy</para></enum>
|
|
<enum name = "cfnr"><para>Call Forwarding No Reply</para></enum>
|
|
<enum name = "unavailable"><para>Callee is Unavailable</para></enum>
|
|
<enum name = "time_of_day"><para>Time of Day</para></enum>
|
|
<enum name = "dnd"><para>Do Not Disturb</para></enum>
|
|
<enum name = "deflection"><para>Call Deflection</para></enum>
|
|
<enum name = "follow_me"><para>Follow Me</para></enum>
|
|
<enum name = "out_of_order"><para>Called DTE Out-Of-Order</para></enum>
|
|
<enum name = "away"><para>Callee is Away</para></enum>
|
|
<enum name = "cf_dte"><para>Call Forwarding By The Called DTE</para></enum>
|
|
<enum name = "cfu"><para>Call Forwarding Unconditional</para></enum>
|
|
</enumlist>
|
|
</description>
|
|
</function>
|
|
***/
|
|
|
|
enum ID_FIELD_STATUS {
|
|
ID_FIELD_VALID,
|
|
ID_FIELD_INVALID,
|
|
ID_FIELD_UNKNOWN
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************* */
|
|
/*!
|
|
* \internal
|
|
* \brief Read values from the party id struct.
|
|
*
|
|
* \param buf Buffer to fill with read value.
|
|
* \param len Length of the buffer
|
|
* \param data Remaining function datatype string
|
|
*
|
|
* \retval ID_FIELD_VALID on success.
|
|
* \retval ID_FIELD_UNKNOWN on unknown field name.
|
|
*/
|
|
static enum ID_FIELD_STATUS redirecting_id_read(char *buf, size_t len, char *data, const struct ast_party_id *id)
|
|
{
|
|
enum ID_FIELD_STATUS status;
|
|
|
|
status = ID_FIELD_VALID;
|
|
|
|
if (!strncasecmp("all", data, 3)) {
|
|
snprintf(buf, len, "\"%s\" <%s>",
|
|
S_OR(id->name, ""),
|
|
S_OR(id->number, ""));
|
|
} else if (!strncasecmp("name", data, 4)) {
|
|
if (id->name) {
|
|
ast_copy_string(buf, id->name, len);
|
|
}
|
|
} else if (!strncasecmp("num", data, 3)) {
|
|
if (id->number) {
|
|
ast_copy_string(buf, id->number, len);
|
|
}
|
|
} else if (!strncasecmp("tag", data, 3)) {
|
|
if (id->tag) {
|
|
ast_copy_string(buf, id->tag, len);
|
|
}
|
|
} else if (!strncasecmp("ton", data, 3)) {
|
|
snprintf(buf, len, "%d", id->number_type);
|
|
} else if (!strncasecmp("pres", data, 4)) {
|
|
ast_copy_string(buf, ast_named_caller_presentation(id->number_presentation), len);
|
|
} else {
|
|
status = ID_FIELD_UNKNOWN;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************* */
|
|
/*!
|
|
* \internal
|
|
* \brief Read values from the redirecting information struct.
|
|
*
|
|
* \param chan Asterisk channel to read
|
|
* \param cmd Not used
|
|
* \param data Redirecting function datatype string
|
|
* \param buf Buffer to fill with read value.
|
|
* \param len Length of the buffer
|
|
*
|
|
* \retval 0 on success.
|
|
* \retval -1 on error.
|
|
*/
|
|
static int redirecting_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
|
{
|
|
/* Ensure that the buffer is empty */
|
|
*buf = 0;
|
|
|
|
if (!chan)
|
|
return -1;
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
if (!strncasecmp("from-", data, 5)) {
|
|
switch (redirecting_id_read(buf, len, data + 5, &chan->redirecting.from)) {
|
|
case ID_FIELD_VALID:
|
|
case ID_FIELD_INVALID:
|
|
break;
|
|
|
|
default:
|
|
ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
|
|
break;
|
|
}
|
|
} else if (!strncasecmp("to-", data, 3)) {
|
|
switch (redirecting_id_read(buf, len, data + 3, &chan->redirecting.to)) {
|
|
case ID_FIELD_VALID:
|
|
case ID_FIELD_INVALID:
|
|
break;
|
|
|
|
default:
|
|
ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
|
|
break;
|
|
}
|
|
} else if (!strncasecmp("pres", data, 4)) {
|
|
ast_copy_string(buf, ast_named_caller_presentation(chan->redirecting.from.number_presentation), len);
|
|
} else if (!strncasecmp("reason", data, 6)) {
|
|
ast_copy_string(buf, ast_redirecting_reason_name(chan->redirecting.reason), len);
|
|
} else if (!strncasecmp("count", data, 5)) {
|
|
snprintf(buf, len, "%d", chan->redirecting.count);
|
|
} else {
|
|
ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
|
|
}
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************* */
|
|
/*!
|
|
* \internal
|
|
* \brief Write new values to the party id struct
|
|
*
|
|
* \param id Party ID struct to write values
|
|
* \param data Remaining function datatype string
|
|
* \param value Value to assign to the party id.
|
|
*
|
|
* \retval ID_FIELD_VALID on success.
|
|
* \retval ID_FIELD_INVALID on error with field value.
|
|
* \retval ID_FIELD_UNKNOWN on unknown field name.
|
|
*/
|
|
static enum ID_FIELD_STATUS redirecting_id_write(struct ast_party_id *id, char *data, const char *value)
|
|
{
|
|
char *val;
|
|
enum ID_FIELD_STATUS status;
|
|
|
|
status = ID_FIELD_VALID;
|
|
|
|
if (!strncasecmp("all", data, 3)) {
|
|
char name[256];
|
|
char num[256];
|
|
|
|
ast_callerid_split(value, name, sizeof(name), num, sizeof(num));
|
|
if (!(id->name = ast_strdup(name))) {
|
|
return ID_FIELD_INVALID;
|
|
}
|
|
if (!(id->number = ast_strdup(num))) {
|
|
return ID_FIELD_INVALID;
|
|
}
|
|
} else if (!strncasecmp("name", data, 4)) {
|
|
id->name = ast_strdup(value);
|
|
ast_trim_blanks(id->name);
|
|
} else if (!strncasecmp("num", data, 3)) {
|
|
id->number = ast_strdup(value);
|
|
ast_trim_blanks(id->number);
|
|
} else if (!strncasecmp("tag", data, 3)) {
|
|
id->tag = ast_strdup(value);
|
|
ast_trim_blanks(id->tag);
|
|
} else if (!strncasecmp("ton", data, 3)) {
|
|
val = ast_strdupa(value);
|
|
ast_trim_blanks(val);
|
|
|
|
if (('0' <= val[0]) && (val[0] <= '9')) {
|
|
id->number_type = atoi(val);
|
|
} else {
|
|
ast_log(LOG_ERROR, "Unknown redirecting type of number '%s', value unchanged\n", val);
|
|
status = ID_FIELD_INVALID;
|
|
}
|
|
} else if (!strncasecmp("pres", data, 4)) {
|
|
int pres;
|
|
|
|
val = ast_strdupa(value);
|
|
ast_trim_blanks(val);
|
|
|
|
if (('0' <= val[0]) && (val[0] <= '9')) {
|
|
pres = atoi(val);
|
|
} else {
|
|
pres = ast_parse_caller_presentation(val);
|
|
}
|
|
|
|
if (pres < 0) {
|
|
ast_log(LOG_ERROR, "Unknown redirecting number presentation '%s', value unchanged\n", val);
|
|
status = ID_FIELD_INVALID;
|
|
} else {
|
|
id->number_presentation = pres;
|
|
}
|
|
} else {
|
|
status = ID_FIELD_UNKNOWN;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************* */
|
|
/*!
|
|
* \internal
|
|
* \brief Write new values to the redirecting information struct.
|
|
*
|
|
* \param chan Asterisk channel to update
|
|
* \param cmd Not used
|
|
* \param data Redirecting function datatype string
|
|
* \param value Value to assign to the redirecting information struct.
|
|
*
|
|
* \retval 0 on success.
|
|
* \retval -1 on error.
|
|
*/
|
|
static int redirecting_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
|
|
{
|
|
struct ast_party_redirecting redirecting;
|
|
char *val;
|
|
char *option;
|
|
void (*set_it)(struct ast_channel *chan, const struct ast_party_redirecting *redirecting);
|
|
|
|
if (!value || !chan) {
|
|
return -1;
|
|
}
|
|
|
|
/* Determine if the update indication inhibit option is present */
|
|
option = strchr(data, ',');
|
|
if (option) {
|
|
option = ast_skip_blanks(option + 1);
|
|
switch (*option) {
|
|
case 'i':
|
|
set_it = ast_channel_set_redirecting;
|
|
break;
|
|
|
|
default:
|
|
ast_log(LOG_ERROR, "Unknown redirecting option '%s'.\n", option);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
set_it = ast_channel_update_redirecting;
|
|
}
|
|
|
|
ast_channel_lock(chan);
|
|
ast_party_redirecting_set_init(&redirecting, &chan->redirecting);
|
|
ast_channel_unlock(chan);
|
|
|
|
value = ast_skip_blanks(value);
|
|
|
|
if (!strncasecmp("from-", data, 5)) {
|
|
switch (redirecting_id_write(&redirecting.from, data + 5, value)) {
|
|
case ID_FIELD_VALID:
|
|
set_it(chan, &redirecting);
|
|
break;
|
|
|
|
case ID_FIELD_INVALID:
|
|
break;
|
|
|
|
default:
|
|
ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
|
|
break;
|
|
}
|
|
ast_party_redirecting_free(&redirecting);
|
|
} else if (!strncasecmp("to-", data, 3)) {
|
|
switch (redirecting_id_write(&redirecting.to, data + 3, value)) {
|
|
case ID_FIELD_VALID:
|
|
set_it(chan, &redirecting);
|
|
break;
|
|
|
|
case ID_FIELD_INVALID:
|
|
break;
|
|
|
|
default:
|
|
ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
|
|
break;
|
|
}
|
|
ast_party_redirecting_free(&redirecting);
|
|
} else if (!strncasecmp("pres", data, 4)) {
|
|
int pres;
|
|
|
|
val = ast_strdupa(value);
|
|
ast_trim_blanks(val);
|
|
|
|
if (('0' <= val[0]) && (val[0] <= '9')) {
|
|
pres = atoi(val);
|
|
} else {
|
|
pres = ast_parse_caller_presentation(val);
|
|
}
|
|
|
|
if (pres < 0) {
|
|
ast_log(LOG_ERROR, "Unknown redirecting number presentation '%s', value unchanged\n", val);
|
|
} else {
|
|
redirecting.from.number_presentation = pres;
|
|
redirecting.to.number_presentation = pres;
|
|
set_it(chan, &redirecting);
|
|
}
|
|
} else if (!strncasecmp("reason", data, 6)) {
|
|
int reason;
|
|
|
|
val = ast_strdupa(value);
|
|
ast_trim_blanks(val);
|
|
|
|
if (('0' <= val[0]) && (val[0] <= '9')) {
|
|
reason = atoi(val);
|
|
} else {
|
|
reason = ast_redirecting_reason_parse(val);
|
|
}
|
|
|
|
if (reason < 0) {
|
|
ast_log(LOG_ERROR, "Unknown redirecting reason '%s', value unchanged\n", val);
|
|
} else {
|
|
redirecting.reason = reason;
|
|
set_it(chan, &redirecting);
|
|
}
|
|
} else if (!strncasecmp("count", data, 5)) {
|
|
val = ast_strdupa(value);
|
|
ast_trim_blanks(val);
|
|
|
|
if (('0' <= val[0]) && (val[0] <= '9')) {
|
|
redirecting.count = atoi(val);
|
|
set_it(chan, &redirecting);
|
|
} else {
|
|
ast_log(LOG_ERROR, "Unknown redirecting count '%s', value unchanged\n", val);
|
|
}
|
|
} else {
|
|
ast_log(LOG_ERROR, "Unknown redirecting data type '%s'.\n", data);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ast_custom_function redirecting_function = {
|
|
.name = "REDIRECTING",
|
|
.read = redirecting_read,
|
|
.write = redirecting_write,
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************* */
|
|
/*!
|
|
* \internal
|
|
* \brief Unload the function module
|
|
*
|
|
* \retval 0 on success.
|
|
* \retval -1 on error.
|
|
*/
|
|
static int unload_module(void)
|
|
{
|
|
return ast_custom_function_unregister(&redirecting_function);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************* */
|
|
/*!
|
|
* \internal
|
|
* \brief Load and initialize the function module.
|
|
*
|
|
* \retval 0 on success.
|
|
* \retval -1 on error.
|
|
*/
|
|
static int load_module(void)
|
|
{
|
|
return ast_custom_function_register(&redirecting_function)
|
|
? AST_MODULE_LOAD_DECLINE
|
|
: AST_MODULE_LOAD_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Redirecting data dialplan function");
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
/* end func_redirecting.c */
|