Thu Apr 28 2011 17:15:27

Asterisk developer's documentation


app_dial.c File Reference

dial() & retrydial() - Trivial application to dial a channel and send an URL on answer More...

#include "asterisk.h"
#include <sys/time.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include "asterisk/paths.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/config.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/callerid.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"
#include "asterisk/causes.h"
#include "asterisk/rtp.h"
#include "asterisk/cdr.h"
#include "asterisk/manager.h"
#include "asterisk/privacy.h"
#include "asterisk/stringfields.h"
#include "asterisk/global_datastores.h"
#include "asterisk/dsp.h"
Include dependency graph for app_dial.c:

Go to the source code of this file.

Data Structures

struct  cause_args
struct  chanlist
 List of channel drivers. More...
struct  privacy_args

Defines

#define AST_MAX_WATCHERS   256
#define CAN_EARLY_BRIDGE(flags, chan, peer)
#define DIAL_NOFORWARDHTML   ((uint64_t)1 << 32)
#define DIAL_STILLGOING   (1 << 31)
#define OPT_CALLEE_GO_ON   ((uint64_t)1 << 35)
#define OPT_CANCEL_ELSEWHERE   ((uint64_t)1 << 33)
#define OPT_PEER_H   ((uint64_t)1 << 34)
#define S_REPLACE(s, new_val)

Enumerations

enum  {
  OPT_ARG_ANNOUNCE = 0, OPT_ARG_SENDDTMF, OPT_ARG_GOTO, OPT_ARG_DURATION_LIMIT,
  OPT_ARG_MUSICBACK, OPT_ARG_CALLEE_MACRO, OPT_ARG_CALLEE_GOSUB, OPT_ARG_CALLEE_GO_ON,
  OPT_ARG_PRIVACY, OPT_ARG_DURATION_STOP, OPT_ARG_OPERMODE, OPT_ARG_SCREEN_NOINTRO,
  OPT_ARG_ARRAY_SIZE
}
enum  {
  OPT_ANNOUNCE = (1 << 0), OPT_RESETCDR = (1 << 1), OPT_DTMF_EXIT = (1 << 2), OPT_SENDDTMF = (1 << 3),
  OPT_FORCECLID = (1 << 4), OPT_GO_ON = (1 << 5), OPT_CALLEE_HANGUP = (1 << 6), OPT_CALLER_HANGUP = (1 << 7),
  OPT_DURATION_LIMIT = (1 << 9), OPT_MUSICBACK = (1 << 10), OPT_CALLEE_MACRO = (1 << 11), OPT_SCREEN_NOINTRO = (1 << 12),
  OPT_SCREEN_NOCLID = (1 << 13), OPT_ORIGINAL_CLID = (1 << 14), OPT_SCREENING = (1 << 15), OPT_PRIVACY = (1 << 16),
  OPT_RINGBACK = (1 << 17), OPT_DURATION_STOP = (1 << 18), OPT_CALLEE_TRANSFER = (1 << 19), OPT_CALLER_TRANSFER = (1 << 20),
  OPT_CALLEE_MONITOR = (1 << 21), OPT_CALLER_MONITOR = (1 << 22), OPT_GOTO = (1 << 23), OPT_OPERMODE = (1 << 24),
  OPT_CALLEE_PARK = (1 << 25), OPT_CALLER_PARK = (1 << 26), OPT_IGNORE_FORWARDING = (1 << 27), OPT_CALLEE_GOSUB = (1 << 28),
  OPT_CALLEE_MIXMONITOR = (1 << 29), OPT_CALLER_MIXMONITOR = (1 << 30)
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int dial_exec (struct ast_channel *chan, void *data)
static int dial_exec_full (struct ast_channel *chan, void *data, struct ast_flags64 *peerflags, int *continue_exec)
static void do_forward (struct chanlist *o, struct cause_args *num, struct ast_flags64 *peerflags, int single)
static int do_timelimit (struct ast_channel *chan, struct ast_bridge_config *config, char *parse, struct timeval *calldurationlimit)
static void end_bridge_callback (void *data)
static void end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
static const char * get_cid_name (char *name, int namelen, struct ast_channel *chan)
static void handle_cause (int cause, struct cause_args *num)
static void hanguptree (struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
static int load_module (void)
static int onedigit_goto (struct ast_channel *chan, const char *context, char exten, int pri)
static void replace_macro_delimiter (char *s)
static int retrydial_exec (struct ast_channel *chan, void *data)
static void senddialendevent (const struct ast_channel *src, const char *dialstatus)
static void senddialevent (struct ast_channel *src, struct ast_channel *dst, const char *dialstring)
static int setup_privacy_args (struct privacy_args *pa, struct ast_flags64 *opts, char *opt_args[], struct ast_channel *chan)
 returns 1 if successful, 0 or <0 if the caller should 'goto out'
static int unload_module (void)
static int valid_priv_reply (struct ast_flags64 *opts, int res)
static struct ast_channelwait_for_answer (struct ast_channel *in, struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags, struct privacy_args *pa, const struct cause_args *num_in, int *result)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialing Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
static char * app = "Dial"
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_app_option dial_exec_options [128] = { [ 'A' ] = { .flag = OPT_ANNOUNCE , .arg_index = OPT_ARG_ANNOUNCE + 1 }, [ 'C' ] = { .flag = OPT_RESETCDR }, [ 'c' ] = { .flag = ((uint64_t)1 << 33) }, [ 'd' ] = { .flag = OPT_DTMF_EXIT }, [ 'D' ] = { .flag = OPT_SENDDTMF , .arg_index = OPT_ARG_SENDDTMF + 1 }, [ 'e' ] = { .flag = ((uint64_t)1 << 34) }, [ 'f' ] = { .flag = OPT_FORCECLID }, [ 'F' ] = { .flag = ((uint64_t)1 << 35) , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ 'g' ] = { .flag = OPT_GO_ON }, [ 'G' ] = { .flag = OPT_GOTO , .arg_index = OPT_ARG_GOTO + 1 }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'i' ] = { .flag = OPT_IGNORE_FORWARDING }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'L' ] = { .flag = OPT_DURATION_LIMIT , .arg_index = OPT_ARG_DURATION_LIMIT + 1 }, [ 'm' ] = { .flag = OPT_MUSICBACK , .arg_index = OPT_ARG_MUSICBACK + 1 }, [ 'M' ] = { .flag = OPT_CALLEE_MACRO , .arg_index = OPT_ARG_CALLEE_MACRO + 1 }, [ 'n' ] = { .flag = OPT_SCREEN_NOINTRO , .arg_index = OPT_ARG_SCREEN_NOINTRO + 1 }, [ 'N' ] = { .flag = OPT_SCREEN_NOCLID }, [ 'o' ] = { .flag = OPT_ORIGINAL_CLID }, [ 'O' ] = { .flag = OPT_OPERMODE , .arg_index = OPT_ARG_OPERMODE + 1 }, [ 'p' ] = { .flag = OPT_SCREENING }, [ 'P' ] = { .flag = OPT_PRIVACY , .arg_index = OPT_ARG_PRIVACY + 1 }, [ 'r' ] = { .flag = OPT_RINGBACK }, [ 'S' ] = { .flag = OPT_DURATION_STOP , .arg_index = OPT_ARG_DURATION_STOP + 1 }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'U' ] = { .flag = OPT_CALLEE_GOSUB , .arg_index = OPT_ARG_CALLEE_GOSUB + 1 }, [ 'w' ] = { .flag = OPT_CALLEE_MONITOR }, [ 'W' ] = { .flag = OPT_CALLER_MONITOR }, [ 'x' ] = { .flag = OPT_CALLEE_MIXMONITOR }, [ 'X' ] = { .flag = OPT_CALLER_MIXMONITOR }, }
static char * rapp = "RetryDial"

Detailed Description

dial() & retrydial() - Trivial application to dial a channel and send an URL on answer

Author:
Mark Spencer <markster@digium.com>

Definition in file app_dial.c.


Define Documentation

#define AST_MAX_WATCHERS   256

Definition at line 597 of file app_dial.c.

Referenced by wait_for_answer().

#define CAN_EARLY_BRIDGE (   flags,
  chan,
  peer 
)
#define DIAL_NOFORWARDHTML   ((uint64_t)1 << 32)

Definition at line 502 of file app_dial.c.

Referenced by dial_exec_full(), and wait_for_answer().

#define DIAL_STILLGOING   (1 << 31)

Definition at line 501 of file app_dial.c.

Referenced by dial_exec_full(), do_forward(), and wait_for_answer().

#define OPT_CALLEE_GO_ON   ((uint64_t)1 << 35)

Definition at line 505 of file app_dial.c.

Referenced by dial_exec_full().

#define OPT_CANCEL_ELSEWHERE   ((uint64_t)1 << 33)

Definition at line 503 of file app_dial.c.

Referenced by dial_exec_full().

#define OPT_PEER_H   ((uint64_t)1 << 34)

Definition at line 504 of file app_dial.c.

Referenced by dial_exec_full().

#define S_REPLACE (   s,
  new_val 
)
Value:
do {           \
      if (s)         \
         ast_free(s);   \
      s = (new_val);    \
   } while (0)

Definition at line 648 of file app_dial.c.

Referenced by dial_exec_full(), and do_forward().


Enumeration Type Documentation

anonymous enum
Enumerator:
OPT_ARG_ANNOUNCE 
OPT_ARG_SENDDTMF 
OPT_ARG_GOTO 
OPT_ARG_DURATION_LIMIT 
OPT_ARG_MUSICBACK 
OPT_ARG_CALLEE_MACRO 
OPT_ARG_CALLEE_GOSUB 
OPT_ARG_CALLEE_GO_ON 
OPT_ARG_PRIVACY 
OPT_ARG_DURATION_STOP 
OPT_ARG_OPERMODE 
OPT_ARG_SCREEN_NOINTRO 
OPT_ARG_ARRAY_SIZE 

Definition at line 507 of file app_dial.c.

anonymous enum
Enumerator:
OPT_ANNOUNCE 
OPT_RESETCDR 
OPT_DTMF_EXIT 
OPT_SENDDTMF 
OPT_FORCECLID 
OPT_GO_ON 
OPT_CALLEE_HANGUP 
OPT_CALLER_HANGUP 
OPT_DURATION_LIMIT 
OPT_MUSICBACK 
OPT_CALLEE_MACRO 
OPT_SCREEN_NOINTRO 
OPT_SCREEN_NOCLID 
OPT_ORIGINAL_CLID 
OPT_SCREENING 
OPT_PRIVACY 
OPT_RINGBACK 
OPT_DURATION_STOP 
OPT_CALLEE_TRANSFER 
OPT_CALLER_TRANSFER 
OPT_CALLEE_MONITOR 
OPT_CALLER_MONITOR 
OPT_GOTO 
OPT_OPERMODE 
OPT_CALLEE_PARK 
OPT_CALLER_PARK 
OPT_IGNORE_FORWARDING 
OPT_CALLEE_GOSUB 
OPT_CALLEE_MIXMONITOR 
OPT_CALLER_MIXMONITOR 

