Thu Jul 9 13:40:17 2009

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

Generated on Thu Jul 9 13:40:17 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7