Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Voicemail System (did you ever think it could be so easy?)
*
* Copyright (C) 2003-2004, Digium Inc.
* Mark Spencer <markster@digium.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/pbx.h>
#include <asterisk/options.h>
#include <asterisk/config.h>
#include <asterisk/say.h>
#include <asterisk/module.h>
#include <asterisk/localtime.h>
Malcolm Davenport
committed
#include <asterisk/cli.h>
#include <asterisk/utils.h>
#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>
Malcolm Davenport
committed
#include <sys/types.h>
Malcolm Davenport
committed
#include <dirent.h>
#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 */
#define SENDMAIL "/usr/sbin/sendmail -t"
#define INTRO "vm-intro"
#define MAXMSG 100
#define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
#define BASEMAXINLINE 256
#define BASELINELEN 72
#define BASEMAXINLINE 256
#define eol "\r\n"
#define MAX_DATETIME_FORMAT 512
#define MAX_NUM_CID_CONTEXTS 10
static int load_config(void);
/* 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
*/
struct baseio {
int iocp;
int iolen;
int linelength;
int ateof;
unsigned char iobuf[BASEMAXINLINE];
};
/* Structure for linked list of users */
struct ast_vm_user {
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];
char uniqueid[20]; /* Unique integer identifier */
int attach;
int alloced;
int svmail;
int review;
int operator;
int envelope;
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;
};
Martin Pycko
committed
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);
Mark Spencer
committed
static int vm_delete(char *file);
static char ext_pass_cmd[128];
Martin Pycko
committed
static char *tdesc = "Comedian Mail (Voicemail System)";
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"
James Golovich
committed
"extension 'o' in the current context.\n"
James Golovich
committed
"If the caller presses '*' during the prompt, the call jumps to\n"
James Golovich
committed
"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";
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";
/* Leave a message */
static char *app = "VoiceMail";
/* Check mail, control, etc */
static char *app2 = "VoiceMailMain";
static char *app3 = "MailboxExists";
static char *app4 = "VMAuthenticate";
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 char externnotify[160];
static char vmfmts[80];
static int vmminmessage;
static int vmmaxmessage;
static int maxgreet;
static int skipms;
static int maxlogins;
static int reviewvm;
static int calloper;
static int saycidinfo;
static int svmailinfo;
static int hearenv;
static int saydurationinfo;
static int saydurationminfo;
Mark Spencer
committed
static int skipaftercmd;
static int forcenm;
static int forcegrt;
static char dialcontext[80];
static char callcontext[80];
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
static char *emailbody = NULL;
static int pbxskip = 0;
static char *emailsubject = NULL;
static char fromstring[100];
static char pagerfromstring[100];
static char emailtitle[100];
static char charset[32] = "ISO-8859-1";
static char adsifdn[4] = "\x00\x00\x00\x0F";
static char adsisec[4] = "\x9B\xDB\xF7\xAC";
static int adsiver = 1;
static void populate_defaults(struct ast_vm_user *vmu)
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);
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
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");
}
} 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);
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
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;
}
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);
}
}
}
static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
struct ast_vm_user *retval;
if (ivm)
retval=ivm;
else
retval=malloc(sizeof(struct ast_vm_user));
if (retval) {
memset(retval, 0, sizeof(struct ast_vm_user));
if (mailbox)
strncpy(retval->mailbox, mailbox, sizeof(retval->mailbox) - 1);
if (context)
strncpy(retval->context, context, sizeof(retval->context) - 1);
strncpy(retval->context, "default", sizeof(retval->context) - 1);
populate_defaults(retval);
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);
} else
apply_option(retval, tmp->name, tmp->value);
tmp = tmp->next;
}
} else {
if (!ivm)
free(retval);
}
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;
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;
}
} else
vmu = find_user_realtime(ivm, context, mailbox);
ast_mutex_unlock(&vmlock);
return vmu;
}
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;
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;
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
/* 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;
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) {
if (configin)
else
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));
return;
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
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--;
}
if (pass) {
*pass = '\0';
pass++;
if (*pass == '>')
pass++;
while (*pass && *pass < 33)
}
if (pass) {
rest = strchr(pass,',');
if (rest) {
*rest = '\0';
rest++;
} 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);
fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
} else {
/* Put it back like it was */
fprintf(configout, orig);
}
}
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);
}
Mark Spencer
committed
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;
}
inbuf(struct baseio *bio, FILE *fi)
if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
if (ferror(fi))
bio->ateof = 1;
bio->iolen= l;
bio->iocp= 0;
inchar(struct baseio *bio, FILE *fi)
if (bio->iocp>=bio->iolen) {
if (!inbuf(bio, fi))
return bio->iobuf[bio->iocp++];
ochar(struct baseio *bio, int c, FILE *so)
if (bio->linelength>=BASELINELEN) {
if (fputs(eol,so)==EOF)
bio->linelength= 0;
if (putc(((unsigned char)c),so)==EOF)
bio->linelength++;
return 1;
}
static int base_encode(char *filename, FILE *so)
{
unsigned char dtable[BASEMAXINLINE];
int i,hiteof= 0;
FILE *fi;
struct baseio bio;
memset(&bio, 0, sizeof(bio));
bio.iocp = BASEMAXINLINE;
if (!(fi = fopen(filename, "rb"))) {
ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
return -1;
}
dtable[i]= 'A'+i;
dtable[i+9]= 'J'+i;
dtable[26+i]= 'a'+i;
dtable[26+i+9]= 'j'+i;
}
dtable[i+18]= 'S'+i;
dtable[26+i+18]= 's'+i;
}
dtable[52+i]= '0'+i;
}
dtable[62]= '+';
dtable[63]= '/';
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) {
hiteof= 1;
break;
}
igroup[n]= (unsigned char)c;
}
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];
ochar(&bio, ogroup[i], so);
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)
/* 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);
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
committed
FILE *p=NULL;
int pfd;
Mark Spencer
committed
char tmp[80] = "/tmp/astmail-XXXXXX";
char tmp2[256];
struct vm_zone *the_zone = NULL;
James Golovich
committed
if (vmu && ast_strlen_zero(vmu->email)) {
ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
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);
Mark Spencer
committed
/* 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;
}
}
else {
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
Mark Spencer
committed
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
/* 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);
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
fprintf(p, emailtitle, msgnum + 1, mailbox) ;
fprintf(p,"\n") ;
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);
fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
if (attach_user_voicemail) {
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);
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 {
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);
}
if (attach_user_voicemail) {
fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, 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
committed
fclose(p);
snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
Mark Spencer
committed
ast_safe_system(tmp2);
ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
Mark Spencer
committed
FILE *p=NULL;
int pfd;
char date[256];
char host[256];
char who[256];
char dur[256];
Mark Spencer
committed
char tmp[80] = "/tmp/astmail-XXXXXX";
char tmp2[256];
struct vm_zone *the_zone = NULL;
pfd = mkstemp(tmp);
Mark Spencer
committed
if (pfd > -1) {
p = fdopen(pfd, "w");
if (!p) {
close(pfd);
pfd = -1;
}
}
if (p) {
gethostname(host, sizeof(host));
if (strchr(srcemail, '@'))
strncpy(who, srcemail, sizeof(who)-1);
else {
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
Mark Spencer
committed
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
/* 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;