Definition at line 468 of file app_dial.c.

     {
   OPT_ANNOUNCE =          (1 << 0),
   OPT_RESETCDR =          (1 << 1),
   OPT_DTMF_EXIT =         (1 << 2),
   OPT_SENDDTMF =          (1 << 3),
   OPT_FORCECLID =         (1 << 4),
   OPT_GO_ON =             (1 << 5),
   OPT_CALLEE_HANGUP =     (1 << 6),
   OPT_CALLER_HANGUP =     (1 << 7),
   OPT_DURATION_LIMIT =    (1 << 9),
   OPT_MUSICBACK =         (1 << 10),
   OPT_CALLEE_MACRO =      (1 << 11),
   OPT_SCREEN_NOINTRO =    (1 << 12),
   OPT_SCREEN_NOCLID =     (1 << 13),
   OPT_ORIGINAL_CLID =     (1 << 14),
   OPT_SCREENING =         (1 << 15),
   OPT_PRIVACY =           (1 << 16),
   OPT_RINGBACK =          (1 << 17),
   OPT_DURATION_STOP =     (1 << 18),
   OPT_CALLEE_TRANSFER =   (1 << 19),
   OPT_CALLER_TRANSFER =   (1 << 20),
   OPT_CALLEE_MONITOR =    (1 << 21),
   OPT_CALLER_MONITOR =    (1 << 22),
   OPT_GOTO =              (1 << 23),
   OPT_OPERMODE =          (1 << 24),
   OPT_CALLEE_PARK =       (1 << 25),
   OPT_CALLER_PARK =       (1 << 26),
   OPT_IGNORE_FORWARDING = (1 << 27),
   OPT_CALLEE_GOSUB =      (1 << 28),
   OPT_CALLEE_MIXMONITOR = (1 << 29),
   OPT_CALLER_MIXMONITOR = (1 << 30),
};

Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 2506 of file app_dial.c.

static void __unreg_module ( void  ) [static]

Definition at line 2506 of file app_dial.c.

static int dial_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 2356 of file app_dial.c.

References dial_exec_full().

Referenced by load_module().

{
   struct ast_flags64 peerflags;

   memset(&peerflags, 0, sizeof(peerflags));

   return dial_exec_full(chan, data, &peerflags, NULL);
}
static int dial_exec_full ( struct ast_channel chan,
void *  data,
struct ast_flags64 peerflags,
int *  continue_exec 
) [static]

Definition at line 1529 of file app_dial.c.

