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);
870 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
874 int x, fmtcnt=1, res=-1,outmsg=0;
876 struct ast_filestream *others[MAX_OTHER_FORMATS];
877 char *sfmt[MAX_OTHER_FORMATS];
880 struct ast_dsp *sildet; /* silence detector dsp */
881 int totalsilence = 0;
883 int gotsilence = 0; /* did we timeout for silence? */
886 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
887 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
890 d = play_and_wait(chan, playfile);
892 d = ast_streamfile(chan, "beep",chan->language);
894 d = ast_waitstream(chan,"");
899 fmts = ast_strdupa(fmt);
902 strsep(&stringp, "|");
903 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
904 sfmt[0] = ast_strdupa(fmts);
906 while((fmt = strsep(&stringp, "|"))) {
907 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
908 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
911 sfmt[fmtcnt++] = ast_strdupa(fmt);
916 for (x=0;x<fmtcnt;x++) {
917 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
918 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
925 sildet = ast_dsp_new(); //Create the silence detector
927 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
930 ast_dsp_set_threshold(sildet, silencethreshold);
932 if (maxsilence > 0) {
933 rfmt = chan->readformat;
934 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
936 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
942 /* Loop forever, writing the packets we read to the writer(s), until
943 we read a # or get a hangup */
946 res = ast_waitfor(chan, 2000);
948 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
949 /* Try one more time in case of masq */
950 res = ast_waitfor(chan, 2000);
952 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
964 if (f->frametype == AST_FRAME_VOICE) {
965 /* write each format */
966 for (x=0;x<fmtcnt;x++) {
967 res = ast_writestream(others[x], f);
970 /* Silence Detection */
971 if (maxsilence > 0) {
973 ast_dsp_silence(sildet, f, &dspsilence);
975 totalsilence = dspsilence;
979 if (totalsilence > maxsilence) {
980 /* Ended happily with silence */
987 /* Exit on any error */
989 ast_log(LOG_WARNING, "Error writing frame\n");
993 } else if (f->frametype == AST_FRAME_VIDEO) {
994 /* Write only once */
995 ast_writestream(others[0], f);
996 } else if (f->frametype == AST_FRAME_DTMF) {
997 if (f->subclass == '#') {
998 if (option_verbose > 2)
999 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1008 if (maxtime < (end - start)) {
1009 if (option_verbose > 2)
1010 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1019 if (option_verbose > 2)
1020 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1025 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1028 for (x=0;x<fmtcnt;x++) {
1032 ast_stream_rewind(others[x], totalsilence-200);
1034 ast_stream_rewind(others[x], 200);
1035 ast_truncstream(others[x]);
1036 ast_closestream(others[x]);
1039 if (ast_set_read_format(chan, rfmt)) {
1040 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1045 /* Let them know it worked */
1046 ast_streamfile(chan, "vm-msgsaved", chan->language);
1047 ast_waitstream(chan, "");
1055 static void free_user(struct ast_vm_user *vmu)
1061 static void free_zone(struct vm_zone *z)
1066 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1076 char prefile[256]="";
1084 struct ast_vm_user *vmu;
1085 struct ast_vm_user svm;
1087 strncpy(tmp, ext, sizeof(tmp) - 1);
1089 context = strchr(tmp, '@');
1095 if ((vmu = find_user(&svm, context, ext))) {
1096 /* Setup pre-file if appropriate */
1098 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1100 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1101 make_dir(dir, sizeof(dir), vmu->context, "", "");
1102 /* It's easier just to try to make it than to check for its existence */
1103 if (mkdir(dir, 0700) && (errno != EEXIST))
1104 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1105 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1106 /* It's easier just to try to make it than to check for its existence */
1107 if (mkdir(dir, 0700) && (errno != EEXIST))
1108 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1109 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1110 if (mkdir(dir, 0700) && (errno != EEXIST))
1111 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1112 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1114 /* Play the beginning intro if desired */
1115 if (strlen(prefile)) {
1116 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1117 if (ast_streamfile(chan, prefile, chan->language) > -1)
1118 res = ast_waitstream(chan, "#0");
1120 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1121 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1124 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1130 /* On a '#' we skip the instructions */
1134 if (!res && !silent) {
1135 res = ast_streamfile(chan, INTRO, chan->language);
1137 res = ast_waitstream(chan, ecodes);
1143 /* Check for a '0' here */
1145 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1146 if (strlen(chan->macrocontext))
1147 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1153 /* Unless we're *really* silent, try to send the beep */
1154 res = ast_streamfile(chan, "beep", chan->language);
1156 res = ast_waitstream(chan, "");
1162 /* The meat of recording the message... All the announcements and beeps have been played*/
1163 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1167 make_file(fn, sizeof(fn), dir, msgnum);
1168 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1169 (chan->callerid ? chan->callerid : "Unknown"),
1170 vmu->fullname, ext, chan->name);
1171 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1174 } while(msgnum < MAXMSG);
1175 if (msgnum < MAXMSG) {
1176 /* Store information */
1177 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1178 txt = fopen(txtfile, "w+");
1180 get_date(date, sizeof(date));
1184 "; Message Information file\n"
1200 chan->callerid ? chan->callerid : "Unknown",
1201 date, (long)time(NULL));
1204 ast_log(LOG_WARNING, "Error opening text file for output\n");
1205 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1208 txt = fopen(txtfile, "a");
1211 fprintf(txt, "duration=%ld\n", (long)(end-start));
1215 strsep(&stringp, "|");
1216 /* Send e-mail if applicable */
1217 if (strlen(vmu->email)) {
1218 int attach_user_voicemail = attach_voicemail;
1219 char *myserveremail = serveremail;
1220 if (vmu->attach > -1)
1221 attach_user_voicemail = vmu->attach;
1222 if (strlen(vmu->serveremail))
1223 myserveremail = vmu->serveremail;
1224 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1226 if (strlen(vmu->pager)) {
1227 char *myserveremail = serveremail;
1228 if (strlen(vmu->serveremail))
1229 myserveremail = vmu->serveremail;
1230 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1233 ast_log(LOG_WARNING, "No more messages possible\n");
1235 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1238 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1239 /* Leave voicemail for someone */
1240 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1244 static char *mbox(int id)
1272 static int count_messages(char *dir)
1276 for (x=0;x<MAXMSG;x++) {
1277 make_file(fn, sizeof(fn), dir, x);
1278 if (ast_fileexists(fn, NULL, NULL) < 1)
1284 static int say_and_wait(struct ast_channel *chan, int num)
1287 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1291 static int copy(char *infile, char *outfile)
1298 if ((ifd = open(infile, O_RDONLY)) < 0) {
1299 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1302 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1303 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1308 len = read(ifd, buf, sizeof(buf));
1310 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1316 res = write(ofd, buf, len);
1318 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1330 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1337 char *dbox = mbox(box);
1339 make_file(sfn, sizeof(sfn), dir, msg);
1340 make_dir(ddir, sizeof(ddir), context, username, dbox);
1342 for (x=0;x<MAXMSG;x++) {
1343 make_file(dfn, sizeof(dfn), ddir, x);
1344 if (ast_fileexists(dfn, NULL, NULL) < 0)
1349 ast_filecopy(sfn, dfn, NULL);
1350 if (strcmp(sfn, dfn)) {
1351 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1352 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1358 static int adsi_logo(unsigned char *buf)
1361 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1362 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1366 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1374 bytes += adsi_data_mode(buf + bytes);
1375 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1378 bytes += adsi_logo(buf);
1379 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1381 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1383 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1384 bytes += adsi_data_mode(buf + bytes);
1385 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1387 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1389 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1390 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1391 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1392 bytes += adsi_voice_mode(buf + bytes, 0);
1393 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1400 bytes += adsi_logo(buf);
1401 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1402 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1403 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1404 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1407 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1408 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1409 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1410 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1411 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1412 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1413 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1416 /* Add another dot */
1418 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1419 bytes += adsi_voice_mode(buf + bytes, 0);
1421 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1422 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1426 /* These buttons we load but don't use yet */
1427 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1428 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1429 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1430 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1431 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1432 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1433 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1436 /* Add another dot */
1438 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1439 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1440 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1445 snprintf(num, sizeof(num), "%d", x);
1446 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1448 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1449 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1452 /* Add another dot */
1454 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1455 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1456 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1459 if (adsi_end_download(chan)) {
1461 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1462 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1463 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1464 bytes += adsi_voice_mode(buf + bytes, 0);
1465 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1469 bytes += adsi_download_disconnect(buf + bytes);
1470 bytes += adsi_voice_mode(buf + bytes, 0);
1471 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1473 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1478 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1479 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1481 ast_log(LOG_DEBUG, "Restarting session...\n");
1484 /* Load the session now */
1485 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1487 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1489 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1491 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1495 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1498 if (!adsi_available(chan))
1500 x = adsi_load_session(chan, adapp, adver, 1);
1504 if (adsi_load_vmail(chan, useadsi)) {
1505 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1512 static void adsi_login(struct ast_channel *chan)
1516 unsigned char keys[8];
1518 if (!adsi_available(chan))
1523 /* Set one key for next */
1524 keys[3] = ADSI_KEY_APPS + 3;
1526 bytes += adsi_logo(buf + bytes);
1527 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1528 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1529 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1530 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1531 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1532 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1533 bytes += adsi_set_keys(buf + bytes, keys);
1534 bytes += adsi_voice_mode(buf + bytes, 0);
1535 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1538 static void adsi_password(struct ast_channel *chan)
1542 unsigned char keys[8];
1544 if (!adsi_available(chan))
1549 /* Set one key for next */
1550 keys[3] = ADSI_KEY_APPS + 3;
1552 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1553 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1554 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1555 bytes += adsi_set_keys(buf + bytes, keys);
1556 bytes += adsi_voice_mode(buf + bytes, 0);
1557 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1560 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1564 unsigned char keys[8];
1567 if (!adsi_available(chan))
1571 y = ADSI_KEY_APPS + 12 + start + x;
1572 if (y > ADSI_KEY_APPS + 12 + 4)
1574 keys[x] = ADSI_KEY_SKT | y;
1576 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1580 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1581 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1582 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1583 bytes += adsi_set_keys(buf + bytes, keys);
1584 bytes += adsi_voice_mode(buf + bytes, 0);
1586 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1589 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1592 char buf[256], buf1[256], buf2[256];
1598 char datetime[21]="";
1601 unsigned char keys[8];
1605 if (!adsi_available(chan))
1608 /* Retrieve important info */
1609 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1610 f = fopen(fn2, "r");
1613 fgets(buf, sizeof(buf), f);
1617 strsep(&stringp, "=");
1618 val = strsep(&stringp, "=");
1619 if (val && strlen(val)) {
1620 if (!strcmp(buf, "callerid"))
1621 strncpy(cid, val, sizeof(cid) - 1);
1622 if (!strcmp(buf, "origdate"))
1623 strncpy(datetime, val, sizeof(datetime) - 1);
1629 /* New meaning for keys */
1631 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1636 /* No prev key, provide "Folder" instead */
1637 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1640 /* If last message ... */
1642 /* but not only message, provide "Folder" instead */
1643 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1644 bytes += adsi_voice_mode(buf + bytes, 0);
1647 /* Otherwise if only message, leave blank */
1653 ast_callerid_parse(cid, &name, &num);
1657 name = "Unknown Caller";
1659 /* If deleted, show "undeleted" */
1662 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1665 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1666 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1667 strcasecmp(folder, "INBOX") ? " Messages" : "");
1668 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1670 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1671 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1672 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1673 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1674 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1675 bytes += adsi_set_keys(buf + bytes, keys);
1676 bytes += adsi_voice_mode(buf + bytes, 0);
1678 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1681 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1685 unsigned char keys[8];
1689 if (!adsi_available(chan))
1692 /* New meaning for keys */
1694 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1700 /* No prev key, provide "Folder" instead */
1701 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1704 /* If last message ... */
1706 /* but not only message, provide "Folder" instead */
1707 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1709 /* Otherwise if only message, leave blank */
1714 /* If deleted, show "undeleted" */
1716 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1719 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1720 bytes += adsi_set_keys(buf + bytes, keys);
1721 bytes += adsi_voice_mode(buf + bytes, 0);
1723 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1726 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1728 char buf[256], buf1[256], buf2[256];
1730 unsigned char keys[8];
1733 char *newm = (new == 1) ? "message" : "messages";
1734 char *oldm = (old == 1) ? "message" : "messages";
1735 if (!adsi_available(chan))
1738 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1740 strcat(buf1, " and");
1741 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1743 snprintf(buf2, sizeof(buf2), "%s.", newm);
1746 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1747 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1749 strcpy(buf1, "You have no messages.");
1752 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1753 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1754 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1757 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1761 /* Don't let them listen if there are none */
1764 bytes += adsi_set_keys(buf + bytes, keys);
1766 bytes += adsi_voice_mode(buf + bytes, 0);
1768 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1771 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1773 char buf[256], buf1[256], buf2[256];
1775 unsigned char keys[8];
1778 char *mess = (messages == 1) ? "message" : "messages";
1780 if (!adsi_available(chan))
1783 /* Original command keys */
1785 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1793 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1794 strcasecmp(folder, "INBOX") ? " folder" : "");
1797 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1799 strcpy(buf2, "no messages.");
1800 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1801 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1802 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1803 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1804 bytes += adsi_set_keys(buf + bytes, keys);
1806 bytes += adsi_voice_mode(buf + bytes, 0);
1808 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1812 static void adsi_clear(struct ast_channel *chan)
1816 if (!adsi_available(chan))
1818 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1819 bytes += adsi_voice_mode(buf + bytes, 0);
1821 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1824 static void adsi_goodbye(struct ast_channel *chan)
1829 if (!adsi_available(chan))
1831 bytes += adsi_logo(buf + bytes);
1832 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1833 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1834 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1835 bytes += adsi_voice_mode(buf + bytes, 0);
1837 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1840 static int get_folder(struct ast_channel *chan, int start)
1845 d = play_and_wait(chan, "vm-press");
1848 for (x = start; x< 5; x++) {
1849 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1851 d = play_and_wait(chan, "vm-for");
1854 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1855 d = play_and_wait(chan, fn);
1858 d = play_and_wait(chan, "vm-messages");
1861 d = ast_waitfordigit(chan, 500);
1865 d = play_and_wait(chan, "vm-tocancel");
1868 d = ast_waitfordigit(chan, 4000);
1872 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1875 res = play_and_wait(chan, fn);
1876 while (((res < '0') || (res > '9')) &&
1877 (res != '#') && (res >= 0)) {
1878 res = get_folder(chan, 0);
1884 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1891 struct ast_config *mif;
1896 struct ast_vm_user *receiver, srec;
1901 res = ast_streamfile(chan, "vm-extension", chan->language);
1904 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1906 if ((receiver = find_user(&srec, context, username))) {
1907 /* if (play_and_wait(chan, "vm-savedto"))
1911 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1912 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1913 ast_log(LOG_DEBUG, sys);
1916 todircount = count_messages(todir);
1917 strncpy(tmp, fmt, sizeof(tmp));
1919 while((s = strsep(&stringp, "|"))) {
1920 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1921 ast_log(LOG_DEBUG, sys);
1924 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1925 ast_log(LOG_DEBUG, sys);
1927 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1929 /* load the information on the source message so we can send an e-mail like a new message */
1930 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1931 if ((mif=ast_load(miffile))) {
1933 /* set callerid and duration variables */
1934 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1935 duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1937 if (strlen(receiver->email)) {
1938 int attach_user_voicemail = attach_voicemail;
1939 char *myserveremail = serveremail;
1940 if (receiver->attach > -1)
1941 attach_user_voicemail = receiver->attach;
1942 if (strlen(receiver->serveremail))
1943 myserveremail = receiver->serveremail;
1944 sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1947 if (strlen(receiver->pager)) {
1948 char *myserveremail = serveremail;
1949 if (strlen(receiver->serveremail))
1950 myserveremail = receiver->serveremail;
1951 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver);
1954 ast_destroy(mif); /* or here */
1956 /* Leave voicemail for someone */
1957 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1959 /* give confirmatopm that the message was saved */
1960 res = play_and_wait(chan, "vm-message");
1962 res = play_and_wait(chan, "vm-saved");
1963 free_user(receiver);
1966 res = play_and_wait(chan, "pbx-invalid");
1979 int deleted[MAXMSG];
1990 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1993 if ((res = ast_streamfile(chan, file, chan->language)))
1994 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1996 res = ast_waitstream(chan, AST_DIGIT_ANY);
2000 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2003 if ((res = ast_streamfile(chan, file, chan->language)))
2004 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2006 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2010 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2013 char filename[256], *origtime;
2014 struct vm_zone *the_zone = NULL;
2015 struct ast_config *msg_cfg;
2019 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2020 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2021 msg_cfg = ast_load(filename);
2023 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2027 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2029 if (sscanf(origtime,"%ld",&tin) < 1) {
2030 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2034 ast_destroy(msg_cfg);
2036 /* Does this user have a timezone specified? */
2037 if (strlen(vmu->zonetag)) {
2038 /* Find the zone in the list */
2042 if (!strcmp(z->name, vmu->zonetag)) {
2050 /* No internal variable parsing for now, so we'll comment it out for the time being */
2052 /* Set the DIFF_* variables */
2053 localtime_r(&t, &time_now);
2054 gettimeofday(&tv_now,NULL);
2055 tnow = tv_now.tv_sec;
2056 localtime_r(&tnow,&time_then);
2058 /* Day difference */
2059 if (time_now.tm_year == time_then.tm_year)
2060 sprintf(temp,"%d",time_now.tm_yday);
2062 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2063 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2065 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2068 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2070 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2072 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2077 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2081 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2082 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2084 res = wait_file2(chan, vms, "vm-first");
2085 else if (msg == vms->lastmsg)
2086 res = wait_file2(chan, vms, "vm-last");
2088 res = wait_file2(chan, vms, "vm-message");
2089 if (msg && (msg != vms->lastmsg)) {
2091 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2096 res = play_message_datetime(chan,vmu,vms);
2099 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2100 vms->heard[msg] = 1;
2101 res = wait_file(chan, vms, vms->fn);
2106 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2108 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2109 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2110 vms->lastmsg = count_messages(vms->curdir) - 1;
2111 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2114 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2117 char ntxt[256] = "";
2119 if (vms->lastmsg > -1) {
2120 /* Get the deleted messages fixed */
2122 for (x=0;x < MAXMSG;x++) {
2123 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2124 /* Save this message. It's not in INBOX or hasn't been heard */
2125 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2126 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2129 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2130 if (strcmp(vms->fn, vms->fn2)) {
2131 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2132 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2133 ast_filerename(vms->fn, vms->fn2, NULL);
2136 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2137 /* Move to old folder before deleting */
2138 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2141 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2142 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2143 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2145 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2146 ast_filedelete(vms->fn, NULL);
2150 memset(vms->deleted, 0, sizeof(vms->deleted));
2151 memset(vms->heard, 0, sizeof(vms->heard));
2154 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2156 /* Introduce messages they have */
2158 res = play_and_wait(chan, "vm-youhave");
2160 if (vms->newmessages) {
2161 res = say_and_wait(chan, vms->newmessages);
2163 res = play_and_wait(chan, "vm-INBOX");
2164 if (vms->oldmessages && !res)
2165 res = play_and_wait(chan, "vm-and");
2167 if ((vms->newmessages == 1))
2168 res = play_and_wait(chan, "vm-message");
2170 res = play_and_wait(chan, "vm-messages");
2174 if (!res && vms->oldmessages) {
2175 res = say_and_wait(chan, vms->oldmessages);
2177 res = play_and_wait(chan, "vm-Old");
2179 if (vms->oldmessages == 1)
2180 res = play_and_wait(chan, "vm-message");
2182 res = play_and_wait(chan, "vm-messages");
2186 if (!vms->oldmessages && !vms->newmessages) {
2187 res = play_and_wait(chan, "vm-no");
2189 res = play_and_wait(chan, "vm-messages");
2196 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2199 /* Play instructions and wait for new command */
2201 if (vms->starting) {
2202 if (vms->lastmsg > -1) {
2203 res = play_and_wait(chan, "vm-onefor");
2205 res = play_and_wait(chan, vms->vmbox);
2207 res = play_and_wait(chan, "vm-messages");
2210 res = play_and_wait(chan, "vm-opts");
2213 res = play_and_wait(chan, "vm-prev");
2215 res = play_and_wait(chan, "vm-repeat");
2216 if (!res && (vms->curmsg != vms->lastmsg))
2217 res = play_and_wait(chan, "vm-next");
2219 if (!vms->deleted[vms->curmsg])
2220 res = play_and_wait(chan, "vm-delete");
2222 res = play_and_wait(chan, "vm-undelete");
2224 res = play_and_wait(chan, "vm-toforward");
2226 res = play_and_wait(chan, "vm-savemessage");
2230 res = play_and_wait(chan, "vm-helpexit");
2232 res = ast_waitfordigit(chan, 6000);
2235 if (vms->repeats > 2) {
2236 res = play_and_wait(chan, "vm-goodbye");
2245 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2249 char newpassword[80] = "";
2250 char newpassword2[80] = "";
2251 char prefile[256]="";
2255 if (adsi_available(chan))
2257 bytes += adsi_logo(buf + bytes);
2258 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2259 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2260 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2261 bytes += adsi_voice_mode(buf + bytes, 0);
2262 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2264 while((cmd >= 0) && (cmd != 't')) {
2269 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2270 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2273 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2274 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2277 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2278 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2281 newpassword[1] = '\0';
2282 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2285 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2288 newpassword2[1] = '\0';
2289 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2293 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2296 if (strcmp(newpassword, newpassword2)) {
2297 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2298 cmd = play_and_wait(chan, "vm-mismatch");
2301 vm_change_password(vmu,newpassword);
2302 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2303 cmd = play_and_wait(chan,"vm-passchanged");
2309 cmd = play_and_wait(chan,"vm-options");
2311 cmd = ast_waitfordigit(chan,6000);
2323 static int vm_execmain(struct ast_channel *chan, void *data)
2325 /* XXX This is, admittedly, some pretty horrendus code. For some
2326 reason it just seemed a lot easier to do with GOTO's. I feel
2327 like I'm back in my GWBASIC days. XXX */
2332 struct localuser *u;
2333 char prefixstr[80] ="";
2334 char empty[80] = "";
2338 char tmp[256], *ext;
2339 char fmtc[256] = "";
2341 struct vm_state vms;
2343 struct ast_vm_user *vmu = NULL, vmus;
2347 memset(&vms, 0, sizeof(vms));
2348 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2349 if (chan->_state != AST_STATE_UP)
2352 if (data && strlen(data)) {
2353 strncpy(tmp, data, sizeof(tmp) - 1);
2358 /* We should skip the user's password */
2363 /* We should prefix the mailbox with the supplied data */
2369 context = strchr(ext, '@');
2376 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2378 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2379 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2386 /* If ADSI is supported, setup login screen */
2387 adsi_begin(chan, &useadsi);
2388 if (!skipuser && useadsi)
2390 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2391 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2395 /* Authenticate them and get their mailbox/password */
2397 while (!valid && (logretries < maxlogins)) {
2398 /* Prompt for, and read in the username */
2399 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2400 ast_log(LOG_WARNING, "Couldn't read username\n");
2403 if (!strlen(vms.username)) {
2404 if (option_verbose > 2)
2405 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2410 adsi_password(chan);
2411 if (ast_streamfile(chan, "vm-password", chan->language)) {
2412 ast_log(LOG_WARNING, "Unable to stream password file\n");
2415 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2416 ast_log(LOG_WARNING, "Unable to read password\n");
2420 char fullusername[80] = "";
2421 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2422 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2423 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2426 vmu = find_user(&vmus, context, vms.username);
2427 if (vmu && !strcmp(vmu->password, password))
2430 if (option_verbose > 2)
2431 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2433 strncpy(vms.username, empty, sizeof(vms.username) -1);
2438 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2443 if (!valid && (logretries >= maxlogins)) {
2444 ast_stopstream(chan);
2445 res = play_and_wait(chan, "vm-goodbye");
2451 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2452 mkdir(vms.curdir, 0700);
2453 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2454 mkdir(vms.curdir, 0700);
2455 /* Retrieve old and new message counts */
2456 open_mailbox(&vms, vmu, 1);
2457 vms.oldmessages = vms.lastmsg + 1;
2458 /* Start in INBOX */
2459 open_mailbox(&vms, vmu, 0);
2460 vms.newmessages = vms.lastmsg + 1;
2463 /* Select proper mailbox FIRST!! */
2464 if (!vms.newmessages && vms.oldmessages) {
2465 /* If we only have old messages start here */
2466 open_mailbox(&vms, vmu, 1);
2470 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2472 cmd = vm_intro(chan, &vms);
2475 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2482 if (vms.lastmsg > -1) {
2483 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2485 cmd = play_and_wait(chan, "vm-youhave");
2487 cmd = play_and_wait(chan, "vm-no");
2489 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2490 cmd = play_and_wait(chan, vms.fn);
2493 cmd = play_and_wait(chan, "vm-messages");
2496 case '2': /* Change folders */
2498 adsi_folders(chan, 0, "Change to folder...");
2499 cmd = get_folder2(chan, "vm-changeto", 0);
2502 } else if (cmd > 0) {
2504 close_mailbox(&vms, vmu);
2505 open_mailbox(&vms, vmu, cmd);
2509 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2511 cmd = play_and_wait(chan, vms.vmbox);
2513 cmd = play_and_wait(chan, "vm-messages");
2519 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2521 cmd = play_and_wait(chan, "vm-nomore");
2525 if (vms.curmsg < vms.lastmsg) {
2527 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2529 cmd = play_and_wait(chan, "vm-nomore");
2533 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2535 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2536 if (vms.deleted[vms.curmsg])
2537 cmd = play_and_wait(chan, "vm-deleted");
2539 cmd = play_and_wait(chan, "vm-undeleted");
2542 if(vms.lastmsg > -1)
2543 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2545 cmd = play_and_wait(chan, "vm-nomore");
2549 adsi_folders(chan, 1, "Save to folder...");
2550 cmd = get_folder2(chan, "vm-savefolder", 1);
2551 box = 0; /* Shut up compiler */
2555 } else if (cmd > 0) {
2556 box = cmd = cmd - '0';
2557 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2558 vms.deleted[vms.curmsg]=1;
2560 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2562 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2564 cmd = play_and_wait(chan, "vm-message");
2566 cmd = say_and_wait(chan, vms.curmsg + 1);
2568 cmd = play_and_wait(chan, "vm-savedto");
2570 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2571 cmd = play_and_wait(chan, vms.fn);
2574 cmd = play_and_wait(chan, "vm-messages");
2577 if (!vms.starting) {
2578 cmd = play_and_wait(chan, "vm-onefor");
2580 cmd = play_and_wait(chan, vms.vmbox);
2582 cmd = play_and_wait(chan, "vm-messages");
2584 cmd = play_and_wait(chan, "vm-opts");
2589 cmd = vm_options(chan, vmu, &vms, vmfmts);
2591 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2593 default: /* Nothing */
2594 cmd = vm_instructions(chan, &vms);
2598 if ((cmd == 't') || (cmd == '#')) {
2608 ast_stopstream(chan);
2611 res = play_and_wait(chan, "vm-goodbye");
2616 adsi_unload_session(chan);
2619 close_mailbox(&vms, vmu);
2623 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2625 LOCAL_USER_REMOVE(u);
2630 static int vm_exec(struct ast_channel *chan, void *data)
2632 int res=0, silent=0, busy=0, unavail=0;
2633 struct localuser *u;
2634 char tmp[256], *ext;
2637 if (chan->_state != AST_STATE_UP)
2640 strncpy(tmp, data, sizeof(tmp) - 1);
2642 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2653 } else if (*ext == 'b') {
2656 } else if (*ext == 'u') {
2662 res = leave_voicemail(chan, ext, silent, busy, unavail);
2663 LOCAL_USER_REMOVE(u);
2667 static int append_mailbox(char *context, char *mbox, char *data)
2669 /* Assumes lock is already held */
2673 struct ast_vm_user *vmu;
2674 strncpy(tmp, data, sizeof(tmp));
2675 vmu = malloc(sizeof(struct ast_vm_user));
2677 memset(vmu, 0, sizeof(struct ast_vm_user));
2678 strncpy(vmu->context, context, sizeof(vmu->context));
2679 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2682 if ((s = strsep(&stringp, ",")))
2683 strncpy(vmu->password, s, sizeof(vmu->password));
2684 if (stringp && (s = strsep(&stringp, ",")))
2685 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2686 if (stringp && (s = strsep(&stringp, ",")))
2687 strncpy(vmu->email, s, sizeof(vmu->email));
2688 if (stringp && (s = strsep(&stringp, ",")))
2689 strncpy(vmu->pager, s, sizeof(vmu->pager));
2690 if (stringp && (s = strsep(&stringp, ",")))
2691 apply_options(vmu, s);
2702 static int load_config(void)
2704 struct ast_vm_user *cur, *l;
2705 struct vm_zone *zcur, *zl;
2706 struct ast_config *cfg;
2708 struct ast_variable *var;
2717 cfg = ast_load(VOICEMAIL_CONFIG);
2718 ast_mutex_lock(&vmlock);
2736 /* General settings */
2737 attach_voicemail = 1;
2738 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
2740 attach_voicemail = ast_true(astattach);
2742 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2743 maxsilence = atoi(silencestr);
2748 silencethreshold = 256;
2749 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2750 silencethreshold = atoi(thresholdstr);
2752 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
2753 astemail = ASTERISK_USERNAME;
2754 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2757 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2758 if (sscanf(s, "%d", &x) == 1) {
2761 ast_log(LOG_WARNING, "Invalid max message time length\n");
2764 fmt = ast_variable_retrieve(cfg, "general", "format");
2767 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2770 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2771 if (sscanf(s, "%d", &x) == 1) {
2774 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2778 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2779 if (sscanf(s, "%d", &x) == 1) {
2782 ast_log(LOG_WARNING, "Invalid skipms value\n");
2787 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2788 if (sscanf(s, "%d", &x) == 1) {
2791 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2796 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2797 strcpy(dbuser, "test");
2801 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2802 strcpy(dbpass, "test");
2806 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2811 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2812 strcpy(dbname, "vmdb");
2818 #ifdef USEPOSTGRESVM
2819 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
2820 strcpy(dboption, "dboption not-specified in voicemail.conf");
2822 strcpy(dboption, s);
2825 cat = ast_category_browse(cfg, NULL);
2827 if (strcasecmp(cat, "general")) {
2828 var = ast_variable_browse(cfg, cat);
2829 if (strcasecmp(cat, "zonemessages")) {
2831 /* Process mailboxes in this context */
2833 append_mailbox(cat, var->name, var->value);
2838 /* Timezones in this context */
2841 z = malloc(sizeof(struct vm_zone));
2843 char *msg_format, *timezone;
2844 msg_format = ast_strdupa(var->value);
2845 if (msg_format != NULL) {
2846 timezone = strsep(&msg_format, "|");
2847 strncpy(z->name, var->name, sizeof(z->name) - 1);
2848 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
2849 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
2859 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2864 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2871 cat = ast_category_browse(cfg, cat);
2873 memset(fromstring,0,sizeof(fromstring));
2874 memset(emailtitle,0,sizeof(emailtitle));
2879 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
2880 pbxskip = ast_true(s);
2881 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
2882 strncpy(fromstring,s,sizeof(fromstring)-1);
2883 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
2884 strncpy(emailtitle,s,sizeof(emailtitle)-1);
2885 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
2886 char *tmpread, *tmpwrite;
2887 emailbody = strdup(s);
2889 /* substitute strings \t and \n into the apropriate characters */
2890 tmpread = tmpwrite = emailbody;
2891 while ((tmpwrite = strchr(tmpread,'\\'))) {
2892 int len = strlen("\n");
2893 switch (tmpwrite[1]) {
2895 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2896 strncpy(tmpwrite,"\n",len);
2899 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2900 strncpy(tmpwrite,"\t",len);
2903 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
2905 tmpread = tmpwrite+len;
2909 ast_mutex_unlock(&vmlock);
2912 ast_mutex_unlock(&vmlock);
2913 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2920 return(load_config());
2923 int unload_module(void)
2926 STANDARD_HANGUP_LOCALUSERS;
2927 res = ast_unregister_application(app);
2928 res |= ast_unregister_application(app2);
2933 int load_module(void)
2936 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2937 res |= ast_register_application(capp, vm_exec, synopsis_vm, descrip_vm);
2938 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2939 res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain);
2943 if ((res=load_config())) {
2947 if ((res = sql_init())) {
2948 ast_log(LOG_WARNING, "SQL init\n");
2954 char *description(void)
2962 STANDARD_USECOUNT(res);
2968 return ASTERISK_GPL_KEY;