Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Voicemail System (did you ever think it could be so easy?)
*
* Copyright (C) 2003, 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>
#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>
/* we define USESQLVM when we have MySQL or POSTGRES */
#ifdef USEMYSQLVM
#include <mysql/mysql.h>
#define USESQLVM 1
#endif
#ifdef USEPOSTGRESVM
/*
* PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
*/
#include <postgresql/libpq-fe.h>
#define USESQLVM 1
#endif
#ifndef USESQLVM
static inline int sql_init(void) { return 0; }
static inline void sql_close(void) { }
#endif
#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"
#define BASEMAXINLINE 256
#define BASELINELEN 72
#define BASEMAXINLINE 256
#define eol "\r\n"
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#define MAX_DATETIME_FORMAT 512
#define DIGITS_DIR AST_SOUNDS "/digits/"
struct baseio {
int iocp;
int iolen;
int linelength;
int ateof;
unsigned char iobuf[BASEMAXINLINE];
};
struct ast_vm_user {
char context[80];
char mailbox[80];
char password[80];
char fullname[80];
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";
static char *adsec = "_AST";
static char *addesc = "Comedian Mail";
static int adver = 1;
static char *synopsis_vm =
"Leave a voicemail message";
static char *descrip_vm =
" VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
"extension (must be configured in voicemail.conf). If the extension is\n"
"preceded by an 's' then instructions for leaving the message will be\n"
"skipped. If the extension is preceeded by 'u' then the \"unavailable\"\n"
"message will be played (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it\n"
"exists. If the extension is preceeded by a 'b' then the the busy message\n"
"will be played (that is, busy instead of unavail).\n"
"If the requested mailbox does not exist, and there exists a priority\n"
"n + 101, then that priority will be taken next.\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"
"a context is specified, logins are considered in that context only.\n"
"Returns -1 if the user hangs up or 0 otherwise.\n";
static char *capp = "VoiceMail2";
static char *app = "VoiceMail";
/* Check mail, control, etc */
static char *capp2 = "VoiceMailMain2";
static ast_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;
static char serveremail[80];
static char vmfmts[80];
static int vmmaxmessage;
static int maxgreet;
static int skipms;
static int maxlogins;
static char *emailbody = NULL;
static int pbxskip = 0;
static char fromstring[100];
static char emailtitle[100];
static void apply_options(struct ast_vm_user *vmu, char *options)
/* Destructively Parse options and apply */
char *stringp = ast_strdupa(options);
char *s;
char *var, *value;
while((s = strsep(&stringp, "|"))) {
value = s;
if ((var = strsep(&value, "=")) && value) {
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, "tz")) {
strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
}
}
}
#ifdef USEMYSQLVM
#include "mysql-vm-routines.h"
#endif
#ifdef USEPOSTGRESVM
PGconn *dbhandler;
char dboption[256];
ast_mutex_t postgreslock;
static int sql_init(void)
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
/* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
dbhandler=PQconnectdb(dboption);
if (PQstatus(dbhandler) == CONNECTION_BAD) {
ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
return(-1);
}
ast_mutex_init(&postgreslock);
/* fprintf(stderr,"postgres login OK\n"); */
return(0);
}
static void sql_close(void)
{
PQfinish(dbhandler);
}
static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
{
PGresult *PGSQLres;
int numFields, i;
char *fname;
char query[240];
char options[160] = "";
struct ast_vm_user *retval;
retval=malloc(sizeof(struct ast_vm_user));
/* fprintf(stderr,"postgres find_user:\n"); */
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);
}
else
{
strcpy(retval->context, "default");
}
if (*retval->context) {
sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", context, mailbox);
} else {
sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='default' AND mailbox='%s'", mailbox);
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
}
/* fprintf(stderr,"postgres find_user: query = %s\n",query); */
ast_mutex_lock(&postgreslock);
PGSQLres=PQexec(dbhandler,query);
if (PGSQLres!=NULL) {
if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
PQclear(PGSQLres);
PQreset(dbhandler);
ast_mutex_unlock(&postgreslock);
free(retval);
return(NULL);
} else {
numFields = PQnfields(PGSQLres);
/* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
if (PQntuples(PGSQLres) != 1) {
ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
PQclear(PGSQLres);
ast_mutex_unlock(&postgreslock);
free(retval);
return(NULL);
}
for (i=0; i<numFields; i++) {
fname = PQfname(PGSQLres,i);
if (!strcmp(fname, "password")) {
strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
} else if (!strcmp(fname, "fullname")) {
strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
} else if (!strcmp(fname, "email")) {
strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
} else if (!strcmp(fname, "pager")) {
strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
} else if (!strcmp(fname, "options")) {
strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
apply_options(retval, options);
}
}
}
PQclear(PGSQLres);
ast_mutex_unlock(&postgreslock);
return(retval);
}
else {
ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
ast_mutex_unlock(&postgreslock);
free(retval);
return(NULL);
}
/* not reached */
} /* malloc() retval */
return(NULL);
}
static void vm_change_password(struct ast_vm_user *vmu, char *password)
{
char query[400];
if (*vmu->context) {
sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->context, vmu->mailbox, vmu->password);
} else {
sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
}
/* fprintf(stderr,"postgres change_password: query = %s\n",query); */
ast_mutex_lock(&postgreslock);
PQexec(dbhandler, query);
strcpy(vmu->password, password);
ast_mutex_unlock(&postgreslock);
}
static void reset_user_pw(char *context, char *mailbox, char *password)
{
char query[320];
if (context) {
sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
} else {
sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
}
ast_mutex_lock(&postgreslock);
/* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
PQexec(dbhandler, query);
ast_mutex_unlock(&postgreslock);
}
#endif /* Postgres */
#ifndef USESQLVM
static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, 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;
}
}
ast_mutex_unlock(&vmlock);
return vmu;
}
static int reset_user_pw(char *context, char *mailbox, 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;
static void vm_change_password(struct ast_vm_user *vmu, 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;
char inbuf[256];
char orig[256];
char tmpin[AST_CONFIG_MAX_PATH];
char tmpout[AST_CONFIG_MAX_PATH];
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);
if (configin)
configout = fopen((char *)tmpout,"w+");
else
configout = NULL;
if(!configin || !configout) {
if (configin)
fclose(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;
}
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
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, vmu->mailbox) && !strcmp(pass, vmu->password)) {
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);
unlink((char *)tmpin);
rename((char *)tmpout,(char *)tmpin);
reset_user_pw(vmu->context, vmu->mailbox, newpassword);
strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
}
#endif
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);
inbuf(struct baseio *bio, FILE *fi)
if(bio->ateof)
if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
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) {
bio->linelength= 0;
}
if(putc(((unsigned char)c),so)==EOF)
return -1;
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;
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
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(&bio, 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(&bio, ogroup[i], so);
}
}
if(fputs(eol,so)==EOF)
return 0;
fclose(fi);
return 1;
}
static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration, int attach_user_voicemail)
{
FILE *p;
char date[256];
char host[256];
struct vm_zone *the_zone = NULL;
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);
else {
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
/* 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 (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)
fprintf(p, "From: %s <%s>\n", fromstring, who);
else
fprintf(p, "From: Asterisk PBX <%s>\n", who);
fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
if( *emailtitle)
{
fprintf(p, emailtitle, msgnum, mailbox) ;
fprintf(p,"\n") ;
}
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);
fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
fprintf(p, "MIME-Version: 1.0\n");
if (attach_user_voicemail) {
// Something unique.
snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
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);
pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
sprintf(passdata,"%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", (callerid ? callerid : "an unknown caller"));
pbx_builtin_setvar_helper(ast, "VM_DATE", date);
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, (callerid ? callerid : "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);
}
pclose(p);
} else {
ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
return -1;
}
return 0;
}
static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
{
FILE *p;
char date[256];
char host[256];
char who[256];
char dur[256];
time_t t;
struct tm tm;
struct vm_zone *the_zone = NULL;
p = popen(SENDMAIL, "w");
if (p) {
gethostname(host, sizeof(host));
if (strchr(srcemail, '@'))
strncpy(who, srcemail, sizeof(who)-1);
else {
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
}
snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
time(&t);
/* 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 (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);
fprintf(p, "Date: %s\n", date);
fprintf(p, "From: Asterisk PBX <%s>\n", who);
fprintf(p, "To: %s\n", pager);
fprintf(p, "Subject: New VM\n\n");
strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
fprintf(p, "New %s long msg in box %s\n"
"from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
pclose(p);
} else {
ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
return -1;
}
return 0;
}
localtime_r(&t,&tm);
return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
if (ast_fileexists(fn, NULL, NULL) > 0) {
res = ast_streamfile(chan, fn, chan->language);
if (res)
return -1;
if (res)
return res;
} else {
res = ast_streamfile(chan, "vm-theperson", chan->language);
if (res)
return -1;
res = ast_say_digit_str(chan, ext, ecodes, chan->language);
if (busy)
res = ast_streamfile(chan, "vm-isonphone", chan->language);
else
res = ast_streamfile(chan, "vm-isunavail", chan->language);
if (res)
return -1;
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 play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
{
char d, *fmts;
int x, fmtcnt=1, res=-1,outmsg=0;
struct ast_frame *f;
struct ast_filestream *others[MAX_OTHER_FORMATS];
char *stringp=NULL;
time_t start, end;
struct ast_dsp *sildet; /* silence detector dsp */
int totalsilence = 0;
int dspsilence = 0;
int gotsilence = 0; /* did we timeout for silence? */
int rfmt=0;
ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
if (playfile) {
d = play_and_wait(chan, playfile);
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
d = ast_streamfile(chan, "beep",chan->language);
if (!d)
d = ast_waitstream(chan,"");
if (d < 0)
return -1;
}
fmts = ast_strdupa(fmt);
stringp=fmts;
strsep(&stringp, "|");
ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
sfmt[0] = ast_strdupa(fmts);
while((fmt = strsep(&stringp, "|"))) {
if (fmtcnt > MAX_OTHER_FORMATS - 1) {
ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
break;
}
sfmt[fmtcnt++] = ast_strdupa(fmt);
}
if (maxtime)
time(&start);
for (x=0;x<fmtcnt;x++) {
others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
if (!others[x]) {
break;
}
}
sildet = ast_dsp_new(); //Create the silence detector
if (!sildet) {
ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
return -1;
}
ast_dsp_set_threshold(sildet, silencethreshold);
if (maxsilence > 0) {
rfmt = chan->readformat;
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
return -1;
}
}
if (x == fmtcnt) {
/* Loop forever, writing the packets we read to the writer(s), until
we read a # or get a hangup */
f = NULL;
for(;;) {
res = ast_waitfor(chan, 2000);
if (!res) {
ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
/* Try one more time in case of masq */
res = ast_waitfor(chan, 2000);
if (!res) {
ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
res = -1;
}
}
if (res < 0) {
f = NULL;
break;
}
f = ast_read(chan);
if (!f)
break;
if (f->frametype == AST_FRAME_VOICE) {
/* write each format */
for (x=0;x<fmtcnt;x++) {
res = ast_writestream(others[x], f);
}
/* Silence Detection */
if (maxsilence > 0) {
dspsilence = 0;
ast_dsp_silence(sildet, f, &dspsilence);
if (dspsilence)
totalsilence = dspsilence;
else
totalsilence = 0;
if (totalsilence > maxsilence) {
/* Ended happily with silence */
ast_frfree(f);
gotsilence = 1;
outmsg=2;
break;
}
}
/* Exit on any error */
if (res) {
ast_log(LOG_WARNING, "Error writing frame\n");
ast_frfree(f);
break;
}
} else if (f->frametype == AST_FRAME_VIDEO) {