2 * Asterisk -- A telephony toolkit for Linux.
4 * Voicemail System (did you ever think it could be so easy?)
6 * Copyright (C) 2003, Digium Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
39 /* we define USESQLVM when we have MySQL or POSTGRES */
41 #include <mysql/mysql.h>
47 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
49 #include <postgresql/libpq-fe.h>
54 static inline int sql_init(void) { return 0; }
55 static inline void sql_close(void) { }
59 #include "../asterisk.h"
60 #include "../astconf.h"
62 #define COMMAND_TIMEOUT 5000
64 #define VOICEMAIL_CONFIG "voicemail.conf"
65 #define ASTERISK_USERNAME "asterisk"
67 #define SENDMAIL "/usr/sbin/sendmail -t"
69 #define INTRO "vm-intro"
72 #define MAX_OTHER_FORMATS 10
74 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
76 #define BASEMAXINLINE 256
77 #define BASELINELEN 72
78 #define BASEMAXINLINE 256
81 #define MAX_DATETIME_FORMAT 512
82 #define DIGITS_DIR AST_SOUNDS "/digits/"
88 unsigned char iobuf[BASEMAXINLINE];
102 struct ast_vm_user *next;
108 char msg_format[512];
109 struct vm_zone *next;
129 static char *tdesc = "Comedian Mail (Voicemail System)";
131 static char *adapp = "CoMa";
133 static char *adsec = "_AST";
135 static char *addesc = "Comedian Mail";
137 static int adver = 1;
139 static char *synopsis_vm =
140 "Leave a voicemail message";
142 static char *descrip_vm =
143 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
144 "extension (must be configured in voicemail.conf). If the extension is\n"
145 "preceded by an 's' then instructions for leaving the message will be\n"
146 "skipped. If the extension is preceeded by 'u' then the \"unavailable\"\n"
147 "message will be played (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it\n"
148 "exists. If the extension is preceeded by a 'b' then the the busy message\n"
149 "will be played (that is, busy instead of unavail).\n"
150 "If the requested mailbox does not exist, and there exists a priority\n"
151 "n + 101, then that priority will be taken next.\n"
152 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
153 "Otherwise, it returns 0.\n";
155 static char *synopsis_vmain =
156 "Enter voicemail system";
158 static char *descrip_vmain =
159 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
160 "for the checking of voicemail. The mailbox can be passed as the option,\n"
161 "which will stop the voicemail system from prompting the user for the mailbox.\n"
162 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
163 "a context is specified, logins are considered in that context only.\n"
164 "Returns -1 if the user hangs up or 0 otherwise.\n";
166 /* Leave a message */
167 static char *capp = "VoiceMail2";
168 static char *app = "VoiceMail";
170 /* Check mail, control, etc */
171 static char *capp2 = "VoiceMailMain2";
172 static char *app2 = "VoiceMailMain";
174 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
175 struct ast_vm_user *users;
176 struct ast_vm_user *usersl;
177 struct vm_zone *zones = NULL;
178 struct vm_zone *zonesl = NULL;
179 static int attach_voicemail;
180 static int maxsilence;
181 static int silencethreshold = 128;
182 static char serveremail[80];
183 static char vmfmts[80];
184 static int vmmaxmessage;
187 static int maxlogins;
189 static char *emailbody = NULL;
190 static int pbxskip = 0;
191 static char fromstring[100];
192 static char emailtitle[100];
198 static void apply_options(struct ast_vm_user *vmu, char *options)
200 /* Destructively Parse options and apply */
201 char *stringp = ast_strdupa(options);
204 while((s = strsep(&stringp, "|"))) {
206 if ((var = strsep(&value, "=")) && value) {
207 if (!strcasecmp(var, "attach")) {
212 } else if (!strcasecmp(var, "serveremail")) {
213 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
214 } else if (!strcasecmp(var, "tz")) {
215 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
223 #include "mysql-vm-routines.h"
230 ast_mutex_t postgreslock;
232 static int sql_init(void)
234 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
235 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
237 dbhandler=PQconnectdb(dboption);
238 if (PQstatus(dbhandler) == CONNECTION_BAD) {
239 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
242 ast_mutex_init(&postgreslock);
244 /* fprintf(stderr,"postgres login OK\n"); */
248 static void sql_close(void)
254 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
262 char options[160] = "";
263 struct ast_vm_user *retval;
265 retval=malloc(sizeof(struct ast_vm_user));
267 /* fprintf(stderr,"postgres find_user:\n"); */
270 *retval->mailbox='\0';
271 *retval->context='\0';
272 strcpy(retval->password, "NULL");
273 *retval->fullname='\0';
276 *retval->serveremail='\0';
281 strcpy(retval->mailbox, mailbox);
284 strcpy(retval->context, context);
288 strcpy(retval->context, "default");
290 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
292 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
293 ast_mutex_lock(&postgreslock);
294 PGSQLres=PQexec(dbhandler,query);
295 if (PGSQLres!=NULL) {
296 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
297 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
298 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
300 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
303 ast_mutex_unlock(&postgreslock);
307 numFields = PQnfields(PGSQLres);
308 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
309 if (PQntuples(PGSQLres) != 1) {
310 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
312 ast_mutex_unlock(&postgreslock);
316 for (i=0; i<numFields; i++) {
317 fname = PQfname(PGSQLres,i);
318 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
319 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
320 } else if (!strcmp(fname, "fullname")) {
321 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
322 } else if (!strcmp(fname, "email")) {
323 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
324 } else if (!strcmp(fname, "pager")) {
325 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
326 } else if (!strcmp(fname, "options")) {
327 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
328 apply_options(retval, options);
333 ast_mutex_unlock(&postgreslock);
337 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
338 ast_mutex_unlock(&postgreslock);
343 } /* malloc() retval */
348 static void vm_change_password(struct ast_vm_user *vmu, char *password)
353 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);
355 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
357 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
358 ast_mutex_lock(&postgreslock);
359 PQexec(dbhandler, query);
360 strcpy(vmu->password, password);
361 ast_mutex_unlock(&postgreslock);
364 static void reset_user_pw(char *context, char *mailbox, char *password)
369 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
371 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
373 ast_mutex_lock(&postgreslock);
374 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
375 PQexec(dbhandler, query);
376 ast_mutex_unlock(&postgreslock);
379 #endif /* Postgres */
382 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
384 /* This function could be made to generate one from a database, too */
385 struct ast_vm_user *vmu=NULL, *cur;
386 ast_mutex_lock(&vmlock);
389 if ((!context || !strcasecmp(context, cur->context)) &&
390 (!strcasecmp(mailbox, cur->mailbox)))
398 /* Make a copy, so that on a reload, we have no race */
399 vmu = malloc(sizeof(struct ast_vm_user));
401 memcpy(vmu, cur, sizeof(struct ast_vm_user));
409 ast_mutex_unlock(&vmlock);
413 static int reset_user_pw(char *context, char *mailbox, char *newpass)
415 /* This function could be made to generate one from a database, too */
416 struct ast_vm_user *cur;
418 ast_mutex_lock(&vmlock);
421 if ((!context || !strcasecmp(context, cur->context)) &&
422 (!strcasecmp(mailbox, cur->mailbox)))
427 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
430 ast_mutex_unlock(&vmlock);
434 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
436 /* There's probably a better way of doing this. */
437 /* That's why I've put the password change in a separate function. */
438 /* This could also be done with a database function */
444 char tmpin[AST_CONFIG_MAX_PATH];
445 char tmpout[AST_CONFIG_MAX_PATH];
446 char *user, *pass, *rest, *trim;
447 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
448 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
449 configin = fopen((char *)tmpin,"r");
451 configout = fopen((char *)tmpout,"w+");
454 if(!configin || !configout) {
458 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
462 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
466 while (!feof(configin)) {
467 /* Read in the line */
468 fgets(inbuf, sizeof(inbuf), configin);
469 if (!feof(configin)) {
470 /* Make a backup of it */
471 memcpy(orig, inbuf, sizeof(orig));
472 /* Strip trailing \n and comment */
473 inbuf[strlen(inbuf) - 1] = '\0';
474 user = strchr(inbuf, ';');
480 pass = strchr(user, '=');
483 while(*trim && *trim < 33) {
493 while(*pass && *pass < 33)
497 rest = strchr(pass,',');
504 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
505 /* This is the line */
507 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
509 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
512 /* Put it back like it was */
513 fprintf(configout, orig);
520 unlink((char *)tmpin);
521 rename((char *)tmpout,(char *)tmpin);
522 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
523 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
527 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
529 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
532 static int make_file(char *dest, int len, char *dir, int num)
534 return snprintf(dest, len, "%s/msg%04d", dir, num);
538 inbuf(struct baseio *bio, FILE *fi)
545 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
560 inchar(struct baseio *bio, FILE *fi)
562 if(bio->iocp>=bio->iolen)
566 return bio->iobuf[bio->iocp++];
570 ochar(struct baseio *bio, int c, FILE *so)
572 if(bio->linelength>=BASELINELEN) {
573 if(fputs(eol,so)==EOF)
579 if(putc(((unsigned char)c),so)==EOF)
587 static int base_encode(char *filename, FILE *so)
589 unsigned char dtable[BASEMAXINLINE];
594 memset(&bio, 0, sizeof(bio));
595 bio.iocp = BASEMAXINLINE;
597 if ( !(fi = fopen(filename, "rb"))) {
598 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
606 dtable[26+i+9]= 'j'+i;
610 dtable[26+i+18]= 's'+i;
619 unsigned char igroup[3],ogroup[4];
622 igroup[0]= igroup[1]= igroup[2]= 0;
625 if ( (c = inchar(&bio, fi)) == EOF) {
630 igroup[n]= (unsigned char)c;
634 ogroup[0]= dtable[igroup[0]>>2];
635 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
636 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
637 ogroup[3]= dtable[igroup[2]&0x3F];
647 ochar(&bio, ogroup[i], so);
651 if(fputs(eol,so)==EOF)
659 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)
670 struct vm_zone *the_zone = NULL;
672 if (!strcmp(format, "wav49"))
674 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
675 p = popen(SENDMAIL, "w");
677 gethostname(host, sizeof(host));
678 if (strchr(srcemail, '@'))
679 strncpy(who, srcemail, sizeof(who)-1);
681 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
683 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
686 /* Does this user have a timezone specified? */
687 if (strlen(vmu->zonetag)) {
688 /* Find the zone in the list */
692 if (!strcmp(z->name, vmu->zonetag)) {
701 ast_localtime(&t,&tm,the_zone->timezone);
703 ast_localtime(&t,&tm,NULL);
704 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
705 fprintf(p, "Date: %s\n", date);
708 fprintf(p, "From: %s <%s>\n", fromstring, who);
710 fprintf(p, "From: Asterisk PBX <%s>\n", who);
711 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
715 fprintf(p, emailtitle, msgnum, mailbox) ;
720 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
722 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
723 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
724 fprintf(p, "MIME-Version: 1.0\n");
725 if (attach_user_voicemail) {
727 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
729 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
731 fprintf(p, "--%s\n", bound);
733 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
734 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
736 struct ast_channel *ast = ast_channel_alloc(0);
739 int vmlen = strlen(emailbody)*3 + 200;
740 if ((passdata = alloca(vmlen))) {
741 memset(passdata, 0, vmlen);
742 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
743 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
744 sprintf(passdata,"%d",msgnum);
745 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
746 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
747 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
748 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
749 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
750 fprintf(p, "%s\n",passdata);
751 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
752 ast_channel_free(ast);
753 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
755 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
757 "in mailbox %s from %s, on %s so you might\n"
758 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
759 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
761 if (attach_user_voicemail) {
762 fprintf(p, "--%s\n", bound);
763 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
764 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
765 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
766 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
768 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
769 base_encode(fname, p);
770 fprintf(p, "\n\n--%s--\n.\n", bound);
774 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
780 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
789 struct vm_zone *the_zone = NULL;
790 p = popen(SENDMAIL, "w");
793 gethostname(host, sizeof(host));
794 if (strchr(srcemail, '@'))
795 strncpy(who, srcemail, sizeof(who)-1);
797 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
799 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
802 /* Does this user have a timezone specified? */
803 if (strlen(vmu->zonetag)) {
804 /* Find the zone in the list */
808 if (!strcmp(z->name, vmu->zonetag)) {
817 ast_localtime(&t,&tm,the_zone->timezone);
819 ast_localtime(&t,&tm,NULL);
821 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
822 fprintf(p, "Date: %s\n", date);
823 fprintf(p, "From: Asterisk PBX <%s>\n", who);
824 fprintf(p, "To: %s\n", pager);
825 fprintf(p, "Subject: New VM\n\n");
826 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
827 fprintf(p, "New %s long msg in box %s\n"
828 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
831 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
837 static int get_date(char *s, int len)
843 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
846 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
850 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
851 if (ast_fileexists(fn, NULL, NULL) > 0) {
852 res = ast_streamfile(chan, fn, chan->language);
855 res = ast_waitstream(chan, ecodes);
859 res = ast_streamfile(chan, "vm-theperson", chan->language);
862 res = ast_waitstream(chan, ecodes);
865 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
870 res = ast_streamfile(chan, "vm-isonphone", chan->language);
872 res = ast_streamfile(chan, "vm-isunavail", chan->language);
875 res = ast_waitstream(chan, ecodes);
879 static int play_and_wait(struct ast_channel *chan, char *fn)
882 d = ast_streamfile(chan, fn, chan->language);
885 d = ast_waitstream(chan, AST_DIGIT_ANY);
886 ast_stopstream(chan);
890 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int beep)
894 int x, fmtcnt=1, res=-1,outmsg=0;
896 struct ast_filestream *others[MAX_OTHER_FORMATS];
897 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
898 char *sfmt[MAX_OTHER_FORMATS];
901 struct ast_dsp *sildet; /* silence detector dsp */
902 int totalsilence = 0;
904 int gotsilence = 0; /* did we timeout for silence? */
906 char prependfile[80];
908 ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
909 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
911 if (playfile || beep) {
913 d = play_and_wait(chan, playfile);
915 d = ast_streamfile(chan, "beep",chan->language);
917 d = ast_waitstream(chan,"");
921 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
922 strcat(prependfile, "-prepend");
924 fmts = ast_strdupa(fmt);
927 strsep(&stringp, "|");
928 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
929 sfmt[0] = ast_strdupa(fmts);
931 while((fmt = strsep(&stringp, "|"))) {
932 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
933 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
936 sfmt[fmtcnt++] = ast_strdupa(fmt);
941 for (x=0;x<fmtcnt;x++) {
942 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
943 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
949 sildet = ast_dsp_new(); //Create the silence detector
951 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
954 ast_dsp_set_threshold(sildet, silencethreshold);
956 if (maxsilence > 0) {
957 rfmt = chan->readformat;
958 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
960 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
966 /* Loop forever, writing the packets we read to the writer(s), until
967 we read a # or get a hangup */
970 res = ast_waitfor(chan, 2000);
972 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
973 /* Try one more time in case of masq */
974 res = ast_waitfor(chan, 2000);
976 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
988 if (f->frametype == AST_FRAME_VOICE) {
989 /* write each format */
990 for (x=0;x<fmtcnt;x++) {
993 res = ast_writestream(others[x], f);
996 /* Silence Detection */
997 if (maxsilence > 0) {
999 ast_dsp_silence(sildet, f, &dspsilence);
1001 totalsilence = dspsilence;
1005 if (totalsilence > maxsilence) {
1006 /* Ended happily with silence */
1013 /* Exit on any error */
1015 ast_log(LOG_WARNING, "Error writing frame\n");
1019 } else if (f->frametype == AST_FRAME_VIDEO) {
1020 /* Write only once */
1021 ast_writestream(others[0], f);
1022 } else if (f->frametype == AST_FRAME_DTMF) {
1023 /* stop recording with any digit */
1024 if (option_verbose > 2)
1025 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1033 if (maxtime < (end - start)) {
1034 if (option_verbose > 2)
1035 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1044 if (option_verbose > 2)
1045 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1049 /* delete all the prepend files */
1050 for (x=0;x<fmtcnt;x++) {
1053 ast_closestream(others[x]);
1054 ast_filedelete(prependfile, sfmt[x]);
1059 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1066 struct ast_frame *fr;
1067 for (x=0;x<fmtcnt;x++) {
1068 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1069 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1070 if (!others[x] || !realfiles[x])
1073 ast_stream_rewind(others[x], totalsilence-200);
1075 ast_stream_rewind(others[x], 200);
1076 ast_truncstream(others[x]);
1077 /* add the original file too */
1078 while ((fr = ast_readframe(realfiles[x]))) {
1079 ast_writestream(others[x],fr);
1081 ast_closestream(others[x]);
1082 ast_closestream(realfiles[x]);
1083 ast_filerename(prependfile, recordfile, sfmt[x]);
1085 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1087 ast_filedelete(prependfile, sfmt[x]);
1091 if (ast_set_read_format(chan, rfmt)) {
1092 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1097 /* Let them know it worked */
1098 ast_streamfile(chan, "vm-msgsaved", chan->language);
1099 ast_waitstream(chan, "");
1105 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
1109 int x, fmtcnt=1, res=-1,outmsg=0;
1110 struct ast_frame *f;
1111 struct ast_filestream *others[MAX_OTHER_FORMATS];
1112 char *sfmt[MAX_OTHER_FORMATS];
1115 struct ast_dsp *sildet; /* silence detector dsp */
1116 int totalsilence = 0;
1118 int gotsilence = 0; /* did we timeout for silence? */
1121 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1122 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1125 d = play_and_wait(chan, playfile);
1127 d = ast_streamfile(chan, "beep",chan->language);
1129 d = ast_waitstream(chan,"");
1134 fmts = ast_strdupa(fmt);
1137 strsep(&stringp, "|");
1138 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1139 sfmt[0] = ast_strdupa(fmts);
1141 while((fmt = strsep(&stringp, "|"))) {
1142 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1143 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1146 sfmt[fmtcnt++] = ast_strdupa(fmt);
1151 for (x=0;x<fmtcnt;x++) {
1152 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1153 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1160 sildet = ast_dsp_new(); //Create the silence detector
1162 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1165 ast_dsp_set_threshold(sildet, silencethreshold);
1167 if (maxsilence > 0) {
1168 rfmt = chan->readformat;
1169 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1171 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1177 /* Loop forever, writing the packets we read to the writer(s), until
1178 we read a # or get a hangup */
1181 res = ast_waitfor(chan, 2000);
1183 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1184 /* Try one more time in case of masq */
1185 res = ast_waitfor(chan, 2000);
1187 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1199 if (f->frametype == AST_FRAME_VOICE) {
1200 /* write each format */
1201 for (x=0;x<fmtcnt;x++) {
1202 res = ast_writestream(others[x], f);
1205 /* Silence Detection */
1206 if (maxsilence > 0) {
1208 ast_dsp_silence(sildet, f, &dspsilence);
1210 totalsilence = dspsilence;
1214 if (totalsilence > maxsilence) {
1215 /* Ended happily with silence */
1222 /* Exit on any error */
1224 ast_log(LOG_WARNING, "Error writing frame\n");
1228 } else if (f->frametype == AST_FRAME_VIDEO) {
1229 /* Write only once */
1230 ast_writestream(others[0], f);
1231 } else if (f->frametype == AST_FRAME_DTMF) {
1232 if (f->subclass == '#') {
1233 if (option_verbose > 2)
1234 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1243 if (maxtime < (end - start)) {
1244 if (option_verbose > 2)
1245 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1254 if (option_verbose > 2)
1255 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1260 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1263 for (x=0;x<fmtcnt;x++) {
1267 ast_stream_rewind(others[x], totalsilence-200);
1269 ast_stream_rewind(others[x], 200);
1270 ast_truncstream(others[x]);
1271 ast_closestream(others[x]);
1274 if (ast_set_read_format(chan, rfmt)) {
1275 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1280 /* Let them know it worked */
1281 ast_streamfile(chan, "vm-msgsaved", chan->language);
1282 ast_waitstream(chan, "");
1290 static void free_user(struct ast_vm_user *vmu)
1296 static void free_zone(struct vm_zone *z)
1301 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1311 char prefile[256]="";
1319 struct ast_vm_user *vmu;
1320 struct ast_vm_user svm;
1322 strncpy(tmp, ext, sizeof(tmp) - 1);
1324 context = strchr(tmp, '@');
1330 if ((vmu = find_user(&svm, context, ext))) {
1331 /* Setup pre-file if appropriate */
1333 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1335 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1336 make_dir(dir, sizeof(dir), vmu->context, "", "");
1337 /* It's easier just to try to make it than to check for its existence */
1338 if (mkdir(dir, 0700) && (errno != EEXIST))
1339 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1340 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1341 /* It's easier just to try to make it than to check for its existence */
1342 if (mkdir(dir, 0700) && (errno != EEXIST))
1343 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1344 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1345 if (mkdir(dir, 0700) && (errno != EEXIST))
1346 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1347 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1349 /* Play the beginning intro if desired */
1350 if (strlen(prefile)) {
1351 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1352 if (ast_streamfile(chan, prefile, chan->language) > -1)
1353 res = ast_waitstream(chan, "#0");
1355 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1356 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1359 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1365 /* On a '#' we skip the instructions */
1369 if (!res && !silent) {
1370 res = ast_streamfile(chan, INTRO, chan->language);
1372 res = ast_waitstream(chan, ecodes);
1378 /* Check for a '0' here */
1380 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1381 if (strlen(chan->macrocontext))
1382 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1388 /* Unless we're *really* silent, try to send the beep */
1389 res = ast_streamfile(chan, "beep", chan->language);
1391 res = ast_waitstream(chan, "");
1397 /* The meat of recording the message... All the announcements and beeps have been played*/
1398 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1402 make_file(fn, sizeof(fn), dir, msgnum);
1403 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1404 (chan->callerid ? chan->callerid : "Unknown"),
1405 vmu->fullname, ext, chan->name);
1406 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1409 } while(msgnum < MAXMSG);
1410 if (msgnum < MAXMSG) {
1411 /* Store information */
1412 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1413 txt = fopen(txtfile, "w+");
1415 get_date(date, sizeof(date));
1419 "; Message Information file\n"
1435 chan->callerid ? chan->callerid : "Unknown",
1436 date, (long)time(NULL));
1439 ast_log(LOG_WARNING, "Error opening text file for output\n");
1440 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1443 txt = fopen(txtfile, "a");
1446 fprintf(txt, "duration=%ld\n", (long)(end-start));
1450 strsep(&stringp, "|");
1451 /* Send e-mail if applicable */
1452 if (strlen(vmu->email)) {
1453 int attach_user_voicemail = attach_voicemail;
1454 char *myserveremail = serveremail;
1455 if (vmu->attach > -1)
1456 attach_user_voicemail = vmu->attach;
1457 if (strlen(vmu->serveremail))
1458 myserveremail = vmu->serveremail;
1459 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1461 if (strlen(vmu->pager)) {
1462 char *myserveremail = serveremail;
1463 if (strlen(vmu->serveremail))
1464 myserveremail = vmu->serveremail;
1465 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1468 ast_log(LOG_WARNING, "No more messages possible\n");
1470 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1473 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1474 /*Send the call to n+101 priority, where n is the current priority*/
1475 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1476 chan->priority+=100;
1478 /* Leave voicemail for someone */
1479 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1483 static char *mbox(int id)
1511 static int count_messages(char *dir)
1515 for (x=0;x<MAXMSG;x++) {
1516 make_file(fn, sizeof(fn), dir, x);
1517 if (ast_fileexists(fn, NULL, NULL) < 1)
1523 static int say_and_wait(struct ast_channel *chan, int num)
1526 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1530 static int copy(char *infile, char *outfile)
1537 if ((ifd = open(infile, O_RDONLY)) < 0) {
1538 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1541 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1542 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1547 len = read(ifd, buf, sizeof(buf));
1549 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1555 res = write(ofd, buf, len);
1557 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1569 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1576 char *dbox = mbox(box);
1578 make_file(sfn, sizeof(sfn), dir, msg);
1579 make_dir(ddir, sizeof(ddir), context, username, dbox);
1581 for (x=0;x<MAXMSG;x++) {
1582 make_file(dfn, sizeof(dfn), ddir, x);
1583 if (ast_fileexists(dfn, NULL, NULL) < 0)
1588 ast_filecopy(sfn, dfn, NULL);
1589 if (strcmp(sfn, dfn)) {
1590 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1591 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1597 static int adsi_logo(unsigned char *buf)
1600 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1601 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1605 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1613 bytes += adsi_data_mode(buf + bytes);
1614 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1617 bytes += adsi_logo(buf);
1618 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1620 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1622 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1623 bytes += adsi_data_mode(buf + bytes);
1624 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1626 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1628 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1629 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1630 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1631 bytes += adsi_voice_mode(buf + bytes, 0);
1632 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1639 bytes += adsi_logo(buf);
1640 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1641 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1642 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1643 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1646 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1647 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1648 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1649 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1650 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1651 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1652 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1655 /* Add another dot */
1657 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1658 bytes += adsi_voice_mode(buf + bytes, 0);
1660 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1661 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1665 /* These buttons we load but don't use yet */
1666 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1667 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1668 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1669 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1670 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1671 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1672 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1675 /* Add another dot */
1677 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1678 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1679 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1684 snprintf(num, sizeof(num), "%d", x);
1685 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1687 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1688 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1691 /* Add another dot */
1693 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1694 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1695 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1698 if (adsi_end_download(chan)) {
1700 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1701 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1702 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1703 bytes += adsi_voice_mode(buf + bytes, 0);
1704 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1708 bytes += adsi_download_disconnect(buf + bytes);
1709 bytes += adsi_voice_mode(buf + bytes, 0);
1710 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1712 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1717 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1718 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1720 ast_log(LOG_DEBUG, "Restarting session...\n");
1723 /* Load the session now */
1724 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1726 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1728 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1730 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1734 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1737 if (!adsi_available(chan))
1739 x = adsi_load_session(chan, adapp, adver, 1);
1743 if (adsi_load_vmail(chan, useadsi)) {
1744 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1751 static void adsi_login(struct ast_channel *chan)
1755 unsigned char keys[8];
1757 if (!adsi_available(chan))
1762 /* Set one key for next */
1763 keys[3] = ADSI_KEY_APPS + 3;
1765 bytes += adsi_logo(buf + bytes);
1766 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1767 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1768 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1769 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1770 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1771 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1772 bytes += adsi_set_keys(buf + bytes, keys);
1773 bytes += adsi_voice_mode(buf + bytes, 0);
1774 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1777 static void adsi_password(struct ast_channel *chan)
1781 unsigned char keys[8];
1783 if (!adsi_available(chan))
1788 /* Set one key for next */
1789 keys[3] = ADSI_KEY_APPS + 3;
1791 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1792 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1793 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1794 bytes += adsi_set_keys(buf + bytes, keys);
1795 bytes += adsi_voice_mode(buf + bytes, 0);
1796 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1799 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1803 unsigned char keys[8];
1806 if (!adsi_available(chan))
1810 y = ADSI_KEY_APPS + 12 + start + x;
1811 if (y > ADSI_KEY_APPS + 12 + 4)
1813 keys[x] = ADSI_KEY_SKT | y;
1815 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1819 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1820 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1821 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1822 bytes += adsi_set_keys(buf + bytes, keys);
1823 bytes += adsi_voice_mode(buf + bytes, 0);
1825 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1828 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1831 char buf[256], buf1[256], buf2[256];
1837 char datetime[21]="";
1840 unsigned char keys[8];
1844 if (!adsi_available(chan))
1847 /* Retrieve important info */
1848 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1849 f = fopen(fn2, "r");
1852 fgets(buf, sizeof(buf), f);
1856 strsep(&stringp, "=");
1857 val = strsep(&stringp, "=");
1858 if (val && strlen(val)) {
1859 if (!strcmp(buf, "callerid"))
1860 strncpy(cid, val, sizeof(cid) - 1);
1861 if (!strcmp(buf, "origdate"))
1862 strncpy(datetime, val, sizeof(datetime) - 1);
1868 /* New meaning for keys */
1870 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1875 /* No prev key, provide "Folder" instead */
1876 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1879 /* If last message ... */
1881 /* but not only message, provide "Folder" instead */
1882 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1883 bytes += adsi_voice_mode(buf + bytes, 0);
1886 /* Otherwise if only message, leave blank */
1892 ast_callerid_parse(cid, &name, &num);
1896 name = "Unknown Caller";
1898 /* If deleted, show "undeleted" */
1901 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1904 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1905 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1906 strcasecmp(folder, "INBOX") ? " Messages" : "");
1907 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1909 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1910 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1911 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1912 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1913 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1914 bytes += adsi_set_keys(buf + bytes, keys);
1915 bytes += adsi_voice_mode(buf + bytes, 0);
1917 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1920 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1924 unsigned char keys[8];
1928 if (!adsi_available(chan))
1931 /* New meaning for keys */
1933 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1939 /* No prev key, provide "Folder" instead */
1940 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1943 /* If last message ... */
1945 /* but not only message, provide "Folder" instead */
1946 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1948 /* Otherwise if only message, leave blank */
1953 /* If deleted, show "undeleted" */
1955 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1958 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1959 bytes += adsi_set_keys(buf + bytes, keys);
1960 bytes += adsi_voice_mode(buf + bytes, 0);
1962 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1965 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1967 char buf[256], buf1[256], buf2[256];
1969 unsigned char keys[8];
1972 char *newm = (new == 1) ? "message" : "messages";
1973 char *oldm = (old == 1) ? "message" : "messages";
1974 if (!adsi_available(chan))
1977 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1979 strcat(buf1, " and");
1980 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1982 snprintf(buf2, sizeof(buf2), "%s.", newm);
1985 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1986 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1988 strcpy(buf1, "You have no messages.");
1991 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1992 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1993 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1996 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2000 /* Don't let them listen if there are none */
2003 bytes += adsi_set_keys(buf + bytes, keys);
2005 bytes += adsi_voice_mode(buf + bytes, 0);
2007 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2010 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2012 char buf[256], buf1[256], buf2[256];
2014 unsigned char keys[8];
2017 char *mess = (messages == 1) ? "message" : "messages";
2019 if (!adsi_available(chan))
2022 /* Original command keys */
2024 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2032 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2033 strcasecmp(folder, "INBOX") ? " folder" : "");
2036 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2038 strcpy(buf2, "no messages.");
2039 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2040 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2041 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2042 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2043 bytes += adsi_set_keys(buf + bytes, keys);
2045 bytes += adsi_voice_mode(buf + bytes, 0);
2047 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2051 static void adsi_clear(struct ast_channel *chan)
2055 if (!adsi_available(chan))
2057 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2058 bytes += adsi_voice_mode(buf + bytes, 0);
2060 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2063 static void adsi_goodbye(struct ast_channel *chan)
2068 if (!adsi_available(chan))
2070 bytes += adsi_logo(buf + bytes);
2071 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2072 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2073 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2074 bytes += adsi_voice_mode(buf + bytes, 0);
2076 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2079 static int get_folder(struct ast_channel *chan, int start)
2084 d = play_and_wait(chan, "vm-press");
2087 for (x = start; x< 5; x++) {
2088 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
2090 d = play_and_wait(chan, "vm-for");
2093 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2094 d = play_and_wait(chan, fn);
2097 d = play_and_wait(chan, "vm-messages");
2100 d = ast_waitfordigit(chan, 500);
2104 d = play_and_wait(chan, "vm-tocancel");
2107 d = ast_waitfordigit(chan, 4000);
2111 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2114 res = play_and_wait(chan, fn);
2115 while (((res < '0') || (res > '9')) &&
2116 (res != '#') && (res >= 0)) {
2117 res = get_folder(chan, 0);
2122 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2127 while((cmd >= 0) && (cmd != 't') && (cmd != '#')) {
2132 /* prepend a message to the current message and return */
2135 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2136 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, 1);
2146 cmd = play_and_wait(chan,"vm-forwardoptions");
2148 cmd = ast_waitfordigit(chan,6000);
2160 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2167 struct ast_config *mif;
2171 int res = 0, cmd = 0;
2172 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2175 int saved_messages = 0, found = 0;
2176 int valid_extensions = 0;
2177 while (!res && !valid_extensions) {
2178 res = ast_streamfile(chan, "vm-extension", chan->language);
2181 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2183 /* start all over if no username */
2184 if (!strlen(username))
2187 s = strsep(&stringp, "*");
2188 /* start optimistic */
2189 valid_extensions = 1;
2191 /* find_user is going to malloc since we have a NULL as first argument */
2192 if ((receiver = find_user(NULL, context, s))) {
2194 vmtmp = extensions = receiver;
2196 vmtmp->next = receiver;
2201 valid_extensions = 0;
2204 s = strsep(&stringp, "*");
2206 /* break from the loop of reading the extensions */
2207 if (valid_extensions)
2209 /* invalid extension, try again */
2210 res = play_and_wait(chan, "pbx-invalid");
2212 /* check if we're clear to proceed */
2213 if (!extensions || !valid_extensions)
2216 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2218 while(!res && vmtmp) {
2219 /* if (play_and_wait(chan, "vm-savedto"))
2222 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2223 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2224 ast_log(LOG_DEBUG, sys);
2227 todircount = count_messages(todir);
2228 strncpy(tmp, fmt, sizeof(tmp));
2230 while((s = strsep(&stringp, "|"))) {
2231 /* XXX This is a hack -- we should use build_filename or similar XXX */
2232 if (!strcasecmp(s, "wav49"))
2234 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2235 ast_log(LOG_DEBUG, sys);
2238 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2239 ast_log(LOG_DEBUG, sys);
2241 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2243 /* load the information on the source message so we can send an e-mail like a new message */
2244 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2245 if ((mif=ast_load(miffile))) {
2247 /* set callerid and duration variables */
2248 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2249 s = ast_variable_retrieve(mif, NULL, "duration");
2254 if (strlen(vmtmp->email)) {
2255 int attach_user_voicemail = attach_voicemail;
2256 char *myserveremail = serveremail;
2257 if (vmtmp->attach > -1)
2258 attach_user_voicemail = vmtmp->attach;
2259 if (strlen(vmtmp->serveremail))
2260 myserveremail = vmtmp->serveremail;
2261 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2264 if (strlen(vmtmp->pager)) {
2265 char *myserveremail = serveremail;
2266 if (strlen(vmtmp->serveremail))
2267 myserveremail = vmtmp->serveremail;
2268 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2271 ast_destroy(mif); /* or here */
2273 /* Leave voicemail for someone */
2274 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2278 vmtmp = vmtmp->next;
2281 if (saved_messages > 0) {
2282 /* give confirmatopm that the message was saved */
2283 if (saved_messages == 1)
2284 res = play_and_wait(chan, "vm-message");
2286 res = play_and_wait(chan, "vm-messages");
2288 res = play_and_wait(chan, "vm-saved");
2290 return res ? res : cmd;
2294 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2297 if ((res = ast_streamfile(chan, file, chan->language)))
2298 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2300 res = ast_waitstream(chan, AST_DIGIT_ANY);
2304 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2307 if ((res = ast_streamfile(chan, file, chan->language)))
2308 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2310 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2314 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2317 char filename[256], *origtime;
2318 struct vm_zone *the_zone = NULL;
2319 struct ast_config *msg_cfg;
2323 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2324 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2325 msg_cfg = ast_load(filename);
2327 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2331 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2333 if (sscanf(origtime,"%ld",&tin) < 1) {
2334 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2338 ast_destroy(msg_cfg);
2340 /* Does this user have a timezone specified? */
2341 if (strlen(vmu->zonetag)) {
2342 /* Find the zone in the list */
2346 if (!strcmp(z->name, vmu->zonetag)) {
2354 /* No internal variable parsing for now, so we'll comment it out for the time being */
2356 /* Set the DIFF_* variables */
2357 localtime_r(&t, &time_now);
2358 gettimeofday(&tv_now,NULL);
2359 tnow = tv_now.tv_sec;
2360 localtime_r(&tnow,&time_then);
2362 /* Day difference */
2363 if (time_now.tm_year == time_then.tm_year)
2364 sprintf(temp,"%d",time_now.tm_yday);
2366 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2367 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2369 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2372 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2374 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2376 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2381 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2385 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2386 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2388 res = wait_file2(chan, vms, "vm-first");
2389 else if (msg == vms->lastmsg)
2390 res = wait_file2(chan, vms, "vm-last");
2392 res = wait_file2(chan, vms, "vm-message");
2393 if (msg && (msg != vms->lastmsg)) {
2395 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2400 res = play_message_datetime(chan,vmu,vms);
2403 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2404 vms->heard[msg] = 1;
2405 res = wait_file(chan, vms, vms->fn);
2410 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2412 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2413 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2414 vms->lastmsg = count_messages(vms->curdir) - 1;
2415 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2418 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2421 char ntxt[256] = "";
2423 if (vms->lastmsg > -1) {
2424 /* Get the deleted messages fixed */
2426 for (x=0;x < MAXMSG;x++) {
2427 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2428 /* Save this message. It's not in INBOX or hasn't been heard */
2429 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2430 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2433 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2434 if (strcmp(vms->fn, vms->fn2)) {
2435 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2436 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2437 ast_filerename(vms->fn, vms->fn2, NULL);
2440 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2441 /* Move to old folder before deleting */
2442 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2445 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2446 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2447 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2449 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2450 ast_filedelete(vms->fn, NULL);
2454 memset(vms->deleted, 0, sizeof(vms->deleted));
2455 memset(vms->heard, 0, sizeof(vms->heard));
2458 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2460 /* Introduce messages they have */
2462 res = play_and_wait(chan, "vm-youhave");
2464 if (vms->newmessages) {
2465 res = say_and_wait(chan, vms->newmessages);
2467 res = play_and_wait(chan, "vm-INBOX");
2468 if (vms->oldmessages && !res)
2469 res = play_and_wait(chan, "vm-and");
2471 if ((vms->newmessages == 1))
2472 res = play_and_wait(chan, "vm-message");
2474 res = play_and_wait(chan, "vm-messages");
2478 if (!res && vms->oldmessages) {
2479 res = say_and_wait(chan, vms->oldmessages);
2481 res = play_and_wait(chan, "vm-Old");
2483 if (vms->oldmessages == 1)
2484 res = play_and_wait(chan, "vm-message");
2486 res = play_and_wait(chan, "vm-messages");
2490 if (!vms->oldmessages && !vms->newmessages) {
2491 res = play_and_wait(chan, "vm-no");
2493 res = play_and_wait(chan, "vm-messages");
2500 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2503 /* Play instructions and wait for new command */
2505 if (vms->starting) {
2506 if (vms->lastmsg > -1) {
2507 res = play_and_wait(chan, "vm-onefor");
2509 res = play_and_wait(chan, vms->vmbox);
2511 res = play_and_wait(chan, "vm-messages");
2514 res = play_and_wait(chan, "vm-opts");
2517 res = play_and_wait(chan, "vm-prev");
2519 res = play_and_wait(chan, "vm-repeat");
2520 if (!res && (vms->curmsg != vms->lastmsg))
2521 res = play_and_wait(chan, "vm-next");
2523 if (!vms->deleted[vms->curmsg])
2524 res = play_and_wait(chan, "vm-delete");
2526 res = play_and_wait(chan, "vm-undelete");
2528 res = play_and_wait(chan, "vm-toforward");
2530 res = play_and_wait(chan, "vm-savemessage");
2534 res = play_and_wait(chan, "vm-helpexit");
2536 res = ast_waitfordigit(chan, 6000);
2539 if (vms->repeats > 2) {
2540 res = play_and_wait(chan, "vm-goodbye");
2549 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2553 char newpassword[80] = "";
2554 char newpassword2[80] = "";
2555 char prefile[256]="";
2559 if (adsi_available(chan))
2561 bytes += adsi_logo(buf + bytes);
2562 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2563 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2564 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2565 bytes += adsi_voice_mode(buf + bytes, 0);
2566 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2568 while((cmd >= 0) && (cmd != 't')) {
2573 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2574 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2577 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2578 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2581 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2582 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2585 newpassword[1] = '\0';
2586 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2589 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2592 newpassword2[1] = '\0';
2593 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2597 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2600 if (strcmp(newpassword, newpassword2)) {
2601 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2602 cmd = play_and_wait(chan, "vm-mismatch");
2605 vm_change_password(vmu,newpassword);
2606 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2607 cmd = play_and_wait(chan,"vm-passchanged");
2613 cmd = play_and_wait(chan,"vm-options");
2615 cmd = ast_waitfordigit(chan,6000);
2627 static int vm_execmain(struct ast_channel *chan, void *data)
2629 /* XXX This is, admittedly, some pretty horrendus code. For some
2630 reason it just seemed a lot easier to do with GOTO's. I feel
2631 like I'm back in my GWBASIC days. XXX */
2636 struct localuser *u;
2637 char prefixstr[80] ="";
2638 char empty[80] = "";
2642 char tmp[256], *ext;
2643 char fmtc[256] = "";
2645 struct vm_state vms;
2647 struct ast_vm_user *vmu = NULL, vmus;
2651 memset(&vms, 0, sizeof(vms));
2652 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2653 if (chan->_state != AST_STATE_UP)
2656 if (data && strlen(data)) {
2657 strncpy(tmp, data, sizeof(tmp) - 1);
2662 /* We should skip the user's password */
2667 /* We should prefix the mailbox with the supplied data */
2673 context = strchr(ext, '@');
2680 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2682 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2683 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2690 /* If ADSI is supported, setup login screen */
2691 adsi_begin(chan, &useadsi);
2692 if (!skipuser && useadsi)
2694 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2695 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2699 /* Authenticate them and get their mailbox/password */
2701 while (!valid && (logretries < maxlogins)) {
2702 /* Prompt for, and read in the username */
2703 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2704 ast_log(LOG_WARNING, "Couldn't read username\n");
2707 if (!strlen(vms.username)) {
2708 if (option_verbose > 2)
2709 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2714 adsi_password(chan);
2715 if (ast_streamfile(chan, "vm-password", chan->language)) {
2716 ast_log(LOG_WARNING, "Unable to stream password file\n");
2719 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2720 ast_log(LOG_WARNING, "Unable to read password\n");
2724 char fullusername[80] = "";
2725 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2726 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2727 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2730 vmu = find_user(&vmus, context, vms.username);
2731 if (vmu && !strcmp(vmu->password, password))
2734 if (option_verbose > 2)
2735 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2737 strncpy(vms.username, empty, sizeof(vms.username) -1);
2742 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2747 if (!valid && (logretries >= maxlogins)) {
2748 ast_stopstream(chan);
2749 res = play_and_wait(chan, "vm-goodbye");
2755 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2756 mkdir(vms.curdir, 0700);
2757 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2758 mkdir(vms.curdir, 0700);
2759 /* Retrieve old and new message counts */
2760 open_mailbox(&vms, vmu, 1);
2761 vms.oldmessages = vms.lastmsg + 1;
2762 /* Start in INBOX */
2763 open_mailbox(&vms, vmu, 0);
2764 vms.newmessages = vms.lastmsg + 1;
2767 /* Select proper mailbox FIRST!! */
2768 if (!vms.newmessages && vms.oldmessages) {
2769 /* If we only have old messages start here */
2770 open_mailbox(&vms, vmu, 1);
2774 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2776 cmd = vm_intro(chan, &vms);
2779 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2786 if (vms.lastmsg > -1) {
2787 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2789 cmd = play_and_wait(chan, "vm-youhave");
2791 cmd = play_and_wait(chan, "vm-no");
2793 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2794 cmd = play_and_wait(chan, vms.fn);
2797 cmd = play_and_wait(chan, "vm-messages");
2800 case '2': /* Change folders */
2802 adsi_folders(chan, 0, "Change to folder...");
2803 cmd = get_folder2(chan, "vm-changeto", 0);
2806 } else if (cmd > 0) {
2808 close_mailbox(&vms, vmu);
2809 open_mailbox(&vms, vmu, cmd);
2813 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2815 cmd = play_and_wait(chan, vms.vmbox);
2817 cmd = play_and_wait(chan, "vm-messages");
2823 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2825 cmd = play_and_wait(chan, "vm-nomore");
2829 if (vms.curmsg < vms.lastmsg) {
2831 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2833 cmd = play_and_wait(chan, "vm-nomore");
2837 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2839 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2840 if (vms.deleted[vms.curmsg])
2841 cmd = play_and_wait(chan, "vm-deleted");
2843 cmd = play_and_wait(chan, "vm-undeleted");
2846 if(vms.lastmsg > -1)
2847 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2849 cmd = play_and_wait(chan, "vm-nomore");
2853 adsi_folders(chan, 1, "Save to folder...");
2854 cmd = get_folder2(chan, "vm-savefolder", 1);
2855 box = 0; /* Shut up compiler */
2859 } else if (cmd > 0) {
2860 box = cmd = cmd - '0';
2861 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2862 vms.deleted[vms.curmsg]=1;
2864 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2866 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2868 cmd = play_and_wait(chan, "vm-message");
2870 cmd = say_and_wait(chan, vms.curmsg + 1);
2872 cmd = play_and_wait(chan, "vm-savedto");
2874 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2875 cmd = play_and_wait(chan, vms.fn);
2878 cmd = play_and_wait(chan, "vm-messages");
2881 if (!vms.starting) {
2882 cmd = play_and_wait(chan, "vm-onefor");
2884 cmd = play_and_wait(chan, vms.vmbox);
2886 cmd = play_and_wait(chan, "vm-messages");
2888 cmd = play_and_wait(chan, "vm-opts");
2893 cmd = vm_options(chan, vmu, &vms, vmfmts);
2895 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2897 default: /* Nothing */
2898 cmd = vm_instructions(chan, &vms);
2902 if ((cmd == 't') || (cmd == '#')) {
2912 ast_stopstream(chan);
2915 res = play_and_wait(chan, "vm-goodbye");
2920 adsi_unload_session(chan);
2923 close_mailbox(&vms, vmu);
2927 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2929 LOCAL_USER_REMOVE(u);
2934 static int vm_exec(struct ast_channel *chan, void *data)
2936 int res=0, silent=0, busy=0, unavail=0;
2937 struct localuser *u;
2938 char tmp[256], *ext;
2941 if (chan->_state != AST_STATE_UP)
2943 if (data && strlen(data))
2944 strncpy(tmp, data, sizeof(tmp) - 1);
2946 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2957 } else if (*ext == 'b') {
2960 } else if (*ext == 'u') {
2966 res = leave_voicemail(chan, ext, silent, busy, unavail);
2967 LOCAL_USER_REMOVE(u);
2971 static int append_mailbox(char *context, char *mbox, char *data)
2973 /* Assumes lock is already held */
2977 struct ast_vm_user *vmu;
2978 strncpy(tmp, data, sizeof(tmp));
2979 vmu = malloc(sizeof(struct ast_vm_user));
2981 memset(vmu, 0, sizeof(struct ast_vm_user));
2982 strncpy(vmu->context, context, sizeof(vmu->context));
2983 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2986 if ((s = strsep(&stringp, ",")))
2987 strncpy(vmu->password, s, sizeof(vmu->password));
2988 if (stringp && (s = strsep(&stringp, ",")))
2989 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2990 if (stringp && (s = strsep(&stringp, ",")))
2991 strncpy(vmu->email, s, sizeof(vmu->email));
2992 if (stringp && (s = strsep(&stringp, ",")))
2993 strncpy(vmu->pager, s, sizeof(vmu->pager));
2994 if (stringp && (s = strsep(&stringp, ",")))
2995 apply_options(vmu, s);
3006 static int load_config(void)
3008 struct ast_vm_user *cur, *l;
3009 struct vm_zone *zcur, *zl;
3010 struct ast_config *cfg;
3012 struct ast_variable *var;
3021 cfg = ast_load(VOICEMAIL_CONFIG);
3022 ast_mutex_lock(&vmlock);
3040 /* General settings */
3041 attach_voicemail = 1;
3042 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
3044 attach_voicemail = ast_true(astattach);
3046 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
3047 maxsilence = atoi(silencestr);
3052 silencethreshold = 256;
3053 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
3054 silencethreshold = atoi(thresholdstr);
3056 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
3057 astemail = ASTERISK_USERNAME;
3058 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
3061 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
3062 if (sscanf(s, "%d", &x) == 1) {
3065 ast_log(LOG_WARNING, "Invalid max message time length\n");
3068 fmt = ast_variable_retrieve(cfg, "general", "format");
3071 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
3074 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
3075 if (sscanf(s, "%d", &x) == 1) {
3078 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
3082 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
3083 if (sscanf(s, "%d", &x) == 1) {
3086 ast_log(LOG_WARNING, "Invalid skipms value\n");
3091 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
3092 if (sscanf(s, "%d", &x) == 1) {
3095 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
3100 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
3101 strcpy(dbuser, "test");
3105 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
3106 strcpy(dbpass, "test");
3110 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
3115 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
3116 strcpy(dbname, "vmdb");
3122 #ifdef USEPOSTGRESVM
3123 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
3124 strcpy(dboption, "dboption not-specified in voicemail.conf");
3126 strcpy(dboption, s);
3129 cat = ast_category_browse(cfg, NULL);
3131 if (strcasecmp(cat, "general")) {
3132 var = ast_variable_browse(cfg, cat);
3133 if (strcasecmp(cat, "zonemessages")) {
3135 /* Process mailboxes in this context */
3137 append_mailbox(cat, var->name, var->value);
3142 /* Timezones in this context */
3145 z = malloc(sizeof(struct vm_zone));
3147 char *msg_format, *timezone;
3148 msg_format = ast_strdupa(var->value);
3149 if (msg_format != NULL) {
3150 timezone = strsep(&msg_format, "|");
3151 strncpy(z->name, var->name, sizeof(z->name) - 1);
3152 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
3153 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
3163 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3168 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3175 cat = ast_category_browse(cfg, cat);
3177 memset(fromstring,0,sizeof(fromstring));
3178 memset(emailtitle,0,sizeof(emailtitle));
3183 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
3184 pbxskip = ast_true(s);
3185 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
3186 strncpy(fromstring,s,sizeof(fromstring)-1);
3187 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
3188 strncpy(emailtitle,s,sizeof(emailtitle)-1);
3189 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
3190 char *tmpread, *tmpwrite;
3191 emailbody = strdup(s);
3193 /* substitute strings \t and \n into the apropriate characters */
3194 tmpread = tmpwrite = emailbody;
3195 while ((tmpwrite = strchr(tmpread,'\\'))) {
3196 int len = strlen("\n");
3197 switch (tmpwrite[1]) {
3199 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3200 strncpy(tmpwrite,"\n",len);
3203 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3204 strncpy(tmpwrite,"\t",len);
3207 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
3209 tmpread = tmpwrite+len;
3213 ast_mutex_unlock(&vmlock);
3216 ast_mutex_unlock(&vmlock);
3217 ast_log(LOG_WARNING, "Error reading voicemail config\n");
3224 return(load_config());
3227 int unload_module(void)
3230 STANDARD_HANGUP_LOCALUSERS;
3231 res = ast_unregister_application(app);
3232 res |= ast_unregister_application(capp);
3233 res |= ast_unregister_application(app2);
3234 res |= ast_unregister_application(capp2);
3239 int load_module(void)
3242 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
3243 res |= ast_register_application(capp, vm_exec, synopsis_vm, descrip_vm);
3244 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
3245 res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain);
3249 if ((res=load_config())) {
3253 if ((res = sql_init())) {
3254 ast_log(LOG_WARNING, "SQL init\n");
3260 char *description(void)
3268 STANDARD_USECOUNT(res);
3274 return ASTERISK_GPL_KEY;