Fri Aug 29 06:37:31 2008

Asterisk developer's documentation


app.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Convenient Application Routines
00022  * 
00023  */
00024 
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <sys/time.h>
00029 #include <signal.h>
00030 #include <errno.h>
00031 #include <unistd.h>
00032 #include <dirent.h>
00033 #include <sys/types.h>
00034 #include <sys/stat.h>
00035 #include <regex.h>
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 74264 $")
00040 
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/file.h"
00044 #include "asterisk/app.h"
00045 #include "asterisk/dsp.h"
00046 #include "asterisk/logger.h"
00047 #include "asterisk/options.h"
00048 #include "asterisk/utils.h"
00049 #include "asterisk/lock.h"
00050 #include "asterisk/indications.h"
00051 #include "asterisk/linkedlists.h"
00052 
00053 #define MAX_OTHER_FORMATS 10
00054 
00055 static AST_LIST_HEAD_STATIC(groups, ast_group_info);
00056 
00057 /* !
00058 This function presents a dialtone and reads an extension into 'collect' 
00059 which must be a pointer to a **pre-initilized** array of char having a 
00060 size of 'size' suitable for writing to.  It will collect no more than the smaller 
00061 of 'maxlen' or 'size' minus the original strlen() of collect digits.
00062 \return 0 if extension does not exist, 1 if extension exists
00063 */
00064 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout) 
00065 {
00066    struct tone_zone_sound *ts;
00067    int res=0, x=0;
00068 
00069    if(maxlen > size)
00070       maxlen = size;
00071    
00072    if(!timeout && chan->pbx)
00073       timeout = chan->pbx->dtimeout;
00074    else if(!timeout)
00075       timeout = 5;
00076    
00077    ts = ast_get_indication_tone(chan->zone,"dial");
00078    if (ts && ts->data[0])
00079       res = ast_playtones_start(chan, 0, ts->data, 0);
00080    else 
00081       ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
00082    
00083    for (x = strlen(collect); strlen(collect) < maxlen; ) {
00084       res = ast_waitfordigit(chan, timeout);
00085       if (!ast_ignore_pattern(context, collect))
00086          ast_playtones_stop(chan);
00087       if (res < 1)
00088          break;
00089       if (res == '#')
00090          break;
00091       collect[x++] = res;
00092       if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num))
00093          break;
00094    }
00095    if (res >= 0) {
00096       if (ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num))
00097          res = 1;
00098       else
00099          res = 0;
00100    }
00101    return res;
00102 }
00103 
00104 
00105 
00106 /*! \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
00107    "ludicrous time" (essentially never times out) */
00108 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
00109 {
00110    int res,to,fto;
00111    /* XXX Merge with full version? XXX */
00112    if (maxlen)
00113       s[0] = '\0';
00114    if (prompt) {
00115       res = ast_streamfile(c, prompt, c->language);
00116       if (res < 0)
00117          return res;
00118    }
00119    fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
00120    to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
00121 
00122    if (timeout > 0) 
00123       fto = to = timeout;
00124    if (timeout < 0) 
00125       fto = to = 1000000000;
00126    res = ast_readstring(c, s, maxlen, to, fto, "#");
00127    return res;
00128 }
00129 
00130 
00131 int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
00132 {
00133    int res,to,fto;
00134    if (prompt) {
00135       res = ast_streamfile(c, prompt, c->language);
00136       if (res < 0)
00137          return res;
00138    }
00139    fto = 6000;
00140    to = 2000;
00141    if (timeout > 0) 
00142       fto = to = timeout;
00143    if (timeout < 0) 
00144       fto = to = 1000000000;
00145    res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
00146    return res;
00147 }
00148 
00149 int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec)
00150 {
00151    int res;
00152    struct ast_filestream *writer;
00153    int rfmt;
00154    int totalms=0, total;
00155    
00156    struct ast_frame *f;
00157    struct ast_dsp *sildet;
00158    /* Play prompt if requested */
00159    if (prompt) {
00160       res = ast_streamfile(c, prompt, c->language);
00161       if (res < 0)
00162          return res;
00163       res = ast_waitstream(c,"");
00164       if (res < 0)
00165          return res;
00166    }
00167    rfmt = c->readformat;
00168    res = ast_set_read_format(c, AST_FORMAT_SLINEAR);
00169    if (res < 0) {
00170       ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00171       return -1;
00172    }
00173    sildet = ast_dsp_new();
00174    if (!sildet) {
00175       ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00176       return -1;
00177    }
00178    writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666);
00179    if (!writer) {
00180       ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt);
00181       ast_dsp_free(sildet);
00182       return -1;
00183    }
00184    for(;;) {
00185       if ((res = ast_waitfor(c, 2000)) < 0) {
00186          ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt);
00187          break;
00188       }
00189       if (res) {
00190          f = ast_read(c);
00191          if (!f) {
00192             ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt);
00193             break;
00194          }
00195          if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
00196             /* Ended happily with DTMF */
00197             ast_frfree(f);
00198             break;
00199          } else if (f->frametype == AST_FRAME_VOICE) {
00200             ast_dsp_silence(sildet, f, &total); 
00201             if (total > silence) {
00202                /* Ended happily with silence */
00203                ast_frfree(f);
00204                break;
00205             }
00206             totalms += f->samples / 8;
00207             if (totalms > maxsec * 1000) {
00208                /* Ended happily with too much stuff */
00209                ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec);
00210                ast_frfree(f);
00211                break;
00212             }
00213             res = ast_writestream(writer, f);
00214             if (res < 0) {
00215                ast_log(LOG_WARNING, "Failed to write to stream at %s!\n", dest);
00216                ast_frfree(f);
00217                break;
00218             }
00219                
00220          }
00221          ast_frfree(f);
00222       }
00223    }
00224    res = ast_set_read_format(c, rfmt);
00225    if (res)
00226       ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name);
00227    ast_dsp_free(sildet);
00228    ast_closestream(writer);
00229    return 0;
00230 }
00231 
00232 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
00233 static int (*ast_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
00234 
00235 void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
00236                int (*messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs))
00237 {
00238    ast_has_voicemail_func = has_voicemail_func;
00239    ast_messagecount_func = messagecount_func;
00240 }
00241 
00242 void ast_uninstall_vm_functions(void)
00243 {
00244    ast_has_voicemail_func = NULL;
00245    ast_messagecount_func = NULL;
00246 }
00247 
00248 int ast_app_has_voicemail(const char *mailbox, const char *folder)
00249 {
00250    static int warned = 0;
00251    if (ast_has_voicemail_func)
00252       return ast_has_voicemail_func(mailbox, folder);
00253 
00254    if ((option_verbose > 2) && !warned) {
00255       ast_verbose(VERBOSE_PREFIX_3 "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX");
00256       warned++;
00257    }
00258    return 0;
00259 }
00260 
00261 
00262 int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
00263 {
00264    static int warned = 0;
00265    if (newmsgs)
00266       *newmsgs = 0;
00267    if (oldmsgs)
00268       *oldmsgs = 0;
00269    if (ast_messagecount_func)
00270       return ast_messagecount_func(mailbox, newmsgs, oldmsgs);
00271 
00272    if (!warned && (option_verbose > 2)) {
00273       warned++;
00274       ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
00275    }
00276 
00277    return 0;
00278 }
00279 
00280 int ast_dtmf_stream(struct ast_channel *chan,struct ast_channel *peer,char *digits,int between) 
00281 {
00282    char *ptr;
00283    int res = 0;
00284    struct ast_frame f;
00285    if (!between)
00286       between = 100;
00287 
00288    if (peer)
00289       res = ast_autoservice_start(peer);
00290 
00291    if (!res) {
00292       res = ast_waitfor(chan,100);
00293       if (res > -1) {
00294          for (ptr=digits; *ptr; ptr++) {
00295             if (*ptr == 'w') {
00296                res = ast_safe_sleep(chan, 500);
00297                if (res) 
00298                   break;
00299                continue;
00300             }
00301             memset(&f, 0, sizeof(f));
00302             f.frametype = AST_FRAME_DTMF;
00303             f.subclass = *ptr;
00304             f.src = "ast_dtmf_stream";
00305             if (strchr("0123456789*#abcdABCD",*ptr)==NULL) {
00306                ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
00307             } else {
00308                res = ast_write(chan, &f);
00309                if (res) 
00310                   break;
00311                /* pause between digits */
00312                res = ast_safe_sleep(chan,between);
00313                if (res) 
00314                   break;
00315             }
00316          }
00317       }
00318       if (peer) {
00319          /* Stop autoservice on the peer channel, but don't overwrite any error condition 
00320             that has occurred previously while acting on the primary channel */  
00321          if (ast_autoservice_stop(peer) && !res)
00322             res = -1;
00323       }
00324    }
00325    return res;
00326 }
00327 
00328 struct linear_state {
00329    int fd;
00330    int autoclose;
00331    int allowoverride;
00332    int origwfmt;
00333 };
00334 
00335 static void linear_release(struct ast_channel *chan, void *params)
00336 {
00337    struct linear_state *ls = params;
00338    if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
00339       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
00340    }
00341    if (ls->autoclose)
00342       close(ls->fd);
00343    free(params);
00344 }
00345 
00346 static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
00347 {
00348    struct ast_frame f;
00349    short buf[2048 + AST_FRIENDLY_OFFSET / 2];
00350    struct linear_state *ls = data;
00351    int res;
00352    len = samples * 2;
00353    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00354       ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
00355       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00356    }
00357    memset(&f, 0, sizeof(f));
00358    res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
00359    if (res > 0) {
00360       f.frametype = AST_FRAME_VOICE;
00361       f.subclass = AST_FORMAT_SLINEAR;
00362       f.data = buf + AST_FRIENDLY_OFFSET/2;
00363       f.datalen = res;
00364       f.samples = res / 2;
00365       f.offset = AST_FRIENDLY_OFFSET;
00366       ast_write(chan, &f);
00367       if (res == len)
00368          return 0;
00369    }
00370    return -1;
00371 }
00372 
00373 static void *linear_alloc(struct ast_channel *chan, void *params)
00374 {
00375    struct linear_state *ls;
00376    /* In this case, params is already malloc'd */
00377    if (params) {
00378       ls = params;
00379       if (ls->allowoverride)
00380          ast_set_flag(chan, AST_FLAG_WRITE_INT);
00381       else
00382          ast_clear_flag(chan, AST_FLAG_WRITE_INT);
00383       ls->origwfmt = chan->writeformat;
00384       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00385          ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
00386          free(ls);
00387          ls = params = NULL;
00388       }
00389    }
00390    return params;
00391 }
00392 
00393 static struct ast_generator linearstream = 
00394 {
00395    alloc: linear_alloc,
00396    release: linear_release,
00397    generate: linear_generator,
00398 };
00399 
00400 int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
00401 {
00402    struct linear_state *lin;
00403    char tmpf[256];
00404    int res = -1;
00405    int autoclose = 0;
00406    if (fd < 0) {
00407       if (ast_strlen_zero(filename))
00408          return -1;
00409       autoclose = 1;
00410       if (filename[0] == '/') 
00411          ast_copy_string(tmpf, filename, sizeof(tmpf));
00412       else
00413          snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_VAR_DIR, "sounds", filename);
00414       fd = open(tmpf, O_RDONLY);
00415       if (fd < 0){
00416          ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
00417          return -1;
00418       }
00419    }
00420    lin = malloc(sizeof(struct linear_state));
00421    if (lin) {
00422       memset(lin, 0, sizeof(lin));
00423       lin->fd = fd;
00424       lin->allowoverride = allowoverride;
00425       lin->autoclose = autoclose;
00426       res = ast_activate_generator(chan, &linearstream, lin);
00427    }
00428    return res;
00429 }
00430 
00431 int ast_control_streamfile(struct ast_channel *chan, const char *file,
00432             const char *fwd, const char *rev,
00433             const char *stop, const char *pause,
00434             const char *restart, int skipms) 
00435 {
00436    char *breaks = NULL;
00437    char *end = NULL;
00438    int blen = 2;
00439    int res;
00440    long pause_restart_point = 0;
00441 
00442    if (stop)
00443       blen += strlen(stop);
00444    if (pause)
00445       blen += strlen(pause);
00446    if (restart)
00447       blen += strlen(restart);
00448 
00449    if (blen > 2) {
00450       breaks = alloca(blen + 1);
00451       breaks[0] = '\0';
00452       if (stop)
00453          strcat(breaks, stop);
00454       if (pause)
00455          strcat(breaks, pause);
00456       if (restart)
00457          strcat(breaks, restart);
00458    }
00459    if (chan->_state != AST_STATE_UP)
00460       res = ast_answer(chan);
00461 
00462    if (file) {
00463       if ((end = strchr(file,':'))) {
00464          if (!strcasecmp(end, ":end")) {
00465             *end = '\0';
00466             end++;
00467          }
00468       }
00469    }
00470 
00471    for (;;) {
00472       ast_stopstream(chan);
00473       res = ast_streamfile(chan, file, chan->language);
00474       if (!res) {
00475          if (pause_restart_point) {
00476             ast_seekstream(chan->stream, pause_restart_point, SEEK_SET);
00477             pause_restart_point = 0;
00478          }
00479          else if (end) {
00480             ast_seekstream(chan->stream, 0, SEEK_END);
00481             end = NULL;
00482          };
00483          res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
00484       }
00485 
00486       if (res < 1)
00487          break;
00488 
00489       /* We go at next loop if we got the restart char */
00490       if (restart && strchr(restart, res)) {
00491          ast_log(LOG_DEBUG, "we'll restart the stream here at next loop\n");
00492          pause_restart_point = 0;
00493          continue;
00494       }
00495 
00496       if (pause && strchr(pause, res)) {
00497          pause_restart_point = ast_tellstream(chan->stream);
00498          for (;;) {
00499             ast_stopstream(chan);
00500             res = ast_waitfordigit(chan, 1000);
00501             if (!res)
00502                continue;
00503             else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
00504                break;
00505          }
00506          if (res == *pause) {
00507             res = 0;
00508             continue;
00509          }
00510       }
00511 
00512       if (res == -1)
00513          break;
00514 
00515       /* if we get one of our stop chars, return it to the calling function */
00516       if (stop && strchr(stop, res))
00517          break;
00518    }
00519 
00520    ast_stopstream(chan);
00521 
00522    return res;
00523 }
00524 
00525 int ast_play_and_wait(struct ast_channel *chan, const char *fn)
00526 {
00527    int d;
00528    d = ast_streamfile(chan, fn, chan->language);
00529    if (d)
00530       return d;
00531    d = ast_waitstream(chan, AST_DIGIT_ANY);
00532    ast_stopstream(chan);
00533    return d;
00534 }
00535 
00536 static int global_silence_threshold = 128;
00537 static int global_maxsilence = 0;
00538 
00539 int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
00540 {
00541    int d;
00542    char *fmts;
00543    char comment[256];
00544    int x, fmtcnt=1, res=-1,outmsg=0;
00545    struct ast_frame *f;
00546    struct ast_filestream *others[MAX_OTHER_FORMATS];
00547    char *sfmt[MAX_OTHER_FORMATS];
00548    char *stringp=NULL;
00549    time_t start, end;
00550    struct ast_dsp *sildet=NULL;     /* silence detector dsp */
00551    int totalsilence = 0;
00552    int dspsilence = 0;
00553    int rfmt=0;
00554    struct ast_silence_generator *silgen = NULL;
00555 
00556    if (silencethreshold < 0)
00557       silencethreshold = global_silence_threshold;
00558 
00559    if (maxsilence < 0)
00560       maxsilence = global_maxsilence;
00561 
00562    /* barf if no pointer passed to store duration in */
00563    if (duration == NULL) {
00564       ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
00565       return -1;
00566    }
00567 
00568    ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
00569    snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
00570 
00571    if (playfile) {
00572       d = ast_play_and_wait(chan, playfile);
00573       if (d > -1)
00574          d = ast_streamfile(chan, "beep",chan->language);
00575       if (!d)
00576          d = ast_waitstream(chan,"");
00577       if (d < 0)
00578          return -1;
00579    }
00580 
00581    fmts = ast_strdupa(fmt);
00582 
00583    stringp=fmts;
00584    strsep(&stringp, "|");
00585    ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
00586    sfmt[0] = ast_strdupa(fmts);
00587 
00588    while((fmt = strsep(&stringp, "|"))) {
00589       if (fmtcnt > MAX_OTHER_FORMATS - 1) {
00590          ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
00591          break;
00592       }
00593       sfmt[fmtcnt++] = ast_strdupa(fmt);
00594    }
00595 
00596    time(&start);
00597    end=start;  /* pre-initialize end to be same as start in case we never get into loop */
00598    for (x=0;x<fmtcnt;x++) {
00599       others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
00600       ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
00601 
00602       if (!others[x]) {
00603          break;
00604       }
00605    }
00606 
00607    if (path)
00608       ast_unlock_path(path);
00609 
00610    if (maxsilence > 0) {
00611       sildet = ast_dsp_new(); /* Create the silence detector */
00612       if (!sildet) {
00613          ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00614          return -1;
00615       }
00616       ast_dsp_set_threshold(sildet, silencethreshold);
00617       rfmt = chan->readformat;
00618       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00619       if (res < 0) {
00620          ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00621          ast_dsp_free(sildet);
00622          return -1;
00623       }
00624    }
00625 
00626    /* Request a video update */
00627    ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00628 
00629    if (option_transmit_silence_during_record)
00630       silgen = ast_channel_start_silence_generator(chan);
00631 
00632    if (x == fmtcnt) {
00633    /* Loop forever, writing the packets we read to the writer(s), until
00634       we read a # or get a hangup */
00635       f = NULL;
00636       for(;;) {
00637          res = ast_waitfor(chan, 2000);
00638          if (!res) {
00639             ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
00640             /* Try one more time in case of masq */
00641             res = ast_waitfor(chan, 2000);
00642             if (!res) {
00643                ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
00644                res = -1;
00645             }
00646          }
00647 
00648          if (res < 0) {
00649             f = NULL;
00650             break;
00651          }
00652          f = ast_read(chan);
00653          if (!f)
00654             break;
00655          if (f->frametype == AST_FRAME_VOICE) {
00656             /* write each format */
00657             for (x=0;x<fmtcnt;x++) {
00658                res = ast_writestream(others[x], f);
00659             }
00660 
00661             /* Silence Detection */
00662             if (maxsilence > 0) {
00663                dspsilence = 0;
00664                ast_dsp_silence(sildet, f, &dspsilence);
00665                if (dspsilence)
00666                   totalsilence = dspsilence;
00667                else
00668                   totalsilence = 0;
00669 
00670                if (totalsilence > maxsilence) {
00671                   /* Ended happily with silence */
00672                   if (option_verbose > 2)
00673                      ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
00674                   ast_frfree(f);
00675                   res = 'S';
00676                   outmsg=2;
00677                   break;
00678                }
00679             }
00680             /* Exit on any error */
00681             if (res) {
00682                ast_log(LOG_WARNING, "Error writing frame\n");
00683                ast_frfree(f);
00684                break;
00685             }
00686          } else if (f->frametype == AST_FRAME_VIDEO) {
00687             /* Write only once */
00688             ast_writestream(others[0], f);
00689          } else if (f->frametype == AST_FRAME_DTMF) {
00690             if (strchr(acceptdtmf, f->subclass)) {
00691                if (option_verbose > 2)
00692                   ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
00693                res = f->subclass;
00694                outmsg = 2;
00695                ast_frfree(f);
00696                break;
00697             }
00698             if (strchr(canceldtmf, f->subclass)) {
00699                if (option_verbose > 2)
00700                   ast_verbose(VERBOSE_PREFIX_3 "User cancelled message by pressing %c\n", f->subclass);
00701                res = f->subclass;
00702                outmsg = 0;
00703                ast_frfree(f);
00704                break;
00705             }
00706          }
00707          if (maxtime) {
00708             time(&end);
00709             if (maxtime < (end - start)) {
00710                if (option_verbose > 2)
00711                   ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
00712                outmsg = 2;
00713                res = 't';
00714                ast_frfree(f);
00715                break;
00716             }
00717          }
00718          ast_frfree(f);
00719       }
00720       if (end == start) time(&end);
00721       if (!f) {
00722          if (option_verbose > 2)
00723             ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
00724          res = -1;
00725          outmsg=1;
00726       }
00727    } else {
00728       ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
00729    }
00730 
00731    if (silgen)
00732       ast_channel_stop_silence_generator(chan, silgen);
00733 
00734    *duration = end - start;
00735 
00736    for (x=0;x<fmtcnt;x++) {
00737       if (!others[x])
00738          break;
00739       if (res > 0) {
00740          if (totalsilence)
00741             ast_stream_rewind(others[x], totalsilence-200);
00742          else
00743             ast_stream_rewind(others[x], 200);
00744       }
00745       ast_truncstream(others[x]);
00746       ast_closestream(others[x]);
00747    }
00748    if (rfmt) {
00749       if (ast_set_read_format(chan, rfmt)) {
00750          ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
00751       }
00752    }
00753    if (outmsg > 1) {
00754       /* Let them know recording is stopped */
00755       if(!ast_streamfile(chan, "auth-thankyou", chan->language))
00756          ast_waitstream(chan, "");
00757    }
00758    if (sildet)
00759       ast_dsp_free(sildet);
00760    return res;
00761 }
00762 
00763 static char default_acceptdtmf[] = "#";
00764 static char default_canceldtmf[] = "0";
00765 
00766 int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
00767 {
00768    return ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path, default_acceptdtmf, default_canceldtmf);
00769 }
00770 
00771 int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
00772 {
00773    int d = 0;
00774    char *fmts;
00775    char comment[256];
00776    int x, fmtcnt=1, res=-1,outmsg=0;
00777    struct ast_frame *f;
00778    struct ast_filestream *others[MAX_OTHER_FORMATS];
00779    struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
00780    char *sfmt[MAX_OTHER_FORMATS];
00781    char *stringp=NULL;
00782    time_t start, end;
00783    struct ast_dsp *sildet;    /* silence detector dsp */
00784    int totalsilence = 0;
00785    int dspsilence = 0;
00786    int rfmt=0; 
00787    char prependfile[80];
00788    
00789    if (silencethreshold < 0)
00790       silencethreshold = global_silence_threshold;
00791 
00792    if (maxsilence < 0)
00793       maxsilence = global_maxsilence;
00794 
00795    /* barf if no pointer passed to store duration in */
00796    if (duration == NULL) {
00797       ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
00798       return -1;
00799    }
00800 
00801    ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
00802    snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
00803 
00804    if (playfile || beep) { 
00805       if (!beep)
00806          d = ast_play_and_wait(chan, playfile);
00807       if (d > -1)
00808          d = ast_streamfile(chan, "beep",chan->language);
00809       if (!d)
00810          d = ast_waitstream(chan,"");
00811       if (d < 0)
00812          return -1;
00813    }
00814    ast_copy_string(prependfile, recordfile, sizeof(prependfile)); 
00815    strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
00816          
00817    fmts = ast_strdupa(fmt);
00818    
00819    stringp=fmts;
00820    strsep(&stringp, "|");
00821    ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);   
00822    sfmt[0] = ast_strdupa(fmts);
00823    
00824    while((fmt = strsep(&stringp, "|"))) {
00825       if (fmtcnt > MAX_OTHER_FORMATS - 1) {
00826          ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
00827          break;
00828       }
00829       sfmt[fmtcnt++] = ast_strdupa(fmt);
00830    }
00831 
00832    time(&start);
00833    end=start;  /* pre-initialize end to be same as start in case we never get into loop */
00834    for (x=0;x<fmtcnt;x++) {
00835       others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
00836       ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
00837       if (!others[x]) {
00838          break;
00839       }
00840    }
00841    
00842    sildet = ast_dsp_new(); /* Create the silence detector */
00843    if (!sildet) {
00844       ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
00845       return -1;
00846    }
00847    ast_dsp_set_threshold(sildet, silencethreshold);
00848 
00849    if (maxsilence > 0) {
00850       rfmt = chan->readformat;
00851       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
00852       if (res < 0) {
00853          ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
00854          ast_dsp_free(sildet);
00855          return -1;
00856       }
00857    }
00858                   
00859    if (x == fmtcnt) {
00860    /* Loop forever, writing the packets we read to the writer(s), until
00861       we read a # or get a hangup */
00862       f = NULL;
00863       for(;;) {
00864          res = ast_waitfor(chan, 2000);
00865          if (!res) {
00866             ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
00867             /* Try one more time in case of masq */
00868             res = ast_waitfor(chan, 2000);
00869             if (!res) {
00870                ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
00871                res = -1;
00872             }
00873          }
00874          
00875          if (res < 0) {
00876             f = NULL;
00877             break;
00878          }
00879          f = ast_read(chan);
00880          if (!f)
00881             break;
00882          if (f->frametype == AST_FRAME_VOICE) {
00883             /* write each format */
00884             for (x=0;x<fmtcnt;x++) {
00885                if (!others[x])
00886                   break;
00887                res = ast_writestream(others[x], f);
00888             }
00889             
00890             /* Silence Detection */
00891             if (maxsilence > 0) {
00892                dspsilence = 0;
00893                ast_dsp_silence(sildet, f, &dspsilence);
00894                if (dspsilence)
00895                   totalsilence = dspsilence;
00896                else
00897                   totalsilence = 0;
00898                
00899                if (totalsilence > maxsilence) {
00900                /* Ended happily with silence */
00901                if (option_verbose > 2) 
00902                   ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
00903                ast_frfree(f);
00904                res = 'S';
00905                outmsg=2;
00906                break;
00907                }
00908             }
00909             /* Exit on any error */
00910             if (res) {
00911                ast_log(LOG_WARNING, "Error writing frame\n");
00912                ast_frfree(f);
00913                break;
00914             }
00915          } else if (f->frametype == AST_FRAME_VIDEO) {
00916             /* Write only once */
00917             ast_writestream(others[0], f);
00918          } else if (f->frametype == AST_FRAME_DTMF) {
00919             /* stop recording with any digit */
00920             if (option_verbose > 2) 
00921                ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
00922             res = 't';
00923             outmsg = 2;
00924             ast_frfree(f);
00925             break;
00926          }
00927          if (maxtime) {
00928             time(&end);
00929             if (maxtime < (end - start)) {
00930                if (option_verbose > 2)
00931                   ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
00932                res = 't';
00933                outmsg=2;
00934                ast_frfree(f);
00935                break;
00936             }
00937          }
00938          ast_frfree(f);
00939       }
00940       if (end == start) time(&end);
00941       if (!f) {
00942          if (option_verbose > 2) 
00943             ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
00944          res = -1;
00945          outmsg=1;
00946 #if 0
00947          /* delete all the prepend files */
00948          for (x=0;x<fmtcnt;x++) {
00949             if (!others[x])
00950                break;
00951             ast_closestream(others[x]);
00952             ast_filedelete(prependfile, sfmt[x]);
00953          }
00954 #endif
00955       }
00956    } else {
00957       ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
00958    }
00959    ast_dsp_free(sildet);
00960    *duration = end - start;
00961 #if 0
00962    if (outmsg > 1) {
00963 #else
00964    if (outmsg) {
00965 #endif
00966       struct ast_frame *fr;
00967       for (x=0;x<fmtcnt;x++) {
00968          snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
00969          realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
00970          if (!others[x] || !realfiles[x])
00971             break;
00972          if (totalsilence)
00973             ast_stream_rewind(others[x], totalsilence-200);
00974          else
00975             ast_stream_rewind(others[x], 200);
00976          ast_truncstream(others[x]);
00977          /* add the original file too */
00978          while ((fr = ast_readframe(realfiles[x]))) {
00979             ast_writestream(others[x],fr);
00980          }
00981          ast_closestream(others[x]);
00982          ast_closestream(realfiles[x]);
00983          ast_filerename(prependfile, recordfile, sfmt[x]);
00984 #if 0
00985          ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
00986 #endif
00987          ast_filedelete(prependfile, sfmt[x]);
00988       }
00989    }
00990    if (rfmt) {
00991       if (ast_set_read_format(chan, rfmt)) {
00992          ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
00993       }
00994    }
00995    if (outmsg) {
00996       if (outmsg > 1) {
00997          /* Let them know it worked */
00998          ast_streamfile(chan, "auth-thankyou", chan->language);
00999          ast_waitstream(chan, "");
01000       }
01001    }  
01002    return res;
01003 }
01004 
01005 /* Channel group core functions */
01006 
01007 int ast_app_group_split_group(char *data, char *group, int group_max, char *category, int category_max)
01008 {
01009    int res=0;
01010    char tmp[256];
01011    char *grp=NULL, *cat=NULL;
01012 
01013    if (!ast_strlen_zero(data)) {
01014       ast_copy_string(tmp, data, sizeof(tmp));
01015       grp = tmp;
01016       cat = strchr(tmp, '@');
01017       if (cat) {
01018          *cat = '\0';
01019          cat++;
01020       }
01021    }
01022 
01023    if (!ast_strlen_zero(grp))
01024       ast_copy_string(group, grp, group_max);
01025    else
01026       res = -1;
01027 
01028    if (!ast_strlen_zero(cat))
01029       ast_copy_string(category, cat, category_max);
01030 
01031    return res;
01032 }
01033 
01034 int ast_app_group_set_channel(struct ast_channel *chan, char *data)
01035 {
01036    int res = 0;
01037    char group[80] = "", category[80] = "";
01038    struct ast_group_info *gi = NULL;
01039    size_t len = 0;
01040 
01041    if (ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category)))
01042       return -1;
01043 
01044    /* Calculate memory we will need if this is new */
01045    len = sizeof(*gi) + strlen(group) + 1;
01046    if (!ast_strlen_zero(category))
01047       len += strlen(category) + 1;
01048 
01049    AST_LIST_LOCK(&groups);
01050    AST_LIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
01051       if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
01052          AST_LIST_REMOVE_CURRENT(&groups, list);
01053          free(gi);
01054          break;
01055       }
01056    }
01057    AST_LIST_TRAVERSE_SAFE_END
01058 
01059    if ((gi = calloc(1, len))) {
01060       gi->chan = chan;
01061       gi->group = (char *) gi + sizeof(*gi);
01062       strcpy(gi->group, group);
01063       if (!ast_strlen_zero(category)) {
01064          gi->category = (char *) gi + sizeof(*gi) + strlen(group) + 1;
01065          strcpy(gi->category, category);
01066       }
01067       AST_LIST_INSERT_TAIL(&groups, gi, list);
01068    } else {
01069       res = -1;
01070    }
01071 
01072    AST_LIST_UNLOCK(&groups);
01073 
01074    return res;
01075 }
01076 
01077 int ast_app_group_get_count(