References __ast_answer(), ast_channel::_state, ast_channel::accountcode, accountcode, ast_channel::adsicpe, ast_channel::appl, asprintf, AST_APP_ARG, ast_app_group_set_channel(), ast_app_parse_options64(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_call(), ast_calloc, ast_cause2str(), AST_CAUSE_INVALID_NUMBER_FORMAT, AST_CDR_FLAG_DIALED, ast_cdr_reset(), ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_inherit(), ast_channel_datastore_remove(), ast_channel_early_bridge(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, AST_CONTROL_HANGUP, AST_CONTROL_PROGRESS, AST_CONTROL_RINGING, ast_copy_flags64, ast_copy_string(), ast_datastore_alloc(), ast_datastore_free(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, ast_dtmf_stream(), ast_exists_extension(), AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_filedelete(), ast_fileexists(), AST_FLAG_ANSWERED_ELSEWHERE, AST_FLAG_END_DTMF_ONLY, AST_FLAG_IN_AUTOLOOP, AST_FRAME_CONTROL, AST_FRAME_DTMF_END, ast_free, ast_frfree, ast_hangup(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_EXTENSION, ast_moh_start(), ast_moh_stop(), AST_OPTION_OPRMODE, ast_parseable_goto(), AST_PBX_INCOMPLETE, ast_pbx_run_args(), ast_pbx_start(), AST_PRIVACY_UNKNOWN, ast_read(), ast_request(), ast_rtp_make_compatible(), ast_sched_runq(), ast_sched_wait(), ast_senddigit(), ast_set2_flag, ast_set2_flag64, ast_set_callerid(), ast_set_flag, ast_set_flag64, ast_spawn_extension(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_strdup, ast_strdupa, ast_streamfile(), ast_string_field_set, ast_strlen_zero(), ast_test_flag, ast_test_flag64, ast_tvadd(), ast_tvnow(), ast_tvzero(), ast_verb, ast_waitfor_n(), CAN_EARLY_BRIDGE, cause, ast_channel::cdr, ast_channel::cdrflags, chanlist::chan, chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_ani2, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_pres, ast_callerid::cid_rdnis, ast_callerid::cid_tns, ast_callerid::cid_ton, ast_channel::context, ast_channel::data, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dial_exec_options, DIAL_NOFORWARDHTML, DIAL_STILLGOING, dialcontext, dialed_interface_info, do_timelimit(), end_bridge_callback(), ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, end_bridge_callback_data_fixup(), ast_bridge_config::end_bridge_callback_data_fixup, ast_bridge_config::end_sound, errno, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_flags64::flags, ast_frame::frametype, get_cid_name(), handle_cause(), ast_channel::hangupcause, hanguptree(), ast_datastore::inheritance, ast_dialed_interface::interface, ast_channel::language, ast_dialed_interface::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::macrocontext, ast_channel::macroexten, oprmode::mode, moh, musicclass, ast_channel::musicclass, ast_channel::name, ast_channel::nativeformats, chanlist::next, ast_pbx_args::no_hangup_chan, OPT_ANNOUNCE, OPT_ARG_ANNOUNCE, OPT_ARG_ARRAY_SIZE, OPT_ARG_CALLEE_GO_ON, OPT_ARG_CALLEE_GOSUB, OPT_ARG_CALLEE_MACRO, OPT_ARG_DURATION_LIMIT, OPT_ARG_DURATION_STOP, OPT_ARG_GOTO, OPT_ARG_OPERMODE, OPT_ARG_PRIVACY, OPT_ARG_SCREEN_NOINTRO, OPT_ARG_SENDDTMF, OPT_CALLEE_GO_ON, OPT_CALLEE_GOSUB, OPT_CALLEE_HANGUP, OPT_CALLEE_MACRO, OPT_CALLEE_MIXMONITOR, OPT_CALLEE_MONITOR, OPT_CALLEE_PARK, OPT_CALLEE_TRANSFER, OPT_CALLER_HANGUP, OPT_CALLER_MIXMONITOR, OPT_CALLER_MONITOR, OPT_CALLER_PARK, OPT_CALLER_TRANSFER, OPT_CANCEL_ELSEWHERE, OPT_DTMF_EXIT, OPT_DURATION_LIMIT, OPT_DURATION_STOP, OPT_FORCECLID, OPT_GO_ON, OPT_GOTO, OPT_IGNORE_FORWARDING, OPT_MUSICBACK, OPT_OPERMODE, OPT_ORIGINAL_CLID, OPT_PEER_H, OPT_PRIVACY, OPT_RESETCDR, OPT_RINGBACK, OPT_SCREEN_NOINTRO, OPT_SCREENING, OPT_SENDDTMF, parse(), pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), oprmode::peer, ast_channel::priority, privacy_args::privdb_val, privacy_args::privintro, replace_macro_delimiter(), S_OR, S_REPLACE, ast_channel::sched, senddialendevent(), senddialevent(), privacy_args::sentringing, setup_privacy_args(), ast_bridge_config::start_sound, privacy_args::status, ast_channel::stream, strsep(), ast_frame::subclass, ast_channel::tech, ast_channel::timingfunc, ast_channel::transfercapability, url, ast_channel::visible_indication, wait_for_answer(), ast_bridge_config::warning_sound, and ast_channel::whentohangup.

Referenced by dial_exec(), and retrydial_exec().

{
   int res = -1; /* default: error */
   char *rest, *cur; /* scan the list of destinations */
   struct chanlist *outgoing = NULL; /* list of destinations */
   struct ast_channel *peer;
   int to; /* timeout */
   struct cause_args num = { chan, 0, 0, 0 };
   int cause;
   char numsubst[256];
   char cidname[AST_MAX_EXTENSION] = "";

   struct ast_bridge_config config = { { 0, } };
   struct timeval calldurationlimit = { 0, };
   char *dtmfcalled = NULL, *dtmfcalling = NULL;
   struct privacy_args pa = {
      .sentringing = 0,
      .privdb_val = 0,
      .status = "INVALIDARGS",
   };
   int sentringing = 0, moh = 0;
   const char *outbound_group = NULL;
   int result = 0;
   char *parse;
   int opermode = 0;
   int delprivintro = 0;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(peers);
      AST_APP_ARG(timeout);
      AST_APP_ARG(options);
      AST_APP_ARG(url);
   );
   struct ast_flags64 opts = { 0, };
   char *opt_args[OPT_ARG_ARRAY_SIZE];
   struct ast_datastore *datastore = NULL;
   int fulldial = 0, num_dialed = 0;

   /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
   pbx_builtin_setvar_helper(chan, "DIALSTATUS", "");
   pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", "");
   pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", "");
   pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", "");
   pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
      pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (!ast_strlen_zero(args.options) &&
      ast_app_parse_options64(dial_exec_options, &opts, opt_args, args.options)) {
      pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
      goto done;
   }

   if (ast_strlen_zero(args.peers)) {
      ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
      pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
      goto done;
   }


   if (ast_test_flag64(&opts, OPT_SCREEN_NOINTRO) && !ast_strlen_zero(opt_args[OPT_ARG_SCREEN_NOINTRO])) {
      delprivintro = atoi(opt_args[OPT_ARG_SCREEN_NOINTRO]);

      if (delprivintro < 0 || delprivintro > 1) {
         ast_log(LOG_WARNING, "Unknown argument %d specified to n option, ignoring\n", delprivintro);
         delprivintro = 0;
      }
   }

   if (ast_test_flag64(&opts, OPT_OPERMODE)) {
      opermode = ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]) ? 1 : atoi(opt_args[OPT_ARG_OPERMODE]);
      ast_verb(3, "Setting operator services mode to %d.\n", opermode);
   }
   
   if (ast_test_flag64(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) {
      calldurationlimit.tv_sec = atoi(opt_args[OPT_ARG_DURATION_STOP]);
      if (!calldurationlimit.tv_sec) {
         ast_log(LOG_WARNING, "Dial does not accept S(%s), hanging up.\n", opt_args[OPT_ARG_DURATION_STOP]);
         pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
         goto done;
      }
      ast_verb(3, "Setting call duration limit to %.3lf milliseconds.\n", calldurationlimit.tv_sec + calldurationlimit.tv_usec / 1000000.0);
   }

   if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
      dtmfcalling = opt_args[OPT_ARG_SENDDTMF];
      dtmfcalled = strsep(&dtmfcalling, ":");
   }

   if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
      if (do_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
         goto done;
   }

   if (ast_test_flag64(&opts, OPT_RESETCDR) && chan->cdr)
      ast_cdr_reset(chan->cdr, NULL);
   if (ast_test_flag64(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
      opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten);

   if (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) {
      res = setup_privacy_args(&pa, &opts, opt_args, chan);
      if (res <= 0)
         goto out;
      res = -1; /* reset default */
   }

   if (ast_test_flag64(&opts, OPT_DTMF_EXIT)) {
      __ast_answer(chan, 0, 0);
   }

   if (continue_exec)
      *continue_exec = 0;

   /* If a channel group has been specified, get it for use when we create peer channels */

   ast_channel_lock(chan);
   if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
      outbound_group = ast_strdupa(outbound_group);   
      pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
   } else if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"))) {
      outbound_group = ast_strdupa(outbound_group);
   }
   ast_channel_unlock(chan);  
   ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB);

   /* loop through the list of dial destinations */
   rest = args.peers;
   while ((cur = strsep(&rest, "&")) ) {
      struct chanlist *tmp;
      struct ast_channel *tc; /* channel for this destination */
      /* Get a technology/[device:]number pair */
      char *number = cur;
      char *interface = ast_strdupa(number);
      char *tech = strsep(&number, "/");
      /* find if we already dialed this interface */
      struct ast_dialed_interface *di;
      AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
      num_dialed++;
      if (ast_strlen_zero(number)) {
         ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n");
         goto out;
      }
      if (!(tmp = ast_calloc(1, sizeof(*tmp))))
         goto out;
      if (opts.flags) {
         ast_copy_flags64(tmp, &opts,
            OPT_CANCEL_ELSEWHERE |
            OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
            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);
      }
      ast_copy_string(numsubst, number, sizeof(numsubst));
      /* Request the peer */

      ast_channel_lock(chan);
      datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
      ast_channel_unlock(chan);

      if (datastore)
         dialed_interfaces = datastore->data;
      else {
         if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
            ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
            ast_free(tmp);
            goto out;
         }

         datastore->inheritance = DATASTORE_INHERIT_FOREVER;

         if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
            ast_datastore_free(datastore);
            ast_free(tmp);
            goto out;
         }

         datastore->data = dialed_interfaces;
         AST_LIST_HEAD_INIT(dialed_interfaces);

         ast_channel_lock(chan);
         ast_channel_datastore_add(chan, datastore);
         ast_channel_unlock(chan);
      }

      AST_LIST_LOCK(dialed_interfaces);
      AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
         if (!strcasecmp(di->interface, interface)) {
            ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
               di->interface);
            break;
         }
      }
      AST_LIST_UNLOCK(dialed_interfaces);

      if (di) {
         fulldial++;
         ast_free(tmp);
         continue;
      }

      /* It is always ok to dial a Local interface.  We only keep track of
       * which "real" interfaces have been dialed.  The Local channel will
       * inherit this list so that if it ends up dialing a real interface,
       * it won't call one that has already been called. */
      if (strcasecmp(tech, "Local")) {
         if (!(di = ast_calloc(1, sizeof(*di) + strlen(interface)))) {
            AST_LIST_UNLOCK(dialed_interfaces);
            ast_free(tmp);
            goto out;
         }
         strcpy(di->interface, interface);

         AST_LIST_LOCK(dialed_interfaces);
         AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
         AST_LIST_UNLOCK(dialed_interfaces);
      }

      tc = ast_request(tech, chan->nativeformats, numsubst, &cause);
      if (!tc) {
         /* If we can't, just go on to the next call */
         ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
            tech, cause, ast_cause2str(cause));
         handle_cause(cause, &num);
         if (!rest) /* we are on the last destination */
            chan->hangupcause = cause;
         ast_free(tmp);
         continue;
      }
      pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);

      /* Setup outgoing SDP to match incoming one */
      if (CAN_EARLY_BRIDGE(peerflags, chan, tc)) {
         ast_rtp_make_compatible(tc, chan, !outgoing && !rest);
      }
      
      /* Inherit specially named variables from parent channel */
      ast_channel_inherit_variables(chan, tc);
      ast_channel_datastore_inherit(chan, tc);

      tc->appl = "AppDial";
      tc->data = "(Outgoing Line)";
      memset(&tc->whentohangup, 0, sizeof(tc->whentohangup));

      S_REPLACE(tc->cid.cid_num, ast_strdup(chan->cid.cid_num));
      S_REPLACE(tc->cid.cid_name, ast_strdup(chan->cid.cid_name));
      S_REPLACE(tc->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
      S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
      
      ast_string_field_set(tc, accountcode, chan->accountcode);
      tc->cdrflags = chan->cdrflags;
      if (ast_strlen_zero(tc->musicclass))
         ast_string_field_set(tc, musicclass, chan->musicclass);
      /* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */
      tc->cid.cid_pres = chan->cid.cid_pres;
      tc->cid.cid_ton = chan->cid.cid_ton;
      tc->cid.cid_tns = chan->cid.cid_tns;
      tc->cid.cid_ani2 = chan->cid.cid_ani2;
      tc->adsicpe = chan->adsicpe;
      tc->transfercapability = chan->transfercapability;

      /* If we have an outbound group, set this peer channel to it */
      if (outbound_group)
         ast_app_group_set_channel(tc, outbound_group);
      /* If the calling channel has the ANSWERED_ELSEWHERE flag set, inherit it. This is to support local channels */
      if (ast_test_flag(chan, AST_FLAG_ANSWERED_ELSEWHERE))
         ast_set_flag(tc, AST_FLAG_ANSWERED_ELSEWHERE);

      /* Check if we're forced by configuration */
      if (ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE))
          ast_set_flag(tc, AST_FLAG_ANSWERED_ELSEWHERE);


      /* Inherit context and extension */
      ast_string_field_set(tc, dialcontext, ast_strlen_zero(chan->macrocontext) ? chan->context : chan->macrocontext);
      if (!ast_strlen_zero(chan->macroexten))
         ast_copy_string(tc->exten, chan->macroexten, sizeof(tc->exten));
      else
         ast_copy_string(tc->exten, chan->exten, sizeof(tc->exten));

      res = ast_call(tc, numsubst, 0); /* Place the call, but don't wait on the answer */

      /* Save the info in cdr's that we called them */
      if (chan->cdr)
         ast_cdr_setdestchan(chan->cdr, tc->name);

      /* check the results of ast_call */
      if (res) {
         /* Again, keep going even if there's an error */
         ast_debug(1, "ast call on peer returned %d\n", res);
         ast_verb(3, "Couldn't call %s\n", numsubst);
         if (tc->hangupcause) {
            chan->hangupcause = tc->hangupcause;
         }
         ast_hangup(tc);
         tc = NULL;
         ast_free(tmp);
         continue;
      } else {
         senddialevent(chan, tc, numsubst);
         ast_verb(3, "Called %s\n", numsubst);
         if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID))
            ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL);
      }
      /* Put them in the list of outgoing thingies...  We're ready now.
         XXX If we're forcibly removed, these outgoing calls won't get
         hung up XXX */
      ast_set_flag64(tmp, DIAL_STILLGOING);
      tmp->chan = tc;
      tmp->next = outgoing;
      outgoing = tmp;
      /* If this line is up, don't try anybody else */
      if (outgoing->chan->_state == AST_STATE_UP)
         break;
   }
   
   if (ast_strlen_zero(args.timeout)) {
      to = -1;
   } else {
      to = atoi(args.timeout);
      if (to > 0)
         to *= 1000;
      else {
         ast_log(LOG_WARNING, "Invalid timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
         to = -1;
      }
   }

   if (!outgoing) {
      strcpy(pa.status, "CHANUNAVAIL");
      if (fulldial == num_dialed) {
         res = -1;
         goto out;
      }
   } else {
      /* Our status will at least be NOANSWER */
      strcpy(pa.status, "NOANSWER");
      if (ast_test_flag64(outgoing, OPT_MUSICBACK)) {
         moh = 1;
         if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
            char *original_moh = ast_strdupa(chan->musicclass);
            ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
            ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
            ast_string_field_set(chan, musicclass, original_moh);
         } else {
            ast_moh_start(chan, NULL, NULL);
         }
         ast_indicate(chan, AST_CONTROL_PROGRESS);
      } else if (ast_test_flag64(outgoing, OPT_RINGBACK)) {
         ast_indicate(chan, AST_CONTROL_RINGING);
         sentringing++;
      }
   }

   peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result);

   /* The ast_channel_datastore_remove() function could fail here if the
    * datastore was moved to another channel during a masquerade. If this is
    * the case, don't free the datastore here because later, when the channel
    * to which the datastore was moved hangs up, it will attempt to free this
    * datastore again, causing a crash
    */
   if (!ast_channel_datastore_remove(chan, datastore))
      ast_datastore_free(datastore);
   if (!peer) {
      if (result) {
         res = result;
      } else if (to) { /* Musta gotten hung up */
         res = -1;
      } else { /* Nobody answered, next please? */
         res = 0;
      }

      /* SIP, in particular, sends back this error code to indicate an
       * overlap dialled number needs more digits. */
      if (chan->hangupcause == AST_CAUSE_INVALID_NUMBER_FORMAT) {
         res = AST_PBX_INCOMPLETE;
      }

      /* almost done, although the 'else' block is 400 lines */
   } else {
      const char *number;

      strcpy(pa.status, "ANSWER");
      pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
      /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
         we will always return with -1 so that it is hung up properly after the
         conversation.  */
      hanguptree(outgoing, peer, 1);
      outgoing = NULL;
      /* If appropriate, log that we have a destination channel */
      if (chan->cdr)
         ast_cdr_setdestchan(chan->cdr, peer->name);
      if (peer->name)
         pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name);
      
      ast_channel_lock(peer);
      number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER"); 
      if (!number)
         number = numsubst;
      pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
      ast_channel_unlock(peer);

      if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
         ast_debug(1, "app_dial: sendurl=%s.\n", args.url);
         ast_channel_sendurl( peer, args.url );
      }
      if ( (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) && pa.privdb_val == AST_PRIVACY_UNKNOWN) {
         if (do_privacy(chan, peer, &opts, opt_args, &pa)) {
            res = 0;
            goto out;
         }
      }
      if (!ast_test_flag64(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
         res = 0;
      } else {
         int digit = 0;
         struct ast_channel *chans[2];
         struct ast_channel *active_chan;

         chans[0] = chan;
         chans[1] = peer;

         /* we need to stream the announcment while monitoring the caller for a hangup */

         /* stream the file */
         res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language);
         if (res) {
            res = 0;
            ast_log(LOG_ERROR, "error streaming file '%s' to callee\n", opt_args[OPT_ARG_ANNOUNCE]);
         }

         ast_set_flag(peer, AST_FLAG_END_DTMF_ONLY);
         while (peer->stream) {
            int ms;

            ms = ast_sched_wait(peer->sched);

            if (ms < 0 && !peer->timingfunc) {
               ast_stopstream(peer);
               break;
            }
            if (ms < 0)
               ms = 1000;

            active_chan = ast_waitfor_n(chans, 2, &ms);
            if (active_chan) {
               struct ast_frame *fr = ast_read(active_chan);
               if (!fr) {
                  ast_hangup(peer);
                  res = -1;
                  goto done;
               }
               switch(fr->frametype) {
                  case AST_FRAME_DTMF_END:
                     digit = fr->subclass;
                     if (active_chan == peer && strchr(AST_DIGIT_ANY, res)) {
                        ast_stopstream(peer);
                        res = ast_senddigit(chan, digit, 0);
                     }
                     break;
                  case AST_FRAME_CONTROL:
                     switch (fr->subclass) {
                        case AST_CONTROL_HANGUP:
                           ast_frfree(fr);
                           ast_hangup(peer);
                           res = -1;
                           goto done;
                        default:
                           break;
                     }
                     break;
                  default:
                     /* Ignore all others */
                     break;
               }
               ast_frfree(fr);
            }
            ast_sched_runq(peer->sched);
         }
         ast_clear_flag(peer, AST_FLAG_END_DTMF_ONLY);
      }

      if (chan && peer && ast_test_flag64(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
         /* chan and peer are going into the PBX, they both
          * should probably get CDR records. */
         ast_clear_flag(chan->cdr, AST_CDR_FLAG_DIALED);
         ast_clear_flag(peer->cdr, AST_CDR_FLAG_DIALED);

         replace_macro_delimiter(opt_args[OPT_ARG_GOTO]);
         ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);
         /* peer goes to the same context and extension as chan, so just copy info from chan*/
         ast_copy_string(peer->context, chan->context, sizeof(peer->context));
         ast_copy_string(peer->exten, chan->exten, sizeof(peer->exten));
         peer->priority = chan->priority + 2;
         ast_pbx_start(peer);
         hanguptree(outgoing, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
         if (continue_exec)
            *continue_exec = 1;
         res = 0;
         goto done;
      }

      if (ast_test_flag64(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
         struct ast_app *theapp;
         const char *macro_result;

         res = ast_autoservice_start(chan);
         if (res) {
            ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
            res = -1;
         }

         theapp = pbx_findapp("Macro");

         if (theapp && !res) { /* XXX why check res here ? */
            /* Set peer->exten and peer->context so that MACRO_EXTEN and MACRO_CONTEXT get set */
            ast_copy_string(peer->context, chan->context, sizeof(peer->context));
            ast_copy_string(peer->exten, chan->exten, sizeof(peer->exten));

            replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
            res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_MACRO]);
            ast_debug(1, "Macro exited with status %d\n", res);
            res = 0;
         } else {
            ast_log(LOG_ERROR, "Could not find application Macro\n");
            res = -1;
         }

         if (ast_autoservice_stop(chan) < 0) {
            res = -1;
         }

         ast_channel_lock(peer);

         if (!res && (macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
            char *macro_transfer_dest;

            if (!strcasecmp(macro_result, "BUSY")) {
               ast_copy_string(pa.status, macro_result, sizeof(pa.status));
               ast_set_flag64(peerflags, OPT_GO_ON);
               res = -1;
            } else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
               ast_copy_string(pa.status, macro_result, sizeof(pa.status));
               ast_set_flag64(peerflags, OPT_GO_ON);
               res = -1;
            } else if (!strcasecmp(macro_result, "CONTINUE")) {
               /* hangup peer and keep chan alive assuming the macro has changed
                  the context / exten / priority or perhaps
                  the next priority in the current exten is desired.
               */
               ast_set_flag64(peerflags, OPT_GO_ON);
               res = -1;
            } else if (!strcasecmp(macro_result, "ABORT")) {
               /* Hangup both ends unless the caller has the g flag */
               res = -1;
            } else if (!strncasecmp(macro_result, "GOTO:", 5) && (macro_transfer_dest = ast_strdupa(macro_result + 5))) {
               res = -1;
               /* perform a transfer to a new extension */
               if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
                  replace_macro_delimiter(macro_transfer_dest);
                  if (!ast_parseable_goto(chan, macro_transfer_dest))
                     ast_set_flag64(peerflags, OPT_GO_ON);
               }
            }
         }

         ast_channel_unlock(peer);
      }

      if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
         struct ast_app *theapp;
         const char *gosub_result;
         char *gosub_args, *gosub_argstart;
         int res9 = -1;

         res9 = ast_autoservice_start(chan);
         if (res9) {
            ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
            res9 = -1;
         }

         theapp = pbx_findapp("Gosub");

         if (theapp && !res9) {
            replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);

            /* Set where we came from */
            ast_copy_string(peer->context, "app_dial_gosub_virtual_context", sizeof(peer->context));
            ast_copy_string(peer->exten, "s", sizeof(peer->exten));
            peer->priority = 0;

            gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], ',');
            if (gosub_argstart) {
               *gosub_argstart = 0;
               if (asprintf(&gosub_args, "%s,s,1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], gosub_argstart + 1) < 0) {
                  ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
                  gosub_args = NULL;
               }
               *gosub_argstart = ',';
            } else {
               if (asprintf(&gosub_args, "%s,s,1", opt_args[OPT_ARG_CALLEE_GOSUB]) < 0) {
                  ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
                  gosub_args = NULL;
               }
            }

            if (gosub_args) {
               res9 = pbx_exec(peer, theapp, gosub_args);
               if (!res9) {
                  struct ast_pbx_args args;
                  /* A struct initializer fails to compile for this case ... */
                  memset(&args, 0, sizeof(args));
                  args.no_hangup_chan = 1;
                  ast_pbx_run_args(peer, &args);
               }
               ast_free(gosub_args);
               ast_debug(1, "Gosub exited with status %d\n", res9);
            } else {
               ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
            }

         } else if (!res9) {
            ast_log(LOG_ERROR, "Could not find application Gosub\n");
            res9 = -1;
         }

         if (ast_autoservice_stop(chan) < 0) {
            ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
            res9 = -1;
         }
         
         ast_channel_lock(peer);

         if (!res9 && (gosub_result = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
            char *gosub_transfer_dest;

            if (!strcasecmp(gosub_result, "BUSY")) {
               ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
               ast_set_flag64(peerflags, OPT_GO_ON);
               res = -1;
            } else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
               ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
               ast_set_flag64(peerflags, OPT_GO_ON);
               res = -1;
            } else if (!strcasecmp(gosub_result, "CONTINUE")) {
               /* hangup peer and keep chan alive assuming the macro has changed
                  the context / exten / priority or perhaps
                  the next priority in the current exten is desired.
               */
               ast_set_flag64(peerflags, OPT_GO_ON);
               res = -1;
            } else if (!strcasecmp(gosub_result, "ABORT")) {
               /* Hangup both ends unless the caller has the g flag */
               res = -1;
            } else if (!strncasecmp(gosub_result, "GOTO:", 5) && (gosub_transfer_dest = ast_strdupa(gosub_result + 5))) {
               res = -1;
               /* perform a transfer to a new extension */
               if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
                  replace_macro_delimiter(gosub_transfer_dest);
                  if (!ast_parseable_goto(chan, gosub_transfer_dest))
                     ast_set_flag64(peerflags, OPT_GO_ON);
               }
            }
         }

         ast_channel_unlock(peer);  
      }

      if (!res) {
         if (!ast_tvzero(calldurationlimit)) {
            struct timeval whentohangup = calldurationlimit;
            peer->whentohangup = ast_tvadd(ast_tvnow(), whentohangup);
         }
         if (!ast_strlen_zero(dtmfcalled)) {
            ast_verb(3, "Sending DTMF '%s' to the called party.\n", dtmfcalled);
            res = ast_dtmf_stream(peer, chan, dtmfcalled, 250, 0);
         }
         if (!ast_strlen_zero(dtmfcalling)) {
            ast_verb(3, "Sending DTMF '%s' to the calling party.\n", dtmfcalling);
            res = ast_dtmf_stream(chan, peer, dtmfcalling, 250, 0);
         }
      }

      if (res) { /* some error */
         res = -1;
      } else {
         if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER))
            ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
         if (ast_test_flag64(peerflags, OPT_CALLER_TRANSFER))
            ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
         if (ast_test_flag64(peerflags, OPT_CALLEE_HANGUP))
            ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
         if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP))
            ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
         if (ast_test_flag64(peerflags, OPT_CALLEE_MONITOR))
            ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
         if (ast_test_flag64(peerflags, OPT_CALLER_MONITOR))
            ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
         if (ast_test_flag64(peerflags, OPT_CALLEE_PARK))
            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 (ast_test_flag64(peerflags, OPT_GO_ON))
            ast_set_flag(&(config.features_caller), AST_FEATURE_NO_H_EXTEN);

         config.end_bridge_callback = end_bridge_callback;
         config.end_bridge_callback_data = chan;
         config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
         
         if (moh) {
            moh = 0;
            ast_moh_stop(chan);
         } else if (sentringing) {
            sentringing = 0;
            ast_indicate(chan, -1);
         }
         /* Be sure no generators are left on it and reset the visible indication */
         ast_deactivate_generator(chan);
         chan->visible_indication = 0;
         /* Make sure channels are compatible */
         res = ast_channel_make_compatible(chan, peer);
         if (res < 0) {
            ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
            ast_hangup(peer);
            res = -1;
            goto done;
         }
         if (opermode) {
            struct oprmode oprmode;

            oprmode.peer = peer;
            oprmode.mode = opermode;

            ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
         }
         res = ast_bridge_call(chan, peer, &config);
      }

      strcpy(peer->context, chan->context);

      if (ast_test_flag64(&opts, OPT_PEER_H) && ast_exists_extension(peer, peer->context, "h", 1, peer->cid.cid_num)) {
         int autoloopflag;
         int found;
         int res9;
         
         strcpy(peer->exten, "h");
         peer->priority = 1;
         autoloopflag = ast_test_flag(peer, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
         ast_set_flag(peer, AST_FLAG_IN_AUTOLOOP);

         while ((res9 = ast_spawn_extension(peer, peer->context, peer->exten, peer->priority, peer->cid.cid_num, &found, 1)) == 0)
            peer->priority++;

         if (found && res9) {
            /* Something bad happened, or a hangup has been requested. */
            ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
            ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", peer->context, peer->exten, peer->priority, peer->name);
         }
         ast_set2_flag(peer, autoloopflag, AST_FLAG_IN_AUTOLOOP);  /* set it back the way it was */
      }
      if (!ast_check_hangup(peer) && ast_test_flag64(&opts, OPT_CALLEE_GO_ON) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {      
         replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
         ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
         ast_pbx_start(peer);
      } else {
         if (!ast_check_hangup(chan))
            chan->hangupcause = peer->hangupcause;
         ast_hangup(peer);
      }
   }
out:
   if (moh) {
      moh = 0;
      ast_moh_stop(chan);
   } else if (sentringing) {
      sentringing = 0;
      ast_indicate(chan, -1);
   }

   if (delprivintro && ast_fileexists(pa.privintro, NULL, NULL) > 0) {
      ast_filedelete(pa.privintro, NULL);
      if (ast_fileexists(pa.privintro, NULL, NULL) > 0) {
         ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", pa.privintro);
      } else {
         ast_verb(3, "Successfully deleted %s intro file\n", pa.privintro);
      }
   }

   ast_channel_early_bridge(chan, NULL);
   hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */
   pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
   senddialendevent(chan, pa.status);
   ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
   
   if ((ast_test_flag64(peerflags, OPT_GO_ON)) && !ast_check_hangup(chan) && (res != AST_PBX_INCOMPLETE)) {
      if (!ast_tvzero(calldurationlimit))
         memset(&chan->whentohangup, 0, sizeof(chan->whentohangup));
      res = 0;
   }

done:
   if (config.warning_sound) {
      ast_free((char *)config.warning_sound);
   }
   if (config.end_sound) {
      ast_free((char *)config.end_sound);
   }
   if (config.start_sound) {
      ast_free((char *)config.start_sound);
   }
   return res;
}
static void do_forward ( struct chanlist o,
struct cause_args num,
struct ast_flags64 peerflags,
int  single 
) [static]

