Wed Oct 14 15:01:47 2009

Asterisk developer's documentation


app_dial.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief dial() & retrydial() - Trivial application to dial a channel and send an URL on answer
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  * 
00025  * \ingroup applications
00026  */
00027 
00028 /*** MODULEINFO
00029         <depend>chan_local</depend>
00030  ***/
00031 
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211596 $")
00036 
00037 #include <stdlib.h>
00038 #include <errno.h>
00039 #include <unistd.h>
00040 #include <string.h>
00041 #include <stdlib.h>
00042 #include <stdio.h>
00043 #include <sys/time.h>
00044 #include <sys/signal.h>
00045 #include <sys/stat.h>
00046 #include <netinet/in.h>
00047 
00048 #include "asterisk/lock.h"
00049 #include "asterisk/file.h"
00050 #include "asterisk/logger.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/options.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/translate.h"
00056 #include "asterisk/say.h"
00057 #include "asterisk/config.h"
00058 #include "asterisk/features.h"
00059 #include "asterisk/musiconhold.h"
00060 #include "asterisk/callerid.h"
00061 #include "asterisk/utils.h"
00062 #include "asterisk/app.h"
00063 #include "asterisk/causes.h"
00064 #include "asterisk/rtp.h"
00065 #include "asterisk/cdr.h"
00066 #include "asterisk/manager.h"
00067 #include "asterisk/privacy.h"
00068 #include "asterisk/stringfields.h"
00069 #include "asterisk/global_datastores.h"
00070 
00071 static char *app = "Dial";
00072 
00073 static char *synopsis = "Place a call and connect to the current channel";
00074 
00075 static char *descrip =
00076 "  Dial(Technology/resource[&Tech2/resource2...][|timeout][|options][|URL]):\n"
00077 "This application will place calls to one or more specified channels. As soon\n"
00078 "as one of the requested channels answers, the originating channel will be\n"
00079 "answered, if it has not already been answered. These two channels will then\n"
00080 "be active in a bridged call. All other channels that were requested will then\n"
00081 "be hung up.\n"
00082 "  Unless there is a timeout specified, the Dial application will wait\n"
00083 "indefinitely until one of the called channels answers, the user hangs up, or\n"
00084 "if all of the called channels are busy or unavailable. Dialplan executing will\n"
00085 "continue if no requested channels can be called, or if the timeout expires.\n\n"
00086 "  This application sets the following channel variables upon completion:\n"
00087 "    DIALEDTIME   - This is the time from dialing a channel until when it\n"
00088 "                   is disconnected.\n" 
00089 "    ANSWEREDTIME - This is the amount of time for actual call.\n"
00090 "    DIALSTATUS   - This is the status of the call:\n"
00091 "                   CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n" 
00092 "                   DONTCALL | TORTURE | INVALIDARGS\n"
00093 "  For the Privacy and Screening Modes, the DIALSTATUS variable will be set to\n"
00094 "DONTCALL if the called party chooses to send the calling party to the 'Go Away'\n"
00095 "script. The DIALSTATUS variable will be set to TORTURE if the called party\n"
00096 "wants to send the caller to the 'torture' script.\n"
00097 "  This application will report normal termination if the originating channel\n"
00098 "hangs up, or if the call is bridged and either of the parties in the bridge\n"
00099 "ends the call.\n"
00100 "  The optional URL will be sent to the called party if the channel supports it.\n"
00101 "  If the OUTBOUND_GROUP variable is set, all peer channels created by this\n"
00102 "application will be put into that group (as in Set(GROUP()=...).\n"
00103 "  If the OUTBOUND_GROUP_ONCE variable is set, all peer channels created by this\n"
00104 "application will be put into that group (as in Set(GROUP()=...). Unlike OUTBOUND_GROUP,\n"
00105 "however, the variable will be unset after use.\n\n"
00106 "  Options:\n"
00107 "    A(x) - Play an announcement to the called party, using 'x' as the file.\n"
00108 "    C    - Reset the CDR for this call.\n"
00109 "    d    - Allow the calling user to dial a 1 digit extension while waiting for\n"
00110 "           a call to be answered. Exit to that extension if it exists in the\n"
00111 "           current context, or the context defined in the EXITCONTEXT variable,\n"
00112 "           if it exists.\n"
00113 "    D([called][:calling]) - Send the specified DTMF strings *after* the called\n"
00114 "           party has answered, but before the call gets bridged. The 'called'\n"
00115 "           DTMF string is sent to the called party, and the 'calling' DTMF\n"
00116 "           string is sent to the calling party. Both parameters can be used\n"
00117 "           alone.\n"   
00118 "    f    - Force the callerid of the *calling* channel to be set as the\n"
00119 "           extension associated with the channel using a dialplan 'hint'.\n"
00120 "           For example, some PSTNs do not allow CallerID to be set to anything\n"
00121 "           other than the number assigned to the caller.\n"
00122 "    g    - Proceed with dialplan execution at the current extension if the\n"
00123 "           destination channel hangs up.\n"
00124 "    G(context^exten^pri) - If the call is answered, transfer the calling party to\n"
00125 "           the specified priority and the called party to the specified priority+1.\n"
00126 "           Optionally, an extension, or extension and context may be specified. \n"
00127 "           Otherwise, the current extension is used. You cannot use any additional\n"
00128 "           action post answer options in conjunction with this option.\n" 
00129 "    h    - Allow the called party to hang up by sending the '*' DTMF digit, or\n"
00130 "           whatever sequence was defined in the featuremap section for\n"
00131 "           'disconnect' in features.conf\n"
00132 "    H    - Allow the calling party to hang up by hitting the '*' DTMF digit, or\n"
00133 "           whatever sequence was defined in the featuremap section for\n"
00134 "           'disconnect' in features.conf\n"
00135 "    i    - Asterisk will ignore any forwarding requests it may receive on this\n"
00136 "           dial attempt.\n"
00137 "    j    - Jump to priority n+101 if all of the requested channels were busy.\n"
00138 "    k    - Allow the called party to enable parking of the call by sending\n"
00139 "           the DTMF sequence defined for call parking in the featuremap section of features.conf.\n"
00140 "    K    - Allow the calling party to enable parking of the call by sending\n"
00141 "           the DTMF sequence defined for call parking in the featuremap section of features.conf.\n"
00142 "    L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n"
00143 "           left. Repeat the warning every 'z' ms. The following special\n"
00144 "           variables can be used with this option:\n"
00145 "           * LIMIT_PLAYAUDIO_CALLER   yes|no (default yes)\n"
00146 "                                      Play sounds to the caller.\n"
00147 "           * LIMIT_PLAYAUDIO_CALLEE   yes|no\n"
00148 "                                      Play sounds to the callee.\n"
00149 "           * LIMIT_TIMEOUT_FILE       File to play when time is up.\n"
00150 "           * LIMIT_CONNECT_FILE       File to play when call begins.\n"
00151 "           * LIMIT_WARNING_FILE       File to play as warning if 'y' is defined.\n"
00152 "                                      The default is to say the time remaining.\n"
00153 "    m([class]) - Provide hold music to the calling party until a requested\n"
00154 "           channel answers. A specific MusicOnHold class can be\n"
00155 "           specified.\n"
00156 "    M(x[^arg]) - Execute the Macro for the *called* channel before connecting\n"
00157 "           to the calling channel. Arguments can be specified to the Macro\n"
00158 "           using '^' as a delimeter. The Macro can set the variable\n"
00159 "           MACRO_RESULT to specify the following actions after the Macro is\n" 
00160 "           finished executing.\n"
00161 "           * ABORT        Hangup both legs of the call.\n"
00162 "           * CONGESTION   Behave as if line congestion was encountered.\n"
00163 "           * BUSY         Behave as if a busy signal was encountered. This will also\n"
00164 "                          have the application jump to priority n+101 if the\n"
00165 "                          'j' option is set.\n"
00166 "           * CONTINUE     Hangup the called party and allow the calling party\n"
00167 "                          to continue dialplan execution at the next priority.\n"
00168 "           * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
00169 "                          specified priority. Optionally, an extension, or\n"
00170 "                          extension and priority can be specified.\n"
00171 "           You cannot use any additional action post answer options in conjunction\n"
00172 "           with this option. Also, pbx services are not run on the peer (called) channel,\n"
00173 "           so you will not be able to set timeouts via the TIMEOUT() function in this macro.\n"
00174 "    n    - This option is a modifier for the screen/privacy mode. It specifies\n"
00175 "           that no introductions are to be saved in the priv-callerintros\n"
00176 "           directory.\n"
00177 "    N    - This option is a modifier for the screen/privacy mode. It specifies\n"
00178 "           that if callerID is present, do not screen the call.\n"
00179 "    o    - Specify that the CallerID that was present on the *calling* channel\n"
00180 "           be set as the CallerID on the *called* channel. This was the\n"
00181 "           behavior of Asterisk 1.0 and earlier.\n"
00182 "    O([x]) - \"Operator Services\" mode (Zaptel channel to Zaptel channel\n"
00183 "             only, if specified on non-Zaptel interface, it will be ignored).\n"
00184 "             When the destination answers (presumably an operator services\n"
00185 "             station), the originator no longer has control of their line.\n"
00186 "             They may hang up, but the switch will not release their line\n"
00187 "             until the destination party hangs up (the operator). Specified\n"
00188 "             without an arg, or with 1 as an arg, the originator hanging up\n"
00189 "             will cause the phone to ring back immediately. With a 2 specified,\n"
00190 "             when the \"operator\" flashes the trunk, it will ring their phone\n"
00191 "             back.\n"
00192 "    p    - This option enables screening mode. This is basically Privacy mode\n"
00193 "           without memory.\n"
00194 "    P([x]) - Enable privacy mode. Use 'x' as the family/key in the database if\n"
00195 "           it is provided. The current extension is used if a database\n"
00196 "           family/key is not specified.\n"
00197 "    r    - Indicate ringing to the calling party. Pass no audio to the calling\n"
00198 "           party until the called channel has answered.\n"
00199 "    S(x) - Hang up the call after 'x' seconds *after* the called party has\n"
00200 "           answered the call.\n"   
00201 "    t    - Allow the called party to transfer the calling party by sending the\n"
00202 "           DTMF sequence defined in the blindxfer setting in the featuremap section\n"
00203 "           of features.conf.\n"
00204 "    T    - Allow the calling party to transfer the called party by sending the\n"
00205 "           DTMF sequence defined in the blindxfer setting in the featuremap section\n"
00206 "           of features.conf.\n"
00207 "    w    - Allow the called party to enable recording of the call by sending\n"
00208 "           the DTMF sequence defined in the automon setting in the featuremap section\n"
00209 "           of features.conf.\n"
00210 "    W    - Allow the calling party to enable recording of the call by sending\n"
00211 "           the DTMF sequence defined in the automon setting in the featuremap section\n"
00212 "           of features.conf.\n";
00213 
00214 /* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
00215 static char *rapp = "RetryDial";
00216 static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension.";
00217 static char *rdescrip =
00218 "  RetryDial(announce|sleep|retries|dialargs): This application will attempt to\n"
00219 "place a call using the normal Dial application. If no channel can be reached,\n"
00220 "the 'announce' file will be played. Then, it will wait 'sleep' number of\n"
00221 "seconds before retrying the call. After 'retries' number of attempts, the\n"
00222 "calling channel will continue at the next priority in the dialplan. If the\n"
00223 "'retries' setting is set to 0, this application will retry endlessly.\n"
00224 "  While waiting to retry a call, a 1 digit extension may be dialed. If that\n"
00225 "extension exists in either the context defined in ${EXITCONTEXT} or the current\n"
00226 "one, The call will jump to that extension immediately.\n"
00227 "  The 'dialargs' are specified in the same format that arguments are provided\n"
00228 "to the Dial application.\n";
00229 
00230 enum {
00231    OPT_ANNOUNCE =    (1 << 0),
00232    OPT_RESETCDR =    (1 << 1),
00233    OPT_DTMF_EXIT =      (1 << 2),
00234    OPT_SENDDTMF =    (1 << 3),
00235    OPT_FORCECLID =      (1 << 4),
00236    OPT_GO_ON =    (1 << 5),
00237    OPT_CALLEE_HANGUP =  (1 << 6),
00238    OPT_CALLER_HANGUP =  (1 << 7),
00239    OPT_PRIORITY_JUMP =  (1 << 8),
00240    OPT_DURATION_LIMIT = (1 << 9),
00241    OPT_MUSICBACK =      (1 << 10),
00242    OPT_CALLEE_MACRO =   (1 << 11),
00243    OPT_SCREEN_NOINTRO = (1 << 12),
00244    OPT_SCREEN_NOCLID =  (1 << 13),
00245    OPT_ORIGINAL_CLID =  (1 << 14),
00246    OPT_SCREENING =      (1 << 15),
00247    OPT_PRIVACY =     (1 << 16),
00248    OPT_RINGBACK =    (1 << 17),
00249    OPT_DURATION_STOP =  (1 << 18),
00250    OPT_CALLEE_TRANSFER =   (1 << 19),
00251    OPT_CALLER_TRANSFER =   (1 << 20),
00252    OPT_CALLEE_MONITOR = (1 << 21),
00253    OPT_CALLER_MONITOR = (1 << 22),
00254    OPT_GOTO =     (1 << 23),
00255    OPT_OPERMODE =       (1 << 24),
00256    OPT_CALLEE_PARK = (1 << 25),
00257    OPT_CALLER_PARK = (1 << 26),
00258    OPT_IGNORE_FORWARDING = (1 << 27),
00259 } dial_exec_option_flags;
00260 
00261 #define DIAL_STILLGOING       (1 << 30)
00262 #define DIAL_NOFORWARDHTML    (1 << 31)
00263 
00264 enum {
00265    OPT_ARG_ANNOUNCE = 0,
00266    OPT_ARG_SENDDTMF,
00267    OPT_ARG_GOTO,
00268    OPT_ARG_DURATION_LIMIT,
00269    OPT_ARG_MUSICBACK,
00270    OPT_ARG_CALLEE_MACRO,
00271    OPT_ARG_PRIVACY,
00272    OPT_ARG_DURATION_STOP,
00273    OPT_ARG_OPERMODE,
00274    /* note: this entry _MUST_ be the last one in the enum */
00275    OPT_ARG_ARRAY_SIZE,
00276 } dial_exec_option_args;
00277 
00278 AST_APP_OPTIONS(dial_exec_options, {
00279    AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
00280    AST_APP_OPTION('C', OPT_RESETCDR),
00281    AST_APP_OPTION('d', OPT_DTMF_EXIT),
00282    AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
00283    AST_APP_OPTION('f', OPT_FORCECLID),
00284    AST_APP_OPTION('g', OPT_GO_ON),
00285    AST_APP_OPTION_ARG('G', OPT_GOTO, OPT_ARG_GOTO),
00286    AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
00287    AST_APP_OPTION('H', OPT_CALLER_HANGUP),
00288    AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
00289    AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
00290    AST_APP_OPTION('k', OPT_CALLEE_PARK),
00291    AST_APP_OPTION('K', OPT_CALLER_PARK),
00292    AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
00293    AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK),
00294    AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO),
00295    AST_APP_OPTION('n', OPT_SCREEN_NOINTRO),
00296    AST_APP_OPTION('N', OPT_SCREEN_NOCLID),
00297    AST_APP_OPTION('o', OPT_ORIGINAL_CLID),
00298    AST_APP_OPTION_ARG('O', OPT_OPERMODE,OPT_ARG_OPERMODE),
00299    AST_APP_OPTION('p', OPT_SCREENING),
00300    AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
00301    AST_APP_OPTION('r', OPT_RINGBACK),
00302    AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
00303    AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
00304    AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
00305    AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
00306    AST_APP_OPTION('W', OPT_CALLER_MONITOR),
00307 });
00308 
00309 #define CAN_EARLY_BRIDGE(flags,chan,peer) (!ast_test_flag(flags, OPT_CALLEE_HANGUP | \
00310    OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
00311    OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK) && \
00312    !chan->audiohooks && !peer->audiohooks)
00313 
00314 /* We define a custom "local user" structure because we
00315    use it not only for keeping track of what is in use but
00316    also for keeping track of who we're dialing. */
00317 
00318 struct dial_localuser {
00319    struct ast_channel *chan;
00320    unsigned int flags;
00321    struct dial_localuser *next;
00322 };
00323 
00324 
00325 static void hanguptree(struct dial_localuser *outgoing, struct ast_channel *exception)
00326 {
00327    /* Hang up a tree of stuff */
00328    struct dial_localuser *oo;
00329    while (outgoing) {
00330       /* Hangup any existing lines we have open */
00331       if (outgoing->chan && (outgoing->chan != exception))
00332          ast_hangup(outgoing->chan);
00333       oo = outgoing;
00334       outgoing=outgoing->next;
00335       free(oo);
00336    }
00337 }
00338 
00339 #define AST_MAX_WATCHERS 256
00340 
00341 #define HANDLE_CAUSE(cause, chan) do { \
00342    switch(cause) { \
00343    case AST_CAUSE_BUSY: \
00344       if (chan->cdr) \
00345          ast_cdr_busy(chan->cdr); \
00346       numbusy++; \
00347       break; \
00348    case AST_CAUSE_CONGESTION: \
00349       if (chan->cdr) \
00350          ast_cdr_failed(chan->cdr); \
00351       numcongestion++; \
00352       break; \
00353    case AST_CAUSE_NO_ROUTE_DESTINATION: \
00354    case AST_CAUSE_UNREGISTERED: \
00355       if (chan->cdr) \
00356          ast_cdr_failed(chan->cdr); \
00357       numnochan++; \
00358       break; \
00359    case AST_CAUSE_NO_ANSWER: \
00360       if (chan->cdr) \
00361          ast_cdr_noanswer(chan->cdr); \
00362       break; \
00363    case AST_CAUSE_NORMAL_CLEARING: \
00364       break; \
00365    default: \
00366       numnochan++; \
00367       break; \
00368    } \
00369 } while (0)
00370 
00371 
00372 static int onedigit_goto(struct ast_channel *chan, const char *context, char exten, int pri) 
00373 {
00374    char rexten[2] = { exten, '\0' };
00375 
00376    if (context) {
00377       if (!ast_goto_if_exists(chan, context, rexten, pri))
00378          return 1;
00379    } else {
00380       if (!ast_goto_if_exists(chan, chan->context, rexten, pri))
00381          return 1;
00382       else if (!ast_strlen_zero(chan->macrocontext)) {
00383          if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri))
00384             return 1;
00385       }
00386    }
00387    return 0;
00388 }
00389 
00390 static int detect_disconnect(struct ast_channel *chan, char code, char *featurecode, int len);
00391 
00392 static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan)
00393 {
00394    const char *context = S_OR(chan->macrocontext, chan->context);
00395    const char *exten = S_OR(chan->macroexten, chan->exten);
00396 
00397    return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
00398 }
00399 
00400 static void senddialevent(struct ast_channel *src, struct ast_channel *dst)
00401 {
00402    /* XXX do we need also CallerIDnum ? */
00403    manager_event(EVENT_FLAG_CALL, "Dial", 
00404             "Source: %s\r\n"
00405             "Destination: %s\r\n"
00406             "CallerID: %s\r\n"
00407             "CallerIDName: %s\r\n"
00408             "SrcUniqueID: %s\r\n"
00409             "DestUniqueID: %s\r\n",
00410             src->name, dst->name, S_OR(src->cid.cid_num, "<unknown>"),
00411             S_OR(src->cid.cid_name, "<unknown>"), src->uniqueid,
00412             dst->uniqueid);
00413 }
00414 
00415 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_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)
00416 {
00417    int numbusy = busystart;
00418    int numcongestion = congestionstart;
00419    int numnochan = nochanstart;
00420    int prestart = busystart + congestionstart + nochanstart;
00421    int orig = *to;
00422    struct ast_channel *peer = NULL;
00423    /* single is set if only one destination is enabled */
00424    int single = outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
00425 
00426    char featurecode[FEATURE_MAX_LEN + 1] = { 0, };
00427 
00428    if (single) {
00429       /* Turn off hold music, etc */
00430       ast_deactivate_generator(in);
00431       /* If we are calling a single channel, make them compatible for in-band tone purpose */
00432       ast_channel_make_compatible(outgoing->chan, in);
00433    }
00434    
00435    
00436    while (*to && !peer) {
00437       struct dial_localuser *o;
00438       int pos = 0;   /* how many channels do we handle */
00439       int numlines = prestart;
00440       struct ast_channel *winner;
00441       struct ast_channel *watchers[AST_MAX_WATCHERS];
00442 
00443       watchers[pos++] = in;
00444       for (o = outgoing; o; o = o->next) {
00445          /* Keep track of important channels */
00446          if (ast_test_flag(o, DIAL_STILLGOING) && o->chan)
00447             watchers[pos++] = o->chan;
00448          numlines++;
00449       }
00450       if (pos == 1) {   /* only the input channel is available */
00451          if (numlines == (numbusy + numcongestion + numnochan)) {
00452             if (option_verbose > 2)
00453                ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
00454             if (numbusy)
00455                strcpy(status, "BUSY"); 
00456             else if (numcongestion)
00457                strcpy(status, "CONGESTION");
00458             else if (numnochan)
00459                strcpy(status, "CHANUNAVAIL");
00460             if (ast_opt_priority_jumping || priority_jump)
00461                ast_goto_if_exists(in, in->context, in->exten, in->priority + 101);
00462          } else {
00463             if (option_verbose > 2)
00464                ast_verbose(VERBOSE_PREFIX_3 "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
00465          }
00466          *to = 0;
00467          return NULL;
00468       }
00469       winner = ast_waitfor_n(watchers, pos, to);
00470       for (o = outgoing; o; o = o->next) {
00471          struct ast_frame *f;
00472          struct ast_channel *c = o->chan;
00473 
00474          if (c == NULL)
00475             continue;
00476          if (ast_test_flag(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) {
00477             if (!peer) {
00478                if (option_verbose > 2)
00479                   ast_verbose(VERBOSE_PREFIX_3 "%s answered %s\n", c->name, in->name);
00480                peer = c;
00481                ast_copy_flags(peerflags, o,
00482                          OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
00483                          OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
00484                          OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
00485                          OPT_CALLEE_PARK | OPT_CALLER_PARK |
00486                          DIAL_NOFORWARDHTML);
00487                ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
00488                ast_copy_string(c->exten, "", sizeof(c->exten));
00489             }
00490             continue;
00491          }
00492          if (c != winner)
00493             continue;
00494          if (!ast_strlen_zero(c->call_forward)) {
00495             char tmpchan[256];
00496             char *stuff;
00497             char *tech;
00498             int cause;
00499 
00500             ast_copy_string(tmpchan, c->call_forward, sizeof(tmpchan));
00501             if ((stuff = strchr(tmpchan, '/'))) {
00502                *stuff++ = '\0';
00503                tech = tmpchan;
00504             } else {
00505                const char *forward_context = pbx_builtin_getvar_helper(c, "FORWARD_CONTEXT");
00506                if (ast_strlen_zero(forward_context)) {
00507                   forward_context = NULL;
00508                }
00509                snprintf(tmpchan, sizeof(tmpchan), "%s@%s", c->call_forward, forward_context ? forward_context : c->context);
00510                stuff = tmpchan;
00511                tech = "Local";
00512             }
00513             /* Before processing channel, go ahead and check for forwarding */
00514             if (option_verbose > 2)
00515                ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name);
00516             /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
00517             if (ast_test_flag(peerflags, OPT_IGNORE_FORWARDING)) {
00518                if (option_verbose > 2)
00519                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff);
00520                c = o->chan = NULL;
00521                cause = AST_CAUSE_BUSY;
00522             } else {
00523                /* Setup parameters */
00524                if ((c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause))) {
00525                   if (single)
00526                      ast_channel_make_compatible(o->chan, in);
00527                   ast_channel_inherit_variables(in, o->chan);
00528                   ast_channel_datastore_inherit(in, o->chan);
00529                } else
00530                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
00531             }
00532             if (!c) {
00533                ast_clear_flag(o, DIAL_STILLGOING); 
00534                HANDLE_CAUSE(cause, in);
00535             } else {
00536                ast_rtp_make_compatible(c, in, single);
00537                if (c->cid.cid_num)
00538                   free(c->cid.cid_num);
00539                c->cid.cid_num = NULL;
00540                if (c->cid.cid_name)
00541                   free(c->cid.cid_name);
00542                c->cid.cid_name = NULL;
00543 
00544                if (ast_test_flag(o, OPT_FORCECLID)) {
00545                   c->cid.cid_num = ast_strdup(S_OR(in->macroexten, in->exten));
00546                   ast_string_field_set(c, accountcode, winner->accountcode);
00547                   c->cdrflags = winner->cdrflags;
00548                } else {
00549                   c->cid.cid_num = ast_strdup(in->cid.cid_num);
00550                   c->cid.cid_name = ast_strdup(in->cid.cid_name);
00551                   ast_string_field_set(c, accountcode, in->accountcode);
00552                   c->cdrflags = in->cdrflags;
00553                }
00554 
00555                if (in->cid.cid_ani) {
00556                   if (c->cid.cid_ani)
00557                      free(c->cid.cid_ani);
00558                   c->cid.cid_ani = ast_strdup(in->cid.cid_ani);
00559                }
00560                if (c->cid.cid_rdnis) 
00561                   free(c->cid.cid_rdnis);
00562                c->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
00563                if (ast_call(c, tmpchan, 0)) {
00564                   ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
00565                   ast_clear_flag(o, DIAL_STILLGOING); 
00566                   ast_hangup(c);
00567                   c = o->chan = NULL;
00568                   numnochan++;
00569                } else {
00570                   senddialevent(in, c);
00571                   /* After calling, set callerid to extension */
00572                   if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID)) {
00573                      char cidname[AST_MAX_EXTENSION] = "";
00574                      ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL);
00575                   }
00576                }
00577                if (single) {
00578                   ast_indicate(in, -1);
00579                }
00580             }
00581             /* Hangup the original channel now, in case we needed it */
00582             ast_hangup(winner);
00583             continue;
00584          }
00585          f = ast_read(winner);
00586          if (!f) {
00587             in->hangupcause = c->hangupcause;
00588             ast_hangup(c);
00589             c = o->chan = NULL;
00590             ast_clear_flag(o, DIAL_STILLGOING);
00591             HANDLE_CAUSE(in->hangupcause, in);
00592             continue;
00593          }
00594          if (f->frametype == AST_FRAME_CONTROL) {
00595             switch(f->subclass) {
00596             case AST_CONTROL_ANSWER:
00597                /* This is our guy if someone answered. */
00598                if (!peer) {
00599                   if (option_verbose > 2)
00600                      ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", c->name, in->name);
00601                   peer = c;
00602                   if (peer->cdr) {
00603                      peer->cdr->answer = ast_tvnow();
00604                      peer->cdr->disposition = AST_CDR_ANSWERED;
00605                   }
00606                   ast_copy_flags(peerflags, o,
00607                             OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
00608                             OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
00609                             OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
00610                             OPT_CALLEE_PARK | OPT_CALLER_PARK |
00611                             DIAL_NOFORWARDHTML);
00612                   ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
00613                   ast_copy_string(c->exten, "", sizeof(c->exten));
00614                   /* Setup RTP early bridge if appropriate */
00615                   if (CAN_EARLY_BRIDGE(peerflags, in, peer))
00616                      ast_rtp_early_bridge(in, peer);
00617                }
00618                /* If call has been answered, then the eventual hangup is likely to be normal hangup */
00619                in->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00620                c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00621                break;
00622             case AST_CONTROL_BUSY:
00623                if (option_verbose > 2)
00624                   ast_verbose(VERBOSE_PREFIX_3 "%s is busy\n", c->name);
00625                in->hangupcause = c->hangupcause;
00626                ast_hangup(c);
00627                c = o->chan = NULL;
00628                ast_clear_flag(o, DIAL_STILLGOING); 
00629                HANDLE_CAUSE(AST_CAUSE_BUSY, in);
00630                break;
00631             case AST_CONTROL_CONGESTION:
00632                if (option_verbose > 2)
00633                   ast_verbose(VERBOSE_PREFIX_3 "%s is circuit-busy\n", c->name);
00634                in->hangupcause = c->hangupcause;
00635                ast_hangup(c);
00636                c = o->chan = NULL;
00637                ast_clear_flag(o, DIAL_STILLGOING);
00638                HANDLE_CAUSE(AST_CAUSE_CONGESTION, in);
00639                break;
00640             case AST_CONTROL_RINGING:
00641                if (option_verbose > 2)
00642                   ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", c->name);
00643                /* Setup early media if appropriate */
00644                if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
00645                   ast_rtp_early_bridge(in, c);
00646                if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) {
00647                   ast_indicate(in, AST_CONTROL_RINGING);
00648                   (*sentringing)++;
00649                }
00650                break;
00651             case AST_CONTROL_PROGRESS:
00652                if (option_verbose > 2)
00653                   ast_verbose (VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", c->name, in->name);
00654                /* Setup early media if appropriate */
00655                if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
00656                   ast_rtp_early_bridge(in, c);
00657                if (!ast_test_flag(outgoing, OPT_RINGBACK))
00658                   ast_indicate(in, AST_CONTROL_PROGRESS);
00659                break;
00660             case AST_CONTROL_VIDUPDATE:
00661                if (option_verbose > 2)
00662                   ast_verbose (VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", c->name, in->name);
00663                ast_indicate(in, AST_CONTROL_VIDUPDATE);
00664                break;
00665             case AST_CONTROL_SRCUPDATE:
00666                if (option_verbose > 2)
00667                   ast_verbose (VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", c->name, in->name);
00668                ast_indicate(in, AST_CONTROL_SRCUPDATE);
00669                break;
00670             case AST_CONTROL_PROCEEDING:
00671                if (option_verbose > 2)
00672                   ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", c->name, in->name);
00673                if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
00674                   ast_rtp_early_bridge(in, c);
00675                if (!ast_test_flag(outgoing, OPT_RINGBACK))
00676                   ast_indicate(in, AST_CONTROL_PROCEEDING);
00677                break;
00678             case AST_CONTROL_HOLD:
00679                if (option_verbose > 2)
00680                   ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", c->name);
00681                ast_indicate(in, AST_CONTROL_HOLD);
00682                break;
00683             case AST_CONTROL_UNHOLD:
00684                if (option_verbose > 2)
00685                   ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", c->name);
00686                ast_indicate(in, AST_CONTROL_UNHOLD);
00687                break;
00688             case AST_CONTROL_OFFHOOK:
00689             case AST_CONTROL_FLASH:
00690                /* Ignore going off hook and flash */
00691                break;
00692             case -1:
00693                if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
00694                   if (option_verbose > 2)
00695                      ast_verbose(VERBOSE_PREFIX_3 "%s stopped sounds\n", c->name);
00696                   ast_indicate(in, -1);
00697                   (*sentringing) = 0;
00698                }
00699                break;
00700             default:
00701                if (option_debug)
00702                   ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
00703             }
00704          } else if (single) {
00705             /* XXX are we sure the logic is correct ? or we should just switch on f->frametype ? */
00706             if (f->frametype == AST_FRAME_VOICE && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
00707                if (ast_write(in, f)) 
00708                   ast_log(LOG_WARNING, "Unable to forward voice frame\n");
00709             } else if (f->frametype == AST_FRAME_IMAGE && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
00710                if (ast_write(in, f))
00711                   ast_log(LOG_WARNING, "Unable to forward image\n");
00712             } else if (f->frametype == AST_FRAME_TEXT && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
00713                if (ast_write(in, f))
00714                   ast_log(LOG_WARNING, "Unable to send text\n");
00715             } else if (f->frametype == AST_FRAME_HTML && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) {
00716                if (ast_channel_sendhtml(in, f->subclass, f->data, f->datalen) == -1)
00717                   ast_log(LOG_WARNING, "Unable to send URL\n");
00718             }
00719          }
00720          ast_frfree(f);
00721       } /* end for */
00722       if (winner == in) {
00723          struct ast_frame *f = ast_read(in);
00724 #if 0
00725          if (f && (f->frametype != AST_FRAME_VOICE))
00726             printf("Frame type: %d, %d\n", f->frametype, f->subclass);
00727          else if (!f || (f->frametype != AST_FRAME_VOICE))
00728             printf("Hangup received on %s\n", in->name);
00729 #endif
00730          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
00731             /* Got hung up */
00732             *to = -1;
00733             ast_cdr_noanswer(in->cdr);
00734             strcpy(status, "CANCEL");
00735             if (f)
00736                ast_frfree(f);
00737             return NULL;
00738          }
00739 
00740          if (f && (f->frametype == AST_FRAME_DTMF)) {
00741             if (ast_test_flag(peerflags, OPT_DTMF_EXIT)) {
00742                const char *context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
00743                if (onedigit_goto(in, context, (char) f->subclass, 1)) {
00744                   if (option_verbose > 2)
00745                      ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
00746                   *to=0;
00747                   ast_cdr_noanswer(in->cdr);
00748                   *result = f->subclass;
00749                   strcpy(status, "CANCEL");
00750                   ast_frfree(f);
00751                   return NULL;
00752                }
00753             }
00754 
00755             if (ast_test_flag(peerflags, OPT_CALLER_HANGUP) &&
00756                detect_disconnect(in, f->subclass, featurecode, sizeof(featurecode))) {
00757                if (option_verbose > 2)
00758                   ast_verbose(VERBOSE_PREFIX_3 "User requested call disconnect.\n");
00759                *to=0;
00760                ast_cdr_noanswer(in->cdr);
00761                strcpy(status, "CANCEL");
00762                ast_frfree(f);
00763                return NULL;
00764             }
00765          }
00766 
00767          /* Forward HTML stuff */
00768          if (single && f && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) 
00769             if(ast_channel_sendhtml(outgoing->chan, f->subclass, f->data, f->datalen) == -1)
00770                ast_log(LOG_WARNING, "Unable to send URL\n");
00771          
00772 
00773          if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF_END)))  {
00774             if (ast_write(outgoing->chan, f))
00775                ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n");
00776          }
00777          if (single && (f->frametype == AST_FRAME_CONTROL) && 
00778             ((f->subclass == AST_CONTROL_HOLD) || 
00779              (f->subclass == AST_CONTROL_UNHOLD) || 
00780              (f->subclass == AST_CONTROL_VIDUPDATE) ||
00781              (f->subclass == AST_CONTROL_SRCUPDATE))) {
00782             if (option_verbose > 2)
00783                ast_verbose(VERBOSE_PREFIX_3 "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name);
00784             ast_indicate_data(outgoing->chan, f->subclass, f->data, f->datalen);
00785          }
00786          ast_frfree(f);
00787       }
00788       if (!*to && (option_verbose > 2))
00789          ast_verbose(VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
00790       if (!*to || ast_check_hangup(in)) {
00791          ast_cdr_noanswer(in->cdr);
00792       }
00793       
00794    }
00795    
00796    return peer;
00797 }
00798 
00799 static int detect_disconnect(struct ast_channel *chan, char code, char *featurecode, int len)
00800 {
00801    struct ast_flags features = { AST_FEATURE_DISCONNECT }; /* only concerned with disconnect feature */
00802    struct ast_call_feature feature = { 0, };
00803    char *tmp;
00804    int res;
00805 
00806    if ((strlen(featurecode)) < (len - 2)) { 
00807       tmp = &featurecode[strlen(featurecode)];
00808       tmp[0] = code;
00809       tmp[1] = '\0';
00810    } else {
00811       featurecode[0] = 0;
00812       return -1; /* no room in featurecode buffer */
00813    }
00814 
00815    res = ast_feature_detect(chan, &features, featurecode, &feature);
00816 
00817    if (res != FEATURE_RETURN_STOREDIGITS) {
00818       featurecode[0] = '\0';
00819    }
00820    if (feature.feature_mask & AST_FEATURE_DISCONNECT) {
00821       return 1;
00822    }
00823 
00824    return 0;
00825 }
00826 
00827 static void replace_macro_delimiter(char *s)
00828 {
00829    for (; *s; s++)
00830       if (*s == '^')
00831          *s = '|';
00832 }
00833 
00834 
00835 /* returns true if there is a valid privacy reply */
00836 static int valid_priv_reply(struct ast_flags *opts, int res)
00837 {
00838    if (res < '1')
00839       return 0;
00840    if (ast_test_flag(opts, OPT_PRIVACY) && res <= '5')
00841       return 1;
00842    if (ast_test_flag(opts, OPT_SCREENING) && res <= '4')
00843       return 1;
00844    return 0;
00845 }
00846 
00847 static void end_bridge_callback (void *data)
00848 {
00849    char buf[80];
00850    time_t end;
00851    struct ast_channel *chan = data;
00852 
00853    if (!chan->cdr) {
00854       return;
00855    }
00856 
00857    time(&end);
00858 
00859    ast_channel_lock(chan);
00860    if (chan->cdr->answer.tv_sec) {
00861       snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->answer.tv_sec);
00862       pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
00863    }
00864 
00865    if (chan->cdr->start.tv_sec) {
00866       snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->start.tv_sec);
00867       pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
00868    }
00869    ast_channel_unlock(chan);
00870 }
00871 
00872 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) {
00873    bconfig->end_bridge_callback_data = originator;
00874 }
00875 
00876 static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags, int *continue_exec)
00877 {
00878    int res = -1;
00879    struct ast_module_user *u;
00880    char *rest, *cur;
00881    struct dial_localuser *outgoing = NULL;
00882    struct ast_channel *peer;
00883    int to;
00884    int numbusy = 0;
00885    int numcongestion = 0;
00886    int numnochan = 0;
00887    int cause;
00888    char numsubst[256];
00889    char cidname[AST_MAX_EXTENSION] = "";
00890    int privdb_val = 0;
00891    int calldurationlimit = -1;
00892    long timelimit = 0;
00893    long play_warning = 0;
00894    long warning_freq = 0;
00895    const char *warning_sound = NULL;
00896    const char *end_sound = NULL;
00897    const char *start_sound = NULL;
00898    char *dtmfcalled = NULL, *dtmfcalling = NULL;
00899    char status[256] = "INVALIDARGS";
00900    int play_to_caller = 0, play_to_callee = 0;
00901    int sentringing = 0, moh = 0;
00902    const char *outbound_group = NULL;
00903    int result = 0;
00904    time_t start_time;
00905    char privintro[1024];
00906    char privcid[256];
00907    char *parse;
00908    int opermode = 0;
00909    AST_DECLARE_APP_ARGS(args,
00910               AST_APP_ARG(peers);
00911               AST_APP_ARG(timeout);
00912               AST_APP_ARG(options);
00913               AST_APP_ARG(url);
00914    );
00915    struct ast_flags opts = { 0, };
00916    char *opt_args[OPT_ARG_ARRAY_SIZE];
00917    struct ast_datastore *datastore = NULL;
00918    int fulldial = 0, num_dialed = 0;
00919 
00920    if (ast_strlen_zero(data)) {
00921       ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
00922       pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
00923       return -1;
00924    }
00925 
00926    /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
00927    pbx_builtin_setvar_helper(chan, "DIALSTATUS", "");
00928    pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", "");
00929    pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", "");
00930    pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", "");
00931    pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
00932 
00933    u = ast_module_user_add(chan);
00934 
00935    parse = ast_strdupa(data);
00936    
00937    AST_STANDARD_APP_ARGS(args, parse);
00938 
00939    if (!ast_strlen_zero(args.options) &&
00940          ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options)) {
00941       pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
00942       goto done;
00943    }
00944 
00945    if (ast_strlen_zero(args.peers)) {
00946       ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
00947       pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
00948       goto done;
00949    }
00950 
00951    if (ast_test_flag(&opts, OPT_OPERMODE)) {
00952       if (ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]))
00953          opermode = 1;
00954       else opermode = atoi(opt_args[OPT_ARG_OPERMODE]);
00955       if (option_verbose > 2)
00956          ast_verbose(VERBOSE_PREFIX_3 "Setting operator services mode to %d.\n", opermode);
00957    }
00958    
00959    if (ast_test_flag(&opts, OPT_DURATION_STOP) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_STOP])) {
00960       calldurationlimit = atoi(opt_args[OPT_ARG_DURATION_STOP]);
00961       if (!calldurationlimit) {
00962          ast_log(LOG_WARNING, "Dial does not accept S(%s), hanging up.\n", opt_args[OPT_ARG_DURATION_STOP]);
00963          pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
00964          goto done;
00965       }
00966       if (option_verbose > 2)
00967          ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n", calldurationlimit);
00968    }
00969 
00970    if (ast_test_flag(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
00971       dtmfcalling = opt_args[OPT_ARG_SENDDTMF];
00972       dtmfcalled = strsep(&dtmfcalling, ":");
00973    }
00974 
00975    if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
00976       char *limit_str, *warning_str, *warnfreq_str;
00977       const char *var;
00978 
00979       warnfreq_str = opt_args[OPT_ARG_DURATION_LIMIT];
00980       limit_str = strsep(&warnfreq_str, ":");
00981       warning_str = strsep(&warnfreq_str, ":");
00982 
00983       timelimit = atol(limit_str);
00984       if (warning_str)
00985          play_warning = atol(warning_str);
00986       if (warnfreq_str)
00987          warning_freq = atol(warnfreq_str);
00988 
00989       if (!timelimit) {
00990          ast_log(LOG_WARNING, "Dial does not accept L(%s), hanging up.\n", limit_str);
00991          goto done;
00992       } else if (play_warning > timelimit) {
00993          /* If the first warning is requested _after_ the entire call would end,
00994             and no warning frequency is requested, then turn off the warning. If
00995             a warning frequency is requested, reduce the 'first warning' time by
00996             that frequency until it falls within the call's total time limit.
00997          */
00998 
00999          if (!warning_freq) {
01000             play_warning = 0;
01001          } else {
01002             /* XXX fix this!! */
01003             while (play_warning > timelimit)
01004                play_warning -= warning_freq;
01005             if (play_warning < 1)
01006                play_warning = warning_freq = 0;
01007          }
01008       }
01009 
01010       var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLER");
01011       play_to_caller = var ? ast_true(var) : 1;
01012       
01013       var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLEE");
01014       play_to_callee = var ? ast_true(var) : 0;
01015       
01016       if (!play_to_caller && !play_to_callee)
01017          play_to_caller = 1;
01018       
01019       var = pbx_builtin_getvar_helper(chan,"LIMIT_WARNING_FILE");
01020       warning_sound = S_OR(var, "timeleft");
01021       
01022       var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE");
01023       end_sound = S_OR(var, NULL);  /* XXX not much of a point in doing this! */
01024       
01025       var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE");
01026       start_sound = S_OR(var, NULL);   /* XXX not much of a point in doing this! */
01027 
01028       /* undo effect of S(x) in case they are both used */
01029       calldurationlimit = -1;
01030       /* more efficient to do it like S(x) does since no advanced opts */
01031       if (!play_warning && !start_sound && !end_sound && timelimit) {
01032          calldurationlimit = timelimit / 1000;
01033          if (option_verbose > 2)
01034             ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n", calldurationlimit);
01035          timelimit = play_to_caller = play_to_callee = play_warning = warning_freq = 0;
01036       } else if (option_verbose > 2) {
01037          ast_verbose(VERBOSE_PREFIX_3 "Limit Data for this call:\n");
01038          ast_verbose(VERBOSE_PREFIX_4 "timelimit      = %ld\n", timelimit);
01039          ast_verbose(VERBOSE_PREFIX_4 "play_warning   = %ld\n", play_warning);
01040          ast_verbose(VERBOSE_PREFIX_4 "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
01041          ast_verbose(VERBOSE_PREFIX_4 "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
01042          ast_verbose(VERBOSE_PREFIX_4 "warning_freq   = %ld\n", warning_freq);
01043          ast_verbose(VERBOSE_PREFIX_4 "start_sound    = %s\n", start_sound);
01044          ast_verbose(VERBOSE_PREFIX_4 "warning_sound  = %s\n", warning_sound);
01045          ast_verbose(VERBOSE_PREFIX_4 "end_sound      = %s\n", end_sound);
01046       }
01047    }
01048 
01049    if (ast_test_flag(&opts, OPT_RESETCDR) && chan->cdr)
01050       ast_cdr_reset(chan->cdr, NULL);
01051    if (ast_test_flag(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
01052       opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten);
01053    if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) {
01054       char callerid[60];
01055       char *l = chan->cid.cid_num;  /* XXX watch out, we are overwriting it */
01056       if (!ast_strlen_zero(l)) {
01057          ast_shrink_phone_number(l);
01058          if( ast_test_flag(&opts, OPT_PRIVACY) ) {
01059             if (option_verbose > 2)
01060                ast_verbose(VERBOSE_PREFIX_3  "Privacy DB is '%s', clid is '%s'\n",
01061                        opt_args[OPT_ARG_PRIVACY], l);
01062             privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
01063          }
01064          else {
01065             if (option_verbose > 2)
01066                ast_verbose(VERBOSE_PREFIX_3  "Privacy Screening, clid is '%s'\n", l);
01067             privdb_val = AST_PRIVACY_UNKNOWN;
01068          }
01069       } else {
01070          char *tnam, *tn2;
01071 
01072          tnam = ast_strdupa(chan->name);
01073          /* clean the channel name so slashes don't try to end up in disk file name */
01074          for(tn2 = tnam; *tn2; tn2++) {
01075             if( *tn2=='/')
01076                *tn2 = '=';  /* any other chars to be afraid of? */
01077          }
01078          if (option_verbose > 2)
01079             ast_verbose(VERBOSE_PREFIX_3  "Privacy-- callerid is empty\n");
01080 
01081          snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam);
01082          l = callerid;
01083          privdb_val = AST_PRIVACY_UNKNOWN;
01084       }
01085       
01086       ast_copy_string(privcid,l,sizeof(privcid));
01087 
01088       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 */  
01089          if (option_verbose > 2)
01090             ast_verbose( VERBOSE_PREFIX_3  "CallerID set (%s); N option set; Screening should be off\n", privcid);
01091          privdb_val = AST_PRIVACY_ALLOW;
01092       }
01093       else if(ast_test_flag(&opts, OPT_SCREEN_NOCLID) && strncmp(privcid,"NOCALLERID",10) == 0 ) {
01094          if (option_verbose > 2)
01095             ast_verbose( VERBOSE_PREFIX_3  "CallerID blank; N option set; Screening should happen; dbval is %d\n", privdb_val);
01096       }
01097       
01098       if(privdb_val == AST_PRIVACY_DENY ) {
01099          ast_copy_string(status, "NOANSWER", sizeof(status));
01100          if (option_verbose > 2)
01101             ast_verbose( VERBOSE_PREFIX_3  "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
01102          res=0;
01103          goto out;
01104       }
01105       else if(privdb_val == AST_PRIVACY_KILL ) {
01106          ast_copy_string(status, "DONTCALL", sizeof(status));
01107          if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
01108             ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201);
01109          }
01110          res = 0;
01111          goto out; /* Is this right? */
01112       }
01113       else if(privdb_val == AST_PRIVACY_TORTURE ) {
01114          ast_copy_string(status, "TORTURE", sizeof(status));
01115          if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
01116             ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 301);
01117          }
01118          res = 0;
01119          goto out; /* is this right??? */
01120       }
01121       else if(privdb_val == AST_PRIVACY_UNKNOWN ) {
01122          /* Get the user's intro, store it in priv-callerintros/$CID, 
01123             unless it is already there-- this should be done before the 
01124             call is actually dialed  */
01125 
01126          /* make sure the priv-callerintros dir actually exists */
01127          snprintf(privintro, sizeof(privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR);
01128          if (mkdir(privintro, 0755) && errno != EEXIST) {
01129             ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(errno));
01130             res = -1;
01131             goto out;
01132          }
01133 
01134          snprintf(privintro,sizeof(privintro), "priv-callerintros/%s", privcid);
01135          if( ast_fileexists(privintro,NULL,NULL ) > 0 && strncmp(privcid,"NOCALLERID",10) != 0) {
01136             /* the DELUX version of this code would allow this caller the
01137                option to hear and retape their previously recorded intro.
01138             */
01139          }
01140          else {
01141             int duration; /* for feedback from play_and_wait */
01142             /* the file doesn't exist yet. Let the caller submit his
01143                vocal intro for posterity */
01144             /* priv-recordintro script:
01145 
01146                "At the tone, please say your name:"
01147 
01148             */
01149             ast_answer(chan);
01150             res = ast_play_and_record(chan, "priv-recordintro", privintro, 4, "gsm", &duration, 128, 2000, 0);  /* NOTE: I've reduced the total time to 4 sec */
01151                               /* don't think we'll need a lock removed, we took care of
01152                                  conflicts by naming the privintro file */
01153             if (res == -1) {
01154                /* Delete the file regardless since they hung up during recording */
01155                                         ast_filedelete(privintro, NULL);
01156                                         if( ast_fileexists(privintro,NULL,NULL ) > 0 )
01157                                                 ast_log(LOG_NOTICE,"privacy: ast_filedelete didn't do its job on %s\n", privintro);
01158                                         else if (option_verbose > 2)
01159                                                 ast_verbose( VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro);
01160                goto out;
01161             }
01162                                 if( !ast_streamfile(chan, "vm-dialout", chan->language) )
01163                                         ast_waitstream(chan, "");
01164          }
01165       }
01166    }
01167 
01168    if (continue_exec)
01169       *continue_exec = 0;
01170    
01171    /* If a channel group has been specified, get it for use when we create peer channels */
01172    if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
01173       outbound_group = ast_strdupa(outbound_group);
01174       pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
01175    } else {
01176       outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
01177    }
01178        
01179    ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING);
01180 
01181    /* loop through the list of dial destinations */
01182    rest = args.peers;
01183    while ((cur = strsep(&rest, "&")) ) {
01184       struct dial_localuser *tmp;
01185       /* Get a technology/[device:]number pair */
01186       char *number = cur;
01187       char *interface = ast_strdupa(number);
01188       char *tech = strsep(&number, "/");
01189       /* find if we already dialed this interface */
01190       struct ast_dialed_interface *di;
01191       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
01192       num_dialed++;
01193       if (!number) {
01194          ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n");
01195          goto out;
01196       }
01197       if (!(tmp = ast_calloc(1, sizeof(*tmp))))
01198          goto out;
01199       if (opts.flags) {
01200          ast_copy_flags(tmp, &opts,
01201                    OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
01202                    OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
01203                    OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
01204                    OPT_CALLEE_PARK | OPT_CALLER_PARK |
01205                    OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
01206          ast_set2_flag(tmp, args.url, DIAL_NOFORWARDHTML);  
01207       }
01208       ast_copy_string(numsubst, number, sizeof(numsubst));
01209       /* Request the peer */
01210 
01211       ast_channel_lock(chan);
01212       datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
01213       ast_channel_unlock(chan);
01214 
01215       if (datastore)
01216          dialed_interfaces = datastore->data;
01217       else {
01218          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
01219             ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n"); 
01220             free(tmp);
01221             goto out;
01222          }
01223 
01224          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
01225 
01226          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
01227             free(tmp);
01228             goto out;
01229          }
01230 
01231          datastore->data = dialed_interfaces;
01232          AST_LIST_HEAD_INIT(dialed_interfaces);
01233 
01234          ast_channel_lock(chan);
01235          ast_channel_datastore_add(chan, datastore);
01236          ast_channel_unlock(chan);
01237       }
01238 
01239       AST_LIST_LOCK(dialed_interfaces);
01240       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
01241          if (!strcasecmp(di->interface, interface)) {
01242             ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n", 
01243                di->interface);
01244             break;
01245          }
01246       }
01247       AST_LIST_UNLOCK(dialed_interfaces);
01248 
01249       if (di) {
01250          fulldial++;
01251          free(tmp);
01252          continue;
01253       }
01254 
01255       /* It is always ok to dial a Local interface.  We only keep track of
01256        * which "real" interfaces have been dialed.  The Local channel will
01257        * inherit this list so that if it ends up dialing a real interface,
01258        * it won't call one that has already been called. */
01259       if (strcasecmp(tech, "Local")) {
01260          if (!(di = ast_calloc(1, sizeof(*di) + strlen(interface)))) {
01261             AST_LIST_UNLOCK(dialed_interfaces);
01262             free(tmp);
01263             goto out;
01264          }
01265          strcpy(di->interface, interface);
01266 
01267          AST_LIST_LOCK(dialed_interfaces);
01268          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
01269          AST_LIST_UNLOCK(dialed_interfaces);
01270       }
01271 
01272       tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause);
01273       if (!tmp->chan) {
01274          /* If we can't, just go on to the next call */
01275          ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n", tech, cause, ast_cause2str(cause));
01276          HANDLE_CAUSE(cause, chan);
01277          if (!rest)  /* we are on the last destination */
01278             chan->hangupcause = cause;
01279          free(tmp);
01280          continue;
01281       }
01282 
01283             pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst);
01284 
01285       /* Setup outgoing SDP to match incoming one */
01286       ast_rtp_make_compatible(tmp->chan, chan, !outgoing && !rest);
01287       
01288       /* Inherit specially named variables from parent channel */
01289       ast_channel_inherit_variables(chan, tmp->chan);
01290       ast_channel_datastore_inherit(chan, tmp->chan);
01291 
01292       tmp->chan->appl = "AppDial";
01293       tmp->chan->data = "(Outgoing Line)";
01294       tmp->chan->whentohangup = 0;
01295 
01296       if (tmp->chan->cid.cid_num)
01297          free(tmp->chan->cid.cid_num);
01298       tmp->chan->cid.cid_num = ast_strdup(chan->cid.cid_num);
01299 
01300       if (tmp->chan->cid.cid_name)
01301          free(tmp->chan->cid.cid_name);
01302       tmp->chan->cid.cid_name = ast_strdup(chan->cid.cid_name);
01303 
01304       if (tmp->chan->cid.cid_ani)
01305          free(tmp->chan->cid.cid_ani);
01306       tmp->chan->cid.cid_ani = ast_strdup(chan->cid.cid_ani);
01307       
01308       /* Copy language from incoming to outgoing */
01309       ast_string_field_set(tmp->chan, language, chan->language);
01310       ast_string_field_set(tmp->chan, accountcode, chan->accountcode);
01311       tmp->chan->cdrflags = chan->cdrflags;
01312       if (ast_strlen_zero(tmp->chan->musicclass))
01313          ast_string_field_set(tmp->chan, musicclass, chan->musicclass);
01314       /* XXX don't we free previous values ? */
01315       tmp->chan->cid.cid_rdnis = ast_strdup(chan->cid.cid_rdnis);
01316       /* Pass callingpres setting */
01317       tmp->chan->cid.cid_pres = chan->cid.cid_pres;
01318       /* Pass type of number */
01319       tmp->chan->cid.cid_ton = chan->cid.cid_ton;
01320       /* Pass type of tns */
01321       tmp->chan->cid.cid_tns = chan->cid.cid_tns;
01322       /* Presense of ADSI CPE on outgoing channel follows ours */
01323       tmp->chan->adsicpe = chan->adsicpe;
01324       /* Pass the transfer capability */
01325       tmp->chan->transfercapability = chan->transfercapability;
01326 
01327       /* If we have an outbound group, set this peer channel to it */
01328       if (outbound_group)
01329          ast_app_group_set_channel(tmp->chan, outbound_group);
01330 
01331       /* Inherit context and extension */
01332       if (!ast_strlen_zero(chan->macrocontext))
01333          ast_copy_string(tmp->chan->dialcontext, chan->macrocontext, sizeof(tmp->chan->dialcontext));
01334       else
01335          ast_copy_string(tmp->chan->dialcontext, chan->context, sizeof(tmp->chan->dialcontext));
01336       if (!ast_strlen_zero(chan->macroexten))
01337          ast_copy_string(tmp->chan->exten, chan->macroexten, sizeof(tmp->chan->exten));
01338       else
01339          ast_copy_string(tmp->chan->exten, chan->exten, sizeof(tmp->chan->exten));
01340 
01341       /* Place the call, but don't wait on the answer */
01342       res = ast_call(tmp->chan, numsubst, 0);
01343 
01344       /* Save the info in cdr's that we called them */
01345       if (chan->cdr)
01346          ast_cdr_setdestchan(chan->cdr, tmp->chan->name);
01347 
01348       /* check the results of ast_call */
01349       if (res) {
01350          /* Again, keep going even if there's an error */
01351          if (option_debug)
01352             ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01353          if (option_verbose > 2)
01354             ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
01355          if (tmp->chan->hangupcause) {
01356             chan->hangupcause = tmp->chan->hangupcause;
01357          }
01358          ast_hangup(tmp->chan);
01359          tmp->chan = NULL;
01360          free(tmp);
01361          continue;
01362       } else {
01363          senddialevent(chan, tmp->chan);
01364          if (option_verbose > 2)
01365             ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
01366          if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID))
01367             ast_set_callerid(tmp->chan, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL);
01368       }
01369       /* Put them in the list of outgoing thingies...  We're ready now. 
01370          XXX If we're forcibly removed, these outgoing calls won't get
01371          hung up XXX */
01372       ast_set_flag(tmp, DIAL_STILLGOING); 
01373       tmp->next = outgoing;
01374       outgoing = tmp;
01375       /* If this line is up, don't try anybody else */
01376       if (outgoing->chan->_state == AST_STATE_UP)
01377          break;
01378    }
01379    
01380    if (ast_strlen_zero(args.timeout)) {
01381       to = -1;
01382    } else {
01383       to = atoi(args.timeout);
01384       if (to > 0)
01385          to *= 1000;
01386       else
01387          ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
01388    }
01389 
01390    if (!outgoing) {
01391       strcpy(status, "CHANUNAVAIL");
01392       if(fulldial == num_dialed) {
01393          res = -1;
01394          goto out;
01395       }
01396    } else {
01397       /* Our status will at least be NOANSWER */
01398       strcpy(status, "NOANSWER");
01399       if (ast_test_flag(outgoing, OPT_MUSICBACK)) {
01400          moh = 1;
01401          if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
01402             char *original_moh = ast_strdupa(chan->musicclass);
01403             ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
01404             ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
01405             ast_string_field_set(chan, musicclass, original_moh);
01406          } else {
01407             ast_moh_start(chan, NULL, NULL);
01408          }
01409          ast_indicate(chan, AST_CONTROL_PROGRESS);
01410       } else if (ast_test_flag(outgoing, OPT_RINGBACK)) {
01411          ast_indicate(chan, AST_CONTROL_RINGING);
01412          sentringing++;
01413       }
01414    }
01415 
01416    time(&start_time);
01417    peer = wait_for_answer(chan, outgoing, &to, peerflags, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result);
01418 
01419    /* The ast_channel_datastore_remove() function could fail here if the
01420     * datastore was moved to another channel during a masquerade. If this is
01421     * the case, don't free the datastore here because later, when the channel
01422     * to which the datastore was moved hangs up, it will attempt to free this
01423     * datastore again, causing a crash
01424     */
01425    if (!ast_channel_datastore_remove(chan, datastore))
01426       ast_channel_datastore_free(datastore);
01427    if (!peer) {
01428       if (result) {
01429          res = result;
01430       } else if (to) { /* Musta gotten hung up */
01431          res = -1;
01432       } else { /* Nobody answered, next please? */
01433          res = 0;
01434       }
01435       /* almost done, although the 'else' block is 400 lines */
01436    } else {
01437       const char *number;
01438 
01439       strcpy(status, "ANSWER");
01440       pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
01441       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
01442          we will always return with -1 so that it is hung up properly after the 
01443          conversation.  */
01444       hanguptree(outgoing, peer);
01445       outgoing = NULL;
01446       /* If appropriate, log that we have a destination channel */
01447       if (chan->cdr)
01448          ast_cdr_setdestchan(chan->cdr, peer->name);
01449       if (peer->name)
01450          pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name);
01451 
01452       number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
01453       if (!number)
01454          number = numsubst;
01455       pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
01456       if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
01457          if (option_debug)
01458             ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", args.url);
01459          ast_channel_sendurl( peer, args.url );
01460       }
01461       if ( (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) && privdb_val == AST_PRIVACY_UNKNOWN) {
01462          int res2;
01463          int loopcount = 0;
01464 
01465          /* Get the user's intro, store it in priv-callerintros/$CID, 
01466             unless it is already there-- this should be done before the 
01467             call is actually dialed  */
01468 
01469          /* all ring indications and moh for the caller has been halted as soon as the 
01470             target extension was picked up. We are going to have to kill some
01471             time and make the caller believe the peer hasn't picked up yet */
01472 
01473          if (ast_test_flag(&opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
01474             char *original_moh = ast_strdupa(chan->musicclass);
01475             ast_indicate(chan, -1);
01476             ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
01477             ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
01478             ast_string_field_set(chan, musicclass, original_moh);
01479          } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
01480             ast_indicate(chan, AST_CONTROL_RINGING);
01481             sentringing++;
01482          }
01483 
01484          /* Start autoservice on the other chan ?? */
01485          res2 = ast_autoservice_start(chan);
01486          /* Now Stream the File */
01487          for (loopcount = 0; loopcount < 3; loopcount++) {
01488             if (res2 && loopcount == 0)   /* error in ast_autoservice_start() */
01489                break;
01490             if (!res2)  /* on timeout, play the message again */
01491                res2 = ast_play_and_wait(peer,"priv-callpending");
01492             if (!valid_priv_reply(&opts, res2))
01493                res2 = 0;
01494             /* priv-callpending script: 
01495                "I have a caller waiting, who introduces themselves as:"
01496             */
01497             if (!res2)
01498                res2 = ast_play_and_wait(peer,privintro);
01499             if (!valid_priv_reply(&opts, res2))
01500                res2 = 0;
01501             /* now get input from the called party, as to their choice */
01502             if( !res2 ) {
01503                /* XXX can we have both, or they are mutually exclusive ? */
01504                if( ast_test_flag(&opts, OPT_PRIVACY) )
01505                   res2 = ast_play_and_wait(peer,"priv-callee-options");
01506                if( ast_test_flag(&opts, OPT_SCREENING) )
01507                   res2 = ast_play_and_wait(peer,"screen-callee-options");
01508             }
01509             /*! \page DialPrivacy Dial Privacy scripts
01510             \par priv-callee-options script:
01511                "Dial 1 if you wish this caller to reach you directly in the future,
01512                   and immediately connect to their incoming call
01513                 Dial 2 if you wish to send this caller to voicemail now and 
01514                   forevermore.
01515                 Dial 3 to send this caller to the torture menus, now and forevermore.
01516                 Dial 4 to send this caller to a simple "go away" menu, now and forevermore.
01517                 Dial 5 to allow this caller to come straight thru to you in the future,
01518                   but right now, just this once, send them to voicemail."
01519             \par screen-callee-options script:
01520                "Dial 1 if you wish to immediately connect to the incoming call
01521                 Dial 2 if you wish to send this caller to voicemail.
01522                 Dial 3 to send this caller to the torture menus.
01523                 Dial 4 to send this caller to a simple "go away" menu.
01524             */
01525             if (valid_priv_reply(&opts, res2))
01526                break;
01527             /* invalid option */
01528             res2 = ast_play_and_wait(peer, "vm-sorry");
01529          }
01530 
01531          if (ast_test_flag(&opts, OPT_MUSICBACK)) {
01532             ast_moh_stop(chan);
01533          } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
01534             ast_indicate(chan, -1);
01535             sentringing=0;
01536          }
01537          ast_autoservice_stop(chan);
01538 
01539          switch (res2) {
01540          case '1':
01541             if( ast_test_flag(&opts, OPT_PRIVACY) ) {
01542                if (option_verbose > 2)
01543                   ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n",
01544                           opt_args[OPT_ARG_PRIVACY], privcid);
01545                ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW);
01546             }
01547             break;
01548          case '2':
01549             if( ast_test_flag(&opts, OPT_PRIVACY) ) {
01550                if (option_verbose > 2)
01551                   ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to DENY\n",
01552                           opt_args[OPT_ARG_PRIVACY], privcid);
01553                ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_DENY);
01554             }
01555             ast_copy_string(status, "NOANSWER", sizeof(status));
01556             ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
01557             res=0;
01558             goto out;
01559          case '3':
01560             if( ast_test_flag(&opts, OPT_PRIVACY) ) {
01561                if (option_verbose > 2)
01562                   ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to TORTURE\n",
01563                           opt_args[OPT_ARG_PRIVACY], privcid);
01564                ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_TORTURE);
01565             }
01566             ast_copy_string(status, "TORTURE", sizeof(status));
01567             
01568             res = 0;
01569             ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
01570             goto out; /* Is this right? */
01571          case '4':
01572             if( ast_test_flag(&opts, OPT_PRIVACY) ) {
01573                if (option_verbose > 2)
01574                   ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to KILL\n",
01575                           opt_args[OPT_ARG_PRIVACY], privcid);
01576                ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_KILL);
01577             }
01578 
01579             ast_copy_string(status, "DONTCALL", sizeof(status));
01580             res = 0;
01581             ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
01582             goto out; /* Is this right? */
01583          case '5':
01584             if( ast_test_flag(&opts, OPT_PRIVACY) ) {
01585                if (option_verbose > 2)
01586                   ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n",
01587                           opt_args[OPT_ARG_PRIVACY], privcid);
01588                ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW);
01589                ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */
01590                res=0;
01591                goto out;
01592             } /* if not privacy, then 5 is the same as "default" case */
01593          default: /* bad input or -1 if failure to start autoservice */
01594             /* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do?  */
01595             /* well, there seems basically two choices. Just patch the caller thru immediately,
01596                  or,... put 'em thru to voicemail. */
01597             /* since the callee may have hung up, let's do the voicemail thing, no database decision */
01598             ast_log(LOG_NOTICE, "privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n");
01599             ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
01600             res=0;
01601             goto out;
01602          }
01603 
01604          /* XXX once again, this path is only taken in the case '1', so it could be
01605           * moved there, although i am not really sure that this is correct - maybe
01606           * the check applies to other cases as well.
01607           */
01608          /* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll 
01609             just clog things up, and it's not useful information, not being tied to a CID */
01610          if( strncmp(privcid,"NOCALLERID",10) == 0 || ast_test_flag(&opts, OPT_SCREEN_NOINTRO) ) {
01611             ast_filedelete(privintro, NULL);
01612             if( ast_fileexists(privintro, NULL, NULL ) > 0 )
01613                ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", privintro);
01614             else if (option_verbose > 2)
01615                ast_verbose(VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro);
01616          }
01617       }
01618       if (!ast_test_flag(&opts, OPT_ANNOUNCE) || ast_strlen_zero(opt_args[OPT_ARG_ANNOUNCE])) {
01619          res = 0;
01620       } else {
01621          int digit = 0;
01622          /* Start autoservice on the other chan */
01623          res = ast_autoservice_start(chan);
01624          /* Now Stream the File */
01625          if (!res)
01626             res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], peer->language);
01627          if (!res) {
01628             digit = ast_waitstream(peer, AST_DIGIT_ANY); 
01629          }
01630          /* Ok, done. stop autoservice */
01631          res = ast_autoservice_stop(chan);
01632          if (digit > 0 && !res)
01633             res = ast_senddigit(chan, digit); 
01634          else
01635             res = digit;
01636 
01637       }
01638 
01639       if (chan && peer && ast_test_flag(&opts, OPT_GOTO) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO])) {
01640          replace_macro_delimiter(opt_args[OPT_ARG_GOTO]);
01641          ast_parseable_goto(chan, opt_args[OPT_ARG_GOTO]);
01642          /* peer goes to the same context and extension as chan, so just copy info from chan*/
01643          ast_copy_string(peer->context, chan->context, sizeof(peer->context));
01644          ast_copy_string(peer->exten, chan->exten, sizeof(peer->exten));
01645          peer->priority = chan->priority + 2;
01646          ast_pbx_start(peer);
01647          hanguptree(outgoing, NULL);
01648          if (continue_exec)
01649             *continue_exec = 1;
01650          res = 0;
01651          goto done;
01652       }
01653 
01654       if (ast_test_flag(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
01655          struct ast_app *theapp;
01656          const char *macro_result;
01657 
01658          res = ast_autoservice_start(chan);
01659          if (res) {
01660             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
01661             res = -1;
01662          }
01663 
01664          theapp = pbx_findapp("Macro");
01665 
01666          if (theapp && !res) {   /* XXX why check res here ? */
01667             replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
01668             res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_MACRO]);
01669             ast_log(LOG_DEBUG, "Macro exited with status %d\n", res);
01670             res = 0;
01671          } else {
01672             ast_log(LOG_ERROR, "Could not find application Macro\n");
01673             res = -1;
01674          }
01675 
01676          if (ast_autoservice_stop(chan) < 0) {
01677             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
01678             res = -1;
01679          }
01680 
01681          if (!res && (macro_result = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
01682                char *macro_transfer_dest;
01683 
01684                if (!strcasecmp(macro_result, "BUSY")) {
01685                   ast_copy_string(status, macro_result, sizeof(status));
01686                   if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
01687                      if (!ast_goto_if_exists(chan, NULL, NULL, chan->priority + 101)) {
01688                         ast_set_flag(peerflags, OPT_GO_ON);
01689                      }
01690                   } else
01691                      ast_set_flag(peerflags, OPT_GO_ON);
01692                   res = -1;
01693                } else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
01694                   ast_copy_string(status, macro_result, sizeof(status));
01695                   ast_set_flag(peerflags, OPT_GO_ON); 
01696                   res = -1;
01697                } else if (!strcasecmp(macro_result, "CONTINUE")) {
01698                   /* hangup peer and keep chan alive assuming the macro has changed 
01699                      the context / exten / priority or perhaps 
01700                      the next priority in the current exten is desired.
01701                   */
01702                   ast_set_flag(peerflags, OPT_GO_ON); 
01703                   res = -1;
01704                } else if (!strcasecmp(macro_result, "ABORT")) {
01705                   /* Hangup both ends unless the caller has the g flag */
01706                   res = -1;
01707                } else if (!strncasecmp(macro_result, "GOTO:", 5) && (macro_transfer_dest = ast_strdupa(macro_result + 5))) {
01708                   res = -1;
01709                   /* perform a transfer to a new extension */
01710                   if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
01711                      replace_macro_delimiter(macro_transfer_dest);
01712                      if (!ast_parseable_goto(chan, macro_transfer_dest))
01713                         ast_set_flag(peerflags, OPT_GO_ON);
01714 
01715                   }
01716                }
01717          }
01718       }
01719 
01720       if (!res) {
01721          if (calldurationlimit > 0) {
01722             peer->whentohangup = time(NULL) + calldurationlimit;
01723          } else if (calldurationlimit != -1 && timelimit > 0) {
01724             /* Not enough granularity to make it less, but we can't use the special value 0 */
01725             peer->whentohangup = time(NULL) + 1;
01726          }
01727          if (!ast_strlen_zero(dtmfcalled)) { 
01728             if (option_verbose > 2)
01729                ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the called party.\n", dtmfcalled);
01730             res = ast_dtmf_stream(peer,chan,dtmfcalled,250);
01731          }
01732          if (!ast_strlen_zero(dtmfcalling)) {
01733             if (option_verbose > 2)
01734                ast_verbose(VERBOSE_PREFIX_3 "Sending DTMF '%s' to the calling party.\n", dtmfcalling);
01735             res = ast_dtmf_stream(chan,peer,dtmfcalling,250);
01736          }
01737       }
01738 
01739       if (!res) {
01740          struct ast_bridge_config config;
01741 
01742          memset(&config,0,sizeof(struct ast_bridge_config));
01743          if (play_to_caller)
01744             ast_set_flag(&(config.features_caller), AST_FEATURE_PLAY_WARNING);
01745          if (play_to_callee)
01746             ast_set_flag(&(config.features_callee), AST_FEATURE_PLAY_WARNING);
01747          if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER))
01748             ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01749          if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER))
01750             ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
01751          if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP))
01752             ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
01753          if (ast_test_flag(peerflags, OPT_CALLER_HANGUP))
01754             ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
01755          if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR))
01756             ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01757          if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) 
01758             ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01759          if (ast_test_flag(peerflags, OPT_CALLEE_PARK))
01760             ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
01761          if (ast_test_flag(peerflags, OPT_CALLER_PARK))
01762             ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
01763          if (ast_test_flag(peerflags, OPT_GO_ON))
01764             ast_set_flag(&(config.features_caller), AST_FEATURE_NO_H_EXTEN);
01765 
01766          config.timelimit = timelimit;
01767          config.play_warning = play_warning;
01768          config.warning_freq = warning_freq;
01769          config.warning_sound = warning_sound;
01770          config.end_sound = end_sound;
01771          config.start_sound = start_sound;
01772          config.end_bridge_callback = end_bridge_callback;
01773          config.end_bridge_callback_data = chan;
01774          config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01775          if (moh) {
01776             moh = 0;
01777             ast_moh_stop(chan);
01778          } else if (sentringing) {
01779             sentringing = 0;
01780             ast_indicate(chan, -1);
01781          }
01782          /* Be sure no generators are left on it */
01783          ast_deactivate_generator(chan);
01784          /* Make sure channels are compatible */
01785          res = ast_channel_make_compatible(chan, peer);
01786          if (res < 0) {
01787             ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
01788             ast_hangup(peer);
01789             res = -1;
01790             goto done;
01791          }
01792          if (opermode && 
01793             (((!strncasecmp(chan->name,"Zap",3)) && (!strncasecmp(peer->name,"Zap",3))) ||
01794              ((!strncasecmp(chan->name,"Dahdi",5)) && (!strncasecmp(peer->name,"Dahdi",5)))))
01795          {
01796             struct oprmode oprmode;
01797 
01798             oprmode.peer = peer;
01799             oprmode.mode = opermode;
01800 
01801             ast_channel_setoption(chan,
01802                AST_OPTION_OPRMODE,&oprmode,sizeof(struct oprmode),0);
01803          }
01804          res = ast_bridge_call(chan,peer,&config);
01805       } else {
01806          res = -1;
01807       }
01808 
01809       if (!chan->_softhangup)
01810          chan->hangupcause = peer->hangupcause;
01811       ast_hangup(peer);
01812    }  
01813 out:
01814    if (moh) {
01815       moh = 0;
01816       ast_moh_stop(chan);
01817    } else if (sentringing) {
01818       sentringing = 0;
01819       ast_indicate(chan, -1);
01820    }
01821    ast_rtp_early_bridge(chan, NULL);
01822    hanguptree(outgoing, NULL);
01823    pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
01824    if (option_debug)
01825       ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s (HANGUPCAUSE=%d)\n", status, chan->hangupcause);
01826    
01827    if (ast_test_flag(peerflags, OPT_GO_ON) && !chan->_softhangup) {
01828       if (calldurationlimit)
01829          chan->whentohangup = 0;
01830       res = 0;
01831    }
01832 done:
01833    ast_module_user_remove(u);    
01834    return res;
01835 }
01836 
01837 static int dial_exec(struct ast_channel *chan, void *data)
01838 {
01839    struct ast_flags peerflags;
01840 
01841    memset(&peerflags, 0, sizeof(peerflags));
01842 
01843    return dial_exec_full(chan, data, &peerflags, NULL);
01844 }
01845 
01846 static int retrydial_exec(struct ast_channel *chan, void *data)
01847 {
01848    char *announce = NULL, *dialdata = NULL;
01849    const char *context = NULL;
01850    int sleep = 0, loops = 0, res = -1;
01851    struct ast_module_user *u;
01852    struct ast_flags peerflags;
01853    
01854    if (ast_strlen_zero(data)) {
01855       ast_log(LOG_WARNING, "RetryDial requires an argument!\n");
01856       return -1;
01857    }  
01858 
01859    u = ast_module_user_add(chan);
01860 
01861    announce = ast_strdupa(data);
01862 
01863    memset(&peerflags, 0, sizeof(peerflags));
01864 
01865    if ((dialdata = strchr(announce, '|'))) {
01866       *dialdata++ = '\0';
01867       if (sscanf(dialdata, "%30d", &sleep) == 1) {
01868          sleep *= 1000;
01869       } else {
01870          ast_log(LOG_ERROR, "%s requires the numerical argument <sleep>\n",rapp);
01871          goto done;
01872       }
01873       if ((dialdata = strchr(dialdata, '|'))) {
01874          *dialdata++ = '\0';
01875          if (sscanf(dialdata, "%30d", &loops) != 1) {
01876             ast_log(LOG_ERROR, "%s requires the numerical argument <loops>\n",rapp);
01877             goto done;
01878          }
01879       }
01880    }
01881    
01882    if (dialdata && (dialdata = strchr(dialdata, '|'))) {
01883       *dialdata++ = '\0';
01884    } else {
01885       ast_log(LOG_ERROR, "%s requires more arguments\n",rapp);
01886       goto done;
01887    }
01888       
01889    if (sleep < 1000)
01890       sleep = 10000;
01891 
01892    if (!loops)
01893       loops = -1; /* run forever */
01894    
01895    context = pbx_builtin_getvar_helper(chan, "EXITCONTEXT");
01896 
01897    res = 0;
01898    while (loops) {
01899       int continue_exec;
01900 
01901       chan->data = "Retrying";
01902       if (ast_test_flag(chan, AST_FLAG_MOH))
01903          ast_moh_stop(chan);
01904 
01905       res = dial_exec_full(chan, dialdata, &peerflags, &continue_exec);
01906       if (continue_exec)
01907          break;
01908 
01909       if (res == 0) {
01910          if (ast_test_flag(&peerflags, OPT_DTMF_EXIT)) {
01911             if (!ast_strlen_zero(announce)) {
01912                if (ast_fileexists(announce, NULL, chan->language) > 0) {
01913                   if(!(res = ast_streamfile(chan, announce, chan->language)))                      
01914                      ast_waitstream(chan, AST_DIGIT_ANY);
01915                } else
01916                   ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", announce);
01917             }
01918             if (!res && sleep) {
01919                if (!ast_test_flag(chan, AST_FLAG_MOH))
01920                   ast_moh_start(chan, NULL, NULL);
01921                res = ast_waitfordigit(chan, sleep);
01922             }
01923          } else {
01924             if (!ast_strlen_zero(announce)) {
01925                if (ast_fileexists(announce, NULL, chan->language) > 0) {
01926                   if (!(res = ast_streamfile(chan, announce, chan->language)))
01927                      res = ast_waitstream(chan, "");
01928                } else
01929                   ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", announce);
01930             }
01931             if (sleep) {
01932                if (!ast_test_flag(chan, AST_FLAG_MOH))
01933                   ast_moh_start(chan, NULL, NULL);
01934                if (!res)
01935                   res = ast_waitfordigit(chan, sleep);
01936             }
01937          }
01938       }
01939 
01940       if (res < 0)
01941          break;
01942       else if (res > 0) { /* Trying to send the call elsewhere (1 digit ext) */
01943          if (onedigit_goto(chan, context, (char) res, 1)) {
01944             res = 0;
01945             break;
01946          }
01947       }
01948       loops--;
01949    }
01950    if (loops == 0)
01951       res = 0;
01952    else if (res == 1)
01953       res = 0;
01954 
01955    if (ast_test_flag(chan, AST_FLAG_MOH))
01956       ast_moh_stop(chan);
01957  done:
01958    ast_module_user_remove(u);
01959    return res;
01960 }
01961 
01962 static int unload_module(void)
01963 {
01964    int res;
01965 
01966    res = ast_unregister_application(app);
01967    res |= ast_unregister_application(rapp);
01968 
01969    ast_module_user_hangup_all();
01970    
01971    return res;
01972 }
01973 
01974 static int load_module(void)
01975 {
01976    int res;
01977 
01978    res = ast_register_application(app, dial_exec, synopsis, descrip);
01979    res |= ast_register_application(rapp, retrydial_exec, rsynopsis, rdescrip);
01980    
01981    return res;
01982 }
01983 
01984 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialing Application");

Generated on Wed Oct 14 15:01:47 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7