Skip to content
Snippets Groups Projects
app_voicemail.c 57.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
     * Voicemail System (did you ever think it could be so easy?)
     * 
    
    Mark Spencer's avatar
    Mark Spencer committed
     * Copyright (C) 1999, Mark Spencer
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * Mark Spencer <markster@linux-support.net>
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/lock.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/file.h>
    #include <asterisk/logger.h>
    #include <asterisk/channel.h>
    #include <asterisk/pbx.h>
    #include <asterisk/options.h>
    #include <asterisk/config.h>
    #include <asterisk/say.h>
    #include <asterisk/module.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/adsi.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/app.h>
    
    #include <asterisk/manager.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/time.h>
    #include <sys/stat.h>
    #include <time.h>
    
    #include <pthread.h>
    #include "../asterisk.h"
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include "../astconf.h"
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    #define COMMAND_TIMEOUT 5000
    
    #define VOICEMAIL_CONFIG "voicemail.conf"
    #define ASTERISK_USERNAME "asterisk"
    
    #define SENDMAIL "/usr/sbin/sendmail -t"
    
    #define INTRO "vm-intro"
    
    #define MAXMSG 100
    
    #define MAX_OTHER_FORMATS 10
    
    #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define BASEMAXINLINE 256
    
    #define BASELINELEN 72
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define BASEMAXINLINE 256
    #define BASELINELEN 72
    #define eol "\r\n"
    
    int iocp;
    int iolen;
    int linelength;
    int ateof;
    unsigned char iobuf[BASEMAXINLINE];
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char *tdesc = "Comedian Mail (Voicemail System)";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *adapp = "CoMa";
    
    static char *adsec = "_AST";
    
    static char *addesc = "Comedian Mail";
    
    static int adver = 1;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *synopsis_vm =
    "Leave a voicemail message";
    
    static char *descrip_vm =
    
    Mark Spencer's avatar
    Mark Spencer committed
    "  VoiceMail([s|u|b]extension): Leaves voicemail for a given  extension (must\n"
    "be configured in voicemail.conf). If the extension is preceeded by an 's'"
    "then instructions for leaving the message will be skipped.  If the extension\n"
    "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
    "/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.  If the extension\n"
    "is preceeded by a 'b' then the the busy message will be played (that is,\n"
    "busy instead of unavail).  At most one of 's', 'u', or 'b' may be specified.\n"
    "Returns  -1 on  error or mailbox not found, or if the user hangs up. \n"
    "Otherwise, it returns 0. \n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char *synopsis_vmain =
    "Enter voicemail system";
    
    static char *descrip_vmain =
    
    Mark Spencer's avatar
    Mark Spencer committed
    "  VoiceMailMain(): Enters the main voicemail system for the checking of voicemail.  The mailbox\n"
    "can be passed as the option, which will stop the voicemail system from prompting the user\n"
    "for the mailbox.  If the mailbox is preceeded by 's' then the passsword check will be skipped.\n"
    "Returns -1 if the user hangs up or 0 otherwise.\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* Leave a message */
    static char *app = "VoiceMail";
    
    /* Check mail, control, etc */
    static char *app2 = "VoiceMailMain";
    
    STANDARD_LOCAL_USER;
    
    LOCAL_USER_DECL;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int make_dir(char *dest, int len, char *ext, char *mailbox)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return snprintf(dest, len, "%s/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,"vm", ext, mailbox);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int make_file(char *dest, int len, char *dir, int num)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return snprintf(dest, len, "%s/msg%04d", dir, num);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int vm_change_password(char *username, char *password, char *newpassword)
    {
            /*  There's probably a better way of doing this. */
            /*  That's why I've put the password change in a separate function. */
    
            FILE *configin;
            FILE *configout;
    		char inbuf[256];
    		char orig[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char tmpin[AST_CONFIG_MAX_PATH];
    		char tmpout[AST_CONFIG_MAX_PATH];
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char *user, *pass, *rest, *trim;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
    	snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
            configin = fopen((char *)tmpin,"r");
            configout = fopen((char *)tmpout,"w+");
    
    Mark Spencer's avatar
    Mark Spencer committed
    
            while (!feof(configin)) {
    			/* Read in the line */
    			fgets(inbuf, sizeof(inbuf), configin);
    			if (!feof(configin)) {
    				/* Make a backup of it */
    				memcpy(orig, inbuf, sizeof(orig));
    				/* Strip trailing \n and comment */
    				inbuf[strlen(inbuf) - 1] = '\0';
    				user = strchr(inbuf, ';');
    				if (user)
    					*user = '\0';
    				user=inbuf;
    				while(*user < 33)
    					user++;
    				pass = strchr(user, '=');
    				if (pass > user) {
    					trim = pass - 1;
    					while(*trim && *trim < 33) {
    						*trim = '\0';
    						trim--;
    					}
    				}
    				if (pass) {
    					*pass = '\0';
    					pass++;
    					if (*pass == '>')
    						pass++;
    					while(*pass && *pass < 33)
    						pass++;
    				}
    				if (pass) {
    					rest = strchr(pass,',');
    					if (rest) {
    						*rest = '\0';
    						rest++;
    					}
    				} else
    					rest = NULL;
    				if (user && pass && *user && *pass && !strcmp(user, username) && !strcmp(pass, password)) {
    					/* This is the line */
    					if (rest) {
    						fprintf(configout, "%s => %s,%s\n", username,newpassword,rest);
    					} else {
    						fprintf(configout, "%s => %s\n", username,newpassword);
    					}
    				} else {
    					/* Put it back like it was */
    					fprintf(configout, orig);
    				}
    			}
            }
            fclose(configin);
            fclose(configout);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
            unlink((char *)tmpin);
            rename((char *)tmpout,(char *)tmpin);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return(1);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int
    inbuf(FILE *fi)
    {
    	int l;
    
    	if(ateof)
    		return 0;
    
    	if ( (l = fread(iobuf,1,BASEMAXINLINE,fi)) <= 0) {
    		if(ferror(fi))
    			return -1;
    
    		ateof = 1;
    		return 0;
    	}
    
    	iolen= l;
    	iocp= 0;
    
    	return 1;
    }
    
    static int 
    inchar(FILE *fi)
    {
    	if(iocp>=iolen)
    		if(!inbuf(fi))
    			return EOF;
    
    	return iobuf[iocp++];
    }
    
    static int
    ochar(int c, FILE *so)
    {
    	if(linelength>=BASELINELEN) {
    		if(fputs(eol,so)==EOF)
    			return -1;
    
    		linelength= 0;
    	}
    
    	if(putc(((unsigned char)c),so)==EOF)
    		return -1;
    
    	linelength++;
    
    	return 1;
    }
    
    static int base_encode(char *filename, FILE *so)
    {
    	unsigned char dtable[BASEMAXINLINE];
    	int i,hiteof= 0;
    	FILE *fi;
    
    	linelength = 0;
    	iocp = BASEMAXINLINE;
    	iolen = 0;
    	ateof = 0;
    
    	if ( !(fi = fopen(filename, "rb"))) {
    		ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
    		return -1;
    	}
    
    	for(i= 0;i<9;i++){
    		dtable[i]= 'A'+i;
    		dtable[i+9]= 'J'+i;
    		dtable[26+i]= 'a'+i;
    		dtable[26+i+9]= 'j'+i;
    	}
    	for(i= 0;i<8;i++){
    		dtable[i+18]= 'S'+i;
    		dtable[26+i+18]= 's'+i;
    	}
    	for(i= 0;i<10;i++){
    		dtable[52+i]= '0'+i;
    	}
    	dtable[62]= '+';
    	dtable[63]= '/';
    
    	while(!hiteof){
    		unsigned char igroup[3],ogroup[4];
    		int c,n;
    
    		igroup[0]= igroup[1]= igroup[2]= 0;
    
    		for(n= 0;n<3;n++){
    			if ( (c = inchar(fi)) == EOF) {
    				hiteof= 1;
    				break;
    			}
    
    			igroup[n]= (unsigned char)c;
    		}
    
    		if(n> 0){
    			ogroup[0]= dtable[igroup[0]>>2];
    			ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
    			ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
    			ogroup[3]= dtable[igroup[2]&0x3F];
    
    			if(n<3) {
    				ogroup[3]= '=';
    
    				if(n<2)
    					ogroup[2]= '=';
    			}
    
    			for(i= 0;i<4;i++)
    				ochar(ogroup[i], so);
    		}
    	}
    
    	if(fputs(eol,so)==EOF)
    		return 0;
    
    	fclose(fi);
    
    	return 1;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	FILE *p;
    	char date[256];
    	char host[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char who[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char bound[256];
    	char fname[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char dur[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	time_t t;
    	struct tm *tm;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *astattach;
    	struct ast_config *cfg;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p = popen(SENDMAIL, "w");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	cfg = ast_load(VOICEMAIL_CONFIG);
    	if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
    		astattach = "yes";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		gethostname(host, sizeof(host));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strchr(srcemail, '@'))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			strncpy(who, srcemail, sizeof(who)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else {
    			snprintf(who, sizeof(who), "%s@%s", srcemail, host);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		time(&t);
    		tm = localtime(&t);
    		strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", tm);
    		fprintf(p, "Date: %s\n", date);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(p, "From: Asterisk PBX <%s>\n", who);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(p, "To: %s <%s>\n", name, email);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox);
    		fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
    		fprintf(p, "MIME-Version: 1.0\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast_true(astattach)) {
    			// Something unique.
    			snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
    
    Mark Spencer's avatar
    Mark Spencer committed
    			fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			fprintf(p, "--%s\n", bound);
    		}
    			fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
    			strftime(date, sizeof(date), "%A, %B %d, %Y at %r", tm);
    			fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    		           "in mailbox %s from %s, on %s so you might\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    				   "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", name, 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				dur, msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
    		if (ast_true(astattach)) {
    			fprintf(p, "--%s\n", bound);
    			fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum, format);
    			fprintf(p, "Content-Transfer-Encoding: BASE64\n");
    			fprintf(p, "Content-Description: Voicemail sound attachment.\n");
    			fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
    
    			snprintf(fname, sizeof(fname), "%s.%s", attach, format);
    			base_encode(fname, p);
    			fprintf(p, "\n\n--%s--\n.\n", bound);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pclose(p);
    	} else {
    		ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
    		return -1;
    	}
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int get_date(char *s, int len)
    {
    	struct tm *tm;
    	time_t t;
    	t = time(0);
    	tm = localtime(&t);
    	return strftime(s, len, "%a %b %e %r %Z %Y", tm);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int invent_message(struct ast_channel *chan, char *ext, int busy, char *ecodes)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char fn[256];
    	snprintf(fn, sizeof(fn), "vm/%s/greet", ext);
    	if (ast_fileexists(fn, NULL, NULL) > 0) {
    		res = ast_streamfile(chan, fn, chan->language);
    		if (res)
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_waitstream(chan, ecodes);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res)
    			return res;
    	} else {
    		res = ast_streamfile(chan, "vm-theperson", chan->language);
    		if (res)
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_waitstream(chan, ecodes);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res)
    			return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_say_digit_str(chan, ext, ecodes, chan->language);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res)
    			return res;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (busy)
    		res = ast_streamfile(chan, "vm-isonphone", chan->language);
    	else
    		res = ast_streamfile(chan, "vm-isunavail", chan->language);
    	if (res)
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = ast_waitstream(chan, ecodes);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_config *cfg;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *copy, *name, *passwd, *email, *fmt, *fmts;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char comment[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *sfmt[MAX_OTHER_FORMATS];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char txtfile[256];
    	FILE *txt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res = -1, fmtcnt=0, x;
    	int msgnum;
    	int outmsg=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int wavother=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int maxmessage=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char date[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char dir[256];
    	char fn[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char prefile[256]="";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *astemail;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *ecodes = "#";
    	char *s;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	time_t start;
    	time_t end;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	cfg = ast_load(VOICEMAIL_CONFIG);
    	if (!cfg) {
    		ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG);
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
    		astemail = ASTERISK_USERNAME;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
    		if (sscanf(s, "%d", &x) == 1) {
    			maxmessage = x;
    		} else {
    			ast_log(LOG_WARNING, "Invalid max message time length\n");
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((copy = ast_variable_retrieve(cfg, NULL, ext))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char *stringp=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Setup pre-file if appropriate */
    		if (busy)
    			snprintf(prefile, sizeof(prefile), "vm/%s/busy", ext);
    		else if (unavail)
    			snprintf(prefile, sizeof(prefile), "vm/%s/unavail", ext);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Make sure they have an entry in the config */
    		copy = strdup(copy);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		stringp=copy;
    		passwd = strsep(&stringp, ",");
    		name = strsep(&stringp, ",");
    		email = strsep(&stringp, ",");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		make_dir(dir, sizeof(dir), ext, "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* It's easier just to try to make it than to check for its existence */
    		if (mkdir(dir, 0700) && (errno != EEXIST))
    			ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		make_dir(dir, sizeof(dir), ext, "INBOX");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (mkdir(dir, 0700) && (errno != EEXIST))
    			ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast_exists_extension(chan, chan->context, "o", 1, chan->callerid))
    			ecodes = "#0";
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Play the beginning intro if desired */
    		if (strlen(prefile)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (ast_fileexists(prefile, NULL, NULL) > 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (ast_streamfile(chan, prefile, chan->language) > -1) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				    silent = ast_waitstream(chan, "#0");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    				ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				silent = invent_message(chan, ext, busy, ecodes);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    			if (silent < 0) {
    				ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
    				free(copy);
    				return -1;
    			}
    		}
    		/* If they hit "#" we should still play the beep sound */
    		if (silent == '#') {
    			if (!ast_streamfile(chan, "beep", chan->language) < 0)
    				silent = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (ast_waitstream(chan, "") <0) {
    				ast_log(LOG_DEBUG, "Hangup during beep\n");
    				free(copy);
    				return -1;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (silent == '0') {
    			strncpy(chan->exten, "0", sizeof(chan->exten) - 1);
    			chan->priority = 0;
    			free(copy);
    			return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Stream an info message */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (silent || !ast_streamfile(chan, INTRO, chan->language)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Wait for the message to finish */
    			if (silent || !ast_waitstream(chan, "")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (!ast_streamfile(chan, "beep", chan->language) < 0)
    					silent = 1;
    				if (ast_waitstream(chan, "") <0) {
    					ast_log(LOG_DEBUG, "Hangup during beep\n");
    					free(copy);
    					return -1;
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				fmt = ast_variable_retrieve(cfg, "general", "format");
    				if (fmt) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					char *stringp=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					fmts = strdup(fmt);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					stringp=fmts;
    					fmt = strsep(&stringp, "|");
    
    Mark Spencer's avatar
    Mark Spencer committed
    					msgnum = 0;
    					do {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						make_file(fn, sizeof(fn), dir, msgnum);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
    											(chan->callerid ? chan->callerid : "Unknown"), 
    											name, ext, chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (ast_fileexists(fn, NULL, chan->language) > 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    							msgnum++;
    							continue;
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    						writer = ast_writefile(fn, fmt, comment, O_EXCL, 1 /* check for other formats */, 0700);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (!writer)
    
    Mark Spencer's avatar
    Mark Spencer committed
    							break;
    						msgnum++;
    					} while(!writer && (msgnum < MAXMSG));
    					if (writer) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						char *stringp=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* Store information */
    						snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
    
    Mark Spencer's avatar
    Mark Spencer committed
     						txt = fopen(txtfile, "w+");
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (txt) {
    							get_date(date, sizeof(date));
    
    Mark Spencer's avatar
    Mark Spencer committed
    							time(&start);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							fprintf(txt, 
    
    ";\n"
    "; Message Information file\n"
    ";\n"
    "[message]\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "origmailbox=%s\n"
    "context=%s\n"
    "exten=%s\n"
    "priority=%d\n"
    "callerchan=%s\n"
    "callerid=%s\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "origdate=%s\n"
    "origtime=%ld\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ext,
    	chan->context,
    	chan->exten,
    	chan->priority,
    	chan->name,
    	chan->callerid ? chan->callerid : "Unknown",
    
    Mark Spencer's avatar
    Mark Spencer committed
    	date, time(NULL));
    
    Mark Spencer's avatar
    Mark Spencer committed
    							fclose(txt);
    						} else
    							ast_log(LOG_WARNING, "Error opening text file for output\n");
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* We need to reset these values */
    						free(fmts);
    						fmt = ast_variable_retrieve(cfg, "general", "format");
    						fmts = strdup(fmt);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						stringp=fmts;
    						strsep(&stringp, "|");
    						while((fmt = strsep(&stringp, "|"))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if (fmtcnt > MAX_OTHER_FORMATS - 1) {
    								ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
    								break;
    							}
    							sfmt[fmtcnt++] = strdup(fmt);
    						}
    						for (x=0;x<fmtcnt;x++) {
    							others[x] = ast_writefile(fn, sfmt[x], comment, 0, 0, 0700);
    							if (!others[x]) {
    								/* Ick, the other format didn't work, but be sure not
    								   to leak memory here */
    								int y;
    								for(y=x+1;y < fmtcnt;y++)
    									free(sfmt[y]);
    								break;
    							}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if(!strcasecmp(sfmt[x], "wav"))
    								wavother++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    							free(sfmt[x]);
    						}
    						if (x == fmtcnt) {
    							/* Loop forever, writing the packets we read to the writer(s), until
    							   we read a # or get a hangup */
    							if (option_verbose > 2) 
    								ast_verbose( VERBOSE_PREFIX_3 "Recording to %s\n", fn);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							f = NULL;
    							for(;;) {
    								res = ast_waitfor(chan, 2000);
    								if (!res) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    									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;
    									}
    
    Mark Spencer's avatar
    Mark Spencer committed
    								}
    								
    								if (res < 0) {
    									f = NULL;
    									break;
    								}
    
    								f = ast_read(chan);
    								if (!f)
    									break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    								if (f->frametype == AST_FRAME_VOICE) {
    									/* Write the primary format */
    									res = ast_writestream(writer, f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    									if (res) {
    										ast_log(LOG_WARNING, "Error writing primary frame\n");
    										break;
    									}
    
    Mark Spencer's avatar
    Mark Spencer committed
    									/* And each of the others */
    
    Mark Spencer's avatar
    Mark Spencer committed
    									for (x=0;x<fmtcnt;x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    										res |= ast_writestream(others[x], f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    									}
    
    Mark Spencer's avatar
    Mark Spencer committed
    									/* Exit on any error */
    									if (res) {
    										ast_log(LOG_WARNING, "Error writing frame\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    										ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    										break;
    									}
    
    Mark Spencer's avatar
    Mark Spencer committed
    								} else if (f->frametype == AST_FRAME_DTMF) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    									if (f->subclass == '#') {
    										if (option_verbose > 2) 
    											ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
    										outmsg=2;
    
    Mark Spencer's avatar
    Mark Spencer committed
    										ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    										res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    										break;
    									}
    								}
    
    Mark Spencer's avatar
    Mark Spencer committed
    								ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    								time(&end);
    								if (maxmessage && (end - start > maxmessage)) {
    									if (option_verbose > 2) 
    										ast_verbose( VERBOSE_PREFIX_3 "Message is too long, ending it now...\n");
    									outmsg = 2;
    									res = 0;
    									break;
    								}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							}
    							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", fn, sfmt[x]); 
    							free(sfmt[x]);
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_closestream(writer);
    						for (x=0;x<fmtcnt;x++) {
    							if (!others[x])
    								break;
    							ast_closestream(others[x]);
    						}
    						if (outmsg) {
    							if (outmsg > 1) {
    								/* Let them know it worked */
    
    Mark Spencer's avatar
    Mark Spencer committed
    								ast_streamfile(chan, "vm-msgsaved", chan->language);
    
    Mark Spencer's avatar
    Mark Spencer committed
    								ast_waitstream(chan, "");
    							}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							txt = fopen(txtfile, "a");
    							if (txt) {
    								time(&end);
    								fprintf(txt, "duration=%ld\n", end-start);
    								fclose(txt);
    							}
    
    Mark Spencer's avatar
    Mark Spencer committed
    							/* Send e-mail if applicable */
    
    Mark Spencer's avatar
    Mark Spencer committed
    							if (email)
    
    Mark Spencer's avatar
    Mark Spencer committed
    								sendmail(astemail, email, name, msgnum, ext, chan->callerid, fn, wavother ? "wav" : fmts, end - start);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						}
    					} else {
    						if (msgnum < MAXMSG)
    							ast_log(LOG_WARNING, "Error writing to mailbox %s\n", ext);
    						else
    							ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext);
    					}
    					free(fmts);
    				} else 
    					ast_log(LOG_WARNING, "No format to save messages in \n");
    			}
    		} else
    			ast_log(LOG_WARNING, "Unable to playback instructions\n");
    			
    		free(copy);
    	} else
    		ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
    	ast_destroy(cfg);
    	/* Leave voicemail for someone */
    
    	manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *mbox(int id)
    {
    	switch(id) {
    	case 0:
    		return "INBOX";
    	case 1:
    		return "Old";
    	case 2:
    		return "Work";
    	case 3:
    		return "Family";
    	case 4:
    		return "Friends";
    	case 5:
    		return "Cust1";
    	case 6:
    		return "Cust2";
    	case 7:
    		return "Cust3";
    	case 8:
    		return "Cust4";
    	case 9:
    		return "Cust5";
    	default:
    		return "Unknown";
    	}
    }
    
    static int count_messages(char *dir)
    {
    	int x;
    	char fn[256];
    	for (x=0;x<MAXMSG;x++) {
    		make_file(fn, sizeof(fn), dir, x);
    		if (ast_fileexists(fn, NULL, NULL) < 1)
    			break;
    	}
    	return x;
    }
    
    static int 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);
    	return d;
    }
    
    static int say_and_wait(struct ast_channel *chan, int num)
    {
    	int d;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return d;
    }
    
    static int copy(char *infile, char *outfile)
    {
    	int ifd;
    	int ofd;
    	int res;
    	int len;
    	char buf[4096];
    	if ((ifd = open(infile, O_RDONLY)) < 0) {
    		ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
    		return -1;
    	}
    	if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
    		ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
    		close(ifd);
    		return -1;
    	}
    	do {
    		len = read(ifd, buf, sizeof(buf));
    		if (len < 0) {
    			ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
    			close(ifd);
    			close(ofd);
    			unlink(outfile);
    		}
    		if (len) {
    			res = write(ofd, buf, len);
    			if (res != len) {
    				ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
    				close(ifd);
    				close(ofd);
    				unlink(outfile);
    			}
    		}
    	} while(len);
    	close(ifd);
    	close(ofd);
    	return 0;
    }
    
    static int save_to_folder(char *dir, int msg, char *username, int box)
    {
    	char sfn[256];
    	char dfn[256];
    	char ddir[256];
    	char txt[256];
    	char ntxt[256];
    	char *dbox = mbox(box);
    	int x;
    	make_file(sfn, sizeof(sfn), dir, msg);
    	make_dir(ddir, sizeof(ddir), username, dbox);
    	mkdir(ddir, 0700);
    	for (x=0;x<MAXMSG;x++) {
    		make_file(dfn, sizeof(dfn), ddir, x);
    		if (ast_fileexists(dfn, NULL, NULL) < 0)
    			break;
    	}
    	if (x >= MAXMSG)
    		return -1;
    	ast_filecopy(sfn, dfn, NULL);
    	if (strcmp(sfn, dfn)) {
    		snprintf(txt, sizeof(txt), "%s.txt", sfn);
    		snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
    		copy(txt, ntxt);
    	}
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int adsi_logo(unsigned char *buf)
    {
    	int bytes = 0;
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
    	return bytes;
    }
    
    static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
    {
    	char buf[256];
    	int bytes=0;
    	int x;
    	char num[5];
    
    	*useadsi = 0;
    	bytes += adsi_data_mode(buf + bytes);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    
    	bytes = 0;
    	bytes += adsi_logo(buf);
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
    #ifdef DISPLAY
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
    #endif
    	bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
    	bytes += adsi_data_mode(buf + bytes);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    
    	if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
    		bytes = 0;
    		bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
    		bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
    		bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
    		bytes += adsi_voice_mode(buf + bytes, 0);
    		adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    		return 0;
    	}
    
    #ifdef DISPLAY
    	/* Add a dot */
    	bytes = 0;
    	bytes += adsi_logo(buf);
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
    	bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    #endif
    	bytes = 0;
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "4", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
    
    #ifdef DISPLAY
    	/* Add another dot */
    	bytes = 0;
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
    	bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    #endif
    
    	bytes = 0;
    	/* These buttons we load but don't use yet */
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
    
    #ifdef DISPLAY
    	/* Add another dot */
    	bytes = 0;
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
    	bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    #endif
    
    	bytes = 0;
    	for (x=0;x<5;x++) {
    		snprintf(num, sizeof(num), "%d", x);
    		bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
    	}
    	bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
    
    #ifdef DISPLAY
    	/* Add another dot */
    	bytes = 0;
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
    	bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    #endif
    
    	if (adsi_end_download(chan)) {
    		bytes = 0;
    		bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
    		bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
    		bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
    		bytes += adsi_voice_mode(buf + bytes, 0);
    		adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    		return 0;
    	}
    	bytes = 0;
    	bytes += adsi_download_disconnect(buf + bytes);
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
    
    	ast_log(LOG_DEBUG, "Done downloading scripts...\n");
    
    #ifdef DISPLAY
    	/* Add last dot */
    	bytes = 0;
    	bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
    	bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
    #endif
    	ast_log(LOG_DEBUG, "Restarting session...\n");
    
    	bytes = 0;
    	/* Load the session now */
    	if (adsi_load_session(chan, adapp, adver, 1) == 1) {
    		*useadsi = 1;
    		bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
    	} else
    		bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
    
     	adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
    	return 0;
    }
    
    static void adsi_begin(struct ast_channel *chan, int *useadsi)
    {
    	int x;
    
    	if (!adsi_available(chan))
    
    Mark Spencer's avatar
    Mark Spencer committed
    	x = adsi_load_session(chan, adapp, adver, 1);
    	if (x < 0)
    		return;