helper function for wait_for_answer()

XXX this code is highly suspicious, as it essentially overwrites the outgoing channel without properly deleting it.

Definition at line 714 of file app_dial.c.

References ast_channel::accountcode, accountcode, ast_call(), AST_CAUSE_BUSY, ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_unlock, ast_clear_flag64, ast_copy_string(), ast_hangup(), ast_indicate(), ast_log(), AST_MAX_EXTENSION, ast_request(), ast_rtp_make_compatible(), ast_set_callerid(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_test_flag64, ast_verb, ast_channel::call_forward, CAN_EARLY_BRIDGE, cause, ast_channel::cdrflags, cause_args::chan, chanlist::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_rdnis, ast_channel::context, DIAL_STILLGOING, ast_channel::exten, get_cid_name(), handle_cause(), LOG_NOTICE, ast_channel::macroexten, ast_channel::name, ast_channel::nativeformats, cause_args::nochan, OPT_FORCECLID, OPT_IGNORE_FORWARDING, OPT_ORIGINAL_CLID, pbx_builtin_getvar_helper(), S_OR, S_REPLACE, senddialevent(), and ast_channel::tech.

Referenced by wait_for_answer().

{
   char tmpchan[256];
   struct ast_channel *original = o->chan;
   struct ast_channel *c = o->chan; /* the winner */
   struct ast_channel *in = num->chan; /* the input channel */
   char *stuff;
   char *tech;
   int cause;

   ast_copy_string(tmpchan, c->call_forward, sizeof(tmpchan));
   if ((stuff = strchr(tmpchan, '/'))) {
      *stuff++ = '\0';
      tech = tmpchan;
   } else {
      const char *forward_context;
      ast_channel_lock(c);
      forward_context = pbx_builtin_getvar_helper(c, "FORWARD_CONTEXT");
      if (ast_strlen_zero(forward_context)) {
         forward_context = NULL;
      }
      snprintf(tmpchan, sizeof(tmpchan), "%s@%s", c->call_forward, forward_context ? forward_context : c->context);
      ast_channel_unlock(c);
      stuff = tmpchan;
      tech = "Local";
   }
   /* Before processing channel, go ahead and check for forwarding */
   ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name);
   /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
   if (ast_test_flag64(peerflags, OPT_IGNORE_FORWARDING)) {
      ast_verb(3, "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff);
      c = o->chan = NULL;
      cause = AST_CAUSE_BUSY;
   } else {
      /* Setup parameters */
      c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
      if (c) {
         if (single)
            ast_channel_make_compatible(o->chan, in);
         ast_channel_inherit_variables(in, o->chan);
         ast_channel_datastore_inherit(in, o->chan);
      } else
         ast_log(LOG_NOTICE,
            "Forwarding failed to create channel to dial '%s/%s' (cause = %d)\n",
            tech, stuff, cause);
   }
   if (!c) {
      ast_clear_flag64(o, DIAL_STILLGOING);
      handle_cause(cause, num);
      ast_hangup(original);
   } else {
      char *new_cid_num, *new_cid_name;
      struct ast_channel *src;

      if (CAN_EARLY_BRIDGE(peerflags, c, in)) {
         ast_rtp_make_compatible(c, in, single);
      }
      if (ast_test_flag64(o, OPT_FORCECLID)) {
         new_cid_num = ast_strdup(S_OR(in->macroexten, in->exten));
         new_cid_name = NULL; /* XXX no name ? */
         src = c; /* XXX possible bug in previous code, which used 'winner' ? it may have changed */
      } else {
         new_cid_num = ast_strdup(in->cid.cid_num);
         new_cid_name = ast_strdup(in->cid.cid_name);
         src = in;
      }
      ast_string_field_set(c, accountcode, src->accountcode);
      c->cdrflags = src->cdrflags;
      S_REPLACE(c->cid.cid_num, new_cid_num);
      S_REPLACE(c->cid.cid_name, new_cid_name);

      if (in->cid.cid_ani) { /* XXX or maybe unconditional ? */
         S_REPLACE(c->cid.cid_ani, ast_strdup(in->cid.cid_ani));
      }
      S_REPLACE(c->cid.cid_rdnis, ast_strdup(S_OR(in->macroexten, in->exten)));
      if (ast_call(c, stuff, 0)) {
         ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
            tech, stuff);
         ast_clear_flag64(o, DIAL_STILLGOING);
         ast_hangup(original);
         ast_hangup(c);
         c = o->chan = NULL;
         num->nochan++;
      } else {
         senddialevent(in, c, stuff);
         /* After calling, set callerid to extension */
         if (!ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
            char cidname[AST_MAX_EXTENSION] = "";
            ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL);
         }
         /* Hangup the original channel now, in case we needed it */
         ast_hangup(original);
      }
      if (single) {
         ast_indicate(in, -1);
      }
   }
}
static int do_timelimit ( struct ast_channel chan,
struct ast_bridge_config config,
char *  parse,
struct timeval *  calldurationlimit 
) [static]

