Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:47 2009

## Asterisk developer's documentation

- Main Page
- Related Pages
- Modules
- Data Structures
- Files
- Directories

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