dect
/
asterisk
Archived
13
0
Fork 0

Move code from res_features into (new file) main/features.c

git-svn-id: http://svn.digium.com/svn/asterisk/trunk@100039 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
qwell 2008-01-23 23:09:11 +00:00
parent b768b73620
commit 015b65c8bc
8 changed files with 469 additions and 467 deletions

View File

@ -1 +1 @@
33
34

View File

@ -409,6 +409,8 @@ Call Features (res_features) Changes
* Updated the ParkedCall application to allow you to not specify a parking
extension. If you don't specify a parking space to pick up, it will grab
the first one available.
* Added cli command 'features reload' to reload call features from features.conf
* Moved into core asterisk binary.
Language Support Changes
------------------------
@ -498,4 +500,3 @@ Miscellaneous
specifying which socket to use to connect to the running Asterisk daemon
(-s)
* Added logging to 'make update' command. See update.log

View File

@ -32,6 +32,7 @@ void ast_event_init(void); /*!< Provided by event.c */
int ast_device_state_engine_init(void); /*!< Provided by devicestate.c */
int astobj2_init(void); /*!< Provided by astobj2.c */
int ast_file_init(void); /*!< Provided by file.c */
int ast_features_init(void); /*!< Provided by features.c */
/*!
* \brief Reload asterisk modules.

View File

@ -109,4 +109,7 @@ struct ast_call_feature *ast_find_call_feature(const char *name);
void ast_rdlock_call_features(void);
void ast_unlock_call_features(void);
/*! \brief Reload call features from features.conf */
int ast_features_reload(void);
#endif /* _AST_FEATURES_H */

View File

@ -29,7 +29,8 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o
astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o \
features.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static

View File

