Mon Mar 31 07:37:54 2008

Asterisk developer's documentation


app.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, 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 Convenient Application Routines
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00029 
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <sys/time.h>
00034 #include <signal.h>
00035 #include <errno.h>
00036 #include <unistd.h>
00037 #include <dirent.h>
00038 #include <sys/types.h>
00039 #include <sys/stat.h>
00040 #include <regex.h>
00041 
00042 #include "asterisk/channel.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/file.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/dsp.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/options.h"
00049 #include "asterisk/utils.h"
00050 #include "asterisk/lock.h"
00051 #include "asterisk/indications.h"
00052 #include "asterisk/linkedlists.h"
00053 
00054 #define MAX_OTHER_FORMATS 10
00055 
00056 static AST_LIST_HEAD_STATIC(groups, ast_group_info);
00057 
00058 /* !
00059 This function presents a dialtone and reads an extension into 'collect' 
00060 which must be a pointer to a **pre-initialized** array of char having a 
00061 size of 'size' suitable for writing to.  It will collect no more than the smaller 
00062 of 'maxlen' or 'size' minus the original strlen() of collect digits.
00063 \return 0 if extension does not exist, 1 if extension exists
00064 */
00065 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout) 
00066 {
00067    struct tone_zone_sound *ts;
00068    int res=0, x=0;
00069 
00070    if (maxlen > size)
00071       maxlen = size;
00072    
00073    if (!timeout && chan->pbx)
00074       timeout = chan->pbx->dtimeout;
00075    else if (!timeout)
00076       timeout = 5;
00077    
00078    ts = ast_get_indication_tone(chan->zone,"dial");
00079    if (ts && ts->data[0])
00080       res = ast_playtones_start(chan, 0, ts->data, 0);
00081    else 
00082       ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
00083    
00084    for (x = strlen(collect); x < maxlen; ) {
00085       res = ast_waitfordigit(chan, timeout);
00086       if (!ast_ignore_pattern(context, collect))
00087          ast_playtones_stop(chan);
00088       if (res < 1)
00089          break;
00090       if (res == '#')
00091          break;
00092       collect[x++] = res;
00093       if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num))
00094          break;
00095    }
00096    if (res >= 0)
00097       res = ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num) ? 1 : 0;
00098    return res;
00099 }
00100 
00101 /*! \param c The channel to read from
00102  *  \param prompt The file to stream to the channel
00103  *  \param s The string to read in to.  Must be at least the size of your length
00104  *  \param maxlen How many digits to read (maximum)
00105  *  \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
00106  *      "ludicrous time" (essentially never times out) */
00107 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
00108 {
00109    int res=0,to,fto, result=0;
00110    /* XXX Merge with full version? XXX */
00111    if (maxlen)
00112       s[0] = '\0';
00113    if (prompt) {
00114                 char *front;
00115                 char *temp = ast_strdupa(prompt);
00116                 while(!res && (front = strsep(&temp, "&"))) {
00117                         if( (res = ast_streamfile(c, front, c->language)) ) {
00118             res = 0;
00119             break;
00120          }
00121          if(!res && !result)
00122             result = ast_waitstream(c, AST_DIGIT_ANY);
00123          if(result)
00124             break;
00125          ast_stopstream(c);
00126                 }
00127    }
00128    fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
00129    to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
00130 
00131    if (timeout > 0) 
00132       fto = to = timeout;
00133    if (timeout < 0) 
00134       fto = to = 1000000000;
00135    res = ast_readstring(c, s, maxlen, to, fto, "#");
00136    if(result) {
00137       char tmp[256];
00138       snprintf(tmp, sizeof(tmp), "%c%s", result, s);
00139       snprintf(s, sizeof(tmp), "%s", tmp);
00140    }
00141    return res;
00142 }
00143 
00144 
00145 int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
00146 {
00147    int res, to, fto;
00148    if (prompt) {
00149       res = ast_streamfile(c, prompt, c->language);
00150       if (res < 0)
00151          return res;
00152    }
00153    fto = 6000;
00154    to = 2000;
00155    if (timeout > 0) 
00156       fto = to = timeout;
00157    if (timeout < 0) 
00158       fto = to = 1000000000;
00159    res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
00160    return res;
00161 }
00162 
00163 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
00164 static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
00165 static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
00166 
00167 void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
00168                int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
00169                int (*messagecount_func)(const char *context, const char *mailbox, const char *folder))
00170 {
00171    ast_has_voicemail_func = has_voicemail_func;
00172    ast_inboxcount_func = inboxcount_func;
00173    ast_messagecount_func = messagecount_func;
00174 }
00175 
00176 void ast_uninstall_vm_functions(void)
00177 {
00178    ast_has_voicemail_func = NULL;
00179    ast_inboxcount_func = NULL;
00180    ast_messagecount_func = NULL;
00181 }
00182 
00183 int ast_app_has_voicemail(const char *mailbox, const char *folder)
00184 {
00185    static int warned = 0;
00186    if (ast_has_voicemail_func)
00187       return ast_has_voicemail_func(mailbox, folder);
00188 
00189    if ((option_verbose > 2) && !warned) {
00190       ast_verbose(VERBOSE_PREFIX_3 "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX");
00191       warned++;
00192    }
00193    return 0;
00194 }
00195 
00196 
00197 int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
00198 {
00199    static int warned = 0;
00200    if (newmsgs)
00201       *newmsgs = 0;
00202    if (oldmsgs)
00203       *oldmsgs = 0;
00204    if (ast_inboxcount_func)
00205       return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
00206 
00207    if (!warned && (option_verbose > 2)) {
00208       warned++;
00209       ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
00210    }
00211 
00212    return 0;
00213 }
00214 
00215 int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
00216 {
00217    static int warned = 0;
00218    if (ast_messagecount_func)
00219       return ast_messagecount_func(context, mailbox, folder);
00220 
00221    if (!warned && (option_verbose > 2)) {
00222       warned++;
00223       ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s@%s/%s but voicemail not loaded.\n", mailbox, context, folder);
00224    }
00225 
00226    return 0;
00227 }
00228 
00229 int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between) 
00230 {
00231    const char *ptr;
00232    int res = 0;
00233    struct ast_silence_generator *silgen = NULL;
00234 
00235    if (!between)
00236       between = 100;
00237 
00238    if (peer)
00239       res = ast_autoservice_start(peer);
00240 
00241    if (!res)
00242       res = ast_waitfor(chan, 100);
00243 
00244    /* ast_waitfor will return the number of remaining ms on success */
00245    if (res < 0)
00246       return res;
00247 
00248    if (ast_opt_transmit_silence) {
00249       silgen = ast_channel_start_silence_generator(chan);
00250    }
00251 
00252    for (ptr = digits; *ptr; ptr++) {
00253       if (*ptr == 'w') {
00254          /* 'w' -- wait half a second */
00255          if ((res = ast_safe_sleep(chan, 500)))
00256             break;
00257       } else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
00258          /* Character represents valid DTMF */
00259          if (*ptr == 'f' || *ptr == 'F') {
00260             /* ignore return values if not supported by channel */
00261             ast_indicate(chan, AST_CONTROL_FLASH);
00262          } else
00263             ast_senddigit(chan, *ptr);
00264          /* pause between digits */
00265          if ((res = ast_safe_sleep(chan, between)))
00266             break;
00267       } else
00268          ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
00269    }
00270 
00271    if (peer) {
00272       /* Stop autoservice on the peer channel, but don't overwrite any error condition 
00273          that has occurred previously while acting on the primary channel */
00274       if (ast_autoservice_stop(peer) && !res)
00275          res = -1;
00276    }
00277 
00278    if (silgen) {
00279       ast_channel_stop_silence_generator(chan, silgen);
00280    }
00281 
00282    return res;
00283 }
00284 
00285 struct linear_state {
00286    int fd;
00287    int autoclose;
00288    int allowoverride;
00289    int origwfmt;
00290 };
00291 
00292 static void linear_release(struct ast_channel *chan, void *params)
00293 {
00294    struct linear_state *ls = params;
00295    if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
00296       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
00297    }
00298    if (ls->autoclose)
00299       close(ls->fd);
00300    free(params);
00301 }
00302 
00303 static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
00304 {
00305    struct ast_frame f;
00306    short buf[2048 + AST_FRIENDLY_OFFSET / 2];
00307    struct linear_state *ls = data;
00308    int res;
00309    len = samples * 2;
00310    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00311       ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
00312       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00313    }
00314    memset(&f, 0, sizeof(f));
00315    res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
00316    if (res > 0) {
00317       f.frametype = AST_FRAME_VOICE;
00318       f.subclass = AST_FORMAT_SLINEAR;
00319       f.data = buf + AST_FRIENDLY_OFFSET/2;
00320       f.datalen = res;
00321       f.samples = res / 2;
00322       f.offset = AST_FRIENDLY_OFFSET;
00323       ast_write(chan, &f);
00324       if (res == len)
00325          return 0;
00326    }
00327    return -1;
00328 }
00329 
00330 static void *linear_alloc(struct ast_channel *chan, void *params)
00331 {
00332    struct linear_state *ls;
00333    /* In this case, params is already malloc'd */
00334    if (params) {
00335       ls = params;
00336       if (ls->allowoverride)
00337          ast_set_flag(chan, AST_FLAG_WRITE_INT);
00338       else
00339          ast_clear_flag(chan, AST_FLAG_WRITE_INT);
00340       ls->origwfmt = chan->writeformat;
00341       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00342          ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
00343          free(ls);
00344          ls = params = NULL;
00345       }
00346    }
00347    return params;
00348 }
00349 
00350 static struct ast_generator linearstream = 
00351 {
00352    alloc: linear_alloc,
00353    release: linear_release,
00354    generate: linear_generator,
00355 };
00356 
00357 int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
00358 {
00359    struct linear_state *lin;
00360    char tmpf[256];
00361    int res = -1;
00362    int autoclose = 0;
00363    if (fd < 0) {
00364       if (ast_strlen_zero(filename))
00365          return -1;
00366       autoclose = 1;
00367       if (filename[0] == '/') 
00368          ast_copy_string(tmpf, filename, sizeof(tmpf));
00369       else
00370          snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_DATA_DIR, "sounds", filename);
00371       fd = open(tmpf, O_RDONLY);
00372       if (fd < 0){
00373          ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
00374          return -1;
00375       }
00376    }
00377    if ((lin = ast_calloc(1, sizeof(*lin)))) {
00378       lin->fd = fd;
00379       lin->allowoverride = allowoverride;
00380       lin->autoclose = autoclose;
00381       res = ast_activate_generator(chan, &linearstream, lin);
00382    }
00383    return res;
00384 }
00385 
00386 int ast_control_streamfile(struct ast_channel *chan, const char *file,
00387             const char *fwd, const char *rev,
00388             const char *stop, const char *pause,
00389             const char *restart, int skipms) 
00390 {
00391    char *breaks = NULL;
00392    char *end = NULL;
00393    int blen = 2;
00394    int res;
00395    long pause_restart_point = 0;
00396 
00397    if (stop)
00398       blen += strlen(stop);
00399    if (pause)
00400       blen += strlen(pause);
00401    if (restart)
00402       blen += strlen(restart);
00403 
00404    if (blen > 2) {
00405       breaks = alloca(blen + 1);
00406       breaks[0] = '\0';
00407       if (stop)
00408          strcat(breaks, stop);
00409       if (pause)
00410          strcat(breaks, pause);
00411       if (restart)
00412          strcat(breaks, restart);
00413    }
00414    if (chan->_state != AST_STATE_UP)
00415       res = ast_answer(chan);
00416 
00417    if (file) {
00418       if ((end = strchr(file,':'))) {
00419          if (!strcasecmp(end, ":end")) {
00420             *end = '\0';
00421             end++;
00422          }
00423       }
00424    }
00425 
00426    for (;;) {
00427       ast_stopstream(chan);
00428       res = ast_streamfile(chan, file, chan->language);
00429       if (!res) {
00430          if (pause_restart_point) {
00431             ast_seekstream(chan->stream, pause_restart_point, SEEK_SET);
00432             pause_restart_point = 0;
00433          }
00434          else if (end) {
00435             ast_seekstream(chan->stream, 0, SEEK_END);
00436             end = NULL;
00437          };
00438          res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
00439       }
00440 
00441       if (res < 1)
00442          break;
00443 
00444       /* We go at next loop if we got the restart char */
00445       if (restart && strchr(restart, res)) {
00446          if (option_debug)
00447             ast_log(LOG_DEBUG, "we'll restart the stream here at next loop\n");
00448          pause_restart_point = 0;
00449          continue;
00450       }
00451 
00452       if (pause && strchr(pause, res)) {
00453          pause_restart_point = ast_tellstream(chan->stream);
00454          for (;;) {
00455             ast_stopstream(chan);
00456             res = ast_waitfordigit(chan, 1000);
00457             if (!res)
00458                continue;
00459             else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
00460                break;
00461          }
00462          if (res == *pause) {
00463             res = 0;
00464             continue;
00465          }
00466       }
00467 
00468       if (res == -1)
00469          break;
00470 
00471       /* if we get one of our stop chars, return it to the calling function */
00472       if (stop && strchr(stop, res))
00473          break;
00474    }
00475 
00476    /* If we are returning a digit cast it as char */
00477    if (res > 0 || chan->stream)
00478       res = (char)res;
00479 
00480    ast_stopstream(chan);
00481 
00482    return res;
00483 }
00484 
00485 int ast_play_and_wait(struct ast_channel *chan, const char *fn)
00486 {
00487    int d;
00488    d = ast_streamfile(chan, fn, chan->language);
00489    if (d)
00490       return d;
00491    d = ast_waitstream(chan, AST_DIGIT_ANY);
00492    ast_stopstream(chan);
00493    return d;
00494 }
00495 
00496 static int global_silence_threshold = 128;
00497 static int global_maxsilence = 0;
00498 
00499 /*! Optionally play a sound file or a beep, then record audio and video from the channel.
00500  * @param chan Channel to playback to/record from.
00501  * @param playfile Filename of sound to play before recording begins.
00502  * @param recordfile Filename to record to.
00503  * @param maxtime Maximum length of recording (in milliseconds).
00504  * @param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
00505  * @param duration Where to store actual length of the recorded message (in milliseconds).
00506  * @param beep Whether to play a beep before starting to record.
00507  * @param silencethreshold 
00508  * @param maxsilence Length of silence that will end a recording (in milliseconds).
00509  * @param path Optional filesystem path to unlock.
00510  * @param prepend If true, prepend the recorded audio to an existing file.
00511  * @param acceptdtmf DTMF digits that will end the recording.
00512  * @param canceldtmf DTMF digits that will cancel the recording.
00513  */
00514 
00515 static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf)
00516 {
00517    int d = 0;
00518    char *fmts;
00519    char comment[256];
00520    int x, fmtcnt = 1, res = -1, outmsg = 0;
00521    struct ast_filestream *others[MAX_OTHER_FORMATS];
00522    char *sfmt[MAX_OTHER_FORMATS];
00523    char *stringp = NULL;
00524    time_t start, end;
00525    struct ast_dsp *sildet = NULL;   /* silence detector dsp */
00526    int totalsilence = 0;
00527    int rfmt = 0;
00528    struct ast_silence_generator *silgen = NULL;
00529    char prependfile[80];
00530    int no_audio = 0;
00531 
00532    if (silencethreshold < 0)
00533       silencethreshold = global_silence_threshold;
00534 
00535    if (maxsilence < 0)
00536       maxsilence = global_maxsilence;
00537 
00538    /* barf if no pointer passed to store duration in */
00539    if (duration == NULL) {
00540       ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
00541       return -1;
00542    }
00543 
00544    if (option_debug)
00545       ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
00546    snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
00547 
00548    if (playfile || beep) {
00549       if (!beep)
00550          d = ast_play_and_wait(chan, playfile);
00551       if (d > -1)
00552          d = ast_stream_and_wait(chan, "beep", chan->language, "");
00553       if (d < 0)
00554          return -1;
00555    }
00556 
00557    if (prepend) {
00558       ast_copy_string(prependfile, recordfile, sizeof(prependfile)); 
00559       strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
00560    }
00561 
00562    fmts = ast_strdupa(fmt);
00563 
00564    stringp = fmts;
00565    strsep(&stringp, "|");
00566    if (option_debug)
00567       ast_log(LOG_DEBUG, "Recording Formats: sfmts=%s\n", fmts);
00568    sfmt[0] = ast_strdupa(fmts);
00569 
00570    while ((fmt = strsep(&stringp, "|"))) {
00571       if (fmtcnt > MAX_OTHER_FORMATS - 1) {
00572          ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
00573          break;
00574       }
00575       sfmt[fmtcnt++] = ast_strdupa(fmt);
00576    }
00577 
00578    end = start = time(NULL);  /* pre-initialize end to be same as start in case we never get into loop */
00579    for (x = 0; x < fmtcnt; x++) {
00580       others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, 0777);
00581       if (option_verbose > 2)
00582          ast_verbose(VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
00583 
00584       if (!others[x])
00585          break;
00586    }
00587 
00588    if (path)
00589       ast_unlock_path(path);
00590 
00591    if (maxsilence > 0) {
00592       sildet = ast_dsp_new(); /* Create the silence detector */
00593       if (!sildet) {
00594          ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00595          return -1;
00596       }
00597       ast_dsp_set_threshold(sildet, silencethreshold);
00598       rfmt = chan->readformat;
00599       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00600       if (res < 0) {
00601          ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00602          ast_dsp_free(sildet);
00603          return -1;
00604       }
00605    }
00606 
00607    if (!prepend) {
00608       /* Request a video update */
00609       ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00610 
00611       if (ast_opt_transmit_silence)
00612          silgen = ast_channel_start_silence_generator(chan);
00613    }
00614 
00615    if (x == fmtcnt) {
00616       /* Loop forever, writing the packets we read to the writer(s), until
00617          we read a digit or get a hangup */
00618       struct ast_frame *f;
00619       for (;;) {
00620          res = ast_waitfor(chan, 2000);
00621          if (!res) {
00622             if (option_debug)
00623                ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
00624             /* Try one more time in case of masq */
00625             no_audio = MAX(maxsilence, 4000);
00626             res = ast_waitfor(chan, no_audio - 2000);
00627             if (!res) {
00628                ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
00629                res = -1;
00630             } else {
00631                no_audio = 0;              
00632             }
00633          }
00634 
00635          if (res < 0) {
00636             f = NULL;
00637             break;
00638          }
00639          f = ast_read(chan);
00640          if (!f)
00641             break;
00642          if (f->frametype == AST_FRAME_VOICE) {
00643             /* write each format */
00644             for (x = 0; x < fmtcnt; x++) {
00645                if (prepend && !others[x])
00646                   break;
00647                res = ast_writestream(others[x], f);
00648             }
00649 
00650             /* Silence Detection */
00651             if (maxsilence > 0) {
00652                int dspsilence = 0;
00653                ast_dsp_silence(sildet, f, &dspsilence);
00654                if (dspsilence)
00655                   totalsilence = dspsilence;
00656                else
00657                   totalsilence = 0;
00658 
00659                if (totalsilence > maxsilence) {
00660                   /* Ended happily with silence */
00661                   if (option_verbose > 2)
00662                      ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
00663                   res = 'S';
00664                   outmsg = 2;
00665                   break;
00666                }
00667             }
00668             /* Exit on any error */
00669             if (res) {
00670                ast_log(LOG_WARNING, "Error writing frame\n");
00671                break;
00672             }
00673          } else if (f->frametype == AST_FRAME_VIDEO) {
00674             /* Write only once */
00675             ast_writestream(others[0], f);
00676          } else if (f->frametype == AST_FRAME_DTMF) {
00677             if (prepend) {
00678             /* stop recording with any digit */
00679                if (option_verbose > 2) 
00680                   ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
00681                res = 't';
00682                outmsg = 2;
00683                break;
00684             }
00685             if (strchr(acceptdtmf, f->subclass)) {
00686                if (option_verbose > 2)
00687                   ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
00688                res = f->subclass;
00689                outmsg = 2;
00690                break;
00691             }
00692             if (strchr(canceldtmf, f->subclass)) {
00693                if (option_verbose > 2)
00694                   ast_verbose(VERBOSE_PREFIX_3 "User cancelled message by pressing %c\n", f->subclass);
00695                res = f->subclass;
00696                outmsg = 0;
00697                break;
00698             }
00699          }
00700          if (maxtime) {
00701             end = time(NULL);
00702             if (maxtime < (end - start)) {
00703                if (option_verbose > 2)
00704                   ast_verbose(VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
00705                res = 't';
00706                outmsg = 2;
00707                break;
00708             }
00709          }
00710          ast_frfree(f);
00711       }
00712       if (!f) {
00713          if (option_verbose > 2)
00714             ast_verbose(VERBOSE_PREFIX_3 "User hung up\n");
00715          res = -1;
00716          outmsg = 1;
00717       } else {
00718          ast_frfree(f);
00719       }
00720    } else {
00721       ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
00722    }
00723 
00724    if (!prepend) {
00725       if (silgen)
00726          ast_channel_stop_silence_generator(chan, silgen);
00727    }
00728 
00729    /*!\note
00730     * Instead of asking how much time passed (end - start), calculate the number
00731     * of seconds of audio which actually went into the file.  This fixes a
00732     * problem where audio is stopped up on the network and never gets to us.
00733     *
00734     * Note that we still want to use the number of seconds passed for the max
00735     * message, otherwise we could get a situation where this stream is never
00736     * closed (which would create a resource leak).
00737     */
00738    *duration = others[0] ? ast_tellstream(others[0]) / 8000 : 0;
00739    if (no_audio) {
00740       *duration -= no_audio / 1000;
00741       *duration = MAX(*duration, 0);  /* just to make sure it does not get negative */
00742    }
00743 
00744    if (!prepend) {
00745       for (x = 0; x < fmtcnt; x++) {
00746          if (!others[x])
00747             break;
00748          /*!\note
00749           * If we ended with silence, trim all but the first 200ms of silence
00750           * off the recording.  However, if we ended with '#', we don't want
00751           * to trim ANY part of the recording.
00752           */
00753          if (res > 0 && totalsilence)
00754             ast_stream_rewind(others[x], totalsilence - 200);
00755          ast_truncstream(others[x]);
00756          ast_closestream(others[x]);
00757       }
00758    }
00759 
00760    if (prepend && outmsg) {
00761       struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
00762       struct ast_frame *fr;
00763 
00764       for (x = 0; x < fmtcnt; x++) {
00765          snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
00766          realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
00767          if (!others[x] || !realfiles[x])
00768             break;
00769          /*!\note Same logic as above. */
00770          if (totalsilence)
00771             ast_stream_rewind(others[x], totalsilence - 200);
00772          ast_truncstream(others[x]);
00773          /* add the original file too */
00774          while ((fr = ast_readframe(realfiles[x]))) {
00775             ast_writestream(others[x], fr);
00776             ast_frfree(fr);
00777          }
00778          ast_closestream(others[x]);
00779          ast_closestream(realfiles[x]);
00780          ast_filerename(prependfile, recordfile, sfmt[x]);
00781          if (option_verbose > 3)
00782             ast_verbose(VERBOSE_PREFIX_4 "Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x], prependfile, recordfile);
00783          ast_filedelete(prependfile, sfmt[x]);
00784       }
00785    }
00786    if (rfmt && ast_set_read_format(chan, rfmt)) {
00787       ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
00788    }
00789    if (outmsg == 2) {
00790       ast_stream_and_wait(chan, "auth-thankyou", chan->language, "");
00791    }
00792    if (sildet)
00793       ast_dsp_free(sildet);
00794    return res;
00795 }
00796 
00797 static char default_acceptdtmf[] = "#";
00798 static char default_canceldtmf[] = "";
00799 
00800 int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
00801 {
00802    return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf));
00803 }
00804 
00805 int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
00806 {
00807    return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf);
00808 }
00809 
00810 int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
00811 {
00812    return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf);
00813 }
00814 
00815 /* Channel group core functions */
00816 
00817 int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max)
00818 {
00819    int res=0;
00820    char tmp[256];
00821    char *grp=NULL, *cat=NULL;
00822 
00823    if (!ast_strlen_zero(data)) {
00824       ast_copy_string(tmp, data, sizeof(tmp));
00825       grp = tmp;
00826       cat = strchr(tmp, '@');
00827       if (cat) {
00828          *cat = '\0';
00829          cat++;
00830       }
00831    }
00832 
00833    if (!ast_strlen_zero(grp))
00834       ast_copy_string(group, grp, group_max);
00835    else
00836       *group = '\0';
00837 
00838    if (!ast_strlen_zero(cat))
00839       ast_copy_string(category, cat, category_max);
00840 
00841    return res;
00842 }
00843 
00844 int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
00845 {
00846    int res = 0;
00847    char group[80] = "", category[80] = "";
00848    struct ast_group_info *gi = NULL;
00849    size_t len = 0;
00850    
00851    if (ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category)))
00852       return -1;
00853    
00854    /* Calculate memory we will need if this is new */
00855    len = sizeof(*gi) + strlen(group) + 1;
00856    if (!ast_strlen_zero(category))
00857       len += strlen(category) + 1;
00858    
00859    AST_LIST_LOCK(&groups);
00860    AST_LIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
00861       if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
00862          AST_LIST_REMOVE_CURRENT(&groups, list);
00863          free(gi);
00864          break;
00865       }
00866    }
00867    AST_LIST_TRAVERSE_SAFE_END
00868 
00869    if (ast_strlen_zero(group)) {
00870       /* Enable unsetting the group */
00871    } else if ((gi = calloc(1, len))) {
00872       gi->chan = chan;
00873       gi->group = (char *) gi + sizeof(*gi);
00874       strcpy(gi->group, group);
00875       if (!ast_strlen_zero(category)) {
00876          gi->category = (char *) gi + sizeof(*gi) + strlen(group) + 1;
00877          strcpy(gi->category, category);
00878       }
00879       AST_LIST_INSERT_TAIL(&groups, gi, list);
00880    } else {
00881       res = -1;
00882    }
00883    
00884    AST_LIST_UNLOCK(&groups);
00885    
00886    return res;
00887 }
00888 
00889 int ast_app_group_get_count(const char *group, const char *category)
00890 {
00891    struct ast_group_info *gi = NULL;
00892    int count = 0;
00893 
00894    if (ast_strlen_zero(group))
00895       return 0;
00896    
00897    AST_LIST_LOCK(&groups);
00898    AST_LIST_TRAVERSE(&groups, gi, list) {
00899       if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
00900          count++;
00901    }
00902    AST_LIST_UNLOCK(&groups);
00903 
00904    return count;
00905 }
00906 
00907 int ast_app_group_match_get_count(const char *groupmatch, const char *category)
00908 {
00909    struct ast_group_info *gi = NULL;
00910    regex_t regexbuf;
00911    int count = 0;
00912 
00913    if (ast_strlen_zero(groupmatch))
00914       return 0;
00915 
00916    /* if regex compilation fails, return zero matches */
00917    if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
00918       return 0;
00919 
00920    AST_LIST_LOCK(&groups);
00921    AST_LIST_TRAVERSE(&groups, gi, list) {
00922       if (!regexec(&regexbuf, gi->group, 0, NULL, 0) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
00923          count++;
00924    }
00925    AST_LIST_UNLOCK(&groups);
00926 
00927    regfree(&regexbuf);
00928 
00929    return count;
00930 }
00931 
00932 int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)
00933 {
00934    struct ast_group_info *gi = NULL;
00935 
00936    AST_LIST_LOCK(&groups);
00937    AST_LIST_TRAVERSE(&groups, gi, list) {
00938       if (gi->chan == old)
00939          gi->chan = new;
00940    }
00941    AST_LIST_UNLOCK(&groups);
00942 
00943    return 0;
00944 }
00945 
00946 int ast_app_group_discard(struct ast_channel *chan)
00947 {
00948    struct ast_group_info *gi = NULL;
00949    
00950    AST_LIST_LOCK(&groups);
00951    AST_LIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
00952       if (gi->chan == chan) {
00953          AST_LIST_REMOVE_CURRENT(&groups, list);
00954          free(gi);
00955       }
00956    }
00957         AST_LIST_TRAVERSE_SAFE_END
00958    AST_LIST_UNLOCK(&groups);
00959    
00960    return 0;
00961 }
00962 
00963 int ast_app_group_list_lock(void)
00964 {
00965    return AST_LIST_LOCK(&groups);
00966 }
00967 
00968 struct ast_group_info *ast_app_group_list_head(void)
00969 {
00970    return AST_LIST_FIRST(&groups);
00971 }
00972 
00973 int ast_app_group_list_unlock(void)
00974 {
00975    return AST_LIST_UNLOCK(&groups);
00976 }
00977 
00978 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
00979 {
00980    int argc;
00981    char *scan;
00982    int paren = 0, quote = 0;
00983 
00984    if (!buf || !array || !arraylen)
00985       return 0;
00986 
00987    memset(array, 0, arraylen * sizeof(*array));
00988 
00989    scan = buf;
00990 
00991    for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
00992       array[argc] = scan;
00993       for (; *scan; scan++) {
00994          if (*scan == '(')
00995             paren++;
00996          else if (*scan == ')') {
00997             if (paren)
00998                paren--;
00999          } else if (*scan == '"' && delim != '"') {
01000             quote = quote ? 0 : 1;
01001             /* Remove quote character from argument */
01002             memmove(scan, scan + 1, strlen(scan));
01003             scan--;
01004          } else if (*scan == '\\') {
01005             /* Literal character, don't parse */
01006             memmove(scan, scan + 1, strlen(scan));
01007          } else if ((*scan == delim) && !paren && !quote) {
01008             *scan++ = '\0';
01009             break;
01010          }
01011       }
01012    }
01013 
01014    if (*scan)
01015       array[argc++] = scan;
01016 
01017    return argc;
01018 }
01019 
01020 enum AST_LOCK_RESULT ast_lock_path(const char *path)
01021 {
01022    char *s;
01023    char *fs;
01024    int res;
01025    int fd;
01026    int lp = strlen(path);
01027    time_t start;
01028 
01029    if (!(s = alloca(lp + 10)) || !(fs = alloca(lp + 20))) {
01030       ast_log(LOG_WARNING, "Out of memory!\n");
01031       return AST_LOCK_FAILURE;
01032    }
01033 
01034    snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
01035    fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
01036    if (fd < 0) {
01037       ast_log(LOG_ERROR, "Unable to create lock file '%s': %s\n", path, strerror(errno));
01038       return AST_LOCK_PATH_NOT_FOUND;
01039    }
01040    close(fd);
01041 
01042    snprintf(s, strlen(path) + 9, "%s/.lock", path);
01043    start = time(NULL);
01044    while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
01045       usleep(1);
01046 
01047    unlink(fs);
01048 
01049    if (res) {
01050       ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
01051       return AST_LOCK_TIMEOUT;
01052    } else {
01053       if (option_debug)
01054          ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
01055       return AST_LOCK_SUCCESS;
01056    }
01057 }
01058 
01059 int ast_unlock_path(const char *path)
01060 {
01061    char *s;
01062    int res;
01063 
01064    if (!(s = alloca(strlen(path) + 10))) {
01065       ast_log(LOG_WARNING, "Out of memory!\n");
01066       return -1;
01067    }
01068 
01069    snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
01070 
01071    if ((res = unlink(s)))
01072       ast_log(LOG_ERROR, "Could not unlock path '%s': %s\n", path, strerror(errno));
01073    else {
01074       if (option_debug)
01075          ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
01076    }
01077 
01078    return res;
01079 }
01080 
01081 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path) 
01082 {
01083    int silencethreshold = 128; 
01084    int maxsilence=0;
01085    int res = 0;
01086    int cmd = 0;
01087    int max_attempts = 3;
01088    int attempts = 0;
01089    int recorded = 0;
01090    int message_exists = 0;
01091    /* Note that urgent and private are for flagging messages as such in the future */
01092 
01093    /* barf if no pointer passed to store duration in */
01094    if (duration == NULL) {
01095       ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
01096       return -1;
01097    }
01098 
01099    cmd = '3';   /* Want to start by recording */
01100 
01101    while ((cmd >= 0) && (cmd != 't')) {
01102       switch (cmd) {
01103       case '1':
01104          if (!message_exists) {
01105             /* In this case, 1 is to record a message */
01106             cmd = '3';
01107             break;
01108          } else {
01109             ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
01110             cmd = 't';
01111             return res;
01112          }
01113       case '2':
01114          /* Review */
01115          ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
01116          cmd = ast_stream_and_wait(chan, recordfile, chan->language, AST_DIGIT_ANY);
01117          break;
01118       case '3':
01119          message_exists = 0;
01120          /* Record */
01121          if (recorded == 1)
01122             ast_verbose(VERBOSE_PREFIX_3 "Re-recording\n");
01123          else  
01124             ast_verbose(VERBOSE_PREFIX_3 "Recording\n");
01125          recorded = 1;
01126          cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path);
01127          if (cmd == -1) {
01128          /* User has hung up, no options to give */
01129             return cmd;
01130          }
01131          if (cmd == '0') {
01132             break;
01133          } else if (cmd == '*') {
01134             break;
01135          } 
01136          else {
01137             /* If all is well, a message exists */
01138             message_exists = 1;
01139             cmd = 0;
01140          }
01141          break;
01142       case '4':
01143       case '5':
01144       case '6':
01145       case '7':
01146       case '8':
01147       case '9':
01148       case '*':
01149       case '#':
01150          cmd = ast_play_and_wait(chan, "vm-sorry");
01151          break;
01152       default:
01153          if (message_exists) {
01154             cmd = ast_play_and_wait(chan, "vm-review");
01155          }
01156          else {
01157             cmd = ast_play_and_wait(chan, "vm-torerecord");
01158             if (!cmd)
01159                cmd = ast_waitfordigit(chan, 600);
01160          }
01161          
01162          if (!cmd)
01163             cmd = ast_waitfordigit(chan, 6000);
01164          if (!cmd) {
01165             attempts++;
01166          }
01167          if (attempts > max_attempts) {
01168             cmd = 't';
01169          }
01170       }
01171    }
01172    if (cmd == 't')
01173       cmd = 0;
01174    return cmd;
01175 }
01176 
01177 #define RES_UPONE (1 << 16)
01178 #define RES_EXIT  (1 << 17)
01179 #define RES_REPEAT (1 << 18)
01180 #define RES_RESTART ((1 << 19) | RES_REPEAT)
01181 
01182 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
01183 
01184 static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
01185 {
01186    int res;
01187    int (*ivr_func)(struct ast_channel *, void *);
01188    char *c;
01189    char *n;
01190    
01191    switch(option->action) {
01192    case AST_ACTION_UPONE:
01193       return RES_UPONE;
01194    case AST_ACTION_EXIT:
01195       return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
01196    case AST_ACTION_REPEAT:
01197       return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
01198    case AST_ACTION_RESTART:
01199       return RES_RESTART ;
01200    case AST_ACTION_NOOP:
01201       return 0;
01202    case AST_ACTION_BACKGROUND:
01203       res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, AST_DIGIT_ANY);
01204       if (res < 0) {
01205          ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
01206          res = 0;
01207       }
01208       return res;
01209    case AST_ACTION_PLAYBACK:
01210       res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, "");
01211       if (res < 0) {
01212          ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
01213          res = 0;
01214       }
01215       return res;
01216    case AST_ACTION_MENU:
01217       res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
01218       /* Do not pass entry errors back up, treaat ast though ti was an "UPONE" */
01219       if (res == -2)
01220          res = 0;
01221       return res;
01222    case AST_ACTION_WAITOPTION:
01223       res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
01224       if (!res)
01225          return 't';
01226       return res;
01227    case AST_ACTION_CALLBACK:
01228       ivr_func = option->adata;
01229       res = ivr_func(chan, cbdata);
01230       return res;
01231    case AST_ACTION_TRANSFER:
01232       res = ast_parseable_goto(chan, option->adata);
01233       return 0;
01234    case AST_ACTION_PLAYLIST:
01235    case AST_ACTION_BACKLIST:
01236       res = 0;
01237       c = ast_strdupa(option->adata);
01238       while ((n = strsep(&c, ";"))) {
01239          if ((res = ast_stream_and_wait(chan, n, chan->language,
01240                (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
01241             break;
01242       }
01243       ast_stopstream(chan);
01244       return res;
01245    default:
01246       ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
01247       return 0;
01248    };
01249    return -1;
01250 }
01251 
01252 static int option_exists(struct ast_ivr_menu *menu, char *option)
01253 {
01254    int x;
01255    for (x = 0; menu->options[x].option; x++)
01256       if (!strcasecmp(menu->options[x].option, option))
01257          return x;
01258    return -1;
01259 }
01260 
01261 static int option_matchmore(struct ast_ivr_menu *menu, char *option)
01262 {
01263    int x;
01264    for (x = 0; menu->options[x].option; x++)
01265       if ((!strncasecmp(menu->options[x].option, option, strlen(option))) && 
01266             (menu->options[x].option[strlen(option)]))
01267          return x;
01268    return -1;
01269 }
01270 
01271 static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
01272 {
01273    int res=0;
01274    int ms;
01275    while (option_matchmore(menu, exten)) {
01276       ms = chan->pbx ? chan->pbx->dtimeout : 5000;
01277       if (strlen(exten) >= maxexten - 1) 
01278          break;
01279       res = ast_waitfordigit(chan, ms);
01280       if (res < 1)
01281          break;
01282       exten[strlen(exten) + 1] = '\0';
01283       exten[strlen(exten)] = res;
01284    }
01285    return res > 0 ? 0 : res;
01286 }
01287 
01288 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
01289 {
01290    /* Execute an IVR menu structure */
01291    int res=0;
01292    int pos = 0;
01293    int retries = 0;
01294    char exten[AST_MAX_EXTENSION] = "s";
01295    if (option_exists(menu, "s") < 0) {
01296       strcpy(exten, "g");
01297       if (option_exists(menu, "g") < 0) {
01298          ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
01299          return -1;
01300       }
01301    }
01302    while(!res) {
01303       while(menu->options[pos].option) {
01304          if (!strcasecmp(menu->options[pos].option, exten)) {
01305             res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
01306             if (option_debug)
01307                ast_log(LOG_DEBUG, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
01308             if (res < 0)
01309                break;
01310             else if (res & RES_UPONE)
01311                return 0;
01312             else if (res & RES_EXIT)
01313                return res;
01314             else if (res & RES_REPEAT) {
01315                int maxretries = res & 0xffff;
01316                if ((res & RES_RESTART) == RES_RESTART) {
01317                   retries = 0;
01318                } else
01319                   retries++;
01320                if (!maxretries)
01321                   maxretries = 3;
01322                if ((maxretries > 0) && (retries >= maxretries)) {
01323                   if (option_debug)
01324                      ast_log(LOG_DEBUG, "Max retries %d exceeded\n", maxretries);
01325                   return -2;
01326                } else {
01327                   if (option_exists(menu, "g") > -1) 
01328                      strcpy(exten, "g");
01329                   else if (option_exists(menu, "s") > -1)
01330                      strcpy(exten, "s");
01331                }
01332                pos = 0;
01333                continue;
01334             } else if (res && strchr(AST_DIGIT_ANY, res)) {
01335                if (option_debug)
01336                   ast_log(LOG_DEBUG, "Got start of extension, %c\n", res);
01337                exten[1] = '\0';
01338                exten[0] = res;
01339                if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
01340                   break;
01341                if (option_exists(menu, exten) < 0) {
01342                   if (option_exists(menu, "i")) {
01343                      if (option_debug)
01344                         ast_log(LOG_DEBUG, "Invalid extension entered, going to 'i'!\n");
01345                      strcpy(exten, "i");
01346                      pos = 0;
01347                      continue;
01348                   } else {
01349                      if (option_debug)
01350                         ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
01351                      res = -2;
01352                      break;
01353                   }
01354                } else {
01355                   if (option_debug)
01356                      ast_log(LOG_DEBUG, "New existing extension: %s\n", exten);
01357                   pos = 0;
01358                   continue;
01359                }
01360             }
01361          }
01362          pos++;
01363       }
01364       if (option_debug)
01365          ast_log(LOG_DEBUG, "Stopping option '%s', res is %d\n", exten, res);
01366       pos = 0;
01367       if (!strcasecmp(exten, "s"))
01368          strcpy(exten, "g");
01369       else
01370          break;
01371    }
01372    return res;
01373 }
01374 
01375 int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
01376 {
01377    int res = ast_ivr_menu_run_internal(chan, menu, cbdata);
01378    /* Hide internal coding */
01379    return res > 0 ? 0 : res;
01380 }
01381    
01382 char *ast_read_textfile(const char *filename)
01383 {
01384    int fd;
01385    char *output = NULL;
01386    struct stat filesize;
01387    int count = 0;
01388    int res;
01389    if (stat(filename, &filesize) == -1) {
01390       ast_log(LOG_WARNING, "Error can't stat %s\n", filename);
01391       return NULL;
01392    }
01393    count = filesize.st_size + 1;
01394    fd = open(filename, O_RDONLY);
01395    if (fd < 0) {
01396       ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
01397       return NULL;
01398    }
01399    if ((output = ast_malloc(count))) {
01400       res = read(fd, output, count - 1);
01401       if (res == count - 1) {
01402          output[res] = '\0';
01403       } else {
01404          ast_log(LOG_WARNING, "Short read of %s (%d of %d): %s\n", filename, res, count - 1, strerror(errno));
01405          free(output);
01406          output = NULL;
01407       }
01408    }
01409    close(fd);
01410    return output;
01411 }
01412 
01413 int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
01414 {
01415    char *s;
01416    int curarg;
01417    unsigned int argloc;
01418    char *arg;
01419    int res = 0;
01420 
01421    ast_clear_flag(flags, AST_FLAGS_ALL);
01422 
01423    if (!optstr)
01424       return 0;
01425 
01426    s = optstr;
01427    while (*s) {
01428       curarg = *s++ & 0x7f;   /* the array (in app.h) has 128 entries */
01429       argloc = options[curarg].arg_index;
01430       if (*s == '(') {
01431          /* Has argument */
01432          arg = ++s;
01433          if ((s = strchr(s, ')'))) {
01434             if (argloc)
01435                args[argloc - 1] = arg;
01436             *s++ = '\0';
01437          } else {
01438             ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
01439             res = -1;
01440             break;
01441          }
01442       } else if (argloc) {
01443          args[argloc - 1] = "";
01444       }
01445       ast_set_flag(flags, options[curarg].flag);
01446    }
01447 
01448    return res;
01449 }
01450 

Generated on Mon Mar 31 07:37:54 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1