Merge Tony's attended # transfer with changes (bug #3241)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4677 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.2-netsec
Mark Spencer 21 years ago
parent 68c634721a
commit 526622d6e0

45
app.c

@ -30,11 +30,56 @@
#include <asterisk/options.h>
#include <asterisk/utils.h>
#include <asterisk/lock.h>
#include <asterisk/indications.h>
#include "asterisk.h"
#include "astconf.h"
#define MAX_OTHER_FORMATS 10
int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout)
{
struct tone_zone_sound *ts;
int res=0, x=0;
if(!timeout && chan->pbx)
timeout = chan->pbx->dtimeout;
else if(!timeout)
timeout = 5;
ts = ast_get_indication_tone(chan->zone,"dial");
if (ts && ts->data[0]) {
res = ast_playtones_start(chan, 0, ts->data, 0);
} else
ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
memset(collect, 0, size);
for (x=0; strlen(collect) < size; ) {
res = ast_waitfordigit(chan, timeout);
if (!ast_ignore_pattern(context, collect))
ast_playtones_stop(chan);
if (res < 1)
break;
collect[x++] = res;
if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num)) {
if (collect[x-1] == '#') {
/* Not a valid extension, ending in #, assume the # was to finish dialing */
collect[x-1] = '\0';
}
break;
}
}
if (res >= 0) {
if (ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num))
res = 1;
else
res = 0;
}
return res;
}
/* set timeout to 0 for "standard" timeouts. Set timeout to -1 for
"ludicrous time" (essentially never times out) */
int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)