@ -96,6 +96,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/network.h"
#include "asterisk/cli.h"
#include "asterisk/channel.h"
#include "asterisk/features.h"
#include "asterisk/ulaw.h"
#include "asterisk/alaw.h"
#include "asterisk/callerid.h"
@ -3165,6 +3166,8 @@ int main(int argc, char *argv[])
exit(1);
}
ast_features_init();
if (init_framer()) {
printf(term_quit());
exit(1);

View File

@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
* Copyright (C) 1999 - 2008, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@ -31,6 +31,8 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/_private.h"
#include <pthread.h>
#include <sys/time.h>
#include <sys/signal.h>
@ -126,7 +128,7 @@ static unsigned int atxferdropcall;
static unsigned int atxferloopdelay;
static unsigned int atxfercallbackretries;
static char *registrar = "res_features"; /*!< Registrar for operations */
static char *registrar = "features"; /*!< Registrar for operations */
/* module and CLI command definitions */
static char *synopsis = "Answer a parked call";
@ -599,9 +601,6 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
struct ast_channel *parker;
struct ast_channel *parkee;
int res = 0;
struct ast_module_user *u;
u = ast_module_user_add(chan);
set_peers(&parker, &parkee, peer, chan, sense);
/* Setup the exten/priority to be s/1 since we don't know
@ -615,8 +614,6 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
if (!res)
res = ast_park_call(parkee, parker, 0, NULL);
ast_module_user_remove(u);
if (!res) {
if (sense == FEATURE_SENSE_CHAN)
res = AST_PBX_NO_HANGUP_PEER;
@ -675,7 +672,7 @@ static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *pee
if (callee_chan->monitor) {
ast_verb(4, "User hit '%s' to stop recording call.\n", code);
ast_monitor_stop(callee_chan, 1);
callee_chan->monitor->stop(callee_chan, 1);
return FEATURE_RETURN_SUCCESS;
}
@ -2511,425 +2508,6 @@ static int park_exec(struct ast_channel *chan, void *data)
return res;
}
/*!
* \brief CLI command to list configured features
* \param e
* \param cmd
* \param a
*
* \retval CLI_SUCCESS on success.
* \retval NULL when tab completion is used.
*/
static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) {
int i;
struct ast_call_feature *feature;
char format[] = "%-25s %-7s %-7s\n";
switch (cmd) {
case CLI_INIT:
e->command = "features show";
e->usage =
"Usage: features show\n"
" Lists configured features\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
ast_cli(a->fd, format, "Builtin Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
ast_cli(a->fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
ast_rwlock_rdlock(&features_lock);
for (i = 0; i < FEATURES_COUNT; i++)
ast_cli(a->fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
ast_rwlock_unlock(&features_lock);
ast_cli(a->fd, "\n");
ast_cli(a->fd, format, "Dynamic Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
if (AST_LIST_EMPTY(&feature_list))
ast_cli(a->fd, "(none)\n");
else {
AST_LIST_LOCK(&feature_list);
AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
ast_cli(a->fd, format, feature->sname, "no def", feature->exten);
AST_LIST_UNLOCK(&feature_list);
}
ast_cli(a->fd, "\nCall parking\n");
ast_cli(a->fd, "------------\n");
ast_cli(a->fd,"%-20s: %s\n", "Parking extension", parking_ext);
ast_cli(a->fd,"%-20s: %s\n", "Parking context", parking_con);
ast_cli(a->fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop);
ast_cli(a->fd,"\n");
return CLI_SUCCESS;
}
static char mandescr_bridge[] =
"Description: Bridge together two channels already in the PBX\n"
"Variables: ( Headers marked with * are required )\n"
" *Channel1: Channel to Bridge to Channel2\n"
" *Channel2: Channel to Bridge to Channel1\n"
" Tone: (Yes|No) Play courtesy tone to Channel 2\n"
"\n";
/*!
* \brief Actual bridge
* \param chan
* \param tmpchan
*
* Stop hold music, lock both channels, masq channels,
* after bridge return channel to next priority.
*/
static void do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan)
{
ast_moh_stop(chan);
ast_channel_lock(chan);
ast_setstate(tmpchan, chan->_state);
tmpchan->readformat = chan->readformat;
tmpchan->writeformat = chan->writeformat;
ast_channel_masquerade(tmpchan, chan);
ast_channel_lock(tmpchan);
ast_do_masquerade(tmpchan);
/* when returning from bridge, the channel will continue at the next priority */
ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1);
ast_channel_unlock(tmpchan);
ast_channel_unlock(chan);
}
/*!
* \brief Bridge channels together
* \param s
* \param m
*
* Make sure valid channels were specified,
* send errors if any of the channels could not be found/locked, answer channels if needed,
* create the placeholder channels and grab the other channels
* make the channels compatible, send error if we fail doing so
* setup the bridge thread object and start the bridge.
*
* \retval 0 on success or on incorrect use.
* \retval 1 on failure to bridge channels.
*/
static int action_bridge(struct mansession *s, const struct message *m)
{
const char *channela = astman_get_header(m, "Channel1");
const char *channelb = astman_get_header(m, "Channel2");
const char *playtone = astman_get_header(m, "Tone");
struct ast_channel *chana = NULL, *chanb = NULL;
struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
struct ast_bridge_thread_obj *tobj = NULL;
/* make sure valid channels were specified */
if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) {
chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela));
chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb));
if (chana)
ast_channel_unlock(chana);
if (chanb)
ast_channel_unlock(chanb);
/* send errors if any of the channels could not be found/locked */
if (!chana) {
char buf[256];
snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
astman_send_error(s, m, buf);
return 0;
}
if (!chanb) {
char buf[256];
snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
astman_send_error(s, m, buf);
return 0;
}
} else {
astman_send_error(s, m, "Missing channel parameter in request");
return 0;
}
/* Answer the channels if needed */
if (chana->_state != AST_STATE_UP)
ast_answer(chana);
if (chanb->_state != AST_STATE_UP)
ast_answer(chanb);
/* create the placeholder channels and grab the other channels */
if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
NULL, NULL, 0, "Bridge/%s", chana->name))) {
astman_send_error(s, m, "Unable to create temporary channel!");
return 1;
}
if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
NULL, NULL, 0, "Bridge/%s", chanb->name))) {
astman_send_error(s, m, "Unable to create temporary channels!");
ast_channel_free(tmpchana);
return 1;
}
do_bridge_masquerade(chana, tmpchana);
do_bridge_masquerade(chanb, tmpchanb);
/* make the channels compatible, send error if we fail doing so */
if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name);
astman_send_error(s, m, "Could not make channels compatible for manager bridge");
ast_hangup(tmpchana);
ast_hangup(tmpchanb);
return 1;
}
/* setup the bridge thread object and start the bridge */
if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno));
astman_send_error(s, m, "Unable to spawn a new bridge thread");
ast_hangup(tmpchana);
ast_hangup(tmpchanb);
return 1;
}
tobj->chan = tmpchana;
tobj->peer = tmpchanb;
tobj->return_to_pbx = 1;
if (ast_true(playtone)) {
if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) {
if (ast_waitstream(tmpchanb, "") < 0)
ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name);
}
}
ast_bridge_call_thread_launch(tobj);
astman_send_ack(s, m, "Launched bridge thread with success");
return 0;
}
/*!
* \brief CLI command to list parked calls
* \param e
* \param cmd
* \param a
*
* Check right usage, lock parking lot, display parked calls, unlock parking lot list.
* \retval CLI_SUCCESS on success.
* \retval CLI_SHOWUSAGE on incorrect number of arguments.
* \retval NULL when tab completion is used.
*/
static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct parkeduser *cur;
int numparked = 0;
switch (cmd) {
case CLI_INIT:
e->command = "parkedcalls show";
e->usage =
"Usage: parkedcalls show\n"
" List currently parked calls\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc > e->args)
return CLI_SHOWUSAGE;
ast_cli(a->fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
, "Context", "Extension", "Pri", "Timeout");
AST_LIST_LOCK(&parkinglot);
AST_LIST_TRAVERSE(&parkinglot, cur, list) {
ast_cli(a->fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
,cur->parkingexten, cur->chan->name, cur->context, cur->exten
,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
numparked++;
}
AST_LIST_UNLOCK(&parkinglot);
ast_cli(a->fd, "%d parked call%s.\n", numparked, ESS(numparked));
return CLI_SUCCESS;
}
static char *handle_parkedcalls_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
char *res = handle_parkedcalls(e, cmd, a);
if (cmd == CLI_INIT)
e->command = "show parkedcalls";
return res;
}
static struct ast_cli_entry cli_show_parkedcalls_deprecated = AST_CLI_DEFINE(handle_parkedcalls_deprecated, "List currently parked calls.");
static struct ast_cli_entry cli_features[] = {
AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls", .deprecate_cmd = &cli_show_parkedcalls_deprecated),
};
/*!
* \brief Dump parking lot status
* \param s
* \param m
*
* Lock parking lot, iterate list and append parked calls status, unlock parking lot.
* \return Always RESULT_SUCCESS
*/
static int manager_parking_status(struct mansession *s, const struct message *m)
{
struct parkeduser *cur;
const char *id = astman_get_header(m, "ActionID");
char idText[256] = "";
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
astman_send_ack(s, m, "Parked calls will follow");
AST_LIST_LOCK(&parkinglot);
AST_LIST_TRAVERSE(&parkinglot, cur, list) {
astman_append(s, "Event: ParkedCall\r\n"
"Exten: %d\r\n"
"Channel: %s\r\n"
"From: %s\r\n"
"Timeout: %ld\r\n"
"CallerIDNum: %s\r\n"
"CallerIDName: %s\r\n"
"%s"
"\r\n",
cur->parkingnum, cur->chan->name, cur->peername,
(long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */
S_OR(cur->chan->cid.cid_name, ""),
idText);
}
astman_append(s,
"Event: ParkedCallsComplete\r\n"
"%s"
"\r\n",idText);
AST_LIST_UNLOCK(&parkinglot);
return RESULT_SUCCESS;
}
static char mandescr_park[] =
"Description: Park a channel.\n"
"Variables: (Names marked with * are required)\n"
" *Channel: Channel name to park\n"
" *Channel2: Channel to announce park info to (and return to if timeout)\n"
" Timeout: Number of milliseconds to wait before callback.\n";
/*!
* \brief Create manager event for parked calls
* \param s
* \param m
*
* Get channels involved in park, create event.
* \return Always 0
*/
static int manager_park(struct mansession *s, const struct message *m)
{
const char *channel = astman_get_header(m, "Channel");
const char *channel2 = astman_get_header(m, "Channel2");
const char *timeout = astman_get_header(m, "Timeout");
char buf[BUFSIZ];
int to = 0;
int res = 0;
int parkExt = 0;
struct ast_channel *ch1, *ch2;
if (ast_strlen_zero(channel)) {
astman_send_error(s, m, "Channel not specified");
return 0;
}
if (ast_strlen_zero(channel2)) {
astman_send_error(s, m, "Channel2 not specified");
return 0;
}
ch1 = ast_get_channel_by_name_locked(channel);
if (!ch1) {
snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
astman_send_error(s, m, buf);
return 0;
}
ch2 = ast_get_channel_by_name_locked(channel2);
if (!ch2) {
snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
astman_send_error(s, m, buf);
ast_channel_unlock(ch1);
return 0;
}
if (!ast_strlen_zero(timeout)) {
sscanf(timeout, "%d", &to);
}
res = ast_masq_park_call(ch1, ch2, to, &parkExt);
if (!res) {
ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
astman_send_ack(s, m, "Park successful");
} else {
astman_send_error(s, m, "Park failure");
}
ast_channel_unlock(ch1);
ast_channel_unlock(ch2);
return 0;
}
/*!
* \brief Pickup a call
* \param chan channel that initiated pickup.
*
* Walk list of channels, checking it is not itself, channel is pbx one,
* check that the callgroup for both channels are the same and the channel is ringing.
* Answer calling channel, flag channel as answered on queue, masq channels together.
*/
int ast_pickup_call(struct ast_channel *chan)
{
struct ast_channel *cur = NULL;
int res = -1;
while ((cur = ast_channel_walk_locked(cur)) != NULL) {
if (!cur->pbx &&
(cur != chan) &&
(chan->pickupgroup & cur->callgroup) &&
((cur->_state == AST_STATE_RINGING) ||
(cur->_state == AST_STATE_RING))) {
break;
}
ast_channel_unlock(cur);
}
if (cur) {
ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
res = ast_answer(chan);
if (res)
ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
res = ast_queue_control(chan, AST_CONTROL_ANSWER);
if (res)
ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
res = ast_channel_masquerade(cur, chan);
if (res)
ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */
ast_channel_unlock(cur);
} else {
ast_debug(1, "No call pickup possible...\n");
}
return res;
}
/*!
* \brief Add parking hints for all defined parking lots
* \param context
@ -2949,7 +2527,6 @@ static void park_add_hints(char *context, int start, int stop)
}
}
static int load_config(void)
{
int start = 0, end = 0;
@ -3005,7 +2582,7 @@ static int load_config(void)
cfg = ast_config_load("features.conf", config_flags);
if (!cfg) {
ast_log(LOG_WARNING,"Could not load features.conf\n");
return AST_MODULE_LOAD_DECLINE;
return 0;
}
for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
if (!strcasecmp(var->name, "parkext")) {
@ -3240,6 +2817,451 @@ static int load_config(void)
}
/*!
* \brief CLI command to list configured features
* \param e
* \param cmd
* \param a
*
* \retval CLI_SUCCESS on success.
* \retval NULL when tab completion is used.
*/
static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int i;
struct ast_call_feature *feature;
char format[] = "%-25s %-7s %-7s\n";
switch (cmd) {
case CLI_INIT:
e->command = "features show";
e->usage =
"Usage: features show\n"
" Lists configured features\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
ast_cli(a->fd, format, "Builtin Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
ast_cli(a->fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
ast_rwlock_rdlock(&features_lock);
for (i = 0; i < FEATURES_COUNT; i++)
ast_cli(a->fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
ast_rwlock_unlock(&features_lock);
ast_cli(a->fd, "\n");
ast_cli(a->fd, format, "Dynamic Feature", "Default", "Current");
ast_cli(a->fd, format, "---------------", "-------", "-------");
if (AST_LIST_EMPTY(&feature_list))
ast_cli(a->fd, "(none)\n");
else {
AST_LIST_LOCK(&feature_list);
AST_LIST_TRAVERSE(&feature_list, feature, feature_entry)
ast_cli(a->fd, format, feature->sname, "no def", feature->exten);
AST_LIST_UNLOCK(&feature_list);
}
ast_cli(a->fd, "\nCall parking\n");
ast_cli(a->fd, "------------\n");
ast_cli(a->fd,"%-20s: %s\n", "Parking extension", parking_ext);
ast_cli(a->fd,"%-20s: %s\n", "Parking context", parking_con);
ast_cli(a->fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop);
ast_cli(a->fd,"\n");
return CLI_SUCCESS;
}
int ast_features_reload(void)
{
load_config();
return RESULT_SUCCESS;
}
static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "features reload";
e->usage =
"Usage: features reload\n"
" Reloads configured call features from features.conf\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
load_config();
return CLI_SUCCESS;
}
static char mandescr_bridge[] =
"Description: Bridge together two channels already in the PBX\n"
"Variables: ( Headers marked with * are required )\n"
" *Channel1: Channel to Bridge to Channel2\n"
" *Channel2: Channel to Bridge to Channel1\n"
" Tone: (Yes|No) Play courtesy tone to Channel 2\n"
"\n";
/*!
* \brief Actual bridge
* \param chan
* \param tmpchan
*
* Stop hold music, lock both channels, masq channels,
* after bridge return channel to next priority.
*/
static void do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan)
{
ast_moh_stop(chan);
ast_channel_lock(chan);
ast_setstate(tmpchan, chan->_state);
tmpchan->readformat = chan->readformat;
tmpchan->writeformat = chan->writeformat;
ast_channel_masquerade(tmpchan, chan);
ast_channel_lock(tmpchan);
ast_do_masquerade(tmpchan);
/* when returning from bridge, the channel will continue at the next priority */
ast_explicit_goto(tmpchan, chan->context, chan->exten, chan->priority + 1);
ast_channel_unlock(tmpchan);
ast_channel_unlock(chan);
}
/*!
* \brief Bridge channels together
* \param s
* \param m
*
* Make sure valid channels were specified,
* send errors if any of the channels could not be found/locked, answer channels if needed,
* create the placeholder channels and grab the other channels
* make the channels compatible, send error if we fail doing so
* setup the bridge thread object and start the bridge.
*
* \retval 0 on success or on incorrect use.
* \retval 1 on failure to bridge channels.
*/
static int action_bridge(struct mansession *s, const struct message *m)
{
const char *channela = astman_get_header(m, "Channel1");
const char *channelb = astman_get_header(m, "Channel2");
const char *playtone = astman_get_header(m, "Tone");
struct ast_channel *chana = NULL, *chanb = NULL;
struct ast_channel *tmpchana = NULL, *tmpchanb = NULL;
struct ast_bridge_thread_obj *tobj = NULL;
/* make sure valid channels were specified */
if (!ast_strlen_zero(channela) && !ast_strlen_zero(channelb)) {
chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela));
chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb));
if (chana)
ast_channel_unlock(chana);
if (chanb)
ast_channel_unlock(chanb);
/* send errors if any of the channels could not be found/locked */
if (!chana) {
char buf[256];
snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela);
astman_send_error(s, m, buf);
return 0;
}
if (!chanb) {
char buf[256];
snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb);
astman_send_error(s, m, buf);
return 0;
}
} else {
astman_send_error(s, m, "Missing channel parameter in request");
return 0;
}
/* Answer the channels if needed */
if (chana->_state != AST_STATE_UP)
ast_answer(chana);
if (chanb->_state != AST_STATE_UP)
ast_answer(chanb);
/* create the placeholder channels and grab the other channels */
if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
NULL, NULL, 0, "Bridge/%s", chana->name))) {
astman_send_error(s, m, "Unable to create temporary channel!");
return 1;
}
if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
NULL, NULL, 0, "Bridge/%s", chanb->name))) {
astman_send_error(s, m, "Unable to create temporary channels!");
ast_channel_free(tmpchana);
return 1;
}
do_bridge_masquerade(chana, tmpchana);
do_bridge_masquerade(chanb, tmpchanb);
/* make the channels compatible, send error if we fail doing so */
if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", tmpchana->name, tmpchanb->name);
astman_send_error(s, m, "Could not make channels compatible for manager bridge");
ast_hangup(tmpchana);
ast_hangup(tmpchanb);
return 1;
}
/* setup the bridge thread object and start the bridge */
if (!(tobj = ast_calloc(1, sizeof(*tobj)))) {
ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", tmpchana->name, tmpchanb->name, strerror(errno));
astman_send_error(s, m, "Unable to spawn a new bridge thread");
ast_hangup(tmpchana);
ast_hangup(tmpchanb);
return 1;
}
tobj->chan = tmpchana;
tobj->peer = tmpchanb;
tobj->return_to_pbx = 1;
if (ast_true(playtone)) {
if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, tmpchanb->language)) {
if (ast_waitstream(tmpchanb, "") < 0)
ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", tmpchanb->name);
}
}
ast_bridge_call_thread_launch(tobj);
astman_send_ack(s, m, "Launched bridge thread with success");
return 0;
}
/*!
* \brief CLI command to list parked calls
* \param e
* \param cmd
* \param a
*
* Check right usage, lock parking lot, display parked calls, unlock parking lot list.
* \retval CLI_SUCCESS on success.
* \retval CLI_SHOWUSAGE on incorrect number of arguments.
* \retval NULL when tab completion is used.
*/
static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct parkeduser *cur;
int numparked = 0;
switch (cmd) {
case CLI_INIT:
e->command = "parkedcalls show";
e->usage =
"Usage: parkedcalls show\n"
" List currently parked calls\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc > e->args)
return CLI_SHOWUSAGE;
ast_cli(a->fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
, "Context", "Extension", "Pri", "Timeout");
AST_LIST_LOCK(&parkinglot);
AST_LIST_TRAVERSE(&parkinglot, cur, list) {
ast_cli(a->fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
,cur->parkingexten, cur->chan->name, cur->context, cur->exten
,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
numparked++;
}
AST_LIST_UNLOCK(&parkinglot);
ast_cli(a->fd, "%d parked call%s.\n", numparked, ESS(numparked));
return CLI_SUCCESS;
}
static char *handle_parkedcalls_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
char *res = handle_parkedcalls(e, cmd, a);
if (cmd == CLI_INIT)
e->command = "show parkedcalls";
return res;
}
static struct ast_cli_entry cli_show_parkedcalls_deprecated = AST_CLI_DEFINE(handle_parkedcalls_deprecated, "List currently parked calls.");
static struct ast_cli_entry cli_features[] = {
AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
AST_CLI_DEFINE(handle_features_reload, "Reloads configured features"),
AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls", .deprecate_cmd = &cli_show_parkedcalls_deprecated),
};
/*!
* \brief Dump parking lot status
* \param s
* \param m
*
* Lock parking lot, iterate list and append parked calls status, unlock parking lot.
* \return Always RESULT_SUCCESS
*/
static int manager_parking_status(struct mansession *s, const struct message *m)
{
struct parkeduser *cur;
const char *id = astman_get_header(m, "ActionID");
char idText[256] = "";
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
astman_send_ack(s, m, "Parked calls will follow");
AST_LIST_LOCK(&parkinglot);
AST_LIST_TRAVERSE(&parkinglot, cur, list) {
astman_append(s, "Event: ParkedCall\r\n"
"Exten: %d\r\n"
"Channel: %s\r\n"
"From: %s\r\n"
"Timeout: %ld\r\n"
"CallerIDNum: %s\r\n"
"CallerIDName: %s\r\n"
"%s"
"\r\n",
cur->parkingnum, cur->chan->name, cur->peername,
(long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */
S_OR(cur->chan->cid.cid_name, ""),
idText);
}
astman_append(s,
"Event: ParkedCallsComplete\r\n"
"%s"
"\r\n",idText);
AST_LIST_UNLOCK(&parkinglot);
return RESULT_SUCCESS;
}
static char mandescr_park[] =
"Description: Park a channel.\n"
"Variables: (Names marked with * are required)\n"
" *Channel: Channel name to park\n"
" *Channel2: Channel to announce park info to (and return to if timeout)\n"
" Timeout: Number of milliseconds to wait before callback.\n";
/*!
* \brief Create manager event for parked calls
* \param s
* \param m
*
* Get channels involved in park, create event.
* \return Always 0
*/
static int manager_park(struct mansession *s, const struct message *m)
{
const char *channel = astman_get_header(m, "Channel");
const char *channel2 = astman_get_header(m, "Channel2");
const char *timeout = astman_get_header(m, "Timeout");
char buf[BUFSIZ];
int to = 0;
int res = 0;
int parkExt = 0;
struct ast_channel *ch1, *ch2;
if (ast_strlen_zero(channel)) {
astman_send_error(s, m, "Channel not specified");
return 0;
}
if (ast_strlen_zero(channel2)) {
astman_send_error(s, m, "Channel2 not specified");
return 0;
}
ch1 = ast_get_channel_by_name_locked(channel);
if (!ch1) {
snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
astman_send_error(s, m, buf);
return 0;
}
ch2 = ast_get_channel_by_name_locked(channel2);
if (!ch2) {
snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
astman_send_error(s, m, buf);
ast_channel_unlock(ch1);
return 0;
}
if (!ast_strlen_zero(timeout)) {
sscanf(timeout, "%d", &to);
}
res = ast_masq_park_call(ch1, ch2, to, &parkExt);
if (!res) {
ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
astman_send_ack(s, m, "Park successful");
} else {
astman_send_error(s, m, "Park failure");
}
ast_channel_unlock(ch1);
ast_channel_unlock(ch2);
return 0;
}
/*!
* \brief Pickup a call
* \param chan channel that initiated pickup.
*
* Walk list of channels, checking it is not itself, channel is pbx one,
* check that the callgroup for both channels are the same and the channel is ringing.
* Answer calling channel, flag channel as answered on queue, masq channels together.
*/
int ast_pickup_call(struct ast_channel *chan)
{
struct ast_channel *cur = NULL;
int res = -1;
while ((cur = ast_channel_walk_locked(cur)) != NULL) {
if (!cur->pbx &&
(cur != chan) &&
(chan->pickupgroup & cur->callgroup) &&
((cur->_state == AST_STATE_RINGING) ||
(cur->_state == AST_STATE_RING))) {
break;
}
ast_channel_unlock(cur);
}
if (cur) {
ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
res = ast_answer(chan);
if (res)
ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
res = ast_queue_control(chan, AST_CONTROL_ANSWER);
if (res)
ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
res = ast_channel_masquerade(cur, chan);
if (res)
ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */
ast_channel_unlock(cur);
} else {
ast_debug(1, "No call pickup possible...\n");
}
return res;
}
static char *app_bridge = "Bridge";
static char *bridge_synopsis = "Bridge two channels";
static char *bridge_descrip =
@ -3388,16 +3410,11 @@ static int bridge_exec(struct ast_channel *chan, void *data)
return 0;
}
static int reload(void)
{
return load_config();
}
static int load_module(void)
int ast_features_init(void)
{
int res;
ast_register_application(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip);
ast_register_application2(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip, NULL);
memset(parking_ext, 0, sizeof(parking_ext));
memset(parking_con, 0, sizeof(parking_con));
@ -3406,9 +3423,9 @@ static int load_module(void)
return res;
ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
res = ast_register_application2(parkedcall, park_exec, synopsis, descrip, NULL);
if (!res)
res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
res = ast_register_application2(parkcall, park_call_exec, synopsis2, descrip2, NULL);
if (!res) {
ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls");
ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
@ -3420,29 +3437,3 @@ static int load_module(void)
return res;
}
static int unload_module(void)
{
struct ast_context *con;
ast_manager_unregister("ParkedCalls");
ast_manager_unregister("Bridge");
ast_manager_unregister("Park");
ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
ast_unregister_application(parkcall);
ast_unregister_application(app_bridge);
ast_devstate_prov_del("Park");
con = ast_context_find(parking_con);
if (con)
ast_context_destroy(con, registrar);
con = ast_context_find(parking_con_dial);
if (con)
ast_context_destroy(con, registrar);
return ast_unregister_application(parkedcall);
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Features Resource",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

View File

@ -46,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/rtp.h"
#include "asterisk/http.h"
#include "asterisk/lock.h"
#include "asterisk/features.h"
#ifdef DLFCNCOMPAT
#include "asterisk/dlfcn-compat.h"
@ -247,6 +248,7 @@ static struct reload_classes {
{ "rtp", ast_rtp_reload },
{ "http", ast_http_reload },
{ "logger", logger_reload },
{ "features", ast_features_reload },
{ NULL, NULL }
};