diff options
Diffstat (limited to '1.2-netsec/apps/app_dial.c')
-rw-r--r-- | 1.2-netsec/apps/app_dial.c | 1747 |
1 files changed, 0 insertions, 1747 deletions
diff --git a/1.2-netsec/apps/app_dial.c b/1.2-netsec/apps/app_dial.c deleted file mode 100644 index 4ef6cad83..000000000 --- a/1.2-netsec/apps/app_dial.c +++ /dev/null @@ -1,1747 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief dial() & retrydial() - Trivial application to dial a channel and send an URL on answer - * - * \ingroup applications - */ - -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <sys/time.h> -#include <sys/signal.h> -#include <netinet/in.h> - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/options.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/manager.h" -#include "asterisk/privacy.h" - -static char *tdesc = "Dialing Application"; - -static char *app = "Dial"; - -static char *synopsis = "Place a call and connect to the current channel"; - -static char *descrip = -" Dial(Technology/resource[&Tech2/resource2...][|timeout][|options][|URL]):\n" -"This applicaiton will place calls to one or more specified channels. As soon\n" -"as one of the requested channels answers, the originating channel will be\n" -"answered, if it has not already been answered. These two channels will then\n" -"be active in a bridged call. All other channels that were requested will then\n" -"be hung up.\n" -" Unless there is a timeout specified, the Dial application will wait\n" -"indefinitely until one of the called channels answers, the user hangs up, or\n" -"if all of the called channels are busy or unavailable. Dialplan executing will\n" -"continue if no requested channels can be called, or if the timeout expires.\n\n" -" This application sets the following channel variables upon completion:\n" -" DIALEDTIME - This is the time from dialing a channel until when it\n" -" is disconnected.\n" -" ANSWEREDTIME - This is the amount of time for actual call.\n" -" DIALSTATUS - This is the status of the call:\n" -" CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n" -" DONTCALL | TORTURE\n" -" For the Privacy and Screening Modes, the DIALSTATUS variable will be set to\n" -"DONTCALL if the called party chooses to send the calling party to the 'Go Away'\n" -"script. The DIALSTATUS variable will be set to TORTURE if the called party\n" -"wants to send the caller to the 'torture' script.\n" -" This application will report normal termination if the originating channel\n" -"hangs up, or if the call is bridged and either of the parties in the bridge\n" -"ends the call.\n" -" The optional URL will be sent to the called party if the channel supports it.\n" -" If the OUTBOUND_GROUP variable is set, all peer channels created by this\n" -"application will be put into that group (as in Set(GROUP()=...).\n\n" -" Options:\n" -" A(x) - Play an announcement to the called party, using 'x' as the file.\n" -" C - Reset the CDR for this call.\n" -" d - Allow the calling user to dial a 1 digit extension while waiting for\n" -" a call to be answered. Exit to that extension if it exists in the\n" -" current context, or the context defined in the EXITCONTEXT variable,\n" -" if it exists.\n" -" D([called][:calling]) - Send the specified DTMF strings *after* the called\n" -" party has answered, but before the call gets bridged. The 'called'\n" -" DTMF string is sent to the called party, and the 'calling' DTMF\n" -" string is sent to the calling party. Both parameters can be used\n" -" alone.\n" -" f - Force the callerid of the *calling* channel to be set as the\n" -" extension associated with the channel using a dialplan 'hint'.\n" -" For example, some PSTNs do not allow CallerID to be set to anything\n" -" other than the number assigned to the caller.\n" -" g - Proceed with dialplan execution at the current extension if the\n" -" destination channel hangs up.\n" -" G(context^exten^pri) - If the call is answered, transfer both parties to\n" -" the specified priority. Optionally, an extension, or extension and\n" -" context may be specified. Otherwise, the current extension is used.\n" -" h - Allow the called party to hang up by sending the '*' DTMF digit.\n" -" H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n" -" j - Jump to priority n+101 if all of the requested channels were busy.\n" -" L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n" -" left. Repeat the warning every 'z' ms. The following special\n" -" variables can be used with this option:\n" -" * LIMIT_PLAYAUDIO_CALLER yes|no (default yes)\n" -" Play sounds to the caller.\n" -" * LIMIT_PLAYAUDIO_CALLEE yes|no\n" -" Play sounds to the callee.\n" -" * LIMIT_TIMEOUT_FILE File to play when time is up.\n" -" * LIMIT_CONNECT_FILE File to play when call begins.\n" -" * LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n" -" The default is to say the time remaining.\n" -" m([class]) - Provide hold music to the calling party until a requested\n" -" channel answers. A specific MusicOnHold class can be\n" -" specified.\n" -" M(x[^arg]) - Execute the Macro for the *called* channel before connecting\n" -" to the calling channel. Arguments can be specified to the Macro\n" -" using '^' as a delimeter. The Macro can set the variable\n" -" MACRO_RESULT to specify the following actions after the Macro is\n" -" finished executing.\n" -" * ABORT Hangup both legs of the call.\n" -" * CONGESTION Behave as if line congestion was encountered.\n" -" * BUSY Behave as if a busy signal was encountered. This will also\n" -" have the application jump to priority n+101 if the\n" -" 'j' option is set.\n" -" * CONTINUE Hangup the called party and allow the calling party\n" -" to continue dialplan execution at the next priority.\n" -" * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n" -" specified priority. Optionally, an extension, or\n" -" extension and priority can be specified.\n" -" n - This option is a modifier for the screen/privacy mode. It specifies\n" -" that no introductions are to be saved in the priv-callerintros\n" -" directory.\n" -" N - This option is a modifier for the screen/privacy mode. It specifies\n" -" that if callerID is present, do not screen the call.\n" -" o - Specify that the CallerID that was present on the *calling* channel\n" -" be set as the CallerID on the *called* channel. This was the\n" -" behavior of Asterisk 1.0 and earlier.\n" -" p - This option enables screening mode. This is basically Privacy mode\n" -" without memory.\n" -" P([x]) - Enable privacy mode. Use 'x' as the family/key in the database if\n" -" it is provided. The current extension is used if a database\n" -" family/key is not specified.\n" -" r - Indicate ringing to the calling party. Pass no audio to the calling\n" -" party until the called channel has answered.\n" -" S(x) - Hang up the call after 'x' seconds *after* the called party has\n" -" answered the call.\n" -" t - Allow the called party to transfer the calling party by sending the\n" -" DTMF sequence defined in features.conf.\n" -" T - Allow the calling party to transfer the called party by sending the\n" -" DTMF sequence defined in features.conf.\n" -" w - Allow the called party to enable recording of the call by sending\n" -" the DTMF sequence defined for one-touch recording in features.conf.\n" -" W - Allow the calling party to enable recording of the call by sending\n" -" the DTMF sequence defined for one-touch recording in features.conf.\n"; - -/* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */ -static char *rapp = "RetryDial"; -static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension."; -static char *rdescrip = -" RetryDial(announce|sleep|retries|dialargs): This application will attempt to\n" -"place a call using the normal Dial application. If no channel can be reached,\n" -"the 'announce' file will be played. Then, it will wait 'sleep' number of\n" -"seconds before retying the call. After 'retires' number of attempts, the\n" -"calling channel will continue at the next priority in the dialplan. If the\n" -"'retries' setting is set to 0, this application will retry endlessly.\n" -" While waiting to retry a call, a 1 digit extension may be dialed. If that\n" -"extension exists in either the context defined in ${EXITCONTEXT} or the current\n" -"one, The call will jump to that extension immediately.\n" -" The 'dialargs' are specified in the same format that arguments are provided\n" -"to the Dial application.\n"; - -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_PRIORITY_JUMP = (1 << 8), - 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), -} dial_exec_option_flags; - -#define DIAL_STILLGOING (1 << 30) -#define DIAL_NOFORWARDHTML (1 << 31) - -enum { - OPT_ARG_ANNOUNCE = 0, - OPT_ARG_SENDDTMF, - OPT_ARG_GOTO, - OPT_ARG_DURATION_LIMIT, - OPT_ARG_MUSICBACK, - OPT_ARG_CALLEE_MACRO, - OPT_ARG_PRIVACY, - OPT_ARG_DURATION_STOP, - /* note: this entry _MUST_ be the last one in the enum */ - OPT_ARG_ARRAY_SIZE, -} dial_exec_option_args; - -AST_APP_OPTIONS(dial_exec_options, { - AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE), - AST_APP_OPTION('C', OPT_RESETCDR), - AST_APP_OPTION('d', OPT_DTMF_EXIT), - AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF), - AST_APP_OPTION('f', OPT_FORCECLID), - AST_APP_OPTION('g', OPT_GO_ON), - AST_APP_OPTION_ARG('G', OPT_GOTO, OPT_ARG_GOTO), - AST_APP_OPTION('h', OPT_CALLEE_HANGUP), - AST_APP_OPTION('H', OPT_CALLER_HANGUP), - AST_APP_OPTION('j', OPT_PRIORITY_JUMP), - AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT), - AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK), - AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO), - AST_APP_OPTION('n', OPT_SCREEN_NOINTRO), - AST_APP_OPTION('N', OPT_SCREEN_NOCLID), - AST_APP_OPTION('o', OPT_ORIGINAL_CLID), - AST_APP_OPTION('p', OPT_SCREENING), - AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY), - AST_APP_OPTION('r', OPT_RINGBACK), - AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP), - AST_APP_OPTION('t', OPT_CALLEE_TRANSFER), - AST_APP_OPTION('T', OPT_CALLER_TRANSFER), - AST_APP_OPTION('w', OPT_CALLEE_MONITOR), - AST_APP_OPTION('W', OPT_CALLER_MONITOR), -}); - -/* We define a custom "local user" structure because we - use it not only for keeping track of what is in use but - also for keeping track of who we're dialing. */ - -struct localuser { - struct ast_channel *chan; - unsigned int flags; - int forwards; - struct localuser *next; -}; - -LOCAL_USER_DECL; - -static void hanguptree(struct localuser *outgoing, struct ast_channel *exception) -{ - /* Hang up a tree of stuff */ - struct localuser *oo; - while (outgoing) { - /* Hangup any existing lines we have open */ - if (outgoing->chan && (outgoing->chan != exception)) - ast_hangup(outgoing->chan); - oo = outgoing; - outgoing=outgoing->next; - free(oo); - } -} - -#define AST_MAX_FORWARDS 8 - -#define AST_MAX_WATCHERS 256 - -#define HANDLE_CAUSE(cause, chan) do { \ - switch(cause) { \ - case AST_CAUSE_BUSY: \ - if (chan->cdr) \ - ast_cdr_busy(chan->cdr); \ - numbusy++; \ - break; \ - case AST_CAUSE_CONGESTION: \ - if (chan->cdr) \ - ast_cdr_failed(chan->cdr); \ - numcongestion++; \ - break; \ - case AST_CAUSE_UNREGISTERED: \ - if (chan->cdr) \ - ast_cdr_failed(chan->cdr); \ - numnochan++; \ - break; \ - default: \ - numnochan++; \ - break; \ - } \ -} while (0) - - -static int onedigit_goto(struct ast_channel *chan, char *context, char exten, int pri) -{ - 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 char *get_cid_name(char *name, int namelen, struct ast_channel *chan) -{ - char *context; - char *exten; - if (!ast_strlen_zero(chan->macrocontext)) - context = chan->macrocontext; - else - context = chan->context; - - if (!ast_strlen_zero(chan->macroexten)) - exten = chan->macroexten; - else - exten = chan->exten; - - if (ast_get_hint(NULL, 0, name, namelen, chan, context, exten)) - return name; - else - return ""; -} - -static void senddialevent(struct ast_channel *src, struct ast_channel *dst) -{ - manager_event(EVENT_FLAG_CALL, "Dial", - "Source: %s\r\n" - "Destination: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "SrcUniqueID: %s\r\n" - "DestUniqueID: %s\r\n", - src->name, dst->name, src->cid.cid_num ? src->cid.cid_num : "<unknown>", - src->cid.cid_name ? src->cid.cid_name : "<unknown>", src->uniqueid, - dst->uniqueid); -} - -static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, struct ast_flags *peerflags, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart, int priority_jump, int *result) -{ - struct localuser *o; - int found; - int numlines; - int numbusy = busystart; - int numcongestion = congestionstart; - int numnochan = nochanstart; - int prestart = busystart + congestionstart + nochanstart; - int cause; - int orig = *to; - struct ast_frame *f; - struct ast_channel *peer = NULL; - struct ast_channel *watchers[AST_MAX_WATCHERS]; - int pos; - int single; - struct ast_channel *winner; - char *context = NULL; - char cidname[AST_MAX_EXTENSION]; - - single = (outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK)); - - 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 */ - ast_channel_make_compatible(outgoing->chan, in); - } - - - while (*to && !peer) { - o = outgoing; - found = -1; - pos = 1; - numlines = prestart; - watchers[0] = in; - while (o) { - /* Keep track of important channels */ - if (ast_test_flag(o, DIAL_STILLGOING) && o->chan) { - watchers[pos++] = o->chan; - found = 1; - } - o = o->next; - numlines++; - } - if (found < 0) { - if (numlines == (numbusy + numcongestion + numnochan)) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan); - if (numbusy) - strcpy(status, "BUSY"); - else if (numcongestion) - strcpy(status, "CONGESTION"); - else if (numnochan) - strcpy(status, "CHANUNAVAIL"); - if (option_priority_jumping || priority_jump) - ast_goto_if_exists(in, in->context, in->exten, in->priority + 101); - } else { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan); - } - *to = 0; - return NULL; - } - winner = ast_waitfor_n(watchers, pos, to); - o = outgoing; - while (o) { - if (ast_test_flag(o, DIAL_STILLGOING) && o->chan && (o->chan->_state == AST_STATE_UP)) { - if (!peer) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); - peer = o->chan; - ast_copy_flags(peerflags, o, - OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | - OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | - OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | - DIAL_NOFORWARDHTML); - } - } else if (o->chan && (o->chan == winner)) { - if (!ast_strlen_zero(o->chan->call_forward)) { - char tmpchan[256]; - char *stuff; - char *tech; - ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); - if ((stuff = strchr(tmpchan, '/'))) { - *stuff = '\0'; - stuff++; - tech = tmpchan; - } else { - snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); - stuff = tmpchan; - tech = "Local"; - } - /* Before processing channel, go ahead and check for forwarding */ - o->forwards++; - if (o->forwards < AST_MAX_FORWARDS) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); - /* Setup parameters */ - o->chan = ast_request(tech, in->nativeformats, stuff, &cause); - if (!o->chan) - ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause); - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", o->chan->name); - cause = AST_CAUSE_CONGESTION; - o->chan = NULL; - } - if (!o->chan) { - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(cause, in); - } else { - if (o->chan->cid.cid_num) - free(o->chan->cid.cid_num); - o->chan->cid.cid_num = NULL; - if (o->chan->cid.cid_name) - free(o->chan->cid.cid_name); - o->chan->cid.cid_name = NULL; - - if (ast_test_flag(o, OPT_FORCECLID)) { - char *newcid = NULL; - - if (!ast_strlen_zero(in->macroexten)) - newcid = in->macroexten; - else - newcid = in->exten; - o->chan->cid.cid_num = strdup(newcid); - ast_copy_string(o->chan->accountcode, winner->accountcode, sizeof(o->chan->accountcode)); - o->chan->cdrflags = winner->cdrflags; - if (!o->chan->cid.cid_num) - ast_log(LOG_WARNING, "Out of memory\n"); - } else { - if (in->cid.cid_num) { - o->chan->cid.cid_num = strdup(in->cid.cid_num); - if (!o->chan->cid.cid_num) - ast_log(LOG_WARNING, "Out of memory\n"); - } - if (in->cid.cid_name) { - o->chan->cid.cid_name = strdup(in->cid.cid_name); - if (!o->chan->cid.cid_name) - ast_log(LOG_WARNING, "Out of memory\n"); - } - ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode)); - o->chan->cdrflags = in->cdrflags; - } - - if (in->cid.cid_ani) { - if (o->chan->cid.cid_ani) - free(o->chan->cid.cid_ani); - o->chan->cid.cid_ani = strdup(in->cid.cid_ani); - if (!o->chan->cid.cid_ani) - ast_log(LOG_WARNING, "Out of memory\n"); - } - if (o->chan->cid.cid_rdnis) - free(o->chan->cid.cid_rdnis); - if (!ast_strlen_zero(in->macroexten)) - o->chan->cid.cid_rdnis = strdup(in->macroexten); - else - o->chan->cid.cid_rdnis = strdup(in->exten); - if (ast_call(o->chan, tmpchan, 0)) { - ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); - ast_clear_flag(o, DIAL_STILLGOING); - ast_hangup(o->chan); - o->chan = NULL; - numnochan++; - } else { - senddialevent(in, o->chan); - /* After calling, set callerid to extension */ - if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID)) - ast_set_callerid(o->chan, ast_strlen_zero(in->macroexten) ? in->exten : in->macroexten, get_cid_name(cidname, sizeof(cidname), in), NULL); - } - } - /* Hangup the original channel now, in case we needed it */ - ast_hangup(winner); - continue; - } - f = ast_read(winner); - if (f) { - if (f->frametype == AST_FRAME_CONTROL) { - switch(f->subclass) { - case AST_CONTROL_ANSWER: - /* This is our guy if someone answered. */ - if (!peer) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); - peer = o->chan; - ast_copy_flags(peerflags, o, - OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | - OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | - OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | - DIAL_NOFORWARDHTML); - } - /* If call has been answered, then the eventual hangup is likely to be normal hangup */ - in->hangupcause = AST_CAUSE_NORMAL_CLEARING; - o->chan->hangupcause = AST_CAUSE_NORMAL_CLEARING; - break; - case AST_CONTROL_BUSY: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); - in->hangupcause = o->chan->hangupcause; - ast_hangup(o->chan); - o->chan = NULL; - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(AST_CAUSE_BUSY, in); - break; - case AST_CONTROL_CONGESTION: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name); - in->hangupcause = o->chan->hangupcause; - ast_hangup(o->chan); - o->chan = NULL; - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(AST_CAUSE_CONGESTION, in); - break; - case AST_CONTROL_RINGING: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); - if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) { - ast_indicate(in, AST_CONTROL_RINGING); - (*sentringing)++; - } - break; - case AST_CONTROL_PROGRESS: - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", o->chan->name,in->name); - if (!ast_test_flag(outgoing, OPT_RINGBACK)) - ast_indicate(in, AST_CONTROL_PROGRESS); - break; - case AST_CONTROL_VIDUPDATE: - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", o->chan->name,in->name); - ast_indicate(in, AST_CONTROL_VIDUPDATE); - break; - case AST_CONTROL_PROCEEDING: - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", o->chan->name,in->name); - if (!ast_test_flag(outgoing, OPT_RINGBACK)) - ast_indicate(in, AST_CONTROL_PROCEEDING); - break; - case AST_CONTROL_HOLD: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", o->chan->name); - ast_indicate(in, AST_CONTROL_HOLD); - break; - case AST_CONTROL_UNHOLD: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", o->chan->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_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", o->chan->name); - ast_indicate(in, -1); - (*sentringing) = 0; - } - break; - default: - ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); - } - } else if (single && (f->frametype == AST_FRAME_VOICE) && - !(ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK))) { - if (ast_write(in, f)) - ast_log(LOG_DEBUG, "Unable to forward frame\n"); - } else if (single && (f->frametype == AST_FRAME_IMAGE) && - !(ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK))) { - if (ast_write(in, f)) - ast_log(LOG_DEBUG, "Unable to forward image\n"); - } else if (single && (f->frametype == AST_FRAME_TEXT) && - !(ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK))) { - if (ast_write(in, f)) - ast_log(LOG_DEBUG, "Unable to text\n"); - } else if (single && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) - ast_channel_sendhtml(in, f->subclass, f->data, f->datalen); - - ast_frfree(f); - } else { - in->hangupcause = o->chan->hangupcause; - ast_hangup(o->chan); - o->chan = NULL; - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(in->hangupcause, in); - } - } - o = o->next; - } - if (winner == in) { - 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(status, "CANCEL"); - if (f) - ast_frfree(f); - return NULL; - } - - if (f && (f->frametype == AST_FRAME_DTMF)) { - if (ast_test_flag(peerflags, OPT_DTMF_EXIT)) { - context = pbx_builtin_getvar_helper(in, "EXITCONTEXT"); - if (onedigit_goto(in, context, (char) f->subclass, 1)) { - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); - *to=0; - *result = f->subclass; - strcpy(status, "CANCEL"); - ast_frfree(f); - return NULL; - } - } - - if (ast_test_flag(peerflags, OPT_CALLER_HANGUP) && - (f->subclass == '*')) { /* hmm it it not guarenteed to be '*' anymore. */ - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); - *to=0; - strcpy(status, "CANCEL"); - ast_frfree(f); - return NULL; - } - } - - /* Forward HTML stuff */ - if (single && f && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) - ast_channel_sendhtml(outgoing->chan, f->subclass, f->data, f->datalen); - - - if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF))) { - if (ast_write(outgoing->chan, f)) - ast_log(LOG_WARNING, "Unable to forward voice\n"); - } - if (single && (f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_VIDUPDATE)) { - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", in->name,outgoing->chan->name); - ast_indicate(outgoing->chan, AST_CONTROL_VIDUPDATE); - } - ast_frfree(f); - } - if (!*to && (option_verbose > 2)) - ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig); - } - - return peer; - -} - -static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags) -{ - int res=-1; - struct localuser *u; - char *tech, *number, *rest, *cur; - char privcid[256]; - char privintro[1024]; - struct localuser *outgoing=NULL, *tmp; - struct ast_channel *peer; - int to; - int numbusy = 0; - int numcongestion = 0; - int numnochan = 0; - int cause; - char numsubst[AST_MAX_EXTENSION]; - char restofit[AST_MAX_EXTENSION]; - char cidname[AST_MAX_EXTENSION]; - char toast[80]; - char *newnum; - char *l; - int privdb_val=0; - unsigned int calldurationlimit=0; - struct ast_bridge_config config; - long timelimit = 0; - long play_warning = 0; - long warning_freq=0; - char *warning_sound=NULL; - char *end_sound=NULL; - char *start_sound=NULL; - char *dtmfcalled=NULL, *dtmfcalling=NULL; - char *var; - char status[256]; - int play_to_caller=0,play_to_callee=0; - int sentringing=0, moh=0; - char *outbound_group = NULL; - char *macro_result = NULL, *macro_transfer_dest = NULL; - int digit = 0, result = 0; - time_t start_time, answer_time, end_time; - struct ast_app *app = NULL; - - char *parse; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(peers); - AST_APP_ARG(timeout); - AST_APP_ARG(options); - AST_APP_ARG(url); - ); - struct ast_flags opts = { 0, }; - char *opt_args[OPT_ARG_ARRAY_SIZE]; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n"); - return -1; - } - - LOCAL_USER_ADD(u); - - if (!(parse = ast_strdupa(data))) { - ast_log(LOG_WARNING, "Memory allocation failure\n"); - LOCAL_USER_REMOVE(u); - return -1; - } - - AST_STANDARD_APP_ARGS(args, parse); - - if (!ast_strlen_zero(args.options)) { - if (ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options)) { - LOCAL_USER_REMOVE(u); - return -1; - } - } - - if (ast_strlen_zero(args.peers)) { - ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n"); - LOCAL_USER_REMOVE(u); - return -1; - } - - if (ast_test_flag(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) { - calldurationlimit = atoi(opt_args[OPT_ARG_DURATION_STOP]); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n",calldurationlimit); - } - - if (ast_test_flag(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) { - parse = opt_args[OPT_ARG_SENDDTMF]; - dtmfcalled = strsep(&parse, ":"); - dtmfcalling = parse; - } - - if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) { - char *limit_str, *warning_str, *warnfreq_str; - - parse = opt_args[OPT_ARG_DURATION_LIMIT]; - limit_str = strsep(&parse, ":"); - warning_str = strsep(&parse, ":"); - warnfreq_str = parse; - - timelimit = atol(limit_str); - if (warning_str) - play_warning = atol(warning_str); - if (warnfreq_str) - warning_freq = atol(warnfreq_str); - - if (!timelimit) { - timelimit = play_to_caller = play_to_callee = play_warning = warning_freq = 0; - warning_sound = NULL; - } - - 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"); - warning_sound = var ? var : "timeleft"; - - var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE"); - end_sound = var ? var : NULL; - - var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE"); - start_sound = var ? var : NULL; - - /* undo effect of S(x) in case they are both used */ - calldurationlimit = 0; - /* more efficient do it like S(x) does since no advanced opts*/ - if (!play_warning && !start_sound && !end_sound && timelimit) { - calldurationlimit = timelimit/1000; - timelimit = play_to_caller = play_to_callee = play_warning = warning_freq = 0; - } else if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Limit Data for this call:\n"); - ast_verbose(VERBOSE_PREFIX_3 "- timelimit = %ld\n", timelimit); - ast_verbose(VERBOSE_PREFIX_3 "- play_warning = %ld\n", play_warning); - ast_verbose(VERBOSE_PREFIX_3 "- play_to_caller= %s\n", play_to_caller ? "yes" : "no"); - ast_verbose(VERBOSE_PREFIX_3 "- play_to_callee= %s\n", play_to_callee ? "yes" : "no"); - ast_verbose(VERBOSE_PREFIX_3 "- warning_freq = %ld\n", warning_freq); - ast_verbose(VERBOSE_PREFIX_3 "- start_sound = %s\n", start_sound ? start_sound : "UNDEF"); - ast_verbose(VERBOSE_PREFIX_3 "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF"); - ast_verbose(VERBOSE_PREFIX_3 "- end_sound = %s\n", end_sound ? end_sound : "UNDEF"); - } - } - - if (ast_test_flag(&opts, OPT_RESETCDR) && chan->cdr) - ast_cdr_reset(chan->cdr, NULL); - if (ast_test_flag(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY])) - opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten); - if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) { - char callerid[60]; - - l = chan->cid.cid_num; - if (!ast_strlen_zero(l)) { - ast_shrink_phone_number(l); - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Privacy DB is '%s', clid is '%s'\n", - opt_args[OPT_ARG_PRIVACY], l); - privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l); - } - else { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Privacy Screening, clid is '%s'\n", l); - 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=='/') - *tn2 = '='; /* any other chars to be afraid of? */ - } - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Privacy-- callerid is empty\n"); - - snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam); - l = callerid; - privdb_val = AST_PRIVACY_UNKNOWN; - } - - ast_copy_string(privcid,l,sizeof(privcid)); - - if( strncmp(privcid,"NOCALLERID",10) != 0 && ast_test_flag(&opts, OPT_SCREEN_NOCLID) ) { /* if callerid is set, and ast_test_flag(&opts, OPT_SCREEN_NOCLID) is set also */ - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "CallerID set (%s); N option set; Screening should be off\n", privcid); - privdb_val = AST_PRIVACY_ALLOW; - } - else if( ast_test_flag(&opts, OPT_SCREEN_NOCLID) && strncmp(privcid,"NOCALLERID",10) == 0 ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "CallerID blank; N option set; Screening should happen; dbval is %d\n", privdb_val); - } - - if( privdb_val == AST_PRIVACY_DENY ) { - ast_verbose( VERBOSE_PREFIX_3 "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n"); - res=0; - goto out; - } - else if( privdb_val == AST_PRIVACY_KILL ) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201); - res = 0; - goto out; /* Is this right? */ - } - else if( privdb_val == AST_PRIVACY_TORTURE ) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 301); - res = 0; - goto out; /* is this right??? */ - - } - else if( 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 exists? */ - - snprintf(privintro,sizeof(privintro),"priv-callerintros/%s", privcid); - if( ast_fileexists(privintro,NULL,NULL ) > 0 && strncmp(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:" - - */ - ast_play_and_record(chan, "priv-recordintro", privintro, 4, "gsm", &duration, 128, 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 privintro file */ - } - } - } - - /* If a channel group has been specified, get it for use when we create peer channels */ - outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"); - - ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP); - cur = args.peers; - do { - /* Remember where to start next time */ - rest = strchr(cur, '&'); - if (rest) { - *rest = 0; - rest++; - } - /* Get a technology/[device:]number pair */ - tech = cur; - number = strchr(tech, '/'); - if (!number) { - ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n"); - goto out; - } - *number = '\0'; - number++; - tmp = malloc(sizeof(struct localuser)); - if (!tmp) { - ast_log(LOG_WARNING, "Out of memory\n"); - goto out; - } - memset(tmp, 0, sizeof(struct localuser)); - if (opts.flags) { - ast_copy_flags(tmp, &opts, - OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | - OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | - OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | - OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID); - ast_set2_flag(tmp, args.url, DIAL_NOFORWARDHTML); - } - ast_copy_string(numsubst, number, sizeof(numsubst)); - /* If we're dialing by extension, look at the extension to know what to dial */ - if ((newnum = strstr(numsubst, "BYEXTENSION"))) { - /* strlen("BYEXTENSION") == 11 */ - ast_copy_string(restofit, newnum + 11, sizeof(restofit)); - snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", chan->exten,restofit); - if (option_debug) - ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst); - } - /* Request the peer */ - tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause); - if (!tmp->chan) { - /* If we can't, just go on to the next call */ - ast_log(LOG_NOTICE, "Unable to create channel of type '%s' (cause %d - %s)\n", tech, cause, ast_cause2str(cause)); - HANDLE_CAUSE(cause, chan); - cur = rest; - if (!cur) - chan->hangupcause = cause; - continue; - } - pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst); - if (!ast_strlen_zero(tmp->chan->call_forward)) { - char tmpchan[256]; - char *stuff; - char *tech; - ast_copy_string(tmpchan, tmp->chan->call_forward, sizeof(tmpchan)); - if ((stuff = strchr(tmpchan, '/'))) { - *stuff = '\0'; - stuff++; - tech = tmpchan; - } else { - snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tmp->chan->call_forward, tmp->chan->context); - stuff = tmpchan; - tech = "Local"; - } - tmp->forwards++; - if (tmp->forwards < AST_MAX_FORWARDS) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name); - ast_hangup(tmp->chan); - /* Setup parameters */ - tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause); - if (!tmp->chan) - ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause); - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", tmp->chan->name); - ast_hangup(tmp->chan); - tmp->chan = NULL; - cause = AST_CAUSE_CONGESTION; - } - if (!tmp->chan) { - HANDLE_CAUSE(cause, chan); - cur = rest; - continue; - } - } - - /* Inherit specially named variables from parent channel */ - ast_channel_inherit_variables(chan, tmp->chan); - - tmp->chan->appl = "AppDial"; - tmp->chan->data = "(Outgoing Line)"; - tmp->chan->whentohangup = 0; - if (tmp->chan->cid.cid_num) - free(tmp->chan->cid.cid_num); - tmp->chan->cid.cid_num = NULL; - if (tmp->chan->cid.cid_name) - free(tmp->chan->cid.cid_name); - tmp->chan->cid.cid_name = NULL; - if (tmp->chan->cid.cid_ani) - free(tmp->chan->cid.cid_ani); - tmp->chan->cid.cid_ani = NULL; - - if (chan->cid.cid_num) - tmp->chan->cid.cid_num = strdup(chan->cid.cid_num); - if (chan->cid.cid_name) - tmp->chan->cid.cid_name = strdup(chan->cid.cid_name); - if (chan->cid.cid_ani) - tmp->chan->cid.cid_ani = strdup(chan->cid.cid_ani); - - /* Copy language from incoming to outgoing */ - ast_copy_string(tmp->chan->language, chan->language, sizeof(tmp->chan->language)); - ast_copy_string(tmp->chan->accountcode, chan->accountcode, sizeof(tmp->chan->accountcode)); - tmp->chan->cdrflags = chan->cdrflags; - if (ast_strlen_zero(tmp->chan->musicclass)) - ast_copy_string(tmp->chan->musicclass, chan->musicclass, sizeof(tmp->chan->musicclass)); - if (chan->cid.cid_rdnis) - tmp->chan->cid.cid_rdnis = strdup(chan->cid.cid_rdnis); - /* Pass callingpres setting */ - tmp->chan->cid.cid_pres = chan->cid.cid_pres; - /* Pass type of number */ - tmp->chan->cid.cid_ton = chan->cid.cid_ton; - /* Pass type of tns */ - tmp->chan->cid.cid_tns = chan->cid.cid_tns; - /* Presense of ADSI CPE on outgoing channel follows ours */ - tmp->chan->adsicpe = chan->adsicpe; - /* Pass the transfer capability */ - tmp->chan->transfercapability = chan->transfercapability; - - /* If we have an outbound group, set this peer channel to it */ - if (outbound_group) - ast_app_group_set_channel(tmp->chan, outbound_group); - - /* Place the call, but don't wait on the answer */ - res = ast_call(tmp->chan, numsubst, 0); - - /* Save the info in cdr's that we called them */ - if (chan->cdr) - ast_cdr_setdestchan(chan->cdr, tmp->chan->name); - - /* check the results of ast_call */ - if (res) { - /* Again, keep going even if there's an error */ - if (option_debug) - ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); - else if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst); - ast_hangup(tmp->chan); - tmp->chan = NULL; - cur = rest; - continue; - } else { - senddialevent(chan, tmp->chan); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst); - if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID)) - ast_set_callerid(tmp->chan, ast_strlen_zero(chan->macroexten) ? chan->exten : chan->macroexten, 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_flag(tmp, DIAL_STILLGOING); - tmp->next = outgoing; - outgoing = tmp; - /* If this line is up, don't try anybody else */ - if (outgoing->chan->_state == AST_STATE_UP) - break; - cur = rest; - } while (cur); - - if (!ast_strlen_zero(args.timeout)) { - to = atoi(args.timeout); - if (to > 0) - to *= 1000; - else - ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout); - } else - to = -1; - - if (outgoing) { - /* Our status will at least be NOANSWER */ - strcpy(status, "NOANSWER"); - if (ast_test_flag(outgoing, OPT_MUSICBACK)) { - moh=1; - ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK]); - } else if (ast_test_flag(outgoing, OPT_RINGBACK)) { - ast_indicate(chan, AST_CONTROL_RINGING); - sentringing++; - } - } else - strcpy(status, "CHANUNAVAIL"); - - time(&start_time); - peer = wait_for_answer(chan, outgoing, &to, peerflags, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result); - - if (!peer) { - if (result) { - res = result; - } else if (to) - /* Musta gotten hung up */ - res = -1; - else - /* Nobody answered, next please? */ - res = 0; - - goto out; - } - if (peer) { - time(&answer_time); -#ifdef OSP_SUPPORT - /* Once call is answered, ditch the OSP Handle */ - pbx_builtin_setvar_helper(chan, "_OSPHANDLE", ""); -#endif - strcpy(status, "ANSWER"); - /* 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); - 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); - - number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER"); - if (!number) - number = numsubst; - pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number); - if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) { - ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", args.url); - ast_channel_sendurl( peer, args.url ); - } - if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) { - int res2; - int loopcount = 0; - if( 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 */ - - /* all ring indications and moh for the caller has been halted as soon as the - target extension was picked up. We are going to have to kill some - time and make the caller believe the peer hasn't picked up yet */ - - if (ast_test_flag(&opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) { - ast_indicate(chan, -1); - ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK]); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, AST_CONTROL_RINGING); - sentringing++; - } - - /* Start autoservice on the other chan ?? */ - res2 = ast_autoservice_start(chan); - /* Now Stream the File */ - if (!res2) { - do { - if (!res2) - res2 = ast_play_and_wait(peer,"priv-callpending"); - if( res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2>'5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4') ) /* uh, interrupting with a bad answer is ... ignorable! */ - res2 = 0; - - /* priv-callpending script: - "I have a caller waiting, who introduces themselves as:" - */ - if (!res2) - res2 = ast_play_and_wait(peer,privintro); - if( res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2>'5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4') ) /* uh, interrupting with a bad answer is ... ignorable! */ - res2 = 0; - /* now get input from the called party, as to their choice */ - if( !res2 ) { - if( ast_test_flag(&opts, OPT_PRIVACY) ) - res2 = ast_play_and_wait(peer,"priv-callee-options"); - if( ast_test_flag(&opts, OPT_SCREENING) ) - res2 = ast_play_and_wait(peer,"screen-callee-options"); - } - /* priv-callee-options script: - "Dial 1 if you wish this caller to reach you directly in the future, - and immediately connect to their incoming call - Dial 2 if you wish to send this caller to voicemail now and - forevermore. - Dial 3 to send this callerr to the torture menus, now and forevermore. - Dial 4 to send this caller to a simple "go away" menu, now and forevermore. - Dial 5 to allow this caller to come straight thru to you in the future, - but right now, just this once, send them to voicemail." - */ - - /* screen-callee-options script: - "Dial 1 if you wish to immediately connect to the incoming call - Dial 2 if you wish to send this caller to voicemail. - Dial 3 to send this callerr to the torture menus. - Dial 4 to send this caller to a simple "go away" menu. - */ - if( !res2 || res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2 > '5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4') ) { - /* invalid option */ - res2 = ast_play_and_wait(peer,"vm-sorry"); - } - loopcount++; /* give the callee a couple chances to make a choice */ - } while( (!res2 || res2 < '1' || (ast_test_flag(&opts, OPT_PRIVACY) && res2 > '5') || (ast_test_flag(&opts, OPT_SCREENING) && res2 > '4')) && loopcount < 2 ); - } - - switch(res2) { - case '1': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW); - } - break; - case '2': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to DENY\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_DENY); - } - if (ast_test_flag(&opts, OPT_MUSICBACK)) { - ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, -1); - sentringing=0; - } - res2 = ast_autoservice_stop(chan); - ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */ - res=0; - goto out; - case '3': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to TORTURE\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_TORTURE); - } - ast_copy_string(status, "TORTURE", sizeof(status)); - - res = 0; - if (ast_test_flag(&opts, OPT_MUSICBACK)) { - ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, -1); - sentringing=0; - } - res2 = ast_autoservice_stop(chan); - ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ - goto out; /* Is this right? */ - case '4': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to KILL\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_KILL); - } - - ast_copy_string(status, "DONTCALL", sizeof(status)); - res = 0; - if (ast_test_flag(&opts, OPT_MUSICBACK)) { - ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, -1); - sentringing=0; - } - res2 = ast_autoservice_stop(chan); - ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ - goto out; /* Is this right? */ - case '5': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW); - if (ast_test_flag(&opts, OPT_MUSICBACK)) { - ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, -1); - sentringing=0; - } - res2 = ast_autoservice_stop(chan); - ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ - res=0; - goto out; - } /* if not privacy, then 5 is the same as "default" case */ - default: - /* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do? */ - /* well, there seems basically two choices. Just patch the caller thru immediately, - or,... put 'em thru to voicemail. */ - /* since the callee may have hung up, let's do the voicemail thing, no database decision */ - if (option_verbose > 2) - ast_log(LOG_NOTICE,"privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n"); - if (ast_test_flag(&opts, OPT_MUSICBACK)) { - ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, -1); - sentringing=0; - } - res2 = ast_autoservice_stop(chan); - ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */ - res=0; - goto out; - } - if (ast_test_flag(&opts, OPT_MUSICBACK)) { - ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, -1); - sentringing=0; - } - res2 = ast_autoservice_stop(chan); - /* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll - just clog things up, and it's not useful information, not being tied to a CID */ - if( strncmp(privcid,"NOCALLERID",10) == 0 || ast_test_flag(&opts, OPT_SCREEN_NOINTRO) ) { - ast_filedelete(privintro, NULL); - if( ast_fileexists(privintro,NULL,NULL ) > 0 ) - ast_log(LOG_NOTICE,"privacy: ast_filedelete didn't do its job on %s\n", privintro); - else if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro); - } - } - } - if (ast_test_flag(&opts, OPT_ANNOUNCE) && !ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) { - /* Start autoservice on the other chan */ - res = ast_autoservice_start(chan); - /* Now Stream the File */ - if (!res) - res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language); - if (!res) { - digit = ast_waitstream(peer, AST_DIGIT_ANY); - } - /* Ok, done. stop autoservice */ - res = ast_autoservice_stop(chan); - if (digit > 0 && !res) - res = ast_senddigit(chan, digit); - else - res = digit; - - } else - res = 0; - - if (chan && peer && ast_test_flag(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) { - char *ch; - - for (ch = opt_args[OPT_ARG_GOTO]; *ch; ch++) { - if (*ch == '^') - *ch = '|'; - } - ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]); - ast_parseable_goto(peer, opt_args[OPT_ARG_GOTO]); - peer->priority++; - ast_pbx_start(peer); - hanguptree(outgoing, NULL); - LOCAL_USER_REMOVE(u); - return 0; - } - - if (ast_test_flag(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) { - char *ch; - - res = ast_autoservice_start(chan); - if (res) { - ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); - res = -1; - } - - app = pbx_findapp("Macro"); - - if (app && !res) { - for (ch = opt_args[OPT_ARG_CALLEE_MACRO]; *ch; ch++) { - if (*ch == '^') - *ch = '|'; - } - res = pbx_exec(peer, app, opt_args[OPT_ARG_CALLEE_MACRO], 1); - ast_log(LOG_DEBUG, "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) { - ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); - res = -1; - } - - if (!res) { - if ((macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) { - if (!strcasecmp(macro_result, "BUSY")) { - ast_copy_string(status, macro_result, sizeof(status)); - if (option_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) { - if (!ast_goto_if_exists(chan, NULL, NULL, chan->priority + 101)) { - ast_set_flag(peerflags, OPT_GO_ON); - } - } else - ast_set_flag(peerflags, OPT_GO_ON); - res = -1; - } - else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) { - ast_copy_string(status, macro_result, sizeof(status)); - ast_set_flag(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_flag(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*/ - /* no brainer mode... substitute ^ with | and feed it to builtin goto */ - for (res=0;res<strlen(macro_transfer_dest);res++) - if (macro_transfer_dest[res] == '^') - macro_transfer_dest[res] = '|'; - - if (!ast_parseable_goto(chan, macro_transfer_dest)) - ast_set_flag(peerflags, OPT_GO_ON); - - } - } - } - } - } - - if (!res) { - if (calldurationlimit > 0) { - time_t now; - - time(&now); - chan->whentohangup = now + calldurationlimit; - } - if (!ast_strlen_zero(dtmfcalled)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the called party.\n",dtmfcalled); - res = ast_dtmf_stream(peer,chan,dtmfcalled,250); - } - if (!ast_strlen_zero(dtmfcalling)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the calling party.\n",dtmfcalling); - res = ast_dtmf_stream(chan,peer,dtmfcalling,250); - } - } - - if (!res) { - memset(&config,0,sizeof(struct ast_bridge_config)); - 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); - if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER)) - ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER)) - ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP)) - ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLER_HANGUP)) - ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR)) - ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); - if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) - ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); - - config.timelimit = timelimit; - config.play_warning = play_warning; - config.warning_freq = warning_freq; - config.warning_sound = warning_sound; - config.end_sound = end_sound; - config.start_sound = start_sound; - 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 */ - ast_deactivate_generator(chan); - /* 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); - LOCAL_USER_REMOVE(u); - return -1; - } - res = ast_bridge_call(chan,peer,&config); - time(&end_time); - snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time)); - pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast); - - } else { - time(&end_time); - res = -1; - } - snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time)); - pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast); - - if (res != AST_PBX_NO_HANGUP_PEER) { - if (!chan->_softhangup) - 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); - } - hanguptree(outgoing, NULL); - pbx_builtin_setvar_helper(chan, "DIALSTATUS", status); - ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status); - - if ((ast_test_flag(peerflags, OPT_GO_ON)) && (!chan->_softhangup) && (res != AST_PBX_KEEPALIVE)) - res=0; - - LOCAL_USER_REMOVE(u); - - return res; -} - -static int dial_exec(struct ast_channel *chan, void *data) -{ - struct ast_flags peerflags; - memset(&peerflags, 0, sizeof(peerflags)); - return dial_exec_full(chan, data, &peerflags); -} - -static int retrydial_exec(struct ast_channel *chan, void *data) -{ - char *announce = NULL, *context = NULL, *dialdata = NULL; - int sleep = 0, loops = 0, res = 0; - struct localuser *u; - struct ast_flags peerflags; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "RetryDial requires an argument!\n"); - return -1; - } - - LOCAL_USER_ADD(u); - - announce = ast_strdupa(data); - if (!announce) { - ast_log(LOG_ERROR, "Out of memory!\n"); - LOCAL_USER_REMOVE(u); - return -1; - } - - memset(&peerflags, 0, sizeof(peerflags)); - - if ((dialdata = strchr(announce, '|'))) { - *dialdata = '\0'; - dialdata++; - if ((sleep = atoi(dialdata))) { - sleep *= 1000; - } else { - ast_log(LOG_ERROR, "%s requires the numerical argument <sleep>\n",rapp); - LOCAL_USER_REMOVE(u); - return -1; - } - if ((dialdata = strchr(dialdata, '|'))) { - *dialdata = '\0'; - dialdata++; - if (!(loops = atoi(dialdata))) { - ast_log(LOG_ERROR, "%s requires the numerical argument <loops>\n",rapp); - LOCAL_USER_REMOVE(u); - return -1; - } - } - } - - if ((dialdata = strchr(dialdata, '|'))) { - *dialdata = '\0'; - dialdata++; - } else { - ast_log(LOG_ERROR, "%s requires more arguments\n",rapp); - LOCAL_USER_REMOVE(u); - return -1; - } - - if (sleep < 1000) - sleep = 10000; - - if (!loops) - loops = -1; - - context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT"); - - while (loops) { - chan->data = "Retrying"; - if (ast_test_flag(chan, AST_FLAG_MOH)) - ast_moh_stop(chan); - - if ((res = dial_exec_full(chan, dialdata, &peerflags)) == 0) { - if (ast_test_flag(&peerflags, OPT_DTMF_EXIT)) { - if (!(res = ast_streamfile(chan, announce, chan->language))) - res = ast_waitstream(chan, AST_DIGIT_ANY); - if (!res && sleep) { - if (!ast_test_flag(chan, AST_FLAG_MOH)) - ast_moh_start(chan, NULL); - res = ast_waitfordigit(chan, sleep); - } - } else { - if (!(res = ast_streamfile(chan, announce, chan->language))) - res = ast_waitstream(chan, ""); - if (sleep) { - if (!ast_test_flag(chan, AST_FLAG_MOH)) - ast_moh_start(chan, NULL); - if (!res) - res = ast_waitfordigit(chan, sleep); - } - } - } - - if (res < 0) - 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 (ast_test_flag(chan, AST_FLAG_MOH)) - ast_moh_stop(chan); - - LOCAL_USER_REMOVE(u); - return loops ? res : 0; - -} - -int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - res |= ast_unregister_application(rapp); - - STANDARD_HANGUP_LOCALUSERS; - - return res; -} - -int load_module(void) -{ - int res; - - res = ast_register_application(app, dial_exec, synopsis, descrip); - res |= ast_register_application(rapp, retrydial_exec, rsynopsis, rdescrip); - - return res; -} - -char *description(void) -{ - return tdesc; -} - -int usecount(void) -{ - int res; - STANDARD_USECOUNT(res); - return res; -} - -char *key() -{ - return ASTERISK_GPL_KEY; -} |