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/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
39 /* we define USESQLVM when we have MySQL or POSTGRES */
41 #include <mysql/mysql.h>
47 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
49 #include <postgresql/libpq-fe.h>
54 static inline int sql_init(void) { return 0; }
55 static inline void sql_close(void) { }
59 #include "../asterisk.h"
60 #include "../astconf.h"
62 #define COMMAND_TIMEOUT 5000
64 #define VOICEMAIL_CONFIG "voicemail.conf"
65 #define ASTERISK_USERNAME "asterisk"
67 #define SENDMAIL "/usr/sbin/sendmail -t"
69 #define INTRO "vm-intro"
72 #define MAX_OTHER_FORMATS 10
74 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
76 #define BASEMAXINLINE 256
77 #define BASELINELEN 72
78 #define BASEMAXINLINE 256
81 #define MAX_DATETIME_FORMAT 512
82 #define DIGITS_DIR AST_SOUNDS "/digits/"
88 unsigned char iobuf[BASEMAXINLINE];
102 struct ast_vm_user *next;
108 char msg_format[512];
109 struct vm_zone *next;
112 static char *tdesc = "Comedian Mail (Voicemail System)";
114 static char *adapp = "CoMa";
116 static char *adsec = "_AST";
118 static char *addesc = "Comedian Mail";
120 static int adver = 1;
122 static char *synopsis_vm =
123 "Leave a voicemail message";
125 static char *descrip_vm =
126 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
127 "extension (must be configured in voicemail.conf). If the extension is\n"
128 "preceded by an 's' then instructions for leaving the message will be\n"
129 "skipped. If the extension is preceeded by 'u' then the \"unavailable\"\n"
130 "message will be played (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it\n"
131 "exists. If the extension is preceeded by a 'b' then the the busy message\n"
132 "will be played (that is, busy instead of unavail).\n"
133 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
134 "Otherwise, it returns 0.\n";
136 static char *synopsis_vmain =
137 "Enter voicemail system";
139 static char *descrip_vmain =
140 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
141 "for the checking of voicemail. The mailbox can be passed as the option,\n"
142 "which will stop the voicemail system from prompting the user for the mailbox.\n"
143 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
144 "a context is specified, logins are considered in that context only.\n"
145 "Returns -1 if the user hangs up or 0 otherwise.\n";
147 /* Leave a message */
148 static char *app = "VoiceMail2";
150 /* Check mail, control, etc */
151 static char *app2 = "VoiceMailMain2";
153 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
154 struct ast_vm_user *users;
155 struct ast_vm_user *usersl;
156 struct vm_zone *zones = NULL;
157 struct vm_zone *zonesl = NULL;
158 static int attach_voicemail;
159 static int maxsilence;
160 static int silencethreshold = 128;
161 static char serveremail[80];
162 static char vmfmts[80];
163 static int vmmaxmessage;
166 static int maxlogins;
168 static char *emailbody = NULL;
169 static int pbxskip = 0;
170 static char fromstring[100];
171 static char emailtitle[100];
177 static void apply_options(struct ast_vm_user *vmu, char *options)
179 /* Destructively Parse options and apply */
180 char *stringp = ast_strdupa(options);
183 while((s = strsep(&stringp, "|"))) {
185 if ((var = strsep(&value, "=")) && value) {
186 if (!strcasecmp(var, "attach")) {
191 } else if (!strcasecmp(var, "serveremail")) {
192 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
193 } else if (!strcasecmp(var, "tz")) {
194 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
202 #include "mysql-vm-routines.h"
209 ast_mutex_t postgreslock;
211 static int sql_init(void)
213 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
214 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
216 dbhandler=PQconnectdb(dboption);
217 if (PQstatus(dbhandler) == CONNECTION_BAD) {
218 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
221 ast_mutex_init(&postgreslock);
223 /* fprintf(stderr,"postgres login OK\n"); */
227 static void sql_close(void)
233 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
241 char options[160] = "";
242 struct ast_vm_user *retval;
244 retval=malloc(sizeof(struct ast_vm_user));
246 /* fprintf(stderr,"postgres find_user:\n"); */
249 *retval->mailbox='\0';
250 *retval->context='\0';
251 *retval->password='\0';
252 *retval->fullname='\0';
255 *retval->serveremail='\0';
260 strcpy(retval->mailbox, mailbox);
263 strcpy(retval->context, context);
266 if (*retval->context) {
267 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", context, mailbox);
269 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE mailbox='%s'", mailbox);
271 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
272 ast_mutex_lock(&postgreslock);
273 PGSQLres=PQexec(dbhandler,query);
274 if (PGSQLres!=NULL) {
275 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
276 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
277 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
279 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
282 ast_mutex_unlock(&postgreslock);
286 numFields = PQnfields(PGSQLres);
287 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
288 if (PQntuples(PGSQLres) != 1) {
289 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
291 ast_mutex_unlock(&postgreslock);
295 for (i=0; i<numFields; i++) {
296 fname = PQfname(PGSQLres,i);
297 if (!strcmp(fname, "password")) {
298 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
299 } else if (!strcmp(fname, "fullname")) {
300 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
301 } else if (!strcmp(fname, "email")) {
302 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
303 } else if (!strcmp(fname, "pager")) {
304 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
305 } else if (!strcmp(fname, "options")) {
306 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
307 apply_options(retval, options);
312 ast_mutex_unlock(&postgreslock);
316 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
317 ast_mutex_unlock(&postgreslock);
322 } /* malloc() retval */
327 static void vm_change_password(struct ast_vm_user *vmu, char *password)
332 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->context, vmu->mailbox, vmu->password);
334 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
336 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
337 ast_mutex_lock(&postgreslock);
338 PQexec(dbhandler, query);
339 strcpy(vmu->password, password);
340 ast_mutex_unlock(&postgreslock);
343 static void reset_user_pw(char *context, char *mailbox, char *password)
348 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
350 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
352 ast_mutex_lock(&postgreslock);
353 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
354 PQexec(dbhandler, query);
355 ast_mutex_unlock(&postgreslock);
358 #endif /* Postgres */
361 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
363 /* This function could be made to generate one from a database, too */
364 struct ast_vm_user *vmu=NULL, *cur;
365 ast_mutex_lock(&vmlock);
368 if ((!context || !strcasecmp(context, cur->context)) &&
369 (!strcasecmp(mailbox, cur->mailbox)))
377 /* Make a copy, so that on a reload, we have no race */
378 vmu = malloc(sizeof(struct ast_vm_user));
380 memcpy(vmu, cur, sizeof(struct ast_vm_user));
388 ast_mutex_unlock(&vmlock);
392 static int reset_user_pw(char *context, char *mailbox, char *newpass)
394 /* This function could be made to generate one from a database, too */
395 struct ast_vm_user *cur;
397 ast_mutex_lock(&vmlock);
400 if ((!context || !strcasecmp(context, cur->context)) &&
401 (!strcasecmp(mailbox, cur->mailbox)))
406 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
409 ast_mutex_unlock(&vmlock);
413 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
415 /* There's probably a better way of doing this. */
416 /* That's why I've put the password change in a separate function. */
417 /* This could also be done with a database function */
423 char tmpin[AST_CONFIG_MAX_PATH];
424 char tmpout[AST_CONFIG_MAX_PATH];
425 char *user, *pass, *rest, *trim;
426 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
427 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
428 configin = fopen((char *)tmpin,"r");
430 configout = fopen((char *)tmpout,"w+");
433 if(!configin || !configout) {
437 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
441 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
445 while (!feof(configin)) {
446 /* Read in the line */
447 fgets(inbuf, sizeof(inbuf), configin);
448 if (!feof(configin)) {
449 /* Make a backup of it */
450 memcpy(orig, inbuf, sizeof(orig));
451 /* Strip trailing \n and comment */
452 inbuf[strlen(inbuf) - 1] = '\0';
453 user = strchr(inbuf, ';');
459 pass = strchr(user, '=');
462 while(*trim && *trim < 33) {
472 while(*pass && *pass < 33)
476 rest = strchr(pass,',');
483 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
484 /* This is the line */
486 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
488 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
491 /* Put it back like it was */
492 fprintf(configout, orig);
499 unlink((char *)tmpin);
500 rename((char *)tmpout,(char *)tmpin);
501 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
502 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
506 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
508 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
511 static int make_file(char *dest, int len, char *dir, int num)
513 return snprintf(dest, len, "%s/msg%04d", dir, num);
517 inbuf(struct baseio *bio, FILE *fi)
524 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
539 inchar(struct baseio *bio, FILE *fi)
541 if(bio->iocp>=bio->iolen)
545 return bio->iobuf[bio->iocp++];
549 ochar(struct baseio *bio, int c, FILE *so)
551 if(bio->linelength>=BASELINELEN) {
552 if(fputs(eol,so)==EOF)
558 if(putc(((unsigned char)c),so)==EOF)
566 static int base_encode(char *filename, FILE *so)
568 unsigned char dtable[BASEMAXINLINE];
573 memset(&bio, 0, sizeof(bio));
574 bio.iocp = BASEMAXINLINE;
576 if ( !(fi = fopen(filename, "rb"))) {
577 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
585 dtable[26+i+9]= 'j'+i;
589 dtable[26+i+18]= 's'+i;
598 unsigned char igroup[3],ogroup[4];
601 igroup[0]= igroup[1]= igroup[2]= 0;
604 if ( (c = inchar(&bio, fi)) == EOF) {
609 igroup[n]= (unsigned char)c;
613 ogroup[0]= dtable[igroup[0]>>2];
614 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
615 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
616 ogroup[3]= dtable[igroup[2]&0x3F];
626 ochar(&bio, ogroup[i], so);
630 if(fputs(eol,so)==EOF)
638 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration, int attach_user_voicemail)
649 struct vm_zone *the_zone = NULL;
651 if (!strcmp(format, "wav49"))
653 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
654 p = popen(SENDMAIL, "w");
656 gethostname(host, sizeof(host));
657 if (strchr(srcemail, '@'))
658 strncpy(who, srcemail, sizeof(who)-1);
660 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
662 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
665 /* Does this user have a timezone specified? */
666 if (strlen(vmu->zonetag)) {
667 /* Find the zone in the list */
671 if (!strcmp(z->name, vmu->zonetag)) {
680 ast_localtime(&t,&tm,the_zone->timezone);
682 ast_localtime(&t,&tm,NULL);
683 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
684 fprintf(p, "Date: %s\n", date);
687 fprintf(p, "From: %s <%s>\n", fromstring, who);
689 fprintf(p, "From: Asterisk PBX <%s>\n", who);
690 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
694 fprintf(p, emailtitle, msgnum, mailbox) ;
699 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
701 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
702 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
703 fprintf(p, "MIME-Version: 1.0\n");
704 if (attach_user_voicemail) {
706 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
708 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
710 fprintf(p, "--%s\n", bound);
712 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
713 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
715 struct ast_channel *ast = ast_channel_alloc(0);
718 int vmlen = strlen(emailbody)*3 + 200;
719 if ((passdata = alloca(vmlen))) {
720 memset(passdata, 0, vmlen);
721 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
722 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
723 sprintf(passdata,"%d",msgnum);
724 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
725 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
726 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
727 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
728 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
729 fprintf(p, "%s\n",passdata);
730 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
731 ast_channel_free(ast);
732 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
734 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
736 "in mailbox %s from %s, on %s so you might\n"
737 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
738 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
740 if (attach_user_voicemail) {
741 fprintf(p, "--%s\n", bound);
742 fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum + 1, format);
743 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
744 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
745 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
747 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
748 base_encode(fname, p);
749 fprintf(p, "\n\n--%s--\n.\n", bound);
753 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
759 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
768 struct vm_zone *the_zone = NULL;
769 p = popen(SENDMAIL, "w");
772 gethostname(host, sizeof(host));
773 if (strchr(srcemail, '@'))
774 strncpy(who, srcemail, sizeof(who)-1);
776 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
778 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
781 /* Does this user have a timezone specified? */
782 if (strlen(vmu->zonetag)) {
783 /* Find the zone in the list */
787 if (!strcmp(z->name, vmu->zonetag)) {
796 ast_localtime(&t,&tm,the_zone->timezone);
798 ast_localtime(&t,&tm,NULL);
800 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
801 fprintf(p, "Date: %s\n", date);
802 fprintf(p, "From: Asterisk PBX <%s>\n", who);
803 fprintf(p, "To: %s\n", pager);
804 fprintf(p, "Subject: New VM\n\n");
805 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
806 fprintf(p, "New %s long msg in box %s\n"
807 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
810 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
816 static int get_date(char *s, int len)
822 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
825 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
829 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
830 if (ast_fileexists(fn, NULL, NULL) > 0) {
831 res = ast_streamfile(chan, fn, chan->language);
834 res = ast_waitstream(chan, ecodes);
838 res = ast_streamfile(chan, "vm-theperson", chan->language);
841 res = ast_waitstream(chan, ecodes);
844 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
849 res = ast_streamfile(chan, "vm-isonphone", chan->language);
851 res = ast_streamfile(chan, "vm-isunavail", chan->language);
854 res = ast_waitstream(chan, ecodes);
858 static int play_and_wait(struct ast_channel *chan, char *fn)
861 d = ast_streamfile(chan, fn, chan->language);
864 d = ast_waitstream(chan, AST_DIGIT_ANY);
868 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
872 int x, fmtcnt=1, res=-1,outmsg=0;
874 struct ast_filestream *others[MAX_OTHER_FORMATS];
875 char *sfmt[MAX_OTHER_FORMATS];
878 struct ast_dsp *sildet; /* silence detector dsp */
879 int totalsilence = 0;
881 int gotsilence = 0; /* did we timeout for silence? */
884 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
885 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
888 d = play_and_wait(chan, playfile);
890 d = ast_streamfile(chan, "beep",chan->language);
892 d = ast_waitstream(chan,"");
897 fmts = ast_strdupa(fmt);
900 strsep(&stringp, "|");
901 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
902 sfmt[0] = ast_strdupa(fmts);
904 while((fmt = strsep(&stringp, "|"))) {
905 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
906 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
909 sfmt[fmtcnt++] = ast_strdupa(fmt);
914 for (x=0;x<fmtcnt;x++) {
915 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
916 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
923 sildet = ast_dsp_new(); //Create the silence detector
925 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
928 ast_dsp_set_threshold(sildet, silencethreshold);
930 if (maxsilence > 0) {
931 rfmt = chan->readformat;
932 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
934 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
940 /* Loop forever, writing the packets we read to the writer(s), until
941 we read a # or get a hangup */
944 res = ast_waitfor(chan, 2000);
946 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
947 /* Try one more time in case of masq */
948 res = ast_waitfor(chan, 2000);
950 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
962 if (f->frametype == AST_FRAME_VOICE) {
963 /* write each format */
964 for (x=0;x<fmtcnt;x++) {
965 res = ast_writestream(others[x], f);
968 /* Silence Detection */
969 if (maxsilence > 0) {
971 ast_dsp_silence(sildet, f, &dspsilence);
973 totalsilence = dspsilence;
977 if (totalsilence > maxsilence) {
978 /* Ended happily with silence */
985 /* Exit on any error */
987 ast_log(LOG_WARNING, "Error writing frame\n");
991 } else if (f->frametype == AST_FRAME_VIDEO) {
992 /* Write only once */
993 ast_writestream(others[0], f);
994 } else if (f->frametype == AST_FRAME_DTMF) {
995 if (f->subclass == '#') {
996 if (option_verbose > 2)
997 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1006 if (maxtime < (end - start)) {
1007 if (option_verbose > 2)
1008 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1017 if (option_verbose > 2)
1018 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1023 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1026 for (x=0;x<fmtcnt;x++) {
1030 ast_stream_rewind(others[x], totalsilence-200);
1032 ast_stream_rewind(others[x], 200);
1033 ast_truncstream(others[x]);
1034 ast_closestream(others[x]);
1037 if (ast_set_read_format(chan, rfmt)) {
1038 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1043 /* Let them know it worked */
1044 ast_streamfile(chan, "vm-msgsaved", chan->language);
1045 ast_waitstream(chan, "");
1053 static void free_user(struct ast_vm_user *vmu)
1059 static void free_zone(struct vm_zone *z)
1064 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1074 char prefile[256]="";
1082 struct ast_vm_user *vmu;
1083 struct ast_vm_user svm;
1085 strncpy(tmp, ext, sizeof(tmp) - 1);
1087 context = strchr(tmp, '@');
1093 if ((vmu = find_user(&svm, context, ext))) {
1094 /* Setup pre-file if appropriate */
1096 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1098 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1099 make_dir(dir, sizeof(dir), vmu->context, "", "");
1100 /* It's easier just to try to make it than to check for its existence */
1101 if (mkdir(dir, 0700) && (errno != EEXIST))
1102 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1103 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1104 /* It's easier just to try to make it than to check for its existence */
1105 if (mkdir(dir, 0700) && (errno != EEXIST))
1106 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1107 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1108 if (mkdir(dir, 0700) && (errno != EEXIST))
1109 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1110 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1112 /* Play the beginning intro if desired */
1113 if (strlen(prefile)) {
1114 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1115 if (ast_streamfile(chan, prefile, chan->language) > -1)
1116 res = ast_waitstream(chan, "#0");
1118 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1119 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1122 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1128 /* On a '#' we skip the instructions */
1132 if (!res && !silent) {
1133 res = ast_streamfile(chan, INTRO, chan->language);
1135 res = ast_waitstream(chan, ecodes);
1141 /* Check for a '0' here */
1143 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1144 if (strlen(chan->macrocontext))
1145 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1151 /* Unless we're *really* silent, try to send the beep */
1152 res = ast_streamfile(chan, "beep", chan->language);
1154 res = ast_waitstream(chan, "");
1160 /* The meat of recording the message... All the announcements and beeps have been played*/
1161 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1165 make_file(fn, sizeof(fn), dir, msgnum);
1166 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1167 (chan->callerid ? chan->callerid : "Unknown"),
1168 vmu->fullname, ext, chan->name);
1169 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1172 } while(msgnum < MAXMSG);
1173 if (msgnum < MAXMSG) {
1174 /* Store information */
1175 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1176 txt = fopen(txtfile, "w+");
1178 get_date(date, sizeof(date));
1182 "; Message Information file\n"
1198 chan->callerid ? chan->callerid : "Unknown",
1199 date, (long)time(NULL));
1202 ast_log(LOG_WARNING, "Error opening text file for output\n");
1203 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1206 txt = fopen(txtfile, "a");
1209 fprintf(txt, "duration=%ld\n", (long)(end-start));
1213 strsep(&stringp, "|");
1214 /* Send e-mail if applicable */
1215 if (strlen(vmu->email)) {
1216 int attach_user_voicemail = attach_voicemail;
1217 char *myserveremail = serveremail;
1218 if (vmu->attach > -1)
1219 attach_user_voicemail = vmu->attach;
1220 if (strlen(vmu->serveremail))
1221 myserveremail = vmu->serveremail;
1222 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1224 if (strlen(vmu->pager)) {
1225 char *myserveremail = serveremail;
1226 if (strlen(vmu->serveremail))
1227 myserveremail = vmu->serveremail;
1228 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1231 ast_log(LOG_WARNING, "No more messages possible\n");
1233 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1236 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1237 /* Leave voicemail for someone */
1238 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1242 static char *mbox(int id)
1270 static int count_messages(char *dir)
1274 for (x=0;x<MAXMSG;x++) {
1275 make_file(fn, sizeof(fn), dir, x);
1276 if (ast_fileexists(fn, NULL, NULL) < 1)
1282 static int say_and_wait(struct ast_channel *chan, int num)
1285 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1289 static int copy(char *infile, char *outfile)
1296 if ((ifd = open(infile, O_RDONLY)) < 0) {
1297 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1300 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1301 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1306 len = read(ifd, buf, sizeof(buf));
1308 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1314 res = write(ofd, buf, len);
1316 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1328 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1335 char *dbox = mbox(box);
1337 make_file(sfn, sizeof(sfn), dir, msg);
1338 make_dir(ddir, sizeof(ddir), context, username, dbox);
1340 for (x=0;x<MAXMSG;x++) {
1341 make_file(dfn, sizeof(dfn), ddir, x);
1342 if (ast_fileexists(dfn, NULL, NULL) < 0)
1347 ast_filecopy(sfn, dfn, NULL);
1348 if (strcmp(sfn, dfn)) {
1349 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1350 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1356 static int adsi_logo(unsigned char *buf)
1359 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1360 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1364 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1372 bytes += adsi_data_mode(buf + bytes);
1373 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1376 bytes += adsi_logo(buf);
1377 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1379 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1381 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1382 bytes += adsi_data_mode(buf + bytes);
1383 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1385 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1387 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1388 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1389 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1390 bytes += adsi_voice_mode(buf + bytes, 0);
1391 bytes += adsi_voice_mode(buf + bytes, 0);
1392 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1399 bytes += adsi_logo(buf);
1400 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1401 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1402 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1403 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1406 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1407 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1408 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1409 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1410 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1411 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1412 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1415 /* Add another dot */
1417 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1418 bytes += adsi_voice_mode(buf + bytes, 0);
1420 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1421 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1425 /* These buttons we load but don't use yet */
1426 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1427 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1428 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1429 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1430 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1431 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1432 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1435 /* Add another dot */
1437 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1438 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1439 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1444 snprintf(num, sizeof(num), "%d", x);
1445 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1447 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1448 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1451 /* Add another dot */
1453 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1454 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1455 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1458 if (adsi_end_download(chan)) {
1460 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1461 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1462 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1463 bytes += adsi_voice_mode(buf + bytes, 0);
1464 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1468 bytes += adsi_download_disconnect(buf + bytes);
1469 bytes += adsi_voice_mode(buf + bytes, 0);
1470 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1472 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1477 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1478 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1480 ast_log(LOG_DEBUG, "Restarting session...\n");
1483 /* Load the session now */
1484 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1486 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1488 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1490 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1494 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1497 if (!adsi_available(chan))
1499 x = adsi_load_session(chan, adapp, adver, 1);
1503 if (adsi_load_vmail(chan, useadsi)) {
1504 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1511 static void adsi_login(struct ast_channel *chan)
1515 unsigned char keys[8];
1517 if (!adsi_available(chan))
1522 /* Set one key for next */
1523 keys[3] = ADSI_KEY_APPS + 3;
1525 bytes += adsi_logo(buf + bytes);
1526 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1527 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1528 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1529 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1530 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1531 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1532 bytes += adsi_set_keys(buf + bytes, keys);
1533 bytes += adsi_voice_mode(buf + bytes, 0);
1534 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1537 static void adsi_password(struct ast_channel *chan)
1541 unsigned char keys[8];
1543 if (!adsi_available(chan))
1548 /* Set one key for next */
1549 keys[3] = ADSI_KEY_APPS + 3;
1550 bytes += adsi_voice_mode(buf + bytes, 0);
1553 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1554 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1555 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1556 bytes += adsi_set_keys(buf + bytes, keys);
1557 bytes += adsi_voice_mode(buf + bytes, 0);
1558 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1561 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1565 unsigned char keys[8];
1568 if (!adsi_available(chan))
1572 y = ADSI_KEY_APPS + 12 + start + x;
1573 if (y > ADSI_KEY_APPS + 12 + 4)
1575 keys[x] = ADSI_KEY_SKT | y;
1577 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1581 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1582 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1583 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1584 bytes += adsi_set_keys(buf + bytes, keys);
1585 bytes += adsi_voice_mode(buf + bytes, 0);
1587 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1590 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1593 char buf[256], buf1[256], buf2[256];
1599 char datetime[21]="";
1602 unsigned char keys[8];
1606 if (!adsi_available(chan))
1609 /* Retrieve important info */
1610 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1611 f = fopen(fn2, "r");
1614 fgets(buf, sizeof(buf), f);
1618 strsep(&stringp, "=");
1619 val = strsep(&stringp, "=");
1620 if (val && strlen(val)) {
1621 if (!strcmp(buf, "callerid"))
1622 strncpy(cid, val, sizeof(cid) - 1);
1623 if (!strcmp(buf, "origdate"))
1624 strncpy(datetime, val, sizeof(datetime) - 1);
1630 /* New meaning for keys */
1632 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1634 bytes += adsi_voice_mode(buf + bytes, 0);
1639 /* No prev key, provide "Folder" instead */
1640 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1643 /* If last message ... */
1645 /* but not only message, provide "Folder" instead */
1646 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1647 bytes += adsi_voice_mode(buf + bytes, 0);
1650 /* Otherwise if only message, leave blank */
1656 ast_callerid_parse(cid, &name, &num);
1660 name = "Unknown Caller";
1662 /* If deleted, show "undeleted" */
1663 bytes += adsi_voice_mode(buf + bytes, 0);
1666 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1669 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1670 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1671 strcasecmp(folder, "INBOX") ? " Messages" : "");
1672 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1674 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1675 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1676 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1677 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1678 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1679 bytes += adsi_set_keys(buf + bytes, keys);
1680 bytes += adsi_voice_mode(buf + bytes, 0);
1682 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1685 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1689 unsigned char keys[8];
1693 if (!adsi_available(chan))
1696 /* New meaning for keys */
1698 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1704 /* No prev key, provide "Folder" instead */
1705 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1708 /* If last message ... */
1710 /* but not only message, provide "Folder" instead */
1711 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1713 /* Otherwise if only message, leave blank */
1718 /* If deleted, show "undeleted" */
1720 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1723 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1724 bytes += adsi_set_keys(buf + bytes, keys);
1725 bytes += adsi_voice_mode(buf + bytes, 0);
1727 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1730 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1732 char buf[256], buf1[256], buf2[256];
1734 unsigned char keys[8];
1737 char *newm = (new == 1) ? "message" : "messages";
1738 char *oldm = (old == 1) ? "message" : "messages";
1739 if (!adsi_available(chan))
1742 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1744 strcat(buf1, " and");
1745 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1747 snprintf(buf2, sizeof(buf2), "%s.", newm);
1750 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1751 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1753 strcpy(buf1, "You have no messages.");
1756 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1757 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1758 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1761 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1765 /* Don't let them listen if there are none */
1768 bytes += adsi_set_keys(buf + bytes, keys);
1770 bytes += adsi_voice_mode(buf + bytes, 0);
1772 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1775 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1777 char buf[256], buf1[256], buf2[256];
1779 unsigned char keys[8];
1782 char *mess = (messages == 1) ? "message" : "messages";
1784 if (!adsi_available(chan))
1787 /* Original command keys */
1789 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1797 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1798 strcasecmp(folder, "INBOX") ? " folder" : "");
1801 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1803 strcpy(buf2, "no messages.");
1804 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1805 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1806 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1807 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1808 bytes += adsi_set_keys(buf + bytes, keys);
1810 bytes += adsi_voice_mode(buf + bytes, 0);
1812 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1816 static void adsi_clear(struct ast_channel *chan)
1820 if (!adsi_available(chan))
1822 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1823 bytes += adsi_voice_mode(buf + bytes, 0);
1825 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1828 static void adsi_goodbye(struct ast_channel *chan)
1833 if (!adsi_available(chan))
1835 bytes += adsi_logo(buf + bytes);
1836 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1837 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1838 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1839 bytes += adsi_voice_mode(buf + bytes, 0);
1841 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1844 static int get_folder(struct ast_channel *chan, int start)
1849 d = play_and_wait(chan, "vm-press");
1852 for (x = start; x< 5; x++) {
1853 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1855 d = play_and_wait(chan, "vm-for");
1858 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1859 d = play_and_wait(chan, fn);
1862 d = play_and_wait(chan, "vm-messages");
1865 d = ast_waitfordigit(chan, 500);
1869 d = play_and_wait(chan, "vm-tocancel");
1872 d = ast_waitfordigit(chan, 4000);
1876 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1879 res = play_and_wait(chan, fn);
1880 while (((res < '0') || (res > '9')) &&
1881 (res != '#') && (res >= 0)) {
1882 res = get_folder(chan, 0);
1888 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1895 struct ast_config *mif;
1900 struct ast_vm_user *receiver, srec;
1905 res = ast_streamfile(chan, "vm-extension", chan->language);
1908 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1910 if ((receiver = find_user(&srec, context, username))) {
1911 /* if (play_and_wait(chan, "vm-savedto"))
1915 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1916 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1917 ast_log(LOG_DEBUG, sys);
1920 todircount = count_messages(todir);
1921 strncpy(tmp, fmt, sizeof(tmp));
1923 while((s = strsep(&stringp, "|"))) {
1924 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1925 ast_log(LOG_DEBUG, sys);
1928 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1929 ast_log(LOG_DEBUG, sys);
1931 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1933 /* load the information on the source message so we can send an e-mail like a new message */
1934 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1935 if ((mif=ast_load(miffile))) {
1937 /* set callerid and duration variables */
1938 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1939 duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1941 if (strlen(receiver->email)) {
1942 int attach_user_voicemail = attach_voicemail;
1943 char *myserveremail = serveremail;
1944 if (receiver->attach > -1)
1945 attach_user_voicemail = receiver->attach;
1946 if (strlen(receiver->serveremail))
1947 myserveremail = receiver->serveremail;
1948 sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1951 if (strlen(receiver->pager)) {
1952 char *myserveremail = serveremail;
1953 if (strlen(receiver->serveremail))
1954 myserveremail = receiver->serveremail;
1955 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver);
1958 ast_destroy(mif); /* or here */
1960 /* Leave voicemail for someone */
1961 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1963 /* give confirmatopm that the message was saved */
1964 res = play_and_wait(chan, "vm-message");
1966 res = play_and_wait(chan, "vm-saved");
1967 free_user(receiver);
1970 res = play_and_wait(chan, "pbx-invalid");
1983 int deleted[MAXMSG];
1994 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1997 if ((res = ast_streamfile(chan, file, chan->language)))
1998 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2000 res = ast_waitstream(chan, AST_DIGIT_ANY);
2004 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2007 if ((res = ast_streamfile(chan, file, chan->language)))
2008 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2010 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2014 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2017 char filename[256], *origtime;
2018 struct vm_zone *the_zone = NULL;
2019 struct ast_config *msg_cfg;
2023 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2024 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2025 msg_cfg = ast_load(filename);
2027 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2031 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2033 if (sscanf(origtime,"%ld",&tin) < 1) {
2034 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2038 ast_destroy(msg_cfg);
2040 /* Does this user have a timezone specified? */
2041 if (strlen(vmu->zonetag)) {
2042 /* Find the zone in the list */
2046 if (!strcmp(z->name, vmu->zonetag)) {
2054 /* No internal variable parsing for now, so we'll comment it out for the time being */
2056 /* Set the DIFF_* variables */
2057 localtime_r(&t, &time_now);
2058 gettimeofday(&tv_now,NULL);
2059 tnow = tv_now.tv_sec;
2060 localtime_r(&tnow,&time_then);
2062 /* Day difference */
2063 if (time_now.tm_year == time_then.tm_year)
2064 sprintf(temp,"%d",time_now.tm_yday);
2066 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2067 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2069 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2072 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2074 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2076 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2081 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2085 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2086 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2088 res = wait_file2(chan, vms, "vm-first");
2089 else if (msg == vms->lastmsg)
2090 res = wait_file2(chan, vms, "vm-last");
2092 res = wait_file2(chan, vms, "vm-message");
2093 if (msg && (msg != vms->lastmsg)) {
2095 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2100 res = play_message_datetime(chan,vmu,vms);
2103 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2104 vms->heard[msg] = 1;
2105 res = wait_file(chan, vms, vms->fn);
2110 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2112 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2113 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2114 vms->lastmsg = count_messages(vms->curdir) - 1;
2115 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2118 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2121 char ntxt[256] = "";
2123 if (vms->lastmsg > -1) {
2124 /* Get the deleted messages fixed */
2126 for (x=0;x < MAXMSG;x++) {
2127 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2128 /* Save this message. It's not in INBOX or hasn't been heard */
2129 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2130 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2133 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2134 if (strcmp(vms->fn, vms->fn2)) {
2135 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2136 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2137 ast_filerename(vms->fn, vms->fn2, NULL);
2140 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2141 /* Move to old folder before deleting */
2142 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2145 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2146 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2147 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2149 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2150 ast_filedelete(vms->fn, NULL);
2154 memset(vms->deleted, 0, sizeof(vms->deleted));
2155 memset(vms->heard, 0, sizeof(vms->heard));
2158 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2160 /* Introduce messages they have */
2162 res = play_and_wait(chan, "vm-youhave");
2164 if (vms->newmessages) {
2165 res = say_and_wait(chan, vms->newmessages);
2167 res = play_and_wait(chan, "vm-INBOX");
2168 if (vms->oldmessages && !res)
2169 res = play_and_wait(chan, "vm-and");
2171 if ((vms->newmessages == 1))
2172 res = play_and_wait(chan, "vm-message");
2174 res = play_and_wait(chan, "vm-messages");
2178 if (!res && vms->oldmessages) {
2179 res = say_and_wait(chan, vms->oldmessages);
2181 res = play_and_wait(chan, "vm-Old");
2183 if (vms->oldmessages == 1)
2184 res = play_and_wait(chan, "vm-message");
2186 res = play_and_wait(chan, "vm-messages");
2190 if (!vms->oldmessages && !vms->newmessages) {
2191 res = play_and_wait(chan, "vm-no");
2193 res = play_and_wait(chan, "vm-messages");
2200 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2203 /* Play instructions and wait for new command */
2205 if (vms->starting) {
2206 if (vms->lastmsg > -1) {
2207 res = play_and_wait(chan, "vm-onefor");
2209 res = play_and_wait(chan, vms->vmbox);
2211 res = play_and_wait(chan, "vm-messages");
2214 res = play_and_wait(chan, "vm-opts");
2217 res = play_and_wait(chan, "vm-prev");
2219 res = play_and_wait(chan, "vm-repeat");
2220 if (!res && (vms->curmsg != vms->lastmsg))
2221 res = play_and_wait(chan, "vm-next");
2223 if (!vms->deleted[vms->curmsg])
2224 res = play_and_wait(chan, "vm-delete");
2226 res = play_and_wait(chan, "vm-undelete");
2228 res = play_and_wait(chan, "vm-toforward");
2230 res = play_and_wait(chan, "vm-savemessage");
2234 res = play_and_wait(chan, "vm-helpexit");
2236 res = ast_waitfordigit(chan, 6000);
2239 if (vms->repeats > 2) {
2240 res = play_and_wait(chan, "vm-goodbye");
2249 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2253 char newpassword[80] = "";
2254 char newpassword2[80] = "";
2255 char prefile[256]="";
2259 if (adsi_available(chan))
2261 bytes += adsi_logo(buf + bytes);
2262 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2263 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2264 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2265 bytes += adsi_voice_mode(buf + bytes, 0);
2266 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2268 while((cmd >= 0) && (cmd != 't')) {
2273 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2274 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2277 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2278 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2281 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2282 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2285 newpassword[1] = '\0';
2286 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2289 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2292 newpassword2[1] = '\0';
2293 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2297 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2300 if (strcmp(newpassword, newpassword2)) {
2301 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2302 cmd = play_and_wait(chan, "vm-mismatch");
2305 vm_change_password(vmu,newpassword);
2306 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2307 cmd = play_and_wait(chan,"vm-passchanged");
2313 cmd = play_and_wait(chan,"vm-options");
2315 cmd = ast_waitfordigit(chan,6000);
2327 static int vm_execmain(struct ast_channel *chan, void *data)
2329 /* XXX This is, admittedly, some pretty horrendus code. For some
2330 reason it just seemed a lot easier to do with GOTO's. I feel
2331 like I'm back in my GWBASIC days. XXX */
2336 struct localuser *u;
2337 char prefixstr[80] ="";
2338 char empty[80] = "";
2342 char tmp[256], *ext;
2343 char fmtc[256] = "";
2345 struct vm_state vms;
2347 struct ast_vm_user *vmu = NULL, vmus;
2351 memset(&vms, 0, sizeof(vms));
2352 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2353 if (chan->_state != AST_STATE_UP)
2356 if (data && strlen(data)) {
2357 strncpy(tmp, data, sizeof(tmp) - 1);
2362 /* We should skip the user's password */
2367 /* We should prefix the mailbox with the supplied data */
2373 context = strchr(ext, '@');
2380 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2382 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2383 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2390 /* If ADSI is supported, setup login screen */
2391 adsi_begin(chan, &useadsi);
2392 if (!skipuser && useadsi)
2394 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2395 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2399 /* Authenticate them and get their mailbox/password */
2401 while (!valid && (logretries < maxlogins)) {
2402 /* Prompt for, and read in the username */
2403 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2404 ast_log(LOG_WARNING, "Couldn't read username\n");
2407 if (!strlen(vms.username)) {
2408 if (option_verbose > 2)
2409 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2414 adsi_password(chan);
2415 if (ast_streamfile(chan, "vm-password", chan->language)) {
2416 ast_log(LOG_WARNING, "Unable to stream password file\n");
2419 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2420 ast_log(LOG_WARNING, "Unable to read password\n");
2424 char fullusername[80] = "";
2425 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2426 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2427 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2430 vmu = find_user(&vmus, context, vms.username);
2431 if (vmu && !strcmp(vmu->password, password))
2434 if (option_verbose > 2)
2435 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2437 strncpy(vms.username, empty, sizeof(vms.username) -1);
2442 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2447 if (!valid && (logretries >= maxlogins)) {
2448 ast_stopstream(chan);
2449 res = play_and_wait(chan, "vm-goodbye");
2455 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2456 mkdir(vms.curdir, 0700);
2457 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2458 mkdir(vms.curdir, 0700);
2459 /* Retrieve old and new message counts */
2460 open_mailbox(&vms, vmu, 1);
2461 vms.oldmessages = vms.lastmsg + 1;
2462 /* Start in INBOX */
2463 open_mailbox(&vms, vmu, 0);
2464 vms.newmessages = vms.lastmsg + 1;
2467 /* Select proper mailbox FIRST!! */
2468 if (!vms.newmessages && vms.oldmessages) {
2469 /* If we only have old messages start here */
2470 open_mailbox(&vms, vmu, 1);
2474 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2476 cmd = vm_intro(chan, &vms);
2479 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2486 if (vms.lastmsg > -1) {
2487 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2489 cmd = play_and_wait(chan, "vm-youhave");
2491 cmd = play_and_wait(chan, "vm-no");
2493 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2494 cmd = play_and_wait(chan, vms.fn);
2497 cmd = play_and_wait(chan, "vm-messages");
2500 case '2': /* Change folders */
2502 adsi_folders(chan, 0, "Change to folder...");
2503 cmd = get_folder2(chan, "vm-changeto", 0);
2506 } else if (cmd > 0) {
2508 close_mailbox(&vms, vmu);
2509 open_mailbox(&vms, vmu, cmd);
2513 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2515 cmd = play_and_wait(chan, vms.vmbox);
2517 cmd = play_and_wait(chan, "vm-messages");
2523 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2525 cmd = play_and_wait(chan, "vm-nomore");
2529 if (vms.curmsg < vms.lastmsg) {
2531 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2533 cmd = play_and_wait(chan, "vm-nomore");
2537 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2539 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2540 if (vms.deleted[vms.curmsg])
2541 cmd = play_and_wait(chan, "vm-deleted");
2543 cmd = play_and_wait(chan, "vm-undeleted");
2546 if(vms.lastmsg > -1)
2547 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2551 adsi_folders(chan, 1, "Save to folder...");
2552 cmd = get_folder2(chan, "vm-savefolder", 1);
2553 box = 0; /* Shut up compiler */
2557 } else if (cmd > 0) {
2558 box = cmd = cmd - '0';
2559 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2560 vms.deleted[vms.curmsg]=1;
2562 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2564 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2566 cmd = play_and_wait(chan, "vm-message");
2568 cmd = say_and_wait(chan, vms.curmsg + 1);
2570 cmd = play_and_wait(chan, "vm-savedto");
2572 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2573 cmd = play_and_wait(chan, vms.fn);
2576 cmd = play_and_wait(chan, "vm-messages");
2579 if (!vms.starting) {
2580 cmd = play_and_wait(chan, "vm-onefor");
2582 cmd = play_and_wait(chan, vms.vmbox);
2584 cmd = play_and_wait(chan, "vm-messages");
2586 cmd = play_and_wait(chan, "vm-opts");
2591 cmd = vm_options(chan, vmu, &vms, vmfmts);
2593 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2595 default: /* Nothing */
2596 cmd = vm_instructions(chan, &vms);
2600 if ((cmd == 't') || (cmd == '#')) {
2610 ast_stopstream(chan);
2612 res = play_and_wait(chan, "vm-goodbye");
2616 adsi_unload_session(chan);
2619 close_mailbox(&vms, vmu);
2623 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2625 LOCAL_USER_REMOVE(u);
2630 static int vm_exec(struct ast_channel *chan, void *data)
2632 int res=0, silent=0, busy=0, unavail=0;
2633 struct localuser *u;
2634 char tmp[256], *ext;
2637 if (chan->_state != AST_STATE_UP)
2640 strncpy(tmp, data, sizeof(tmp) - 1);
2642 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2653 } else if (*ext == 'b') {
2656 } else if (*ext == 'u') {
2662 res = leave_voicemail(chan, ext, silent, busy, unavail);
2663 LOCAL_USER_REMOVE(u);
2667 static int append_mailbox(char *context, char *mbox, char *data)
2669 /* Assumes lock is already held */
2673 struct ast_vm_user *vmu;
2674 strncpy(tmp, data, sizeof(tmp));
2675 vmu = malloc(sizeof(struct ast_vm_user));
2677 memset(vmu, 0, sizeof(struct ast_vm_user));
2678 strncpy(vmu->context, context, sizeof(vmu->context));
2679 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2682 if ((s = strsep(&stringp, ",")))
2683 strncpy(vmu->password, s, sizeof(vmu->password));
2684 if (stringp && (s = strsep(&stringp, ",")))
2685 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2686 if (stringp && (s = strsep(&stringp, ",")))
2687 strncpy(vmu->email, s, sizeof(vmu->email));
2688 if (stringp && (s = strsep(&stringp, ",")))
2689 strncpy(vmu->pager, s, sizeof(vmu->pager));
2690 if (stringp && (s = strsep(&stringp, ",")))
2691 apply_options(vmu, s);
2702 static int load_config(void)
2704 struct ast_vm_user *cur, *l;
2705 struct vm_zone *zcur, *zl;
2706 struct ast_config *cfg;
2708 struct ast_variable *var;
2717 cfg = ast_load(VOICEMAIL_CONFIG);
2718 ast_mutex_lock(&vmlock);
2736 /* General settings */
2737 attach_voicemail = 1;
2738 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
2740 attach_voicemail = ast_true(astattach);
2742 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2743 maxsilence = atoi(silencestr);
2748 silencethreshold = 256;
2749 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2750 silencethreshold = atoi(thresholdstr);
2752 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
2753 astemail = ASTERISK_USERNAME;
2754 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2757 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2758 if (sscanf(s, "%d", &x) == 1) {
2761 ast_log(LOG_WARNING, "Invalid max message time length\n");
2764 fmt = ast_variable_retrieve(cfg, "general", "format");
2767 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2770 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2771 if (sscanf(s, "%d", &x) == 1) {
2774 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2778 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2779 if (sscanf(s, "%d", &x) == 1) {
2782 ast_log(LOG_WARNING, "Invalid skipms value\n");
2787 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2788 if (sscanf(s, "%d", &x) == 1) {
2791 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2796 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2797 strcpy(dbuser, "test");
2801 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2802 strcpy(dbpass, "test");
2806 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2811 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2812 strcpy(dbname, "vmdb");
2818 #ifdef USEPOSTGRESVM
2819 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
2820 strcpy(dboption, "dboption not-specified in voicemail.conf");
2822 strcpy(dboption, s);
2825 cat = ast_category_browse(cfg, NULL);
2827 if (strcasecmp(cat, "general")) {
2828 var = ast_variable_browse(cfg, cat);
2829 if (strcasecmp(cat, "zonemessages")) {
2831 /* Process mailboxes in this context */
2833 append_mailbox(cat, var->name, var->value);
2838 /* Timezones in this context */
2841 z = malloc(sizeof(struct vm_zone));
2843 char *msg_format, *timezone;
2844 msg_format = ast_strdupa(var->value);
2845 if (msg_format != NULL) {
2846 timezone = strsep(&msg_format, "|");
2847 strncpy(z->name, var->name, sizeof(z->name) - 1);
2848 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
2849 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
2859 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2864 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2871 cat = ast_category_browse(cfg, cat);
2873 memset(fromstring,0,sizeof(fromstring));
2874 memset(emailtitle,0,sizeof(emailtitle));
2879 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
2880 pbxskip = ast_true(s);
2881 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
2882 strncpy(fromstring,s,sizeof(fromstring)-1);
2883 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
2884 strncpy(emailtitle,s,sizeof(emailtitle)-1);
2885 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
2886 char *tmpread, *tmpwrite;
2887 emailbody = strdup(s);
2889 /* substitute strings \t and \n into the apropriate characters */
2890 tmpread = tmpwrite = emailbody;
2891 while ((tmpwrite = strchr(tmpread,'\\'))) {
2892 int len = strlen("\n");
2893 switch (tmpwrite[1]) {
2895 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2896 strncpy(tmpwrite,"\n",len);
2899 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2900 strncpy(tmpwrite,"\t",len);
2903 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
2905 tmpread = tmpwrite+len;
2909 ast_mutex_unlock(&vmlock);
2912 ast_mutex_unlock(&vmlock);
2913 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2920 return(load_config());
2923 int unload_module(void)
2926 STANDARD_HANGUP_LOCALUSERS;
2927 res = ast_unregister_application(app);
2928 res |= ast_unregister_application(app2);
2933 int load_module(void)
2936 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);