aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xapp.c45
-rwxr-xr-xapps/app_dial.c24
-rwxr-xr-xconfigs/features.conf.sample6
-rwxr-xr-xinclude/asterisk/app.h3
-rwxr-xr-xinclude/asterisk/channel.h2
-rwxr-xr-xres/res_features.c265
-rwxr-xr-xsounds.txt2
7 files changed, 328 insertions, 19 deletions
diff --git a/app.c b/app.c
index f964bd2a1..8b7ecb05c 100755
--- a/app.c
+++ b/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)
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 307d80cc3..d628aa90c 100755
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -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)
diff --git a/configs/features.conf.sample b/configs/features.conf.sample
index dc6cda1b7..63bb6da7a 100755
--- a/configs/features.conf.sample
+++ b/configs/features.conf.sample
@@ -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
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index ba7d823c0..207ba83da 100755
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -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
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 3e0743022..908ff5585 100755
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -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)
diff --git a/res/res_features.c b/res/res_features.c
index b725564c7..f1e58b5ec 100755
--- a/res/res_features.c
+++ b/res/res_features.c
@@ -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 },
};
diff --git a/sounds.txt b/sounds.txt
index 0095f6341..41b54bd2f 100755
--- a/sounds.txt
+++ b/sounds.txt
@@ -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.