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];
1595 bytes += adsi_voice_mode(buf + bytes, 0);
1600 char datetime[21]="";
1603 unsigned char keys[8];
1607 if (!adsi_available(chan))
1610 /* Retrieve important info */
1611 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1612 f = fopen(fn2, "r");
1615 fgets(buf, sizeof(buf), f);
1619 strsep(&stringp, "=");
1620 val = strsep(&stringp, "=");
1621 if (val && strlen(val)) {
1622 if (!strcmp(buf, "callerid"))
1623 strncpy(cid, val, sizeof(cid) - 1);
1624 if (!strcmp(buf, "origdate"))
1625 strncpy(datetime, val, sizeof(datetime) - 1);
1631 /* New meaning for keys */
1633 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1635 bytes += adsi_voice_mode(buf + bytes, 0);
1640 /* No prev key, provide "Folder" instead */
1641 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1644 /* If last message ... */
1646 /* but not only message, provide "Folder" instead */
1647 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1648 bytes += adsi_voice_mode(buf + bytes, 0);
1651 /* Otherwise if only message, leave blank */
1657 ast_callerid_parse(cid, &name, &num);
1661 name = "Unknown Caller";
1663 /* If deleted, show "undeleted" */
1664 bytes += adsi_voice_mode(buf + bytes, 0);
1667 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1670 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1671 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1672 strcasecmp(folder, "INBOX") ? " Messages" : "");
1673 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1675 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1676 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1677 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1678 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1679 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1680 bytes += adsi_set_keys(buf + bytes, keys);
1681 bytes += adsi_voice_mode(buf + bytes, 0);
1683 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1686 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1690 unsigned char keys[8];
1694 if (!adsi_available(chan))
1697 /* New meaning for keys */
1699 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1705 /* No prev key, provide "Folder" instead */
1706 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1709 /* If last message ... */
1711 /* but not only message, provide "Folder" instead */
1712 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1714 /* Otherwise if only message, leave blank */
1719 /* If deleted, show "undeleted" */
1721 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1724 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1725 bytes += adsi_set_keys(buf + bytes, keys);
1726 bytes += adsi_voice_mode(buf + bytes, 0);
1728 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1731 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1733 char buf[256], buf1[256], buf2[256];
1735 unsigned char keys[8];
1738 char *newm = (new == 1) ? "message" : "messages";
1739 char *oldm = (old == 1) ? "message" : "messages";
1740 if (!adsi_available(chan))
1743 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1745 strcat(buf1, " and");
1746 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1748 snprintf(buf2, sizeof(buf2), "%s.", newm);
1751 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1752 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1754 strcpy(buf1, "You have no messages.");
1757 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1758 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1759 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1762 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1766 /* Don't let them listen if there are none */
1769 bytes += adsi_set_keys(buf + bytes, keys);
1771 bytes += adsi_voice_mode(buf + bytes, 0);
1773 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1776 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1778 char buf[256], buf1[256], buf2[256];
1780 unsigned char keys[8];
1783 char *mess = (messages == 1) ? "message" : "messages";
1785 if (!adsi_available(chan))
1788 /* Original command keys */
1790 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1798 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1799 strcasecmp(folder, "INBOX") ? " folder" : "");
1802 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1804 strcpy(buf2, "no messages.");
1805 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1806 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1807 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1808 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1809 bytes += adsi_set_keys(buf + bytes, keys);
1811 bytes += adsi_voice_mode(buf + bytes, 0);
1813 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1817 static void adsi_clear(struct ast_channel *chan)
1821 if (!adsi_available(chan))
1823 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1824 bytes += adsi_voice_mode(buf + bytes, 0);
1826 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1829 static void adsi_goodbye(struct ast_channel *chan)
1834 if (!adsi_available(chan))
1836 bytes += adsi_logo(buf + bytes);
1837 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1838 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1839 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1840 bytes += adsi_voice_mode(buf + bytes, 0);
1842 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1845 static int get_folder(struct ast_channel *chan, int start)
1850 d = play_and_wait(chan, "vm-press");
1853 for (x = start; x< 5; x++) {
1854 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1856 d = play_and_wait(chan, "vm-for");
1859 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1860 d = play_and_wait(chan, fn);
1863 d = play_and_wait(chan, "vm-messages");
1866 d = ast_waitfordigit(chan, 500);
1870 d = play_and_wait(chan, "vm-tocancel");
1873 d = ast_waitfordigit(chan, 4000);
1877 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1880 res = play_and_wait(chan, fn);
1881 while (((res < '0') || (res > '9')) &&
1882 (res != '#') && (res >= 0)) {
1883 res = get_folder(chan, 0);
1889 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1896 struct ast_config *mif;
1901 struct ast_vm_user *receiver, srec;
1906 res = ast_streamfile(chan, "vm-extension", chan->language);
1909 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1911 if ((receiver = find_user(&srec, context, username))) {
1912 /* if (play_and_wait(chan, "vm-savedto"))
1916 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1917 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1918 ast_log(LOG_DEBUG, sys);
1921 todircount = count_messages(todir);
1922 strncpy(tmp, fmt, sizeof(tmp));
1924 while((s = strsep(&stringp, "|"))) {
1925 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1926 ast_log(LOG_DEBUG, sys);
1929 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1930 ast_log(LOG_DEBUG, sys);
1932 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1934 /* load the information on the source message so we can send an e-mail like a new message */
1935 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1936 if ((mif=ast_load(miffile))) {
1938 /* set callerid and duration variables */
1939 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1940 duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1942 if (strlen(receiver->email)) {
1943 int attach_user_voicemail = attach_voicemail;
1944 char *myserveremail = serveremail;
1945 if (receiver->attach > -1)
1946 attach_user_voicemail = receiver->attach;
1947 if (strlen(receiver->serveremail))
1948 myserveremail = receiver->serveremail;
1949 sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1952 if (strlen(receiver->pager)) {
1953 char *myserveremail = serveremail;
1954 if (strlen(receiver->serveremail))
1955 myserveremail = receiver->serveremail;
1956 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver);
1959 ast_destroy(mif); /* or here */
1961 /* Leave voicemail for someone */
1962 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1964 /* give confirmatopm that the message was saved */
1965 res = play_and_wait(chan, "vm-message");
1967 res = play_and_wait(chan, "vm-saved");
1968 free_user(receiver);
1971 res = play_and_wait(chan, "pbx-invalid");
1984 int deleted[MAXMSG];
1995 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1998 if ((res = ast_streamfile(chan, file, chan->language)))
1999 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2001 res = ast_waitstream(chan, AST_DIGIT_ANY);
2005 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2008 if ((res = ast_streamfile(chan, file, chan->language)))
2009 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2011 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2015 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2018 char filename[256], *origtime;
2019 struct vm_zone *the_zone = NULL;
2020 struct ast_config *msg_cfg;
2024 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2025 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2026 msg_cfg = ast_load(filename);
2028 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2032 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2034 if (sscanf(origtime,"%ld",&tin) < 1) {
2035 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2039 ast_destroy(msg_cfg);
2041 /* Does this user have a timezone specified? */
2042 if (strlen(vmu->zonetag)) {
2043 /* Find the zone in the list */
2047 if (!strcmp(z->name, vmu->zonetag)) {
2055 /* No internal variable parsing for now, so we'll comment it out for the time being */
2057 /* Set the DIFF_* variables */
2058 localtime_r(&t, &time_now);
2059 gettimeofday(&tv_now,NULL);
2060 tnow = tv_now.tv_sec;
2061 localtime_r(&tnow,&time_then);
2063 /* Day difference */
2064 if (time_now.tm_year == time_then.tm_year)
2065 sprintf(temp,"%d",time_now.tm_yday);
2067 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2068 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2070 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2073 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2075 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2077 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2082 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2086 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2087 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2089 res = wait_file2(chan, vms, "vm-first");
2090 else if (msg == vms->lastmsg)
2091 res = wait_file2(chan, vms, "vm-last");
2093 res = wait_file2(chan, vms, "vm-message");
2094 if (msg && (msg != vms->lastmsg)) {
2096 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2101 res = play_message_datetime(chan,vmu,vms);
2104 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2105 vms->heard[msg] = 1;
2106 res = wait_file(chan, vms, vms->fn);
2111 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2113 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2114 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2115 vms->lastmsg = count_messages(vms->curdir) - 1;
2116 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2119 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2122 char ntxt[256] = "";
2124 if (vms->lastmsg > -1) {
2125 /* Get the deleted messages fixed */
2127 for (x=0;x < MAXMSG;x++) {
2128 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2129 /* Save this message. It's not in INBOX or hasn't been heard */
2130 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2131 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2134 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2135 if (strcmp(vms->fn, vms->fn2)) {
2136 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2137 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2138 ast_filerename(vms->fn, vms->fn2, NULL);
2141 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2142 /* Move to old folder before deleting */
2143 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2146 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2147 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2148 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2150 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2151 ast_filedelete(vms->fn, NULL);
2155 memset(vms->deleted, 0, sizeof(vms->deleted));
2156 memset(vms->heard, 0, sizeof(vms->heard));
2159 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2161 /* Introduce messages they have */
2163 res = play_and_wait(chan, "vm-youhave");
2165 if (vms->newmessages) {
2166 res = say_and_wait(chan, vms->newmessages);
2168 res = play_and_wait(chan, "vm-INBOX");
2169 if (vms->oldmessages && !res)
2170 res = play_and_wait(chan, "vm-and");
2172 if ((vms->newmessages == 1))
2173 res = play_and_wait(chan, "vm-message");
2175 res = play_and_wait(chan, "vm-messages");
2179 if (!res && vms->oldmessages) {
2180 res = say_and_wait(chan, vms->oldmessages);
2182 res = play_and_wait(chan, "vm-Old");
2184 if (vms->oldmessages == 1)
2185 res = play_and_wait(chan, "vm-message");
2187 res = play_and_wait(chan, "vm-messages");
2191 if (!vms->oldmessages && !vms->newmessages) {
2192 res = play_and_wait(chan, "vm-no");
2194 res = play_and_wait(chan, "vm-messages");
2201 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2204 /* Play instructions and wait for new command */
2206 if (vms->starting) {
2207 if (vms->lastmsg > -1) {
2208 res = play_and_wait(chan, "vm-onefor");
2210 res = play_and_wait(chan, vms->vmbox);
2212 res = play_and_wait(chan, "vm-messages");
2215 res = play_and_wait(chan, "vm-opts");
2218 res = play_and_wait(chan, "vm-prev");
2220 res = play_and_wait(chan, "vm-repeat");
2221 if (!res && (vms->curmsg != vms->lastmsg))
2222 res = play_and_wait(chan, "vm-next");
2224 if (!vms->deleted[vms->curmsg])
2225 res = play_and_wait(chan, "vm-delete");
2227 res = play_and_wait(chan, "vm-undelete");
2229 res = play_and_wait(chan, "vm-toforward");
2231 res = play_and_wait(chan, "vm-savemessage");
2235 res = play_and_wait(chan, "vm-helpexit");
2237 res = ast_waitfordigit(chan, 6000);
2240 if (vms->repeats > 2) {
2241 res = play_and_wait(chan, "vm-goodbye");
2250 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2254 char newpassword[80] = "";
2255 char newpassword2[80] = "";
2256 char prefile[256]="";
2260 if (adsi_available(chan))
2262 bytes += adsi_logo(buf + bytes);
2263 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2264 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2265 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2266 bytes += adsi_voice_mode(buf + bytes, 0);
2267 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2269 while((cmd >= 0) && (cmd != 't')) {
2274 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2275 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2278 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2279 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2282 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2283 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2286 newpassword[1] = '\0';
2287 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2290 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2293 newpassword2[1] = '\0';
2294 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2298 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2301 if (strcmp(newpassword, newpassword2)) {
2302 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2303 cmd = play_and_wait(chan, "vm-mismatch");
2306 vm_change_password(vmu,newpassword);
2307 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2308 cmd = play_and_wait(chan,"vm-passchanged");
2314 cmd = play_and_wait(chan,"vm-options");
2316 cmd = ast_waitfordigit(chan,6000);
2328 static int vm_execmain(struct ast_channel *chan, void *data)
2330 /* XXX This is, admittedly, some pretty horrendus code. For some
2331 reason it just seemed a lot easier to do with GOTO's. I feel
2332 like I'm back in my GWBASIC days. XXX */
2337 struct localuser *u;
2338 char prefixstr[80] ="";
2339 char empty[80] = "";
2343 char tmp[256], *ext;
2344 char fmtc[256] = "";
2346 struct vm_state vms;
2348 struct ast_vm_user *vmu = NULL, vmus;
2352 memset(&vms, 0, sizeof(vms));
2353 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2354 if (chan->_state != AST_STATE_UP)
2357 if (data && strlen(data)) {
2358 strncpy(tmp, data, sizeof(tmp) - 1);
2363 /* We should skip the user's password */
2368 /* We should prefix the mailbox with the supplied data */
2374 context = strchr(ext, '@');
2381 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2383 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2384 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2391 /* If ADSI is supported, setup login screen */
2392 adsi_begin(chan, &useadsi);
2393 if (!skipuser && useadsi)
2395 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2396 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2400 /* Authenticate them and get their mailbox/password */
2402 while (!valid && (logretries < maxlogins)) {
2403 /* Prompt for, and read in the username */
2404 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2405 ast_log(LOG_WARNING, "Couldn't read username\n");
2408 if (!strlen(vms.username)) {
2409 if (option_verbose > 2)
2410 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2415 adsi_password(chan);
2416 if (ast_streamfile(chan, "vm-password", chan->language)) {
2417 ast_log(LOG_WARNING, "Unable to stream password file\n");
2420 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2421 ast_log(LOG_WARNING, "Unable to read password\n");
2425 char fullusername[80] = "";
2426 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2427 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2428 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2431 vmu = find_user(&vmus, context, vms.username);
2432 if (vmu && !strcmp(vmu->password, password))
2435 if (option_verbose > 2)
2436 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2438 strncpy(vms.username, empty, sizeof(vms.username) -1);
2443 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2448 if (!valid && (logretries >= maxlogins)) {
2449 ast_stopstream(chan);
2450 res = play_and_wait(chan, "vm-goodbye");
2456 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2457 mkdir(vms.curdir, 0700);
2458 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2459 mkdir(vms.curdir, 0700);
2460 /* Retrieve old and new message counts */
2461 open_mailbox(&vms, vmu, 1);
2462 vms.oldmessages = vms.lastmsg + 1;
2463 /* Start in INBOX */
2464 open_mailbox(&vms, vmu, 0);
2465 vms.newmessages = vms.lastmsg + 1;
2468 /* Select proper mailbox FIRST!! */
2469 if (!vms.newmessages && vms.oldmessages) {
2470 /* If we only have old messages start here */
2471 open_mailbox(&vms, vmu, 1);
2475 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2477 cmd = vm_intro(chan, &vms);
2480 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2487 if (vms.lastmsg > -1) {
2488 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2490 cmd = play_and_wait(chan, "vm-youhave");
2492 cmd = play_and_wait(chan, "vm-no");
2494 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2495 cmd = play_and_wait(chan, vms.fn);
2498 cmd = play_and_wait(chan, "vm-messages");
2501 case '2': /* Change folders */
2503 adsi_folders(chan, 0, "Change to folder...");
2504 cmd = get_folder2(chan, "vm-changeto", 0);
2507 } else if (cmd > 0) {
2509 close_mailbox(&vms, vmu);
2510 open_mailbox(&vms, vmu, cmd);
2514 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2516 cmd = play_and_wait(chan, vms.vmbox);
2518 cmd = play_and_wait(chan, "vm-messages");
2524 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2526 cmd = play_and_wait(chan, "vm-nomore");
2530 if (vms.curmsg < vms.lastmsg) {
2532 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2534 cmd = play_and_wait(chan, "vm-nomore");
2538 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2540 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2541 if (vms.deleted[vms.curmsg])
2542 cmd = play_and_wait(chan, "vm-deleted");
2544 cmd = play_and_wait(chan, "vm-undeleted");
2547 if(vms.lastmsg > -1)
2548 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2552 adsi_folders(chan, 1, "Save to folder...");
2553 cmd = get_folder2(chan, "vm-savefolder", 1);
2554 box = 0; /* Shut up compiler */
2558 } else if (cmd > 0) {
2559 box = cmd = cmd - '0';
2560 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2561 vms.deleted[vms.curmsg]=1;
2563 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2565 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2567 cmd = play_and_wait(chan, "vm-message");
2569 cmd = say_and_wait(chan, vms.curmsg + 1);
2571 cmd = play_and_wait(chan, "vm-savedto");
2573 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2574 cmd = play_and_wait(chan, vms.fn);
2577 cmd = play_and_wait(chan, "vm-messages");
2580 if (!vms.starting) {
2581 cmd = play_and_wait(chan, "vm-onefor");
2583 cmd = play_and_wait(chan, vms.vmbox);
2585 cmd = play_and_wait(chan, "vm-messages");
2587 cmd = play_and_wait(chan, "vm-opts");
2592 cmd = vm_options(chan, vmu, &vms, vmfmts);
2594 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2596 default: /* Nothing */
2597 cmd = vm_instructions(chan, &vms);
2601 if ((cmd == 't') || (cmd == '#')) {
2611 ast_stopstream(chan);
2613 res = play_and_wait(chan, "vm-goodbye");
2617 adsi_unload_session(chan);
2620 close_mailbox(&vms, vmu);
2624 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2626 LOCAL_USER_REMOVE(u);
2631 static int vm_exec(struct ast_channel *chan, void *data)
2633 int res=0, silent=0, busy=0, unavail=0;
2634 struct localuser *u;
2635 char tmp[256], *ext;
2638 if (chan->_state != AST_STATE_UP)
2641 strncpy(tmp, data, sizeof(tmp) - 1);
2643 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2654 } else if (*ext == 'b') {
2657 } else if (*ext == 'u') {
2663 res = leave_voicemail(chan, ext, silent, busy, unavail);
2664 LOCAL_USER_REMOVE(u);
2668 static int append_mailbox(char *context, char *mbox, char *data)
2670 /* Assumes lock is already held */
2674 struct ast_vm_user *vmu;
2675 strncpy(tmp, data, sizeof(tmp));
2676 vmu = malloc(sizeof(struct ast_vm_user));
2678 memset(vmu, 0, sizeof(struct ast_vm_user));
2679 strncpy(vmu->context, context, sizeof(vmu->context));
2680 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2683 if ((s = strsep(&stringp, ",")))
2684 strncpy(vmu->password, s, sizeof(vmu->password));
2685 if (stringp && (s = strsep(&stringp, ",")))
2686 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2687 if (stringp && (s = strsep(&stringp, ",")))
2688 strncpy(vmu->email, s, sizeof(vmu->email));
2689 if (stringp && (s = strsep(&stringp, ",")))
2690 strncpy(vmu->pager, s, sizeof(vmu->pager));
2691 if (stringp && (s = strsep(&stringp, ",")))
2692 apply_options(vmu, s);
2703 static int load_config(void)
2705 struct ast_vm_user *cur, *l;
2706 struct vm_zone *zcur, *zl;
2707 struct ast_config *cfg;
2709 struct ast_variable *var;
2718 cfg = ast_load(VOICEMAIL_CONFIG);
2719 ast_mutex_lock(&vmlock);
2737 /* General settings */
2738 attach_voicemail = 1;
2739 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
2741 attach_voicemail = ast_true(astattach);
2743 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2744 maxsilence = atoi(silencestr);
2749 silencethreshold = 256;
2750 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2751 silencethreshold = atoi(thresholdstr);
2753 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
2754 astemail = ASTERISK_USERNAME;
2755 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2758 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2759 if (sscanf(s, "%d", &x) == 1) {
2762 ast_log(LOG_WARNING, "Invalid max message time length\n");
2765 fmt = ast_variable_retrieve(cfg, "general", "format");
2768 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2771 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2772 if (sscanf(s, "%d", &x) == 1) {
2775 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2779 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2780 if (sscanf(s, "%d", &x) == 1) {
2783 ast_log(LOG_WARNING, "Invalid skipms value\n");
2788 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2789 if (sscanf(s, "%d", &x) == 1) {
2792 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2797 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2798 strcpy(dbuser, "test");
2802 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2803 strcpy(dbpass, "test");
2807 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2812 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2813 strcpy(dbname, "vmdb");
2819 #ifdef USEPOSTGRESVM
2820 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
2821 strcpy(dboption, "dboption not-specified in voicemail.conf");
2823 strcpy(dboption, s);
2826 cat = ast_category_browse(cfg, NULL);
2828 if (strcasecmp(cat, "general")) {
2829 var = ast_variable_browse(cfg, cat);
2830 if (strcasecmp(cat, "zonemessages")) {
2832 /* Process mailboxes in this context */
2834 append_mailbox(cat, var->name, var->value);
2839 /* Timezones in this context */
2842 z = malloc(sizeof(struct vm_zone));
2844 char *msg_format, *timezone;
2845 msg_format = ast_strdupa(var->value);
2846 if (msg_format != NULL) {
2847 timezone = strsep(&msg_format, "|");
2848 strncpy(z->name, var->name, sizeof(z->name) - 1);
2849 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
2850 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
2860 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2865 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2872 cat = ast_category_browse(cfg, cat);
2874 memset(fromstring,0,sizeof(fromstring));
2875 memset(emailtitle,0,sizeof(emailtitle));
2880 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
2881 pbxskip = ast_true(s);
2882 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
2883 strncpy(fromstring,s,sizeof(fromstring)-1);
2884 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
2885 strncpy(emailtitle,s,sizeof(emailtitle)-1);
2886 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
2887 char *tmpread, *tmpwrite;
2888 emailbody = strdup(s);
2890 /* substitute strings \t and \n into the apropriate characters */
2891 tmpread = tmpwrite = emailbody;
2892 while ((tmpwrite = strchr(tmpread,'\\'))) {
2893 int len = strlen("\n");
2894 switch (tmpwrite[1]) {
2896 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2897 strncpy(tmpwrite,"\n",len);
2900 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2901 strncpy(tmpwrite,"\t",len);
2904 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
2906 tmpread = tmpwrite+len;
2910 ast_mutex_unlock(&vmlock);
2913 ast_mutex_unlock(&vmlock);
2914 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2921 return(load_config());
2924 int unload_module(void)
2927 STANDARD_HANGUP_LOCALUSERS;
2928 res = ast_unregister_application(app);
2929 res |= ast_unregister_application(app2);
2934 int load_module(void)
2937 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2938 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2942 if ((res=load_config())) {
2946 if ((res = sql_init())) {
2947 ast_log(LOG_WARNING, "SQL init\n");
2953 char *description(void)
2961 STANDARD_USECOUNT(res);
2967 return ASTERISK_GPL_KEY;