Definition at line 1157 of file app_dial.c.

References ast_channel_lock, ast_channel_unlock, AST_FEATURE_PLAY_WARNING, ast_log(), ast_set_flag, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_true(), ast_verb, ast_bridge_config::end_sound, ast_bridge_config::features_callee, ast_bridge_config::features_caller, LOG_WARNING, pbx_builtin_getvar_helper(), ast_bridge_config::play_warning, S_OR, ast_bridge_config::start_sound, strsep(), ast_bridge_config::timelimit, var, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound.

Referenced by dial_exec_full().

{
   char *stringp = ast_strdupa(parse);
   char *limit_str, *warning_str, *warnfreq_str;
   const char *var;
   int play_to_caller = 0, play_to_callee = 0;
   int delta;

   limit_str = strsep(&stringp, ":");
   warning_str = strsep(&stringp, ":");
   warnfreq_str = strsep(&stringp, ":");

   config->timelimit = atol(limit_str);
   if (warning_str)
      config->play_warning = atol(warning_str);
   if (warnfreq_str)
      config->warning_freq = atol(warnfreq_str);

   if (!config->timelimit) {
      ast_log(LOG_WARNING, "Dial does not accept L(%s), hanging up.\n", limit_str);
      config->timelimit = config->play_warning = config->warning_freq = 0;
      config->warning_sound = NULL;
      return -1; /* error */
   } else if ( (delta = config->play_warning - config->timelimit) > 0) {
      int w = config->warning_freq;

      /* If the first warning is requested _after_ the entire call would end,
         and no warning frequency is requested, then turn off the warning. If
         a warning frequency is requested, reduce the 'first warning' time by
         that frequency until it falls within the call's total time limit.
         Graphically:
              timelim->|    delta        |<-playwarning
         0__________________|_________________|
                | w  |    |    |    |

         so the number of intervals to cut is 1+(delta-1)/w
      */

      if (w == 0) {
         config->play_warning = 0;
      } else {
         config->play_warning -= w * ( 1 + (delta-1)/w );
         if (config->play_warning < 1)
            config->play_warning = config->warning_freq = 0;
      }
   }
   
   ast_channel_lock(chan);

   var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");

   play_to_caller = var ? ast_true(var) : 1;

   var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
   play_to_callee = var ? ast_true(var) : 0;

   if (!play_to_caller && !play_to_callee)
      play_to_caller = 1;

   var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
   config->warning_sound = !ast_strlen_zero(var) ? ast_strdup(var) : ast_strdup("timeleft");

   /* The code looking at config wants a NULL, not just "", to decide
    * that the message should not be played, so we replace "" with NULL.
    * Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
    * not found.
    */

   var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
   config->end_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;

   var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
   config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;

   ast_channel_unlock(chan);

   /* undo effect of S(x) in case they are both used */
   calldurationlimit->tv_sec = 0;
   calldurationlimit->tv_usec = 0;

   /* more efficient to do it like S(x) does since no advanced opts */
   if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
      calldurationlimit->tv_sec = config->timelimit / 1000;
      calldurationlimit->tv_usec = (config->timelimit % 1000) * 1000;
      ast_verb(3, "Setting call duration limit to %.3lf seconds.\n",
         calldurationlimit->tv_sec + calldurationlimit->tv_usec / 1000000.0);
      config->timelimit = play_to_caller = play_to_callee =
      config->play_warning = config->warning_freq = 0;
   } else {
      ast_verb(3, "Limit Data for this call:\n");
      ast_verb(4, "timelimit      = %ld\n", config->timelimit);
      ast_verb(4, "play_warning   = %ld\n", config->play_warning);
      ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
      ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
      ast_verb(4, "warning_freq   = %ld\n", config->warning_freq);
      ast_verb(4, "start_sound    = %s\n", S_OR(config->start_sound, ""));
      ast_verb(4, "warning_sound  = %s\n", config->warning_sound);
      ast_verb(4, "end_sound      = %s\n", S_OR(config->end_sound, ""));
   }
   if (play_to_caller)
      ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
   if (play_to_callee)
      ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
   return 0;
}
static void end_bridge_callback ( void *  data) [static]

