Skip to content
Snippets Groups Projects
app_voicemail.c 147 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
    
     * Voicemail System
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 
    
     * Copyright (C) 2003-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
     */
    
    
    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/channel_pvt.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #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>
    
    #include <asterisk/dsp.h>
    
    #include <asterisk/localtime.h>
    
    #include <asterisk/utils.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>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <time.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    #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"
    
    
    /* Default mail command to mail voicemail. Change it with the
        mailcmd= command in voicemail.conf */
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define SENDMAIL "/usr/sbin/sendmail -t"
    
    #define INTRO "vm-intro"
    
    #define MAXMSG 100
    
    #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define BASEMAXINLINE 256
    #define BASELINELEN 72
    #define BASEMAXINLINE 256
    #define eol "\r\n"
    
    
    #define MAX_DATETIME_FORMAT	512
    
    #define MAX_NUM_CID_CONTEXTS 10
    
    
    /* Syntaxes supported, not really language codes.
    	en - English
    	de - German
    	es - Spanish
    	fr - French
    
    	nl - Dutch
    	pt - Portuguese
    
    German requires the following additional soundfile:
    1F	einE (feminine)
    
    Spanish requires the following additional soundfile:
    1M      un (masculine)
    
    Dutch, Portuguese & Spanish require the following additional soundfiles:
    vm-INBOXs	singular of 'new'
    vm-Olds		singular of 'old/heard/read'
    
    NB these are plural:
    vm-INBOX	nieuwe (nl)
    vm-Old		oude (nl)
    
    Dutch also uses:
    nl-om		'at'?
    
    Spanish also uses:
    vm-youhaveno
    
    
    
    Italian requires the following additional soundfile:
    
    For vm_intro_it:
    vm-nuovo	new
    vm-nuovi	new plural
    vm-vecchio	old
    vm-vecchi	old plural
    Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folderS, spelled among others when you have to change folder.
    For the above reasons, vm-INBOX and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
    
    
    struct baseio {
    	int iocp;
    	int iolen;
    	int linelength;
    	int ateof;
    	unsigned char iobuf[BASEMAXINLINE];
    };
    
    
    /* Structure for linked list of users */
    
    	char context[80];		/* Voicemail context */
    	char mailbox[80];		/* Mailbox id, unique within vm context */
    	char password[80];		/* Secret pin code, numbers only */
    	char fullname[80];		/* Full name, for directory app */
    	char email[80];			/* E-mail address */
    	char pager[80];			/* E-mail address to pager (no attachment) */
    	char serveremail[80];		/* From: Mail address */
    	char mailcmd[160];		/* Configurable mail command */
    	char language[MAX_LANGUAGE];    /* Config: Language setting */
    	char zonetag[80];		/* Time zone */
    
    	char callback[80];
    	char dialout[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char uniqueid[20];		/* Unique integer identifier */
    
    	char exit[80];
    
    	int review;
    	int operator;
    
    	int forcename;
    	int forcegreetings;
    
    	int sayduration;
    	int saydurationm;
    
    	struct ast_vm_user *next;
    };
    
    struct vm_zone {
    	char name[80];
    	char timezone[80];
    	char msg_format[512];
    	struct vm_zone *next;
    };
    
    struct vm_state {
    	char curbox[80];
    	char username[80];
    	char curdir[256];
    	char vmbox[256];
    	char fn[256];
    	char fn2[256];
    	int deleted[MAXMSG];
    	int heard[MAXMSG];
    	int curmsg;
    	int lastmsg;
    	int newmessages;
    	int oldmessages;
    	int starting;
    	int repeats;
    };
    
    static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
    static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
    static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration);
    
    static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc);
    
    static char ext_pass_cmd[128];
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *tdesc = "Comedian Mail (Voicemail System)";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *addesc = "Comedian Mail";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *synopsis_vm =
    "Leave a voicemail message";
    
    static char *descrip_vm =
    
    "  VoiceMail([s|u|b]extension[@context][&extension[@context]][...]):  Leaves"
    "voicemail for a given extension (must be configured in voicemail.conf).\n"
    
    " If the extension is preceded by \n"
    "* 's' then instructions for leaving the message will be skipped.\n"
    "* 'u' then the \"unavailable\" message will be played.\n"
    "  (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
    "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
    "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
    
    "extension 'o' in the current context.\n"
    
    "If the caller presses '*' during the prompt, the call jumps to\n"
    
    "extension 'a' in the current context.\n"
    
    "If the requested mailbox does not exist, and there exists a priority\n"
    "n + 101, then that priority will be taken next.\n"
    
    "When multiple mailboxes are specified, the unavailable or busy message\n"
    "will be taken from the first mailbox 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 =
    
    "  VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
    "for the checking of voicemail.  The mailbox can be passed as the option,\n"
    "which will stop the voicemail system from prompting the user for the mailbox.\n"
    "If the mailbox is preceded by 's' then the password check will be skipped.  If\n"
    
    "the mailbox is preceded by 'p' then the supplied mailbox is prepended to the\n"
    "user's entry and the resulting string is used as the mailbox number.  This is\n"
    "useful for virtual hosting of voicemail boxes.  If a context is specified,\n"
    "logins are considered in that voicemail context only.\n"
    
    "Returns -1 if the user hangs up or 0 otherwise.\n";
    
    static char *synopsis_vm_box_exists =
    "Check if vmbox exists";
    
    static char *descrip_vm_box_exists =
    "  MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
    "if the specified voice mailbox exists.\n";
    
    
    static char *synopsis_vmauthenticate =
    "Authenticate off voicemail passwords";
    
    static char *descrip_vmauthenticate =
    "  VMAuthenticate([mailbox][@context]): Behaves identically to the Authenticate\n"
    "application, with the exception that the passwords are taken from\n"
    "voicemail.conf.\n"
    "  If the mailbox is specified, only that mailbox's password will be considered\n"
    "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
    "be set with the authenticated mailbox.\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* Leave a message */
    static char *app = "VoiceMail";
    
    /* Check mail, control, etc */
    static char *app2 = "VoiceMailMain";
    
    
    static char *app4 = "VMAuthenticate";
    
    AST_MUTEX_DEFINE_STATIC(vmlock);
    
    struct ast_vm_user *users;
    struct ast_vm_user *usersl;
    struct vm_zone *zones = NULL;
    struct vm_zone *zonesl = NULL;
    static int attach_voicemail;
    static int maxsilence;
    static int silencethreshold = 128;
    static char serveremail[80];
    
    static char mailcmd[160];	/* Configurable mail cmd */
    
    static int vmmaxmessage;
    static int maxgreet;
    static int skipms;
    static int maxlogins;
    
    
    static int reviewvm;
    static int calloper;
    static int saycidinfo;
    
    static int saydurationinfo;
    static int saydurationminfo;
    
    static int forcenm;
    static int forcegrt;
    
    static char dialcontext[80];
    static char callcontext[80];
    
    static char exitcontext[80];
    
    
    static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
    
    
    
    static char *emailbody = NULL;
    static int pbxskip = 0;
    
    static char *emailsubject = NULL;
    
    static char charset[32] = "ISO-8859-1";
    
    static int directory_forward;
    
    static char adsifdn[4] = "\x00\x00\x00\x0F";
    static char adsisec[4] = "\x9B\xDB\xF7\xAC";
    static int adsiver = 1;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    STANDARD_LOCAL_USER;
    
    LOCAL_USER_DECL;
    
    
    static void populate_defaults(struct ast_vm_user *vmu)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	vmu->attach = -1;
    
    	if (reviewvm)
    		vmu->review = 1;
    	if (calloper)
    		vmu->operator = 1;
    	if (saycidinfo)
    		vmu->saycid = 1;
    
    	if (svmailinfo)
    		vmu->svmail = 1; 
    
    	if (hearenv)
    		vmu->envelope = 1;
    
    	if (saydurationinfo)
    		vmu->sayduration = 1;
    	if (saydurationminfo>0)
    		vmu->saydurationm = saydurationminfo;
    
    	if (forcenm)
    		vmu->forcename = 1;
    	if (forcegrt)
    		vmu->forcegreetings = 1;
    
    	if (callcontext)
    		strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
    	if (dialcontext)
    		strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
    
    	if (exitcontext)
    		strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!strcasecmp(var, "attach")) {
    		if (ast_true(value))
    			vmu->attach = 1;
    		else
    			vmu->attach = 0;
    	} else if (!strcasecmp(var, "serveremail")) {
    		strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
    	} else if (!strcasecmp(var, "language")) {
    		strncpy(vmu->language, value, sizeof(vmu->language) - 1);
    	} else if (!strcasecmp(var, "tz")) {
    		strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
    	} else if (!strcasecmp(var, "delete")) {
    		vmu->delete = ast_true(value);
    	} else if (!strcasecmp(var, "saycid")){
    		if (ast_true(value))
    			vmu->saycid = 1;
    		else
    			vmu->saycid = 0;
    	} else if (!strcasecmp(var,"sendvoicemail")){
    		if (ast_true(value))
    			vmu->svmail =1;
    		else
    			vmu->svmail =0;
    	} else if (!strcasecmp(var, "review")){
    		if (ast_true(value))
    			vmu->review = 1;
    		else
    			vmu->review = 0;
    	} else if (!strcasecmp(var, "operator")){
    		if (ast_true(value))
    			vmu->operator = 1;
    		else
    			vmu->operator = 0;
    	} else if (!strcasecmp(var, "envelope")){
    		if (ast_true(value))
    			vmu->envelope = 1;
    		else
    			vmu->envelope = 0;
    
    	} else if (!strcasecmp(var, "sayduration")){
    		if(ast_true(value))
    			vmu->sayduration = 1;
    		else
    			vmu->sayduration = 0;
    	} else if (!strcasecmp(var, "saydurationm")){
    		if (sscanf(value, "%d", &x) == 1) {
    			vmu->saydurationm = x;
    		} else {
    			ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else if (!strcasecmp(var, "forcename")){
    		if (ast_true(value))
    			vmu->forcename = 1;
    		else
    			vmu->forcename = 0;
    	} else if (!strcasecmp(var, "forcegreetings")){
    		if (ast_true(value))
    			vmu->forcegreetings = 1;
    		else
    			vmu->forcegreetings = 0;
    	} else if (!strcasecmp(var, "callback")) {
    		strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
    	} else if (!strcasecmp(var, "dialout")) {
    		strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
    	} else if (!strcasecmp(var, "exitcontext")) {
    		strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    	if (!ast_strlen_zero(vmu->uniqueid)) {
    		res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password);
    		if (!res)
    			strncpy(vmu->password, password, sizeof(vmu->password) - 1);
    		return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void apply_options(struct ast_vm_user *vmu, const char *options)
    {	/* Destructively Parse options and apply */
    	char *stringp;
    	char *s;
    	char *var, *value;
    	stringp = ast_strdupa(options);
    	while ((s = strsep(&stringp, "|"))) {
    		value = s;
    		if ((var = strsep(&value, "=")) && value) {
    			apply_option(vmu, var, value);
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_variable *var, *tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ivm)
    		retval=ivm;
    	else
    		retval=malloc(sizeof(struct ast_vm_user));
    
    		memset(retval, 0, sizeof(struct ast_vm_user));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!ivm)
    			retval->alloced=1;
    
    			strncpy(retval->mailbox, mailbox, sizeof(retval->mailbox) - 1);
    
    			strncpy(retval->context, context, sizeof(retval->context) - 1);
    
    			strncpy(retval->context, "default", sizeof(retval->context) - 1);
    
    		populate_defaults(retval);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", retval->context, NULL);
    		if (var) {
    			tmp = var;
    			while(tmp) {
    				printf("%s => %s\n", tmp->name, tmp->value);
    				if (!strcasecmp(tmp->name, "password")) {
    					strncpy(retval->password, tmp->value, sizeof(retval->password) - 1);
    				} else if (!strcasecmp(tmp->name, "uniqueid")) {
    					strncpy(retval->uniqueid, tmp->value, sizeof(retval->uniqueid) - 1);
    
    				} else if (!strcasecmp(tmp->name, "pager")) {
    					strncpy(retval->pager, tmp->value, sizeof(retval->pager) - 1);
    				} else if (!strcasecmp(tmp->name, "email")) {
    					strncpy(retval->email, tmp->value, sizeof(retval->email) - 1);
    				} else if (!strcasecmp(tmp->name, "fullname")) {
    					strncpy(retval->fullname, tmp->value, sizeof(retval->fullname) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else
    					apply_option(retval, tmp->name, tmp->value);
    				tmp = tmp->next;
    			} 
    		} else { 
    			if (!ivm) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    			retval = NULL;
    		}	
    	} 
    	return retval;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
    
    {
    	/* This function could be made to generate one from a database, too */
    	struct ast_vm_user *vmu=NULL, *cur;
    	ast_mutex_lock(&vmlock);
    	cur = users;
    
    	while (cur) {
    
    		if ((!context || !strcasecmp(context, cur->context)) &&
    			(!strcasecmp(mailbox, cur->mailbox)))
    				break;
    		cur=cur->next;
    	}
    	if (cur) {
    		if (ivm)
    			vmu = ivm;
    		else
    			/* Make a copy, so that on a reload, we have no race */
    			vmu = malloc(sizeof(struct ast_vm_user));
    		if (vmu) {
    			memcpy(vmu, cur, sizeof(struct ast_vm_user));
    			if (ivm)
    				vmu->alloced = 0;
    			else
    				vmu->alloced = 1;
    			vmu->next = NULL;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    		vmu = find_user_realtime(ivm, context, mailbox);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
    
    {
    	/* This function could be made to generate one from a database, too */
    	struct ast_vm_user *cur;
    	int res = -1;
    	ast_mutex_lock(&vmlock);
    	cur = users;
    
    	while (cur) {
    
    		if ((!context || !strcasecmp(context, cur->context)) &&
    			(!strcasecmp(mailbox, cur->mailbox)))
    				break;
    		cur=cur->next;
    	}
    	if (cur) {
    		strncpy(cur->password, newpass, sizeof(cur->password) - 1);
    		res = 0;
    	}
    	ast_mutex_unlock(&vmlock);
    	return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/*  There's probably a better way of doing this. */
    	/*  That's why I've put the password change in a separate function. */
    	/*  This could also be done with a database function */
    
    	FILE *configin;
    	FILE *configout;
    	int linenum=0;
    	char inbuf[256];
    	char orig[256];
    	char currcontext[256] ="";
    	char tmpin[AST_CONFIG_MAX_PATH];
    	char tmpout[AST_CONFIG_MAX_PATH];
    	char *user, *pass, *rest, *trim, *tempcontext;
    	struct stat statbuf;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!change_password_realtime(vmu, newpassword))
    		return;
    
    
    	tempcontext = NULL;
    	snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
    	snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
    	configin = fopen(tmpin,"r");
    	if (configin)
    		configout = fopen(tmpout,"w+");
    	else
    		configout = NULL;
    	if (!configin || !configout) {
    
    			fclose(configin);
    
    			ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
    		if (configout)
    			fclose(configout);
    		else
    			ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
    
    	while (!feof(configin)) {
    		/* Read in the line */
    		fgets(inbuf, sizeof(inbuf), configin);
    		linenum++;
    		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++;
    			/* check for '[' (opening of context name ) */
    			tempcontext = strchr(user, '[');
    			if (tempcontext) {
    				strncpy(currcontext, tempcontext +1, sizeof(currcontext) - 1);
    				/* now check for ']' */
    				tempcontext = strchr(currcontext, ']');
    				if (tempcontext) 
    					*tempcontext = '\0';
    				else
    					currcontext[0] = '\0';
    			}
    			pass = strchr(user, '=');
    			if (pass > user) {
    				trim = pass - 1;
    				while (*trim && *trim < 33) {
    					*trim = '\0';
    					trim--;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    			}
    			if (pass) {
    				*pass = '\0';
    				pass++;
    				if (*pass == '>')
    					pass++;
    				while (*pass && *pass < 33)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					pass++;
    
    			}
    			if (pass) {
    				rest = strchr(pass,',');
    				if (rest) {
    					*rest = '\0';
    					rest++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    			} else
    				rest = NULL;
    
    			/* Compare user, pass AND context */
    			if (user && *user && !strcmp(user, vmu->mailbox) &&
    				 pass && !strcmp(pass, vmu->password) &&
    				 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
    				/* This is the line */
    				if (rest) {
    					fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else {
    
    					fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    			} else {
    				/* Put it back like it was */
    				fprintf(configout, orig);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    		}
    	}
    	fclose(configin);
    	fclose(configout);
    
    	stat((char *)tmpin, &statbuf);
    	chmod((char *)tmpout, statbuf.st_mode);
    	chown((char *)tmpout, statbuf.st_uid, statbuf.st_gid);
    	unlink((char *)tmpin);
    	rename((char *)tmpout,(char *)tmpin);
    
    	reset_user_pw(vmu->context, vmu->mailbox, newpassword);
    	strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
    }
    
    
    static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
    {
    	char buf[255];
    	snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
    	ast_safe_system(buf);
    }
    
    
    static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
    {
    	return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
    }
    
    static int make_file(char *dest, int len, char *dir, int num)
    {
    	return snprintf(dest, len, "%s/msg%04d", dir, num);
    
    static int last_message_index(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-1;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int
    
    inbuf(struct baseio *bio, FILE *fi)
    
    	if (bio->ateof)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    
    	if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
    		if (ferror(fi))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 1;
    }
    
    static int 
    
    inchar(struct baseio *bio, FILE *fi)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	if (bio->iocp>=bio->iolen) {
    		if (!inbuf(bio, fi))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return EOF;
    
    	return bio->iobuf[bio->iocp++];
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int
    
    ochar(struct baseio *bio, int c, FILE *so)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	if (bio->linelength>=BASELINELEN) {
    		if (fputs(eol,so)==EOF)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    
    	if (putc(((unsigned char)c),so)==EOF)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 1;
    }
    
    static int base_encode(char *filename, FILE *so)
    {
    	unsigned char dtable[BASEMAXINLINE];
    	int i,hiteof= 0;
    	FILE *fi;
    
    	memset(&bio, 0, sizeof(bio));
    	bio.iocp = BASEMAXINLINE;
    
    	if (!(fi = fopen(filename, "rb"))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
    		return -1;
    	}
    
    
    	for (i= 0;i<9;i++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dtable[i+18]= 'S'+i;
    		dtable[26+i+18]= 's'+i;
    	}
    
    	for (i= 0;i<10;i++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		dtable[52+i]= '0'+i;
    	}
    	dtable[62]= '+';
    	dtable[63]= '/';
    
    
    	while (!hiteof){
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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(&bio, fi)) == EOF) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				hiteof= 1;
    				break;
    			}
    
    			igroup[n]= (unsigned char)c;
    		}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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];
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ogroup[3]= '=';
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ogroup[2]= '=';
    			}
    
    
    			for (i= 0;i<4;i++)
    
    	if (fputs(eol,so)==EOF)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    	fclose(fi);
    
    	return 1;
    }
    
    
    static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize)
    
    	char callerid[256];
    
    	/* Prepare variables for substition in email body and subject */
    	pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
    	pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
    
    	snprintf(passdata, passdatasize, "%d", msgnum);
    
    	pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
    	pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
    
    	pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
    	pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
    
    	pbx_builtin_setvar_helper(ast, "VM_DATE", date);
    }
    
    
    static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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];
    
    	char tmp[80] = "/tmp/astmail-XXXXXX";
    	char tmp2[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	time_t t;
    
    	struct vm_zone *the_zone = NULL;
    
    	if (vmu && ast_strlen_zero(vmu->email)) {
    
    Malcolm Davenport's avatar
    Malcolm Davenport committed
    		ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
    
    Malcolm Davenport's avatar
    Malcolm Davenport committed
    		return(0);
    	}
    
    	if (!strcmp(format, "wav49"))
    		format = "WAV";
    	ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
    
    	/* Make a temporary file instead of piping directly to sendmail, in case the mail
    	   command hangs */
    	pfd = mkstemp(tmp);
    	if (pfd > -1) {
    		p = fdopen(pfd, "w");
    		if (!p) {
    			close(pfd);
    			pfd = -1;
    		}
    	}
    
    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);
    		}
    
    		snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		time(&t);
    
    
    		/* Does this user have a timezone specified? */
    
    		if (!ast_strlen_zero(vmu->zonetag)) {
    
    			/* Find the zone in the list */
    			struct vm_zone *z;
    			z = zones;
    			while (z) {
    				if (!strcmp(z->name, vmu->zonetag)) {
    					the_zone = z;
    					break;
    				}
    				z = z->next;
    			}
    		}
    
    		if (the_zone)
    			ast_localtime(&t,&tm,the_zone->timezone);
    		else
    			ast_localtime(&t,&tm,NULL);
    
    		strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(p, "Date: %s\n", date);
    
    
    		if (*fromstring) {
    			struct ast_channel *ast = ast_channel_alloc(0);
    			if (ast) {
    				char *passdata;
    				int vmlen = strlen(fromstring)*3 + 200;
    				if ((passdata = alloca(vmlen))) {
    					memset(passdata, 0, vmlen);
    
    					prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
    
    					pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
    					fprintf(p, "From: %s <%s>\n",passdata,who);
    				} else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
    				ast_channel_free(ast);
    			} else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
    		} else
    
    			fprintf(p, "From: Asterisk PBX <%s>\n", who);
    		fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
    
    
    		if (emailsubject) {
    			struct ast_channel *ast = ast_channel_alloc(0);
    			if (ast) {
    				char *passdata;
    				int vmlen = strlen(emailsubject)*3 + 200;
    				if ((passdata = alloca(vmlen))) {
    					memset(passdata, 0, vmlen);
    
    					prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
    
    					pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
    					fprintf(p, "Subject: %s\n",passdata);
    				} else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
    				ast_channel_free(ast);
    			} else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
    		} else
    
    		if (*emailtitle) {
    
    			fprintf(p, emailtitle, msgnum + 1, mailbox) ;
    
    		} else if (pbxskip)
    
    			fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
    		else
    			fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(p, "MIME-Version: 1.0\n");
    
    			/* Something unique. */
    
    			snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
    
    			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=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
    
    		strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
    		if (emailbody) {
    			struct ast_channel *ast = ast_channel_alloc(0);
    			if (ast) {
    				char *passdata;
    				int vmlen = strlen(emailbody)*3 + 200;
    				if ((passdata = alloca(vmlen))) {
    					memset(passdata, 0, vmlen);
    
    					prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
    
    					pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
    					fprintf(p, "%s\n",passdata);
    				} else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
    				ast_channel_free(ast);
    			} else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
    		} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
    
    			"in mailbox %s from %s, on %s so you might\n"
    			"want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
    
    			dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			fprintf(p, "--%s\n", bound);
    
    			fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
    
    			fprintf(p, "Content-Transfer-Encoding: base64\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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);
    		}
    
    		fclose(p);
    		snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
    
    		ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	return 0;
    }
    
    
    static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char date[256];
    	char host[256];
    	char who[256];
    	char dur[256];
    
    	char tmp[80] = "/tmp/astmail-XXXXXX";
    	char tmp2[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	time_t t;
    	struct tm tm;
    
    	struct vm_zone *the_zone = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p) {
    		gethostname(host, sizeof(host));
    		if (strchr(srcemail, '@'))
    			strncpy(who, srcemail, sizeof(who)-1);
    		else {
    			snprintf(who, sizeof(who), "%s@%s", srcemail, host);