Skip to content
Snippets Groups Projects
app.c 26.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
    
     * Convenient Application Routines
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 
    
     * Copyright (C) 1999-2004, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Mark Spencer <markster@digium.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/time.h>
    #include <signal.h>
    #include <errno.h>
    #include <unistd.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <dirent.h>
    
    #include <sys/types.h>
    #include <regex.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/channel.h>
    
    #include <asterisk/pbx.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/file.h>
    #include <asterisk/app.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/dsp.h>
    #include <asterisk/logger.h>
    #include <asterisk/options.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include "asterisk.h"
    #include "astconf.h"
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
       "ludicrous time" (essentially never times out) */
    int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
    {
    	int res,to,fto;
    
    	/* XXX Merge with full version? XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (prompt) {
    		res = ast_streamfile(c, prompt, c->language);
    		if (res < 0)
    			return res;
    	}
    
    	fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
    	to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (timeout > 0) fto = to = timeout;
    	if (timeout < 0) fto = to = 1000000000;
    	res = ast_readstring(c, s, maxlen, to, fto, "#");
    	return res;
    }
    
    
    
    int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
    {
    	int res,to,fto;
    	if (prompt) {
    		res = ast_streamfile(c, prompt, c->language);
    		if (res < 0)
    			return res;
    	}
    	fto = 6000;
    	to = 2000;
    	if (timeout > 0) fto = to = timeout;
    	if (timeout < 0) fto = to = 1000000000;
    	res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec)
    {
    	int res;
    	struct ast_filestream *writer;
    	int rfmt;
    
    	int totalms=0, total;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	struct ast_frame *f;
    	struct ast_dsp *sildet;
    	/* Play prompt if requested */
    	if (prompt) {
    		res = ast_streamfile(c, prompt, c->language);
    		if (res < 0)
    			return res;
    		res = ast_waitstream(c,"");
    		if (res < 0)
    			return res;
    	}
    	rfmt = c->readformat;
    
    	res = ast_set_read_format(c, AST_FORMAT_SLINEAR);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res < 0) {
    		ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
    		return -1;
    	}
    	sildet = ast_dsp_new();
    	if (!sildet) {
    		ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
    		return -1;
    	}
    	writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666);
    	if (!writer) {
    		ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt);
    		ast_dsp_free(sildet);
    		return -1;
    	}
    	for(;;) {
    		if ((res = ast_waitfor(c, 2000)) < 0) {
    			ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt);
    			break;
    		}
    		if (res) {
    			f = ast_read(c);
    			if (!f) {
    				ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt);
    				break;
    			}
    			if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
    				/* Ended happily with DTMF */
    				ast_frfree(f);
    				break;
    			} else if (f->frametype == AST_FRAME_VOICE) {
    				ast_dsp_silence(sildet, f, &total); 
    				if (total > silence) {
    					/* Ended happily with silence */
    					ast_frfree(f);
    					break;
    				}
    				totalms += f->samples / 8;
    				if (totalms > maxsec * 1000) {
    					/* Ended happily with too much stuff */
    					ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec);
    					ast_frfree(f);
    					break;
    				}
    			}
    			ast_frfree(f);
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res)
    		ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name);
    	ast_dsp_free(sildet);
    	ast_closestream(writer);
    	return 0;
    }
    
    
    int ast_app_has_voicemail(const char *mailbox)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	DIR *dir;
    	struct dirent *de;
    	char fn[256];
    
    	char tmp[256]="";
    	char *mb, *cur;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* If no mailbox, return immediately */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    	if (strchr(mailbox, ',')) {
    
    		strncpy(tmp, mailbox, sizeof(tmp) - 1);
    
    		mb = tmp;
    		ret = 0;
    
    		while((cur = strsep(&mb, ","))) {
    
    				if (ast_app_has_voicemail(cur))
    					return 1; 
    			}
    		}
    		return 0;
    	}
    
    	strncpy(tmp, mailbox, sizeof(tmp) - 1);
    	context = strchr(tmp, '@');
    	if (context) {
    		*context = '\0';
    		context++;
    	} else
    		context = "default";
    
    	snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	dir = opendir(fn);
    	if (!dir)
    		return 0;
    	while ((de = readdir(dir))) {
    		if (!strncasecmp(de->d_name, "msg", 3))
    			break;
    	}
    	closedir(dir);
    	if (de)
    		return 1;
    	return 0;
    }
    
    
    int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
    {
    	DIR *dir;
    	struct dirent *de;
    	char fn[256];
    	char tmp[256]="";
    	char *mb, *cur;
    
    	int ret;
    	if (newmsgs)
    		*newmsgs = 0;
    	if (oldmsgs)
    		*oldmsgs = 0;
    	/* If no mailbox, return immediately */
    
    		return 0;
    	if (strchr(mailbox, ',')) {
    		int tmpnew, tmpold;
    
    		strncpy(tmp, mailbox, sizeof(tmp) - 1);
    
    		mb = tmp;
    		ret = 0;
    		while((cur = strsep(&mb, ", "))) {
    
    				if (ast_app_messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
    					return -1;
    				else {
    					if (newmsgs)
    						*newmsgs += tmpnew; 
    					if (oldmsgs)
    						*oldmsgs += tmpold;
    				}
    			}
    		}
    		return 0;
    	}
    
    	strncpy(tmp, mailbox, sizeof(tmp) - 1);
    	context = strchr(tmp, '@');
    	if (context) {
    		*context = '\0';
    		context++;
    	} else
    		context = "default";
    
    		snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
    
    		dir = opendir(fn);
    		if (dir) {
    			while ((de = readdir(dir))) {
    				if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
    					!strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
    
    Mark Spencer's avatar
    Mark Spencer committed
    						(*newmsgs)++;
    
    		snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/Old", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
    
    		dir = opendir(fn);
    		if (dir) {
    			while ((de = readdir(dir))) {
    				if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
    					!strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
    
    Mark Spencer's avatar
    Mark Spencer committed
    						(*oldmsgs)++;
    
    James Golovich's avatar
    James Golovich committed
    int ast_dtmf_stream(struct ast_channel *chan,struct ast_channel *peer,char *digits,int between) 
    {
    	char *ptr=NULL;
    	int res=0;
    	struct ast_frame f;
    	if (!between)
    		between = 100;
    
    James Golovich's avatar
    James Golovich committed
    	if (peer)
    		res = ast_autoservice_start(peer);
    
    James Golovich's avatar
    James Golovich committed
    	if (!res) {
    		res = ast_waitfor(chan,100);
    		if (res > -1) {
    			for (ptr=digits;*ptr;*ptr++) {
    				if (*ptr == 'w') {
    					res = ast_safe_sleep(chan, 500);
    					if (res) 
    						break;
    					continue;
    				}
    				memset(&f, 0, sizeof(f));
    				f.frametype = AST_FRAME_DTMF;
    				f.subclass = *ptr;
    				f.src = "ast_dtmf_stream";
    				if (strchr("0123456789*#abcdABCD",*ptr)==NULL) {
    					ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
    				} else {
    					res = ast_write(chan, &f);
    					if (res) 
    						break;
    					/* pause between digits */
    					res = ast_safe_sleep(chan,between);
    					if (res) 
    						break;
    				}
    			}
    		}
    		if (peer)
    			res = ast_autoservice_stop(peer);
    	}
    	return res;
    
    
    struct linear_state {
    	int fd;
    	int autoclose;
    	int allowoverride;
    	int origwfmt;
    };
    
    static void linear_release(struct ast_channel *chan, void *params)
    {
    	struct linear_state *ls = params;
    	if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
    		ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
    	}
    	if (ls->autoclose)
    		close(ls->fd);
    	free(params);
    }
    
    static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
    {
    	struct ast_frame f;
    	short buf[2048 + AST_FRIENDLY_OFFSET / 2];
    	struct linear_state *ls = data;
    	int res;
    	len = samples * 2;
    	if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
    		ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
    		len = sizeof(buf) - AST_FRIENDLY_OFFSET;
    	}
    	memset(&f, 0, sizeof(f));
    	res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
    	if (res > 0) {
    		f.frametype = AST_FRAME_VOICE;
    		f.subclass = AST_FORMAT_SLINEAR;
    		f.data = buf + AST_FRIENDLY_OFFSET/2;
    		f.datalen = res;
    		f.samples = res / 2;
    		f.offset = AST_FRIENDLY_OFFSET;
    		ast_write(chan, &f);
    		if (res == len)
    			return 0;
    	}
    	return -1;
    }
    
    static void *linear_alloc(struct ast_channel *chan, void *params)
    {
    	struct linear_state *ls;
    	/* In this case, params is already malloc'd */
    	if (params) {
    		ls = params;
    		if (ls->allowoverride)
    			chan->writeinterrupt = 1;
    		else
    			chan->writeinterrupt = 0;
    		ls->origwfmt = chan->writeformat;
    		if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
    			ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
    			free(ls);
    			ls = params = NULL;
    		}
    	}
    	return params;
    }
    
    static struct ast_generator linearstream = 
    {
    	alloc: linear_alloc,
    	release: linear_release,
    	generate: linear_generator,
    };
    
    int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
    {
    	struct linear_state *lin;
    	char tmpf[256] = "";
    	int res = -1;
    	int autoclose = 0;
    	if (fd < 0) {
    		if (!filename || ast_strlen_zero(filename))
    			return -1;
    		autoclose = 1;
    		if (filename[0] == '/') 
    			strncpy(tmpf, filename, sizeof(tmpf) - 1);
    		else
    			snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_VAR_DIR, "sounds", filename);
    		fd = open(tmpf, O_RDONLY);
    		if (fd < 0){
    			ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
    			return -1;
    		}
    	}
    	lin = malloc(sizeof(struct linear_state));
    	if (lin) {
    		memset(lin, 0, sizeof(lin));
    		lin->fd = fd;
    		lin->allowoverride = allowoverride;
    		lin->autoclose = autoclose;
    		res = ast_activate_generator(chan, &linearstream, lin);
    	}
    	return res;
    }
    
    James Golovich's avatar
    James Golovich committed
    int ast_control_streamfile(struct ast_channel *chan, char *file, char *fwd, char *rev, char *stop, char *pause, int skipms) 
    
    James Golovich's avatar
    James Golovich committed
    {
    
    	struct timeval started, ended;
    	long elapsed = 0,last_elapsed =0;
    
    	int blen=2;
    	int res=0;
    
    	if (stop)
    		blen += strlen(stop);
    	if (pause)
    		blen += strlen(pause);
    
    
    James Golovich's avatar
    James Golovich committed
    	if (blen > 2) {
    
    		breaks = alloca(blen + 1);
    		breaks[0] = '\0';
    		strcat(breaks, stop);
    		strcat(breaks, pause);
    	}
    
    	if (chan->_state != AST_STATE_UP)
    		res = ast_answer(chan);
    
    
    James Golovich's avatar
    James Golovich committed
    	if (chan)
    
    James Golovich's avatar
    James Golovich committed
    	if (file) {
    		if ((end = strchr(file,':'))) {
    			if (!strcasecmp(end, ":end")) {
    
    Anthony Minessale II's avatar
    Anthony Minessale II committed
    				*end = '\0';
    				end++;
    			}
    
    James Golovich's avatar
    James Golovich committed
    		}
    
    James Golovich's avatar
    James Golovich committed
    	}
    
    James Golovich's avatar
    James Golovich committed
    	for (;;) {
    
    James Golovich's avatar
    James Golovich committed
    		if (chan)
    
    			ast_stopstream(chan);
    		res = ast_streamfile(chan, file, chan->language);
    
    James Golovich's avatar
    James Golovich committed
    		if (!res) {
    
    James Golovich's avatar
    James Golovich committed
    			if (end) {
    
    				ast_seekstream(chan->stream, 0, SEEK_END);
    				end=NULL;
    			}
    
    James Golovich's avatar
    James Golovich committed
    			if (elapsed) {
    				ast_stream_fastforward(chan->stream, elapsed);
    
    James Golovich's avatar
    James Golovich committed
    			if (res)
    				res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
    			else
    
    		if (pause != NULL && strchr(pause, res)) {
    
    James Golovich's avatar
    James Golovich committed
    			gettimeofday(&ended, NULL);
    
    			elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000) + last_elapsed);
    			for(;;) {
    
    James Golovich's avatar
    James Golovich committed
    				if (chan)
    
    					ast_stopstream(chan);
    				res = ast_waitfordigit(chan, 1000);
    
    James Golovich's avatar
    James Golovich committed
    				if (res == 0)
    
    					continue;
    
    James Golovich's avatar
    James Golovich committed
    				else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
    
    James Golovich's avatar
    James Golovich committed
    			if (res == *pause) {
    
    James Golovich's avatar
    James Golovich committed
    		if (res == -1)
    
    		/* if we get one of our stop chars, return it to the calling function */
    
    		if (stop && strchr(stop, res)) {
    
    James Golovich's avatar
    James Golovich committed
    	if (chan)
    
    James Golovich's avatar
    James Golovich committed
    	return res;
    
    508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
    
    int ast_play_and_wait(struct ast_channel *chan, char *fn)
    {
    	int d;
    	d = ast_streamfile(chan, fn, chan->language);
    	if (d)
    		return d;
    	d = ast_waitstream(chan, AST_DIGIT_ANY);
    	ast_stopstream(chan);
    	return d;
    }
    
    static int global_silence_threshold = 128;
    static int global_maxsilence = 0;
    
    int ast_play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int silencethreshold, int maxsilence)
    {
    	char d, *fmts;
    	char comment[256];
    	int x, fmtcnt=1, res=-1,outmsg=0;
    	struct ast_frame *f;
    	struct ast_filestream *others[MAX_OTHER_FORMATS];
    	char *sfmt[MAX_OTHER_FORMATS];
    	char *stringp=NULL;
    	time_t start, end;
    	struct ast_dsp *sildet;   	/* silence detector dsp */
    	int totalsilence = 0;
    	int dspsilence = 0;
    	int gotsilence = 0;		/* did we timeout for silence? */
    	int rfmt=0;
    
    	if (silencethreshold < 0)
    		silencethreshold = global_silence_threshold;
    
    	if (maxsilence < 0)
    		maxsilence = global_maxsilence;
    
    	/* barf if no pointer passed to store duration in */
    	if (duration == NULL) {
    		ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
    		return -1;
    	}
    
    	ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
    	snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
    
    	if (playfile) {
    		d = ast_play_and_wait(chan, playfile);
    		if (d > -1)
    			d = ast_streamfile(chan, "beep",chan->language);
    		if (!d)
    			d = ast_waitstream(chan,"");
    		if (d < 0)
    			return -1;
    	}
    
    	fmts = ast_strdupa(fmt);
    
    	stringp=fmts;
    	strsep(&stringp, "|");
    	ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
    	sfmt[0] = ast_strdupa(fmts);
    
    	while((fmt = strsep(&stringp, "|"))) {
    		if (fmtcnt > MAX_OTHER_FORMATS - 1) {
    			ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
    			break;
    		}
    		sfmt[fmtcnt++] = ast_strdupa(fmt);
    	}
    
    	time(&start);
    	end=start;  /* pre-initialize end to be same as start in case we never get into loop */
    	for (x=0;x<fmtcnt;x++) {
    		others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
    		ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
    
    		if (!others[x]) {
    			break;
    		}
    	}
    
    	sildet = ast_dsp_new(); /* Create the silence detector */
    	if (!sildet) {
    		ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
    		return -1;
    	}
    	ast_dsp_set_threshold(sildet, silencethreshold);
    	
    	if (maxsilence > 0) {
    		rfmt = chan->readformat;
    		res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
    		if (res < 0) {
    			ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
    			return -1;
    		}
    	}
    
    	if (x == fmtcnt) {
    	/* Loop forever, writing the packets we read to the writer(s), until
    	   we read a # or get a hangup */
    		f = NULL;
    		for(;;) {
    		 	res = ast_waitfor(chan, 2000);
    			if (!res) {
    				ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
    				/* Try one more time in case of masq */
    			 	res = ast_waitfor(chan, 2000);
    				if (!res) {
    					ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
    					res = -1;
    				}
    			}
    
    			if (res < 0) {
    				f = NULL;
    				break;
    			}
    			f = ast_read(chan);
    			if (!f)
    				break;
    			if (f->frametype == AST_FRAME_VOICE) {
    				/* write each format */
    				for (x=0;x<fmtcnt;x++) {
    					res = ast_writestream(others[x], f);
    				}
    
    				/* Silence Detection */
    				if (maxsilence > 0) {
    					dspsilence = 0;
    					ast_dsp_silence(sildet, f, &dspsilence);
    					if (dspsilence)
    						totalsilence = dspsilence;
    					else
    						totalsilence = 0;
    
    					if (totalsilence > maxsilence) {
    					/* Ended happily with silence */
                                            if (option_verbose > 2)
                                                    ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
    					ast_frfree(f);
    					gotsilence = 1;
    					outmsg=2;
    					break;
    					}
    				}
    				/* Exit on any error */
    				if (res) {
    					ast_log(LOG_WARNING, "Error writing frame\n");
    					ast_frfree(f);
    					break;
    				}
    			} else if (f->frametype == AST_FRAME_VIDEO) {
    				/* Write only once */
    				ast_writestream(others[0], f);
    			} else if (f->frametype == AST_FRAME_DTMF) {
    				if (f->subclass == '#') {
    					if (option_verbose > 2)
    						ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
    					res = '#';
    					outmsg = 2;
    					ast_frfree(f);
    					break;
    				}
    			}
    				if (f->subclass == '0') {
    				/* Check for a '0' during message recording also, in case caller wants operator */
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
    					res = '0';
    					outmsg = 0;
    					ast_frfree(f);
    					break;
    				}
    			if (maxtime) {
    				time(&end);
    				if (maxtime < (end - start)) {
    					if (option_verbose > 2)
    						ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
    					outmsg = 2;
    					res = 't';
    					ast_frfree(f);
    					break;
    				}
    			}
    			ast_frfree(f);
    		}
    		if (end == start) time(&end);
    		if (!f) {
    			if (option_verbose > 2)
    				ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
    			res = -1;
    			outmsg=1;
    		}
    	} else {
    		ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
    	}
    
    	*duration = end - start;
    
    	for (x=0;x<fmtcnt;x++) {
    		if (!others[x])
    			break;
    		if (totalsilence)
    			ast_stream_rewind(others[x], totalsilence-200);
    		else
    			ast_stream_rewind(others[x], 200);
    		ast_truncstream(others[x]);
    		ast_closestream(others[x]);
    	}
    	if (rfmt) {
    		if (ast_set_read_format(chan, rfmt)) {
    			ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
    		}
    	}
    	if (outmsg) {
    		if (outmsg > 1) {
    		/* Let them know recording is stopped */
    			ast_streamfile(chan, "auth-thankyou", chan->language);
    			ast_waitstream(chan, "");
    		}
    	}
    
    	return res;
    }
    
    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)
    {
    	char d = 0, *fmts;
    	char comment[256];
    	int x, fmtcnt=1, res=-1,outmsg=0;
    	struct ast_frame *f;
    	struct ast_filestream *others[MAX_OTHER_FORMATS];
    	struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
    	char *sfmt[MAX_OTHER_FORMATS];
    	char *stringp=NULL;
    	time_t start, end;
    	struct ast_dsp *sildet;   	/* silence detector dsp */
    	int totalsilence = 0;
    	int dspsilence = 0;
    	int gotsilence = 0;		/* did we timeout for silence? */
    	int rfmt=0;	
    	char prependfile[80];
    	
    	if (silencethreshold < 0)
    		silencethreshold = global_silence_threshold;
    
    	if (maxsilence < 0)
    		maxsilence = global_maxsilence;
    
    	/* barf if no pointer passed to store duration in */
    	if (duration == NULL) {
    		ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
    		return -1;
    	}
    
    	ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
    	snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
    
    	if (playfile || beep) {	
    		if (!beep)
    			d = ast_play_and_wait(chan, playfile);
    		if (d > -1)
    			d = ast_streamfile(chan, "beep",chan->language);
    		if (!d)
    			d = ast_waitstream(chan,"");
    		if (d < 0)
    			return -1;
    	}
    	strncpy(prependfile, recordfile, sizeof(prependfile) -1);	
    	strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
    			
    	fmts = ast_strdupa(fmt);
    	
    	stringp=fmts;
    	strsep(&stringp, "|");
    	ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);	
    	sfmt[0] = ast_strdupa(fmts);
    	
    	while((fmt = strsep(&stringp, "|"))) {
    		if (fmtcnt > MAX_OTHER_FORMATS - 1) {
    			ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
    			break;
    		}
    		sfmt[fmtcnt++] = ast_strdupa(fmt);
    	}
    
    	time(&start);
    	end=start;  /* pre-initialize end to be same as start in case we never get into loop */
    	for (x=0;x<fmtcnt;x++) {
    		others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
    		ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
    		if (!others[x]) {
    			break;
    		}
    	}
    	
    	sildet = ast_dsp_new(); /* Create the silence detector */
    	if (!sildet) {
    		ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
    		return -1;
    	}
    	ast_dsp_set_threshold(sildet, silencethreshold);
    
    	if (maxsilence > 0) {
    		rfmt = chan->readformat;
    		res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
    		if (res < 0) {
    			ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
    			return -1;
    		}
    	}
    						
    	if (x == fmtcnt) {
    	/* Loop forever, writing the packets we read to the writer(s), until
    	   we read a # or get a hangup */
    		f = NULL;
    		for(;;) {
    		 	res = ast_waitfor(chan, 2000);
    			if (!res) {
    				ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
    				/* Try one more time in case of masq */
    			 	res = ast_waitfor(chan, 2000);
    				if (!res) {
    					ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
    					res = -1;
    				}
    			}
    			
    			if (res < 0) {
    				f = NULL;
    				break;
    			}
    			f = ast_read(chan);
    			if (!f)
    				break;
    			if (f->frametype == AST_FRAME_VOICE) {
    				/* write each format */
    				for (x=0;x<fmtcnt;x++) {
    					if (!others[x])
    						break;
    					res = ast_writestream(others[x], f);
    				}
    				
    				/* Silence Detection */
    				if (maxsilence > 0) {
    					dspsilence = 0;
    					ast_dsp_silence(sildet, f, &dspsilence);
    					if (dspsilence)
    						totalsilence = dspsilence;
    					else
    						totalsilence = 0;
    					
    					if (totalsilence > maxsilence) {
    					/* Ended happily with silence */
    					if (option_verbose > 2) 
    						ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
    					ast_frfree(f);
    					gotsilence = 1;
    					outmsg=2;
    					break;
    					}
    				}
    				/* Exit on any error */
    				if (res) {
    					ast_log(LOG_WARNING, "Error writing frame\n");
    					ast_frfree(f);
    					break;
    				}
    			} else if (f->frametype == AST_FRAME_VIDEO) {
    				/* Write only once */
    				ast_writestream(others[0], f);
    			} else if (f->frametype == AST_FRAME_DTMF) {
    				/* stop recording with any digit */
    				if (option_verbose > 2) 
    					ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
    				res = 't';
    				outmsg = 2;
    				ast_frfree(f);
    				break;
    			}
    			if (maxtime) {
    				time(&end);
    				if (maxtime < (end - start)) {
    					if (option_verbose > 2)
    						ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
    					res = 't';
    					outmsg=2;
    					ast_frfree(f);
    					break;
    				}
    			}
    			ast_frfree(f);
    		}
    		if (end == start) time(&end);
    		if (!f) {
    			if (option_verbose > 2) 
    				ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
    			res = -1;
    			outmsg=1;
    #if 0
    			/* delete all the prepend files */
    			for (x=0;x<fmtcnt;x++) {
    				if (!others[x])
    					break;
    				ast_closestream(others[x]);
    				ast_filedelete(prependfile, sfmt[x]);
    			}
    #endif
    		}
    	} else {
    		ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
    	}
    	*duration = end - start;
    #if 0
    	if (outmsg > 1) {
    #else
    	if (outmsg) {
    #endif
    		struct ast_frame *fr;
    		for (x=0;x<fmtcnt;x++) {
    			snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
    			realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
    			if (!others[x] || !realfiles[x])
    				break;
    			if (totalsilence)
    				ast_stream_rewind(others[x], totalsilence-200);
    			else
    				ast_stream_rewind(others[x], 200);
    			ast_truncstream(others[x]);
    			/* add the original file too */
    			while ((fr = ast_readframe(realfiles[x]))) {
    				ast_writestream(others[x],fr);
    			}
    			ast_closestream(others[x]);
    			ast_closestream(realfiles[x]);
    			ast_filerename(prependfile, recordfile, sfmt[x]);
    #if 0
    			ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
    #endif
    			ast_filedelete(prependfile, sfmt[x]);
    		}
    	}
    	if (rfmt) {
    		if (ast_set_read_format(chan, rfmt)) {
    			ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
    		}
    	}
    	if (outmsg) {
    		if (outmsg > 1) {
    			/* Let them know it worked */
    			ast_streamfile(chan, "auth-thankyou", chan->language);
    			ast_waitstream(chan, "");
    		}
    	}	
    	return res;
    }
    
    
    /* Channel group core functions */
    
    int ast_app_group_split_group(char *data, char *group, int group_max, char *category, int category_max)
    {
    	int res=0;
    	char tmp[256] = "";
    	char *grp=NULL, *cat=NULL;
    
    	if (data && !ast_strlen_zero(data)) {
    		strncpy(tmp, data, sizeof(tmp) - 1);
    		grp = tmp;
    		cat = strchr(tmp, '@');
    		if (cat) {
    			*cat = '\0';
    			cat++;
    		}
    	}
    
    	if (grp && !ast_strlen_zero(grp))
    		strncpy(group, grp, group_max -1);
    	else
    		res = -1;
    
    	if (cat)
    		snprintf(category, category_max, "%s_%s", GROUP_CATEGORY_PREFIX, cat);
    	else
    		strncpy(category, GROUP_CATEGORY_PREFIX, category_max - 1);
    
    	return res;
    }
    
    int ast_app_group_set_channel(struct ast_channel *chan, char *data)
    {
    	int res=0;
    	char group[80] = "";