diff options
author | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2003-11-13 01:47:23 +0000 |
---|---|---|
committer | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2003-11-13 01:47:23 +0000 |
commit | 031a2bae1d243c964ef487a482775c9215c99090 (patch) | |
tree | 1c2e5619f3f5fd04fa74c12b36c6e48a7af56d6e | |
parent | 5244329d3c18aa5f9443c8b2ad162c6b2876e9ac (diff) |
Move voicemail2 to voicemail, retain compatibility with "Voicemail2"
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@1735 f38db490-d61c-443f-a65b-d21fe96a405b
-rwxr-xr-x | Makefile | 1 | ||||
-rwxr-xr-x | apps/Makefile | 2 | ||||
-rwxr-xr-x | apps/app_voicemail.c | 2700 | ||||
-rwxr-xr-x | apps/app_voicemail2.c | 2965 |
4 files changed, 1685 insertions, 3983 deletions
@@ -313,6 +313,7 @@ bininstall: all rm -f $(DESTDIR)$(MODULES_DIR)/cdr_mysql.so rm -f $(DESTDIR)$(MODULES_DIR)/codec_mp3_d.so rm -f $(DESTDIR)$(MODULES_DIR)/format_mp3.so + rm -f $(DESTDIR)$(MODULES_DIR)/app_voicemail2.so mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/sounds mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/keys diff --git a/apps/Makefile b/apps/Makefile index daba282d2..c3910a74a 100755 --- a/apps/Makefile +++ b/apps/Makefile @@ -23,7 +23,7 @@ APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_mp3.so\ app_setcidname.so app_lookupcidname.so app_substring.so app_macro.so \ app_authenticate.so app_softhangup.so app_lookupblacklist.so \ app_waitforring.so app_privacy.so app_db.so app_chanisavail.so \ - app_enumlookup.so app_voicemail2.so app_transfer.so app_setcidnum.so app_cdr.so \ + app_enumlookup.so app_transfer.so app_setcidnum.so app_cdr.so \ app_hasnewvoicemail.so app_sayunixtime.so app_cut.so app_read.so ifneq (${OSARCH},Darwin) diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 8c2a23f9d..0b45b0133 100755 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -3,9 +3,9 @@ * * Voicemail System (did you ever think it could be so easy?) * - * Copyright (C) 1999, Mark Spencer + * Copyright (C) 2003, Digium Inc. * - * Mark Spencer <markster@linux-support.net> + * Mark Spencer <markster@digium.com> * * This program is free software, distributed under the terms of * the GNU General Public License @@ -15,6 +15,7 @@ #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> @@ -24,6 +25,7 @@ #include <asterisk/app.h> #include <asterisk/manager.h> #include <asterisk/dsp.h> +#include <asterisk/localtime.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> @@ -34,6 +36,25 @@ #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 + #include <pthread.h> #include "../asterisk.h" #include "../astconf.h" @@ -48,28 +69,45 @@ #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" -static int iocp; -static int iolen; -static int linelength; -static int ateof; -static unsigned char iobuf[BASEMAXINLINE]; +#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)"; @@ -85,51 +123,301 @@ static char *synopsis_vm = "Leave a voicemail message"; static char *descrip_vm = -" VoiceMail([s|u|b]extension): Leaves voicemail for a given extension (must\n" -"be configured in voicemail.conf). If the extension is preceeded by an 's'" -"then instructions for leaving the message will be skipped. If the extension\n" -"is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n" -"/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists. If the extension\n" -"is preceeded by a 'b' then the the busy message will be played (that is,\n" -"busy instead of unavail). At most one of 's', 'u', or 'b' may be specified.\n" -"Returns -1 on error or mailbox not found, or if the user hangs up. \n" -"Otherwise, it returns 0. \n"; +" 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" +"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(): Enters the main voicemail system for the checking of\n" -"voicemail. The mailbox can be passed as the option, which will stop the\n" -"voicemail system from prompting the user for the mailbox. If the mailbox\n" -"is preceded by 's' then the password check will be skipped. Returns -1 if\n" -"the user hangs up or 0 otherwise.\n"; +" 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"; /* Leave a message */ +static char *capp = "VoiceMail2"; static char *app = "VoiceMail"; /* Check mail, control, etc */ +static char *capp2 = "VoiceMailMain2"; static char *app2 = "VoiceMailMain"; +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]; + STANDARD_LOCAL_USER; LOCAL_USER_DECL; -static int make_dir(char *dest, int len, char *ext, char *mailbox) +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) { - return snprintf(dest, len, "%s/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,"vm", ext, mailbox); + 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 int make_file(char *dest, int len, char *dir, int num) +static void sql_close(void) { - return snprintf(dest, len, "%s/msg%04d", dir, num); + PQfinish(dbhandler); } -static int vm_change_password(char *username, char *password, char *newpassword) + +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); + } + + 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 mailbox='%s'", mailbox); + } +/* 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]; @@ -137,10 +425,24 @@ static int vm_change_password(char *username, char *password, char *newpassword) char tmpin[AST_CONFIG_MAX_PATH]; char tmpout[AST_CONFIG_MAX_PATH]; char *user, *pass, *rest, *trim; - 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); + snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR); + snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR); configin = fopen((char *)tmpin,"r"); - configout = fopen((char *)tmpout,"w+"); + 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; + } while (!feof(configin)) { /* Read in the line */ @@ -180,12 +482,12 @@ static int vm_change_password(char *username, char *password, char *newpassword) } } else rest = NULL; - if (user && pass && *user && *pass && !strcmp(user, username) && !strcmp(pass, password)) { + if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) { /* This is the line */ if (rest) { - fprintf(configout, "%s => %s,%s\n", username,newpassword,rest); + fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest); } else { - fprintf(configout, "%s => %s\n", username,newpassword); + fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword); } } else { /* Put it back like it was */ @@ -198,55 +500,67 @@ static int vm_change_password(char *username, char *password, char *newpassword) unlink((char *)tmpin); rename((char *)tmpout,(char *)tmpin); - return(1); + 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); } static int -inbuf(FILE *fi) +inbuf(struct baseio *bio, FILE *fi) { int l; - if(ateof) + if(bio->ateof) return 0; - if ( (l = fread(iobuf,1,BASEMAXINLINE,fi)) <= 0) { + if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) { if(ferror(fi)) return -1; - ateof = 1; + bio->ateof = 1; return 0; } - iolen= l; - iocp= 0; + bio->iolen= l; + bio->iocp= 0; return 1; } static int -inchar(FILE *fi) +inchar(struct baseio *bio, FILE *fi) { - if(iocp>=iolen) - if(!inbuf(fi)) + if(bio->iocp>=bio->iolen) + if(!inbuf(bio, fi)) return EOF; - return iobuf[iocp++]; + return bio->iobuf[bio->iocp++]; } static int -ochar(int c, FILE *so) +ochar(struct baseio *bio, int c, FILE *so) { - if(linelength>=BASELINELEN) { + if(bio->linelength>=BASELINELEN) { if(fputs(eol,so)==EOF) return -1; - linelength= 0; + bio->linelength= 0; } if(putc(((unsigned char)c),so)==EOF) return -1; - linelength++; + bio->linelength++; return 1; } @@ -256,11 +570,10 @@ static int base_encode(char *filename, FILE *so) unsigned char dtable[BASEMAXINLINE]; int i,hiteof= 0; FILE *fi; + struct baseio bio; - linelength = 0; - iocp = BASEMAXINLINE; - iolen = 0; - ateof = 0; + 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)); @@ -290,7 +603,7 @@ static int base_encode(char *filename, FILE *so) igroup[0]= igroup[1]= igroup[2]= 0; for(n= 0;n<3;n++){ - if ( (c = inchar(fi)) == EOF) { + if ( (c = inchar(&bio, fi)) == EOF) { hiteof= 1; break; } @@ -312,7 +625,7 @@ static int base_encode(char *filename, FILE *so) } for(i= 0;i<4;i++) - ochar(ogroup[i], so); + ochar(&bio, ogroup[i], so); } } @@ -324,7 +637,7 @@ static int base_encode(char *filename, FILE *so) return 1; } -static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration) +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]; @@ -335,12 +648,12 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m char dur[256]; time_t t; struct tm tm; - char *astattach; - struct ast_config *cfg; + 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); p = popen(SENDMAIL, "w"); - cfg = ast_load(VOICEMAIL_CONFIG); - if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) - astattach = "yes"; if (p) { gethostname(host, sizeof(host)); if (strchr(srcemail, '@')) @@ -350,15 +663,47 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m } snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60); time(&t); - localtime_r(&t,&tm); + + /* 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 <%s>\n", name, email); - fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox); + + 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 (ast_true(astattach)) { + if (attach_user_voicemail) { // Something unique. snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid()); @@ -366,16 +711,37 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m fprintf(p, "--%s\n", bound); } - fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n"); - strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm); + fprintf(p, "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", name, - dur, msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date); - if (ast_true(astattach)) { + "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, "--%s\n", bound); - fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum, format); + fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum + 1, 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); @@ -392,7 +758,7 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m return 0; } -static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration) +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]; @@ -401,9 +767,8 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char char dur[256]; time_t t; struct tm tm; - struct ast_config *cfg; + struct vm_zone *the_zone = NULL; p = popen(SENDMAIL, "w"); - cfg = ast_load(VOICEMAIL_CONFIG); if (p) { gethostname(host, sizeof(host)); @@ -414,7 +779,26 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char } snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60); time(&t); - localtime_r(&t,&tm); + + /* 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); @@ -440,11 +824,11 @@ static int get_date(char *s, int len) return strftime(s, len, "%a %b %e %r %Z %Y", &tm); } -static int invent_message(struct ast_channel *chan, char *ext, int busy, char *ecodes) +static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes) { int res; char fn[256]; - snprintf(fn, sizeof(fn), "vm/%s/greet", ext); + 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) @@ -473,81 +857,256 @@ static int invent_message(struct ast_channel *chan, char *ext, int busy, char *e return res; } -static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail) +static int play_and_wait(struct ast_channel *chan, char *fn) { - struct ast_config *cfg; - char *copy, *name, *passwd, *email, *pager, *fmt, *fmts; + 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; char comment[256]; - struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS]; + int x, fmtcnt=1, res=-1,outmsg=0; + struct ast_frame *f; + struct ast_filestream *others[MAX_OTHER_FORMATS]; char *sfmt[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); + if (!d) + 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) { + /* Write only once */ + ast_writestream(others[0], f); + } else if (f->frametype == AST_FRAME_DTMF) { + if (f->subclass == '#') { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass); + res = '#'; + outmsg = 2; + ast_frfree(f); + break; + } + } + if (maxtime) { + time(&end); + if (maxtime < (end - start)) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n"); + res = 't'; + ast_frfree(f); + break; + } + } + ast_frfree(f); + } + if (!f) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "User hung up\n"); + res = -1; + outmsg=1; + } + } else { + ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); + } + + for (x=0;x<fmtcnt;x++) { + if (!others[x]) + break; + if (totalsilence) + ast_stream_rewind(others[x], totalsilence-200); + else + ast_stream_rewind(others[x], 200); + ast_truncstream(others[x]); + ast_closestream(others[x]); + } + if (rfmt) { + if (ast_set_read_format(chan, rfmt)) { + ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); + } + } + if (outmsg) { + if (outmsg > 1) { + /* Let them know it worked */ + ast_streamfile(chan, "vm-msgsaved", chan->language); + ast_waitstream(chan, ""); + } + } + + + return res; +} + +static void free_user(struct ast_vm_user *vmu) +{ + if (vmu->alloced) + 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]; char txtfile[256]; FILE *txt; - int res = -1, fmtcnt=0, x; + int res = 0; int msgnum; - int outmsg=0; - int wavother=0; - int maxmessage=0; - struct ast_frame *f; char date[256]; char dir[256]; char fn[256]; char prefile[256]=""; - char *astemail; + char fmt[80]; + char *context; char *ecodes = "#"; - char *s; + char *stringp; time_t start; time_t end; - struct ast_dsp *sildet; /* silence detector dsp */ - int totalsilence = 0; - int dspsilence = 0; - int silence = 0; /* amount of silence to allow */ - int gotsilence = 0; /* did we timeout for silence? */ - char *silencestr; - char *thresholdstr; - int rfmt; - int threshold = 128; - - cfg = ast_load(VOICEMAIL_CONFIG); - if (!cfg) { - ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG); - return -1; - } - if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) { - silence = atoi(silencestr); - if (silence > 0) - silence *= 1000; - } - if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold"))) - threshold = atoi(thresholdstr); - - if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) - astemail = ASTERISK_USERNAME; - if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) { - if (sscanf(s, "%d", &x) == 1) { - maxmessage = x; - } else { - ast_log(LOG_WARNING, "Invalid max message time length\n"); - } + char tmp[256] = ""; + struct ast_vm_user *vmu; + struct ast_vm_user svm; + + strncpy(tmp, ext, sizeof(tmp) - 1); + ext = tmp; + context = strchr(tmp, '@'); + if (context) { + *context = '\0'; + context++; } - if ((copy = ast_variable_retrieve(cfg, NULL, ext))) { - char *stringp=NULL; + + if ((vmu = find_user(&svm, context, ext))) { /* Setup pre-file if appropriate */ if (busy) - snprintf(prefile, sizeof(prefile), "vm/%s/busy", ext); + snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext); else if (unavail) - snprintf(prefile, sizeof(prefile), "vm/%s/unavail", ext); - /* Make sure they have an entry in the config */ - copy = strdup(copy); - stringp=copy; - passwd = strsep(&stringp, ","); - name = strsep(&stringp, ","); - email = strsep(&stringp, ","); - pager = strsep(&stringp, ","); - make_dir(dir, sizeof(dir), ext, ""); + snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext); + make_dir(dir, sizeof(dir), vmu->context, "", ""); /* It's easier just to try to make it than to check for its existence */ if (mkdir(dir, 0700) && (errno != EEXIST)) ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno)); - make_dir(dir, sizeof(dir), ext, "INBOX"); + make_dir(dir, sizeof(dir), vmu->context, ext, ""); + /* It's easier just to try to make it than to check for its existence */ + if (mkdir(dir, 0700) && (errno != EEXIST)) + ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno)); + make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX"); if (mkdir(dir, 0700) && (errno != EEXIST)) ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno)); if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid)) @@ -556,75 +1115,71 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int if (strlen(prefile)) { if (ast_fileexists(prefile, NULL, NULL) > 0) { if (ast_streamfile(chan, prefile, chan->language) > -1) - silent = ast_waitstream(chan, "#0"); + res = ast_waitstream(chan, "#0"); } else { ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile); - silent = invent_message(chan, ext, busy, ecodes); + res = invent_message(chan, vmu->context, ext, busy, ecodes); } - if (silent < 0) { + if (res < 0) { ast_log(LOG_DEBUG, "Hang up during prefile playback\n"); - free(copy); + free_user(vmu); return -1; } } - /* If they hit "#" we should still play the beep sound */ - if (silent == '#') { - if (!ast_streamfile(chan, "beep", chan->language) < 0) + if (res == '#') { + /* On a '#' we skip the instructions */ + silent = 1; + res = 0; + } + if (!res && !silent) { + res = ast_streamfile(chan, INTRO, chan->language); + if (!res) + res = ast_waitstream(chan, ecodes); + if (res == '#') { silent = 1; - if (ast_waitstream(chan, "") <0) { - ast_log(LOG_DEBUG, "Hangup during beep\n"); - free(copy); - return -1; + res = 0; } - } else if (silent == '0') { + } + /* Check for a '0' here */ + if (res == '0') { strncpy(chan->exten, "o", sizeof(chan->exten) - 1); if (strlen(chan->macrocontext)) strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1); chan->priority = 0; - free(copy); + free_user(vmu); return 0; } - /* Stream an info message */ - if (silent || !ast_streamfile(chan, INTRO, chan->language)) { - /* Wait for the message to finish */ - if (silent || !ast_waitstream(chan, "")) { - if (!ast_streamfile(chan, "beep", chan->language) < 0) - silent = 1; - if (ast_waitstream(chan, "") <0) { - ast_log(LOG_DEBUG, "Hangup during beep\n"); - free(copy); - return -1; - } - fmt = ast_variable_retrieve(cfg, "general", "format"); - if (fmt) { - char *stringp=NULL; - fmts = strdup(fmt); - stringp=fmts; - fmt = strsep(&stringp, "|"); - msgnum = 0; - do { - make_file(fn, sizeof(fn), dir, msgnum); - snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n", - (chan->callerid ? chan->callerid : "Unknown"), - name, ext, chan->name); - if (ast_fileexists(fn, NULL, chan->language) > 0) { - msgnum++; - continue; - } - writer = ast_writefile(fn, fmt, comment, O_EXCL, 1 /* check for other formats */, 0700); - if (!writer) - break; - msgnum++; - } while(!writer && (msgnum < MAXMSG)); - if (writer) { - char *stringp=NULL; - /* Store information */ - snprintf(txtfile, sizeof(txtfile), "%s.txt", fn); - txt = fopen(txtfile, "w+"); - if (txt) { - get_date(date, sizeof(date)); - time(&start); - fprintf(txt, + if (res >= 0) { + /* Unless we're *really* silent, try to send the beep */ + res = ast_streamfile(chan, "beep", chan->language); + if (!res) + res = ast_waitstream(chan, ""); + } + if (res < 0) { + free_user(vmu); + return -1; + } + /* The meat of recording the message... All the announcements and beeps have been played*/ + strncpy(fmt, vmfmts, sizeof(fmt) - 1); + if (strlen(fmt)) { + msgnum = 0; + do { + make_file(fn, sizeof(fn), dir, msgnum); + snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n", + (chan->callerid ? chan->callerid : "Unknown"), + vmu->fullname, ext, chan->name); + if (ast_fileexists(fn, NULL, chan->language) <= 0) + break; + msgnum++; + } while(msgnum < MAXMSG); + if (msgnum < MAXMSG) { + /* Store information */ + snprintf(txtfile, sizeof(txtfile), "%s.txt", fn); + txt = fopen(txtfile, "w+"); + if (txt) { + get_date(date, sizeof(date)); + time(&start); + fprintf(txt, ";\n" "; Message Information file\n" ";\n" @@ -643,198 +1198,44 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int chan->priority, chan->name, chan->callerid ? chan->callerid : "Unknown", - date, (long) time(NULL)); - fclose(txt); - } else - ast_log(LOG_WARNING, "Error opening text file for output\n"); - - /* We need to reset these values */ - free(fmts); - fmt = ast_variable_retrieve(cfg, "general", "format"); - fmts = strdup(fmt); - stringp=fmts; - strsep(&stringp, "|"); - 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++] = strdup(fmt); - } - for (x=0;x<fmtcnt;x++) { - others[x] = ast_writefile(fn, sfmt[x], comment, 0, 0, 0700); - if (!others[x]) { - /* Ick, the other format didn't work, but be sure not - to leak memory here */ - int y; - for(y=x+1;y < fmtcnt;y++) - free(sfmt[y]); - break; - } - if(!strcasecmp(sfmt[x], "wav")) - wavother++; - free(sfmt[x]); - } - - sildet = ast_dsp_new(); //Create the silence detector - if (silence > 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 (!sildet) { - ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); - return -1; - } - ast_dsp_set_threshold(sildet, 50); - } - - if (x == fmtcnt) { - /* Loop forever, writing the packets we read to the writer(s), until - we read a # or get a hangup */ - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Recording to %s\n", fn); - 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 the primary format */ - res = ast_writestream(writer, f); - if (res) { - ast_log(LOG_WARNING, "Error writing primary frame\n"); - break; - } - /* And each of the others */ - for (x=0;x<fmtcnt;x++) { - res |= ast_writestream(others[x], f); - } - /* Exit on any error */ - if (res) { - ast_log(LOG_WARNING, "Error writing frame\n"); - ast_frfree(f); - break; - } - /* Silence Detection */ - if (silence > 0) { - dspsilence = 0; - ast_dsp_silence(sildet, f, &dspsilence); - if (dspsilence) { - totalsilence = dspsilence; - } else { - totalsilence = 0; - } - if (totalsilence > silence) { - /* Ended happily with silence */ - outmsg=2; - ast_frfree(f); - gotsilence = 1; - res = 0; - break; - } - } - - } else if (f->frametype == AST_FRAME_DTMF) { - if (f->subclass == '#') { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass); - outmsg=2; - ast_frfree(f); - res = 0; - break; - } - } - ast_frfree(f); - time(&end); - if (maxmessage && (end - start > maxmessage)) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Message is too long, ending it now...\n"); - outmsg = 2; - res = 0; - break; - } - } - if (!f) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "User hung up\n"); - res = -1; - outmsg=1; - } - if (gotsilence) { - ast_stream_rewind(writer, silence-1000); - ast_truncstream(writer); - - /* And each of the others */ - for (x=0;x<fmtcnt;x++) { - ast_stream_rewind(others[x], silence-1000); - ast_truncstream(others[x]); - } - } - } else { - ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", fn, sfmt[x]); - free(sfmt[x]); - } - - ast_closestream(writer); - for (x=0;x<fmtcnt;x++) { - if (!others[x]) - break; - ast_closestream(others[x]); - } - if (outmsg) { - if (outmsg > 1) { - /* Let them know it worked */ - ast_streamfile(chan, "vm-msgsaved", chan->language); - ast_waitstream(chan, ""); - } - txt = fopen(txtfile, "a"); - if (txt) { - time(&end); - fprintf(txt, "duration=%ld\n", (long)(end-start)); - fclose(txt); - } - /* Send e-mail if applicable */ - if (email) - sendmail(astemail, email, name, msgnum, ext, chan->callerid, fn, wavother ? "wav" : fmts, end - start); - if (pager) - sendpage(astemail, pager, msgnum, ext, chan->callerid, end - start); - } - } else { - if (msgnum < MAXMSG) - ast_log(LOG_WARNING, "Error writing to mailbox %s\n", ext); - else - ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext); - } - free(fmts); - } else - ast_log(LOG_WARNING, "No format to save messages in \n"); - } + date, (long)time(NULL)); + fclose(txt); + } else + ast_log(LOG_WARNING, "Error opening text file for output\n"); + res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt); + if (res > 0) + res = 0; + txt = fopen(txtfile, "a"); + if (txt) { + time(&end); + fprintf(txt, "duration=%ld\n", (long)(end-start)); + fclose(txt); + } + stringp = fmt; + strsep(&stringp, "|"); + /* Send e-mail if applicable */ + if (strlen(vmu->email)) { + int attach_user_voicemail = attach_voicemail; + char *myserveremail = serveremail; + if (vmu->attach > -1) + attach_user_voicemail = vmu->attach; + if (strlen(vmu->serveremail)) + myserveremail = vmu->serveremail; + sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail); + } + if (strlen(vmu->pager)) { + char *myserveremail = serveremail; + if (strlen(vmu->serveremail)) + myserveremail = vmu->serveremail; + sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu); + } + } else + ast_log(LOG_WARNING, "No more messages possible\n"); } else - ast_log(LOG_WARNING, "Unable to playback instructions\n"); - - free(copy); + ast_log(LOG_WARNING, "No format for saving voicemail?\n"); + free_user(vmu); } else ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext); - ast_destroy(cfg); /* Leave voicemail for someone */ manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext)); return res; @@ -880,16 +1281,6 @@ static int count_messages(char *dir) return x; } -static int play_and_wait(struct ast_channel *chan, char *fn) -{ - int d; - d = ast_streamfile(chan, fn, chan->language); - if (d) - return d; - d = ast_waitstream(chan, AST_DIGIT_ANY); - return d; -} - static int say_and_wait(struct ast_channel *chan, int num) { int d; @@ -936,7 +1327,7 @@ static int copy(char *infile, char *outfile) return 0; } -static int save_to_folder(char *dir, int msg, char *username, int box) +static int save_to_folder(char *dir, int msg, char *context, char *username, int box) { char sfn[256]; char dfn[256]; @@ -946,7 +1337,7 @@ static int save_to_folder(char *dir, int msg, char *username, int box) char *dbox = mbox(box); int x; make_file(sfn, sizeof(sfn), dir, msg); - make_dir(ddir, sizeof(ddir), username, dbox); + make_dir(ddir, sizeof(ddir), context, username, dbox); mkdir(ddir, 0700); for (x=0;x<MAXMSG;x++) { make_file(dfn, sizeof(dfn), ddir, x); @@ -1016,7 +1407,7 @@ static int adsi_load_vmail(struct ast_channel *chan, int *useadsi) bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1); bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1); bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "4", 1); + bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1); bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1); bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1); adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD); @@ -1025,6 +1416,8 @@ static int adsi_load_vmail(struct ast_channel *chan, int *useadsi) /* Add another dot */ bytes = 0; bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", ""); + bytes += adsi_voice_mode(buf + bytes, 0); + bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); #endif @@ -1074,6 +1467,7 @@ static int adsi_load_vmail(struct ast_channel *chan, int *useadsi) } bytes = 0; bytes += adsi_download_disconnect(buf + bytes); + bytes += adsi_voice_mode(buf + bytes, 0); adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD); ast_log(LOG_DEBUG, "Done downloading scripts...\n"); @@ -1159,6 +1553,7 @@ static void adsi_password(struct ast_channel *chan) bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", ""); bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT); bytes += adsi_set_keys(buf + bytes, keys); + bytes += adsi_voice_mode(buf + bytes, 0); adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1186,6 +1581,8 @@ static void adsi_folders(struct ast_channel *chan, int start, char *label) bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", ""); bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); bytes += adsi_set_keys(buf + bytes, keys); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1194,6 +1591,7 @@ static void adsi_message(struct ast_channel *chan, char *folder, int msg, int la int bytes=0; char buf[256], buf1[256], buf2[256]; char fn2[256]; + char cid[256]=""; char *val; char *name, *num; @@ -1243,6 +1641,8 @@ static void adsi_message(struct ast_channel *chan, char *folder, int msg, int la if (msg) { /* but not only message, provide "Folder" instead */ keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1); + bytes += adsi_voice_mode(buf + bytes, 0); + } else { /* Otherwise if only message, leave blank */ keys[3] = 1; @@ -1257,6 +1657,7 @@ static void adsi_message(struct ast_channel *chan, char *folder, int msg, int la name = "Unknown Caller"; /* If deleted, show "undeleted" */ + if (deleted) keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11); @@ -1272,6 +1673,8 @@ static void adsi_message(struct ast_channel *chan, char *folder, int msg, int la bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, ""); bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); bytes += adsi_set_keys(buf + bytes, keys); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1315,6 +1718,8 @@ static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted /* Except "Exit" */ keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5); bytes += adsi_set_keys(buf + bytes, keys); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1358,6 +1763,8 @@ static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg) keys[0] = 1; bytes += adsi_set_keys(buf + bytes, keys); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1396,6 +1803,8 @@ static void adsi_status2(struct ast_channel *chan, char *folder, int messages) bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); bytes += adsi_set_keys(buf + bytes, keys); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1407,6 +1816,8 @@ static void adsi_clear(struct ast_channel *chan) if (!adsi_available(chan)) return; bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1421,6 +1832,8 @@ static void adsi_goodbye(struct ast_channel *chan) bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", ""); bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", ""); bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); } @@ -1456,8 +1869,19 @@ static int get_folder(struct ast_channel *chan, int start) return d; } +static int get_folder2(struct ast_channel *chan, char *fn, int start) +{ + int res = 0; + res = play_and_wait(chan, fn); + while (((res < '0') || (res > '9')) && + (res != '#') && (res >= 0)) { + res = get_folder(chan, 0); + } + return res; +} + static int -forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int curmsg, char* myusername) +forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt) { char username[70]; char sys[256]; @@ -1466,341 +1890,435 @@ forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int long duration; struct ast_config *mif; char miffile[256]; - char *copy, *name, *passwd, *email, *pager; - char *mycopy, *myname, *mypasswd, *myemail, *mypager; - char *astemail; char fn[256]; char callerid[512]; + int res = 0; + struct ast_vm_user *receiver, srec; + char tmp[256]; + char *stringp, *s; - while(1) { - ast_streamfile(chan, "vm-extension", chan->language); - - if (ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0) - return 0; - if (ast_variable_retrieve(cfg, NULL, username)) { - printf("Got %d\n", atoi(username)); + while(!res) { + res = ast_streamfile(chan, "vm-extension", chan->language); + if (res) + break; + if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0)) + break; + if ((receiver = find_user(&srec, context, username))) { /* if (play_and_wait(chan, "vm-savedto")) break; */ - snprintf(todir, sizeof(todir), "%s/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR,"vm", username); + snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username); snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir); - puts(sys); + ast_log(LOG_DEBUG, sys); system(sys); todircount = count_messages(todir); - - snprintf(sys, sizeof(sys), "cp %s/msg%04d.gsm %s/msg%04d.gsm\n", dir, curmsg, todir, todircount); - puts(sys); - system(sys); - - /* TODO: use config to determine what other formats to copy the message in */ - snprintf(sys, sizeof(sys), "cp %s/msg%04d.wav %s/msg%04d.wav\n", dir, curmsg, todir, todircount); - puts(sys); - system(sys); - - /* copy the message information file too */ + strncpy(tmp, fmt, sizeof(tmp)); + stringp = tmp; + while((s = strsep(&stringp, "|"))) { + snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s); + ast_log(LOG_DEBUG, sys); + system(sys); + } snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount); - puts(sys); + ast_log(LOG_DEBUG, sys); system(sys); - snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount); /* load the information on the source message so we can send an e-mail like a new message */ snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg); if ((mif=ast_load(miffile))) { - /* send an e-mail like it was a new message if appropriate */ - if ((copy = ast_variable_retrieve(cfg, NULL, username))) { - char *stringp=NULL; - /* Make sure they have an entry in the config */ - copy = strdup(copy); - stringp=copy; - passwd = strsep(&stringp, ","); - name = strsep(&stringp, ","); - email = strsep(&stringp, ","); - pager = strsep(&stringp, ","); - } - - if ((mycopy = ast_variable_retrieve(cfg, NULL, myusername))) { - char *mystringp=NULL; - /* Make sure they have an entry in the config */ - mycopy = strdup(mycopy); - mystringp=mycopy; - mypasswd = strsep(&mystringp, ","); - myname = strsep(&mystringp, ","); - myemail = strsep(&mystringp, ","); - mypager = strsep(&mystringp, ","); - } - - /* set the outbound email from address */ - if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) - astemail = ASTERISK_USERNAME; - /* set callerid and duration variables */ - snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", myname, ast_variable_retrieve(mif, NULL, "callerid")); + snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid")); duration = atol(ast_variable_retrieve(mif, NULL, "duration")); - if (email) - sendmail(astemail, email, name, todircount, username, callerid, fn, "wav", atol(ast_variable_retrieve(mif, NULL, "duration"))); - - if (pager) - sendpage(astemail, pager, todircount, username, callerid, duration); + if (strlen(receiver->email)) { + int attach_user_voicemail = attach_voicemail; + char *myserveremail = serveremail; + if (receiver->attach > -1) + attach_user_voicemail = receiver->attach; + if (strlen(receiver->serveremail)) + myserveremail = receiver->serveremail; + sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail); + } + + if (strlen(receiver->pager)) { + char *myserveremail = serveremail; + if (strlen(receiver->serveremail)) + myserveremail = receiver->serveremail; + sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver); + } - free(copy); /* no leaks here */ - free(mycopy); /* or here */ ast_destroy(mif); /* or here */ } + /* Leave voicemail for someone */ + manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username)); /* give confirmatopm that the message was saved */ - if (play_and_wait(chan, "vm-message")) break; - if (play_and_wait(chan, "vm-saved")) break; - + res = play_and_wait(chan, "vm-message"); + if (!res) + res = play_and_wait(chan, "vm-saved"); + free_user(receiver); break; } else { - if ( play_and_wait(chan, "pbx-invalid")) - break; + res = play_and_wait(chan, "pbx-invalid"); } } - return 0; + return res; } -#define WAITCMD(a) do { \ - d = (a); \ - if (d < 0) \ - goto out; \ - if (d) \ - goto cmd; \ -} while(0) - -#define WAITFILE2(file) do { \ - if (ast_streamfile(chan, file, chan->language)) \ - ast_log(LOG_WARNING, "Unable to play message %s\n", file); \ - d = ast_waitstream(chan, AST_DIGIT_ANY); \ - if (d < 0) { \ - goto out; \ - }\ -} while(0) - -#define WAITFILE(file) do { \ - if (ast_streamfile(chan, file, chan->language)) \ - ast_log(LOG_WARNING, "Unable to play message %s\n", file); \ - if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) { \ - if (sscanf(s, "%d", &x) == 1) \ - ms = x; \ - } \ - d = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",ms); \ - if (!d) { \ - repeats = 0; \ - goto instructions; \ - } else if (d < 0) { \ - goto out; \ - } else goto cmd;\ -} while(0) - -#define PLAYMSG(a) do { \ - starting = 0; \ - make_file(fn, sizeof(fn), curdir, a); \ - adsi_message(chan, curbox, a, lastmsg, deleted[a], fn); \ - if (!a) \ - WAITFILE2("vm-first"); \ - else if (a == lastmsg) \ - WAITFILE2("vm-last"); \ - WAITFILE2("vm-message"); \ - if (a && (a != lastmsg)) { \ - d = ast_say_number(chan, a + 1, AST_DIGIT_ANY, chan->language); \ - if (d < 0) goto out; \ - if (d) goto cmd; \ - } \ - make_file(fn, sizeof(fn), curdir, a); \ - heard[a] = 1; \ - WAITFILE(fn); \ -} while(0) - -#define CLOSE_MAILBOX do { \ - if (lastmsg > -1) { \ - /* Get the deleted messages fixed */ \ - curmsg = -1; \ - for (x=0;x<=lastmsg;x++) { \ - if (!deleted[x] && (strcasecmp(curbox, "INBOX") || !heard[x])) { \ - /* Save this message. It's not in INBOX or hasn't been heard */ \ - curmsg++; \ - make_file(fn, sizeof(fn), curdir, x); \ - make_file(fn2, sizeof(fn2), curdir, curmsg); \ - if (strcmp(fn, fn2)) { \ - snprintf(txt, sizeof(txt), "%s.txt", fn); \ - snprintf(ntxt, sizeof(ntxt), "%s.txt", fn2); \ - ast_filerename(fn, fn2, NULL); \ - rename(txt, ntxt); \ - } \ - } else if (!strcasecmp(curbox, "INBOX") && heard[x] && !deleted[x]) { \ - /* Move to old folder before deleting */ \ - save_to_folder(curdir, x, username, 1); \ - } \ - } \ - for (x = curmsg + 1; x<=lastmsg; x++) { \ - make_file(fn, sizeof(fn), curdir, x); \ - snprintf(txt, sizeof(txt), "%s.txt", fn); \ - ast_filedelete(fn, NULL); \ - unlink(txt); \ - } \ - } \ - memset(deleted, 0, sizeof(deleted)); \ - memset(heard, 0, sizeof(heard)); \ -} while(0) - -#define OPEN_MAILBOX(a) do { \ - strcpy(curbox, mbox(a)); \ - make_dir(curdir, sizeof(curdir), username, curbox); \ - lastmsg = count_messages(curdir) - 1; \ - snprintf(vmbox, sizeof(vmbox), "vm-%s", curbox); \ -} while (0) - -static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime) +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 wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file) { - char d, *fmt, *fmts; - char comment[256]; - int x, fmtcnt=1, res=-1,outmsg=0, wavother=0; - struct ast_frame *f; - struct ast_config *cfg; - struct ast_filestream *others[MAX_OTHER_FORMATS]; - char *sfmt[MAX_OTHER_FORMATS]; - char *stringp=NULL; - time_t start, end; - - - ast_log(LOG_DEBUG,"play_and_record: %s, %s\n", playfile, recordfile); - snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile, recordfile, chan->name); - - d = play_and_wait(chan, playfile); - if (d < 0) - return -1; - ast_streamfile(chan, "beep",chan->language); - ast_waitstream(chan,""); - cfg = ast_load(VOICEMAIL_CONFIG); - - fmt = ast_variable_retrieve(cfg, "general", "format"); - ast_log(LOG_DEBUG,"Recording Formats: fmt=%s\n", fmt); - - fmts = strdup(fmt); - - ast_destroy(cfg); + int res; + if ((res = ast_streamfile(chan, file, chan->language))) + ast_log(LOG_WARNING, "Unable to play message %s\n", file); + if (!res) + res = ast_waitstream(chan, AST_DIGIT_ANY); + return res; +} - stringp=fmts; - strsep(&stringp, "|"); - ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts); - sfmt[0] = strdup(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; +static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) +{ + int res; + if ((res = ast_streamfile(chan, file, chan->language))) + ast_log(LOG_WARNING, "Unable to play message %s\n", file); + if (!res) + res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms); + return res; +} + +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; + struct vm_zone *the_zone = NULL; + struct ast_config *msg_cfg; + time_t t; + long tin; + + 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",&tin) < 1) { + ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename); + return 0; + } + t = tin; + 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; } - sfmt[fmtcnt++] = strdup(fmt); + z = z->next; } + } - 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\n", x, recordfile, sfmt[x]); - - if (!others[x]) { - /* Ick, the other format didn't work, but be sure not - to leak memory here */ - int y; - for(y=x+1;y < fmtcnt;y++) - free(sfmt[y]); - break; - } - if(!strcasecmp(sfmt[x], "wav")) - wavother++; - free(sfmt[x]); - } - if (x == fmtcnt) { - /* Loop forever, writing the packets we read to the writer(s), until - we read a # or get a hangup */ - 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; - } +/* No internal variable parsing for now, so we'll comment it out for the time being */ +#if 0 + /* Set the DIFF_* variables */ + localtime_r(&t, &time_now); + gettimeofday(&tv_now,NULL); + tnow = tv_now.tv_sec; + localtime_r(&tnow,&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); - 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); - } - /* Exit on any error */ - if (res) { - ast_log(LOG_WARNING, "Error writing frame\n"); - ast_frfree(f); - break; - } - } else if (f->frametype == AST_FRAME_DTMF) { - if (f->subclass == '#') { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass); - outmsg=2; - break; - } - } - if (maxtime) { - time(&end); - if (maxtime < (end - start)) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n"); - outmsg=2; - break; - } - } - ast_frfree(f); - } - if (!f) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "User hung up\n"); - res = -1; - outmsg=1; - } - } else { - ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); - free(sfmt[x]); - } + /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */ +#endif + if (the_zone) + res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone); + else + res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL); +#if 0 + pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL); +#endif + return res; +} - for (x=0;x<fmtcnt;x++) { - if (!others[x]) +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; + make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg); + adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn); + if (!msg) + res = wait_file2(chan, vms, "vm-first"); + else if (msg == vms->lastmsg) + res = wait_file2(chan, vms, "vm-last"); + if (!res) { + res = wait_file2(chan, vms, "vm-message"); + if (msg && (msg != vms->lastmsg)) { + if (!res) + 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; + res = wait_file(chan, vms, vms->fn); + } + return res; +} + +static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box) +{ + strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1); + make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox); + vms->lastmsg = count_messages(vms->curdir) - 1; + snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox); +} + +static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu) +{ + int x; + char ntxt[256] = ""; + char txt[256] = ""; + if (vms->lastmsg > -1) { + /* Get the deleted messages fixed */ + vms->curmsg = -1; + for (x=0;x < MAXMSG;x++) { + if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { + /* Save this message. It's not in INBOX or hasn't been heard */ + make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); + if (ast_fileexists(vms->fn, NULL, NULL) < 1) break; - ast_closestream(others[x]); - } - if (outmsg) { - if (outmsg > 1) { - /* Let them know it worked */ - ast_streamfile(chan, "vm-msgsaved", chan->language); - ast_waitstream(chan, ""); - } - } + vms->curmsg++; + make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); + if (strcmp(vms->fn, vms->fn2)) { + snprintf(txt, sizeof(txt), "%s.txt", vms->fn); + snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); + ast_filerename(vms->fn, vms->fn2, NULL); + rename(txt, ntxt); + } + } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { + /* Move to old folder before deleting */ + save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); + } + } + for (x = vms->curmsg + 1; x <= MAXMSG; x++) { + make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); + if (ast_fileexists(vms->fn, NULL, NULL) < 1) + break; + snprintf(txt, sizeof(txt), "%s.txt", vms->fn); + ast_filedelete(vms->fn, NULL); + unlink(txt); + } + } + memset(vms->deleted, 0, sizeof(vms->deleted)); + memset(vms->heard, 0, sizeof(vms->heard)); +} - - return 0; +static int vm_intro(struct ast_channel *chan,struct vm_state *vms) +{ + /* Introduce messages they have */ + int res; + res = play_and_wait(chan, "vm-youhave"); + if (!res) { + if (vms->newmessages) { + res = say_and_wait(chan, vms->newmessages); + if (!res) + res = play_and_wait(chan, "vm-INBOX"); + if (vms->oldmessages && !res) + res = play_and_wait(chan, "vm-and"); + else if (!res) { + if ((vms->newmessages == 1)) + res = play_and_wait(chan, "vm-message"); + else + res = play_and_wait(chan, "vm-messages"); + } + + } + if (!res && vms->oldmessages) { + res = say_and_wait(chan, vms->oldmessages); + if (!res) + res = play_and_wait(chan, "vm-Old"); + if (!res) { + if (vms->oldmessages == 1) + res = play_and_wait(chan, "vm-message"); + else + res = play_and_wait(chan, "vm-messages"); + } + } + if (!res) { + if (!vms->oldmessages && !vms->newmessages) { + res = play_and_wait(chan, "vm-no"); + if (!res) + res = play_and_wait(chan, "vm-messages"); + } + } + } + return res; +} + +static int vm_instructions(struct ast_channel *chan, struct vm_state *vms) +{ + int res = 0; + /* Play instructions and wait for new command */ + while(!res) { + if (vms->starting) { + if (vms->lastmsg > -1) { + res = play_and_wait(chan, "vm-onefor"); + if (!res) + res = play_and_wait(chan, vms->vmbox); + if (!res) + res = play_and_wait(chan, "vm-messages"); + } + if (!res) + res = play_and_wait(chan, "vm-opts"); + } else { + if (vms->curmsg) + res = play_and_wait(chan, "vm-prev"); + if (!res) + res = play_and_wait(chan, "vm-repeat"); + if (!res && (vms->curmsg != vms->lastmsg)) + res = play_and_wait(chan, "vm-next"); + if (!res) { + if (!vms->deleted[vms->curmsg]) + res = play_and_wait(chan, "vm-delete"); + else + res = play_and_wait(chan, "vm-undelete"); + if (!res) + res = play_and_wait(chan, "vm-toforward"); + if (!res) + res = play_and_wait(chan, "vm-savemessage"); + } + } + if (!res) + res = play_and_wait(chan, "vm-helpexit"); + if (!res) + res = ast_waitfordigit(chan, 6000); + if (!res) { + vms->repeats++; + if (vms->repeats > 2) { + res = play_and_wait(chan, "vm-goodbye"); + if (!res) + res = 't'; + } + } + } + return res; } +static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc) +{ + int cmd = 0; + int retries = 0; + char newpassword[80] = ""; + char newpassword2[80] = ""; + char prefile[256]=""; + char buf[256]; + int bytes=0; + if (adsi_available(chan)) + { + bytes += adsi_logo(buf + bytes); + bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", ""); + bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", ""); + bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); + bytes += adsi_voice_mode(buf + bytes, 0); + adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); + } + while((cmd >= 0) && (cmd != 't')) { + if (cmd) + retries = 0; + switch (cmd) { + case '1': + snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username); + cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc); + break; + case '2': + snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username); + cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc); + break; + case '3': + snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username); + cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc); + break; + case '4': + newpassword[1] = '\0'; + newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword"); + if (cmd < 0) + break; + if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) { + break; + } + newpassword2[1] = '\0'; + newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword"); + if (cmd < 0) + break; + if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) { + break; + } + if (strcmp(newpassword, newpassword2)) { + ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2); + cmd = play_and_wait(chan, "vm-mismatch"); + break; + } + vm_change_password(vmu,newpassword); + ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword)); + cmd = play_and_wait(chan,"vm-passchanged"); + break; + case '*': + cmd = 't'; + break; + default: + cmd = play_and_wait(chan,"vm-options"); + if (!cmd) + cmd = ast_waitfordigit(chan,6000); + if (!cmd) + retries++; + if (retries > 3) + cmd = 't'; + } + } + if (cmd == 't') + cmd = 0; + return cmd; +} static int vm_execmain(struct ast_channel *chan, void *data) { @@ -1810,57 +2328,28 @@ static int vm_execmain(struct ast_channel *chan, void *data) int res=-1; int valid = 0; int prefix = 0; - char d; + int cmd=0; struct localuser *u; - char username[80] =""; char prefixstr[80] =""; char empty[80] = ""; - char password[80] = "", *copy; - char newpassword[80] = ""; - char newpassword2[80] = ""; - char curbox[80] = ""; - char curdir[256] = ""; - char vmbox[256] = ""; - char fn[256] = ""; - char fn2[256] = ""; - char prefile[256]=""; - int x; - char ntxt[256] = ""; - char txt[256] = ""; - int deleted[MAXMSG] = { 0, }; - int heard[MAXMSG] = { 0, }; - int newmessages; - int oldmessages; - int repeats = 0; - int curmsg = 0; - int lastmsg = 0; - int starting = 1; int box; int useadsi = 0; int skipuser = 0; - char *s; - int ms = 3000; - int maxgreet = 0; char tmp[256], *ext; - struct ast_config *cfg; + char fmtc[256] = ""; + char password[80]; + struct vm_state vms; + int logretries = 0; + struct ast_vm_user *vmu = NULL, vmus; + char *context=NULL; LOCAL_USER_ADD(u); - cfg = ast_load(VOICEMAIL_CONFIG); - if (!cfg) { - ast_log(LOG_WARNING, "No voicemail configuration\n"); - goto out; - } - if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) { - if (sscanf(s, "%d", &x) == 1) { - maxgreet = x; - } else { - ast_log(LOG_WARNING, "Invalid max message greeting length\n"); - } - } + memset(&vms, 0, sizeof(vms)); + strncpy(fmtc, vmfmts, sizeof(fmtc) - 1); if (chan->_state != AST_STATE_UP) ast_answer(chan); - if (strlen(data)) { + if (data && strlen(data)) { strncpy(tmp, data, sizeof(tmp) - 1); ext = tmp; @@ -1877,13 +2366,17 @@ static int vm_execmain(struct ast_channel *chan, void *data) break; } + context = strchr(ext, '@'); + if (context) { + *context = '\0'; + context++; + } if (prefix) strncpy(prefixstr, ext, sizeof(prefixstr) - 1); else - strncpy(username, ext, sizeof(username) - 1); - /* make sure username passed as an option is valid */ - if (ast_variable_retrieve(cfg, NULL, username)) + strncpy(vms.username, ext, sizeof(vms.username) - 1); + if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username))) skipuser++; else valid = 0; @@ -1901,13 +2394,13 @@ static int vm_execmain(struct ast_channel *chan, void *data) /* Authenticate them and get their mailbox/password */ - while (!valid) { + while (!valid && (logretries < maxlogins)) { /* Prompt for, and read in the username */ - if (!skipuser && ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0) { + if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) { ast_log(LOG_WARNING, "Couldn't read username\n"); goto out; } - if (!strlen(username)) { + if (!strlen(vms.username)) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n"); res = 0; @@ -1926,311 +2419,212 @@ static int vm_execmain(struct ast_channel *chan, void *data) if (prefix) { char fullusername[80] = ""; strncpy(fullusername, prefixstr, sizeof(fullusername) - 1); - strncat(fullusername, username, sizeof(fullusername) - 1); - strncpy(username, fullusername, sizeof(username) - 1); - } - copy = ast_variable_retrieve(cfg, NULL, username); - if (copy) { - char *stringp=NULL; - copy = strdup(copy); - stringp=copy; - strsep(&stringp, ","); - if (!strcmp(password,copy)) - valid++; - else { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s'\n", password, username); - if (prefix) - strncpy(username, empty, sizeof(username) -1); - } - free(copy); - } else { - skipuser = 0; + strncat(fullusername, vms.username, sizeof(fullusername) - 1); + strncpy(vms.username, fullusername, sizeof(vms.username) - 1); + } + if (!skipuser) + vmu = find_user(&vmus, context, vms.username); + if (vmu && !strcmp(vmu->password, password)) + valid++; + else { if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "No such user '%s' in config file\n", username); + ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>"); + if (prefix) + strncpy(vms.username, empty, sizeof(vms.username) -1); } if (!valid) { if (useadsi) adsi_login(chan); if (ast_streamfile(chan, "vm-incorrect", chan->language)) break; -#if 0 - if (ast_waitstream(chan, "")) - break; -#endif } + logretries++; + } + if (!valid && (logretries >= maxlogins)) { + ast_stopstream(chan); + res = play_and_wait(chan, "vm-goodbye"); + if (res > 0) + res = 0; } if (valid) { - snprintf(curdir, sizeof(curdir), "%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,"vm", username); - mkdir(curdir, 0700); - OPEN_MAILBOX(1); - oldmessages = lastmsg + 1; + snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context); + mkdir(vms.curdir, 0700); + snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username); + mkdir(vms.curdir, 0700); + /* Retrieve old and new message counts */ + open_mailbox(&vms, vmu, 1); + vms.oldmessages = vms.lastmsg + 1; /* Start in INBOX */ - OPEN_MAILBOX(0); - newmessages = lastmsg + 1; + open_mailbox(&vms, vmu, 0); + vms.newmessages = vms.lastmsg + 1; /* Select proper mailbox FIRST!! */ - if (!newmessages && oldmessages) { + if (!vms.newmessages && vms.oldmessages) { /* If we only have old messages start here */ - OPEN_MAILBOX(1); + open_mailbox(&vms, vmu, 1); } if (useadsi) - adsi_status(chan, newmessages, oldmessages, lastmsg); - - WAITCMD(play_and_wait(chan, "vm-youhave")); - if (newmessages) { - WAITCMD(say_and_wait(chan, newmessages)); - WAITCMD(play_and_wait(chan, "vm-INBOX")); - - if (oldmessages) - WAITCMD(play_and_wait(chan, "vm-and")); - else { - if (newmessages == 1) - WAITCMD(play_and_wait(chan, "vm-message")); + adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg); + res = 0; + cmd = vm_intro(chan, &vms); + vms.repeats = 0; + vms.starting = 1; + while((cmd > -1) && (cmd != 't') && (cmd != '#')) { + /* Run main menu */ + switch(cmd) { + case '1': + vms.curmsg = 0; + /* Fall through */ + case '5': + if (vms.lastmsg > -1) { + cmd = play_message(chan, vmu, &vms, vms.curmsg); + } else { + cmd = play_and_wait(chan, "vm-youhave"); + if (!cmd) + cmd = play_and_wait(chan, "vm-no"); + if (!cmd) { + snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox); + cmd = play_and_wait(chan, vms.fn); + } + if (!cmd) + cmd = play_and_wait(chan, "vm-messages"); + } + break; + case '2': /* Change folders */ + if (useadsi) + adsi_folders(chan, 0, "Change to folder..."); + cmd = get_folder2(chan, "vm-changeto", 0); + if (cmd == '#') { + cmd = 0; + } else if (cmd > 0) { + cmd = cmd - '0'; + close_mailbox(&vms, vmu); + open_mailbox(&vms, vmu, cmd); + cmd = 0; + } + if (useadsi) + adsi_status2(chan, vms.curbox, vms.lastmsg + 1); + if (!cmd) + cmd = play_and_wait(chan, vms.vmbox); + if (!cmd) + cmd = play_and_wait(chan, "vm-messages"); + vms.starting = 1; + break; + case '4': + if (vms.curmsg) { + vms.curmsg--; + cmd = play_message(chan, vmu, &vms, vms.curmsg); + } else { + cmd = play_and_wait(chan, "vm-nomore"); + } + break; + case '6': + if (vms.curmsg < vms.lastmsg) { + vms.curmsg++; + cmd = play_message(chan, vmu, &vms, vms.curmsg); + } else { + cmd = play_and_wait(chan, "vm-nomore"); + } + break; + case '7': + vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg]; + if (useadsi) + adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]); + if (vms.deleted[vms.curmsg]) + cmd = play_and_wait(chan, "vm-deleted"); else - WAITCMD(play_and_wait(chan, "vm-messages")); - } - - } - if (oldmessages) { - WAITCMD(say_and_wait(chan, oldmessages)); - WAITCMD(play_and_wait(chan, "vm-Old")); - if (oldmessages == 1) - WAITCMD(play_and_wait(chan, "vm-message")); - else - WAITCMD(play_and_wait(chan, "vm-messages")); - } - if (!oldmessages && !newmessages) { - WAITCMD(play_and_wait(chan, "vm-no")); - WAITCMD(play_and_wait(chan, "vm-messages")); - } - repeats = 0; - starting = 1; -instructions: - if (starting) { - if (lastmsg > -1) { - WAITCMD(play_and_wait(chan, "vm-onefor")); - WAITCMD(play_and_wait(chan, vmbox)); - WAITCMD(play_and_wait(chan, "vm-messages")); - } - WAITCMD(play_and_wait(chan, "vm-opts")); - } else { - if (curmsg) - WAITCMD(play_and_wait(chan, "vm-prev")); - WAITCMD(play_and_wait(chan, "vm-repeat")); - if (curmsg != lastmsg) - WAITCMD(play_and_wait(chan, "vm-next")); - if (!deleted[curmsg]) - WAITCMD(play_and_wait(chan, "vm-delete")); - else - WAITCMD(play_and_wait(chan, "vm-undelete")); - WAITCMD(play_and_wait(chan, "vm-toforward")); - WAITCMD(play_and_wait(chan, "vm-savemessage")); - } - WAITCMD(play_and_wait(chan, "vm-helpexit")); - d = ast_waitfordigit(chan, 6000); - if (d < 0) - goto out; - if (!d) { - repeats++; - if (repeats > 2) { - play_and_wait(chan, "vm-goodbye"); - goto out; + cmd = play_and_wait(chan, "vm-undeleted"); + break; + case '8': + if(vms.lastmsg > -1) + cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts); + else + cmd = play_and_wait(chan, "vm-nomore"); + break; + case '9': + if (useadsi) + adsi_folders(chan, 1, "Save to folder..."); + cmd = get_folder2(chan, "vm-savefolder", 1); + box = 0; /* Shut up compiler */ + if (cmd == '#') { + cmd = 0; + break; + } else if (cmd > 0) { + box = cmd = cmd - '0'; + cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd); + vms.deleted[vms.curmsg]=1; + } + make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg); + if (useadsi) + adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn); + if (!cmd) + cmd = play_and_wait(chan, "vm-message"); + if (!cmd) + cmd = say_and_wait(chan, vms.curmsg + 1); + if (!cmd) + cmd = play_and_wait(chan, "vm-savedto"); + if (!cmd) { + snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box)); + cmd = play_and_wait(chan, vms.fn); + } + if (!cmd) + cmd = play_and_wait(chan, "vm-messages"); + break; + case '*': + if (!vms.starting) { + cmd = play_and_wait(chan, "vm-onefor"); + if (!cmd) + cmd = play_and_wait(chan, vms.vmbox); + if (!cmd) + cmd = play_and_wait(chan, "vm-messages"); + if (!cmd) + cmd = play_and_wait(chan, "vm-opts"); + } else + cmd = 0; + break; + case '0': + cmd = vm_options(chan, vmu, &vms, vmfmts); + if (useadsi) + adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg); + break; + default: /* Nothing */ + cmd = vm_instructions(chan, &vms); + break; } - goto instructions; } -cmd: - switch(d) { - case '2': - if (useadsi) - adsi_folders(chan, 0, "Change to folder..."); - box = play_and_wait(chan, "vm-changeto"); - if (box < 0) - goto out; - while((box < '0') || (box > '9')) { - box = get_folder(chan, 0); - if (box < 0) - goto out; - if (box == '#') - goto instructions; - } - box = box - '0'; - CLOSE_MAILBOX; - OPEN_MAILBOX(box); - if (useadsi) - adsi_status2(chan, curbox, lastmsg + 1); - WAITCMD(play_and_wait(chan, vmbox)); - WAITCMD(play_and_wait(chan, "vm-messages")); - starting = 1; - goto instructions; - case '4': - if (curmsg) { - curmsg--; - PLAYMSG(curmsg); - } else { - WAITCMD(play_and_wait(chan, "vm-nomore")); - goto instructions; - } - case '1': - curmsg = 0; - /* Fall through */ - case '5': - if (lastmsg > -1) { - PLAYMSG(curmsg); - } else { - WAITCMD(play_and_wait(chan, "vm-youhave")); - WAITCMD(play_and_wait(chan, "vm-no")); - snprintf(fn, sizeof(fn), "vm-%s", curbox); - WAITCMD(play_and_wait(chan, fn)); - WAITCMD(play_and_wait(chan, "vm-messages")); - goto instructions; - } - case '6': - if (curmsg < lastmsg) { - curmsg++; - PLAYMSG(curmsg); - } else { - WAITCMD(play_and_wait(chan, "vm-nomore")); - goto instructions; - } - case '7': - deleted[curmsg] = !deleted[curmsg]; - if (useadsi) - adsi_delete(chan, curmsg, lastmsg, deleted[curmsg]); - if (deleted[curmsg]) - WAITCMD(play_and_wait(chan, "vm-deleted")); - else - WAITCMD(play_and_wait(chan, "vm-undeleted")); - goto instructions; - case '8': - if(lastmsg > -1) - if(forward_message(chan, cfg, curdir, curmsg, username) < 0) - goto out; - goto instructions; - case '9': - if (useadsi) - adsi_folders(chan, 1, "Save to folder..."); - box = play_and_wait(chan, "vm-savefolder"); - if (box < 0) - goto out; - while((box < '1') || (box > '9')) { - box = get_folder(chan, 1); - if (box < 0) - goto out; - if (box == '#') - goto instructions; - } - box = box - '0'; - if (option_debug) - ast_log(LOG_DEBUG, "Save to folder: %s (%d)\n", mbox(box), box); - if (save_to_folder(curdir, curmsg, username, box)) - goto out; - deleted[curmsg]=1; - make_file(fn, sizeof(fn), curdir, curmsg); - if (useadsi) - adsi_message(chan, curbox, curmsg, lastmsg, deleted[curmsg], fn); - WAITCMD(play_and_wait(chan, "vm-message")); - WAITCMD(say_and_wait(chan, curmsg + 1) ); - WAITCMD(play_and_wait(chan, "vm-savedto")); - snprintf(fn, sizeof(fn), "vm-%s", mbox(box)); - WAITCMD(play_and_wait(chan, fn)); - WAITCMD(play_and_wait(chan, "vm-messages")); - goto instructions; - case '*': - if (!starting) { - WAITCMD(play_and_wait(chan, "vm-onefor")); - WAITCMD(play_and_wait(chan, vmbox)); - WAITCMD(play_and_wait(chan, "vm-messages")); - WAITCMD(play_and_wait(chan, "vm-opts")); - } - goto instructions; - case '#': - ast_stopstream(chan); - adsi_goodbye(chan); - play_and_wait(chan, "vm-goodbye"); + if ((cmd == 't') || (cmd == '#')) { + /* Timeout */ res = 0; - goto out2; - - case '0': - goto vm_options; - - default: - goto instructions; + } else { + /* Hangup */ + res = -1; } } out: - adsi_goodbye(chan); -out2: - CLOSE_MAILBOX; - ast_stopstream(chan); - if (cfg) - ast_destroy(cfg); - if (useadsi) - adsi_unload_session(chan); + if (res > -1) { + ast_stopstream(chan); + adsi_goodbye(chan); + if(valid) { + res = play_and_wait(chan, "vm-goodbye"); + if (res > 0) + res = 0; + } + if (useadsi) + adsi_unload_session(chan); + } + if (vmu) + close_mailbox(&vms, vmu); + if (vmu) + free_user(vmu); if (valid) { - manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username)); + manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username)); } LOCAL_USER_REMOVE(u); return res; -vm_options: - d = play_and_wait(chan,"vm-options"); - if (!d) - d = ast_waitfordigit(chan,6000); - if (d < 0) - goto out; - switch (d) { - - case '1': - snprintf(prefile,sizeof(prefile),"vm/%s/unavail",username); - play_and_record(chan,"vm-rec-unv",prefile, maxgreet); - break; - case '2': - snprintf(prefile,sizeof(prefile),"vm/%s/busy",username); - play_and_record(chan,"vm-rec-busy",prefile, maxgreet); - break; - case '3': - snprintf(prefile,sizeof(prefile),"vm/%s/greet",username); - play_and_record(chan,"vm-rec-name",prefile, maxgreet); - break; - case '4': - newpassword[1] = '\0'; - newpassword[0] = play_and_wait(chan,"vm-newpassword"); - if (ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#") < 0) { - play_and_wait(chan, "vm-sorry"); - ast_log(LOG_NOTICE,"Unable to read new password\n"); - goto vm_options; - } - newpassword2[1] = '\0'; - newpassword2[0] = play_and_wait(chan,"vm-reenterpassword"); - - if (ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#") < 0) { - play_and_wait(chan, "vm-sorry"); - ast_log(LOG_NOTICE,"Unable to read re-entered password\n"); - goto vm_options; - } - if (strcmp(newpassword, newpassword2)) { - ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", username, newpassword, newpassword2); - play_and_wait(chan, "vm-mismatch"); - goto vm_options; - } - if (vm_change_password(username,password,newpassword) < 0) - { - ast_log(LOG_DEBUG,"Failed to set new password of user %s\n",username); - } else - ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",username,newpassword,strlen(newpassword)); - play_and_wait(chan,"vm-passchanged"); - break; - case '*': - goto instructions; - - default: - goto vm_options; - } - goto vm_options; } static int vm_exec(struct ast_channel *chan, void *data) @@ -2252,27 +2646,287 @@ static int vm_exec(struct ast_channel *chan, void *data) return 0; } ext = tmp; - if (*ext == 's') { - silent++; - ext++; - } else if (*ext == 'b') { - busy++; - ext++; - } else if (*ext == 'u') { - unavail++; - ext++; + while(*ext) { + if (*ext == 's') { + silent = 2; + ext++; + } else if (*ext == 'b') { + busy=1; + ext++; + } else if (*ext == 'u') { + unavail=1; + ext++; + } else + break; } res = leave_voicemail(chan, ext, silent, busy, unavail); LOCAL_USER_REMOVE(u); return res; } +static int append_mailbox(char *context, char *mbox, char *data) +{ + /* Assumes lock is already held */ + char tmp[256] = ""; + char *stringp; + char *s; + struct ast_vm_user *vmu; + strncpy(tmp, data, sizeof(tmp)); + vmu = malloc(sizeof(struct ast_vm_user)); + if (vmu) { + memset(vmu, 0, sizeof(struct ast_vm_user)); + strncpy(vmu->context, context, sizeof(vmu->context)); + strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox)); + vmu->attach = -1; + stringp = tmp; + if ((s = strsep(&stringp, ","))) + strncpy(vmu->password, s, sizeof(vmu->password)); + if (stringp && (s = strsep(&stringp, ","))) + strncpy(vmu->fullname, s, sizeof(vmu->fullname)); + if (stringp && (s = strsep(&stringp, ","))) + strncpy(vmu->email, s, sizeof(vmu->email)); + if (stringp && (s = strsep(&stringp, ","))) + strncpy(vmu->pager, s, sizeof(vmu->pager)); + if (stringp && (s = strsep(&stringp, ","))) + apply_options(vmu, s); + vmu->next = NULL; + if (usersl) + usersl->next = vmu; + else + users = vmu; + usersl = vmu; + } + return 0; +} + +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; + char *astattach; + char *silencestr; + char *thresholdstr; + char *fmt; + char *astemail; + char *s; + int x; + + cfg = ast_load(VOICEMAIL_CONFIG); + ast_mutex_lock(&vmlock); + cur = users; + while(cur) { + l = cur; + 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) { + /* General settings */ + attach_voicemail = 1; + if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) + astattach = "yes"; + attach_voicemail = ast_true(astattach); + maxsilence = 0; + if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) { + maxsilence = atoi(silencestr); + if (maxsilence > 0) + maxsilence *= 1000; + } + + silencethreshold = 256; + if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold"))) + silencethreshold = atoi(thresholdstr); + + if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) + astemail = ASTERISK_USERNAME; + strncpy(serveremail, astemail, sizeof(serveremail) - 1); + + vmmaxmessage = 0; + if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) { + if (sscanf(s, "%d", &x) == 1) { + vmmaxmessage = x; + } else { + ast_log(LOG_WARNING, "Invalid max message time length\n"); + } + } + fmt = ast_variable_retrieve(cfg, "general", "format"); + if (!fmt) + fmt = "wav"; + strncpy(vmfmts, fmt, sizeof(vmfmts) - 1); + + skipms = 3000; + if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) { + if (sscanf(s, "%d", &x) == 1) { + maxgreet = x; + } else { + ast_log(LOG_WARNING, "Invalid max message greeting length\n"); + } + } + + if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) { + if (sscanf(s, "%d", &x) == 1) { + skipms = x; + } else { + ast_log(LOG_WARNING, "Invalid skipms value\n"); + } + } + + maxlogins = 3; + if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) { + if (sscanf(s, "%d", &x) == 1) { + maxlogins = x; + } else { + ast_log(LOG_WARNING, "Invalid max failed login attempts\n"); + } + } + +#ifdef USEMYSQLVM + if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) { + strcpy(dbuser, "test"); + } else { + strcpy(dbuser, s); + } + if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) { + strcpy(dbpass, "test"); + } else { + strcpy(dbpass, s); + } + if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) { + strcpy(dbhost, ""); + } else { + strcpy(dbhost, s); + } + if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) { + strcpy(dbname, "vmdb"); + } else { + strcpy(dbname, s); + } +#endif + +#ifdef USEPOSTGRESVM + if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) { + strcpy(dboption, "dboption not-specified in voicemail.conf"); + } else { + strcpy(dboption, s); + } +#endif + cat = ast_category_browse(cfg, NULL); + while(cat) { + if (strcasecmp(cat, "general")) { + var = ast_variable_browse(cfg, cat); + if (strcasecmp(cat, "zonemessages")) { +#ifndef USESQLVM + /* 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 = ast_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); + } + memset(fromstring,0,sizeof(fromstring)); + memset(emailtitle,0,sizeof(emailtitle)); + if (emailbody) { + free(emailbody); + emailbody = NULL; + } + if ((s=ast_variable_retrieve(cfg, "general", "pbxskip"))) + pbxskip = ast_true(s); + if ((s=ast_variable_retrieve(cfg, "general", "fromstring"))) + strncpy(fromstring,s,sizeof(fromstring)-1); + if ((s=ast_variable_retrieve(cfg, "general", "emailtitle"))) + strncpy(emailtitle,s,sizeof(emailtitle)-1); + if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) { + char *tmpread, *tmpwrite; + emailbody = strdup(s); + + /* substitute strings \t and \n into the apropriate characters */ + tmpread = tmpwrite = emailbody; + while ((tmpwrite = strchr(tmpread,'\\'))) { + int len = strlen("\n"); + switch (tmpwrite[1]) { + case 'n': + strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1); + strncpy(tmpwrite,"\n",len); + break; + case 't': + strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1); + strncpy(tmpwrite,"\t",len); + break; + default: + ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]); + } + tmpread = tmpwrite+len; + } + } + ast_destroy(cfg); + ast_mutex_unlock(&vmlock); + return 0; + } else { + ast_mutex_unlock(&vmlock); + ast_log(LOG_WARNING, "Error reading voicemail config\n"); + return -1; + } +} + +int reload(void) +{ + return(load_config()); +} + int unload_module(void) { int res; STANDARD_HANGUP_LOCALUSERS; res = ast_unregister_application(app); res |= ast_unregister_application(app2); + sql_close(); return res; } @@ -2280,8 +2934,20 @@ int load_module(void) { int res; res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm); - if (!res) - res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain); + res |= ast_register_application(capp, vm_exec, synopsis_vm, descrip_vm); + res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain); + res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain); + if (res) + return(res); + + if ((res=load_config())) { + return(res); + } + + if ((res = sql_init())) { + ast_log(LOG_WARNING, "SQL init\n"); + return res; + } return res; } diff --git a/apps/app_voicemail2.c b/apps/app_voicemail2.c deleted file mode 100755 index 66699f9e4..000000000 --- a/apps/app_voicemail2.c +++ /dev/null @@ -1,2965 +0,0 @@ -/* - * Asterisk -- A telephony toolkit for Linux. - * - * Voicemail System (did you ever think it could be so easy?) - * - * Copyright (C) 1999, Mark Spencer - * - * Mark Spencer <markster@linux-support.net> - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - -#include <asterisk/lock.h> -#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/adsi.h> -#include <asterisk/app.h> -#include <asterisk/manager.h> -#include <asterisk/dsp.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 - -#include <pthread.h> -#include "../asterisk.h" -#include "../astconf.h" - -#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" - -#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" -"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"; - -/* Leave a message */ -static char *app = "VoiceMail2"; - -/* Check mail, control, etc */ -static char *app2 = "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]; - -STANDARD_LOCAL_USER; - -LOCAL_USER_DECL; - -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) -{ - 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); - } - - 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 mailbox='%s'", mailbox); - } -/* 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]; - char *user, *pass, *rest, *trim; - snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR); - snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR); - configin = fopen((char *)tmpin,"r"); - 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; - } - - 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)) { - /* This is the line */ - if (rest) { - fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest); - } else { - 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); -} - -static int -inbuf(struct baseio *bio, FILE *fi) -{ - int l; - - if(bio->ateof) - return 0; - - if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) { - if(ferror(fi)) - return -1; - - bio->ateof = 1; - return 0; - } - - bio->iolen= l; - bio->iocp= 0; - - return 1; -} - -static int -inchar(struct baseio *bio, FILE *fi) -{ - if(bio->iocp>=bio->iolen) - if(!inbuf(bio, fi)) - return EOF; - - return bio->iobuf[bio->iocp++]; -} - -static int -ochar(struct baseio *bio, int c, FILE *so) -{ - if(bio->linelength>=BASELINELEN) { - if(fputs(eol,so)==EOF) - return -1; - - 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; - - 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]; - char who[256]; - char bound[256]; - char fname[256]; - char dur[256]; - time_t t; - struct tm tm; - 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); - 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); - - 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, "--%s\n", bound); - } - fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n"); - strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm); - 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, "--%s\n", bound); - fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum + 1, 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; -} - -static int get_date(char *s, int len) -{ - struct tm tm; - time_t t; - t = time(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) -{ - int res; - char fn[256]; - 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; - res = ast_waitstream(chan, ecodes); - if (res) - return res; - } else { - res = ast_streamfile(chan, "vm-theperson", chan->language); - if (res) - return -1; - res = ast_waitstream(chan, ecodes); - if (res) - return res; - res = ast_say_digit_str(chan, ext, ecodes, chan->language); - if (res) - return res; - } - if (busy) - res = ast_streamfile(chan, "vm-isonphone", chan->language); - else - res = ast_streamfile(chan, "vm-isunavail", chan->language); - if (res) - return -1; - res = ast_waitstream(chan, ecodes); - return res; -} - -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; - char comment[256]; - int x, fmtcnt=1, res=-1,outmsg=0; - struct ast_frame *f; - struct ast_filestream *others[MAX_OTHER_FORMATS]; - char *sfmt[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); - if (!d) - 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) { - /* Write only once */ - ast_writestream(others[0], f); - } else if (f->frametype == AST_FRAME_DTMF) { - if (f->subclass == '#') { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass); - res = '#'; - outmsg = 2; - ast_frfree(f); - break; - } - } - if (maxtime) { - time(&end); - if (maxtime < (end - start)) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n"); - res = 't'; - ast_frfree(f); - break; - } - } - ast_frfree(f); - } - if (!f) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "User hung up\n"); - res = -1; - outmsg=1; - } - } else { - ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); - } - - for (x=0;x<fmtcnt;x++) { - if (!others[x]) - break; - if (totalsilence) - ast_stream_rewind(others[x], totalsilence-200); - else - ast_stream_rewind(others[x], 200); - ast_truncstream(others[x]); - ast_closestream(others[x]); - } - if (rfmt) { - if (ast_set_read_format(chan, rfmt)) { - ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); - } - } - if (outmsg) { - if (outmsg > 1) { - /* Let them know it worked */ - ast_streamfile(chan, "vm-msgsaved", chan->language); - ast_waitstream(chan, ""); - } - } - - - return res; -} - -static void free_user(struct ast_vm_user *vmu) -{ - if (vmu->alloced) - 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]; - char txtfile[256]; - FILE *txt; - int res = 0; - int msgnum; - char date[256]; - char dir[256]; - char fn[256]; - char prefile[256]=""; - char fmt[80]; - char *context; - char *ecodes = "#"; - char *stringp; - time_t start; - time_t end; - char tmp[256] = ""; - struct ast_vm_user *vmu; - struct ast_vm_user svm; - - strncpy(tmp, ext, sizeof(tmp) - 1); - ext = tmp; - context = strchr(tmp, '@'); - if (context) { - *context = '\0'; - context++; - } - - if ((vmu = find_user(&svm, context, ext))) { - /* Setup pre-file if appropriate */ - if (busy) - snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext); - else if (unavail) - snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext); - make_dir(dir, sizeof(dir), vmu->context, "", ""); - /* It's easier just to try to make it than to check for its existence */ - if (mkdir(dir, 0700) && (errno != EEXIST)) - ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno)); - make_dir(dir, sizeof(dir), vmu->context, ext, ""); - /* It's easier just to try to make it than to check for its existence */ - if (mkdir(dir, 0700) && (errno != EEXIST)) - ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno)); - make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX"); - if (mkdir(dir, 0700) && (errno != EEXIST)) - ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno)); - if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid)) - ecodes = "#0"; - /* Play the beginning intro if desired */ - if (strlen(prefile)) { - if (ast_fileexists(prefile, NULL, NULL) > 0) { - if (ast_streamfile(chan, prefile, chan->language) > -1) - res = ast_waitstream(chan, "#0"); - } else { - ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile); - res = invent_message(chan, vmu->context, ext, busy, ecodes); - } - if (res < 0) { - ast_log(LOG_DEBUG, "Hang up during prefile playback\n"); - free_user(vmu); - return -1; - } - } - if (res == '#') { - /* On a '#' we skip the instructions */ - silent = 1; - res = 0; - } - if (!res && !silent) { - res = ast_streamfile(chan, INTRO, chan->language); - if (!res) - res = ast_waitstream(chan, ecodes); - if (res == '#') { - silent = 1; - res = 0; - } - } - /* Check for a '0' here */ - if (res == '0') { - strncpy(chan->exten, "o", sizeof(chan->exten) - 1); - if (strlen(chan->macrocontext)) - strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1); - chan->priority = 0; - free_user(vmu); - return 0; - } - if (res >= 0) { - /* Unless we're *really* silent, try to send the beep */ - res = ast_streamfile(chan, "beep", chan->language); - if (!res) - res = ast_waitstream(chan, ""); - } - if (res < 0) { - free_user(vmu); - return -1; - } - /* The meat of recording the message... All the announcements and beeps have been played*/ - strncpy(fmt, vmfmts, sizeof(fmt) - 1); - if (strlen(fmt)) { - msgnum = 0; - do { - make_file(fn, sizeof(fn), dir, msgnum); - snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n", - (chan->callerid ? chan->callerid : "Unknown"), - vmu->fullname, ext, chan->name); - if (ast_fileexists(fn, NULL, chan->language) <= 0) - break; - msgnum++; - } while(msgnum < MAXMSG); - if (msgnum < MAXMSG) { - /* Store information */ - snprintf(txtfile, sizeof(txtfile), "%s.txt", fn); - txt = fopen(txtfile, "w+"); - if (txt) { - get_date(date, sizeof(date)); - time(&start); - fprintf(txt, -";\n" -"; Message Information file\n" -";\n" -"[message]\n" -"origmailbox=%s\n" -"context=%s\n" -"exten=%s\n" -"priority=%d\n" -"callerchan=%s\n" -"callerid=%s\n" -"origdate=%s\n" -"origtime=%ld\n", - ext, - chan->context, - chan->exten, - chan->priority, - chan->name, - chan->callerid ? chan->callerid : "Unknown", - date, (long)time(NULL)); - fclose(txt); - } else - ast_log(LOG_WARNING, "Error opening text file for output\n"); - res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt); - if (res > 0) - res = 0; - txt = fopen(txtfile, "a"); - if (txt) { - time(&end); - fprintf(txt, "duration=%ld\n", (long)(end-start)); - fclose(txt); - } - stringp = fmt; - strsep(&stringp, "|"); - /* Send e-mail if applicable */ - if (strlen(vmu->email)) { - int attach_user_voicemail = attach_voicemail; - char *myserveremail = serveremail; - if (vmu->attach > -1) - attach_user_voicemail = vmu->attach; - if (strlen(vmu->serveremail)) - myserveremail = vmu->serveremail; - sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail); - } - if (strlen(vmu->pager)) { - char *myserveremail = serveremail; - if (strlen(vmu->serveremail)) - myserveremail = vmu->serveremail; - sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu); - } - } else - ast_log(LOG_WARNING, "No more messages possible\n"); - } else - ast_log(LOG_WARNING, "No format for saving voicemail?\n"); - free_user(vmu); - } else - ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext); - /* Leave voicemail for someone */ - manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext)); - return res; -} - -static char *mbox(int id) -{ - switch(id) { - case 0: - return "INBOX"; - case 1: - return "Old"; - case 2: - return "Work"; - case 3: - return "Family"; - case 4: - return "Friends"; - case 5: - return "Cust1"; - case 6: - return "Cust2"; - case 7: - return "Cust3"; - case 8: - return "Cust4"; - case 9: - return "Cust5"; - default: - return "Unknown"; - } -} - -static int count_messages(char *dir) -{ - int x; - char fn[256]; - for (x=0;x<MAXMSG;x++) { - make_file(fn, sizeof(fn), dir, x); - if (ast_fileexists(fn, NULL, NULL) < 1) - break; - } - return x; -} - -static int say_and_wait(struct ast_channel *chan, int num) -{ - int d; - d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language); - return d; -} - -static int copy(char *infile, char *outfile) -{ - int ifd; - int ofd; - int res; - int len; - char buf[4096]; - if ((ifd = open(infile, O_RDONLY)) < 0) { - ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile); - return -1; - } - if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) { - ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile); - close(ifd); - return -1; - } - do { - len = read(ifd, buf, sizeof(buf)); - if (len < 0) { - ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno)); - close(ifd); - close(ofd); - unlink(outfile); - } - if (len) { - res = write(ofd, buf, len); - if (res != len) { - ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno)); - close(ifd); - close(ofd); - unlink(outfile); - } - } - } while(len); - close(ifd); - close(ofd); - return 0; -} - -static int save_to_folder(char *dir, int msg, char *context, char *username, int box) -{ - char sfn[256]; - char dfn[256]; - char ddir[256]; - char txt[256]; - char ntxt[256]; - char *dbox = mbox(box); - int x; - make_file(sfn, sizeof(sfn), dir, msg); - make_dir(ddir, sizeof(ddir), context, username, dbox); - mkdir(ddir, 0700); - for (x=0;x<MAXMSG;x++) { - make_file(dfn, sizeof(dfn), ddir, x); - if (ast_fileexists(dfn, NULL, NULL) < 0) - break; - } - if (x >= MAXMSG) - return -1; - ast_filecopy(sfn, dfn, NULL); - if (strcmp(sfn, dfn)) { - snprintf(txt, sizeof(txt), "%s.txt", sfn); - snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn); - copy(txt, ntxt); - } - return 0; -} - -static int adsi_logo(unsigned char *buf) -{ - int bytes = 0; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", ""); - return bytes; -} - -static int adsi_load_vmail(struct ast_channel *chan, int *useadsi) -{ - char buf[256]; - int bytes=0; - int x; - char num[5]; - - *useadsi = 0; - bytes += adsi_data_mode(buf + bytes); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); - - bytes = 0; - bytes += adsi_logo(buf); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", ""); -#ifdef DISPLAY - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", ""); -#endif - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_data_mode(buf + bytes); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); - - if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) { - bytes = 0; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_voice_mode(buf + bytes, 0); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); - return 0; - } - -#ifdef DISPLAY - /* Add a dot */ - bytes = 0; - bytes += adsi_logo(buf); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -#endif - bytes = 0; - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD); - -#ifdef DISPLAY - /* Add another dot */ - bytes = 0; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", ""); - bytes += adsi_voice_mode(buf + bytes, 0); - - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -#endif - - bytes = 0; - /* These buttons we load but don't use yet */ - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD); - -#ifdef DISPLAY - /* Add another dot */ - bytes = 0; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -#endif - - bytes = 0; - for (x=0;x<5;x++) { - snprintf(num, sizeof(num), "%d", x); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1); - } - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD); - -#ifdef DISPLAY - /* Add another dot */ - bytes = 0; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -#endif - - if (adsi_end_download(chan)) { - bytes = 0; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_voice_mode(buf + bytes, 0); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); - return 0; - } - bytes = 0; - bytes += adsi_download_disconnect(buf + bytes); - bytes += adsi_voice_mode(buf + bytes, 0); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD); - - ast_log(LOG_DEBUG, "Done downloading scripts...\n"); - -#ifdef DISPLAY - /* Add last dot */ - bytes = 0; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); -#endif - ast_log(LOG_DEBUG, "Restarting session...\n"); - - bytes = 0; - /* Load the session now */ - if (adsi_load_session(chan, adapp, adver, 1) == 1) { - *useadsi = 1; - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", ""); - } else - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", ""); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); - return 0; -} - -static void adsi_begin(struct ast_channel *chan, int *useadsi) -{ - int x; - if (!adsi_available(chan)) - return; - x = adsi_load_session(chan, adapp, adver, 1); - if (x < 0) - return; - if (!x) { - if (adsi_load_vmail(chan, useadsi)) { - ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n"); - return; - } - } else - *useadsi = 1; -} - -static void adsi_login(struct ast_channel *chan) -{ - char buf[256]; - int bytes=0; - unsigned char keys[8]; - int x; - if (!adsi_available(chan)) - return; - - for (x=0;x<8;x++) - keys[x] = 0; - /* Set one key for next */ - keys[3] = ADSI_KEY_APPS + 3; - - bytes += adsi_logo(buf + bytes); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", ""); - bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT); - bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1); - bytes += adsi_set_keys(buf + bytes, keys); - bytes += adsi_voice_mode(buf + bytes, 0); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static void adsi_password(struct ast_channel *chan) -{ - char buf[256]; - int bytes=0; - unsigned char keys[8]; - int x; - if (!adsi_available(chan)) - return; - - for (x=0;x<8;x++) - keys[x] = 0; - /* Set one key for next */ - keys[3] = ADSI_KEY_APPS + 3; - - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", ""); - bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT); - bytes += adsi_set_keys(buf + bytes, keys); - bytes += adsi_voice_mode(buf + bytes, 0); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static void adsi_folders(struct ast_channel *chan, int start, char *label) -{ - char buf[256]; - int bytes=0; - unsigned char keys[8]; - int x,y; - - if (!adsi_available(chan)) - return; - - for (x=0;x<5;x++) { - y = ADSI_KEY_APPS + 12 + start + x; - if (y > ADSI_KEY_APPS + 12 + 4) - y = 0; - keys[x] = ADSI_KEY_SKT | y; - } - keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17); - keys[6] = 0; - keys[7] = 0; - - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_set_keys(buf + bytes, keys); - bytes += adsi_voice_mode(buf + bytes, 0); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn) -{ - int bytes=0; - char buf[256], buf1[256], buf2[256]; - char fn2[256]; - - char cid[256]=""; - char *val; - char *name, *num; - char datetime[21]=""; - FILE *f; - - unsigned char keys[8]; - - int x; - - if (!adsi_available(chan)) - return; - - /* Retrieve important info */ - snprintf(fn2, sizeof(fn2), "%s.txt", fn); - f = fopen(fn2, "r"); - if (f) { - while(!feof(f)) { - fgets(buf, sizeof(buf), f); - if (!feof(f)) { - char *stringp=NULL; - stringp=buf; - strsep(&stringp, "="); - val = strsep(&stringp, "="); - if (val && strlen(val)) { - if (!strcmp(buf, "callerid")) - strncpy(cid, val, sizeof(cid) - 1); - if (!strcmp(buf, "origdate")) - strncpy(datetime, val, sizeof(datetime) - 1); - } - } - } - fclose(f); - } - /* New meaning for keys */ - for (x=0;x<5;x++) - keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x); - keys[6] = 0x0; - keys[7] = 0x0; - - if (!msg) { - /* No prev key, provide "Folder" instead */ - keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1); - } - if (msg >= last) { - /* If last message ... */ - if (msg) { - /* but not only message, provide "Folder" instead */ - keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1); - bytes += adsi_voice_mode(buf + bytes, 0); - - } else { - /* Otherwise if only message, leave blank */ - keys[3] = 1; - } - } - - if (strlen(cid)) { - ast_callerid_parse(cid, &name, &num); - if (!name) - name = num; - } else - name = "Unknown Caller"; - - /* If deleted, show "undeleted" */ - - if (deleted) - keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11); - - /* Except "Exit" */ - keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5); - snprintf(buf1, sizeof(buf1), "%s%s", folder, - strcasecmp(folder, "INBOX") ? " Messages" : ""); - snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1); - - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_set_keys(buf + bytes, keys); - bytes += adsi_voice_mode(buf + bytes, 0); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted) -{ - int bytes=0; - char buf[256]; - unsigned char keys[8]; - - int x; - - if (!adsi_available(chan)) - return; - - /* New meaning for keys */ - for (x=0;x<5;x++) - keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x); - - keys[6] = 0x0; - keys[7] = 0x0; - - if (!msg) { - /* No prev key, provide "Folder" instead */ - keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1); - } - if (msg >= last) { - /* If last message ... */ - if (msg) { - /* but not only message, provide "Folder" instead */ - keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1); - } else { - /* Otherwise if only message, leave blank */ - keys[3] = 1; - } - } - - /* If deleted, show "undeleted" */ - if (deleted) - keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11); - - /* Except "Exit" */ - keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5); - bytes += adsi_set_keys(buf + bytes, keys); - bytes += adsi_voice_mode(buf + bytes, 0); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg) -{ - char buf[256], buf1[256], buf2[256]; - int bytes=0; - unsigned char keys[8]; - int x; - - char *newm = (new == 1) ? "message" : "messages"; - char *oldm = (old == 1) ? "message" : "messages"; - if (!adsi_available(chan)) - return; - if (new) { - snprintf(buf1, sizeof(buf1), "You have %d new", new); - if (old) { - strcat(buf1, " and"); - snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm); - } else { - snprintf(buf2, sizeof(buf2), "%s.", newm); - } - } else if (old) { - snprintf(buf1, sizeof(buf1), "You have %d old", old); - snprintf(buf2, sizeof(buf2), "%s.", oldm); - } else { - strcpy(buf1, "You have no messages."); - strcpy(buf2, " "); - } - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - - for (x=0;x<6;x++) - keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x); - keys[6] = 0; - keys[7] = 0; - - /* Don't let them listen if there are none */ - if (lastmsg < 0) - keys[0] = 1; - bytes += adsi_set_keys(buf + bytes, keys); - - bytes += adsi_voice_mode(buf + bytes, 0); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static void adsi_status2(struct ast_channel *chan, char *folder, int messages) -{ - char buf[256], buf1[256], buf2[256]; - int bytes=0; - unsigned char keys[8]; - int x; - - char *mess = (messages == 1) ? "message" : "messages"; - - if (!adsi_available(chan)) - return; - - /* Original command keys */ - for (x=0;x<6;x++) - keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x); - - keys[6] = 0; - keys[7] = 0; - - if (messages < 1) - keys[0] = 0; - - snprintf(buf1, sizeof(buf1), "%s%s has", folder, - strcasecmp(folder, "INBOX") ? " folder" : ""); - - if (messages) - snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess); - else - strcpy(buf2, "no messages."); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_set_keys(buf + bytes, keys); - - bytes += adsi_voice_mode(buf + bytes, 0); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); - -} - -static void adsi_clear(struct ast_channel *chan) -{ - char buf[256]; - int bytes=0; - if (!adsi_available(chan)) - return; - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_voice_mode(buf + bytes, 0); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static void adsi_goodbye(struct ast_channel *chan) -{ - char buf[256]; - int bytes=0; - - if (!adsi_available(chan)) - return; - bytes += adsi_logo(buf + bytes); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_voice_mode(buf + bytes, 0); - - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); -} - -static int get_folder(struct ast_channel *chan, int start) -{ - int x; - int d; - char fn[256]; - d = play_and_wait(chan, "vm-press"); - if (d) - return d; - for (x = start; x< 5; x++) { - if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language))) - return d; - d = play_and_wait(chan, "vm-for"); - if (d) - return d; - snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); - d = play_and_wait(chan, fn); - if (d) - return d; - d = play_and_wait(chan, "vm-messages"); - if (d) - return d; - d = ast_waitfordigit(chan, 500); - if (d) - return d; - } - d = play_and_wait(chan, "vm-tocancel"); - if (d) - return d; - d = ast_waitfordigit(chan, 4000); - return d; -} - -static int get_folder2(struct ast_channel *chan, char *fn, int start) -{ - int res = 0; - res = play_and_wait(chan, fn); - while (((res < '0') || (res > '9')) && - (res != '#') && (res >= 0)) { - res = get_folder(chan, 0); - } - return res; -} - -static int -forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt) -{ - char username[70]; - char sys[256]; - char todir[256]; - int todircount=0; - long duration; - struct ast_config *mif; - char miffile[256]; - char fn[256]; - char callerid[512]; - int res = 0; - struct ast_vm_user *receiver, srec; - char tmp[256]; - char *stringp, *s; - - while(!res) { - res = ast_streamfile(chan, "vm-extension", chan->language); - if (res) - break; - if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0)) - break; - if ((receiver = find_user(&srec, context, username))) { - /* if (play_and_wait(chan, "vm-savedto")) - break; - */ - - snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username); - snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir); - ast_log(LOG_DEBUG, sys); - system(sys); - - todircount = count_messages(todir); - strncpy(tmp, fmt, sizeof(tmp)); - stringp = tmp; - while((s = strsep(&stringp, "|"))) { - snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s); - ast_log(LOG_DEBUG, sys); - system(sys); - } - snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount); - ast_log(LOG_DEBUG, sys); - system(sys); - snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount); - - /* load the information on the source message so we can send an e-mail like a new message */ - snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg); - if ((mif=ast_load(miffile))) { - - /* set callerid and duration variables */ - snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid")); - duration = atol(ast_variable_retrieve(mif, NULL, "duration")); - - if (strlen(receiver->email)) { - int attach_user_voicemail = attach_voicemail; - char *myserveremail = serveremail; - if (receiver->attach > -1) - attach_user_voicemail = receiver->attach; - if (strlen(receiver->serveremail)) - myserveremail = receiver->serveremail; - sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail); - } - - if (strlen(receiver->pager)) { - char *myserveremail = serveremail; - if (strlen(receiver->serveremail)) - myserveremail = receiver->serveremail; - sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver); - } - - ast_destroy(mif); /* or here */ - } - /* Leave voicemail for someone */ - manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username)); - - /* give confirmatopm that the message was saved */ - res = play_and_wait(chan, "vm-message"); - if (!res) - res = play_and_wait(chan, "vm-saved"); - free_user(receiver); - break; - } else { - res = play_and_wait(chan, "pbx-invalid"); - } - } - return res; -} - -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 wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file) -{ - int res; - if ((res = ast_streamfile(chan, file, chan->language))) - ast_log(LOG_WARNING, "Unable to play message %s\n", file); - if (!res) - res = ast_waitstream(chan, AST_DIGIT_ANY); - return res; -} - -static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) -{ - int res; - if ((res = ast_streamfile(chan, file, chan->language))) - ast_log(LOG_WARNING, "Unable to play message %s\n", file); - if (!res) - res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms); - return res; -} - -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; - struct vm_zone *the_zone = NULL; - struct ast_config *msg_cfg; - time_t t; - long tin; - - 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",&tin) < 1) { - ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename); - return 0; - } - t = tin; - 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; - } - } - -/* No internal variable parsing for now, so we'll comment it out for the time being */ -#if 0 - /* Set the DIFF_* variables */ - localtime_r(&t, &time_now); - gettimeofday(&tv_now,NULL); - tnow = tv_now.tv_sec; - localtime_r(&tnow,&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. */ -#endif - if (the_zone) - res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone); - else - res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL); -#if 0 - pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL); -#endif - 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; - make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg); - adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn); - if (!msg) - res = wait_file2(chan, vms, "vm-first"); - else if (msg == vms->lastmsg) - res = wait_file2(chan, vms, "vm-last"); - if (!res) { - res = wait_file2(chan, vms, "vm-message"); - if (msg && (msg != vms->lastmsg)) { - if (!res) - 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; - res = wait_file(chan, vms, vms->fn); - } - return res; -} - -static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box) -{ - strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1); - make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox); - vms->lastmsg = count_messages(vms->curdir) - 1; - snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox); -} - -static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu) -{ - int x; - char ntxt[256] = ""; - char txt[256] = ""; - if (vms->lastmsg > -1) { - /* Get the deleted messages fixed */ - vms->curmsg = -1; - for (x=0;x < MAXMSG;x++) { - if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { - /* Save this message. It's not in INBOX or hasn't been heard */ - make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); - if (ast_fileexists(vms->fn, NULL, NULL) < 1) - break; - vms->curmsg++; - make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); - if (strcmp(vms->fn, vms->fn2)) { - snprintf(txt, sizeof(txt), "%s.txt", vms->fn); - snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); - ast_filerename(vms->fn, vms->fn2, NULL); - rename(txt, ntxt); - } - } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { - /* Move to old folder before deleting */ - save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); - } - } - for (x = vms->curmsg + 1; x <= MAXMSG; x++) { - make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); - if (ast_fileexists(vms->fn, NULL, NULL) < 1) - break; - snprintf(txt, sizeof(txt), "%s.txt", vms->fn); - ast_filedelete(vms->fn, NULL); - unlink(txt); - } - } - memset(vms->deleted, 0, sizeof(vms->deleted)); - memset(vms->heard, 0, sizeof(vms->heard)); -} - -static int vm_intro(struct ast_channel *chan,struct vm_state *vms) -{ - /* Introduce messages they have */ - int res; - res = play_and_wait(chan, "vm-youhave"); - if (!res) { - if (vms->newmessages) { - res = say_and_wait(chan, vms->newmessages); - if (!res) - res = play_and_wait(chan, "vm-INBOX"); - if (vms->oldmessages && !res) - res = play_and_wait(chan, "vm-and"); - else if (!res) { - if ((vms->newmessages == 1)) - res = play_and_wait(chan, "vm-message"); - else - res = play_and_wait(chan, "vm-messages"); - } - - } - if (!res && vms->oldmessages) { - res = say_and_wait(chan, vms->oldmessages); - if (!res) - res = play_and_wait(chan, "vm-Old"); - if (!res) { - if (vms->oldmessages == 1) - res = play_and_wait(chan, "vm-message"); - else - res = play_and_wait(chan, "vm-messages"); - } - } - if (!res) { - if (!vms->oldmessages && !vms->newmessages) { - res = play_and_wait(chan, "vm-no"); - if (!res) - res = play_and_wait(chan, "vm-messages"); - } - } - } - return res; -} - -static int vm_instructions(struct ast_channel *chan, struct vm_state *vms) -{ - int res = 0; - /* Play instructions and wait for new command */ - while(!res) { - if (vms->starting) { - if (vms->lastmsg > -1) { - res = play_and_wait(chan, "vm-onefor"); - if (!res) - res = play_and_wait(chan, vms->vmbox); - if (!res) - res = play_and_wait(chan, "vm-messages"); - } - if (!res) - res = play_and_wait(chan, "vm-opts"); - } else { - if (vms->curmsg) - res = play_and_wait(chan, "vm-prev"); - if (!res) - res = play_and_wait(chan, "vm-repeat"); - if (!res && (vms->curmsg != vms->lastmsg)) - res = play_and_wait(chan, "vm-next"); - if (!res) { - if (!vms->deleted[vms->curmsg]) - res = play_and_wait(chan, "vm-delete"); - else - res = play_and_wait(chan, "vm-undelete"); - if (!res) - res = play_and_wait(chan, "vm-toforward"); - if (!res) - res = play_and_wait(chan, "vm-savemessage"); - } - } - if (!res) - res = play_and_wait(chan, "vm-helpexit"); - if (!res) - res = ast_waitfordigit(chan, 6000); - if (!res) { - vms->repeats++; - if (vms->repeats > 2) { - res = play_and_wait(chan, "vm-goodbye"); - if (!res) - res = 't'; - } - } - } - return res; -} - -static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc) -{ - int cmd = 0; - int retries = 0; - char newpassword[80] = ""; - char newpassword2[80] = ""; - char prefile[256]=""; - char buf[256]; - int bytes=0; - - if (adsi_available(chan)) - { - bytes += adsi_logo(buf + bytes); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", ""); - bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", ""); - bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1); - bytes += adsi_voice_mode(buf + bytes, 0); - adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY); - } - while((cmd >= 0) && (cmd != 't')) { - if (cmd) - retries = 0; - switch (cmd) { - case '1': - snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username); - cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc); - break; - case '2': - snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username); - cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc); - break; - case '3': - snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username); - cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc); - break; - case '4': - newpassword[1] = '\0'; - newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword"); - if (cmd < 0) - break; - if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) { - break; - } - newpassword2[1] = '\0'; - newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword"); - if (cmd < 0) - break; - - if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) { - break; - } - if (strcmp(newpassword, newpassword2)) { - ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2); - cmd = play_and_wait(chan, "vm-mismatch"); - break; - } - vm_change_password(vmu,newpassword); - ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword)); - cmd = play_and_wait(chan,"vm-passchanged"); - break; - case '*': - cmd = 't'; - break; - default: - cmd = play_and_wait(chan,"vm-options"); - if (!cmd) - cmd = ast_waitfordigit(chan,6000); - if (!cmd) - retries++; - if (retries > 3) - cmd = 't'; - } - } - if (cmd == 't') - cmd = 0; - return cmd; -} - -static int vm_execmain(struct ast_channel *chan, void *data) -{ - /* XXX This is, admittedly, some pretty horrendus code. For some - reason it just seemed a lot easier to do with GOTO's. I feel - like I'm back in my GWBASIC days. XXX */ - int res=-1; - int valid = 0; - int prefix = 0; - int cmd=0; - struct localuser *u; - char prefixstr[80] =""; - char empty[80] = ""; - int box; - int useadsi = 0; - int skipuser = 0; - char tmp[256], *ext; - char fmtc[256] = ""; - char password[80]; - struct vm_state vms; - int logretries = 0; - struct ast_vm_user *vmu = NULL, vmus; - char *context=NULL; - - LOCAL_USER_ADD(u); - memset(&vms, 0, sizeof(vms)); - strncpy(fmtc, vmfmts, sizeof(fmtc) - 1); - if (chan->_state != AST_STATE_UP) - ast_answer(chan); - - if (data && strlen(data)) { - strncpy(tmp, data, sizeof(tmp) - 1); - ext = tmp; - - switch (*ext) { - case 's': - /* We should skip the user's password */ - valid++; - ext++; - break; - case 'p': - /* We should prefix the mailbox with the supplied data */ - prefix++; - ext++; - break; - } - - context = strchr(ext, '@'); - if (context) { - *context = '\0'; - context++; - } - - if (prefix) - strncpy(prefixstr, ext, sizeof(prefixstr) - 1); - else - strncpy(vms.username, ext, sizeof(vms.username) - 1); - if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username))) - skipuser++; - else - valid = 0; - - } - - /* If ADSI is supported, setup login screen */ - adsi_begin(chan, &useadsi); - if (!skipuser && useadsi) - adsi_login(chan); - if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) { - ast_log(LOG_WARNING, "Couldn't stream login file\n"); - goto out; - } - - /* Authenticate them and get their mailbox/password */ - - while (!valid && (logretries < maxlogins)) { - /* Prompt for, and read in the username */ - if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) { - ast_log(LOG_WARNING, "Couldn't read username\n"); - goto out; - } - if (!strlen(vms.username)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n"); - res = 0; - goto out; - } - if (useadsi) - adsi_password(chan); - if (ast_streamfile(chan, "vm-password", chan->language)) { - ast_log(LOG_WARNING, "Unable to stream password file\n"); - goto out; - } - if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) { - ast_log(LOG_WARNING, "Unable to read password\n"); - goto out; - } - if (prefix) { - char fullusername[80] = ""; - strncpy(fullusername, prefixstr, sizeof(fullusername) - 1); - strncat(fullusername, vms.username, sizeof(fullusername) - 1); - strncpy(vms.username, fullusername, sizeof(vms.username) - 1); - } - if (!skipuser) - vmu = find_user(&vmus, context, vms.username); - if (vmu && !strcmp(vmu->password, password)) - valid++; - else { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>"); - if (prefix) - strncpy(vms.username, empty, sizeof(vms.username) -1); - } - if (!valid) { - if (useadsi) - adsi_login(chan); - if (ast_streamfile(chan, "vm-incorrect", chan->language)) - break; - } - logretries++; - } - if (!valid && (logretries >= maxlogins)) { - ast_stopstream(chan); - res = play_and_wait(chan, "vm-goodbye"); - if (res > 0) - res = 0; - } - - if (valid) { - snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context); - mkdir(vms.curdir, 0700); - snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username); - mkdir(vms.curdir, 0700); - /* Retrieve old and new message counts */ - open_mailbox(&vms, vmu, 1); - vms.oldmessages = vms.lastmsg + 1; - /* Start in INBOX */ - open_mailbox(&vms, vmu, 0); - vms.newmessages = vms.lastmsg + 1; - - - /* Select proper mailbox FIRST!! */ - if (!vms.newmessages && vms.oldmessages) { - /* If we only have old messages start here */ - open_mailbox(&vms, vmu, 1); - } - - if (useadsi) - adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg); - res = 0; - cmd = vm_intro(chan, &vms); - vms.repeats = 0; - vms.starting = 1; - while((cmd > -1) && (cmd != 't') && (cmd != '#')) { - /* Run main menu */ - switch(cmd) { - case '1': - vms.curmsg = 0; - /* Fall through */ - case '5': - if (vms.lastmsg > -1) { - cmd = play_message(chan, vmu, &vms, vms.curmsg); - } else { - cmd = play_and_wait(chan, "vm-youhave"); - if (!cmd) - cmd = play_and_wait(chan, "vm-no"); - if (!cmd) { - snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox); - cmd = play_and_wait(chan, vms.fn); - } - if (!cmd) - cmd = play_and_wait(chan, "vm-messages"); - } - break; - case '2': /* Change folders */ - if (useadsi) - adsi_folders(chan, 0, "Change to folder..."); - cmd = get_folder2(chan, "vm-changeto", 0); - if (cmd == '#') { - cmd = 0; - } else if (cmd > 0) { - cmd = cmd - '0'; - close_mailbox(&vms, vmu); - open_mailbox(&vms, vmu, cmd); - cmd = 0; - } - if (useadsi) - adsi_status2(chan, vms.curbox, vms.lastmsg + 1); - if (!cmd) - cmd = play_and_wait(chan, vms.vmbox); - if (!cmd) - cmd = play_and_wait(chan, "vm-messages"); - vms.starting = 1; - break; - case '4': - if (vms.curmsg) { - vms.curmsg--; - cmd = play_message(chan, vmu, &vms, vms.curmsg); - } else { - cmd = play_and_wait(chan, "vm-nomore"); - } - break; - case '6': - if (vms.curmsg < vms.lastmsg) { - vms.curmsg++; - cmd = play_message(chan, vmu, &vms, vms.curmsg); - } else { - cmd = play_and_wait(chan, "vm-nomore"); - } - break; - case '7': - vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg]; - if (useadsi) - adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]); - if (vms.deleted[vms.curmsg]) - cmd = play_and_wait(chan, "vm-deleted"); - else - cmd = play_and_wait(chan, "vm-undeleted"); - break; - case '8': - if(vms.lastmsg > -1) - cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts); - else - cmd = play_and_wait(chan, "vm-nomore"); - break; - case '9': - if (useadsi) - adsi_folders(chan, 1, "Save to folder..."); - cmd = get_folder2(chan, "vm-savefolder", 1); - box = 0; /* Shut up compiler */ - if (cmd == '#') { - cmd = 0; - break; - } else if (cmd > 0) { - box = cmd = cmd - '0'; - cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd); - vms.deleted[vms.curmsg]=1; - } - make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg); - if (useadsi) - adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn); - if (!cmd) - cmd = play_and_wait(chan, "vm-message"); - if (!cmd) - cmd = say_and_wait(chan, vms.curmsg + 1); - if (!cmd) - cmd = play_and_wait(chan, "vm-savedto"); - if (!cmd) { - snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box)); - cmd = play_and_wait(chan, vms.fn); - } - if (!cmd) - cmd = play_and_wait(chan, "vm-messages"); - break; - case '*': - if (!vms.starting) { - cmd = play_and_wait(chan, "vm-onefor"); - if (!cmd) - cmd = play_and_wait(chan, vms.vmbox); - if (!cmd) - cmd = play_and_wait(chan, "vm-messages"); - if (!cmd) - cmd = play_and_wait(chan, "vm-opts"); - } else - cmd = 0; - break; - case '0': - cmd = vm_options(chan, vmu, &vms, vmfmts); - if (useadsi) - adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg); - break; - default: /* Nothing */ - cmd = vm_instructions(chan, &vms); - break; - } - } - if ((cmd == 't') || (cmd == '#')) { - /* Timeout */ - res = 0; - } else { - /* Hangup */ - res = -1; - } - } -out: - if (res > -1) { - ast_stopstream(chan); - adsi_goodbye(chan); - if(valid) { - res = play_and_wait(chan, "vm-goodbye"); - if (res > 0) - res = 0; - } - if (useadsi) - adsi_unload_session(chan); - } - if (vmu) - close_mailbox(&vms, vmu); - if (vmu) - free_user(vmu); - if (valid) { - manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username)); - } - LOCAL_USER_REMOVE(u); - return res; - -} - -static int vm_exec(struct ast_channel *chan, void *data) -{ - int res=0, silent=0, busy=0, unavail=0; - struct localuser *u; - char tmp[256], *ext; - - LOCAL_USER_ADD(u); - if (chan->_state != AST_STATE_UP) - ast_answer(chan); - if (data) - strncpy(tmp, data, sizeof(tmp) - 1); - else { - res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0); - if (res < 0) - return res; - if (!strlen(tmp)) - return 0; - } - ext = tmp; - while(*ext) { - if (*ext == 's') { - silent = 2; - ext++; - } else if (*ext == 'b') { - busy=1; - ext++; - } else if (*ext == 'u') { - unavail=1; - ext++; - } else - break; - } - res = leave_voicemail(chan, ext, silent, busy, unavail); - LOCAL_USER_REMOVE(u); - return res; -} - -static int append_mailbox(char *context, char *mbox, char *data) -{ - /* Assumes lock is already held */ - char tmp[256] = ""; - char *stringp; - char *s; - struct ast_vm_user *vmu; - strncpy(tmp, data, sizeof(tmp)); - vmu = malloc(sizeof(struct ast_vm_user)); - if (vmu) { - memset(vmu, 0, sizeof(struct ast_vm_user)); - strncpy(vmu->context, context, sizeof(vmu->context)); - strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox)); - vmu->attach = -1; - stringp = tmp; - if ((s = strsep(&stringp, ","))) - strncpy(vmu->password, s, sizeof(vmu->password)); - if (stringp && (s = strsep(&stringp, ","))) - strncpy(vmu->fullname, s, sizeof(vmu->fullname)); - if (stringp && (s = strsep(&stringp, ","))) - strncpy(vmu->email, s, sizeof(vmu->email)); - if (stringp && (s = strsep(&stringp, ","))) - strncpy(vmu->pager, s, sizeof(vmu->pager)); - if (stringp && (s = strsep(&stringp, ","))) - apply_options(vmu, s); - vmu->next = NULL; - if (usersl) - usersl->next = vmu; - else - users = vmu; - usersl = vmu; - } - return 0; -} - -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; - char *astattach; - char *silencestr; - char *thresholdstr; - char *fmt; - char *astemail; - char *s; - int x; - - cfg = ast_load(VOICEMAIL_CONFIG); - ast_mutex_lock(&vmlock); - cur = users; - while(cur) { - l = cur; - 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) { - /* General settings */ - attach_voicemail = 1; - if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) - astattach = "yes"; - attach_voicemail = ast_true(astattach); - maxsilence = 0; - if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) { - maxsilence = atoi(silencestr); - if (maxsilence > 0) - maxsilence *= 1000; - } - - silencethreshold = 256; - if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold"))) - silencethreshold = atoi(thresholdstr); - - if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) - astemail = ASTERISK_USERNAME; - strncpy(serveremail, astemail, sizeof(serveremail) - 1); - - vmmaxmessage = 0; - if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) { - if (sscanf(s, "%d", &x) == 1) { - vmmaxmessage = x; - } else { - ast_log(LOG_WARNING, "Invalid max message time length\n"); - } - } - fmt = ast_variable_retrieve(cfg, "general", "format"); - if (!fmt) - fmt = "wav"; - strncpy(vmfmts, fmt, sizeof(vmfmts) - 1); - - skipms = 3000; - if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) { - if (sscanf(s, "%d", &x) == 1) { - maxgreet = x; - } else { - ast_log(LOG_WARNING, "Invalid max message greeting length\n"); - } - } - - if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) { - if (sscanf(s, "%d", &x) == 1) { - skipms = x; - } else { - ast_log(LOG_WARNING, "Invalid skipms value\n"); - } - } - - maxlogins = 3; - if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) { - if (sscanf(s, "%d", &x) == 1) { - maxlogins = x; - } else { - ast_log(LOG_WARNING, "Invalid max failed login attempts\n"); - } - } - -#ifdef USEMYSQLVM - if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) { - strcpy(dbuser, "test"); - } else { - strcpy(dbuser, s); - } - if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) { - strcpy(dbpass, "test"); - } else { - strcpy(dbpass, s); - } - if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) { - strcpy(dbhost, ""); - } else { - strcpy(dbhost, s); - } - if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) { - strcpy(dbname, "vmdb"); - } else { - strcpy(dbname, s); - } -#endif - -#ifdef USEPOSTGRESVM - if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) { - strcpy(dboption, "dboption not-specified in voicemail.conf"); - } else { - strcpy(dboption, s); - } -#endif - cat = ast_category_browse(cfg, NULL); - while(cat) { - if (strcasecmp(cat, "general")) { - var = ast_variable_browse(cfg, cat); - if (strcasecmp(cat, "zonemessages")) { -#ifndef USESQLVM - /* 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 = ast_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); - } - memset(fromstring,0,sizeof(fromstring)); - memset(emailtitle,0,sizeof(emailtitle)); - if (emailbody) { - free(emailbody); - emailbody = NULL; - } - if ((s=ast_variable_retrieve(cfg, "general", "pbxskip"))) - pbxskip = ast_true(s); - if ((s=ast_variable_retrieve(cfg, "general", "fromstring"))) - strncpy(fromstring,s,sizeof(fromstring)-1); - if ((s=ast_variable_retrieve(cfg, "general", "emailtitle"))) - strncpy(emailtitle,s,sizeof(emailtitle)-1); - if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) { - char *tmpread, *tmpwrite; - emailbody = strdup(s); - - /* substitute strings \t and \n into the apropriate characters */ - tmpread = tmpwrite = emailbody; - while ((tmpwrite = strchr(tmpread,'\\'))) { - int len = strlen("\n"); - switch (tmpwrite[1]) { - case 'n': - strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1); - strncpy(tmpwrite,"\n",len); - break; - case 't': - strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1); - strncpy(tmpwrite,"\t",len); - break; - default: - ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]); - } - tmpread = tmpwrite+len; - } - } - ast_destroy(cfg); - ast_mutex_unlock(&vmlock); - return 0; - } else { - ast_mutex_unlock(&vmlock); - ast_log(LOG_WARNING, "Error reading voicemail config\n"); - return -1; - } -} - -int reload(void) -{ - return(load_config()); -} - -int unload_module(void) -{ - int res; - STANDARD_HANGUP_LOCALUSERS; - res = ast_unregister_application(app); - res |= ast_unregister_application(app2); - sql_close(); - return res; -} - -int load_module(void) -{ - int res; - res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm); - res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain); - if (res) - return(res); - - if ((res=load_config())) { - return(res); - } - - if ((res = sql_init())) { - ast_log(LOG_WARNING, "SQL init\n"); - return res; - } - return res; -} - -char *description(void) -{ - return tdesc; -} - -int usecount(void) -{ - int res; - STANDARD_USECOUNT(res); - return res; -} - -char *key() -{ - return ASTERISK_GPL_KEY; -} |