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>
37 #include <mysql/mysql.h>
41 #include "../asterisk.h"
42 #include "../astconf.h"
44 #define COMMAND_TIMEOUT 5000
46 #define VOICEMAIL_CONFIG "voicemail.conf"
47 #define ASTERISK_USERNAME "asterisk"
49 #define SENDMAIL "/usr/sbin/sendmail -t"
51 #define INTRO "vm-intro"
55 #define MAX_OTHER_FORMATS 10
57 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
59 #define BASEMAXINLINE 256
61 #define BASELINELEN 72
67 #define BASEMAXINLINE 256
68 #define BASELINELEN 72
76 unsigned char iobuf[BASEMAXINLINE];
88 struct ast_vm_user *next;
91 static char *tdesc = "Comedian Mail (Voicemail System)";
93 static char *adapp = "CoMa";
95 static char *adsec = "_AST";
97 static char *addesc = "Comedian Mail";
101 static char *synopsis_vm =
102 "Leave a voicemail message";
104 static char *descrip_vm =
105 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given extension (must\n"
106 "be configured in voicemail.conf). If the extension is preceeded by an 's'"
107 "then instructions for leaving the message will be skipped. If the extension\n"
108 "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
109 "/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists. If the extension\n"
110 "is preceeded by a 'b' then the the busy message will be played (that is,\n"
111 "busy instead of unavail). At most one of 's', 'u', or 'b' may be specified.\n"
112 "Returns -1 on error or mailbox not found, or if the user hangs up. \n"
113 "Otherwise, it returns 0. \n";
115 static char *synopsis_vmain =
116 "Enter voicemail system";
118 static char *descrip_vmain =
119 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system for the checking of\n"
120 "voicemail. The mailbox can be passed as the option, which will stop the\n"
121 "voicemail system from prompting the user for the mailbox. If the mailbox\n"
122 "is preceded by 's' then the password check will be skipped. If a context is\n"
123 "specified, logins are considered in that context only. Returns -1 if\n"
124 "the user hangs up or 0 otherwise.\n";
126 /* Leave a message */
127 static char *app = "VoiceMail2";
129 /* Check mail, control, etc */
130 static char *app2 = "VoiceMailMain2";
132 static pthread_mutex_t vmlock = AST_MUTEX_INITIALIZER;
133 struct ast_vm_user *users;
134 struct ast_vm_user *usersl;
135 static int attach_voicemail;
136 static int maxsilence;
137 static int silencethreshold;
138 static char serveremail[80];
139 static char vmfmts[80];
140 static int vmmaxmessage;
143 static int maxlogins;
150 MYSQL *dbhandler=NULL;
151 pthread_mutex_t mysqllock;
156 static int mysql_login(void)
158 ast_verbose( VERBOSE_PREFIX_3 "Logging into database with user %s, password %s, and database %s\n", dbuser, dbpass, dbname);
160 dbhandler=mysql_init(NULL);
161 if (!mysql_real_connect(dbhandler, NULL, dbuser, dbpass, dbname, 0, NULL, 0)) {
162 ast_log(LOG_WARNING, "Error Logging into database\n");
165 pthread_mutex_init(&mysqllock, NULL);
169 static void mysql_logout(void)
171 mysql_close(dbhandler);
174 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
181 struct ast_vm_user *retval;
183 retval=malloc(sizeof(struct ast_vm_user));
185 *retval->mailbox='\0';
186 *retval->context='\0';
187 *retval->password='\0';
188 *retval->fullname='\0';
191 *retval->options='\0';
195 strcpy(retval->mailbox, mailbox);
198 strcpy(retval->context, context);
201 if (*retval->context) {
202 sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE context='%s' AND mailbox='%s'", context, mailbox);
204 sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE mailbox='%s'", mailbox);
206 pthread_mutex_lock(&mysqllock);
207 mysql_query(dbhandler, query);
208 if ((result=mysql_store_result(dbhandler))!=NULL) {
209 if ((rowval=mysql_fetch_row(result))!=NULL) {
210 numFields=mysql_num_fields(result);
211 fields=mysql_fetch_fields(result);
212 for (i=0; i<numFields; i++) {
214 if (!strcmp(fields[i].name, "password")) {
215 strcpy(retval->password, rowval[i]);
216 } else if (!strcmp(fields[i].name, "fullname")) {
217 strcpy(retval->fullname, rowval[i]);
218 } else if (!strcmp(fields[i].name, "email")) {
219 strcpy(retval->email, rowval[i]);
220 } else if (!strcmp(fields[i].name, "pager")) {
221 strcpy(retval->pager, rowval[i]);
222 } else if (!strcmp(fields[i].name, "options")) {
223 strcpy(retval->options, rowval[i]);
227 mysql_free_result(result);
228 pthread_mutex_unlock(&mysqllock);
231 mysql_free_result(result);
232 pthread_mutex_unlock(&mysqllock);
237 pthread_mutex_unlock(&mysqllock);
242 static void vm_change_password(struct ast_vm_user *vmu, char *password)
247 sprintf(query, "UPDATE users SET password='%s' WHERE context='%s' AND mailbox='%s' AND password='%s'", password, vmu->context, vmu->mailbox, vmu->password);
249 sprintf(query, "UPDATE users SET password='%s' WHERE mailbox='%s' AND password='%s'", password, vmu->mailbox, vmu->password);
251 pthread_mutex_lock(&mysqllock);
252 mysql_query(dbhandler, query);
253 strcpy(vmu->password, password);
254 pthread_mutex_unlock(&mysqllock);
257 static void reset_user_pw(char *context, char *mailbox, char *password)
262 sprintf(query, "UPDATE users SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
264 sprintf(query, "UPDATE users SET password='%s' WHERE mailbox='%s'", password, mailbox);
266 pthread_mutex_lock(&mysqllock);
267 mysql_query(dbhandler, query);
268 pthread_mutex_unlock(&mysqllock);
272 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
274 /* This function could be made to generate one from a database, too */
275 struct ast_vm_user *vmu=NULL, *cur;
276 ast_pthread_mutex_lock(&vmlock);
279 if ((!context || !strcasecmp(context, cur->context)) &&
280 (!strcasecmp(mailbox, cur->mailbox)))
288 /* Make a copy, so that on a reload, we have no race */
289 vmu = malloc(sizeof(struct ast_vm_user));
291 memcpy(vmu, cur, sizeof(struct ast_vm_user));
299 ast_pthread_mutex_unlock(&vmlock);
303 static int reset_user_pw(char *context, char *mailbox, char *newpass)
305 /* This function could be made to generate one from a database, too */
306 struct ast_vm_user *cur;
308 ast_pthread_mutex_lock(&vmlock);
311 if ((!context || !strcasecmp(context, cur->context)) &&
312 (!strcasecmp(mailbox, cur->mailbox)))
317 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
320 ast_pthread_mutex_unlock(&vmlock);
324 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
326 /* There's probably a better way of doing this. */
327 /* That's why I've put the password change in a separate function. */
328 /* This could also be done with a database function */
334 char tmpin[AST_CONFIG_MAX_PATH];
335 char tmpout[AST_CONFIG_MAX_PATH];
336 char *user, *pass, *rest, *trim;
337 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
338 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
339 configin = fopen((char *)tmpin,"r");
340 configout = fopen((char *)tmpout,"w+");
342 while (!feof(configin)) {
343 /* Read in the line */
344 fgets(inbuf, sizeof(inbuf), configin);
345 if (!feof(configin)) {
346 /* Make a backup of it */
347 memcpy(orig, inbuf, sizeof(orig));
348 /* Strip trailing \n and comment */
349 inbuf[strlen(inbuf) - 1] = '\0';
350 user = strchr(inbuf, ';');
356 pass = strchr(user, '=');
359 while(*trim && *trim < 33) {
369 while(*pass && *pass < 33)
373 rest = strchr(pass,',');
380 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
381 /* This is the line */
383 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
385 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
388 /* Put it back like it was */
389 fprintf(configout, orig);
396 unlink((char *)tmpin);
397 rename((char *)tmpout,(char *)tmpin);
398 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
399 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
403 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
405 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
408 static int make_file(char *dest, int len, char *dir, int num)
410 return snprintf(dest, len, "%s/msg%04d", dir, num);
414 inbuf(struct baseio *bio, FILE *fi)
421 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
436 inchar(struct baseio *bio, FILE *fi)
438 if(bio->iocp>=bio->iolen)
442 return bio->iobuf[bio->iocp++];
446 ochar(struct baseio *bio, int c, FILE *so)
448 if(bio->linelength>=BASELINELEN) {
449 if(fputs(eol,so)==EOF)
455 if(putc(((unsigned char)c),so)==EOF)
463 static int base_encode(char *filename, FILE *so)
465 unsigned char dtable[BASEMAXINLINE];
470 memset(&bio, 0, sizeof(bio));
471 bio.iocp = BASEMAXINLINE;
473 if ( !(fi = fopen(filename, "rb"))) {
474 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
482 dtable[26+i+9]= 'j'+i;
486 dtable[26+i+18]= 's'+i;
495 unsigned char igroup[3],ogroup[4];
498 igroup[0]= igroup[1]= igroup[2]= 0;
501 if ( (c = inchar(&bio, fi)) == EOF) {
506 igroup[n]= (unsigned char)c;
510 ogroup[0]= dtable[igroup[0]>>2];
511 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
512 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
513 ogroup[3]= dtable[igroup[2]&0x3F];
523 ochar(&bio, ogroup[i], so);
527 if(fputs(eol,so)==EOF)
535 static int getoptionvalue(char *options, char *optionname, char *value)
539 if (!(c=strstr(options, optionname))) {
542 if (c!=options && *(c-1)!='|') {
545 if (*(c+strlen(optionname))!='=') {
549 c+=strlen(optionname)+1;
550 if ((d=strchr(c, '|'))) {
551 strncpy(value, c, d-c);
559 static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration, int attach_user_voicemail)
570 if (!strcmp(format, "wav49"))
572 p = popen(SENDMAIL, "w");
574 gethostname(host, sizeof(host));
575 if (strchr(srcemail, '@'))
576 strncpy(who, srcemail, sizeof(who)-1);
578 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
580 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
583 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
584 fprintf(p, "Date: %s\n", date);
585 fprintf(p, "From: Asterisk PBX <%s>\n", who);
586 fprintf(p, "To: %s <%s>\n", name, email);
587 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox);
588 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
589 fprintf(p, "MIME-Version: 1.0\n");
590 if ((attach_user_voicemail==-1 && attach_voicemail) || attach_user_voicemail==1) {
592 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
594 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
596 fprintf(p, "--%s\n", bound);
598 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
599 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
600 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
602 "in mailbox %s from %s, on %s so you might\n"
603 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", name,
604 dur, msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
605 if ((attach_user_voicemail==-1 && attach_voicemail) || attach_user_voicemail==1) {
606 fprintf(p, "--%s\n", bound);
607 fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum, format);
608 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
609 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
610 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
612 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
613 base_encode(fname, p);
614 fprintf(p, "\n\n--%s--\n.\n", bound);
618 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
624 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration)
633 p = popen(SENDMAIL, "w");
636 gethostname(host, sizeof(host));
637 if (strchr(srcemail, '@'))
638 strncpy(who, srcemail, sizeof(who)-1);
640 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
642 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
645 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
646 fprintf(p, "Date: %s\n", date);
647 fprintf(p, "From: Asterisk PBX <%s>\n", who);
648 fprintf(p, "To: %s\n", pager);
649 fprintf(p, "Subject: New VM\n\n");
650 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
651 fprintf(p, "New %s long msg in box %s\n"
652 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
655 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
661 static int get_date(char *s, int len)
667 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
670 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
674 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
675 if (ast_fileexists(fn, NULL, NULL) > 0) {
676 res = ast_streamfile(chan, fn, chan->language);
679 res = ast_waitstream(chan, ecodes);
683 res = ast_streamfile(chan, "vm-theperson", chan->language);
686 res = ast_waitstream(chan, ecodes);
689 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
694 res = ast_streamfile(chan, "vm-isonphone", chan->language);
696 res = ast_streamfile(chan, "vm-isunavail", chan->language);
699 res = ast_waitstream(chan, ecodes);
703 static int play_and_wait(struct ast_channel *chan, char *fn)
706 d = ast_streamfile(chan, fn, chan->language);
709 d = ast_waitstream(chan, AST_DIGIT_ANY);
713 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
717 int x, fmtcnt=1, res=-1,outmsg=0;
719 struct ast_filestream *others[MAX_OTHER_FORMATS];
720 char *sfmt[MAX_OTHER_FORMATS];
723 struct ast_dsp *sildet; /* silence detector dsp */
724 int totalsilence = 0;
726 int gotsilence = 0; /* did we timeout for silence? */
729 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
730 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
733 d = play_and_wait(chan, playfile);
735 d = ast_streamfile(chan, "beep",chan->language);
737 d = ast_waitstream(chan,"");
742 fmts = ast_strdupa(fmt);
745 strsep(&stringp, "|");
746 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
747 sfmt[0] = ast_strdupa(fmts);
749 while((fmt = strsep(&stringp, "|"))) {
750 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
751 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
754 sfmt[fmtcnt++] = ast_strdupa(fmt);
759 for (x=0;x<fmtcnt;x++) {
760 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
761 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
768 sildet = ast_dsp_new(); //Create the silence detector
770 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
773 ast_dsp_set_threshold(sildet, 50);
775 if (maxsilence > 0) {
776 rfmt = chan->readformat;
777 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
779 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
785 /* Loop forever, writing the packets we read to the writer(s), until
786 we read a # or get a hangup */
789 res = ast_waitfor(chan, 2000);
791 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
792 /* Try one more time in case of masq */
793 res = ast_waitfor(chan, 2000);
795 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
807 if (f->frametype == AST_FRAME_VOICE) {
808 /* write each format */
809 for (x=0;x<fmtcnt;x++) {
810 res = ast_writestream(others[x], f);
813 /* Silence Detection */
814 if (maxsilence > 0) {
816 ast_dsp_silence(sildet, f, &dspsilence);
818 totalsilence = dspsilence;
822 if (totalsilence > maxsilence) {
823 /* Ended happily with silence */
830 /* Exit on any error */
832 ast_log(LOG_WARNING, "Error writing frame\n");
836 } else if (f->frametype == AST_FRAME_DTMF) {
837 if (f->subclass == '#') {
838 if (option_verbose > 2)
839 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
848 if (maxtime < (end - start)) {
849 if (option_verbose > 2)
850 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
859 if (option_verbose > 2)
860 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
865 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
868 for (x=0;x<fmtcnt;x++) {
872 ast_stream_rewind(others[x], totalsilence-200);
874 ast_stream_rewind(others[x], 1000);
875 ast_truncstream(others[x]);
876 ast_closestream(others[x]);
879 if (ast_set_read_format(chan, rfmt)) {
880 ast_log(LOG_WARNING, "Unable to restore format %d to channel '%s'\n", rfmt, chan->name);
885 /* Let them know it worked */
886 ast_streamfile(chan, "vm-msgsaved", chan->language);
887 ast_waitstream(chan, "");
895 static void free_user(struct ast_vm_user *vmu)
901 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
911 char prefile[256]="";
919 struct ast_vm_user *vmu;
920 struct ast_vm_user svm;
922 strncpy(tmp, ext, sizeof(tmp) - 1);
924 context = strchr(tmp, '@');
930 if ((vmu = find_user(&svm, context, ext))) {
931 /* Setup pre-file if appropriate */
933 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
935 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
936 make_dir(dir, sizeof(dir), vmu->context, "", "");
937 /* It's easier just to try to make it than to check for its existence */
938 if (mkdir(dir, 0700) && (errno != EEXIST))
939 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
940 make_dir(dir, sizeof(dir), vmu->context, ext, "");
941 /* It's easier just to try to make it than to check for its existence */
942 if (mkdir(dir, 0700) && (errno != EEXIST))
943 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
944 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
945 if (mkdir(dir, 0700) && (errno != EEXIST))
946 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
947 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
949 /* Play the beginning intro if desired */
950 if (strlen(prefile)) {
951 if (ast_fileexists(prefile, NULL, NULL) > 0) {
952 if (ast_streamfile(chan, prefile, chan->language) > -1)
953 res = ast_waitstream(chan, "#0");
955 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
956 res = invent_message(chan, vmu->context, ext, busy, ecodes);
959 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
965 /* On a '#' we skip the instructions */
969 if (!res && !silent) {
970 res = ast_streamfile(chan, INTRO, chan->language);
972 res = ast_waitstream(chan, ecodes);
978 /* Check for a '0' here */
980 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
981 if (strlen(chan->macrocontext))
982 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
988 /* Unless we're *really* silent, try to send the beep */
989 res = ast_streamfile(chan, "beep", chan->language);
991 res = ast_waitstream(chan, "");
997 /* The meat of recording the message... All the announcements and beeps have been played*/
998 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1002 make_file(fn, sizeof(fn), dir, msgnum);
1003 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1004 (chan->callerid ? chan->callerid : "Unknown"),
1005 vmu->fullname, ext, chan->name);
1006 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1009 } while(msgnum < MAXMSG);
1010 if (msgnum < MAXMSG) {
1011 /* Store information */
1012 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1013 txt = fopen(txtfile, "w+");
1015 get_date(date, sizeof(date));
1019 "; Message Information file\n"
1035 chan->callerid ? chan->callerid : "Unknown",
1039 ast_log(LOG_WARNING, "Error opening text file for output\n");
1040 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1043 txt = fopen(txtfile, "a");
1046 fprintf(txt, "duration=%ld\n", end-start);
1050 strsep(&stringp, "|");
1051 /* Send e-mail if applicable */
1052 if (strlen(vmu->email)) {
1053 int attach_user_voicemail=-1;
1055 if (getoptionvalue(vmu->options, "attach", tmpvalue)) {
1056 attach_user_voicemail=ast_true(tmpvalue);
1058 sendmail(serveremail, vmu->email, vmu->fullname, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1060 if (strlen(vmu->pager))
1061 sendpage(serveremail, vmu->pager, msgnum, ext, chan->callerid, end - start);
1063 ast_log(LOG_WARNING, "No more messages possible\n");
1065 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1068 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1069 /* Leave voicemail for someone */
1070 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1074 static char *mbox(int id)
1102 static int count_messages(char *dir)
1106 for (x=0;x<MAXMSG;x++) {
1107 make_file(fn, sizeof(fn), dir, x);
1108 if (ast_fileexists(fn, NULL, NULL) < 1)
1114 static int say_and_wait(struct ast_channel *chan, int num)
1117 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1121 static int copy(char *infile, char *outfile)
1128 if ((ifd = open(infile, O_RDONLY)) < 0) {
1129 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1132 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1133 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1138 len = read(ifd, buf, sizeof(buf));
1140 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1146 res = write(ofd, buf, len);
1148 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1160 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1167 char *dbox = mbox(box);
1169 make_file(sfn, sizeof(sfn), dir, msg);
1170 make_dir(ddir, sizeof(ddir), context, username, dbox);
1172 for (x=0;x<MAXMSG;x++) {
1173 make_file(dfn, sizeof(dfn), ddir, x);
1174 if (ast_fileexists(dfn, NULL, NULL) < 0)
1179 ast_filecopy(sfn, dfn, NULL);
1180 if (strcmp(sfn, dfn)) {
1181 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1182 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1188 static int adsi_logo(unsigned char *buf)
1191 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1192 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1196 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1204 bytes += adsi_data_mode(buf + bytes);
1205 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1208 bytes += adsi_logo(buf);
1209 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1211 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1213 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1214 bytes += adsi_data_mode(buf + bytes);
1215 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1217 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1219 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1220 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1221 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1222 bytes += adsi_voice_mode(buf + bytes, 0);
1223 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1230 bytes += adsi_logo(buf);
1231 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1232 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1233 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1234 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1237 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1238 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1239 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1240 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "4", 1);
1241 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1242 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1243 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1246 /* Add another dot */
1248 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1249 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1250 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1254 /* These buttons we load but don't use yet */
1255 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1256 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1257 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1258 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1259 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1260 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1261 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1264 /* Add another dot */
1266 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1267 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1268 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1273 snprintf(num, sizeof(num), "%d", x);
1274 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1276 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1277 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1280 /* Add another dot */
1282 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1283 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1284 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1287 if (adsi_end_download(chan)) {
1289 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1290 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1291 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1292 bytes += adsi_voice_mode(buf + bytes, 0);
1293 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1297 bytes += adsi_download_disconnect(buf + bytes);
1298 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1300 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1305 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1306 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1308 ast_log(LOG_DEBUG, "Restarting session...\n");
1311 /* Load the session now */
1312 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1314 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1316 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1318 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1322 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1325 if (!adsi_available(chan))
1327 x = adsi_load_session(chan, adapp, adver, 1);
1331 if (adsi_load_vmail(chan, useadsi)) {
1332 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1339 static void adsi_login(struct ast_channel *chan)
1343 unsigned char keys[8];
1345 if (!adsi_available(chan))
1350 /* Set one key for next */
1351 keys[3] = ADSI_KEY_APPS + 3;
1353 bytes += adsi_logo(buf + bytes);
1354 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1355 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1356 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1357 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1358 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1359 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1360 bytes += adsi_set_keys(buf + bytes, keys);
1361 bytes += adsi_voice_mode(buf + bytes, 0);
1362 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1365 static void adsi_password(struct ast_channel *chan)
1369 unsigned char keys[8];
1371 if (!adsi_available(chan))
1376 /* Set one key for next */
1377 keys[3] = ADSI_KEY_APPS + 3;
1379 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1380 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1381 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1382 bytes += adsi_set_keys(buf + bytes, keys);
1383 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1386 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1390 unsigned char keys[8];
1393 if (!adsi_available(chan))
1397 y = ADSI_KEY_APPS + 12 + start + x;
1398 if (y > ADSI_KEY_APPS + 12 + 4)
1400 keys[x] = ADSI_KEY_SKT | y;
1402 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1406 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1407 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1408 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1409 bytes += adsi_set_keys(buf + bytes, keys);
1410 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1413 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1416 char buf[256], buf1[256], buf2[256];
1421 char datetime[21]="";
1424 unsigned char keys[8];
1428 if (!adsi_available(chan))
1431 /* Retrieve important info */
1432 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1433 f = fopen(fn2, "r");
1436 fgets(buf, sizeof(buf), f);
1440 strsep(&stringp, "=");
1441 val = strsep(&stringp, "=");
1442 if (val && strlen(val)) {
1443 if (!strcmp(buf, "callerid"))
1444 strncpy(cid, val, sizeof(cid) - 1);
1445 if (!strcmp(buf, "origdate"))
1446 strncpy(datetime, val, sizeof(datetime) - 1);
1452 /* New meaning for keys */
1454 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1459 /* No prev key, provide "Folder" instead */
1460 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1463 /* If last message ... */
1465 /* but not only message, provide "Folder" instead */
1466 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1468 /* Otherwise if only message, leave blank */
1474 ast_callerid_parse(cid, &name, &num);
1478 name = "Unknown Caller";
1480 /* If deleted, show "undeleted" */
1482 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1485 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1486 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1487 strcasecmp(folder, "INBOX") ? " Messages" : "");
1488 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1490 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1491 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1492 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1493 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1494 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1495 bytes += adsi_set_keys(buf + bytes, keys);
1496 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1499 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1503 unsigned char keys[8];
1507 if (!adsi_available(chan))
1510 /* New meaning for keys */
1512 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1518 /* No prev key, provide "Folder" instead */
1519 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1522 /* If last message ... */
1524 /* but not only message, provide "Folder" instead */
1525 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1527 /* Otherwise if only message, leave blank */
1532 /* If deleted, show "undeleted" */
1534 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1537 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1538 bytes += adsi_set_keys(buf + bytes, keys);
1539 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1542 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1544 char buf[256], buf1[256], buf2[256];
1546 unsigned char keys[8];
1549 char *newm = (new == 1) ? "message" : "messages";
1550 char *oldm = (old == 1) ? "message" : "messages";
1551 if (!adsi_available(chan))
1554 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1556 strcat(buf1, " and");
1557 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1559 snprintf(buf2, sizeof(buf2), "%s.", newm);
1562 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1563 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1565 strcpy(buf1, "You have no messages.");
1568 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1569 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1570 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1573 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1577 /* Don't let them listen if there are none */
1580 bytes += adsi_set_keys(buf + bytes, keys);
1582 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1585 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1587 char buf[256], buf1[256], buf2[256];
1589 unsigned char keys[8];
1592 char *mess = (messages == 1) ? "message" : "messages";
1594 if (!adsi_available(chan))
1597 /* Original command keys */
1599 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1607 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1608 strcasecmp(folder, "INBOX") ? " folder" : "");
1611 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1613 strcpy(buf2, "no messages.");
1614 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1615 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1616 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1617 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1618 bytes += adsi_set_keys(buf + bytes, keys);
1620 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1624 static void adsi_clear(struct ast_channel *chan)
1628 if (!adsi_available(chan))
1630 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1631 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1634 static void adsi_goodbye(struct ast_channel *chan)
1639 if (!adsi_available(chan))
1641 bytes += adsi_logo(buf + bytes);
1642 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1643 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1644 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1645 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1648 static int get_folder(struct ast_channel *chan, int start)
1653 d = play_and_wait(chan, "vm-press");
1656 for (x = start; x< 5; x++) {
1657 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1659 d = play_and_wait(chan, "vm-for");
1662 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1663 d = play_and_wait(chan, fn);
1666 d = play_and_wait(chan, "vm-messages");
1669 d = ast_waitfordigit(chan, 500);
1673 d = play_and_wait(chan, "vm-tocancel");
1676 d = ast_waitfordigit(chan, 4000);
1680 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1683 res = play_and_wait(chan, fn);
1684 while (((res < '0') || (res > '9')) &&
1685 (res != '#') && (res >= 0)) {
1686 res = get_folder(chan, 0);
1692 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1699 struct ast_config *mif;
1704 struct ast_vm_user *receiver, srec;
1709 res = ast_streamfile(chan, "vm-extension", chan->language);
1712 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1714 if ((receiver = find_user(&srec, context, username))) {
1715 printf("Got %d\n", atoi(username));
1716 /* if (play_and_wait(chan, "vm-savedto"))
1720 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1721 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1722 ast_log(LOG_DEBUG, sys);
1725 todircount = count_messages(todir);
1726 strncpy(tmp, fmt, sizeof(tmp));
1728 while((s = strsep(&stringp, "|"))) {
1729 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1730 ast_log(LOG_DEBUG, sys);
1733 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1735 /* load the information on the source message so we can send an e-mail like a new message */
1736 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1737 if ((mif=ast_load(miffile))) {
1739 /* set callerid and duration variables */
1740 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1741 duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1743 if (strlen(receiver->email)) {
1744 int attach_user_voicemail=-1;
1746 if (getoptionvalue(receiver->options, "attach", tmpvalue)) {
1747 attach_user_voicemail=ast_true(tmpvalue);
1749 sendmail(serveremail, receiver->email, receiver->fullname, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1752 if (strlen(receiver->pager))
1753 sendpage(serveremail, receiver->pager, todircount, username, callerid, duration);
1755 ast_destroy(mif); /* or here */
1758 /* give confirmatopm that the message was saved */
1759 res = play_and_wait(chan, "vm-message");
1761 res = play_and_wait(chan, "vm-saved");
1762 free_user(receiver);
1765 res = play_and_wait(chan, "pbx-invalid");
1778 int deleted[MAXMSG];
1789 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1792 if ((res = ast_streamfile(chan, file, chan->language)))
1793 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1795 res = ast_waitstream(chan, AST_DIGIT_ANY);
1799 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
1802 if ((res = ast_streamfile(chan, file, chan->language)))
1803 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1805 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
1809 static int play_message(struct ast_channel *chan, struct vm_state *vms, int msg)
1813 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
1814 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
1816 res = wait_file2(chan, vms, "vm-first");
1817 else if (msg == vms->lastmsg)
1818 res = wait_file2(chan, vms, "vm-last");
1820 res = wait_file2(chan, vms, "vm-message");
1821 if (msg && (msg != vms->lastmsg)) {
1823 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
1828 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
1829 vms->heard[msg] = 1;
1830 res = wait_file(chan, vms, vms->fn);
1835 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
1837 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
1838 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
1839 vms->lastmsg = count_messages(vms->curdir) - 1;
1840 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
1843 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
1846 char ntxt[256] = "";
1848 if (vms->lastmsg > -1) {
1849 /* Get the deleted messages fixed */
1851 for (x=0;x<=vms->lastmsg;x++) {
1852 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
1853 /* Save this message. It's not in INBOX or hasn't been heard */
1855 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
1856 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
1857 if (strcmp(vms->fn, vms->fn2)) {
1858 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
1859 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
1860 ast_filerename(vms->fn, vms->fn2, NULL);
1863 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
1864 /* Move to old folder before deleting */
1865 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
1868 for (x = vms->curmsg + 1; x<=vms->lastmsg; x++) {
1869 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
1870 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
1871 ast_filedelete(vms->fn, NULL);
1875 memset(vms->deleted, 0, sizeof(vms->deleted));
1876 memset(vms->heard, 0, sizeof(vms->heard));
1879 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
1881 /* Introduce messages they have */
1883 res = play_and_wait(chan, "vm-youhave");
1885 if (vms->newmessages) {
1886 res = say_and_wait(chan, vms->newmessages);
1888 res = play_and_wait(chan, "vm-INBOX");
1889 if (vms->oldmessages && !res)
1890 res = play_and_wait(chan, "vm-and");
1892 if ((vms->newmessages == 1))
1893 res = play_and_wait(chan, "vm-message");
1895 res = play_and_wait(chan, "vm-messages");
1899 if (!res && vms->oldmessages) {
1900 res = say_and_wait(chan, vms->oldmessages);
1902 res = play_and_wait(chan, "vm-Old");
1904 if (vms->oldmessages == 1)
1905 res = play_and_wait(chan, "vm-message");
1907 res = play_and_wait(chan, "vm-messages");
1911 if (!vms->oldmessages && !vms->newmessages) {
1912 res = play_and_wait(chan, "vm-no");
1914 res = play_and_wait(chan, "vm-messages");
1921 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
1924 /* Play instructions and wait for new command */
1926 if (vms->starting) {
1927 if (vms->lastmsg > -1) {
1928 res = play_and_wait(chan, "vm-onefor");
1930 res = play_and_wait(chan, vms->vmbox);
1932 res = play_and_wait(chan, "vm-messages");
1935 res = play_and_wait(chan, "vm-opts");
1938 res = play_and_wait(chan, "vm-prev");
1940 res = play_and_wait(chan, "vm-repeat");
1941 if (!res && (vms->curmsg != vms->lastmsg))
1942 res = play_and_wait(chan, "vm-next");
1944 if (!vms->deleted[vms->curmsg])
1945 res = play_and_wait(chan, "vm-delete");
1947 res = play_and_wait(chan, "vm-undelete");
1949 res = play_and_wait(chan, "vm-toforward");
1951 res = play_and_wait(chan, "vm-savemessage");
1955 res = play_and_wait(chan, "vm-helpexit");
1957 res = ast_waitfordigit(chan, 6000);
1960 if (vms->repeats > 2) {
1961 res = play_and_wait(chan, "vm-goodbye");
1970 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
1974 char newpassword[80] = "";
1975 char newpassword2[80] = "";
1976 char prefile[256]="";
1977 while((cmd >= 0) && (cmd != 't')) {
1982 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
1983 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
1986 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
1987 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
1990 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
1991 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
1994 newpassword[1] = '\0';
1995 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
1998 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2001 newpassword2[1] = '\0';
2002 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2006 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2009 if (strcmp(newpassword, newpassword2)) {
2010 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2011 cmd = play_and_wait(chan, "vm-mismatch");
2014 vm_change_password(vmu,newpassword);
2015 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2016 cmd = play_and_wait(chan,"vm-passchanged");
2022 cmd = play_and_wait(chan,"vm-options");
2024 cmd = ast_waitfordigit(chan,6000);
2036 static int vm_execmain(struct ast_channel *chan, void *data)
2038 /* XXX This is, admittedly, some pretty horrendus code. For some
2039 reason it just seemed a lot easier to do with GOTO's. I feel
2040 like I'm back in my GWBASIC days. XXX */
2045 struct localuser *u;
2046 char prefixstr[80] ="";
2047 char empty[80] = "";
2051 char tmp[256], *ext;
2052 char fmtc[256] = "";
2054 struct vm_state vms;
2056 struct ast_vm_user *vmu = NULL, vmus;
2060 memset(&vms, 0, sizeof(vms));
2061 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2062 if (chan->_state != AST_STATE_UP)
2065 if (data && strlen(data)) {
2066 strncpy(tmp, data, sizeof(tmp) - 1);
2071 /* We should skip the user's password */
2076 /* We should prefix the mailbox with the supplied data */
2082 context = strchr(ext, '@');
2089 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2091 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2092 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2099 /* If ADSI is supported, setup login screen */
2100 adsi_begin(chan, &useadsi);
2101 if (!skipuser && useadsi)
2103 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2104 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2108 /* Authenticate them and get their mailbox/password */
2110 while (!valid && (logretries < maxlogins)) {
2111 /* Prompt for, and read in the username */
2112 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2113 ast_log(LOG_WARNING, "Couldn't read username\n");
2116 if (!strlen(vms.username)) {
2117 if (option_verbose > 2)
2118 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2123 adsi_password(chan);
2124 if (ast_streamfile(chan, "vm-password", chan->language)) {
2125 ast_log(LOG_WARNING, "Unable to stream password file\n");
2128 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2129 ast_log(LOG_WARNING, "Unable to read password\n");
2133 char fullusername[80] = "";
2134 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2135 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2136 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2139 vmu = find_user(&vmus, context, vms.username);
2140 if (vmu && !strcmp(vmu->password, password))
2143 if (option_verbose > 2)
2144 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2146 strncpy(vms.username, empty, sizeof(vms.username) -1);
2151 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2156 if (!valid && (logretries >= maxlogins)) {
2157 ast_stopstream(chan);
2158 res = play_and_wait(chan, "vm-goodbye");
2164 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2165 mkdir(vms.curdir, 0700);
2166 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2167 mkdir(vms.curdir, 0700);
2168 /* Retrieve old and new message counts */
2169 open_mailbox(&vms, vmu, 1);
2170 vms.oldmessages = vms.lastmsg + 1;
2171 /* Start in INBOX */
2172 open_mailbox(&vms, vmu, 0);
2173 vms.newmessages = vms.lastmsg + 1;
2176 /* Select proper mailbox FIRST!! */
2177 if (!vms.newmessages && vms.oldmessages) {
2178 /* If we only have old messages start here */
2179 open_mailbox(&vms, vmu, 1);
2183 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2185 cmd = vm_intro(chan, &vms);
2188 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2195 if (vms.lastmsg > -1) {
2196 cmd = play_message(chan, &vms, vms.curmsg);
2198 cmd = play_and_wait(chan, "vm-youhave");
2200 cmd = play_and_wait(chan, "vm-no");
2202 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2203 cmd = play_and_wait(chan, vms.fn);
2206 cmd = play_and_wait(chan, "vm-messages");
2209 case '2': /* Change folders */
2211 adsi_folders(chan, 0, "Change to folder...");
2212 cmd = get_folder2(chan, "vm-changeto", 0);
2215 } else if (cmd > 0) {
2217 close_mailbox(&vms, vmu);
2218 open_mailbox(&vms, vmu, cmd);
2222 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2224 cmd = play_and_wait(chan, vms.vmbox);
2226 cmd = play_and_wait(chan, "vm-messages");
2232 cmd = play_message(chan, &vms, vms.curmsg);
2234 cmd = play_and_wait(chan, "vm-nomore");
2238 if (vms.curmsg < vms.lastmsg) {
2240 cmd = play_message(chan, &vms, vms.curmsg);
2242 cmd = play_and_wait(chan, "vm-nomore");
2246 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2248 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2249 if (vms.deleted[vms.curmsg])
2250 cmd = play_and_wait(chan, "vm-deleted");
2252 cmd = play_and_wait(chan, "vm-undeleted");
2255 if(vms.lastmsg > -1)
2256 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2260 adsi_folders(chan, 1, "Save to folder...");
2261 cmd = get_folder2(chan, "vm-savefolder", 1);
2262 box = 0; /* Shut up compiler */
2266 } else if (cmd > 0) {
2267 box = cmd = cmd - '0';
2268 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2269 vms.deleted[vms.curmsg]=1;
2271 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2273 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2275 cmd = play_and_wait(chan, "vm-message");
2277 cmd = say_and_wait(chan, vms.curmsg + 1);
2279 cmd = play_and_wait(chan, "vm-savedto");
2281 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2282 cmd = play_and_wait(chan, vms.fn);
2285 cmd = play_and_wait(chan, "vm-messages");
2288 if (!vms.starting) {
2289 cmd = play_and_wait(chan, "vm-onefor");
2291 cmd = play_and_wait(chan, vms.vmbox);
2293 cmd = play_and_wait(chan, "vm-messages");
2295 cmd = play_and_wait(chan, "vm-opts");
2300 cmd = vm_options(chan, vmu, &vms, vmfmts);
2302 default: /* Nothing */
2303 cmd = vm_instructions(chan, &vms);
2307 if ((cmd == 't') || (cmd == '#')) {
2317 ast_stopstream(chan);
2319 res = play_and_wait(chan, "vm-goodbye");
2323 adsi_unload_session(chan);
2326 close_mailbox(&vms, vmu);
2330 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2332 LOCAL_USER_REMOVE(u);
2337 static int vm_exec(struct ast_channel *chan, void *data)
2339 int res=0, silent=0, busy=0, unavail=0;
2340 struct localuser *u;
2341 char tmp[256], *ext;
2344 if (chan->_state != AST_STATE_UP)
2347 strncpy(tmp, data, sizeof(tmp) - 1);
2349 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2360 } else if (*ext == 'b') {
2363 } else if (*ext == 'u') {
2369 res = leave_voicemail(chan, ext, silent, busy, unavail);
2370 LOCAL_USER_REMOVE(u);
2374 static int append_mailbox(char *context, char *mbox, char *data)
2376 /* Assumes lock is already held */
2380 struct ast_vm_user *vmu;
2381 strncpy(tmp, data, sizeof(tmp));
2382 vmu = malloc(sizeof(struct ast_vm_user));
2384 memset(vmu, 0, sizeof(struct ast_vm_user));
2385 strncpy(vmu->context, context, sizeof(vmu->context));
2386 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2388 if ((s = strsep(&stringp, ",")))
2389 strncpy(vmu->password, s, sizeof(vmu->password));
2390 if ((s = strsep(&stringp, ",")))
2391 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2392 if ((s = strsep(&stringp, ",")))
2393 strncpy(vmu->email, s, sizeof(vmu->email));
2394 if ((s = strsep(&stringp, ",")))
2395 strncpy(vmu->pager, s, sizeof(vmu->pager));
2396 if ((s = strsep(&stringp, ",")))
2397 strncpy(vmu->options, s, sizeof(vmu->options));
2408 static int load_config(void)
2410 struct ast_vm_user *cur, *l;
2411 struct ast_config *cfg;
2413 struct ast_variable *var;
2422 cfg = ast_load(VOICEMAIL_CONFIG);
2423 ast_pthread_mutex_lock(&vmlock);
2433 /* General settings */
2434 attach_voicemail = 1;
2435 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
2437 attach_voicemail = ast_true(astattach);
2439 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2440 maxsilence = atoi(silencestr);
2445 silencethreshold = 256;
2446 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2447 silencethreshold = atoi(thresholdstr);
2449 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
2450 astemail = ASTERISK_USERNAME;
2451 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2454 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2455 if (sscanf(s, "%d", &x) == 1) {
2458 ast_log(LOG_WARNING, "Invalid max message time length\n");
2461 fmt = ast_variable_retrieve(cfg, "general", "format");
2464 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2467 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2468 if (sscanf(s, "%d", &x) == 1) {
2471 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2475 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2476 if (sscanf(s, "%d", &x) == 1) {
2479 ast_log(LOG_WARNING, "Invalid skipms value\n");
2484 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2485 if (sscanf(s, "%d", &x) == 1) {
2488 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2493 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2494 strcpy(dbuser, "test");
2498 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2499 strcpy(dbpass, "test");
2503 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2504 strcpy(dbname, "vmdb");
2509 cat = ast_category_browse(cfg, NULL);
2511 if (strcasecmp(cat, "general")) {
2512 /* Process mailboxes in this context */
2513 var = ast_variable_browse(cfg, cat);
2515 append_mailbox(cat, var->name, var->value);
2519 cat = ast_category_browse(cfg, cat);
2523 ast_pthread_mutex_unlock(&vmlock);
2526 ast_pthread_mutex_unlock(&vmlock);
2527 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2534 return(load_config());
2537 int unload_module(void)
2540 STANDARD_HANGUP_LOCALUSERS;
2541 res = ast_unregister_application(app);
2542 res |= ast_unregister_application(app2);
2549 int load_module(void)
2552 if ((res=load_config())) {
2556 if ((res=mysql_login())) {
2560 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2562 res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2566 char *description(void)
2574 STANDARD_USECOUNT(res);
2580 return ASTERISK_GPL_KEY;