Merge Tony's attended # transfer with changes (bug #3241)
authorMark Spencer <markster@digium.com>
Wed, 5 Jan 2005 19:56:47 +0000 (19:56 +0000)
committerMark Spencer <markster@digium.com>
Wed, 5 Jan 2005 19:56:47 +0000 (19:56 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4677 65c4cc65-6c06-0410-ace0-fbb531ad65f3

app.c
apps/app_dial.c
configs/features.conf.sample
include/asterisk/app.h
include/asterisk/channel.h
res/res_features.c
sounds.txt

diff --git a/app.c b/app.c
index f964bd2..8b7ecb0 100755 (executable)
--- a/app.c
+++ b/app.c
 #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)
index 307d80c..d628aa9 100755 (executable)
@@ -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)
index dc6cda1..63bb6da 100755 (executable)
@@ -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
index ba7d823..207ba83 100755 (executable)
@@ -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
index 3e07430..908ff55 100755 (executable)
@@ -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)
 
index b725564..f1e58b5 100755 (executable)
@@ -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 },
 };
 
index 0095f63..41b54bd 100755 (executable)
@@ -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.