Definition at line 1500 of file app_dial.c.

References ast_cdr::answer, ast_channel_lock, ast_channel_unlock, buf, ast_channel::cdr, ast_channel::data, pbx_builtin_setvar_helper(), and ast_cdr::start.

Referenced by dial_exec_full().

{
   char buf[80];
   time_t end;
   struct ast_channel *chan = data;

   if (!chan->cdr) {
      return;
   }

   time(&end);

   ast_channel_lock(chan);
   if (chan->cdr->answer.tv_sec) {
      snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
      pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
   }

   if (chan->cdr->start.tv_sec) {
      snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
      pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
   }
   ast_channel_unlock(chan);
}
static void end_bridge_callback_data_fixup ( struct ast_bridge_config bconfig,
struct ast_channel originator,
struct ast_channel terminator 
) [static]

Definition at line 1525 of file app_dial.c.

References ast_bridge_config::end_bridge_callback_data.

Referenced by dial_exec_full().

                                                                                                                                              {
   bconfig->end_bridge_callback_data = originator;
}
static const char* get_cid_name ( char *  name,
int  namelen,
struct ast_channel chan 
) [static]

Definition at line 674 of file app_dial.c.

References ast_get_hint(), ast_channel::context, context, ast_channel::exten, exten, ast_channel::macrocontext, ast_channel::macroexten, and S_OR.

Referenced by dial_exec_full(), and do_forward().

{
   const char *context = S_OR(chan->macrocontext, chan->context);
   const char *exten = S_OR(chan->macroexten, chan->exten);

   return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
}
static void handle_cause ( int  cause,
struct cause_args num 
) [static]
static void hanguptree ( struct chanlist outgoing,
struct ast_channel exception,
int  answered_elsewhere 
) [static]

Definition at line 576 of file app_dial.c.

References AST_CAUSE_ANSWERED_ELSEWHERE, AST_FLAG_ANSWERED_ELSEWHERE, ast_free, ast_hangup(), ast_set_flag, chanlist::chan, ast_channel::hangupcause, and chanlist::next.

Referenced by dial_exec_full().

{
   /* Hang up a tree of stuff */
   struct chanlist *oo;
   while (outgoing) {
      /* Hangup any existing lines we have open */
      if (outgoing->chan && (outgoing->chan != exception)) {
         if (answered_elsewhere) {
            /* The flag is used for local channel inheritance and stuff */
            ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
            /* This is for the channel drivers */
            outgoing->chan->hangupcause = AST_CAUSE_ANSWERED_ELSEWHERE;
         }
         ast_hangup(outgoing->chan);
      }
      oo = outgoing;
      outgoing = outgoing->next;
      ast_free(oo);
   }
}
static int load_module ( void  ) [static]

Definition at line 2489 of file app_dial.c.

References ast_add_extension2(), ast_context_find_or_create(), ast_free_ptr, ast_log(), ast_register_application_xml, ast_strdup, dial_exec(), LOG_ERROR, and retrydial_exec().

{
   int res;
   struct ast_context *con;

   con = ast_context_find_or_create(NULL, NULL, "app_dial_gosub_virtual_context", "app_dial");
   if (!con)
      ast_log(LOG_ERROR, "Dial virtual context 'app_dial_gosub_virtual_context' does not exist and unable to create\n");
   else
      ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_dial");

   res = ast_register_application_xml(app, dial_exec);
   res |= ast_register_application_xml(rapp, retrydial_exec);

   return res;
}
static int onedigit_goto ( struct ast_channel chan,
const char *  context,
char  exten,
int  pri 
) [static]

Definition at line 655 of file app_dial.c.

References ast_goto_if_exists(), ast_strlen_zero(), ast_channel::context, exten, and ast_channel::macrocontext.

Referenced by retrydial_exec(), and wait_for_answer().

{
   char rexten[2] = { exten, '\0' };

   if (context) {
      if (!ast_goto_if_exists(chan, context, rexten, pri))
         return 1;
   } else {
      if (!ast_goto_if_exists(chan, chan->context, rexten, pri))
         return 1;
      else if (!ast_strlen_zero(chan->macrocontext)) {
         if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri))
            return 1;
      }
   }
   return 0;
}
static void replace_macro_delimiter ( char *  s) [static]

Definition at line 1138 of file app_dial.c.

References s.

Referenced by dial_exec_full().

{
   for (; *s; s++)
      if (*s == '^')
         *s = ',';
}
static int retrydial_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 2365 of file app_dial.c.

References AST_APP_ARG, ast_channel_lock, ast_channel_unlock, AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, ast_fileexists(), AST_FLAG_MOH, ast_log(), ast_moh_start(), ast_moh_stop(), AST_PBX_INCOMPLETE, AST_STANDARD_APP_ARGS, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_test_flag64, ast_waitfordigit(), ast_waitstream(), context, ast_channel::data, dial_exec_full(), ast_channel::language, LOG_ERROR, LOG_WARNING, onedigit_goto(), OPT_DTMF_EXIT, parse(), and pbx_builtin_getvar_helper().

Referenced by load_module().

{
   char *parse;
   const char *context = NULL;
   int sleepms = 0, loops = 0, res = -1;
   struct ast_flags64 peerflags = { 0, };
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(announce);
      AST_APP_ARG(sleep);
      AST_APP_ARG(retries);
      AST_APP_ARG(dialdata);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "RetryDial requires an argument!\n");
      return -1;
   }

   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);

   if (!ast_strlen_zero(args.sleep) && (sleepms = atoi(args.sleep)))
      sleepms *= 1000;

   if (!ast_strlen_zero(args.retries)) {
      loops = atoi(args.retries);
   }

   if (!args.dialdata) {
      ast_log(LOG_ERROR, "%s requires a 4th argument (dialdata)\n", rapp);
      goto done;
   }

   if (sleepms < 1000)
      sleepms = 10000;

   if (!loops)
      loops = -1; /* run forever */

   ast_channel_lock(chan);
   context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT");
   context = !ast_strlen_zero(context) ? ast_strdupa(context) : NULL;
   ast_channel_unlock(chan);

   res = 0;
   while (loops) {
      int continue_exec;

      chan->data = "Retrying";
      if (ast_test_flag(chan, AST_FLAG_MOH))
         ast_moh_stop(chan);

      res = dial_exec_full(chan, args.dialdata, &peerflags, &continue_exec);
      if (continue_exec)
         break;

      if (res == 0) {
         if (ast_test_flag64(&peerflags, OPT_DTMF_EXIT)) {
            if (!ast_strlen_zero(args.announce)) {
               if (ast_fileexists(args.announce, NULL, chan->language) > 0) {
                  if (!(res = ast_streamfile(chan, args.announce, chan->language)))
                     ast_waitstream(chan, AST_DIGIT_ANY);
               } else
                  ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
            }
            if (!res && sleepms) {
               if (!ast_test_flag(chan, AST_FLAG_MOH))
                  ast_moh_start(chan, NULL, NULL);
               res = ast_waitfordigit(chan, sleepms);
            }
         } else {
            if (!ast_strlen_zero(args.announce)) {
               if (ast_fileexists(args.announce, NULL, chan->language) > 0) {
                  if (!(res = ast_streamfile(chan, args.announce, chan->language)))
                     res = ast_waitstream(chan, "");
               } else
                  ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", args.announce);
            }
            if (sleepms) {
               if (!ast_test_flag(chan, AST_FLAG_MOH))
                  ast_moh_start(chan, NULL, NULL);
               if (!res)
                  res = ast_waitfordigit(chan, sleepms);
            }
         }
      }

      if (res < 0 || res == AST_PBX_INCOMPLETE) {
         break;
      } else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
         if (onedigit_goto(chan, context, (char) res, 1)) {
            res = 0;
            break;
         }
      }
      loops--;
   }
   if (loops == 0)
      res = 0;
   else if (res == 1)
      res = 0;

   if (ast_test_flag(chan, AST_FLAG_MOH))
      ast_moh_stop(chan);
 done:
   return res;
}
static void senddialendevent ( const struct ast_channel src,
const char *  dialstatus 
) [static]

Definition at line 698 of file app_dial.c.

References EVENT_FLAG_CALL, manager_event, ast_channel::name, and ast_channel::uniqueid.

Referenced by dial_exec_full().

{
   manager_event(EVENT_FLAG_CALL, "Dial",
      "SubEvent: End\r\n"
      "Channel: %s\r\n"
      "UniqueID: %s\r\n"
      "DialStatus: %s\r\n",
      src->name, src->uniqueid, dialstatus);
}
static void senddialevent ( struct ast_channel src,
struct ast_channel dst,
const char *  dialstring 
) [static]

Definition at line 682 of file app_dial.c.

References ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, EVENT_FLAG_CALL, manager_event, ast_channel::name, S_OR, and ast_channel::uniqueid.

Referenced by dial_exec_full(), and do_forward().

{
   manager_event(EVENT_FLAG_CALL, "Dial",
      "SubEvent: Begin\r\n"
      "Channel: %s\r\n"
      "Destination: %s\r\n"
      "CallerIDNum: %s\r\n"
      "CallerIDName: %s\r\n"
      "UniqueID: %s\r\n"
      "DestUniqueID: %s\r\n"
      "Dialstring: %s\r\n",
      src->name, dst->name, S_OR(src->cid.cid_num, "<unknown>"),
      S_OR(src->cid.cid_name, "<unknown>"), src->uniqueid,
      dst->uniqueid, dialstring ? dialstring : "");
}
static int setup_privacy_args ( struct privacy_args pa,
struct ast_flags64 opts,
char *  opt_args[],
struct ast_channel chan 
) [static]

returns 1 if successful, 0 or <0 if the caller should 'goto out'

Definition at line 1399 of file app_dial.c.

