2 * Asterisk -- A telephony toolkit for Linux.
4 * Voicemail System (did you ever think it could be so easy?)
6 * Copyright (C) 2003, Digium Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
39 /* we define USESQLVM when we have MySQL or POSTGRES */
41 #include <mysql/mysql.h>
47 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
49 #include <postgresql/libpq-fe.h>
54 static inline int sql_init(void) { return 0; }
55 static inline void sql_close(void) { }
59 #include "../asterisk.h"
60 #include "../astconf.h"
62 #define COMMAND_TIMEOUT 5000
64 #define VOICEMAIL_CONFIG "voicemail.conf"
65 #define ASTERISK_USERNAME "asterisk"
67 #define SENDMAIL "/usr/sbin/sendmail -t"
69 #define INTRO "vm-intro"
72 #define MAX_OTHER_FORMATS 10
74 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
76 #define BASEMAXINLINE 256
77 #define BASELINELEN 72
78 #define BASEMAXINLINE 256
81 #define MAX_DATETIME_FORMAT 512
82 #define DIGITS_DIR AST_SOUNDS "/digits/"
88 unsigned char iobuf[BASEMAXINLINE];
102 struct ast_vm_user *next;
108 char msg_format[512];
109 struct vm_zone *next;
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 *capp = "VoiceMail2";
149 static char *app = "VoiceMail";
151 /* Check mail, control, etc */
152 static char *capp2 = "VoiceMailMain2";
153 static char *app2 = "VoiceMailMain";
155 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
156 struct ast_vm_user *users;
157 struct ast_vm_user *usersl;
158 struct vm_zone *zones = NULL;
159 struct vm_zone *zonesl = NULL;
160 static int attach_voicemail;
161 static int maxsilence;
162 static int silencethreshold = 128;
163 static char serveremail[80];
164 static char vmfmts[80];
165 static int vmmaxmessage;
168 static int maxlogins;
170 static char *emailbody = NULL;
171 static int pbxskip = 0;
172 static char fromstring[100];
173 static char emailtitle[100];
179 static void apply_options(struct ast_vm_user *vmu, char *options)
181 /* Destructively Parse options and apply */
182 char *stringp = ast_strdupa(options);
185 while((s = strsep(&stringp, "|"))) {
187 if ((var = strsep(&value, "=")) && value) {
188 if (!strcasecmp(var, "attach")) {
193 } else if (!strcasecmp(var, "serveremail")) {
194 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
195 } else if (!strcasecmp(var, "tz")) {
196 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
204 #include "mysql-vm-routines.h"
211 ast_mutex_t postgreslock;
213 static int sql_init(void)
215 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
216 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
218 dbhandler=PQconnectdb(dboption);
219 if (PQstatus(dbhandler) == CONNECTION_BAD) {
220 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
223 ast_mutex_init(&postgreslock);
225 /* fprintf(stderr,"postgres login OK\n"); */
229 static void sql_close(void)
235 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
243 char options[160] = "";
244 struct ast_vm_user *retval;
246 retval=malloc(sizeof(struct ast_vm_user));
248 /* fprintf(stderr,"postgres find_user:\n"); */
251 *retval->mailbox='\0';
252 *retval->context='\0';
253 *retval->password='\0';
254 *retval->fullname='\0';
257 *retval->serveremail='\0';
262 strcpy(retval->mailbox, mailbox);
265 strcpy(retval->context, context);
268 if (*retval->context) {
269 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", context, mailbox);
271 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE mailbox='%s'", mailbox);
273 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
274 ast_mutex_lock(&postgreslock);
275 PGSQLres=PQexec(dbhandler,query);
276 if (PGSQLres!=NULL) {
277 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
278 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
279 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
281 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
284 ast_mutex_unlock(&postgreslock);
288 numFields = PQnfields(PGSQLres);
289 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
290 if (PQntuples(PGSQLres) != 1) {
291 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
293 ast_mutex_unlock(&postgreslock);
297 for (i=0; i<numFields; i++) {
298 fname = PQfname(PGSQLres,i);
299 if (!strcmp(fname, "password")) {
300 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
301 } else if (!strcmp(fname, "fullname")) {
302 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
303 } else if (!strcmp(fname, "email")) {
304 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
305 } else if (!strcmp(fname, "pager")) {
306 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
307 } else if (!strcmp(fname, "options")) {
308 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
309 apply_options(retval, options);
314 ast_mutex_unlock(&postgreslock);
318 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
319 ast_mutex_unlock(&postgreslock);
324 } /* malloc() retval */
329 static void vm_change_password(struct ast_vm_user *vmu, char *password)
334 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);
336 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
338 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
339 ast_mutex_lock(&postgreslock);
340 PQexec(dbhandler, query);
341 strcpy(vmu->password, password);
342 ast_mutex_unlock(&postgreslock);
345 static void reset_user_pw(char *context, char *mailbox, char *password)
350 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
352 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
354 ast_mutex_lock(&postgreslock);
355 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
356 PQexec(dbhandler, query);
357 ast_mutex_unlock(&postgreslock);
360 #endif /* Postgres */
363 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
365 /* This function could be made to generate one from a database, too */
366 struct ast_vm_user *vmu=NULL, *cur;
367 ast_mutex_lock(&vmlock);
370 if ((!context || !strcasecmp(context, cur->context)) &&
371 (!strcasecmp(mailbox, cur->mailbox)))
379 /* Make a copy, so that on a reload, we have no race */
380 vmu = malloc(sizeof(struct ast_vm_user));
382 memcpy(vmu, cur, sizeof(struct ast_vm_user));
390 ast_mutex_unlock(&vmlock);
394 static int reset_user_pw(char *context, char *mailbox, char *newpass)
396 /* This function could be made to generate one from a database, too */
397 struct ast_vm_user *cur;
399 ast_mutex_lock(&vmlock);
402 if ((!context || !strcasecmp(context, cur->context)) &&
403 (!strcasecmp(mailbox, cur->mailbox)))
408 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
411 ast_mutex_unlock(&vmlock);
415 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
417 /* There's probably a better way of doing this. */
418 /* That's why I've put the password change in a separate function. */
419 /* This could also be done with a database function */
425 char tmpin[AST_CONFIG_MAX_PATH];
426 char tmpout[AST_CONFIG_MAX_PATH];
427 char *user, *pass, *rest, *trim;
428 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
429 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
430 configin = fopen((char *)tmpin,"r");
432 configout = fopen((char *)tmpout,"w+");
435 if(!configin || !configout) {
439 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
443 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
447 while (!feof(configin)) {
448 /* Read in the line */
449 fgets(inbuf, sizeof(inbuf), configin);
450 if (!feof(configin)) {
451 /* Make a backup of it */
452 memcpy(orig, inbuf, sizeof(orig));
453 /* Strip trailing \n and comment */
454 inbuf[strlen(inbuf) - 1] = '\0';
455 user = strchr(inbuf, ';');
461 pass = strchr(user, '=');
464 while(*trim && *trim < 33) {
474 while(*pass && *pass < 33)
478 rest = strchr(pass,',');
485 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
486 /* This is the line */
488 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
490 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
493 /* Put it back like it was */
494 fprintf(configout, orig);
501 unlink((char *)tmpin);
502 rename((char *)tmpout,(char *)tmpin);
503 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
504 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
508 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
510 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
513 static int make_file(char *dest, int len, char *dir, int num)
515 return snprintf(dest, len, "%s/msg%04d", dir, num);
519 inbuf(struct baseio *bio, FILE *fi)
526 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
541 inchar(struct baseio *bio, FILE *fi)
543 if(bio->iocp>=bio->iolen)
547 return bio->iobuf[bio->iocp++];
551 ochar(struct baseio *bio, int c, FILE *so)
553 if(bio->linelength>=BASELINELEN) {
554 if(fputs(eol,so)==EOF)
560 if(putc(((unsigned char)c),so)==EOF)
568 static int base_encode(char *filename, FILE *so)
570 unsigned char dtable[BASEMAXINLINE];
575 memset(&bio, 0, sizeof(bio));
576 bio.iocp = BASEMAXINLINE;
578 if ( !(fi = fopen(filename, "rb"))) {
579 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
587 dtable[26+i+9]= 'j'+i;
591 dtable[26+i+18]= 's'+i;
600 unsigned char igroup[3],ogroup[4];
603 igroup[0]= igroup[1]= igroup[2]= 0;
606 if ( (c = inchar(&bio, fi)) == EOF) {
611 igroup[n]= (unsigned char)c;
615 ogroup[0]= dtable[igroup[0]>>2];
616 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
617 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
618 ogroup[3]= dtable[igroup[2]&0x3F];
628 ochar(&bio, ogroup[i], so);
632 if(fputs(eol,so)==EOF)
640 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)
651 struct vm_zone *the_zone = NULL;
653 if (!strcmp(format, "wav49"))
655 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
656 p = popen(SENDMAIL, "w");
658 gethostname(host, sizeof(host));
659 if (strchr(srcemail, '@'))
660 strncpy(who, srcemail, sizeof(who)-1);
662 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
664 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
667 /* Does this user have a timezone specified? */
668 if (strlen(vmu->zonetag)) {
669 /* Find the zone in the list */
673 if (!strcmp(z->name, vmu->zonetag)) {
682 ast_localtime(&t,&tm,the_zone->timezone);
684 ast_localtime(&t,&tm,NULL);
685 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
686 fprintf(p, "Date: %s\n", date);
689 fprintf(p, "From: %s <%s>\n", fromstring, who);
691 fprintf(p, "From: Asterisk PBX <%s>\n", who);
692 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
696 fprintf(p, emailtitle, msgnum, mailbox) ;
701 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
703 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
704 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
705 fprintf(p, "MIME-Version: 1.0\n");
706 if (attach_user_voicemail) {
708 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
710 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
712 fprintf(p, "--%s\n", bound);
714 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
715 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
717 struct ast_channel *ast = ast_channel_alloc(0);
720 int vmlen = strlen(emailbody)*3 + 200;
721 if ((passdata = alloca(vmlen))) {
722 memset(passdata, 0, vmlen);
723 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
724 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
725 sprintf(passdata,"%d",msgnum);
726 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
727 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
728 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
729 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
730 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
731 fprintf(p, "%s\n",passdata);
732 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
733 ast_channel_free(ast);
734 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
736 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
738 "in mailbox %s from %s, on %s so you might\n"
739 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
740 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
742 if (attach_user_voicemail) {
743 fprintf(p, "--%s\n", bound);
744 fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum + 1, format);
745 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
746 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
747 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
749 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
750 base_encode(fname, p);
751 fprintf(p, "\n\n--%s--\n.\n", bound);
755 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
761 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
770 struct vm_zone *the_zone = NULL;
771 p = popen(SENDMAIL, "w");
774 gethostname(host, sizeof(host));
775 if (strchr(srcemail, '@'))
776 strncpy(who, srcemail, sizeof(who)-1);
778 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
780 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
783 /* Does this user have a timezone specified? */
784 if (strlen(vmu->zonetag)) {
785 /* Find the zone in the list */
789 if (!strcmp(z->name, vmu->zonetag)) {
798 ast_localtime(&t,&tm,the_zone->timezone);
800 ast_localtime(&t,&tm,NULL);
802 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
803 fprintf(p, "Date: %s\n", date);
804 fprintf(p, "From: Asterisk PBX <%s>\n", who);
805 fprintf(p, "To: %s\n", pager);
806 fprintf(p, "Subject: New VM\n\n");
807 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
808 fprintf(p, "New %s long msg in box %s\n"
809 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
812 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
818 static int get_date(char *s, int len)
824 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
827 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
831 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
832 if (ast_fileexists(fn, NULL, NULL) > 0) {
833 res = ast_streamfile(chan, fn, chan->language);
836 res = ast_waitstream(chan, ecodes);
840 res = ast_streamfile(chan, "vm-theperson", chan->language);
843 res = ast_waitstream(chan, ecodes);
846 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
851 res = ast_streamfile(chan, "vm-isonphone", chan->language);
853 res = ast_streamfile(chan, "vm-isunavail", chan->language);
856 res = ast_waitstream(chan, ecodes);
860 static int play_and_wait(struct ast_channel *chan, char *fn)
863 d = ast_streamfile(chan, fn, chan->language);
866 d = ast_waitstream(chan, AST_DIGIT_ANY);
867 ast_stopstream(chan);
871 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
875 int x, fmtcnt=1, res=-1,outmsg=0;
877 struct ast_filestream *others[MAX_OTHER_FORMATS];
878 char *sfmt[MAX_OTHER_FORMATS];
881 struct ast_dsp *sildet; /* silence detector dsp */
882 int totalsilence = 0;
884 int gotsilence = 0; /* did we timeout for silence? */
887 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
888 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
891 d = play_and_wait(chan, playfile);
893 d = ast_streamfile(chan, "beep",chan->language);
895 d = ast_waitstream(chan,"");
900 fmts = ast_strdupa(fmt);
903 strsep(&stringp, "|");
904 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
905 sfmt[0] = ast_strdupa(fmts);
907 while((fmt = strsep(&stringp, "|"))) {
908 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
909 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
912 sfmt[fmtcnt++] = ast_strdupa(fmt);
917 for (x=0;x<fmtcnt;x++) {
918 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
919 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
926 sildet = ast_dsp_new(); //Create the silence detector
928 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
931 ast_dsp_set_threshold(sildet, silencethreshold);
933 if (maxsilence > 0) {
934 rfmt = chan->readformat;
935 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
937 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
943 /* Loop forever, writing the packets we read to the writer(s), until
944 we read a # or get a hangup */
947 res = ast_waitfor(chan, 2000);
949 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
950 /* Try one more time in case of masq */
951 res = ast_waitfor(chan, 2000);
953 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
965 if (f->frametype == AST_FRAME_VOICE) {
966 /* write each format */
967 for (x=0;x<fmtcnt;x++) {
968 res = ast_writestream(others[x], f);
971 /* Silence Detection */
972 if (maxsilence > 0) {
974 ast_dsp_silence(sildet, f, &dspsilence);
976 totalsilence = dspsilence;
980 if (totalsilence > maxsilence) {
981 /* Ended happily with silence */
988 /* Exit on any error */
990 ast_log(LOG_WARNING, "Error writing frame\n");
994 } else if (f->frametype == AST_FRAME_VIDEO) {
995 /* Write only once */
996 ast_writestream(others[0], f);
997 } else if (f->frametype == AST_FRAME_DTMF) {
998 if (f->subclass == '#') {
999 if (option_verbose > 2)
1000 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1009 if (maxtime < (end - start)) {
1010 if (option_verbose > 2)
1011 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1020 if (option_verbose > 2)
1021 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1026 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1029 for (x=0;x<fmtcnt;x++) {
1033 ast_stream_rewind(others[x], totalsilence-200);
1035 ast_stream_rewind(others[x], 200);
1036 ast_truncstream(others[x]);
1037 ast_closestream(others[x]);
1040 if (ast_set_read_format(chan, rfmt)) {
1041 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1046 /* Let them know it worked */
1047 ast_streamfile(chan, "vm-msgsaved", chan->language);
1048 ast_waitstream(chan, "");
1056 static void free_user(struct ast_vm_user *vmu)
1062 static void free_zone(struct vm_zone *z)
1067 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1077 char prefile[256]="";
1085 struct ast_vm_user *vmu;
1086 struct ast_vm_user svm;
1088 strncpy(tmp, ext, sizeof(tmp) - 1);
1090 context = strchr(tmp, '@');
1096 if ((vmu = find_user(&svm, context, ext))) {
1097 /* Setup pre-file if appropriate */
1099 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1101 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1102 make_dir(dir, sizeof(dir), vmu->context, "", "");
1103 /* It's easier just to try to make it than to check for its existence */
1104 if (mkdir(dir, 0700) && (errno != EEXIST))
1105 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1106 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1107 /* It's easier just to try to make it than to check for its existence */
1108 if (mkdir(dir, 0700) && (errno != EEXIST))
1109 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1110 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1111 if (mkdir(dir, 0700) && (errno != EEXIST))
1112 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1113 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1115 /* Play the beginning intro if desired */
1116 if (strlen(prefile)) {
1117 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1118 if (ast_streamfile(chan, prefile, chan->language) > -1)
1119 res = ast_waitstream(chan, "#0");
1121 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1122 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1125 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1131 /* On a '#' we skip the instructions */
1135 if (!res && !silent) {
1136 res = ast_streamfile(chan, INTRO, chan->language);
1138 res = ast_waitstream(chan, ecodes);
1144 /* Check for a '0' here */
1146 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1147 if (strlen(chan->macrocontext))
1148 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1154 /* Unless we're *really* silent, try to send the beep */
1155 res = ast_streamfile(chan, "beep", chan->language);
1157 res = ast_waitstream(chan, "");
1163 /* The meat of recording the message... All the announcements and beeps have been played*/
1164 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1168 make_file(fn, sizeof(fn), dir, msgnum);
1169 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1170 (chan->callerid ? chan->callerid : "Unknown"),
1171 vmu->fullname, ext, chan->name);
1172 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1175 } while(msgnum < MAXMSG);
1176 if (msgnum < MAXMSG) {
1177 /* Store information */
1178 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1179 txt = fopen(txtfile, "w+");
1181 get_date(date, sizeof(date));
1185 "; Message Information file\n"
1201 chan->callerid ? chan->callerid : "Unknown",
1202 date, (long)time(NULL));
1205 ast_log(LOG_WARNING, "Error opening text file for output\n");
1206 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1209 txt = fopen(txtfile, "a");
1212 fprintf(txt, "duration=%ld\n", (long)(end-start));
1216 strsep(&stringp, "|");
1217 /* Send e-mail if applicable */
1218 if (strlen(vmu->email)) {
1219 int attach_user_voicemail = attach_voicemail;
1220 char *myserveremail = serveremail;
1221 if (vmu->attach > -1)
1222 attach_user_voicemail = vmu->attach;
1223 if (strlen(vmu->serveremail))
1224 myserveremail = vmu->serveremail;
1225 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1227 if (strlen(vmu->pager)) {
1228 char *myserveremail = serveremail;
1229 if (strlen(vmu->serveremail))
1230 myserveremail = vmu->serveremail;
1231 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1234 ast_log(LOG_WARNING, "No more messages possible\n");
1236 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1239 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1240 /* Leave voicemail for someone */
1241 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1245 static char *mbox(int id)
1273 static int count_messages(char *dir)
1277 for (x=0;x<MAXMSG;x++) {
1278 make_file(fn, sizeof(fn), dir, x);
1279 if (ast_fileexists(fn, NULL, NULL) < 1)
1285 static int say_and_wait(struct ast_channel *chan, int num)
1288 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1292 static int copy(char *infile, char *outfile)
1299 if ((ifd = open(infile, O_RDONLY)) < 0) {
1300 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1303 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1304 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1309 len = read(ifd, buf, sizeof(buf));
1311 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1317 res = write(ofd, buf, len);
1319 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1331 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1338 char *dbox = mbox(box);
1340 make_file(sfn, sizeof(sfn), dir, msg);
1341 make_dir(ddir, sizeof(ddir), context, username, dbox);
1343 for (x=0;x<MAXMSG;x++) {
1344 make_file(dfn, sizeof(dfn), ddir, x);
1345 if (ast_fileexists(dfn, NULL, NULL) < 0)
1350 ast_filecopy(sfn, dfn, NULL);
1351 if (strcmp(sfn, dfn)) {
1352 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1353 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1359 static int adsi_logo(unsigned char *buf)
1362 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1363 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1367 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1375 bytes += adsi_data_mode(buf + bytes);
1376 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1379 bytes += adsi_logo(buf);
1380 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1382 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1384 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1385 bytes += adsi_data_mode(buf + bytes);
1386 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1388 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1390 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1391 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1392 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1393 bytes += adsi_voice_mode(buf + bytes, 0);
1394 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1401 bytes += adsi_logo(buf);
1402 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1403 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1404 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1405 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1408 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1409 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1410 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1411 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1412 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1413 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1414 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1417 /* Add another dot */
1419 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1420 bytes += adsi_voice_mode(buf + bytes, 0);
1422 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1423 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1427 /* These buttons we load but don't use yet */
1428 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1429 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1430 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1431 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1432 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1433 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1434 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1437 /* Add another dot */
1439 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1440 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1441 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1446 snprintf(num, sizeof(num), "%d", x);
1447 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1449 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1450 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1453 /* Add another dot */
1455 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1456 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1457 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1460 if (adsi_end_download(chan)) {
1462 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1463 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1464 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1465 bytes += adsi_voice_mode(buf + bytes, 0);
1466 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1470 bytes += adsi_download_disconnect(buf + bytes);
1471 bytes += adsi_voice_mode(buf + bytes, 0);
1472 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1474 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1479 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1480 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1482 ast_log(LOG_DEBUG, "Restarting session...\n");
1485 /* Load the session now */
1486 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1488 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1490 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1492 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1496 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1499 if (!adsi_available(chan))
1501 x = adsi_load_session(chan, adapp, adver, 1);
1505 if (adsi_load_vmail(chan, useadsi)) {
1506 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1513 static void adsi_login(struct ast_channel *chan)
1517 unsigned char keys[8];
1519 if (!adsi_available(chan))
1524 /* Set one key for next */
1525 keys[3] = ADSI_KEY_APPS + 3;
1527 bytes += adsi_logo(buf + bytes);
1528 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1529 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1530 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1531 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1532 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1533 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1534 bytes += adsi_set_keys(buf + bytes, keys);
1535 bytes += adsi_voice_mode(buf + bytes, 0);
1536 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1539 static void adsi_password(struct ast_channel *chan)
1543 unsigned char keys[8];
1545 if (!adsi_available(chan))
1550 /* Set one key for next */
1551 keys[3] = ADSI_KEY_APPS + 3;
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);
1637 /* No prev key, provide "Folder" instead */
1638 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1641 /* If last message ... */
1643 /* but not only message, provide "Folder" instead */
1644 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1645 bytes += adsi_voice_mode(buf + bytes, 0);
1648 /* Otherwise if only message, leave blank */
1654 ast_callerid_parse(cid, &name, &num);
1658 name = "Unknown Caller";
1660 /* If deleted, show "undeleted" */
1663 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1666 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1667 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1668 strcasecmp(folder, "INBOX") ? " Messages" : "");
1669 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1671 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1672 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1673 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1674 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1675 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1676 bytes += adsi_set_keys(buf + bytes, keys);
1677 bytes += adsi_voice_mode(buf + bytes, 0);
1679 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1682 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1686 unsigned char keys[8];
1690 if (!adsi_available(chan))
1693 /* New meaning for keys */
1695 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1701 /* No prev key, provide "Folder" instead */
1702 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1705 /* If last message ... */
1707 /* but not only message, provide "Folder" instead */
1708 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1710 /* Otherwise if only message, leave blank */
1715 /* If deleted, show "undeleted" */
1717 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1720 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1721 bytes += adsi_set_keys(buf + bytes, keys);
1722 bytes += adsi_voice_mode(buf + bytes, 0);
1724 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1727 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1729 char buf[256], buf1[256], buf2[256];
1731 unsigned char keys[8];
1734 char *newm = (new == 1) ? "message" : "messages";
1735 char *oldm = (old == 1) ? "message" : "messages";
1736 if (!adsi_available(chan))
1739 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1741 strcat(buf1, " and");
1742 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1744 snprintf(buf2, sizeof(buf2), "%s.", newm);
1747 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1748 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1750 strcpy(buf1, "You have no messages.");
1753 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1754 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1755 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1758 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1762 /* Don't let them listen if there are none */
1765 bytes += adsi_set_keys(buf + bytes, keys);
1767 bytes += adsi_voice_mode(buf + bytes, 0);
1769 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1772 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1774 char buf[256], buf1[256], buf2[256];
1776 unsigned char keys[8];
1779 char *mess = (messages == 1) ? "message" : "messages";
1781 if (!adsi_available(chan))
1784 /* Original command keys */
1786 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1794 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1795 strcasecmp(folder, "INBOX") ? " folder" : "");
1798 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1800 strcpy(buf2, "no messages.");
1801 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1802 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1803 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1804 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1805 bytes += adsi_set_keys(buf + bytes, keys);
1807 bytes += adsi_voice_mode(buf + bytes, 0);
1809 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1813 static void adsi_clear(struct ast_channel *chan)
1817 if (!adsi_available(chan))
1819 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1820 bytes += adsi_voice_mode(buf + bytes, 0);
1822 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1825 static void adsi_goodbye(struct ast_channel *chan)
1830 if (!adsi_available(chan))
1832 bytes += adsi_logo(buf + bytes);
1833 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1834 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1835 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1836 bytes += adsi_voice_mode(buf + bytes, 0);
1838 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1841 static int get_folder(struct ast_channel *chan, int start)
1846 d = play_and_wait(chan, "vm-press");
1849 for (x = start; x< 5; x++) {
1850 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1852 d = play_and_wait(chan, "vm-for");
1855 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1856 d = play_and_wait(chan, fn);
1859 d = play_and_wait(chan, "vm-messages");
1862 d = ast_waitfordigit(chan, 500);
1866 d = play_and_wait(chan, "vm-tocancel");
1869 d = ast_waitfordigit(chan, 4000);
1873 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1876 res = play_and_wait(chan, fn);
1877 while (((res < '0') || (res > '9')) &&
1878 (res != '#') && (res >= 0)) {
1879 res = get_folder(chan, 0);
1885 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1892 struct ast_config *mif;
1897 struct ast_vm_user *receiver, srec;
1902 res = ast_streamfile(chan, "vm-extension", chan->language);
1905 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1907 if ((receiver = find_user(&srec, context, username))) {
1908 /* if (play_and_wait(chan, "vm-savedto"))
1912 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1913 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1914 ast_log(LOG_DEBUG, sys);
1917 todircount = count_messages(todir);
1918 strncpy(tmp, fmt, sizeof(tmp));
1920 while((s = strsep(&stringp, "|"))) {
1921 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1922 ast_log(LOG_DEBUG, sys);
1925 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1926 ast_log(LOG_DEBUG, sys);
1928 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1930 /* load the information on the source message so we can send an e-mail like a new message */
1931 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1932 if ((mif=ast_load(miffile))) {
1934 /* set callerid and duration variables */
1935 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1936 duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1938 if (strlen(receiver->email)) {
1939 int attach_user_voicemail = attach_voicemail;
1940 char *myserveremail = serveremail;
1941 if (receiver->attach > -1)
1942 attach_user_voicemail = receiver->attach;
1943 if (strlen(receiver->serveremail))
1944 myserveremail = receiver->serveremail;
1945 sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1948 if (strlen(receiver->pager)) {
1949 char *myserveremail = serveremail;
1950 if (strlen(receiver->serveremail))
1951 myserveremail = receiver->serveremail;
1952 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver);
1955 ast_destroy(mif); /* or here */
1957 /* Leave voicemail for someone */
1958 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1960 /* give confirmatopm that the message was saved */
1961 res = play_and_wait(chan, "vm-message");
1963 res = play_and_wait(chan, "vm-saved");
1964 free_user(receiver);
1967 res = play_and_wait(chan, "pbx-invalid");
1980 int deleted[MAXMSG];
1991 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1994 if ((res = ast_streamfile(chan, file, chan->language)))
1995 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1997 res = ast_waitstream(chan, AST_DIGIT_ANY);
2001 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2004 if ((res = ast_streamfile(chan, file, chan->language)))
2005 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2007 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2011 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2014 char filename[256], *origtime;
2015 struct vm_zone *the_zone = NULL;
2016 struct ast_config *msg_cfg;
2020 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2021 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2022 msg_cfg = ast_load(filename);
2024 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2028 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2030 if (sscanf(origtime,"%ld",&tin) < 1) {
2031 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2035 ast_destroy(msg_cfg);
2037 /* Does this user have a timezone specified? */
2038 if (strlen(vmu->zonetag)) {
2039 /* Find the zone in the list */
2043 if (!strcmp(z->name, vmu->zonetag)) {
2051 /* No internal variable parsing for now, so we'll comment it out for the time being */
2053 /* Set the DIFF_* variables */
2054 localtime_r(&t, &time_now);
2055 gettimeofday(&tv_now,NULL);
2056 tnow = tv_now.tv_sec;
2057 localtime_r(&tnow,&time_then);
2059 /* Day difference */
2060 if (time_now.tm_year == time_then.tm_year)
2061 sprintf(temp,"%d",time_now.tm_yday);
2063 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2064 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2066 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2069 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2071 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2073 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2078 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2082 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2083 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2085 res = wait_file2(chan, vms, "vm-first");
2086 else if (msg == vms->lastmsg)
2087 res = wait_file2(chan, vms, "vm-last");
2089 res = wait_file2(chan, vms, "vm-message");
2090 if (msg && (msg != vms->lastmsg)) {
2092 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2097 res = play_message_datetime(chan,vmu,vms);
2100 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2101 vms->heard[msg] = 1;
2102 res = wait_file(chan, vms, vms->fn);
2107 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2109 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2110 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2111 vms->lastmsg = count_messages(vms->curdir) - 1;
2112 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2115 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2118 char ntxt[256] = "";
2120 if (vms->lastmsg > -1) {
2121 /* Get the deleted messages fixed */
2123 for (x=0;x < MAXMSG;x++) {
2124 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2125 /* Save this message. It's not in INBOX or hasn't been heard */
2126 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2127 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2130 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2131 if (strcmp(vms->fn, vms->fn2)) {
2132 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2133 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2134 ast_filerename(vms->fn, vms->fn2, NULL);
2137 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2138 /* Move to old folder before deleting */
2139 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2142 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2143 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2144 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2146 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2147 ast_filedelete(vms->fn, NULL);
2151 memset(vms->deleted, 0, sizeof(vms->deleted));
2152 memset(vms->heard, 0, sizeof(vms->heard));
2155 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2157 /* Introduce messages they have */
2159 res = play_and_wait(chan, "vm-youhave");
2161 if (vms->newmessages) {
2162 res = say_and_wait(chan, vms->newmessages);
2164 res = play_and_wait(chan, "vm-INBOX");
2165 if (vms->oldmessages && !res)
2166 res = play_and_wait(chan, "vm-and");
2168 if ((vms->newmessages == 1))
2169 res = play_and_wait(chan, "vm-message");
2171 res = play_and_wait(chan, "vm-messages");
2175 if (!res && vms->oldmessages) {
2176 res = say_and_wait(chan, vms->oldmessages);
2178 res = play_and_wait(chan, "vm-Old");
2180 if (vms->oldmessages == 1)
2181 res = play_and_wait(chan, "vm-message");
2183 res = play_and_wait(chan, "vm-messages");
2187 if (!vms->oldmessages && !vms->newmessages) {
2188 res = play_and_wait(chan, "vm-no");
2190 res = play_and_wait(chan, "vm-messages");
2197 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2200 /* Play instructions and wait for new command */
2202 if (vms->starting) {
2203 if (vms->lastmsg > -1) {
2204 res = play_and_wait(chan, "vm-onefor");
2206 res = play_and_wait(chan, vms->vmbox);
2208 res = play_and_wait(chan, "vm-messages");
2211 res = play_and_wait(chan, "vm-opts");
2214 res = play_and_wait(chan, "vm-prev");
2216 res = play_and_wait(chan, "vm-repeat");
2217 if (!res && (vms->curmsg != vms->lastmsg))
2218 res = play_and_wait(chan, "vm-next");
2220 if (!vms->deleted[vms->curmsg])
2221 res = play_and_wait(chan, "vm-delete");
2223 res = play_and_wait(chan, "vm-undelete");
2225 res = play_and_wait(chan, "vm-toforward");
2227 res = play_and_wait(chan, "vm-savemessage");
2231 res = play_and_wait(chan, "vm-helpexit");
2233 res = ast_waitfordigit(chan, 6000);
2236 if (vms->repeats > 2) {
2237 res = play_and_wait(chan, "vm-goodbye");
2246 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2250 char newpassword[80] = "";
2251 char newpassword2[80] = "";
2252 char prefile[256]="";
2256 if (adsi_available(chan))
2258 bytes += adsi_logo(buf + bytes);
2259 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2260 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2261 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2262 bytes += adsi_voice_mode(buf + bytes, 0);
2263 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2265 while((cmd >= 0) && (cmd != 't')) {
2270 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2271 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2274 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2275 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2278 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2279 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2282 newpassword[1] = '\0';
2283 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2286 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2289 newpassword2[1] = '\0';
2290 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2294 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2297 if (strcmp(newpassword, newpassword2)) {
2298 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2299 cmd = play_and_wait(chan, "vm-mismatch");
2302 vm_change_password(vmu,newpassword);
2303 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2304 cmd = play_and_wait(chan,"vm-passchanged");
2310 cmd = play_and_wait(chan,"vm-options");
2312 cmd = ast_waitfordigit(chan,6000);
2324 static int vm_execmain(struct ast_channel *chan, void *data)
2326 /* XXX This is, admittedly, some pretty horrendus code. For some
2327 reason it just seemed a lot easier to do with GOTO's. I feel
2328 like I'm back in my GWBASIC days. XXX */
2333 struct localuser *u;
2334 char prefixstr[80] ="";
2335 char empty[80] = "";
2339 char tmp[256], *ext;
2340 char fmtc[256] = "";
2342 struct vm_state vms;
2344 struct ast_vm_user *vmu = NULL, vmus;
2348 memset(&vms, 0, sizeof(vms));
2349 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2350 if (chan->_state != AST_STATE_UP)
2353 if (data && strlen(data)) {
2354 strncpy(tmp, data, sizeof(tmp) - 1);
2359 /* We should skip the user's password */
2364 /* We should prefix the mailbox with the supplied data */
2370 context = strchr(ext, '@');
2377 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2379 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2380 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2387 /* If ADSI is supported, setup login screen */
2388 adsi_begin(chan, &useadsi);
2389 if (!skipuser && useadsi)
2391 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2392 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2396 /* Authenticate them and get their mailbox/password */
2398 while (!valid && (logretries < maxlogins)) {
2399 /* Prompt for, and read in the username */
2400 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2401 ast_log(LOG_WARNING, "Couldn't read username\n");
2404 if (!strlen(vms.username)) {
2405 if (option_verbose > 2)
2406 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2411 adsi_password(chan);
2412 if (ast_streamfile(chan, "vm-password", chan->language)) {
2413 ast_log(LOG_WARNING, "Unable to stream password file\n");
2416 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2417 ast_log(LOG_WARNING, "Unable to read password\n");
2421 char fullusername[80] = "";
2422 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2423 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2424 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2427 vmu = find_user(&vmus, context, vms.username);
2428 if (vmu && !strcmp(vmu->password, password))
2431 if (option_verbose > 2)
2432 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2434 strncpy(vms.username, empty, sizeof(vms.username) -1);
2439 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2444 if (!valid && (logretries >= maxlogins)) {
2445 ast_stopstream(chan);
2446 res = play_and_wait(chan, "vm-goodbye");
2452 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2453 mkdir(vms.curdir, 0700);
2454 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2455 mkdir(vms.curdir, 0700);
2456 /* Retrieve old and new message counts */
2457 open_mailbox(&vms, vmu, 1);
2458 vms.oldmessages = vms.lastmsg + 1;
2459 /* Start in INBOX */
2460 open_mailbox(&vms, vmu, 0);
2461 vms.newmessages = vms.lastmsg + 1;
2464 /* Select proper mailbox FIRST!! */
2465 if (!vms.newmessages && vms.oldmessages) {
2466 /* If we only have old messages start here */
2467 open_mailbox(&vms, vmu, 1);
2471 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2473 cmd = vm_intro(chan, &vms);
2476 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2483 if (vms.lastmsg > -1) {
2484 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2486 cmd = play_and_wait(chan, "vm-youhave");
2488 cmd = play_and_wait(chan, "vm-no");
2490 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2491 cmd = play_and_wait(chan, vms.fn);
2494 cmd = play_and_wait(chan, "vm-messages");
2497 case '2': /* Change folders */
2499 adsi_folders(chan, 0, "Change to folder...");
2500 cmd = get_folder2(chan, "vm-changeto", 0);
2503 } else if (cmd > 0) {
2505 close_mailbox(&vms, vmu);
2506 open_mailbox(&vms, vmu, cmd);
2510 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2512 cmd = play_and_wait(chan, vms.vmbox);
2514 cmd = play_and_wait(chan, "vm-messages");
2520 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2522 cmd = play_and_wait(chan, "vm-nomore");
2526 if (vms.curmsg < vms.lastmsg) {
2528 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2530 cmd = play_and_wait(chan, "vm-nomore");
2534 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2536 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2537 if (vms.deleted[vms.curmsg])
2538 cmd = play_and_wait(chan, "vm-deleted");
2540 cmd = play_and_wait(chan, "vm-undeleted");
2543 if(vms.lastmsg > -1)
2544 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2546 cmd = play_and_wait(chan, "vm-nomore");
2550 adsi_folders(chan, 1, "Save to folder...");
2551 cmd = get_folder2(chan, "vm-savefolder", 1);
2552 box = 0; /* Shut up compiler */
2556 } else if (cmd > 0) {
2557 box = cmd = cmd - '0';
2558 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2559 vms.deleted[vms.curmsg]=1;
2561 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2563 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2565 cmd = play_and_wait(chan, "vm-message");
2567 cmd = say_and_wait(chan, vms.curmsg + 1);
2569 cmd = play_and_wait(chan, "vm-savedto");
2571 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2572 cmd = play_and_wait(chan, vms.fn);
2575 cmd = play_and_wait(chan, "vm-messages");
2578 if (!vms.starting) {
2579 cmd = play_and_wait(chan, "vm-onefor");
2581 cmd = play_and_wait(chan, vms.vmbox);
2583 cmd = play_and_wait(chan, "vm-messages");
2585 cmd = play_and_wait(chan, "vm-opts");
2590 cmd = vm_options(chan, vmu, &vms, vmfmts);
2592 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2594 default: /* Nothing */
2595 cmd = vm_instructions(chan, &vms);
2599 if ((cmd == 't') || (cmd == '#')) {
2609 ast_stopstream(chan);
2612 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(capp, vm_exec, synopsis_vm, descrip_vm);
2939 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2940 res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain);
2944 if ((res=load_config())) {
2948 if ((res = sql_init())) {
2949 ast_log(LOG_WARNING, "SQL init\n");
2955 char *description(void)
2963 STANDARD_USECOUNT(res);
2969 return ASTERISK_GPL_KEY;