Merge tilghman's voicemail changes

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1250 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Mark Spencer 2003-08-02 18:11:21 +00:00
parent 0484eac549
commit 895b4077d1
2 changed files with 488 additions and 71 deletions

View File

@ -52,23 +52,17 @@
#define INTRO "vm-intro"
#define MAXMSG 100
#define MAX_OTHER_FORMATS 10
#define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
#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"
#define MAX_DATETIME_FORMAT 512
#define DIGITS_DIR AST_SOUNDS "/digits/"
struct baseio {
int iocp;
int iolen;
@ -85,11 +79,19 @@ struct ast_vm_user {
char email[80];
char pager[80];
char serveremail[80];
char zonetag[80];
int attach;
int alloced;
struct ast_vm_user *next;
};
struct vm_zone {
char name[80];
char timezone[80];
char msg_format[512];
struct vm_zone *next;
};
static char *tdesc = "Comedian Mail (Voicemail System)";
static char *adapp = "CoMa";
@ -134,6 +136,8 @@ static char *app2 = "VoiceMailMain2";
static pthread_mutex_t vmlock = AST_MUTEX_INITIALIZER;
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;
@ -156,7 +160,7 @@ LOCAL_USER_DECL;
static void apply_options(struct ast_vm_user *vmu, char *options)
{
/* Destructively Parse options and apply */
char *stringp = options;
char *stringp = strdupa(options);
char *s;
char *var, *value;
while((s = strsep(&stringp, "|"))) {
@ -169,6 +173,8 @@ static void apply_options(struct ast_vm_user *vmu, char *options)
vmu->attach = 0;
} else if (!strcasecmp(var, "serveremail")) {
strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
} else if (!strcasecmp(var, "tz")) {
strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
}
}
}
@ -212,62 +218,64 @@ static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, cha
retval=malloc(sizeof(struct ast_vm_user));
*retval->mailbox='\0';
*retval->context='\0';
*retval->password='\0';
*retval->fullname='\0';
*retval->email='\0';
*retval->pager='\0';
*retval->serveremail='\0';
retval->attach=-1;
retval->alloced=1;
retval->next=NULL;
if (mailbox) {
strcpy(retval->mailbox, mailbox);
}
if (context) {
strcpy(retval->context, context);
}
if (retval) {
*retval->mailbox='\0';
*retval->context='\0';
*retval->password='\0';
*retval->fullname='\0';
*retval->email='\0';
*retval->pager='\0';
*retval->serveremail='\0';
retval->attach=-1;
retval->alloced=1;
retval->next=NULL;
if (mailbox) {
strcpy(retval->mailbox, mailbox);
}
if (context) {
strcpy(retval->context, context);
}
if (*retval->context) {
sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE context='%s' AND mailbox='%s'", context, mailbox);
} else {
sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE mailbox='%s'", mailbox);
}
pthread_mutex_lock(&mysqllock);
mysql_query(dbhandler, query);
if ((result=mysql_store_result(dbhandler))!=NULL) {
if ((rowval=mysql_fetch_row(result))!=NULL) {
numFields=mysql_num_fields(result);
fields=mysql_fetch_fields(result);
for (i=0; i<numFields; i++) {
if (rowval[i]) {
if (!strcmp(fields[i].name, "password")) {
strcpy(retval->password, rowval[i]);
} else if (!strcmp(fields[i].name, "fullname")) {
strcpy(retval->fullname, rowval[i]);
} else if (!strcmp(fields[i].name, "email")) {
strcpy(retval->email, rowval[i]);
} else if (!strcmp(fields[i].name, "pager")) {
strcpy(retval->pager, rowval[i]);
} else if (!strcmp(fields[i].name, "options")) {
strncpy(options, rowval[i], sizeof(options) - 1);
apply_options(retval, options);
if (*retval->context) {
sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE context='%s' AND mailbox='%s'", context, mailbox);
} else {
sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE mailbox='%s'", mailbox);
}
pthread_mutex_lock(&mysqllock);
mysql_query(dbhandler, query);
if ((result=mysql_store_result(dbhandler))!=NULL) {
if ((rowval=mysql_fetch_row(result))!=NULL) {
numFields=mysql_num_fields(result);
fields=mysql_fetch_fields(result);
for (i=0; i<numFields; i++) {
if (rowval[i]) {
if (!strcmp(fields[i].name, "password")) {
strcpy(retval->password, rowval[i]);
} else if (!strcmp(fields[i].name, "fullname")) {
strcpy(retval->fullname, rowval[i]);
} else if (!strcmp(fields[i].name, "email")) {
strcpy(retval->email, rowval[i]);
} else if (!strcmp(fields[i].name, "pager")) {
strcpy(retval->pager, rowval[i]);
} else if (!strcmp(fields[i].name, "options")) {
strncpy(options, rowval[i], sizeof(options) - 1);
apply_options(retval, options);
}
}
}
mysql_free_result(result);
pthread_mutex_unlock(&mysqllock);
return(retval);
} else {
mysql_free_result(result);
pthread_mutex_unlock(&mysqllock);
free(retval);
return(NULL);
}
mysql_free_result(result);
pthread_mutex_unlock(&mysqllock);
return(retval);
} else {
mysql_free_result(result);
pthread_mutex_unlock(&mysqllock);
free(retval);
return(NULL);
}
pthread_mutex_unlock(&mysqllock);
free(retval);
}
pthread_mutex_unlock(&mysqllock);
free(retval);
return(NULL);
}
@ -946,6 +954,11 @@ static void free_user(struct ast_vm_user *vmu)
free(vmu);
}
static void free_zone(struct vm_zone *z)
{
free(z);
}
static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
{
char comment[256];
@ -1864,7 +1877,354 @@ static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
return res;
}
static int play_message(struct ast_channel *chan, struct vm_state *vms, int msg)
static int play_datetime_format(struct ast_channel *chan, time_t time, struct vm_state *vms, struct vm_zone *zone)
{
int d = 0, offset = 0, sndoffset = 0;
char sndfile[256], nextmsg[256];
struct tm tm;
char *tzenv, current_tz[256] = "", *qmark;
tzenv = getenv("TZ");
if (tzenv != NULL)
strncpy(current_tz, tzenv, sizeof(current_tz) - 1);
if (zone->timezone && strcmp(current_tz,zone->timezone)) {
setenv("TZ", zone->timezone, 1);
tzset();
localtime_r(&time, &tm);
if (tzenv != NULL)
setenv("TZ", current_tz, 1);
else
unsetenv("TZ");
} else {
/* No need to change the timezone */
localtime_r(&time, &tm);
}
/* Check for a subexpression */
if ((qmark = index(zone->msg_format, '?'))) {
/* TODO Allow subexpressions - we probably need to implement a parser here. */
}
for (offset=0 ; zone->msg_format[offset] != '\0' ; offset++) {
ast_log(LOG_NOTICE, "Parsing %c in %s\n", zone->msg_format[offset], zone->msg_format);
switch (zone->msg_format[offset]) {
/* NOTE: if you add more options here, please try to be consistent with strftime(3) */
case '\'':
/* Literal name of a sound file */
sndoffset=0;
for (sndoffset=0 ; zone->msg_format[++offset] != '\'' ; sndoffset++)
sndfile[sndoffset] = zone->msg_format[offset];
sndfile[sndoffset] = '\0';
snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
d = wait_file(chan,vms,nextmsg);
break;
case '$':
/* Ooooh, variables and/or expressions */
{
struct vm_zone z;
memcpy(&z,zone,sizeof(struct vm_zone));
pbx_substitute_variables_helper(chan, zone->msg_format + offset, z.msg_format, sizeof(z.msg_format));
d = play_datetime_format(chan, time, vms, &z);
/* Subtract one, so that when the for loop increments, we point at the nil */
offset = strlen(zone->msg_format) - 1;
}
break;
case 'A':
case 'a':
/* Sunday - Saturday */
snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "day-%d", tm.tm_wday);
d = wait_file(chan,vms,nextmsg);
break;
case 'B':
case 'b':
case 'h':
/* January - December */
snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "mon-%d", tm.tm_mon);
d = wait_file(chan,vms,nextmsg);
break;
case 'd':
case 'e':
/* First - Thirtyfirst */
if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "h-%d", tm.tm_mday);
d = wait_file(chan,vms,nextmsg);
} else if (tm.tm_mday == 31) {
/* "Thirty" and "first" */
d = wait_file(chan,vms,DIGITS_DIR "30");
if (!d) {
d = wait_file(chan,vms,DIGITS_DIR "h-1");
}
} else {
/* Between 21 and 29 - two sounds */
d = wait_file(chan,vms,DIGITS_DIR "20");
if (!d) {
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "h-%d", tm.tm_mday - 20);
d = wait_file(chan,vms,nextmsg);
}
}
break;
case 'Y':
/* Year */
if (tm.tm_year > 99) {
d = wait_file(chan,vms,DIGITS_DIR "2");
if (!d) {
d = wait_file(chan,vms,DIGITS_DIR "thousand");
}
if (tm.tm_year > 100) {
if (!d) {
/* This works until the end of 2020 */
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year - 100);
d = wait_file(chan,vms,nextmsg);
}
}
} else {
if (tm.tm_year < 1) {
/* I'm not going to handle 1900 and prior */
/* We'll just be silent on the year, instead of bombing out. */
} else {
d = wait_file(chan,vms,DIGITS_DIR "19");
if (!d) {
if (tm.tm_year < 20) {
/* 1901 - 1920 */
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year);
d = wait_file(chan,vms,nextmsg);
} else {
/* 1921 - 1999 */
int ten, one;
ten = tm.tm_year / 10;
one = tm.tm_year % 10;
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten * 10);
d = wait_file(chan,vms,nextmsg);
if (!d) {
if (one != 0) {
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
d = wait_file(chan,vms,nextmsg);
}
}
}
}
}
}
break;
case 'I':
case 'l':
/* 12-Hour */
if (tm.tm_hour == 0)
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "12");
else if (tm.tm_hour > 12)
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour - 12);
else
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour);
d = wait_file(chan,vms,nextmsg);
break;
case 'H':
case 'k':
/* 24-Hour */
if (zone->msg_format[offset] == 'H') {
/* e.g. oh-eight */
if (tm.tm_hour < 10) {
d = wait_file(chan,vms,DIGITS_DIR "oh");
}
} else {
/* e.g. eight */
if (tm.tm_hour == 0) {
d = wait_file(chan,vms,DIGITS_DIR "oh");
}
}
if (!d) {
if (tm.tm_hour != 0) {
snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/digits/%d", tm.tm_hour);
d = wait_file(chan,vms,nextmsg);
}
}
break;
case 'M':
/* Minute */
if (tm.tm_min == 0) {
d = wait_file(chan,vms,DIGITS_DIR "oclock");
} else if (tm.tm_min < 10) {
d = wait_file(chan,vms,DIGITS_DIR "oh");
if (!d) {
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
d = wait_file(chan,vms,nextmsg);
}
} else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
d = wait_file(chan,vms,nextmsg);
} else {
int ten, one;
ten = (tm.tm_min / 10) * 10;
one = (tm.tm_min % 10);
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten);
d = wait_file(chan,vms,nextmsg);
if (!d) {
/* Fifty, not fifty-zero */
if (one != 0) {
snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
d = wait_file(chan,vms,nextmsg);
}
}
}
break;
case 'P':
case 'p':
/* AM/PM */
if ((tm.tm_hour == 0) || (tm.tm_hour > 12))
snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "p-m");
else
snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "a-m");
d = wait_file(chan,vms,nextmsg);
break;
case 'Q':
/* Shorthand for "Today", "Yesterday", or ABdY */
{
struct timeval now;
struct tm tmnow;
time_t beg_today;
gettimeofday(&now,NULL);
localtime_r(&now.tv_sec,&tmnow);
tmnow.tm_hour = 0;
tmnow.tm_min = 0;
tmnow.tm_sec = 0;
beg_today = mktime(&tmnow);
if (beg_today < time) {
/* Today */
d = wait_file(chan,vms,DIGITS_DIR "today");
} else if (beg_today - 86400 < time) {
/* Yesterday */
d = wait_file(chan,vms,DIGITS_DIR "yesterday");
} else {
struct vm_zone z;
memcpy(&z, zone, sizeof(struct vm_zone));
strcpy(z.msg_format, "ABdY");
d = play_datetime_format(chan, time, vms, &z);
}
}
break;
case 'q':
/* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
{
struct timeval now;
struct tm tmnow;
time_t beg_today;
gettimeofday(&now,NULL);
localtime_r(&now.tv_sec,&tmnow);
tmnow.tm_hour = 0;
tmnow.tm_min = 0;
tmnow.tm_sec = 0;
beg_today = mktime(&tmnow);
if (beg_today < time) {
/* Today */
} else if (beg_today - 86400 < time) {
/* Yesterday */
d = wait_file(chan,vms,DIGITS_DIR "yesterday");
} else if (beg_today - 86400 * 6 < time) {
/* Within the last week */
struct vm_zone z;
memcpy(&z, zone, sizeof(struct vm_zone));
strcpy(z.msg_format, "A");
d = play_datetime_format(chan, time, vms, &z);
} else {
struct vm_zone z;
memcpy(&z, zone, sizeof(struct vm_zone));
strcpy(z.msg_format, "ABdY");
d = play_datetime_format(chan, time, vms, &z);
}
}
break;
case 'R':
{
struct vm_zone z;
memcpy(&z, zone, sizeof(struct vm_zone));
strcpy(z.msg_format, "HM");
d = play_datetime_format(chan, time, vms, &z);
}
break;
case ' ':
case ' ':
/* Just ignore spaces and tabs */
break;
default:
/* Unknown character */
ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c\n", zone->msg_format, zone->msg_format[offset]);
}
/* Jump out on DTMF */
if (d) {
break;
}
}
return d;
}
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
{
int res = 0;
char filename[256], *origtime, temp[256];
struct vm_zone *the_zone = NULL;
struct ast_config *msg_cfg;
time_t t;
struct timeval tv_now;
struct tm time_now, time_then;
make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
msg_cfg = ast_load(filename);
if (!msg_cfg) {
ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
return 0;
}
if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
return 0;
if (sscanf(origtime,"%ld",&t) < 1) {
ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
return 0;
}
ast_destroy(msg_cfg);
/* Does this user have a timezone specified? */
if (strlen(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 no zone, use a default */
if (!the_zone) {
the_zone = alloca(sizeof(struct vm_zone));
memset(the_zone,0,sizeof(struct vm_zone));
strncpy(the_zone->msg_format, "'vm-received' q 'digits/at' IMp", sizeof(the_zone->msg_format) - 1);
}
/* Set the DIFF_* variables */
localtime_r(&t, &time_now);
gettimeofday(&tv_now,NULL);
localtime_r(&tv_now.tv_sec,&time_then);
/* Day difference */
if (time_now.tm_year == time_then.tm_year)
sprintf(temp,"%d",time_now.tm_yday);
else
sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
/* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
res = play_datetime_format(chan, t, vms, the_zone);
pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
return res;
}
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
{
int res = 0;
vms->starting = 0;
@ -1881,7 +2241,10 @@ static int play_message(struct ast_channel *chan, struct vm_state *vms, int msg)
res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
}
}
if (!res)
res = play_message_datetime(chan,vmu,vms);
if (!res) {
make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
vms->heard[msg] = 1;
@ -2255,7 +2618,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
/* Fall through */
case '5':
if (vms.lastmsg > -1) {
cmd = play_message(chan, &vms, vms.curmsg);
cmd = play_message(chan, vmu, &vms, vms.curmsg);
} else {
cmd = play_and_wait(chan, "vm-youhave");
if (!cmd)
@ -2291,7 +2654,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
case '4':
if (vms.curmsg) {
vms.curmsg--;
cmd = play_message(chan, &vms, vms.curmsg);
cmd = play_message(chan, vmu, &vms, vms.curmsg);
} else {
cmd = play_and_wait(chan, "vm-nomore");
}
@ -2299,7 +2662,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
case '6':
if (vms.curmsg < vms.lastmsg) {
vms.curmsg++;
cmd = play_message(chan, &vms, vms.curmsg);
cmd = play_message(chan, vmu, &vms, vms.curmsg);
} else {
cmd = play_and_wait(chan, "vm-nomore");
}
@ -2471,6 +2834,7 @@ static int append_mailbox(char *context, char *mbox, char *data)
static int load_config(void)
{
struct ast_vm_user *cur, *l;
struct vm_zone *zcur, *zl;
struct ast_config *cfg;
char *cat;
struct ast_variable *var;
@ -2490,6 +2854,14 @@ static int load_config(void)
cur = cur->next;
free_user(l);
}
zcur = zones;
while(zcur) {
zl = zcur;
zcur = zcur->next;
free_zone(zl);
}
zones = NULL;
zonesl = NULL;
users = NULL;
usersl = NULL;
if (cfg) {
@ -2568,20 +2940,55 @@ static int load_config(void)
} else {
strcpy(dbname, s);
}
#else
#endif
cat = ast_category_browse(cfg, NULL);
while(cat) {
if (strcasecmp(cat, "general")) {
/* Process mailboxes in this context */
var = ast_variable_browse(cfg, cat);
while(var) {
append_mailbox(cat, var->name, var->value);
var = var->next;
if (strcasecmp(cat, "zonemessages")) {
#ifndef USEMYSQLVM
/* Process mailboxes in this context */
while(var) {
append_mailbox(cat, var->name, var->value);
var = var->next;
}
#endif
} else {
/* Timezones in this context */
while(var) {
struct vm_zone *z;
z = malloc(sizeof(struct vm_zone));
if (z != NULL) {
char *msg_format, *timezone;
msg_format = strdupa(var->value);
if (msg_format != NULL) {
timezone = strsep(&msg_format, "|");
strncpy(z->name, var->name, sizeof(z->name) - 1);
strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
z->next = NULL;
if (zones) {
zonesl->next = z;
zonesl = z;
} else {
zones = z;
zonesl = z;
}
} else {
ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
free(z);
return -1;
}
} else {
ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
return -1;
}
var = var->next;
}
}
}
cat = ast_category_browse(cfg, cat);
}
#endif
memset(fromstring,0,sizeof(fromstring));
memset(emailtitle,0,sizeof(emailtitle));
if (emailbody) {

View File

@ -37,7 +37,7 @@ maxlogins=3
;
[default]
1234 => 4242,Example Mailbox,root@localhost
;4200 => 9855,Mark Spencer,markster@linux-support.net,mypager@digium.com,attach=no|serveremail=myaddy@digium.com
;4200 => 9855,Mark Spencer,markster@linux-support.net,mypager@digium.com,attach=no|serveremail=myaddy@digium.com|tz=central
;4300 => 3456,Ben Rigas,ben@american-computer.net
;4310 => 5432,Sales,sales@marko.net
;4069 => 6522,Matt Brooks,matt@marko.net
@ -51,3 +51,13 @@ maxlogins=3
[other]
1234 => 5678,Company2 User,root@localhost
;
; Users may be located in different timezones. Set the message and the
; timezone each user hears here. Set the user into one of these zones with
; the tz= attribute in the options field of the mailbox.
;
[zonemessages]
eastern=America/NewYork|'vm-received' Q 'digits/at' IMp
central=America/Chicago|'vm-received' Q 'digits/at' IMp
central24=America/Chicago|'vm-received' 'digits/at' H 'digits/hundred' M