References ast_answer(), ast_config_AST_DATA_DIR, ast_copy_string(), ast_dsp_get_threshold_from_settings(), ast_filedelete(), ast_fileexists(), ast_log(), ast_mkdir(), ast_play_and_record(), AST_PRIVACY_ALLOW, ast_privacy_check(), AST_PRIVACY_DENY, AST_PRIVACY_KILL, AST_PRIVACY_TORTURE, AST_PRIVACY_UNKNOWN, ast_shrink_phone_number(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag64, ast_verb, ast_waitstream(), ast_channel::cid, ast_callerid::cid_num, ast_channel::exten, ast_channel::language, LOG_NOTICE, LOG_WARNING, ast_channel::name, OPT_ARG_PRIVACY, OPT_PRIVACY, OPT_SCREEN_NOCLID, privacy_args::privcid, privacy_args::privdb_val, privacy_args::privintro, silencethreshold, privacy_args::status, and THRESHOLD_SILENCE.

Referenced by dial_exec_full().

{
   char callerid[60];
   int res;
   char *l;
   int silencethreshold;

   if (!ast_strlen_zero(chan->cid.cid_num)) {
      l = ast_strdupa(chan->cid.cid_num);
      ast_shrink_phone_number(l);
      if (ast_test_flag64(opts, OPT_PRIVACY) ) {
         ast_verb(3, "Privacy DB is '%s', clid is '%s'\n", opt_args[OPT_ARG_PRIVACY], l);
         pa->privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
      } else {
         ast_verb(3, "Privacy Screening, clid is '%s'\n", l);
         pa->privdb_val = AST_PRIVACY_UNKNOWN;
      }
   } else {
      char *tnam, *tn2;

      tnam = ast_strdupa(chan->name);
      /* clean the channel name so slashes don't try to end up in disk file name */
      for (tn2 = tnam; *tn2; tn2++) {
         if (*tn2 == '/')  /* any other chars to be afraid of? */
            *tn2 = '=';
      }
      ast_verb(3, "Privacy-- callerid is empty\n");

      snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam);
      l = callerid;
      pa->privdb_val = AST_PRIVACY_UNKNOWN;
   }

   ast_copy_string(pa->privcid, l, sizeof(pa->privcid));

   if (strncmp(pa->privcid, "NOCALLERID", 10) != 0 && ast_test_flag64(opts, OPT_SCREEN_NOCLID)) {
      /* if callerid is set and OPT_SCREEN_NOCLID is set also */
      ast_verb(3, "CallerID set (%s); N option set; Screening should be off\n", pa->privcid);
      pa->privdb_val = AST_PRIVACY_ALLOW;
   } else if (ast_test_flag64(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid, "NOCALLERID", 10) == 0) {
      ast_verb(3, "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val);
   }
   
   if (pa->privdb_val == AST_PRIVACY_DENY) {
      ast_verb(3, "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
      ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
      return 0;
   } else if (pa->privdb_val == AST_PRIVACY_KILL) {
      ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
      return 0; /* Is this right? */
   } else if (pa->privdb_val == AST_PRIVACY_TORTURE) {
      ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
      return 0; /* is this right??? */
   } else if (pa->privdb_val == AST_PRIVACY_UNKNOWN) {
      /* Get the user's intro, store it in priv-callerintros/$CID,
         unless it is already there-- this should be done before the
         call is actually dialed  */

      /* make sure the priv-callerintros dir actually exists */
      snprintf(pa->privintro, sizeof(pa->privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR);
      if ((res = ast_mkdir(pa->privintro, 0755))) {
         ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(res));
         return -1;
      }

      snprintf(pa->privintro, sizeof(pa->privintro), "priv-callerintros/%s", pa->privcid);
      if (ast_fileexists(pa->privintro, NULL, NULL ) > 0 && strncmp(pa->privcid, "NOCALLERID", 10) != 0) {
         /* the DELUX version of this code would allow this caller the
            option to hear and retape their previously recorded intro.
         */
      } else {
         int duration; /* for feedback from play_and_wait */
         /* the file doesn't exist yet. Let the caller submit his
            vocal intro for posterity */
         /* priv-recordintro script:

            "At the tone, please say your name:"

         */
         silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
         ast_answer(chan);
         res = ast_play_and_record(chan, "priv-recordintro", pa->privintro, 4, "sln", &duration, silencethreshold, 2000, 0);  /* NOTE: I've reduced the total time to 4 sec */
                           /* don't think we'll need a lock removed, we took care of
                              conflicts by naming the pa.privintro file */
         if (res == -1) {
            /* Delete the file regardless since they hung up during recording */
            ast_filedelete(pa->privintro, NULL);
            if (ast_fileexists(pa->privintro, NULL, NULL) > 0)
               ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", pa->privintro);
            else
               ast_verb(3, "Successfully deleted %s intro file\n", pa->privintro);
            return -1;
         }
         if (!ast_streamfile(chan, "vm-dialout", chan->language) )
            ast_waitstream(chan, "");
      }
   }
   return 1; /* success */
}
static int unload_module ( void  ) [static]

Definition at line 2473 of file app_dial.c.

References ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), and ast_unregister_application().

{
   int res;
   struct ast_context *con;

   res = ast_unregister_application(app);
   res |= ast_unregister_application(rapp);

   if ((con = ast_context_find("app_dial_gosub_virtual_context"))) {
      ast_context_remove_extension2(con, "s", 1, NULL, 0);
      ast_context_destroy(con, "app_dial"); /* leave nothing behind */
   }

   return res;
}
static int valid_priv_reply ( struct ast_flags64 opts,
int  res 
) [static]

Definition at line 1146 of file app_dial.c.

References ast_test_flag64, OPT_PRIVACY, and OPT_SCREENING.

{
   if (res < '1')
      return 0;
   if (ast_test_flag64(opts, OPT_PRIVACY) && res <= '5')
      return 1;
   if (ast_test_flag64(opts, OPT_SCREENING) && res <= '4')
      return 1;
   return 0;
}
static struct ast_channel* wait_for_answer ( struct ast_channel in,
struct chanlist outgoing,
int *  to,
struct ast_flags64 peerflags,
struct privacy_args pa,
const struct cause_args num_in,
int *  result 
) [static, read]

Definition at line 823 of file app_dial.c.

References ast_channel::_state, ast_cdr::answer, AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, AST_CAUSE_NORMAL_CLEARING, AST_CDR_ANSWERED, ast_cdr_failed(), ast_cdr_noanswer(), ast_channel_early_bridge(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendhtml(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag64, AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OFFHOOK, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, AST_CONTROL_RINGING, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_VIDUPDATE, ast_copy_flags64, ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, AST_FRAME_HTML, AST_FRAME_IMAGE, AST_FRAME_TEXT, AST_FRAME_VOICE, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_WATCHERS, ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), AST_STATE_UP, ast_string_field_set, ast_strlen_zero(), ast_test_flag64, ast_tvnow(), ast_verb, ast_waitfor_n(), ast_write(), cause_args::busy, ast_channel::call_forward, CAN_EARLY_BRIDGE, ast_channel::cdr, chanlist::chan, cause_args::congestion, context, ast_frame::data, ast_frame::datalen, DIAL_NOFORWARDHTML, DIAL_STILLGOING, dialcontext, ast_cdr::disposition, do_forward(), ast_channel::exten, f, ast_frame::frametype, handle_cause(), ast_channel::hangupcause, LOG_WARNING, ast_channel::name, chanlist::next, cause_args::nochan, onedigit_goto(), OPT_CALLEE_HANGUP, OPT_CALLEE_MIXMONITOR, OPT_CALLEE_MONITOR, OPT_CALLEE_PARK, OPT_CALLEE_TRANSFER, OPT_CALLER_HANGUP, OPT_CALLER_MIXMONITOR, OPT_CALLER_MONITOR, OPT_CALLER_PARK, OPT_CALLER_TRANSFER, OPT_DTMF_EXIT, OPT_MUSICBACK, OPT_RINGBACK, pbx_builtin_getvar_helper(), ast_frame::ptr, privacy_args::sentringing, privacy_args::status, ast_frame::subclass, and ast_frame::uint32.

Referenced by dial_exec_full().

{
   struct cause_args num = *num_in;
   int prestart = num.busy + num.congestion + num.nochan;
   int orig = *to;
   struct ast_channel *peer = NULL;
   /* single is set if only one destination is enabled */
   int single = outgoing && !outgoing->next && !ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
#ifdef HAVE_EPOLL
   struct chanlist *epollo;
#endif

   if (single) {
      /* Turn off hold music, etc */
      ast_deactivate_generator(in);
      /* If we are calling a single channel, make them compatible for in-band tone purpose */
      if (ast_channel_make_compatible(outgoing->chan, in) < 0) {
         /* If these channels can not be made compatible, 
          * there is no point in continuing.  The bridge
          * will just fail if it gets that far.
          */
         *to = -1;
         strcpy(pa->status, "CONGESTION");
         ast_cdr_failed(in->cdr);
         return NULL;
      }
   }

#ifdef HAVE_EPOLL
   for (epollo = outgoing; epollo; epollo = epollo->next)
      ast_poll_channel_add(in, epollo->chan);
#endif

   while (*to && !peer) {
      struct chanlist *o;
      int pos = 0; /* how many channels do we handle */
      int numlines = prestart;
      struct ast_channel *winner;
      struct ast_channel *watchers[AST_MAX_WATCHERS];

      watchers[pos++] = in;
      for (o = outgoing; o; o = o->next) {
         /* Keep track of important channels */
         if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan)
            watchers[pos++] = o->chan;
         numlines++;
      }
      if (pos == 1) { /* only the input channel is available */
         if (numlines == (num.busy + num.congestion + num.nochan)) {
            ast_verb(2, "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
            if (num.busy)
               strcpy(pa->status, "BUSY");
            else if (num.congestion)
               strcpy(pa->status, "CONGESTION");
            else if (num.nochan)
               strcpy(pa->status, "CHANUNAVAIL");
         } else {
            ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
         }
         *to = 0;
         return NULL;
      }
      winner = ast_waitfor_n(watchers, pos, to);
      for (o = outgoing; o; o = o->next) {
         struct ast_frame *f;
         struct ast_channel *c = o->chan;

         if (c == NULL)
            continue;
         if (ast_test_flag64(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) {
            if (!peer) {
               ast_verb(3, "%s answered %s\n", c->name, in->name);
               peer = c;
               ast_copy_flags64(peerflags, o,
                  OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
                  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_string_field_set(c, dialcontext, "");
               ast_copy_string(c->exten, "", sizeof(c->exten));
            }
            continue;
         }
         if (c != winner)
            continue;
         /* here, o->chan == c == winner */
         if (!ast_strlen_zero(c->call_forward)) {
            do_forward(o, &num, peerflags, single);
            continue;
         }
         f = ast_read(winner);
         if (!f) {
            in->hangupcause = c->hangupcause;
#ifdef HAVE_EPOLL
            ast_poll_channel_del(in, c);
#endif
            ast_hangup(c);
            c = o->chan = NULL;
            ast_clear_flag64(o, DIAL_STILLGOING);
            handle_cause(in->hangupcause, &num);
            continue;
         }
         if (f->frametype == AST_FRAME_CONTROL) {
            switch(f->subclass) {
            case AST_CONTROL_ANSWER:
               /* This is our guy if someone answered. */
               if (!peer) {
                  ast_verb(3, "%s answered %s\n", c->name, in->name);
                  peer = c;
                  if (peer->cdr) {
                     peer->cdr->answer = ast_tvnow();
                     peer->cdr->disposition = AST_CDR_ANSWERED;
                  }
                  ast_copy_flags64(peerflags, o,
                     OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
                     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_string_field_set(c, dialcontext, "");
                  ast_copy_string(c->exten, "", sizeof(c->exten));
                  if (CAN_EARLY_BRIDGE(peerflags, in, peer))
                     /* Setup early bridge if appropriate */
                     ast_channel_early_bridge(in, peer);
               }
               /* If call has been answered, then the eventual hangup is likely to be normal hangup */
               in->hangupcause = AST_CAUSE_NORMAL_CLEARING;
               c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
               break;
            case AST_CONTROL_BUSY:
               ast_verb(3, "%s is busy\n", c->name);
               in->hangupcause = c->hangupcause;
               ast_hangup(c);
               c = o->chan = NULL;
               ast_clear_flag64(o, DIAL_STILLGOING);
               handle_cause(AST_CAUSE_BUSY, &num);
               break;
            case AST_CONTROL_CONGESTION:
               ast_verb(3, "%s is circuit-busy\n", c->name);
               in->hangupcause = c->hangupcause;
               ast_hangup(c);
               c = o->chan = NULL;
               ast_clear_flag64(o, DIAL_STILLGOING);
               handle_cause(AST_CAUSE_CONGESTION, &num);
               break;
            case AST_CONTROL_RINGING:
               ast_verb(3, "%s is ringing\n", c->name);
               /* Setup early media if appropriate */
               if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
                  ast_channel_early_bridge(in, c);
               if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK)) {
                  ast_indicate(in, AST_CONTROL_RINGING);
                  pa->sentringing++;
               }
               break;
            case AST_CONTROL_PROGRESS:
               ast_verb(3, "%s is making progress passing it to %s\n", c->name, in->name);
               /* Setup early media if appropriate */
               if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
                  ast_channel_early_bridge(in, c);
               if (!ast_test_flag64(outgoing, OPT_RINGBACK))
                  if (single || (!single && !pa->sentringing)) {
                     ast_indicate(in, AST_CONTROL_PROGRESS);
                  }
               break;
            case AST_CONTROL_VIDUPDATE:
               ast_verb(3, "%s requested a video update, passing it to %s\n", c->name, in->name);
               ast_indicate(in, AST_CONTROL_VIDUPDATE);
               break;
            case AST_CONTROL_SRCUPDATE:
               ast_verb(3, "%s requested a source update, passing it to %s\n", c->name, in->name);
               ast_indicate(in, AST_CONTROL_SRCUPDATE);
               break;
            case AST_CONTROL_PROCEEDING:
               ast_verb(3, "%s is proceeding passing it to %s\n", c->name, in->name);
               if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
                  ast_channel_early_bridge(in, c);
               if (!ast_test_flag64(outgoing, OPT_RINGBACK))
                  ast_indicate(in, AST_CONTROL_PROCEEDING);
               break;
            case AST_CONTROL_HOLD:
               ast_verb(3, "Call on %s placed on hold\n", c->name);
               ast_indicate(in, AST_CONTROL_HOLD);
               break;
            case AST_CONTROL_UNHOLD:
               ast_verb(3, "Call on %s left from hold\n", c->name);
               ast_indicate(in, AST_CONTROL_UNHOLD);
               break;
            case AST_CONTROL_OFFHOOK:
            case AST_CONTROL_FLASH:
               /* Ignore going off hook and flash */
               break;
            case -1:
               if (!ast_test_flag64(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
                  ast_verb(3, "%s stopped sounds\n", c->name);
                  ast_indicate(in, -1);
                  pa->sentringing = 0;
               }
               break;
            default:
               ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
            }
         } else if (single) {
            switch (f->frametype) {
               case AST_FRAME_VOICE:
               case AST_FRAME_IMAGE:
               case AST_FRAME_TEXT:
                  if (ast_write(in, f)) {
                     ast_log(LOG_WARNING, "Unable to write frame\n");
                  }
                  break;
               case AST_FRAME_HTML:
                  if (!ast_test_flag64(outgoing, DIAL_NOFORWARDHTML) && ast_channel_sendhtml(in, f->subclass, f->data.ptr, f->datalen) == -1) {
                     ast_log(LOG_WARNING, "Unable to send URL\n");
                  }
                  break;
               default:
                  break;
            }
         }
         ast_frfree(f);
      } /* end for */
      if (winner == in) {
         struct ast_frame *f = ast_read(in);
#if 0
         if (f && (f->frametype != AST_FRAME_VOICE))
            printf("Frame type: %d, %d\n", f->frametype, f->subclass);
         else if (!f || (f->frametype != AST_FRAME_VOICE))
            printf("Hangup received on %s\n", in->name);
#endif
         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
            /* Got hung up */
            *to = -1;
            strcpy(pa->status, "CANCEL");
            ast_cdr_noanswer(in->cdr);
            if (f) {
               if (f->data.uint32) {
                  in->hangupcause = f->data.uint32;
               }
               ast_frfree(f);
            }
            return NULL;
         }

         /* now f is guaranteed non-NULL */
         if (f->frametype == AST_FRAME_DTMF) {
            if (ast_test_flag64(peerflags, OPT_DTMF_EXIT)) {
               const char *context;
               ast_channel_lock(in);
               context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
               if (onedigit_goto(in, context, (char) f->subclass, 1)) {
                  ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
                  *to = 0;
                  ast_cdr_noanswer(in->cdr);
                  *result = f->subclass;
                  strcpy(pa->status, "CANCEL");
                  ast_frfree(f);
                  ast_channel_unlock(in);
                  return NULL;
               }
               ast_channel_unlock(in);
            }

            if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
                  (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */
               ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
               *to = 0;
               strcpy(pa->status, "CANCEL");
               ast_cdr_noanswer(in->cdr);
               ast_frfree(f);
               return NULL;
            }
         }

         /* Forward HTML stuff */
         if (single && (f->frametype == AST_FRAME_HTML) && !ast_test_flag64(outgoing, DIAL_NOFORWARDHTML))
            if (ast_channel_sendhtml(outgoing->chan, f->subclass, f->data.ptr, f->datalen) == -1)
               ast_log(LOG_WARNING, "Unable to send URL\n");

         if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF_END)))  {
            if (ast_write(outgoing->chan, f))
               ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n");
         }
         if (single && (f->frametype == AST_FRAME_CONTROL) &&
            ((f->subclass == AST_CONTROL_HOLD) ||
            (f->subclass == AST_CONTROL_UNHOLD) ||
            (f->subclass == AST_CONTROL_VIDUPDATE) ||
             (f->subclass == AST_CONTROL_SRCUPDATE))) {
            ast_verb(3, "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name);
            ast_indicate_data(outgoing->chan, f->subclass, f->data.ptr, f->datalen);
         }
         ast_frfree(f);
      }
      if (!*to)
         ast_verb(3, "Nobody picked up in %d ms\n", orig);
      if (!*to || ast_check_hangup(in))
         ast_cdr_noanswer(in->cdr);
   }

#ifdef HAVE_EPOLL
   for (epollo = outgoing; epollo; epollo = epollo->next) {
      if (epollo->chan)
         ast_poll_channel_del(in, epollo->chan);
   }
#endif

   return peer;
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Dialing Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 2506 of file app_dial.c.

char* app = "Dial" [static]

Definition at line 465 of file app_dial.c.

Definition at line 2506 of file app_dial.c.

struct ast_app_option dial_exec_options[128] = { [ 'A' ] = { .flag = OPT_ANNOUNCE , .arg_index = OPT_ARG_ANNOUNCE + 1 }, [ 'C' ] = { .flag = OPT_RESETCDR }, [ 'c' ] = { .flag = ((uint64_t)1 << 33) }, [ 'd' ] = { .flag = OPT_DTMF_EXIT }, [ 'D' ] = { .flag = OPT_SENDDTMF , .arg_index = OPT_ARG_SENDDTMF + 1 }, [ 'e' ] = { .flag = ((uint64_t)1 << 34) }, [ 'f' ] = { .flag = OPT_FORCECLID }, [ 'F' ] = { .flag = ((uint64_t)1 << 35) , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ 'g' ] = { .flag = OPT_GO_ON }, [ 'G' ] = { .flag = OPT_GOTO , .arg_index = OPT_ARG_GOTO + 1 }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'i' ] = { .flag = OPT_IGNORE_FORWARDING }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'L' ] = { .flag = OPT_DURATION_LIMIT , .arg_index = OPT_ARG_DURATION_LIMIT + 1 }, [ 'm' ] = { .flag = OPT_MUSICBACK , .arg_index = OPT_ARG_MUSICBACK + 1 }, [ 'M' ] = { .flag = OPT_CALLEE_MACRO , .arg_index = OPT_ARG_CALLEE_MACRO + 1 }, [ 'n' ] = { .flag = OPT_SCREEN_NOINTRO , .arg_index = OPT_ARG_SCREEN_NOINTRO + 1 }, [ 'N' ] = { .flag = OPT_SCREEN_NOCLID }, [ 'o' ] = { .flag = OPT_ORIGINAL_CLID }, [ 'O' ] = { .flag = OPT_OPERMODE , .arg_index = OPT_ARG_OPERMODE + 1 }, [ 'p' ] = { .flag = OPT_SCREENING }, [ 'P' ] = { .flag = OPT_PRIVACY , .arg_index = OPT_ARG_PRIVACY + 1 }, [ 'r' ] = { .flag = OPT_RINGBACK }, [ 'S' ] = { .flag = OPT_DURATION_STOP , .arg_index = OPT_ARG_DURATION_STOP + 1 }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'U' ] = { .flag = OPT_CALLEE_GOSUB , .arg_index = OPT_ARG_CALLEE_GOSUB + 1 }, [ 'w' ] = { .flag = OPT_CALLEE_MONITOR }, [ 'W' ] = { .flag = OPT_CALLER_MONITOR }, [ 'x' ] = { .flag = OPT_CALLEE_MIXMONITOR }, [ 'X' ] = { .flag = OPT_CALLER_MIXMONITOR }, } [static]

Definition at line 558 of file app_dial.c.

Referenced by dial_exec_full().

char* rapp = "RetryDial" [static]

Definition at line 466 of file app_dial.c.