Wed Aug 18 22:33:40 2010

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

Generated on Wed Aug 18 22:33:40 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7