Index: apps/app_dial.c =================================================================== --- apps/app_dial.c (revision 89243) +++ apps/app_dial.c (working copy) @@ -208,8 +208,12 @@ " w - Allow the called party to enable recording of the call by sending\n" " the DTMF sequence defined for one-touch recording in features.conf.\n" " W - Allow the calling party to enable recording of the call by sending\n" -" the DTMF sequence defined for one-touch recording in features.conf.\n"; - +" the DTMF sequence defined for one-touch recording in features.conf.\n" +" x - Allow the called party to enable recording of the call by sending\n" +" the DTMF sequence defined for one-touch automixmonitor in features.conf\n" +" X - Allow the calling party to enable recording of the call by sending\n" +" the DTMF sequence defined for one-touch automixmonitor in features.conf\n"; + /* RetryDial App by Anthony Minessale II Jan/2005 */ static char *rapp = "RetryDial"; static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension."; @@ -255,12 +259,14 @@ OPT_CALLER_PARK = (1 << 26), OPT_IGNORE_FORWARDING = (1 << 27), OPT_CALLEE_GOSUB = (1 << 28), - OPT_CANCEL_ELSEWHERE = (1 << 29), - OPT_PEER_H = (1 << 30), + OPT_CALLEE_MIXMONITOR = (1 << 29), + OPT_CALLER_MIXMONITOR = (1 << 30), }; #define DIAL_STILLGOING (1 << 31) #define DIAL_NOFORWARDHTML ((uint64_t)1 << 32) /* flags are now 64 bits, so keep it up! */ +#define OPT_CANCEL_ELSEWHERE ((uint64_t)1 << 33) +#define OPT_PEER_H ((uint64_t)1 << 34) enum { OPT_ARG_ANNOUNCE = 0, @@ -310,6 +316,8 @@ AST_APP_OPTION_ARG('U', OPT_CALLEE_GOSUB, OPT_ARG_CALLEE_GOSUB), AST_APP_OPTION('w', OPT_CALLEE_MONITOR), AST_APP_OPTION('W', OPT_CALLER_MONITOR), + AST_APP_OPTION('x', OPT_CALLEE_MIXMONITOR), + AST_APP_OPTION('X', OPT_CALLER_MIXMONITOR), }); #define CAN_EARLY_BRIDGE(flags) (!ast_test_flag64(flags, OPT_CALLEE_HANGUP | \ @@ -626,6 +634,7 @@ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK | + OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR | DIAL_NOFORWARDHTML); ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext)); ast_copy_string(c->exten, "", sizeof(c->exten)); @@ -660,6 +669,7 @@ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK | + OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR | DIAL_NOFORWARDHTML); ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext)); ast_copy_string(c->exten, "", sizeof(c->exten)); @@ -1318,6 +1328,7 @@ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK | + OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR | OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID); ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML); } @@ -1744,6 +1755,10 @@ ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL); if (ast_test_flag64(peerflags, OPT_CALLER_PARK)) ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL); + if (ast_test_flag64(peerflags, OPT_CALLEE_MIXMONITOR)) + ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMIXMON); + if (ast_test_flag64(peerflags, OPT_CALLER_MIXMONITOR)) + ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMIXMON); if (moh) { moh = 0; Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 89243) +++ apps/app_queue.c (working copy) @@ -159,6 +159,9 @@ " the DTMF sequence defined for call parking in features.conf.\n" " 'K' -- Allow the calling party to enable parking of the call by sending\n" " the DTMF sequence defined for call parking in features.conf.\n" +" 'x' -- allow the called user to write the conversation to disk via MixMonitor\n" +" 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n" + " In addition to transferring the call, a call may be parked and then picked\n" "up by another user.\n" " The optional URL will be sent to the called party if the channel supports\n" @@ -2746,6 +2749,13 @@ case 'i': forwardsallowed = 0; break; + case 'x': + ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); + break; + case 'X': + ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON); + break; + } /* Hold the lock while we setup the outgoing calls */ Index: include/asterisk/channel.h =================================================================== --- include/asterisk/channel.h (revision 89243) +++ include/asterisk/channel.h (working copy) @@ -564,6 +564,7 @@ AST_FEATURE_ATXFER = (1 << 3), AST_FEATURE_AUTOMON = (1 << 4), AST_FEATURE_PARKCALL = (1 << 5), + AST_FEATURE_AUTOMIXMON = (1 << 6), }; /*! \brief bridge configuration */ Index: include/asterisk/app.h =================================================================== --- include/asterisk/app.h (revision 89243) +++ include/asterisk/app.h (working copy) @@ -347,7 +347,7 @@ */ struct ast_app_option { /*! \brief The flag bit that represents this option. */ - unsigned int flag; + uint64_t flag; /*! \brief The index of the entry in the arguments array that should be used for this option's argument. */ unsigned int arg_index; Index: include/asterisk/audiohook.h =================================================================== --- include/asterisk/audiohook.h (revision 89243) +++ include/asterisk/audiohook.h (working copy) @@ -162,6 +162,28 @@ */ void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook); +/*! + \brief Find out how many audiohooks from a certain source exist on a given channel, regardless of status. + \param chan The channel on which to find the spies + \param source The audiohook's source + \param type The type of audiohook + \return Return the number of audiohooks which are from the source specified + + Note: Function performs nlocking. +*/ +int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type); + +/*! + \brief Find out how many spies of a certain type exist on a given channel, and are in state running. + \param chan The channel on which to find the spies + \param source The source of the audiohook + \param type The type of spy to look for + \return Return the number of running audiohooks which are from the source specified + + Note: Function performs no locking. +*/ +int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type); + /*! \brief Lock an audiohook * \param ah Audiohook structure */ Index: main/audiohook.c =================================================================== --- main/audiohook.c (revision 89243) +++ main/audiohook.c (working copy) @@ -619,3 +619,83 @@ return; } + +/* Count number of channel audiohooks by type, regardless of type */ +int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type) +{ + int count = 0; + struct ast_audiohook *ah = NULL; + + if (!chan->audiohooks) + return -1; + + switch (type) { + case AST_AUDIOHOOK_TYPE_SPY: + AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) { + if (!strcmp(ah->source, source)) { + count++; + } + } + AST_LIST_TRAVERSE_SAFE_END; + break; + case AST_AUDIOHOOK_TYPE_WHISPER: + AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) { + if (!strcmp(ah->source, source)) { + count++; + } + } + AST_LIST_TRAVERSE_SAFE_END; + break; + case AST_AUDIOHOOK_TYPE_MANIPULATE: + AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) { + if (!strcmp(ah->source, source)) { + count++; + } + } + AST_LIST_TRAVERSE_SAFE_END; + break; + default: + ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type); + return -1; + } + + return count; +} + +/* Count number of channel audiohooks by type that are running */ +int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type) +{ + int count = 0; + struct ast_audiohook *ah = NULL; + if (!chan->audiohooks) + return -1; + + switch (type) { + case AST_AUDIOHOOK_TYPE_SPY: + AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) { + if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING)) + count++; + } + AST_LIST_TRAVERSE_SAFE_END; + break; + case AST_AUDIOHOOK_TYPE_WHISPER: + AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) { + if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING)) + count++; + } + AST_LIST_TRAVERSE_SAFE_END; + break; + case AST_AUDIOHOOK_TYPE_MANIPULATE: + AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) { + if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING)) + count++; + } + AST_LIST_TRAVERSE_SAFE_END; + break; + default: + ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type); + return -1; + } + return count; +} + Index: configs/features.conf.sample =================================================================== --- configs/features.conf.sample (revision 89243) +++ configs/features.conf.sample (working copy) @@ -52,6 +52,7 @@ ;automon => *1 ; One Touch Record a.k.a. Touch Monitor ;atxfer => *2 ; Attended transfer ;parkcall => #72 ; Park call (one step parking) +;automixmon => *3 ; One Touch Record a.k.a. Touch MixMonitor [applicationmap] ; Note that the DYNAMIC_FEATURES channel variable must be set to use the features Index: res/res_features.c =================================================================== --- res/res_features.c (revision 89243) +++ res/res_features.c (working copy) @@ -58,6 +58,7 @@ #include "asterisk/adsi.h" #include "asterisk/devicestate.h" #include "asterisk/monitor.h" +#include "asterisk/audiohook.h" #define DEFAULT_PARK_TIME 45000 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 @@ -158,6 +159,12 @@ static struct ast_app *monitor_app = NULL; static int monitor_ok = 1; +static struct ast_app *mixmonitor_app = NULL; +static int mixmonitor_ok = 1; + +static struct ast_app *stopmixmonitor_app = NULL; +static int stopmixmonitor_ok = 1; + struct parkeduser { struct ast_channel *chan; /*!< Parking channel */ struct timeval start; /*!< Time the parking started */ @@ -717,6 +724,118 @@ return -1; } +static int builtin_automixmonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) +{ + char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL; + int x = 0; + size_t len; + struct ast_channel *caller_chan, *callee_chan; + const char *mixmonitor_spy_type = "MixMonitor"; + int count = 0; + + if (!mixmonitor_ok) { + ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n"); + return -1; + } + + if (!(mixmonitor_app = pbx_findapp("MixMonitor"))) { + mixmonitor_ok = 0; + ast_log(LOG_ERROR,"Cannot record the call. The mixmonitor application is disabled.\n"); + return -1; + } + + set_peers(&caller_chan, &callee_chan, peer, chan, sense); + + if (!ast_strlen_zero(courtesytone)) { + if (ast_autoservice_start(callee_chan)) + return -1; + if (ast_stream_and_wait(caller_chan, courtesytone, "")) { + ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); + ast_autoservice_stop(callee_chan); + return -1; + } + if (ast_autoservice_stop(callee_chan)) + return -1; + } + + ast_channel_lock(callee_chan); + count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY); + ast_channel_unlock(callee_chan); + + // This means a mixmonitor is attached to the channel, running or not is unknown. + if (count > 0) { + + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code); + + //Make sure they are running + ast_channel_lock(callee_chan); + count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY); + ast_channel_unlock(callee_chan); + if (count > 0) { + if (!stopmixmonitor_ok) { + ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n"); + return -1; + } + if (!(stopmixmonitor_app = pbx_findapp("StopMixMonitor"))) { + stopmixmonitor_ok = 0; + ast_log(LOG_ERROR,"Cannot stop recording the call. The stopmixmonitor application is disabled.\n"); + return -1; + } else { + pbx_exec(callee_chan, stopmixmonitor_app, ""); + return FEATURE_RETURN_SUCCESS; + } + } + + ast_log(LOG_WARNING,"Stopped MixMonitors are attached to the channel.\n"); + } + + if (caller_chan && callee_chan) { + const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR_FORMAT"); + const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MIXMONITOR"); + + if (!touch_format) + touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR_FORMAT"); + + if (!touch_monitor) + touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MIXMONITOR"); + + if (touch_monitor) { + len = strlen(touch_monitor) + 50; + args = alloca(len); + touch_filename = alloca(len); + snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor); + snprintf(args, len, "%s.%s,b", touch_filename, (touch_format) ? touch_format : "wav"); + } else { + caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name)); + callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name)); + len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50; + args = alloca(len); + touch_filename = alloca(len); + snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id); + snprintf(args, len, "%s.%s,b", touch_filename, S_OR(touch_format, "wav")); + } + + for( x = 0; x < strlen(args); x++) { + if (args[x] == '/') + args[x] = '-'; + } + + if (option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, touch_filename); + + pbx_exec(callee_chan, mixmonitor_app, args); + pbx_builtin_setvar_helper(callee_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename); + pbx_builtin_setvar_helper(caller_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename); + return FEATURE_RETURN_SUCCESS; + + } + + ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n"); + return -1; + +} + static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) { ast_verb(4, "User hit '%s' to disconnect call.\n", code); @@ -1136,6 +1255,7 @@ { 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, "" }, { AST_FEATURE_PARKCALL, "Park Call", "parkcall", "", "", builtin_parkcall, AST_FEATURE_FLAG_NEEDSDTMF, "" }, + { AST_FEATURE_AUTOMIXMON, "One Touch MixMonitor", "automixmon", "", "", builtin_automixmonitor, AST_FEATURE_FLAG_NEEDSDTMF, "" }, };