2 * Asterisk -- A telephony toolkit for Linux.
4 * Voicemail System (did you ever think it could be so easy?)
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
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/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/config.h>
21 #include <asterisk/say.h>
22 #include <asterisk/module.h>
23 #include <asterisk/adsi.h>
24 #include <asterisk/app.h>
25 #include <asterisk/manager.h>
26 #include <asterisk/dsp.h>
38 #include "../asterisk.h"
39 #include "../astconf.h"
41 #define COMMAND_TIMEOUT 5000
43 #define VOICEMAIL_CONFIG "voicemail.conf"
44 #define ASTERISK_USERNAME "asterisk"
46 #define SENDMAIL "/usr/sbin/sendmail -t"
48 #define INTRO "vm-intro"
52 #define MAX_OTHER_FORMATS 10
54 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
56 #define BASEMAXINLINE 256
58 #define BASELINELEN 72
64 #define BASEMAXINLINE 256
65 #define BASELINELEN 72
73 unsigned char iobuf[BASEMAXINLINE];
84 struct ast_vm_user *next;
87 static char *tdesc = "Comedian Mail (Voicemail System)";
89 static char *adapp = "CoMa";
91 static char *adsec = "_AST";
93 static char *addesc = "Comedian Mail";
97 static char *synopsis_vm =
98 "Leave a voicemail message";
100 static char *descrip_vm =
101 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given extension (must\n"
102 "be configured in voicemail.conf). If the extension is preceeded by an 's'"
103 "then instructions for leaving the message will be skipped. If the extension\n"
104 "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
105 "/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists. If the extension\n"
106 "is preceeded by a 'b' then the the busy message will be played (that is,\n"
107 "busy instead of unavail). At most one of 's', 'u', or 'b' may be specified.\n"
108 "Returns -1 on error or mailbox not found, or if the user hangs up. \n"
109 "Otherwise, it returns 0. \n";
111 static char *synopsis_vmain =
112 "Enter voicemail system";
114 static char *descrip_vmain =
115 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system for the checking of\n"
116 "voicemail. The mailbox can be passed as the option, which will stop the\n"
117 "voicemail system from prompting the user for the mailbox. If the mailbox\n"
118 "is preceded by 's' then the password check will be skipped. If a context is\n"
119 "specified, logins are considered in that context only. Returns -1 if\n"
120 "the user hangs up or 0 otherwise.\n";
122 /* Leave a message */
123 static char *app = "VoiceMail2";
125 /* Check mail, control, etc */
126 static char *app2 = "VoiceMailMain2";
128 static pthread_mutex_t vmlock = AST_MUTEX_INITIALIZER;
129 struct ast_vm_user *users;
130 struct ast_vm_user *usersl;
131 static int attach_voicemail;
132 static int maxsilence;
133 static int silencethreshold;
134 static char serveremail[80];
135 static char vmfmts[80];
136 static int vmmaxmessage;
139 static int maxlogins;
145 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
147 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
150 static int make_file(char *dest, int len, char *dir, int num)
152 return snprintf(dest, len, "%s/msg%04d", dir, num);
155 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
157 /* This function could be made to generate one from a database, too */
158 struct ast_vm_user *vmu=NULL, *cur;
159 ast_pthread_mutex_lock(&vmlock);
162 if ((!context || !strcasecmp(context, cur->context)) &&
163 (!strcasecmp(mailbox, cur->mailbox)))
171 /* Make a copy, so that on a reload, we have no race */
172 vmu = malloc(sizeof(struct ast_vm_user));
174 memcpy(vmu, cur, sizeof(struct ast_vm_user));
182 ast_pthread_mutex_unlock(&vmlock);
186 static int reset_user_pw(char *context, char *mailbox, char *newpass)
188 /* This function could be made to generate one from a database, too */
189 struct ast_vm_user *cur;
191 ast_pthread_mutex_lock(&vmlock);
194 if ((!context || !strcasecmp(context, cur->context)) &&
195 (!strcasecmp(mailbox, cur->mailbox)))
200 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
203 ast_pthread_mutex_unlock(&vmlock);
207 static int vm_change_password(struct ast_vm_user *vmu, char *newpassword)
209 /* There's probably a better way of doing this. */
210 /* That's why I've put the password change in a separate function. */
211 /* This could also be done with a database function */
217 char tmpin[AST_CONFIG_MAX_PATH];
218 char tmpout[AST_CONFIG_MAX_PATH];
219 char *user, *pass, *rest, *trim;
220 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
221 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
222 configin = fopen((char *)tmpin,"r");
223 configout = fopen((char *)tmpout,"w+");
225 while (!feof(configin)) {
226 /* Read in the line */
227 fgets(inbuf, sizeof(inbuf), configin);
228 if (!feof(configin)) {
229 /* Make a backup of it */
230 memcpy(orig, inbuf, sizeof(orig));
231 /* Strip trailing \n and comment */
232 inbuf[strlen(inbuf) - 1] = '\0';
233 user = strchr(inbuf, ';');
239 pass = strchr(user, '=');
242 while(*trim && *trim < 33) {
252 while(*pass && *pass < 33)
256 rest = strchr(pass,',');
263 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
264 /* This is the line */
266 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
268 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
271 /* Put it back like it was */
272 fprintf(configout, orig);
279 unlink((char *)tmpin);
280 rename((char *)tmpout,(char *)tmpin);
281 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
282 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
287 inbuf(struct baseio *bio, FILE *fi)
294 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
309 inchar(struct baseio *bio, FILE *fi)
311 if(bio->iocp>=bio->iolen)
315 return bio->iobuf[bio->iocp++];
319 ochar(struct baseio *bio, int c, FILE *so)
321 if(bio->linelength>=BASELINELEN) {
322 if(fputs(eol,so)==EOF)
328 if(putc(((unsigned char)c),so)==EOF)
336 static int base_encode(char *filename, FILE *so)
338 unsigned char dtable[BASEMAXINLINE];
343 memset(&bio, 0, sizeof(bio));
344 bio.iocp = BASEMAXINLINE;
346 if ( !(fi = fopen(filename, "rb"))) {
347 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
355 dtable[26+i+9]= 'j'+i;
359 dtable[26+i+18]= 's'+i;
368 unsigned char igroup[3],ogroup[4];
371 igroup[0]= igroup[1]= igroup[2]= 0;
374 if ( (c = inchar(&bio, fi)) == EOF) {
379 igroup[n]= (unsigned char)c;
383 ogroup[0]= dtable[igroup[0]>>2];
384 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
385 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
386 ogroup[3]= dtable[igroup[2]&0x3F];
396 ochar(&bio, ogroup[i], so);
400 if(fputs(eol,so)==EOF)
408 static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration)
419 if (!strcmp(format, "wav49"))
421 p = popen(SENDMAIL, "w");
423 gethostname(host, sizeof(host));
424 if (strchr(srcemail, '@'))
425 strncpy(who, srcemail, sizeof(who)-1);
427 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
429 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
432 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
433 fprintf(p, "Date: %s\n", date);
434 fprintf(p, "From: Asterisk PBX <%s>\n", who);
435 fprintf(p, "To: %s <%s>\n", name, email);
436 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox);
437 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
438 fprintf(p, "MIME-Version: 1.0\n");
439 if (attach_voicemail) {
441 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
443 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
445 fprintf(p, "--%s\n", bound);
447 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
448 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
449 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
451 "in mailbox %s from %s, on %s so you might\n"
452 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", name,
453 dur, msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
454 if (attach_voicemail) {
455 fprintf(p, "--%s\n", bound);
456 fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum, format);
457 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
458 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
459 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
461 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
462 base_encode(fname, p);
463 fprintf(p, "\n\n--%s--\n.\n", bound);
467 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
473 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration)
482 p = popen(SENDMAIL, "w");
485 gethostname(host, sizeof(host));
486 if (strchr(srcemail, '@'))
487 strncpy(who, srcemail, sizeof(who)-1);
489 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
491 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
494 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
495 fprintf(p, "Date: %s\n", date);
496 fprintf(p, "From: Asterisk PBX <%s>\n", who);
497 fprintf(p, "To: %s\n", pager);
498 fprintf(p, "Subject: New VM\n\n");
499 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
500 fprintf(p, "New %s long msg in box %s\n"
501 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
504 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
510 static int get_date(char *s, int len)
516 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
519 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
523 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
524 if (ast_fileexists(fn, NULL, NULL) > 0) {
525 res = ast_streamfile(chan, fn, chan->language);
528 res = ast_waitstream(chan, ecodes);
532 res = ast_streamfile(chan, "vm-theperson", chan->language);
535 res = ast_waitstream(chan, ecodes);
538 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
543 res = ast_streamfile(chan, "vm-isonphone", chan->language);
545 res = ast_streamfile(chan, "vm-isunavail", chan->language);
548 res = ast_waitstream(chan, ecodes);
552 static int play_and_wait(struct ast_channel *chan, char *fn)
555 d = ast_streamfile(chan, fn, chan->language);
558 d = ast_waitstream(chan, AST_DIGIT_ANY);
562 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
566 int x, fmtcnt=1, res=-1,outmsg=0;
568 struct ast_filestream *others[MAX_OTHER_FORMATS];
569 char *sfmt[MAX_OTHER_FORMATS];
574 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
575 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
578 d = play_and_wait(chan, playfile);
580 d = ast_streamfile(chan, "beep",chan->language);
582 d = ast_waitstream(chan,"");
590 strsep(&stringp, "|");
591 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
592 sfmt[0] = strdupa(fmts);
594 while((fmt = strsep(&stringp, "|"))) {
595 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
596 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
599 sfmt[fmtcnt++] = strdupa(fmt);
604 for (x=0;x<fmtcnt;x++) {
605 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
606 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
613 /* Loop forever, writing the packets we read to the writer(s), until
614 we read a # or get a hangup */
617 res = ast_waitfor(chan, 2000);
619 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
620 /* Try one more time in case of masq */
621 res = ast_waitfor(chan, 2000);
623 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
635 if (f->frametype == AST_FRAME_VOICE) {
636 /* write each format */
637 for (x=0;x<fmtcnt;x++) {
638 res = ast_writestream(others[x], f);
640 /* Exit on any error */
642 ast_log(LOG_WARNING, "Error writing frame\n");
646 } else if (f->frametype == AST_FRAME_DTMF) {
647 if (f->subclass == '#') {
648 if (option_verbose > 2)
649 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
658 if (maxtime < (end - start)) {
659 if (option_verbose > 2)
660 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
669 if (option_verbose > 2)
670 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
675 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
678 for (x=0;x<fmtcnt;x++) {
681 ast_closestream(others[x]);
685 /* Let them know it worked */
686 ast_streamfile(chan, "vm-msgsaved", chan->language);
687 ast_waitstream(chan, "");
695 static void free_user(struct ast_vm_user *vmu)
701 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
712 char prefile[256]="";
720 /* XXX Need to be moved to play_and_record */
721 struct ast_dsp *sildet; /* silence detector dsp */
722 int totalsilence = 0;
724 int gotsilence = 0; /* did we timeout for silence? */
728 struct ast_vm_user *vmu;
729 struct ast_vm_user svm;
731 strncpy(tmp, ext, sizeof(tmp) - 1);
733 context = strchr(tmp, '@');
739 if ((vmu = find_user(&svm, context, ext))) {
740 /* Setup pre-file if appropriate */
742 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
744 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
745 make_dir(dir, sizeof(dir), vmu->context, "", "");
746 /* It's easier just to try to make it than to check for its existence */
747 if (mkdir(dir, 0700) && (errno != EEXIST))
748 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
749 make_dir(dir, sizeof(dir), vmu->context, ext, "");
750 /* It's easier just to try to make it than to check for its existence */
751 if (mkdir(dir, 0700) && (errno != EEXIST))
752 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
753 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
754 if (mkdir(dir, 0700) && (errno != EEXIST))
755 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
756 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
758 /* Play the beginning intro if desired */
759 if (strlen(prefile)) {
760 if (ast_fileexists(prefile, NULL, NULL) > 0) {
761 if (ast_streamfile(chan, prefile, chan->language) > -1)
762 res = ast_waitstream(chan, "#0");
764 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
765 res = invent_message(chan, vmu->context, ext, busy, ecodes);
768 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
774 /* On a '#' we skip the instructions */
778 if (!res && !silent) {
779 res = ast_streamfile(chan, INTRO, chan->language);
781 res = ast_waitstream(chan, ecodes);
787 /* Check for a '0' here */
789 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
790 if (strlen(chan->macrocontext))
791 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
797 /* Unless we're *really* silent, try to send the beep */
798 res = ast_streamfile(chan, "beep", chan->language);
800 res = ast_waitstream(chan, "");
806 /* The meat of recording the message... All the announcements and beeps have been played*/
807 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
811 make_file(fn, sizeof(fn), dir, msgnum);
812 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
813 (chan->callerid ? chan->callerid : "Unknown"),
814 vmu->fullname, ext, chan->name);
815 if (ast_fileexists(fn, NULL, chan->language) <= 0)
818 } while(msgnum < MAXMSG);
819 if (msgnum < MAXMSG) {
820 /* Store information */
821 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
822 txt = fopen(txtfile, "w+");
824 get_date(date, sizeof(date));
828 "; Message Information file\n"
844 chan->callerid ? chan->callerid : "Unknown",
848 ast_log(LOG_WARNING, "Error opening text file for output\n");
849 res = play_and_record(chan, NULL, fn, maxmessage, fmt);
852 txt = fopen(txtfile, "a");
855 fprintf(txt, "duration=%ld\n", end-start);
859 strsep(&stringp, "|");
860 /* Send e-mail if applicable */
861 if (strlen(vmu->email))
862 sendmail(serveremail, vmu->email, vmu->fullname, msgnum, ext, chan->callerid, fn, fmt, end - start);
863 if (strlen(vmu->pager))
864 sendpage(serveremail, vmu->pager, msgnum, ext, chan->callerid, end - start);
866 ast_log(LOG_WARNING, "No more messages possible\n");
868 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
871 sildet = ast_dsp_new(); //Create the silence detector
873 rfmt = chan->readformat;
874 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
876 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
880 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
883 ast_dsp_set_threshold(sildet, 50);
888 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
889 /* Leave voicemail for someone */
890 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
894 static char *mbox(int id)
922 static int count_messages(char *dir)
926 for (x=0;x<MAXMSG;x++) {
927 make_file(fn, sizeof(fn), dir, x);
928 if (ast_fileexists(fn, NULL, NULL) < 1)
934 static int say_and_wait(struct ast_channel *chan, int num)
937 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
941 static int copy(char *infile, char *outfile)
948 if ((ifd = open(infile, O_RDONLY)) < 0) {
949 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
952 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
953 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
958 len = read(ifd, buf, sizeof(buf));
960 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
966 res = write(ofd, buf, len);
968 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
980 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
987 char *dbox = mbox(box);
989 make_file(sfn, sizeof(sfn), dir, msg);
990 make_dir(ddir, sizeof(ddir), context, username, dbox);
992 for (x=0;x<MAXMSG;x++) {
993 make_file(dfn, sizeof(dfn), ddir, x);
994 if (ast_fileexists(dfn, NULL, NULL) < 0)
999 ast_filecopy(sfn, dfn, NULL);
1000 if (strcmp(sfn, dfn)) {
1001 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1002 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1008 static int adsi_logo(unsigned char *buf)
1011 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1012 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1016 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1024 bytes += adsi_data_mode(buf + bytes);
1025 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1028 bytes += adsi_logo(buf);
1029 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1031 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1033 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1034 bytes += adsi_data_mode(buf + bytes);
1035 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1037 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1039 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1040 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1041 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1042 bytes += adsi_voice_mode(buf + bytes, 0);
1043 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1050 bytes += adsi_logo(buf);
1051 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1052 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1053 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1054 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1057 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1058 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1059 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1060 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "4", 1);
1061 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1062 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1063 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1066 /* Add another dot */
1068 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1069 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1070 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1074 /* These buttons we load but don't use yet */
1075 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1076 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1077 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1078 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1079 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1080 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1081 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1084 /* Add another dot */
1086 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1087 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1088 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1093 snprintf(num, sizeof(num), "%d", x);
1094 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1096 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1097 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1100 /* Add another dot */
1102 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1103 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1104 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1107 if (adsi_end_download(chan)) {
1109 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1110 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1111 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1112 bytes += adsi_voice_mode(buf + bytes, 0);
1113 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1117 bytes += adsi_download_disconnect(buf + bytes);
1118 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1120 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1125 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1126 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1128 ast_log(LOG_DEBUG, "Restarting session...\n");
1131 /* Load the session now */
1132 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1134 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1136 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1138 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1142 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1145 if (!adsi_available(chan))
1147 x = adsi_load_session(chan, adapp, adver, 1);
1151 if (adsi_load_vmail(chan, useadsi)) {
1152 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1159 static void adsi_login(struct ast_channel *chan)
1163 unsigned char keys[8];
1165 if (!adsi_available(chan))
1170 /* Set one key for next */
1171 keys[3] = ADSI_KEY_APPS + 3;
1173 bytes += adsi_logo(buf + bytes);
1174 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1175 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1176 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1177 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1178 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1179 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1180 bytes += adsi_set_keys(buf + bytes, keys);
1181 bytes += adsi_voice_mode(buf + bytes, 0);
1182 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1185 static void adsi_password(struct ast_channel *chan)
1189 unsigned char keys[8];
1191 if (!adsi_available(chan))
1196 /* Set one key for next */
1197 keys[3] = ADSI_KEY_APPS + 3;
1199 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1200 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1201 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1202 bytes += adsi_set_keys(buf + bytes, keys);
1203 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1206 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1210 unsigned char keys[8];
1213 if (!adsi_available(chan))
1217 y = ADSI_KEY_APPS + 12 + start + x;
1218 if (y > ADSI_KEY_APPS + 12 + 4)
1220 keys[x] = ADSI_KEY_SKT | y;
1222 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1226 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1227 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1228 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1229 bytes += adsi_set_keys(buf + bytes, keys);
1230 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1233 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1236 char buf[256], buf1[256], buf2[256];
1241 char datetime[21]="";
1244 unsigned char keys[8];
1248 if (!adsi_available(chan))
1251 /* Retrieve important info */
1252 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1253 f = fopen(fn2, "r");
1256 fgets(buf, sizeof(buf), f);
1260 strsep(&stringp, "=");
1261 val = strsep(&stringp, "=");
1262 if (val && strlen(val)) {
1263 if (!strcmp(buf, "callerid"))
1264 strncpy(cid, val, sizeof(cid) - 1);
1265 if (!strcmp(buf, "origdate"))
1266 strncpy(datetime, val, sizeof(datetime) - 1);
1272 /* New meaning for keys */
1274 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1279 /* No prev key, provide "Folder" instead */
1280 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1283 /* If last message ... */
1285 /* but not only message, provide "Folder" instead */
1286 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1288 /* Otherwise if only message, leave blank */
1294 ast_callerid_parse(cid, &name, &num);
1298 name = "Unknown Caller";
1300 /* If deleted, show "undeleted" */
1302 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1305 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1306 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1307 strcasecmp(folder, "INBOX") ? " Messages" : "");
1308 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1310 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1311 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1312 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1313 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1314 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1315 bytes += adsi_set_keys(buf + bytes, keys);
1316 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1319 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1323 unsigned char keys[8];
1327 if (!adsi_available(chan))
1330 /* New meaning for keys */
1332 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1338 /* No prev key, provide "Folder" instead */
1339 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1342 /* If last message ... */
1344 /* but not only message, provide "Folder" instead */
1345 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1347 /* Otherwise if only message, leave blank */
1352 /* If deleted, show "undeleted" */
1354 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1357 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1358 bytes += adsi_set_keys(buf + bytes, keys);
1359 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1362 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1364 char buf[256], buf1[256], buf2[256];
1366 unsigned char keys[8];
1369 char *newm = (new == 1) ? "message" : "messages";
1370 char *oldm = (old == 1) ? "message" : "messages";
1371 if (!adsi_available(chan))
1374 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1376 strcat(buf1, " and");
1377 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1379 snprintf(buf2, sizeof(buf2), "%s.", newm);
1382 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1383 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1385 strcpy(buf1, "You have no messages.");
1388 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1389 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1390 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1393 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1397 /* Don't let them listen if there are none */
1400 bytes += adsi_set_keys(buf + bytes, keys);
1402 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1405 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1407 char buf[256], buf1[256], buf2[256];
1409 unsigned char keys[8];
1412 char *mess = (messages == 1) ? "message" : "messages";
1414 if (!adsi_available(chan))
1417 /* Original command keys */
1419 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1427 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1428 strcasecmp(folder, "INBOX") ? " folder" : "");
1431 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1433 strcpy(buf2, "no messages.");
1434 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1435 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1436 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1437 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1438 bytes += adsi_set_keys(buf + bytes, keys);
1440 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1444 static void adsi_clear(struct ast_channel *chan)
1448 if (!adsi_available(chan))
1450 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1451 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1454 static void adsi_goodbye(struct ast_channel *chan)
1459 if (!adsi_available(chan))
1461 bytes += adsi_logo(buf + bytes);
1462 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1463 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1464 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1465 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1468 static int get_folder(struct ast_channel *chan, int start)
1473 d = play_and_wait(chan, "vm-press");
1476 for (x = start; x< 5; x++) {
1477 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1479 d = play_and_wait(chan, "vm-for");
1482 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1483 d = play_and_wait(chan, fn);
1486 d = play_and_wait(chan, "vm-messages");
1489 d = ast_waitfordigit(chan, 500);
1493 d = play_and_wait(chan, "vm-tocancel");
1496 d = ast_waitfordigit(chan, 4000);
1500 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1503 res = play_and_wait(chan, fn);
1504 while (((res < '0') || (res > '9')) &&
1505 (res != '#') && (res >= 0)) {
1506 res = get_folder(chan, 0);
1512 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1519 struct ast_config *mif;
1524 struct ast_vm_user *receiver, srec;
1529 res = ast_streamfile(chan, "vm-extension", chan->language);
1532 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1534 if ((receiver = find_user(&srec, context, username))) {
1535 printf("Got %d\n", atoi(username));
1536 /* if (play_and_wait(chan, "vm-savedto"))
1540 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1541 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1542 ast_log(LOG_DEBUG, sys);
1545 todircount = count_messages(todir);
1546 strncpy(tmp, fmt, sizeof(tmp));
1548 while((s = strsep(&stringp, "|"))) {
1549 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1550 ast_log(LOG_DEBUG, sys);
1553 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1555 /* load the information on the source message so we can send an e-mail like a new message */
1556 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1557 if ((mif=ast_load(miffile))) {
1559 /* set callerid and duration variables */
1560 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1561 duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1563 if (strlen(receiver->email))
1564 sendmail(serveremail, receiver->email, receiver->fullname, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")));
1566 if (strlen(receiver->pager))
1567 sendpage(serveremail, receiver->pager, todircount, username, callerid, duration);
1569 ast_destroy(mif); /* or here */
1572 /* give confirmatopm that the message was saved */
1573 res = play_and_wait(chan, "vm-message");
1575 res = play_and_wait(chan, "vm-saved");
1576 free_user(receiver);
1579 res = play_and_wait(chan, "pbx-invalid");
1592 int deleted[MAXMSG];
1603 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1606 if ((res = ast_streamfile(chan, file, chan->language)))
1607 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1609 res = ast_waitstream(chan, AST_DIGIT_ANY);
1613 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
1616 if ((res = ast_streamfile(chan, file, chan->language)))
1617 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1619 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
1623 static int play_message(struct ast_channel *chan, struct vm_state *vms, int msg)
1627 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
1628 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
1630 res = wait_file2(chan, vms, "vm-first");
1631 else if (msg == vms->lastmsg)
1632 res = wait_file2(chan, vms, "vm-last");
1634 res = wait_file2(chan, vms, "vm-message");
1635 if (msg && (msg != vms->lastmsg)) {
1637 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
1642 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
1643 vms->heard[msg] = 1;
1644 res = wait_file(chan, vms, vms->fn);
1649 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
1651 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
1652 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
1653 vms->lastmsg = count_messages(vms->curdir) - 1;
1654 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
1657 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
1660 char ntxt[256] = "";
1662 if (vms->lastmsg > -1) {
1663 /* Get the deleted messages fixed */
1665 for (x=0;x<=vms->lastmsg;x++) {
1666 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
1667 /* Save this message. It's not in INBOX or hasn't been heard */
1669 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
1670 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
1671 if (strcmp(vms->fn, vms->fn2)) {
1672 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
1673 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
1674 ast_filerename(vms->fn, vms->fn2, NULL);
1677 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
1678 /* Move to old folder before deleting */
1679 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
1682 for (x = vms->curmsg + 1; x<=vms->lastmsg; x++) {
1683 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
1684 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
1685 ast_filedelete(vms->fn, NULL);
1689 memset(vms->deleted, 0, sizeof(vms->deleted));
1690 memset(vms->heard, 0, sizeof(vms->heard));
1693 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
1695 /* Introduce messages they have */
1697 res = play_and_wait(chan, "vm-youhave");
1699 if (vms->newmessages) {
1700 res = say_and_wait(chan, vms->newmessages);
1702 res = play_and_wait(chan, "vm-INBOX");
1703 if (vms->oldmessages && !res)
1704 res = play_and_wait(chan, "vm-and");
1706 if ((vms->newmessages == 1))
1707 res = play_and_wait(chan, "vm-message");
1709 res = play_and_wait(chan, "vm-messages");
1713 if (!res && vms->oldmessages) {
1714 res = say_and_wait(chan, vms->oldmessages);
1716 res = play_and_wait(chan, "vm-Old");
1718 if (vms->oldmessages == 1)
1719 res = play_and_wait(chan, "vm-message");
1721 res = play_and_wait(chan, "vm-messages");
1725 if (!vms->oldmessages && !vms->newmessages) {
1726 res = play_and_wait(chan, "vm-no");
1728 res = play_and_wait(chan, "vm-messages");
1735 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
1738 /* Play instructions and wait for new command */
1740 if (vms->starting) {
1741 if (vms->lastmsg > -1) {
1742 res = play_and_wait(chan, "vm-onefor");
1744 res = play_and_wait(chan, vms->vmbox);
1746 res = play_and_wait(chan, "vm-messages");
1749 res = play_and_wait(chan, "vm-opts");
1752 res = play_and_wait(chan, "vm-prev");
1754 res = play_and_wait(chan, "vm-repeat");
1755 if (!res && (vms->curmsg != vms->lastmsg))
1756 res = play_and_wait(chan, "vm-next");
1758 if (!vms->deleted[vms->curmsg])
1759 res = play_and_wait(chan, "vm-delete");
1761 res = play_and_wait(chan, "vm-undelete");
1763 res = play_and_wait(chan, "vm-toforward");
1765 res = play_and_wait(chan, "vm-savemessage");
1769 res = play_and_wait(chan, "vm-helpexit");
1771 res = ast_waitfordigit(chan, 6000);
1774 if (vms->repeats > 2) {
1775 res = play_and_wait(chan, "vm-goodbye");
1784 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
1788 char newpassword[80] = "";
1789 char newpassword2[80] = "";
1790 char prefile[256]="";
1791 while((cmd >= 0) && (cmd != 't')) {
1796 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
1797 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
1800 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
1801 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
1804 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
1805 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
1808 newpassword[1] = '\0';
1809 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
1812 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
1815 newpassword2[1] = '\0';
1816 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
1820 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
1823 if (strcmp(newpassword, newpassword2)) {
1824 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
1825 cmd = play_and_wait(chan, "vm-mismatch");
1828 if (vm_change_password(vmu,newpassword) < 0)
1830 ast_log(LOG_DEBUG,"Failed to set new password of user %s\n",vms->username);
1832 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
1833 cmd = play_and_wait(chan,"vm-passchanged");
1839 cmd = play_and_wait(chan,"vm-options");
1841 cmd = ast_waitfordigit(chan,6000);
1853 static int vm_execmain(struct ast_channel *chan, void *data)
1855 /* XXX This is, admittedly, some pretty horrendus code. For some
1856 reason it just seemed a lot easier to do with GOTO's. I feel
1857 like I'm back in my GWBASIC days. XXX */
1862 struct localuser *u;
1863 char prefixstr[80] ="";
1864 char empty[80] = "";
1868 char tmp[256], *ext;
1869 char fmtc[256] = "";
1871 struct vm_state vms;
1873 struct ast_vm_user *vmu = NULL, vmus;
1877 memset(&vms, 0, sizeof(vms));
1878 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
1879 if (chan->_state != AST_STATE_UP)
1882 if (data && strlen(data)) {
1883 strncpy(tmp, data, sizeof(tmp) - 1);
1888 /* We should skip the user's password */
1893 /* We should prefix the mailbox with the supplied data */
1899 context = strchr(ext, '@');
1906 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
1908 strncpy(vms.username, ext, sizeof(vms.username) - 1);
1909 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
1916 /* If ADSI is supported, setup login screen */
1917 adsi_begin(chan, &useadsi);
1918 if (!skipuser && useadsi)
1920 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
1921 ast_log(LOG_WARNING, "Couldn't stream login file\n");
1925 /* Authenticate them and get their mailbox/password */
1927 while (!valid && (logretries < maxlogins)) {
1928 /* Prompt for, and read in the username */
1929 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
1930 ast_log(LOG_WARNING, "Couldn't read username\n");
1933 if (!strlen(vms.username)) {
1934 if (option_verbose > 2)
1935 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
1940 adsi_password(chan);
1941 if (ast_streamfile(chan, "vm-password", chan->language)) {
1942 ast_log(LOG_WARNING, "Unable to stream password file\n");
1945 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
1946 ast_log(LOG_WARNING, "Unable to read password\n");
1950 char fullusername[80] = "";
1951 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
1952 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
1953 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
1956 vmu = find_user(&vmus, context, vms.username);
1957 if (vmu && !strcmp(vmu->password, password))
1960 if (option_verbose > 2)
1961 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
1963 strncpy(vms.username, empty, sizeof(vms.username) -1);
1968 if (ast_streamfile(chan, "vm-incorrect", chan->language))
1973 if (!valid && (logretries >= maxlogins)) {
1974 ast_stopstream(chan);
1975 res = play_and_wait(chan, "vm-goodbye");
1981 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
1982 mkdir(vms.curdir, 0700);
1983 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
1984 mkdir(vms.curdir, 0700);
1985 /* Retrieve old and new message counts */
1986 open_mailbox(&vms, vmu, 1);
1987 vms.oldmessages = vms.lastmsg + 1;
1988 /* Start in INBOX */
1989 open_mailbox(&vms, vmu, 0);
1990 vms.newmessages = vms.lastmsg + 1;
1993 /* Select proper mailbox FIRST!! */
1994 if (!vms.newmessages && vms.oldmessages) {
1995 /* If we only have old messages start here */
1996 open_mailbox(&vms, vmu, 1);
2000 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2002 cmd = vm_intro(chan, &vms);
2005 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2012 if (vms.lastmsg > -1) {
2013 cmd = play_message(chan, &vms, vms.curmsg);
2015 cmd = play_and_wait(chan, "vm-youhave");
2017 cmd = play_and_wait(chan, "vm-no");
2019 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2020 cmd = play_and_wait(chan, vms.fn);
2023 cmd = play_and_wait(chan, "vm-messages");
2026 case '2': /* Change folders */
2028 adsi_folders(chan, 0, "Change to folder...");
2029 cmd = get_folder2(chan, "vm-changeto", 0);
2032 } else if (cmd > 0) {
2034 close_mailbox(&vms, vmu);
2035 open_mailbox(&vms, vmu, cmd);
2039 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2041 cmd = play_and_wait(chan, vms.vmbox);
2043 cmd = play_and_wait(chan, "vm-messages");
2049 cmd = play_message(chan, &vms, vms.curmsg);
2051 cmd = play_and_wait(chan, "vm-nomore");
2055 if (vms.curmsg < vms.lastmsg) {
2057 cmd = play_message(chan, &vms, vms.curmsg);
2059 cmd = play_and_wait(chan, "vm-nomore");
2063 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2065 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2066 if (vms.deleted[vms.curmsg])
2067 cmd = play_and_wait(chan, "vm-deleted");
2069 cmd = play_and_wait(chan, "vm-undeleted");
2072 if(vms.lastmsg > -1)
2073 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2077 adsi_folders(chan, 1, "Save to folder...");
2078 cmd = get_folder2(chan, "vm-savefolder", 1);
2079 box = 0; /* Shut up compiler */
2083 } else if (cmd > 0) {
2084 box = cmd = cmd - '0';
2085 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2086 vms.deleted[vms.curmsg]=1;
2088 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2090 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2092 cmd = play_and_wait(chan, "vm-message");
2094 cmd = say_and_wait(chan, vms.curmsg + 1);
2096 cmd = play_and_wait(chan, "vm-savedto");
2098 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2099 cmd = play_and_wait(chan, vms.fn);
2102 cmd = play_and_wait(chan, "vm-messages");
2105 if (!vms.starting) {
2106 cmd = play_and_wait(chan, "vm-onefor");
2108 cmd = play_and_wait(chan, vms.vmbox);
2110 cmd = play_and_wait(chan, "vm-messages");
2112 cmd = play_and_wait(chan, "vm-opts");
2117 cmd = vm_options(chan, vmu, &vms, vmfmts);
2120 ast_stopstream(chan);
2122 cmd = play_and_wait(chan, "vm-goodbye");
2126 default: /* Nothing */
2127 cmd = vm_instructions(chan, &vms);
2131 if ((cmd == 't') || (cmd == '#')) {
2141 ast_stopstream(chan);
2144 adsi_unload_session(chan);
2147 close_mailbox(&vms, vmu);
2151 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2153 LOCAL_USER_REMOVE(u);
2158 static int vm_exec(struct ast_channel *chan, void *data)
2160 int res=0, silent=0, busy=0, unavail=0;
2161 struct localuser *u;
2162 char tmp[256], *ext;
2165 if (chan->_state != AST_STATE_UP)
2168 strncpy(tmp, data, sizeof(tmp) - 1);
2170 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2181 } else if (*ext == 'b') {
2184 } else if (*ext == 'u') {
2190 res = leave_voicemail(chan, ext, silent, busy, unavail);
2191 LOCAL_USER_REMOVE(u);
2195 static int append_mailbox(char *context, char *mbox, char *data)
2197 /* Assumes lock is already held */
2201 struct ast_vm_user *vmu;
2202 strncpy(tmp, data, sizeof(tmp));
2203 vmu = malloc(sizeof(struct ast_vm_user));
2205 memset(vmu, 0, sizeof(struct ast_vm_user));
2206 strncpy(vmu->context, context, sizeof(vmu->context));
2207 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2209 if ((s = strsep(&stringp, ",")))
2210 strncpy(vmu->password, s, sizeof(vmu->password));
2211 if ((s = strsep(&stringp, ",")))
2212 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2213 if ((s = strsep(&stringp, ",")))
2214 strncpy(vmu->email, s, sizeof(vmu->email));
2215 if ((s = strsep(&stringp, ",")))
2216 strncpy(vmu->pager, s, sizeof(vmu->pager));
2227 static int load_users(void)
2229 struct ast_vm_user *cur, *l;
2230 struct ast_config *cfg;
2232 struct ast_variable *var;
2240 cfg = ast_load(VOICEMAIL_CONFIG);
2241 ast_pthread_mutex_lock(&vmlock);
2251 /* General settings */
2252 attach_voicemail = 1;
2253 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
2255 attach_voicemail = ast_true(astattach);
2257 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2258 maxsilence = atoi(silencestr);
2263 silencethreshold = 256;
2264 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2265 silencethreshold = atoi(thresholdstr);
2267 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
2268 astemail = ASTERISK_USERNAME;
2269 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2272 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2273 if (sscanf(s, "%d", &x) == 1) {
2276 ast_log(LOG_WARNING, "Invalid max message time length\n");
2279 fmt = ast_variable_retrieve(cfg, "general", "format");
2282 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2285 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2286 if (sscanf(s, "%d", &x) == 1) {
2289 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2293 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2294 if (sscanf(s, "%d", &x) == 1) {
2297 ast_log(LOG_WARNING, "Invalid skipms value\n");
2302 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2303 if (sscanf(s, "%d", &x) == 1) {
2306 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2310 cat = ast_category_browse(cfg, NULL);
2312 if (strcasecmp(cat, "general")) {
2313 /* Process mailboxes in this context */
2314 var = ast_variable_browse(cfg, cat);
2316 append_mailbox(cat, var->name, var->value);
2320 cat = ast_category_browse(cfg, cat);
2324 ast_pthread_mutex_unlock(&vmlock);
2334 int unload_module(void)
2337 STANDARD_HANGUP_LOCALUSERS;
2338 res = ast_unregister_application(app);
2339 res |= ast_unregister_application(app2);
2343 int load_module(void)
2347 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2349 res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2353 char *description(void)
2361 STANDARD_USECOUNT(res);
2367 return ASTERISK_GPL_KEY;