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