@ -60,8 +60,10 @@ static char *descrip =
" This application returns -1 if the originating channel hangs up, or if the\n"
"call is bridged and either of the parties in the bridge terminate the call.\n"
"The option string may contain zero or more of the following characters:\n"
" 't' -- allow the called user transfer the calling user by hitting #.\n"
" 't' -- allow the called user to transfer the calling user by hitting #.\n"
" 'T' -- allow the calling user to transfer the call by hitting #.\n"
" 'w' -- allow the called user to write the conversation to disk via app_monitor\n"
" 'W' -- allow the calling user to write the conversation to disk via app_monitor\n"
" 'f' -- Forces callerid to be set as the extension of the line \n"
" making/redirecting the outgoing call. For example, some PSTNs\n"
" don't allow callerids from other extensions then the ones\n"
@ -474,6 +476,8 @@ static int dial_exec(struct ast_channel *chan, void *data)
int to;
int allowredir_in=0;
int allowredir_out=0;
int monitor_in = 0;
int monitor_out = 0;
int allowdisconnect_in=0;
int allowdisconnect_out=0;
int hasmacro = 0;
@ -789,21 +793,25 @@ static int dial_exec(struct ast_channel *chan, void *data)
}
memset(tmp, 0, sizeof(struct localuser));
if (transfer) {
if (strchr(transfer, 'w'))
monitor_in = 1;
if (strchr(transfer, 'W'))
monitor_out = 1;
if (strchr(transfer, 't'))
tmp->allowredirect_in = 1;
else tmp->allowredirect_in = 0;
else tmp->allowredirect_in = 0;
if (strchr(transfer, 'T'))
tmp->allowredirect_out = 1;
else tmp->allowredirect_out = 0;
else tmp->allowredirect_out = 0;
if (strchr(transfer, 'r'))
tmp->ringbackonly = 1;
else tmp->ringbackonly = 0;
else tmp->ringbackonly = 0;
if (strchr(transfer, 'm'))
tmp->musiconhold = 1;
else tmp->musiconhold = 0;
else tmp->musiconhold = 0;
if (strchr(transfer, 'H'))
allowdisconnect_out = tmp->allowdisconnect_out = 1;
else allowdisconnect_out = tmp->allowdisconnect_out = 0;
else allowdisconnect_out = tmp->allowdisconnect_out = 0;
if(strchr(transfer, 'h'))
allowdisconnect_in = tmp->allowdisconnect_in = 1;
else allowdisconnect_in = tmp->allowdisconnect_in = 0;
@ -1143,6 +1151,10 @@ static int dial_exec(struct ast_channel *chan, void *data)
config.features_callee |= AST_FEATURE_REDIRECT;
if (allowredir_out)
config.features_caller |= AST_FEATURE_REDIRECT;
if (monitor_in)
config.features_callee |= AST_FEATURE_AUTOMON;
if (monitor_out)
config.features_caller |= AST_FEATURE_AUTOMON;
if (allowdisconnect_in)
config.features_callee |= AST_FEATURE_DISCONNECT;
if (allowdisconnect_out)

@ -17,5 +17,7 @@ context => parkedcalls ; Which context parked calls are in
; feature activation. Default is 500
[featuremap]
;blindxfer => # ; Blind transfer
;disconnect => * ; Disconnect
;blindxfer => #1 ; Blind transfer
;disconnect => *0 ; Disconnect
;automon => *1 ; One Touch Record
;atxfer => *2 ; Attended transfer

@ -90,6 +90,9 @@ int ast_app_group_match_get_count(char *groupmatch, char *category);
//! Create an argc argv type structure for app args
int ast_seperate_app_args(char *buf, char delim, char **array, int arraylen);
//! Present a dialtone and collect a certain length extension. Returns 1 on valid extension entered, -1 on hangup, or 0 on invalid extension.
int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

@ -236,6 +236,8 @@ struct ast_channel {
#define AST_FEATURE_PLAY_WARNING (1 << 0)
#define AST_FEATURE_REDIRECT (1 << 1)
#define AST_FEATURE_DISCONNECT (1 << 2)
#define AST_FEATURE_ATXFER (1 << 3)
#define AST_FEATURE_AUTOMON (1 << 4)
#define AST_FEATURE_FLAG_NEEDSDTMF (1 << 0)

@ -133,6 +133,54 @@ char *ast_pickup_ext(void)
return pickup_ext;
}
struct ast_bridge_thread_obj
{
struct ast_bridge_config bconfig;
struct ast_channel *chan;
struct ast_channel *peer;
};
static void *ast_bridge_call_thread(void *data)
{
struct ast_bridge_thread_obj *tobj = data;
tobj->chan->appl = "Transferred Call";
tobj->chan->data = tobj->peer->name;
tobj->peer->appl = "Transferred Call";
tobj->peer->data = tobj->chan->name;
if (tobj->chan->cdr) {
ast_cdr_reset(tobj->chan->cdr,0);
ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
}
if (tobj->peer->cdr) {
ast_cdr_reset(tobj->peer->cdr,0);
ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
}
ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
ast_hangup(tobj->chan);
ast_hangup(tobj->peer);
tobj->chan = tobj->peer = NULL;
free(tobj);
tobj=NULL;
return NULL;
}
static void ast_bridge_call_thread_launch(void *data)
{
pthread_t thread;
pthread_attr_t attr;
int result;
result = pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
result = ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
result = pthread_attr_destroy(&attr);
}
static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
{
int res;
@ -311,6 +359,30 @@ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int
#define FEATURE_SENSE_PEER (1 << 1)
#define FEATURE_MAX_LEN 11
static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
{
char *args;
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call.\n", code);
if (monitor_ok) {
if (!monitor_app) {
if (!(monitor_app = pbx_findapp("Monitor")))
monitor_ok=0;
}
/* Copy to local variable just in case one of the channels goes away */
args = pbx_builtin_getvar_helper(chan, "TOUCH_MONITOR");
if (!args)
args = pbx_builtin_getvar_helper(peer, "TOUCH_MONITOR");
if (!args)
args = "WAV||m";
pbx_exec(peer, monitor_app, args, 1);
return FEATURE_RETURN_SUCCESS;
}
return -1;
}
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)
@ -369,18 +441,8 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p
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;
}
res = ast_app_dtget(transferer, transferer_real_context, newext, sizeof(newext), 100, transferdigittimeout);
if (res < 0) {
ast_moh_stop(transferee);
ast_autoservice_stop(transferee);
@ -447,6 +509,184 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p
return FEATURE_RETURN_SUCCESS;
}
static int builtin_atxfer(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;
struct ast_channel *newchan, *xferchan=NULL;
int outstate=0;
struct ast_bridge_config bconfig;
char *transferer_real_context;
char xferto[256],dialstr[265];
char *cid_num;
char *cid_name;
int res;
struct ast_frame *f = NULL;
struct ast_bridge_thread_obj *tobj;
ast_log(LOG_DEBUG, "Executing Attended 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);
/* 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;
}
if((ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout))) {
cid_num = transferer->cid.cid_num;
cid_name = transferer->cid.cid_name;
if (ast_exists_extension(transferer, transferer_real_context,xferto, 1, cid_num)) {
snprintf(dialstr, sizeof(dialstr), "%s@%s/n", xferto, transferer_real_context);
if((newchan = ast_request_and_dial("Local", ast_best_codec(transferer->nativeformats), dialstr,30000, &outstate, cid_num, cid_name))) {
res = ast_channel_make_compatible(transferer, newchan);
if (res < 0) {
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferer->name, newchan->name);
ast_hangup(newchan);
return -1;
}
memset(&bconfig,0,sizeof(struct ast_bridge_config));
bconfig.features_caller |= AST_FEATURE_DISCONNECT;
bconfig.features_callee |= AST_FEATURE_DISCONNECT;
res = ast_bridge_call(transferer,newchan,&bconfig);
if(newchan->_softhangup || newchan->_state != AST_STATE_UP) {
ast_hangup(newchan);
if (f) {
ast_frfree(f);
f = NULL;
}
if (!ast_streamfile(transferer, "beep", transferer->language)) {
if (ast_waitstream(transferer, "") < 0) {
ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
}
}
ast_moh_stop(transferee);
ast_autoservice_stop(transferee);
transferer->_softhangup = 0;
return FEATURE_RETURN_SUCCESS;
}
res = ast_channel_make_compatible(transferee, newchan);
if (res < 0) {
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferee->name, newchan->name);
ast_hangup(newchan);
return -1;
}
ast_moh_stop(transferee);
if((ast_autoservice_stop(transferee) < 0)
||(ast_waitfordigit(transferee,100) < 0)
|| (ast_waitfordigit(newchan,100) < 0)
|| ast_check_hangup(transferee)
|| ast_check_hangup(newchan)) {
ast_hangup(newchan);
res = -1;
return -1;
}
if ((xferchan = ast_channel_alloc(0))) {
snprintf(xferchan->name, sizeof (xferchan->name), "Transfered/%s",transferee->name);
/* Make formats okay */
xferchan->readformat = transferee->readformat;
xferchan->writeformat = transferee->writeformat;
ast_channel_masquerade(xferchan, transferee);
ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
xferchan->_state = AST_STATE_UP;
xferchan->flags = 0;
xferchan->_softhangup = 0;
if((f = ast_read(xferchan))) {
ast_frfree(f);
f = NULL;
}
} else {
ast_hangup(newchan);
return -1;
}
newchan->_state = AST_STATE_UP;
newchan->flags = 0;
newchan->_softhangup = 0;
tobj = malloc(sizeof(struct ast_bridge_thread_obj));
if (tobj) {
memset(tobj,0,sizeof(struct ast_bridge_thread_obj));
tobj->chan = xferchan;
tobj->peer = newchan;
tobj->bconfig = *config;
if (!ast_streamfile(newchan, "beep", newchan->language)) {
if (ast_waitstream(newchan, "") < 0) {
ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
}
}
ast_bridge_call_thread_launch(tobj);
} else {
ast_log(LOG_WARNING, "Out of memory!\n");
ast_hangup(xferchan);
ast_hangup(newchan);
}
return -1;
} else {
ast_log(LOG_WARNING, "Unable to create channel Local/%s do you have chan_local?\n",dialstr);
ast_moh_stop(transferee);
ast_autoservice_stop(transferee);
res = ast_streamfile(transferer, "beeperr", transferer->language);
if (!res && (ast_waitstream(transferer, "") < 0)) {
return -1;
}
return -1;
}
} else {
ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
ast_moh_stop(transferee);
ast_autoservice_stop(transferee);
res = ast_streamfile(transferer, "beeperr", transferer->language);
if (!res && (ast_waitstream(transferer, "") < 0)) {
return -1;
}
}
} else {
ast_log(LOG_WARNING, "Did not read data.\n");
res = ast_streamfile(transferer, "beeperr", transferer->language);
if (ast_waitstream(transferer, "") < 0) {
return -1;
}
}
ast_moh_stop(transferee);
ast_autoservice_stop(transferee);
return FEATURE_RETURN_SUCCESS;
}
struct ast_call_feature {
int feature_mask;
char *fname;
@ -457,10 +697,13 @@ struct ast_call_feature {
unsigned int flags;
};
/* add atxfer and automon as undefined so you can only use em if you configure them */
#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_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF },
{ AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF },
{ AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
};

@ -24,6 +24,8 @@
%beep.gsm%(this is a simple beep tone)
%beeperr.gsm%(this is an error beep tone)
%conf-getconfno.gsm%Please enter your conference number followed by the pound key.
%conf-getchannel.gsm%Please enter your channel number followed by the pound key.

Loading…
Cancel
Save