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");
291 if (*retval->context) {
292 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", context, mailbox);
294 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='default' AND mailbox='%s'", mailbox);
296 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
297 ast_mutex_lock(&postgreslock);
298 PGSQLres=PQexec(dbhandler,query);
299 if (PGSQLres!=NULL) {
300 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
301 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
302 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
304 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
307 ast_mutex_unlock(&postgreslock);
311 numFields = PQnfields(PGSQLres);
312 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
313 if (PQntuples(PGSQLres) != 1) {
314 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
316 ast_mutex_unlock(&postgreslock);
320 for (i=0; i<numFields; i++) {
321 fname = PQfname(PGSQLres,i);
322 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
323 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
324 } else if (!strcmp(fname, "fullname")) {
325 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
326 } else if (!strcmp(fname, "email")) {
327 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
328 } else if (!strcmp(fname, "pager")) {
329 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
330 } else if (!strcmp(fname, "options")) {
331 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
332 apply_options(retval, options);
337 ast_mutex_unlock(&postgreslock);
341 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
342 ast_mutex_unlock(&postgreslock);
347 } /* malloc() retval */
352 static void vm_change_password(struct ast_vm_user *vmu, char *password)
357 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);
359 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
361 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
362 ast_mutex_lock(&postgreslock);
363 PQexec(dbhandler, query);
364 strcpy(vmu->password, password);
365 ast_mutex_unlock(&postgreslock);
368 static void reset_user_pw(char *context, char *mailbox, char *password)
373 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
375 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
377 ast_mutex_lock(&postgreslock);
378 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
379 PQexec(dbhandler, query);
380 ast_mutex_unlock(&postgreslock);
383 #endif /* Postgres */
386 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
388 /* This function could be made to generate one from a database, too */
389 struct ast_vm_user *vmu=NULL, *cur;
390 ast_mutex_lock(&vmlock);
393 if ((!context || !strcasecmp(context, cur->context)) &&
394 (!strcasecmp(mailbox, cur->mailbox)))
402 /* Make a copy, so that on a reload, we have no race */
403 vmu = malloc(sizeof(struct ast_vm_user));
405 memcpy(vmu, cur, sizeof(struct ast_vm_user));
413 ast_mutex_unlock(&vmlock);
417 static int reset_user_pw(char *context, char *mailbox, char *newpass)
419 /* This function could be made to generate one from a database, too */
420 struct ast_vm_user *cur;
422 ast_mutex_lock(&vmlock);
425 if ((!context || !strcasecmp(context, cur->context)) &&
426 (!strcasecmp(mailbox, cur->mailbox)))
431 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
434 ast_mutex_unlock(&vmlock);
438 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
440 /* There's probably a better way of doing this. */
441 /* That's why I've put the password change in a separate function. */
442 /* This could also be done with a database function */
448 char tmpin[AST_CONFIG_MAX_PATH];
449 char tmpout[AST_CONFIG_MAX_PATH];
450 char *user, *pass, *rest, *trim;
451 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
452 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
453 configin = fopen((char *)tmpin,"r");
455 configout = fopen((char *)tmpout,"w+");
458 if(!configin || !configout) {
462 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
466 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
470 while (!feof(configin)) {
471 /* Read in the line */
472 fgets(inbuf, sizeof(inbuf), configin);
473 if (!feof(configin)) {
474 /* Make a backup of it */
475 memcpy(orig, inbuf, sizeof(orig));
476 /* Strip trailing \n and comment */
477 inbuf[strlen(inbuf) - 1] = '\0';
478 user = strchr(inbuf, ';');
484 pass = strchr(user, '=');
487 while(*trim && *trim < 33) {
497 while(*pass && *pass < 33)
501 rest = strchr(pass,',');
508 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
509 /* This is the line */
511 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
513 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
516 /* Put it back like it was */
517 fprintf(configout, orig);
524 unlink((char *)tmpin);
525 rename((char *)tmpout,(char *)tmpin);
526 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
527 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
531 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
533 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
536 static int make_file(char *dest, int len, char *dir, int num)
538 return snprintf(dest, len, "%s/msg%04d", dir, num);
542 inbuf(struct baseio *bio, FILE *fi)
549 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
564 inchar(struct baseio *bio, FILE *fi)
566 if(bio->iocp>=bio->iolen)
570 return bio->iobuf[bio->iocp++];
574 ochar(struct baseio *bio, int c, FILE *so)
576 if(bio->linelength>=BASELINELEN) {
577 if(fputs(eol,so)==EOF)
583 if(putc(((unsigned char)c),so)==EOF)
591 static int base_encode(char *filename, FILE *so)
593 unsigned char dtable[BASEMAXINLINE];
598 memset(&bio, 0, sizeof(bio));
599 bio.iocp = BASEMAXINLINE;
601 if ( !(fi = fopen(filename, "rb"))) {
602 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
610 dtable[26+i+9]= 'j'+i;
614 dtable[26+i+18]= 's'+i;
623 unsigned char igroup[3],ogroup[4];
626 igroup[0]= igroup[1]= igroup[2]= 0;
629 if ( (c = inchar(&bio, fi)) == EOF) {
634 igroup[n]= (unsigned char)c;
638 ogroup[0]= dtable[igroup[0]>>2];
639 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
640 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
641 ogroup[3]= dtable[igroup[2]&0x3F];
651 ochar(&bio, ogroup[i], so);
655 if(fputs(eol,so)==EOF)
663 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)
674 struct vm_zone *the_zone = NULL;
676 if (!strcmp(format, "wav49"))
678 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
679 p = popen(SENDMAIL, "w");
681 gethostname(host, sizeof(host));
682 if (strchr(srcemail, '@'))
683 strncpy(who, srcemail, sizeof(who)-1);
685 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
687 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
690 /* Does this user have a timezone specified? */
691 if (strlen(vmu->zonetag)) {
692 /* Find the zone in the list */
696 if (!strcmp(z->name, vmu->zonetag)) {
705 ast_localtime(&t,&tm,the_zone->timezone);
707 ast_localtime(&t,&tm,NULL);
708 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
709 fprintf(p, "Date: %s\n", date);
712 fprintf(p, "From: %s <%s>\n", fromstring, who);
714 fprintf(p, "From: Asterisk PBX <%s>\n", who);
715 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
719 fprintf(p, emailtitle, msgnum, mailbox) ;
724 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
726 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
727 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
728 fprintf(p, "MIME-Version: 1.0\n");
729 if (attach_user_voicemail) {
731 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
733 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
735 fprintf(p, "--%s\n", bound);
737 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
738 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
740 struct ast_channel *ast = ast_channel_alloc(0);
743 int vmlen = strlen(emailbody)*3 + 200;
744 if ((passdata = alloca(vmlen))) {
745 memset(passdata, 0, vmlen);
746 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
747 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
748 sprintf(passdata,"%d",msgnum);
749 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
750 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
751 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
752 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
753 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
754 fprintf(p, "%s\n",passdata);
755 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
756 ast_channel_free(ast);
757 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
759 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
761 "in mailbox %s from %s, on %s so you might\n"
762 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
763 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
765 if (attach_user_voicemail) {
766 fprintf(p, "--%s\n", bound);
767 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
768 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
769 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
770 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
772 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
773 base_encode(fname, p);
774 fprintf(p, "\n\n--%s--\n.\n", bound);
778 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
784 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
793 struct vm_zone *the_zone = NULL;
794 p = popen(SENDMAIL, "w");
797 gethostname(host, sizeof(host));
798 if (strchr(srcemail, '@'))
799 strncpy(who, srcemail, sizeof(who)-1);
801 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
803 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
806 /* Does this user have a timezone specified? */
807 if (strlen(vmu->zonetag)) {
808 /* Find the zone in the list */
812 if (!strcmp(z->name, vmu->zonetag)) {
821 ast_localtime(&t,&tm,the_zone->timezone);
823 ast_localtime(&t,&tm,NULL);
825 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
826 fprintf(p, "Date: %s\n", date);
827 fprintf(p, "From: Asterisk PBX <%s>\n", who);
828 fprintf(p, "To: %s\n", pager);
829 fprintf(p, "Subject: New VM\n\n");
830 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
831 fprintf(p, "New %s long msg in box %s\n"
832 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
835 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
841 static int get_date(char *s, int len)
847 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
850 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
854 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
855 if (ast_fileexists(fn, NULL, NULL) > 0) {
856 res = ast_streamfile(chan, fn, chan->language);
859 res = ast_waitstream(chan, ecodes);
863 res = ast_streamfile(chan, "vm-theperson", chan->language);
866 res = ast_waitstream(chan, ecodes);
869 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
874 res = ast_streamfile(chan, "vm-isonphone", chan->language);
876 res = ast_streamfile(chan, "vm-isunavail", chan->language);
879 res = ast_waitstream(chan, ecodes);
883 static int play_and_wait(struct ast_channel *chan, char *fn)
886 d = ast_streamfile(chan, fn, chan->language);
889 d = ast_waitstream(chan, AST_DIGIT_ANY);
890 ast_stopstream(chan);
894 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int beep)
898 int x, fmtcnt=1, res=-1,outmsg=0;
900 struct ast_filestream *others[MAX_OTHER_FORMATS];
901 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
902 char *sfmt[MAX_OTHER_FORMATS];
905 struct ast_dsp *sildet; /* silence detector dsp */
906 int totalsilence = 0;
908 int gotsilence = 0; /* did we timeout for silence? */
910 char prependfile[80];
912 ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
913 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
915 if (playfile || beep) {
917 d = play_and_wait(chan, playfile);
919 d = ast_streamfile(chan, "beep",chan->language);
921 d = ast_waitstream(chan,"");
925 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
926 strcat(prependfile, "-prepend");
928 fmts = ast_strdupa(fmt);
931 strsep(&stringp, "|");
932 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
933 sfmt[0] = ast_strdupa(fmts);
935 while((fmt = strsep(&stringp, "|"))) {
936 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
937 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
940 sfmt[fmtcnt++] = ast_strdupa(fmt);
945 for (x=0;x<fmtcnt;x++) {
946 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
947 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
953 sildet = ast_dsp_new(); //Create the silence detector
955 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
958 ast_dsp_set_threshold(sildet, silencethreshold);
960 if (maxsilence > 0) {
961 rfmt = chan->readformat;
962 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
964 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
970 /* Loop forever, writing the packets we read to the writer(s), until
971 we read a # or get a hangup */
974 res = ast_waitfor(chan, 2000);
976 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
977 /* Try one more time in case of masq */
978 res = ast_waitfor(chan, 2000);
980 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
992 if (f->frametype == AST_FRAME_VOICE) {
993 /* write each format */
994 for (x=0;x<fmtcnt;x++) {
997 res = ast_writestream(others[x], f);
1000 /* Silence Detection */
1001 if (maxsilence > 0) {
1003 ast_dsp_silence(sildet, f, &dspsilence);
1005 totalsilence = dspsilence;
1009 if (totalsilence > maxsilence) {
1010 /* Ended happily with silence */
1017 /* Exit on any error */
1019 ast_log(LOG_WARNING, "Error writing frame\n");
1023 } else if (f->frametype == AST_FRAME_VIDEO) {
1024 /* Write only once */
1025 ast_writestream(others[0], f);
1026 } else if (f->frametype == AST_FRAME_DTMF) {
1027 /* stop recording with any digit */
1028 if (option_verbose > 2)
1029 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1037 if (maxtime < (end - start)) {
1038 if (option_verbose > 2)
1039 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1048 if (option_verbose > 2)
1049 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1053 /* delete all the prepend files */
1054 for (x=0;x<fmtcnt;x++) {
1057 ast_closestream(others[x]);
1058 ast_filedelete(prependfile, sfmt[x]);
1063 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1070 struct ast_frame *fr;
1071 for (x=0;x<fmtcnt;x++) {
1072 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1073 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1074 if (!others[x] || !realfiles[x])
1077 ast_stream_rewind(others[x], totalsilence-200);
1079 ast_stream_rewind(others[x], 200);
1080 ast_truncstream(others[x]);
1081 /* add the original file too */
1082 while ((fr = ast_readframe(realfiles[x]))) {
1083 ast_writestream(others[x],fr);
1085 ast_closestream(others[x]);
1086 ast_closestream(realfiles[x]);
1087 ast_filerename(prependfile, recordfile, sfmt[x]);
1089 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1091 ast_filedelete(prependfile, sfmt[x]);
1095 if (ast_set_read_format(chan, rfmt)) {
1096 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1101 /* Let them know it worked */
1102 ast_streamfile(chan, "vm-msgsaved", chan->language);
1103 ast_waitstream(chan, "");
1109 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
1113 int x, fmtcnt=1, res=-1,outmsg=0;
1114 struct ast_frame *f;
1115 struct ast_filestream *others[MAX_OTHER_FORMATS];
1116 char *sfmt[MAX_OTHER_FORMATS];
1119 struct ast_dsp *sildet; /* silence detector dsp */
1120 int totalsilence = 0;
1122 int gotsilence = 0; /* did we timeout for silence? */
1125 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1126 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1129 d = play_and_wait(chan, playfile);
1131 d = ast_streamfile(chan, "beep",chan->language);
1133 d = ast_waitstream(chan,"");
1138 fmts = ast_strdupa(fmt);
1141 strsep(&stringp, "|");
1142 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1143 sfmt[0] = ast_strdupa(fmts);
1145 while((fmt = strsep(&stringp, "|"))) {
1146 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1147 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1150 sfmt[fmtcnt++] = ast_strdupa(fmt);
1155 for (x=0;x<fmtcnt;x++) {
1156 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1157 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1164 sildet = ast_dsp_new(); //Create the silence detector
1166 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1169 ast_dsp_set_threshold(sildet, silencethreshold);
1171 if (maxsilence > 0) {
1172 rfmt = chan->readformat;
1173 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1175 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1181 /* Loop forever, writing the packets we read to the writer(s), until
1182 we read a # or get a hangup */
1185 res = ast_waitfor(chan, 2000);
1187 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1188 /* Try one more time in case of masq */
1189 res = ast_waitfor(chan, 2000);
1191 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1203 if (f->frametype == AST_FRAME_VOICE) {
1204 /* write each format */
1205 for (x=0;x<fmtcnt;x++) {
1206 res = ast_writestream(others[x], f);
1209 /* Silence Detection */
1210 if (maxsilence > 0) {
1212 ast_dsp_silence(sildet, f, &dspsilence);
1214 totalsilence = dspsilence;
1218 if (totalsilence > maxsilence) {
1219 /* Ended happily with silence */
1226 /* Exit on any error */
1228 ast_log(LOG_WARNING, "Error writing frame\n");
1232 } else if (f->frametype == AST_FRAME_VIDEO) {
1233 /* Write only once */
1234 ast_writestream(others[0], f);
1235 } else if (f->frametype == AST_FRAME_DTMF) {
1236 if (f->subclass == '#') {
1237 if (option_verbose > 2)
1238 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1247 if (maxtime < (end - start)) {
1248 if (option_verbose > 2)
1249 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1258 if (option_verbose > 2)
1259 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1264 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1267 for (x=0;x<fmtcnt;x++) {
1271 ast_stream_rewind(others[x], totalsilence-200);
1273 ast_stream_rewind(others[x], 200);
1274 ast_truncstream(others[x]);
1275 ast_closestream(others[x]);
1278 if (ast_set_read_format(chan, rfmt)) {
1279 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1284 /* Let them know it worked */
1285 ast_streamfile(chan, "vm-msgsaved", chan->language);
1286 ast_waitstream(chan, "");
1294 static void free_user(struct ast_vm_user *vmu)
1300 static void free_zone(struct vm_zone *z)
1305 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1315 char prefile[256]="";
1323 struct ast_vm_user *vmu;
1324 struct ast_vm_user svm;
1326 strncpy(tmp, ext, sizeof(tmp) - 1);
1328 context = strchr(tmp, '@');
1334 if ((vmu = find_user(&svm, context, ext))) {
1335 /* Setup pre-file if appropriate */
1337 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1339 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1340 make_dir(dir, sizeof(dir), vmu->context, "", "");
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, "");
1345 /* It's easier just to try to make it than to check for its existence */
1346 if (mkdir(dir, 0700) && (errno != EEXIST))
1347 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1348 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1349 if (mkdir(dir, 0700) && (errno != EEXIST))
1350 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1351 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1353 /* Play the beginning intro if desired */
1354 if (strlen(prefile)) {
1355 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1356 if (ast_streamfile(chan, prefile, chan->language) > -1)
1357 res = ast_waitstream(chan, "#0");
1359 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1360 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1363 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1369 /* On a '#' we skip the instructions */
1373 if (!res && !silent) {
1374 res = ast_streamfile(chan, INTRO, chan->language);
1376 res = ast_waitstream(chan, ecodes);
1382 /* Check for a '0' here */
1384 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1385 if (strlen(chan->macrocontext))
1386 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1392 /* Unless we're *really* silent, try to send the beep */
1393 res = ast_streamfile(chan, "beep", chan->language);
1395 res = ast_waitstream(chan, "");
1401 /* The meat of recording the message... All the announcements and beeps have been played*/
1402 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1406 make_file(fn, sizeof(fn), dir, msgnum);
1407 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1408 (chan->callerid ? chan->callerid : "Unknown"),
1409 vmu->fullname, ext, chan->name);
1410 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1413 } while(msgnum < MAXMSG);
1414 if (msgnum < MAXMSG) {
1415 /* Store information */
1416 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1417 txt = fopen(txtfile, "w+");
1419 get_date(date, sizeof(date));
1423 "; Message Information file\n"
1439 chan->callerid ? chan->callerid : "Unknown",
1440 date, (long)time(NULL));
1443 ast_log(LOG_WARNING, "Error opening text file for output\n");
1444 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1447 txt = fopen(txtfile, "a");
1450 fprintf(txt, "duration=%ld\n", (long)(end-start));
1454 strsep(&stringp, "|");
1455 /* Send e-mail if applicable */
1456 if (strlen(vmu->email)) {
1457 int attach_user_voicemail = attach_voicemail;
1458 char *myserveremail = serveremail;
1459 if (vmu->attach > -1)
1460 attach_user_voicemail = vmu->attach;
1461 if (strlen(vmu->serveremail))
1462 myserveremail = vmu->serveremail;
1463 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1465 if (strlen(vmu->pager)) {
1466 char *myserveremail = serveremail;
1467 if (strlen(vmu->serveremail))
1468 myserveremail = vmu->serveremail;
1469 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1472 ast_log(LOG_WARNING, "No more messages possible\n");
1474 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1477 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1478 /*Send the call to n+101 priority, where n is the current priority*/
1479 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1480 chan->priority+=100;
1482 /* Leave voicemail for someone */
1483 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1487 static char *mbox(int id)
1515 static int count_messages(char *dir)
1519 for (x=0;x<MAXMSG;x++) {
1520 make_file(fn, sizeof(fn), dir, x);
1521 if (ast_fileexists(fn, NULL, NULL) < 1)
1527 static int say_and_wait(struct ast_channel *chan, int num)
1530 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1534 static int copy(char *infile, char *outfile)
1541 if ((ifd = open(infile, O_RDONLY)) < 0) {
1542 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1545 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1546 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1551 len = read(ifd, buf, sizeof(buf));
1553 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1559 res = write(ofd, buf, len);
1561 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1573 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1580 char *dbox = mbox(box);
1582 make_file(sfn, sizeof(sfn), dir, msg);
1583 make_dir(ddir, sizeof(ddir), context, username, dbox);
1585 for (x=0;x<MAXMSG;x++) {
1586 make_file(dfn, sizeof(dfn), ddir, x);
1587 if (ast_fileexists(dfn, NULL, NULL) < 0)
1592 ast_filecopy(sfn, dfn, NULL);
1593 if (strcmp(sfn, dfn)) {
1594 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1595 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1601 static int adsi_logo(unsigned char *buf)
1604 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1605 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1609 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1617 bytes += adsi_data_mode(buf + bytes);
1618 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1621 bytes += adsi_logo(buf);
1622 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1624 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1626 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1627 bytes += adsi_data_mode(buf + bytes);
1628 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1630 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1632 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1633 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1634 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1635 bytes += adsi_voice_mode(buf + bytes, 0);
1636 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1643 bytes += adsi_logo(buf);
1644 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1645 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1646 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1647 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1650 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1651 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1652 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1653 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1654 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1655 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1656 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1659 /* Add another dot */
1661 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1662 bytes += adsi_voice_mode(buf + bytes, 0);
1664 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1665 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1669 /* These buttons we load but don't use yet */
1670 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1671 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1672 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1673 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1674 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1675 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1676 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1679 /* Add another dot */
1681 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1682 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1683 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1688 snprintf(num, sizeof(num), "%d", x);
1689 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1691 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1692 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1695 /* Add another dot */
1697 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1698 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1699 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1702 if (adsi_end_download(chan)) {
1704 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1705 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1706 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1707 bytes += adsi_voice_mode(buf + bytes, 0);
1708 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1712 bytes += adsi_download_disconnect(buf + bytes);
1713 bytes += adsi_voice_mode(buf + bytes, 0);
1714 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1716 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1721 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1722 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1724 ast_log(LOG_DEBUG, "Restarting session...\n");
1727 /* Load the session now */
1728 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1730 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1732 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1734 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1738 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1741 if (!adsi_available(chan))
1743 x = adsi_load_session(chan, adapp, adver, 1);
1747 if (adsi_load_vmail(chan, useadsi)) {
1748 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1755 static void adsi_login(struct ast_channel *chan)
1759 unsigned char keys[8];
1761 if (!adsi_available(chan))
1766 /* Set one key for next */
1767 keys[3] = ADSI_KEY_APPS + 3;
1769 bytes += adsi_logo(buf + bytes);
1770 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1771 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1772 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1773 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1774 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1775 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1776 bytes += adsi_set_keys(buf + bytes, keys);
1777 bytes += adsi_voice_mode(buf + bytes, 0);
1778 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1781 static void adsi_password(struct ast_channel *chan)
1785 unsigned char keys[8];
1787 if (!adsi_available(chan))
1792 /* Set one key for next */
1793 keys[3] = ADSI_KEY_APPS + 3;
1795 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1796 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1797 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1798 bytes += adsi_set_keys(buf + bytes, keys);
1799 bytes += adsi_voice_mode(buf + bytes, 0);
1800 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1803 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1807 unsigned char keys[8];
1810 if (!adsi_available(chan))
1814 y = ADSI_KEY_APPS + 12 + start + x;
1815 if (y > ADSI_KEY_APPS + 12 + 4)
1817 keys[x] = ADSI_KEY_SKT | y;
1819 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1823 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1824 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1825 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1826 bytes += adsi_set_keys(buf + bytes, keys);
1827 bytes += adsi_voice_mode(buf + bytes, 0);
1829 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1832 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1835 char buf[256], buf1[256], buf2[256];
1841 char datetime[21]="";
1844 unsigned char keys[8];
1848 if (!adsi_available(chan))
1851 /* Retrieve important info */
1852 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1853 f = fopen(fn2, "r");
1856 fgets(buf, sizeof(buf), f);
1860 strsep(&stringp, "=");
1861 val = strsep(&stringp, "=");
1862 if (val && strlen(val)) {
1863 if (!strcmp(buf, "callerid"))
1864 strncpy(cid, val, sizeof(cid) - 1);
1865 if (!strcmp(buf, "origdate"))
1866 strncpy(datetime, val, sizeof(datetime) - 1);
1872 /* New meaning for keys */
1874 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1879 /* No prev key, provide "Folder" instead */
1880 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1883 /* If last message ... */
1885 /* but not only message, provide "Folder" instead */
1886 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1887 bytes += adsi_voice_mode(buf + bytes, 0);
1890 /* Otherwise if only message, leave blank */
1896 ast_callerid_parse(cid, &name, &num);
1900 name = "Unknown Caller";
1902 /* If deleted, show "undeleted" */
1905 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1908 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1909 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1910 strcasecmp(folder, "INBOX") ? " Messages" : "");
1911 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1913 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1914 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1915 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1916 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1917 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1918 bytes += adsi_set_keys(buf + bytes, keys);
1919 bytes += adsi_voice_mode(buf + bytes, 0);
1921 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1924 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1928 unsigned char keys[8];
1932 if (!adsi_available(chan))
1935 /* New meaning for keys */
1937 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1943 /* No prev key, provide "Folder" instead */
1944 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1947 /* If last message ... */
1949 /* but not only message, provide "Folder" instead */
1950 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1952 /* Otherwise if only message, leave blank */
1957 /* If deleted, show "undeleted" */
1959 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1962 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1963 bytes += adsi_set_keys(buf + bytes, keys);
1964 bytes += adsi_voice_mode(buf + bytes, 0);
1966 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1969 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1971 char buf[256], buf1[256], buf2[256];
1973 unsigned char keys[8];
1976 char *newm = (new == 1) ? "message" : "messages";
1977 char *oldm = (old == 1) ? "message" : "messages";
1978 if (!adsi_available(chan))
1981 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1983 strcat(buf1, " and");
1984 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1986 snprintf(buf2, sizeof(buf2), "%s.", newm);
1989 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1990 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1992 strcpy(buf1, "You have no messages.");
1995 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1996 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1997 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2000 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2004 /* Don't let them listen if there are none */
2007 bytes += adsi_set_keys(buf + bytes, keys);
2009 bytes += adsi_voice_mode(buf + bytes, 0);
2011 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2014 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2016 char buf[256], buf1[256], buf2[256];
2018 unsigned char keys[8];
2021 char *mess = (messages == 1) ? "message" : "messages";
2023 if (!adsi_available(chan))
2026 /* Original command keys */
2028 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2036 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2037 strcasecmp(folder, "INBOX") ? " folder" : "");
2040 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2042 strcpy(buf2, "no messages.");
2043 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2044 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2045 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2046 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2047 bytes += adsi_set_keys(buf + bytes, keys);
2049 bytes += adsi_voice_mode(buf + bytes, 0);
2051 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2055 static void adsi_clear(struct ast_channel *chan)
2059 if (!adsi_available(chan))
2061 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2062 bytes += adsi_voice_mode(buf + bytes, 0);
2064 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2067 static void adsi_goodbye(struct ast_channel *chan)
2072 if (!adsi_available(chan))
2074 bytes += adsi_logo(buf + bytes);
2075 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2076 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2077 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2078 bytes += adsi_voice_mode(buf + bytes, 0);
2080 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2083 static int get_folder(struct ast_channel *chan, int start)
2088 d = play_and_wait(chan, "vm-press");
2091 for (x = start; x< 5; x++) {
2092 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
2094 d = play_and_wait(chan, "vm-for");
2097 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2098 d = play_and_wait(chan, fn);
2101 d = play_and_wait(chan, "vm-messages");
2104 d = ast_waitfordigit(chan, 500);
2108 d = play_and_wait(chan, "vm-tocancel");
2111 d = ast_waitfordigit(chan, 4000);
2115 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2118 res = play_and_wait(chan, fn);
2119 while (((res < '0') || (res > '9')) &&
2120 (res != '#') && (res >= 0)) {
2121 res = get_folder(chan, 0);
2126 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2131 while((cmd >= 0) && (cmd != 't') && (cmd != '#')) {
2136 /* prepend a message to the current message and return */
2139 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2140 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, 1);
2150 cmd = play_and_wait(chan,"vm-forwardoptions");
2152 cmd = ast_waitfordigit(chan,6000);
2164 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2171 struct ast_config *mif;
2175 int res = 0, cmd = 0;
2176 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2179 int saved_messages = 0, found = 0;
2180 int valid_extensions = 0;
2181 while (!res && !valid_extensions) {
2182 res = ast_streamfile(chan, "vm-extension", chan->language);
2185 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2187 /* start all over if no username */
2188 if (!strlen(username))
2191 s = strsep(&stringp, "*");
2192 /* start optimistic */
2193 valid_extensions = 1;
2195 /* find_user is going to malloc since we have a NULL as first argument */
2196 if ((receiver = find_user(NULL, context, s))) {
2198 vmtmp = extensions = receiver;
2200 vmtmp->next = receiver;
2205 valid_extensions = 0;
2208 s = strsep(&stringp, "*");
2210 /* break from the loop of reading the extensions */
2211 if (valid_extensions)
2213 /* invalid extension, try again */
2214 res = play_and_wait(chan, "pbx-invalid");
2216 /* check if we're clear to proceed */
2217 if (!extensions || !valid_extensions)
2220 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2222 while(!res && vmtmp) {
2223 /* if (play_and_wait(chan, "vm-savedto"))
2226 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2227 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2228 ast_log(LOG_DEBUG, sys);
2231 todircount = count_messages(todir);
2232 strncpy(tmp, fmt, sizeof(tmp));
2234 while((s = strsep(&stringp, "|"))) {
2235 /* XXX This is a hack -- we should use build_filename or similar XXX */
2236 if (!strcasecmp(s, "wav49"))
2238 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2239 ast_log(LOG_DEBUG, sys);
2242 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2243 ast_log(LOG_DEBUG, sys);
2245 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2247 /* load the information on the source message so we can send an e-mail like a new message */
2248 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2249 if ((mif=ast_load(miffile))) {
2251 /* set callerid and duration variables */
2252 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2253 s = ast_variable_retrieve(mif, NULL, "duration");
2258 if (strlen(vmtmp->email)) {
2259 int attach_user_voicemail = attach_voicemail;
2260 char *myserveremail = serveremail;
2261 if (vmtmp->attach > -1)
2262 attach_user_voicemail = vmtmp->attach;
2263 if (strlen(vmtmp->serveremail))
2264 myserveremail = vmtmp->serveremail;
2265 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2268 if (strlen(vmtmp->pager)) {
2269 char *myserveremail = serveremail;
2270 if (strlen(vmtmp->serveremail))
2271 myserveremail = vmtmp->serveremail;
2272 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2275 ast_destroy(mif); /* or here */
2277 /* Leave voicemail for someone */
2278 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2282 vmtmp = vmtmp->next;
2285 if (saved_messages > 0) {
2286 /* give confirmatopm that the message was saved */
2287 if (saved_messages == 1)
2288 res = play_and_wait(chan, "vm-message");
2290 res = play_and_wait(chan, "vm-messages");
2292 res = play_and_wait(chan, "vm-saved");
2294 return res ? res : cmd;
2298 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2301 if ((res = ast_streamfile(chan, file, chan->language)))
2302 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2304 res = ast_waitstream(chan, AST_DIGIT_ANY);
2308 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2311 if ((res = ast_streamfile(chan, file, chan->language)))
2312 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2314 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2318 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2321 char filename[256], *origtime;
2322 struct vm_zone *the_zone = NULL;
2323 struct ast_config *msg_cfg;
2327 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2328 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2329 msg_cfg = ast_load(filename);
2331 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2335 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2337 if (sscanf(origtime,"%ld",&tin) < 1) {
2338 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2342 ast_destroy(msg_cfg);
2344 /* Does this user have a timezone specified? */
2345 if (strlen(vmu->zonetag)) {
2346 /* Find the zone in the list */
2350 if (!strcmp(z->name, vmu->zonetag)) {
2358 /* No internal variable parsing for now, so we'll comment it out for the time being */
2360 /* Set the DIFF_* variables */
2361 localtime_r(&t, &time_now);
2362 gettimeofday(&tv_now,NULL);
2363 tnow = tv_now.tv_sec;
2364 localtime_r(&tnow,&time_then);
2366 /* Day difference */
2367 if (time_now.tm_year == time_then.tm_year)
2368 sprintf(temp,"%d",time_now.tm_yday);
2370 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2371 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2373 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2376 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2378 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2380 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2385 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2389 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2390 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2392 res = wait_file2(chan, vms, "vm-first");
2393 else if (msg == vms->lastmsg)
2394 res = wait_file2(chan, vms, "vm-last");
2396 res = wait_file2(chan, vms, "vm-message");
2397 if (msg && (msg != vms->lastmsg)) {
2399 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2404 res = play_message_datetime(chan,vmu,vms);
2407 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2408 vms->heard[msg] = 1;
2409 res = wait_file(chan, vms, vms->fn);
2414 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2416 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2417 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2418 vms->lastmsg = count_messages(vms->curdir) - 1;
2419 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2422 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2425 char ntxt[256] = "";
2427 if (vms->lastmsg > -1) {
2428 /* Get the deleted messages fixed */
2430 for (x=0;x < MAXMSG;x++) {
2431 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2432 /* Save this message. It's not in INBOX or hasn't been heard */
2433 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2434 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2437 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2438 if (strcmp(vms->fn, vms->fn2)) {
2439 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2440 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2441 ast_filerename(vms->fn, vms->fn2, NULL);
2444 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2445 /* Move to old folder before deleting */
2446 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2449 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2450 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2451 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2453 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2454 ast_filedelete(vms->fn, NULL);
2458 memset(vms->deleted, 0, sizeof(vms->deleted));
2459 memset(vms->heard, 0, sizeof(vms->heard));
2462 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2464 /* Introduce messages they have */
2466 res = play_and_wait(chan, "vm-youhave");
2468 if (vms->newmessages) {
2469 res = say_and_wait(chan, vms->newmessages);
2471 res = play_and_wait(chan, "vm-INBOX");
2472 if (vms->oldmessages && !res)
2473 res = play_and_wait(chan, "vm-and");
2475 if ((vms->newmessages == 1))
2476 res = play_and_wait(chan, "vm-message");
2478 res = play_and_wait(chan, "vm-messages");
2482 if (!res && vms->oldmessages) {
2483 res = say_and_wait(chan, vms->oldmessages);
2485 res = play_and_wait(chan, "vm-Old");
2487 if (vms->oldmessages == 1)
2488 res = play_and_wait(chan, "vm-message");
2490 res = play_and_wait(chan, "vm-messages");
2494 if (!vms->oldmessages && !vms->newmessages) {
2495 res = play_and_wait(chan, "vm-no");
2497 res = play_and_wait(chan, "vm-messages");
2504 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2507 /* Play instructions and wait for new command */
2509 if (vms->starting) {
2510 if (vms->lastmsg > -1) {
2511 res = play_and_wait(chan, "vm-onefor");
2513 res = play_and_wait(chan, vms->vmbox);
2515 res = play_and_wait(chan, "vm-messages");
2518 res = play_and_wait(chan, "vm-opts");
2521 res = play_and_wait(chan, "vm-prev");
2523 res = play_and_wait(chan, "vm-repeat");
2524 if (!res && (vms->curmsg != vms->lastmsg))
2525 res = play_and_wait(chan, "vm-next");
2527 if (!vms->deleted[vms->curmsg])
2528 res = play_and_wait(chan, "vm-delete");
2530 res = play_and_wait(chan, "vm-undelete");
2532 res = play_and_wait(chan, "vm-toforward");
2534 res = play_and_wait(chan, "vm-savemessage");
2538 res = play_and_wait(chan, "vm-helpexit");
2540 res = ast_waitfordigit(chan, 6000);
2543 if (vms->repeats > 2) {
2544 res = play_and_wait(chan, "vm-goodbye");
2553 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2557 char newpassword[80] = "";
2558 char newpassword2[80] = "";
2559 char prefile[256]="";
2563 if (adsi_available(chan))
2565 bytes += adsi_logo(buf + bytes);
2566 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2567 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2568 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2569 bytes += adsi_voice_mode(buf + bytes, 0);
2570 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2572 while((cmd >= 0) && (cmd != 't')) {
2577 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2578 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2581 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2582 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2585 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2586 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2589 newpassword[1] = '\0';
2590 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2593 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2596 newpassword2[1] = '\0';
2597 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2601 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2604 if (strcmp(newpassword, newpassword2)) {
2605 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2606 cmd = play_and_wait(chan, "vm-mismatch");
2609 vm_change_password(vmu,newpassword);
2610 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2611 cmd = play_and_wait(chan,"vm-passchanged");
2617 cmd = play_and_wait(chan,"vm-options");
2619 cmd = ast_waitfordigit(chan,6000);
2631 static int vm_execmain(struct ast_channel *chan, void *data)
2633 /* XXX This is, admittedly, some pretty horrendus code. For some
2634 reason it just seemed a lot easier to do with GOTO's. I feel
2635 like I'm back in my GWBASIC days. XXX */
2640 struct localuser *u;
2641 char prefixstr[80] ="";
2642 char empty[80] = "";
2646 char tmp[256], *ext;
2647 char fmtc[256] = "";
2649 struct vm_state vms;
2651 struct ast_vm_user *vmu = NULL, vmus;
2655 memset(&vms, 0, sizeof(vms));
2656 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2657 if (chan->_state != AST_STATE_UP)
2660 if (data && strlen(data)) {
2661 strncpy(tmp, data, sizeof(tmp) - 1);
2666 /* We should skip the user's password */
2671 /* We should prefix the mailbox with the supplied data */
2677 context = strchr(ext, '@');
2684 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2686 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2687 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2694 /* If ADSI is supported, setup login screen */
2695 adsi_begin(chan, &useadsi);
2696 if (!skipuser && useadsi)
2698 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2699 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2703 /* Authenticate them and get their mailbox/password */
2705 while (!valid && (logretries < maxlogins)) {
2706 /* Prompt for, and read in the username */
2707 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2708 ast_log(LOG_WARNING, "Couldn't read username\n");
2711 if (!strlen(vms.username)) {
2712 if (option_verbose > 2)
2713 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2718 adsi_password(chan);
2719 if (ast_streamfile(chan, "vm-password", chan->language)) {
2720 ast_log(LOG_WARNING, "Unable to stream password file\n");
2723 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2724 ast_log(LOG_WARNING, "Unable to read password\n");
2728 char fullusername[80] = "";
2729 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2730 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2731 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2734 vmu = find_user(&vmus, context, vms.username);
2735 if (vmu && !strcmp(vmu->password, password))
2738 if (option_verbose > 2)
2739 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2741 strncpy(vms.username, empty, sizeof(vms.username) -1);
2746 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2751 if (!valid && (logretries >= maxlogins)) {
2752 ast_stopstream(chan);
2753 res = play_and_wait(chan, "vm-goodbye");
2759 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2760 mkdir(vms.curdir, 0700);
2761 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2762 mkdir(vms.curdir, 0700);
2763 /* Retrieve old and new message counts */
2764 open_mailbox(&vms, vmu, 1);
2765 vms.oldmessages = vms.lastmsg + 1;
2766 /* Start in INBOX */
2767 open_mailbox(&vms, vmu, 0);
2768 vms.newmessages = vms.lastmsg + 1;
2771 /* Select proper mailbox FIRST!! */
2772 if (!vms.newmessages && vms.oldmessages) {
2773 /* If we only have old messages start here */
2774 open_mailbox(&vms, vmu, 1);
2778 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2780 cmd = vm_intro(chan, &vms);
2783 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2790 if (vms.lastmsg > -1) {
2791 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2793 cmd = play_and_wait(chan, "vm-youhave");
2795 cmd = play_and_wait(chan, "vm-no");
2797 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2798 cmd = play_and_wait(chan, vms.fn);
2801 cmd = play_and_wait(chan, "vm-messages");
2804 case '2': /* Change folders */
2806 adsi_folders(chan, 0, "Change to folder...");
2807 cmd = get_folder2(chan, "vm-changeto", 0);
2810 } else if (cmd > 0) {
2812 close_mailbox(&vms, vmu);
2813 open_mailbox(&vms, vmu, cmd);
2817 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2819 cmd = play_and_wait(chan, vms.vmbox);
2821 cmd = play_and_wait(chan, "vm-messages");
2827 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2829 cmd = play_and_wait(chan, "vm-nomore");
2833 if (vms.curmsg < vms.lastmsg) {
2835 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2837 cmd = play_and_wait(chan, "vm-nomore");
2841 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2843 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2844 if (vms.deleted[vms.curmsg])
2845 cmd = play_and_wait(chan, "vm-deleted");
2847 cmd = play_and_wait(chan, "vm-undeleted");
2850 if(vms.lastmsg > -1)
2851 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2853 cmd = play_and_wait(chan, "vm-nomore");
2857 adsi_folders(chan, 1, "Save to folder...");
2858 cmd = get_folder2(chan, "vm-savefolder", 1);
2859 box = 0; /* Shut up compiler */
2863 } else if (cmd > 0) {
2864 box = cmd = cmd - '0';
2865 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2866 vms.deleted[vms.curmsg]=1;
2868 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2870 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2872 cmd = play_and_wait(chan, "vm-message");
2874 cmd = say_and_wait(chan, vms.curmsg + 1);
2876 cmd = play_and_wait(chan, "vm-savedto");
2878 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2879 cmd = play_and_wait(chan, vms.fn);
2882 cmd = play_and_wait(chan, "vm-messages");
2885 if (!vms.starting) {
2886 cmd = play_and_wait(chan, "vm-onefor");
2888 cmd = play_and_wait(chan, vms.vmbox);
2890 cmd = play_and_wait(chan, "vm-messages");
2892 cmd = play_and_wait(chan, "vm-opts");
2897 cmd = vm_options(chan, vmu, &vms, vmfmts);
2899 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2901 default: /* Nothing */
2902 cmd = vm_instructions(chan, &vms);
2906 if ((cmd == 't') || (cmd == '#')) {
2916 ast_stopstream(chan);
2919 res = play_and_wait(chan, "vm-goodbye");
2924 adsi_unload_session(chan);
2927 close_mailbox(&vms, vmu);
2931 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2933 LOCAL_USER_REMOVE(u);
2938 static int vm_exec(struct ast_channel *chan, void *data)
2940 int res=0, silent=0, busy=0, unavail=0;
2941 struct localuser *u;
2942 char tmp[256], *ext;
2945 if (chan->_state != AST_STATE_UP)
2947 if (data && strlen(data))
2948 strncpy(tmp, data, sizeof(tmp) - 1);
2950 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2961 } else if (*ext == 'b') {
2964 } else if (*ext == 'u') {
2970 res = leave_voicemail(chan, ext, silent, busy, unavail);
2971 LOCAL_USER_REMOVE(u);
2975 static int append_mailbox(char *context, char *mbox, char *data)
2977 /* Assumes lock is already held */
2981 struct ast_vm_user *vmu;
2982 strncpy(tmp, data, sizeof(tmp));
2983 vmu = malloc(sizeof(struct ast_vm_user));
2985 memset(vmu, 0, sizeof(struct ast_vm_user));
2986 strncpy(vmu->context, context, sizeof(vmu->context));
2987 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2990 if ((s = strsep(&stringp, ",")))
2991 strncpy(vmu->password, s, sizeof(vmu->password));
2992 if (stringp && (s = strsep(&stringp, ",")))
2993 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2994 if (stringp && (s = strsep(&stringp, ",")))
2995 strncpy(vmu->email, s, sizeof(vmu->email));
2996 if (stringp && (s = strsep(&stringp, ",")))
2997 strncpy(vmu->pager, s, sizeof(vmu->pager));
2998 if (stringp && (s = strsep(&stringp, ",")))
2999 apply_options(vmu, s);
3010 static int load_config(void)
3012 struct ast_vm_user *cur, *l;
3013 struct vm_zone *zcur, *zl;
3014 struct ast_config *cfg;
3016 struct ast_variable *var;
3025 cfg = ast_load(VOICEMAIL_CONFIG);
3026 ast_mutex_lock(&vmlock);
3044 /* General settings */
3045 attach_voicemail = 1;
3046 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
3048 attach_voicemail = ast_true(astattach);
3050 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
3051 maxsilence = atoi(silencestr);
3056 silencethreshold = 256;
3057 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
3058 silencethreshold = atoi(thresholdstr);
3060 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
3061 astemail = ASTERISK_USERNAME;
3062 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
3065 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
3066 if (sscanf(s, "%d", &x) == 1) {
3069 ast_log(LOG_WARNING, "Invalid max message time length\n");
3072 fmt = ast_variable_retrieve(cfg, "general", "format");
3075 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
3078 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
3079 if (sscanf(s, "%d", &x) == 1) {
3082 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
3086 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
3087 if (sscanf(s, "%d", &x) == 1) {
3090 ast_log(LOG_WARNING, "Invalid skipms value\n");
3095 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
3096 if (sscanf(s, "%d", &x) == 1) {
3099 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
3104 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
3105 strcpy(dbuser, "test");
3109 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
3110 strcpy(dbpass, "test");
3114 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
3119 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
3120 strcpy(dbname, "vmdb");
3126 #ifdef USEPOSTGRESVM
3127 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
3128 strcpy(dboption, "dboption not-specified in voicemail.conf");
3130 strcpy(dboption, s);
3133 cat = ast_category_browse(cfg, NULL);
3135 if (strcasecmp(cat, "general")) {
3136 var = ast_variable_browse(cfg, cat);
3137 if (strcasecmp(cat, "zonemessages")) {
3139 /* Process mailboxes in this context */
3141 append_mailbox(cat, var->name, var->value);
3146 /* Timezones in this context */
3149 z = malloc(sizeof(struct vm_zone));
3151 char *msg_format, *timezone;
3152 msg_format = ast_strdupa(var->value);
3153 if (msg_format != NULL) {
3154 timezone = strsep(&msg_format, "|");
3155 strncpy(z->name, var->name, sizeof(z->name) - 1);
3156 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
3157 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
3167 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3172 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3179 cat = ast_category_browse(cfg, cat);
3181 memset(fromstring,0,sizeof(fromstring));
3182 memset(emailtitle,0,sizeof(emailtitle));
3187 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
3188 pbxskip = ast_true(s);
3189 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
3190 strncpy(fromstring,s,sizeof(fromstring)-1);
3191 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
3192 strncpy(emailtitle,s,sizeof(emailtitle)-1);
3193 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
3194 char *tmpread, *tmpwrite;
3195 emailbody = strdup(s);
3197 /* substitute strings \t and \n into the apropriate characters */
3198 tmpread = tmpwrite = emailbody;
3199 while ((tmpwrite = strchr(tmpread,'\\'))) {
3200 int len = strlen("\n");
3201 switch (tmpwrite[1]) {
3203 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3204 strncpy(tmpwrite,"\n",len);
3207 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3208 strncpy(tmpwrite,"\t",len);
3211 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
3213 tmpread = tmpwrite+len;
3217 ast_mutex_unlock(&vmlock);
3220 ast_mutex_unlock(&vmlock);
3221 ast_log(LOG_WARNING, "Error reading voicemail config\n");
3228 return(load_config());
3231 int unload_module(void)
3234 STANDARD_HANGUP_LOCALUSERS;
3235 res = ast_unregister_application(app);
3236 res |= ast_unregister_application(capp);
3237 res |= ast_unregister_application(app2);
3238 res |= ast_unregister_application(capp2);
3243 int load_module(void)
3246 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
3247 res |= ast_register_application(capp, vm_exec, synopsis_vm, descrip_vm);
3248 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
3249 res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain);
3253 if ((res=load_config())) {
3257 if ((res = sql_init())) {
3258 ast_log(LOG_WARNING, "SQL init\n");
3264 char *description(void)
3272 STANDARD_USECOUNT(res);
3278 return ASTERISK_GPL_KEY;