mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-08 22:27:07 +00:00
Make features configurable and easier to implement
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4650 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -1135,12 +1135,18 @@ static int dial_exec(struct ast_channel *chan, void *data)
|
|||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
memset(&config,0,sizeof(struct ast_bridge_config));
|
memset(&config,0,sizeof(struct ast_bridge_config));
|
||||||
config.play_to_caller=play_to_caller;
|
if (play_to_caller)
|
||||||
config.play_to_callee=play_to_callee;
|
config.features_caller |= AST_FEATURE_PLAY_WARNING;
|
||||||
config.allowredirect_in = allowredir_in;
|
if (play_to_callee)
|
||||||
config.allowredirect_out = allowredir_out;
|
config.features_callee |= AST_FEATURE_PLAY_WARNING;
|
||||||
config.allowdisconnect_in = allowdisconnect_in;
|
if (allowredir_in)
|
||||||
config.allowdisconnect_out = allowdisconnect_out;
|
config.features_callee |= AST_FEATURE_REDIRECT;
|
||||||
|
if (allowredir_out)
|
||||||
|
config.features_caller |= AST_FEATURE_REDIRECT;
|
||||||
|
if (allowdisconnect_in)
|
||||||
|
config.features_callee |= AST_FEATURE_DISCONNECT;
|
||||||
|
if (allowdisconnect_out)
|
||||||
|
config.features_caller |= AST_FEATURE_DISCONNECT;
|
||||||
config.timelimit = timelimit;
|
config.timelimit = timelimit;
|
||||||
config.play_warning = play_warning;
|
config.play_warning = play_warning;
|
||||||
config.warning_freq = warning_freq;
|
config.warning_freq = warning_freq;
|
||||||
|
@@ -1480,10 +1480,14 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
|
|||||||
time(&callstart);
|
time(&callstart);
|
||||||
|
|
||||||
memset(&config,0,sizeof(struct ast_bridge_config));
|
memset(&config,0,sizeof(struct ast_bridge_config));
|
||||||
config.allowredirect_in = ast_test_flag(&flags, QUEUE_FLAG_REDIR_IN);
|
if (ast_test_flag(&flags, QUEUE_FLAG_REDIR_IN))
|
||||||
config.allowredirect_out = ast_test_flag(&flags, QUEUE_FLAG_REDIR_OUT);
|
config.features_callee |= AST_FEATURE_REDIRECT;
|
||||||
config.allowdisconnect_in = ast_test_flag(&flags, QUEUE_FLAG_DISCON_IN);
|
if (ast_test_flag(&flags, QUEUE_FLAG_REDIR_OUT))
|
||||||
config.allowdisconnect_out = ast_test_flag(&flags, QUEUE_FLAG_DISCON_OUT);
|
config.features_caller |= AST_FEATURE_REDIRECT;
|
||||||
|
if (ast_test_flag(&flags, QUEUE_FLAG_DISCON_IN))
|
||||||
|
config.features_callee |= AST_FEATURE_DISCONNECT;
|
||||||
|
if (ast_test_flag(&flags, QUEUE_FLAG_DISCON_OUT))
|
||||||
|
config.features_caller |= AST_FEATURE_DISCONNECT;
|
||||||
bridge = ast_bridge_call(qe->chan,peer,&config);
|
bridge = ast_bridge_call(qe->chan,peer,&config);
|
||||||
|
|
||||||
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
|
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
|
||||||
|
27
channel.c
27
channel.c
@@ -2600,7 +2600,6 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
{
|
{
|
||||||
/* Copy voice back and forth between the two channels. Give the peer
|
/* Copy voice back and forth between the two channels. Give the peer
|
||||||
the ability to transfer calls with '#<extension' syntax. */
|
the ability to transfer calls with '#<extension' syntax. */
|
||||||
int flags;
|
|
||||||
struct ast_channel *cs[3];
|
struct ast_channel *cs[3];
|
||||||
int to = -1;
|
int to = -1;
|
||||||
struct ast_frame *f;
|
struct ast_frame *f;
|
||||||
@@ -2614,8 +2613,6 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
long elapsed_ms=0, time_left_ms=0;
|
long elapsed_ms=0, time_left_ms=0;
|
||||||
int playit=0, playitagain=1, first_time=1;
|
int playit=0, playitagain=1, first_time=1;
|
||||||
|
|
||||||
flags = (config->allowdisconnect_out||config->allowredirect_out ? AST_BRIDGE_DTMF_CHANNEL_0 : 0) + (config->allowdisconnect_in||config->allowredirect_in ? AST_BRIDGE_DTMF_CHANNEL_1 : 0);
|
|
||||||
|
|
||||||
*fo = NULL;
|
*fo = NULL;
|
||||||
firstpass = config->firstpass;
|
firstpass = config->firstpass;
|
||||||
config->firstpass = 0;
|
config->firstpass = 0;
|
||||||
@@ -2624,9 +2621,9 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
gettimeofday(&start_time,NULL);
|
gettimeofday(&start_time,NULL);
|
||||||
time_left_ms = config->timelimit;
|
time_left_ms = config->timelimit;
|
||||||
|
|
||||||
if (config->play_to_caller && config->start_sound && firstpass)
|
if ((config->features_caller & AST_FEATURE_PLAY_WARNING) && config->start_sound && firstpass)
|
||||||
bridge_playfile(c0,c1,config->start_sound,time_left_ms / 1000);
|
bridge_playfile(c0,c1,config->start_sound,time_left_ms / 1000);
|
||||||
if (config->play_to_callee && config->start_sound && firstpass)
|
if ((config->features_callee & AST_FEATURE_PLAY_WARNING) && config->start_sound && firstpass)
|
||||||
bridge_playfile(c1,c0,config->start_sound,time_left_ms / 1000);
|
bridge_playfile(c1,c0,config->start_sound,time_left_ms / 1000);
|
||||||
|
|
||||||
/* Stop if we're a zombie or need a soft hangup */
|
/* Stop if we're a zombie or need a soft hangup */
|
||||||
@@ -2664,7 +2661,7 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
elapsed_ms = tvdiff(&precise_now,&start_time);
|
elapsed_ms = tvdiff(&precise_now,&start_time);
|
||||||
time_left_ms = config->timelimit - elapsed_ms;
|
time_left_ms = config->timelimit - elapsed_ms;
|
||||||
|
|
||||||
if (playitagain && (config->play_to_caller || config->play_to_callee) && (config->play_warning && time_left_ms <= config->play_warning)) {
|
if (playitagain && ((config->features_caller & AST_FEATURE_PLAY_WARNING) || (config->features_callee & AST_FEATURE_PLAY_WARNING)) && (config->play_warning && time_left_ms <= config->play_warning)) {
|
||||||
/* narrowing down to the end */
|
/* narrowing down to the end */
|
||||||
if (config->warning_freq == 0) {
|
if (config->warning_freq == 0) {
|
||||||
playit = 1;
|
playit = 1;
|
||||||
@@ -2680,9 +2677,9 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (time_left_ms <= 0) {
|
if (time_left_ms <= 0) {
|
||||||
if (config->play_to_caller && config->end_sound)
|
if ((config->features_caller & AST_FEATURE_PLAY_WARNING) && config->end_sound)
|
||||||
bridge_playfile(c0,c1,config->end_sound,0);
|
bridge_playfile(c0,c1,config->end_sound,0);
|
||||||
if (config->play_to_callee && config->end_sound)
|
if ((config->features_callee & AST_FEATURE_PLAY_WARNING) && config->end_sound)
|
||||||
bridge_playfile(c1,c0,config->end_sound,0);
|
bridge_playfile(c1,c0,config->end_sound,0);
|
||||||
*fo = NULL;
|
*fo = NULL;
|
||||||
if (who) *rc = who;
|
if (who) *rc = who;
|
||||||
@@ -2690,9 +2687,9 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (time_left_ms >= 5000 && playit) {
|
if (time_left_ms >= 5000 && playit) {
|
||||||
if (config->play_to_caller && config->warning_sound && config->play_warning)
|
if ((config->features_caller & AST_FEATURE_PLAY_WARNING) && config->warning_sound && config->play_warning)
|
||||||
bridge_playfile(c0,c1,config->warning_sound,time_left_ms / 1000);
|
bridge_playfile(c0,c1,config->warning_sound,time_left_ms / 1000);
|
||||||
if (config->play_to_callee && config->warning_sound && config->play_warning)
|
if ((config->features_callee & AST_FEATURE_PLAY_WARNING) && config->warning_sound && config->play_warning)
|
||||||
bridge_playfile(c1,c0,config->warning_sound,time_left_ms / 1000);
|
bridge_playfile(c1,c0,config->warning_sound,time_left_ms / 1000);
|
||||||
playit = 0;
|
playit = 0;
|
||||||
}
|
}
|
||||||
@@ -2711,7 +2708,7 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
/* Looks like they share a bridge code */
|
/* Looks like they share a bridge code */
|
||||||
if (option_verbose > 2)
|
if (option_verbose > 2)
|
||||||
ast_verbose(VERBOSE_PREFIX_3 "Attempting native bridge of %s and %s\n", c0->name, c1->name);
|
ast_verbose(VERBOSE_PREFIX_3 "Attempting native bridge of %s and %s\n", c0->name, c1->name);
|
||||||
if (!(res = c0->pvt->bridge(c0, c1, flags, fo, rc))) {
|
if (!(res = c0->pvt->bridge(c0, c1, config->flags, fo, rc))) {
|
||||||
c0->_bridge = NULL;
|
c0->_bridge = NULL;
|
||||||
c1->_bridge = NULL;
|
c1->_bridge = NULL;
|
||||||
manager_event(EVENT_FLAG_CALL, "Unlink",
|
manager_event(EVENT_FLAG_CALL, "Unlink",
|
||||||
@@ -2759,7 +2756,7 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
|
if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
|
||||||
*fo = f;
|
*fo = f;
|
||||||
*rc = who;
|
*rc = who;
|
||||||
res = 0;
|
res = 0;
|
||||||
@@ -2772,9 +2769,9 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
(f->frametype == AST_FRAME_IMAGE) ||
|
(f->frametype == AST_FRAME_IMAGE) ||
|
||||||
(f->frametype == AST_FRAME_DTMF)) {
|
(f->frametype == AST_FRAME_DTMF)) {
|
||||||
if ((f->frametype == AST_FRAME_DTMF) &&
|
if ((f->frametype == AST_FRAME_DTMF) &&
|
||||||
(flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
|
(config->flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
|
||||||
if ((who == c0)) {
|
if ((who == c0)) {
|
||||||
if ((flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
|
if ((config->flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
|
||||||
*rc = c0;
|
*rc = c0;
|
||||||
*fo = f;
|
*fo = f;
|
||||||
/* Take out of conference mode */
|
/* Take out of conference mode */
|
||||||
@@ -2785,7 +2782,7 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct as
|
|||||||
goto tackygoto;
|
goto tackygoto;
|
||||||
} else
|
} else
|
||||||
if ((who == c1)) {
|
if ((who == c1)) {
|
||||||
if (flags & AST_BRIDGE_DTMF_CHANNEL_1) {
|
if (config->flags & AST_BRIDGE_DTMF_CHANNEL_1) {
|
||||||
*rc = c1;
|
*rc = c1;
|
||||||
*fo = f;
|
*fo = f;
|
||||||
res = 0;
|
res = 0;
|
||||||
|
@@ -13,3 +13,9 @@ context => parkedcalls ; Which context parked calls are in
|
|||||||
; when someone dials a parked call
|
; when someone dials a parked call
|
||||||
;adsipark = yes ; if you want ADSI parking announcements
|
;adsipark = yes ; if you want ADSI parking announcements
|
||||||
;pickupexten = *8 ; Configure the pickup extension. Default is *8
|
;pickupexten = *8 ; Configure the pickup extension. Default is *8
|
||||||
|
;featuredigittimeout = 500 ; Max time (ms) between digits for
|
||||||
|
; feature activation. Default is 500
|
||||||
|
|
||||||
|
[featuremap]
|
||||||
|
;blindxfer => # ; Blind transfer
|
||||||
|
;disconnect => * ; Disconnect
|
||||||
|
@@ -233,13 +233,15 @@ struct ast_channel {
|
|||||||
#define AST_FLAG_EXCEPTION (1 << 5) /* if there is a pending exception */
|
#define AST_FLAG_EXCEPTION (1 << 5) /* if there is a pending exception */
|
||||||
#define AST_FLAG_MOH (1 << 6) /* XXX anthm promises me this will disappear XXX listening to moh */
|
#define AST_FLAG_MOH (1 << 6) /* XXX anthm promises me this will disappear XXX listening to moh */
|
||||||
|
|
||||||
|
#define AST_FEATURE_PLAY_WARNING (1 << 0)
|
||||||
|
#define AST_FEATURE_REDIRECT (1 << 1)
|
||||||
|
#define AST_FEATURE_DISCONNECT (1 << 2)
|
||||||
|
|
||||||
|
#define AST_FEATURE_FLAG_NEEDSDTMF (1 << 0)
|
||||||
|
|
||||||
struct ast_bridge_config {
|
struct ast_bridge_config {
|
||||||
int play_to_caller;
|
unsigned int features_caller;
|
||||||
int play_to_callee;
|
unsigned int features_callee;
|
||||||
int allowredirect_in;
|
|
||||||
int allowredirect_out;
|
|
||||||
int allowdisconnect_in;
|
|
||||||
int allowdisconnect_out;
|
|
||||||
long timelimit;
|
long timelimit;
|
||||||
long play_warning;
|
long play_warning;
|
||||||
long warning_freq;
|
long warning_freq;
|
||||||
@@ -247,6 +249,7 @@ struct ast_bridge_config {
|
|||||||
char *end_sound;
|
char *end_sound;
|
||||||
char *start_sound;
|
char *start_sound;
|
||||||
int firstpass;
|
int firstpass;
|
||||||
|
int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chanmon;
|
struct chanmon;
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include <asterisk/options.h>
|
#include <asterisk/options.h>
|
||||||
#include <asterisk/module.h>
|
#include <asterisk/module.h>
|
||||||
#include <asterisk/translate.h>
|
#include <asterisk/translate.h>
|
||||||
|
#include <asterisk/app.h>
|
||||||
#include <asterisk/say.h>
|
#include <asterisk/say.h>
|
||||||
#include <asterisk/channel_pvt.h>
|
#include <asterisk/channel_pvt.h>
|
||||||
#include <asterisk/features.h>
|
#include <asterisk/features.h>
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
|
|
||||||
#define DEFAULT_PARK_TIME 45000
|
#define DEFAULT_PARK_TIME 45000
|
||||||
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
|
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
|
||||||
|
#define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
|
||||||
|
|
||||||
static char *parkedcall = "ParkedCall";
|
static char *parkedcall = "ParkedCall";
|
||||||
|
|
||||||
@@ -67,6 +69,7 @@ static int parking_stop = 750;
|
|||||||
static int adsipark = 0;
|
static int adsipark = 0;
|
||||||
|
|
||||||
static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
|
static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
|
||||||
|
static int featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
|
||||||
|
|
||||||
/* Default courtesy tone played when party joins conference */
|
/* Default courtesy tone played when party joins conference */
|
||||||
static char courtesytone[256] = "";
|
static char courtesytone[256] = "";
|
||||||
@@ -295,24 +298,258 @@ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define FEATURE_RETURN_HANGUP -1
|
||||||
|
#define FEATURE_RETURN_SUCCESSBREAK 0
|
||||||
|
#define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
|
||||||
|
#define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
|
||||||
|
#define FEATURE_RETURN_PASSDIGITS 21
|
||||||
|
#define FEATURE_RETURN_STOREDIGITS 22
|
||||||
|
#define FEATURE_RETURN_SUCCESS 23
|
||||||
|
|
||||||
|
#define FEATURE_SENSE_CHAN (1 << 0)
|
||||||
|
#define FEATURE_SENSE_PEER (1 << 1)
|
||||||
|
#define FEATURE_MAX_LEN 11
|
||||||
|
|
||||||
|
static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
|
||||||
|
{
|
||||||
|
if (option_verbose > 3)
|
||||||
|
ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
|
||||||
|
return FEATURE_RETURN_HANGUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
|
||||||
|
{
|
||||||
|
struct ast_channel *transferer;
|
||||||
|
struct ast_channel *transferee;
|
||||||
|
char *transferer_real_context;
|
||||||
|
char newext[256], *ptr;
|
||||||
|
int res;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
ast_log(LOG_NOTICE, "XXX Blind Transfer %s, %s (sense=%d) XXX\n", chan->name, peer->name, sense);
|
||||||
|
if (sense == FEATURE_SENSE_PEER) {
|
||||||
|
transferer = peer;
|
||||||
|
transferee = chan;
|
||||||
|
} else {
|
||||||
|
transferer = chan;
|
||||||
|
transferee = peer;
|
||||||
|
}
|
||||||
|
if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
|
||||||
|
!(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
|
||||||
|
/* Use the non-macro context to transfer the call */
|
||||||
|
if (!ast_strlen_zero(transferer->macrocontext))
|
||||||
|
transferer_real_context = transferer->macrocontext;
|
||||||
|
else
|
||||||
|
transferer_real_context = transferer->context;
|
||||||
|
}
|
||||||
|
/* Start autoservice on chan while we talk
|
||||||
|
to the originator */
|
||||||
|
ast_autoservice_start(transferee);
|
||||||
|
ast_moh_start(transferee, NULL);
|
||||||
|
|
||||||
|
memset(newext, 0, sizeof(newext));
|
||||||
|
ptr = newext;
|
||||||
|
|
||||||
|
/* Transfer */
|
||||||
|
if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
|
||||||
|
ast_moh_stop(transferee);
|
||||||
|
ast_autoservice_stop(transferee);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
|
||||||
|
ast_moh_stop(transferee);
|
||||||
|
ast_autoservice_stop(transferee);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
ast_stopstream(transferer);
|
||||||
|
if (res > 0) {
|
||||||
|
/* If they've typed a digit already, handle it */
|
||||||
|
newext[0] = res;
|
||||||
|
ptr++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
res = 0;
|
||||||
|
while (strlen(newext) < sizeof(newext) - 1) {
|
||||||
|
res = ast_waitfordigit(transferer, transferdigittimeout);
|
||||||
|
if (res < 1)
|
||||||
|
break;
|
||||||
|
if (res == '#')
|
||||||
|
break;
|
||||||
|
*(ptr++) = res;
|
||||||
|
if (!ast_matchmore_extension(transferer, transferer_real_context, newext, 1, transferer->cid.cid_num))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
ast_moh_stop(transferee);
|
||||||
|
ast_autoservice_stop(transferee);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (!strcmp(newext, ast_parking_ext())) {
|
||||||
|
ast_moh_stop(transferee);
|
||||||
|
|
||||||
|
if (ast_autoservice_stop(transferee))
|
||||||
|
res = -1;
|
||||||
|
else if (!ast_park_call(transferee, transferer, 0, NULL)) {
|
||||||
|
/* We return non-zero, but tell the PBX not to hang the channel when
|
||||||
|
the thread dies -- We have to be careful now though. We are responsible for
|
||||||
|
hanging up the channel, else it will never be hung up! */
|
||||||
|
|
||||||
|
if (transferer==peer)
|
||||||
|
res=AST_PBX_KEEPALIVE;
|
||||||
|
else
|
||||||
|
res=AST_PBX_NO_HANGUP_PEER;
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
|
||||||
|
}
|
||||||
|
/* XXX Maybe we should have another message here instead of invalid extension XXX */
|
||||||
|
} else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
|
||||||
|
pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
|
||||||
|
pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
|
||||||
|
ast_moh_stop(transferee);
|
||||||
|
res=ast_autoservice_stop(transferee);
|
||||||
|
if (!transferee->pbx) {
|
||||||
|
/* Doh! Use our handy async_goto functions */
|
||||||
|
if (option_verbose > 2)
|
||||||
|
ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
|
||||||
|
,transferee->name, newext, transferer_real_context);
|
||||||
|
if (ast_async_goto(transferee, transferer_real_context, newext, 1))
|
||||||
|
ast_log(LOG_WARNING, "Async goto failed :-(\n");
|
||||||
|
res = -1;
|
||||||
|
} else {
|
||||||
|
/* Set the channel's new extension, since it exists, using transferer context */
|
||||||
|
strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
|
||||||
|
strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
|
||||||
|
transferee->priority = 0;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
if (option_verbose > 2)
|
||||||
|
ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
|
||||||
|
}
|
||||||
|
res = ast_streamfile(transferer, "pbx-invalid", transferee->language);
|
||||||
|
if (res) {
|
||||||
|
ast_moh_stop(transferee);
|
||||||
|
ast_autoservice_stop(transferee);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
res = ast_waitstream(transferer, AST_DIGIT_ANY);
|
||||||
|
ast_stopstream(transferer);
|
||||||
|
ast_moh_stop(transferee);
|
||||||
|
res = ast_autoservice_stop(transferee);
|
||||||
|
if (res) {
|
||||||
|
if (option_verbose > 1)
|
||||||
|
ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return FEATURE_RETURN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ast_call_feature {
|
||||||
|
int feature_mask;
|
||||||
|
char *fname;
|
||||||
|
char *sname;
|
||||||
|
char exten[FEATURE_MAX_LEN];
|
||||||
|
char default_exten[FEATURE_MAX_LEN];
|
||||||
|
int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense);
|
||||||
|
unsigned int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
|
||||||
|
struct ast_call_feature builtin_features[] =
|
||||||
|
{
|
||||||
|
{ AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF },
|
||||||
|
{ AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void unmap_features(void)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
for (x=0;x<FEATURES_COUNT;x++)
|
||||||
|
strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int remap_feature(const char *name, const char *value)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int res = -1;
|
||||||
|
for (x=0;x<FEATURES_COUNT;x++) {
|
||||||
|
if (!strcasecmp(name, builtin_features[x].sname)) {
|
||||||
|
strncpy(builtin_features[x].exten, value, sizeof(builtin_features[x].exten) - 1);
|
||||||
|
if (option_verbose > 1)
|
||||||
|
ast_verbose(VERBOSE_PREFIX_2 "Remapping feature %s (%s) to sequence '%s'\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
|
||||||
|
res = 0;
|
||||||
|
} else if (!strcmp(value, builtin_features[x].exten))
|
||||||
|
ast_log(LOG_WARNING, "Sequence '%s' already mapped to function %s (%s) while assigning to %s\n", value, builtin_features[x].fname, builtin_features[x].sname, name);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
unsigned int features;
|
||||||
|
int res = FEATURE_RETURN_PASSDIGITS;
|
||||||
|
|
||||||
|
if (sense == FEATURE_SENSE_CHAN)
|
||||||
|
features = config->features_caller;
|
||||||
|
else
|
||||||
|
features = config->features_callee;
|
||||||
|
ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d\n", chan->name, peer->name, sense, features);
|
||||||
|
for (x=0;x<FEATURES_COUNT;x++) {
|
||||||
|
if ((features & builtin_features[x].feature_mask) &&
|
||||||
|
!ast_strlen_zero(builtin_features[x].exten)) {
|
||||||
|
/* Feature is up for consideration */
|
||||||
|
if (!strcmp(builtin_features[x].exten, code)) {
|
||||||
|
res = builtin_features[x].operation(chan, peer, config, code, sense);
|
||||||
|
break;
|
||||||
|
} else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
|
||||||
|
if (res == FEATURE_RETURN_PASSDIGITS)
|
||||||
|
res = FEATURE_RETURN_STOREDIGITS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_config_flags(struct ast_bridge_config *config)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
config->flags = 0;
|
||||||
|
for (x=0;x<FEATURES_COUNT;x++) {
|
||||||
|
if (config->features_caller & builtin_features[x].feature_mask) {
|
||||||
|
if (builtin_features[x].flags & AST_FEATURE_FLAG_NEEDSDTMF)
|
||||||
|
ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
|
||||||
|
}
|
||||||
|
if (config->features_callee & builtin_features[x].feature_mask) {
|
||||||
|
if (builtin_features[x].flags & AST_FEATURE_FLAG_NEEDSDTMF)
|
||||||
|
ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
|
int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
|
||||||
{
|
{
|
||||||
/* Copy voice back and forth between the two channels. Give the peer
|
/* Copy voice back and forth between the two channels. Give the peer
|
||||||
the ability to transfer calls with '#<extension' syntax. */
|
the ability to transfer calls with '#<extension' syntax. */
|
||||||
int len;
|
|
||||||
struct ast_frame *f;
|
struct ast_frame *f;
|
||||||
struct ast_channel *who;
|
struct ast_channel *who;
|
||||||
char newext[256], *ptr;
|
char chan_featurecode[FEATURE_MAX_LEN + 1]="";
|
||||||
|
char peer_featurecode[FEATURE_MAX_LEN + 1]="";
|
||||||
int res;
|
int res;
|
||||||
int diff;
|
int diff;
|
||||||
|
int hasfeatures=0;
|
||||||
|
int hadfeatures=0;
|
||||||
struct ast_option_header *aoh;
|
struct ast_option_header *aoh;
|
||||||
struct ast_channel *transferer;
|
|
||||||
struct ast_channel *transferee;
|
|
||||||
struct timeval start, end;
|
struct timeval start, end;
|
||||||
char *transferer_real_context;
|
struct ast_bridge_config backup_config;
|
||||||
int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
|
int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
|
||||||
char *monitor_exec;
|
char *monitor_exec;
|
||||||
|
|
||||||
|
memset(&backup_config, 0, sizeof(backup_config));
|
||||||
|
|
||||||
if (chan && peer) {
|
if (chan && peer) {
|
||||||
pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
|
pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
|
||||||
pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
|
pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
|
||||||
@@ -330,10 +567,11 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
|||||||
pbx_exec(peer, monitor_app, monitor_exec, 1);
|
pbx_exec(peer, monitor_app, monitor_exec, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
allowdisconnect_in = config->allowdisconnect_in;
|
allowdisconnect_in = (config->features_callee & AST_FEATURE_DISCONNECT);
|
||||||
allowdisconnect_out = config->allowdisconnect_out;
|
allowdisconnect_out = (config->features_caller & AST_FEATURE_DISCONNECT);
|
||||||
allowredirect_in = config->allowredirect_in;
|
allowredirect_in = (config->features_callee & AST_FEATURE_REDIRECT);
|
||||||
allowredirect_out = config->allowredirect_out;
|
allowredirect_out = (config->features_caller & AST_FEATURE_REDIRECT);
|
||||||
|
set_config_flags(config);
|
||||||
config->firstpass = 1;
|
config->firstpass = 1;
|
||||||
|
|
||||||
/* Answer if need be */
|
/* Answer if need be */
|
||||||
@@ -363,14 +601,52 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
|||||||
diff = (end.tv_sec - start.tv_sec) * 1000;
|
diff = (end.tv_sec - start.tv_sec) * 1000;
|
||||||
diff += (end.tv_usec - start.tv_usec) / 1000;
|
diff += (end.tv_usec - start.tv_usec) / 1000;
|
||||||
config->timelimit -= diff;
|
config->timelimit -= diff;
|
||||||
if (config->timelimit <=0) {
|
if (hasfeatures) {
|
||||||
/* We ran out of time */
|
/* Running on backup config, meaning a feature might be being
|
||||||
config->timelimit = 0;
|
activated, but that's no excuse to keep things going
|
||||||
who = chan;
|
indefinitely! */
|
||||||
if (f)
|
if (backup_config.timelimit && ((backup_config.timelimit -= diff) <= 0)) {
|
||||||
ast_frfree(f);
|
ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
|
||||||
f = NULL;
|
config->timelimit = 0;
|
||||||
res = 0;
|
who = chan;
|
||||||
|
if (f)
|
||||||
|
ast_frfree(f);
|
||||||
|
f = NULL;
|
||||||
|
res = 0;
|
||||||
|
} else if (config->timelimit <= 0) {
|
||||||
|
/* Not *really* out of time, just out of time for
|
||||||
|
digits to come in for features. */
|
||||||
|
ast_log(LOG_DEBUG, "Timed out for feature!\n");
|
||||||
|
if (!ast_strlen_zero(peer_featurecode)) {
|
||||||
|
ast_dtmf_stream(chan, peer, peer_featurecode, 0);
|
||||||
|
memset(peer_featurecode, 0, sizeof(peer_featurecode));
|
||||||
|
}
|
||||||
|
if (!ast_strlen_zero(chan_featurecode)) {
|
||||||
|
ast_dtmf_stream(peer, chan, chan_featurecode, 0);
|
||||||
|
memset(chan_featurecode, 0, sizeof(chan_featurecode));
|
||||||
|
}
|
||||||
|
if (f)
|
||||||
|
ast_frfree(f);
|
||||||
|
hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
|
||||||
|
if (!hasfeatures) {
|
||||||
|
/* Restore original (possibly time modified) bridge config */
|
||||||
|
memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
|
||||||
|
memset(&backup_config, 0, sizeof(backup_config));
|
||||||
|
}
|
||||||
|
hadfeatures = hasfeatures;
|
||||||
|
/* Continue as we were */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (config->timelimit <=0) {
|
||||||
|
/* We ran out of time */
|
||||||
|
config->timelimit = 0;
|
||||||
|
who = chan;
|
||||||
|
if (f)
|
||||||
|
ast_frfree(f);
|
||||||
|
f = NULL;
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
@@ -412,150 +688,62 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* check for '*', if we find it it's time to disconnect */
|
/* check for '*', if we find it it's time to disconnect */
|
||||||
if (f && (f->frametype == AST_FRAME_DTMF) &&
|
if (f && (f->frametype == AST_FRAME_DTMF)) {
|
||||||
(((who == chan) && allowdisconnect_out) || ((who == peer) && allowdisconnect_in)) &&
|
char *featurecode;
|
||||||
(f->subclass == '*')) {
|
int sense;
|
||||||
|
struct ast_channel *other;
|
||||||
if (option_verbose > 3)
|
hadfeatures = hasfeatures;
|
||||||
ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
|
/* This cannot overrun because the longest feature is one shorter than our buffer */
|
||||||
res = -1;
|
if (who == chan) {
|
||||||
break;
|
other = peer;
|
||||||
}
|
sense = FEATURE_SENSE_CHAN;
|
||||||
|
featurecode = chan_featurecode;
|
||||||
if ((f->frametype == AST_FRAME_DTMF) &&
|
} else {
|
||||||
((allowredirect_in && who == peer) || (allowredirect_out && who == chan)) &&
|
other = chan;
|
||||||
(f->subclass == '#')) {
|
sense = FEATURE_SENSE_PEER;
|
||||||
if (allowredirect_in && who == peer) {
|
featurecode = peer_featurecode;
|
||||||
transferer = peer;
|
|
||||||
transferee = chan;
|
|
||||||
} else {
|
|
||||||
transferer = chan;
|
|
||||||
transferee = peer;
|
|
||||||
}
|
|
||||||
if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
|
|
||||||
!(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
|
|
||||||
/* Use the non-macro context to transfer the call */
|
|
||||||
if (!ast_strlen_zero(transferer->macrocontext))
|
|
||||||
transferer_real_context = transferer->macrocontext;
|
|
||||||
else
|
|
||||||
transferer_real_context = transferer->context;
|
|
||||||
}
|
|
||||||
/* Start autoservice on chan while we talk
|
|
||||||
to the originator */
|
|
||||||
ast_autoservice_start(transferee);
|
|
||||||
ast_moh_start(transferee, NULL);
|
|
||||||
|
|
||||||
memset(newext, 0, sizeof(newext));
|
|
||||||
ptr = newext;
|
|
||||||
|
|
||||||
/* Transfer */
|
|
||||||
if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
|
|
||||||
ast_moh_stop(transferee);
|
|
||||||
ast_autoservice_stop(transferee);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
|
|
||||||
ast_moh_stop(transferee);
|
|
||||||
ast_autoservice_stop(transferee);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ast_stopstream(transferer);
|
|
||||||
if (res > 0) {
|
|
||||||
/* If they've typed a digit already, handle it */
|
|
||||||
newext[0] = res;
|
|
||||||
ptr++;
|
|
||||||
len --;
|
|
||||||
}
|
|
||||||
res = 0;
|
|
||||||
while (strlen(newext) < sizeof(newext) - 1) {
|
|
||||||
res = ast_waitfordigit(transferer, transferdigittimeout);
|
|
||||||
if (res < 1)
|
|
||||||
break;
|
|
||||||
if (res == '#')
|
|
||||||
break;
|
|
||||||
*(ptr++) = res;
|
|
||||||
if (!ast_matchmore_extension(transferer, transferer_real_context
|
|
||||||
, newext, 1, transferer->cid.cid_num)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res < 0) {
|
|
||||||
ast_moh_stop(transferee);
|
|
||||||
ast_autoservice_stop(transferee);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!strcmp(newext, ast_parking_ext())) {
|
|
||||||
ast_moh_stop(transferee);
|
|
||||||
|
|
||||||
if (ast_autoservice_stop(transferee))
|
|
||||||
res = -1;
|
|
||||||
else if (!ast_park_call(transferee, transferer, 0, NULL)) {
|
|
||||||
/* We return non-zero, but tell the PBX not to hang the channel when
|
|
||||||
the thread dies -- We have to be careful now though. We are responsible for
|
|
||||||
hanging up the channel, else it will never be hung up! */
|
|
||||||
|
|
||||||
if (transferer==peer)
|
|
||||||
res=AST_PBX_KEEPALIVE;
|
|
||||||
else
|
|
||||||
res=AST_PBX_NO_HANGUP_PEER;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
|
|
||||||
}
|
|
||||||
/* XXX Maybe we should have another message here instead of invalid extension XXX */
|
|
||||||
} else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
|
|
||||||
pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
|
|
||||||
pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
|
|
||||||
ast_moh_stop(transferee);
|
|
||||||
res=ast_autoservice_stop(transferee);
|
|
||||||
if (!transferee->pbx) {
|
|
||||||
/* Doh! Use our handy async_goto functions */
|
|
||||||
if (option_verbose > 2)
|
|
||||||
ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
|
|
||||||
,transferee->name, newext, transferer_real_context);
|
|
||||||
if (ast_async_goto(transferee, transferer_real_context, newext, 1))
|
|
||||||
ast_log(LOG_WARNING, "Async goto failed :-(\n");
|
|
||||||
res = -1;
|
|
||||||
} else {
|
|
||||||
/* Set the channel's new extension, since it exists, using transferer context */
|
|
||||||
strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
|
|
||||||
strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
|
|
||||||
transferee->priority = 0;
|
|
||||||
ast_frfree(f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (option_verbose > 2)
|
|
||||||
ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
|
|
||||||
}
|
|
||||||
res = ast_streamfile(transferer, "pbx-invalid", transferee->language);
|
|
||||||
if (res) {
|
|
||||||
ast_moh_stop(transferee);
|
|
||||||
ast_autoservice_stop(transferee);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
res = ast_waitstream(transferer, AST_DIGIT_ANY);
|
|
||||||
ast_stopstream(transferer);
|
|
||||||
ast_moh_stop(transferee);
|
|
||||||
res = ast_autoservice_stop(transferee);
|
|
||||||
if (res) {
|
|
||||||
if (option_verbose > 1)
|
|
||||||
ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (f && (f->frametype == AST_FRAME_DTMF)) {
|
|
||||||
if (who == peer)
|
|
||||||
ast_write(chan, f);
|
|
||||||
else
|
|
||||||
ast_write(peer, f);
|
|
||||||
}
|
|
||||||
#if 1
|
|
||||||
ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if (f)
|
featurecode[strlen(featurecode)] = f->subclass;
|
||||||
ast_frfree(f);
|
res = ast_feature_interpret(chan, peer, config, featurecode, sense);
|
||||||
|
switch(res) {
|
||||||
|
case FEATURE_RETURN_PASSDIGITS:
|
||||||
|
ast_dtmf_stream(other, who, featurecode, 0);
|
||||||
|
/* Fall through */
|
||||||
|
case FEATURE_RETURN_SUCCESS:
|
||||||
|
memset(featurecode, 0, sizeof(chan_featurecode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (res >= FEATURE_RETURN_PASSDIGITS) {
|
||||||
|
res = 0;
|
||||||
|
} else {
|
||||||
|
ast_frfree(f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
|
||||||
|
if (hadfeatures && !hasfeatures) {
|
||||||
|
/* Restore backup */
|
||||||
|
memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
|
||||||
|
memset(&backup_config, 0, sizeof(struct ast_bridge_config));
|
||||||
|
} else if (hasfeatures) {
|
||||||
|
if (!hadfeatures) {
|
||||||
|
/* Backup configuration */
|
||||||
|
memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
|
||||||
|
/* Setup temporary config options */
|
||||||
|
config->play_warning = 0;
|
||||||
|
config->features_caller &= ~(AST_FEATURE_PLAY_WARNING);
|
||||||
|
config->features_callee &= ~(AST_FEATURE_PLAY_WARNING);
|
||||||
|
config->warning_freq = 0;
|
||||||
|
config->warning_sound = NULL;
|
||||||
|
config->end_sound = NULL;
|
||||||
|
config->start_sound = NULL;
|
||||||
|
config->firstpass = 0;
|
||||||
|
}
|
||||||
|
config->timelimit = featuredigittimeout;
|
||||||
|
ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->timelimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f)
|
||||||
|
ast_frfree(f);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -813,10 +1001,8 @@ static int park_exec(struct ast_channel *chan, void *data)
|
|||||||
ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
|
ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
|
||||||
|
|
||||||
memset(&config,0,sizeof(struct ast_bridge_config));
|
memset(&config,0,sizeof(struct ast_bridge_config));
|
||||||
config.allowredirect_in = 1;
|
config.features_callee |= AST_FEATURE_REDIRECT;
|
||||||
config.allowredirect_out = 1;
|
config.features_caller |= AST_FEATURE_REDIRECT;
|
||||||
config.allowdisconnect_out = 0;
|
|
||||||
config.allowdisconnect_in = 0;
|
|
||||||
config.timelimit = 0;
|
config.timelimit = 0;
|
||||||
config.play_warning = 0;
|
config.play_warning = 0;
|
||||||
config.warning_freq = 0;
|
config.warning_freq = 0;
|
||||||
@@ -928,6 +1114,9 @@ int load_module(void)
|
|||||||
struct ast_config *cfg;
|
struct ast_config *cfg;
|
||||||
struct ast_variable *var;
|
struct ast_variable *var;
|
||||||
|
|
||||||
|
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
|
||||||
|
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
|
||||||
|
|
||||||
ast_cli_register(&showparked);
|
ast_cli_register(&showparked);
|
||||||
|
|
||||||
cfg = ast_load("features.conf");
|
cfg = ast_load("features.conf");
|
||||||
@@ -964,6 +1153,11 @@ int load_module(void)
|
|||||||
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
|
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
|
||||||
} else
|
} else
|
||||||
transferdigittimeout = transferdigittimeout * 1000;
|
transferdigittimeout = transferdigittimeout * 1000;
|
||||||
|
} else if (!strcasecmp(var->name, "featuredigittimeout")) {
|
||||||
|
if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (transferdigittimeout < 1)) {
|
||||||
|
ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
|
||||||
|
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
|
||||||
|
}
|
||||||
} else if (!strcasecmp(var->name, "courtesytone")) {
|
} else if (!strcasecmp(var->name, "courtesytone")) {
|
||||||
strncpy(courtesytone, var->value, sizeof(courtesytone) - 1);
|
strncpy(courtesytone, var->value, sizeof(courtesytone) - 1);
|
||||||
} else if (!strcasecmp(var->name, "pickupexten")) {
|
} else if (!strcasecmp(var->name, "pickupexten")) {
|
||||||
@@ -971,6 +1165,13 @@ int load_module(void)
|
|||||||
}
|
}
|
||||||
var = var->next;
|
var = var->next;
|
||||||
}
|
}
|
||||||
|
unmap_features();
|
||||||
|
var = ast_variable_browse(cfg, "featuremap");
|
||||||
|
while(var) {
|
||||||
|
if (remap_feature(var->name, var->value))
|
||||||
|
ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
|
||||||
|
var = var->next;
|
||||||
|
}
|
||||||
ast_destroy(cfg);
|
ast_destroy(cfg);
|
||||||
}
|
}
|
||||||
con = ast_context_find(parking_con);
|
con = ast_context_find(parking_con);
|
||||||
|
Reference in New Issue
Block a user