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 "If the requested mailbox does not exist, and there exists a priority\n"
134 "n + 101, then that priority will be taken next.\n"
135 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
136 "Otherwise, it returns 0.\n";
138 static char *synopsis_vmain =
139 "Enter voicemail system";
141 static char *descrip_vmain =
142 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
143 "for the checking of voicemail. The mailbox can be passed as the option,\n"
144 "which will stop the voicemail system from prompting the user for the mailbox.\n"
145 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
146 "a context is specified, logins are considered in that context only.\n"
147 "Returns -1 if the user hangs up or 0 otherwise.\n";
149 /* Leave a message */
150 static char *capp = "VoiceMail2";
151 static char *app = "VoiceMail";
153 /* Check mail, control, etc */
154 static char *capp2 = "VoiceMailMain2";
155 static char *app2 = "VoiceMailMain";
157 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
158 struct ast_vm_user *users;
159 struct ast_vm_user *usersl;
160 struct vm_zone *zones = NULL;
161 struct vm_zone *zonesl = NULL;
162 static int attach_voicemail;
163 static int maxsilence;
164 static int silencethreshold = 128;
165 static char serveremail[80];
166 static char vmfmts[80];
167 static int vmmaxmessage;
170 static int maxlogins;
172 static char *emailbody = NULL;
173 static int pbxskip = 0;
174 static char fromstring[100];
175 static char emailtitle[100];
181 static void apply_options(struct ast_vm_user *vmu, char *options)
183 /* Destructively Parse options and apply */
184 char *stringp = ast_strdupa(options);
187 while((s = strsep(&stringp, "|"))) {
189 if ((var = strsep(&value, "=")) && value) {
190 if (!strcasecmp(var, "attach")) {
195 } else if (!strcasecmp(var, "serveremail")) {
196 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
197 } else if (!strcasecmp(var, "tz")) {
198 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
206 #include "mysql-vm-routines.h"
213 ast_mutex_t postgreslock;
215 static int sql_init(void)
217 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
218 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
220 dbhandler=PQconnectdb(dboption);
221 if (PQstatus(dbhandler) == CONNECTION_BAD) {
222 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
225 ast_mutex_init(&postgreslock);
227 /* fprintf(stderr,"postgres login OK\n"); */
231 static void sql_close(void)
237 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
245 char options[160] = "";
246 struct ast_vm_user *retval;
248 retval=malloc(sizeof(struct ast_vm_user));
250 /* fprintf(stderr,"postgres find_user:\n"); */
253 *retval->mailbox='\0';
254 *retval->context='\0';
255 *retval->password='\0';
256 *retval->fullname='\0';
259 *retval->serveremail='\0';
264 strcpy(retval->mailbox, mailbox);
267 strcpy(retval->context, context);
271 strcpy(retval->context, "default");
274 if (*retval->context) {
275 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", context, mailbox);
277 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='default' AND mailbox='%s'", mailbox);
279 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
280 ast_mutex_lock(&postgreslock);
281 PGSQLres=PQexec(dbhandler,query);
282 if (PGSQLres!=NULL) {
283 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
284 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
285 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
287 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
290 ast_mutex_unlock(&postgreslock);
294 numFields = PQnfields(PGSQLres);
295 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
296 if (PQntuples(PGSQLres) != 1) {
297 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
299 ast_mutex_unlock(&postgreslock);
303 for (i=0; i<numFields; i++) {
304 fname = PQfname(PGSQLres,i);
305 if (!strcmp(fname, "password")) {
306 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
307 } else if (!strcmp(fname, "fullname")) {
308 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
309 } else if (!strcmp(fname, "email")) {
310 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
311 } else if (!strcmp(fname, "pager")) {
312 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
313 } else if (!strcmp(fname, "options")) {
314 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
315 apply_options(retval, options);
320 ast_mutex_unlock(&postgreslock);
324 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
325 ast_mutex_unlock(&postgreslock);
330 } /* malloc() retval */
335 static void vm_change_password(struct ast_vm_user *vmu, char *password)
340 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);
342 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
344 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
345 ast_mutex_lock(&postgreslock);
346 PQexec(dbhandler, query);
347 strcpy(vmu->password, password);
348 ast_mutex_unlock(&postgreslock);
351 static void reset_user_pw(char *context, char *mailbox, char *password)
356 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
358 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
360 ast_mutex_lock(&postgreslock);
361 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
362 PQexec(dbhandler, query);
363 ast_mutex_unlock(&postgreslock);
366 #endif /* Postgres */
369 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
371 /* This function could be made to generate one from a database, too */
372 struct ast_vm_user *vmu=NULL, *cur;
373 ast_mutex_lock(&vmlock);
376 if ((!context || !strcasecmp(context, cur->context)) &&
377 (!strcasecmp(mailbox, cur->mailbox)))
385 /* Make a copy, so that on a reload, we have no race */
386 vmu = malloc(sizeof(struct ast_vm_user));
388 memcpy(vmu, cur, sizeof(struct ast_vm_user));
396 ast_mutex_unlock(&vmlock);
400 static int reset_user_pw(char *context, char *mailbox, char *newpass)
402 /* This function could be made to generate one from a database, too */
403 struct ast_vm_user *cur;
405 ast_mutex_lock(&vmlock);
408 if ((!context || !strcasecmp(context, cur->context)) &&
409 (!strcasecmp(mailbox, cur->mailbox)))
414 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
417 ast_mutex_unlock(&vmlock);
421 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
423 /* There's probably a better way of doing this. */
424 /* That's why I've put the password change in a separate function. */
425 /* This could also be done with a database function */
431 char tmpin[AST_CONFIG_MAX_PATH];
432 char tmpout[AST_CONFIG_MAX_PATH];
433 char *user, *pass, *rest, *trim;
434 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
435 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
436 configin = fopen((char *)tmpin,"r");
438 configout = fopen((char *)tmpout,"w+");
441 if(!configin || !configout) {
445 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
449 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
453 while (!feof(configin)) {
454 /* Read in the line */
455 fgets(inbuf, sizeof(inbuf), configin);
456 if (!feof(configin)) {
457 /* Make a backup of it */
458 memcpy(orig, inbuf, sizeof(orig));
459 /* Strip trailing \n and comment */
460 inbuf[strlen(inbuf) - 1] = '\0';
461 user = strchr(inbuf, ';');
467 pass = strchr(user, '=');
470 while(*trim && *trim < 33) {
480 while(*pass && *pass < 33)
484 rest = strchr(pass,',');
491 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
492 /* This is the line */
494 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
496 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
499 /* Put it back like it was */
500 fprintf(configout, orig);
507 unlink((char *)tmpin);
508 rename((char *)tmpout,(char *)tmpin);
509 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
510 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
514 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
516 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
519 static int make_file(char *dest, int len, char *dir, int num)
521 return snprintf(dest, len, "%s/msg%04d", dir, num);
525 inbuf(struct baseio *bio, FILE *fi)
532 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
547 inchar(struct baseio *bio, FILE *fi)
549 if(bio->iocp>=bio->iolen)
553 return bio->iobuf[bio->iocp++];
557 ochar(struct baseio *bio, int c, FILE *so)
559 if(bio->linelength>=BASELINELEN) {
560 if(fputs(eol,so)==EOF)
566 if(putc(((unsigned char)c),so)==EOF)
574 static int base_encode(char *filename, FILE *so)
576 unsigned char dtable[BASEMAXINLINE];
581 memset(&bio, 0, sizeof(bio));
582 bio.iocp = BASEMAXINLINE;
584 if ( !(fi = fopen(filename, "rb"))) {
585 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
593 dtable[26+i+9]= 'j'+i;
597 dtable[26+i+18]= 's'+i;
606 unsigned char igroup[3],ogroup[4];
609 igroup[0]= igroup[1]= igroup[2]= 0;
612 if ( (c = inchar(&bio, fi)) == EOF) {
617 igroup[n]= (unsigned char)c;
621 ogroup[0]= dtable[igroup[0]>>2];
622 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
623 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
624 ogroup[3]= dtable[igroup[2]&0x3F];
634 ochar(&bio, ogroup[i], so);
638 if(fputs(eol,so)==EOF)
646 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)
657 struct vm_zone *the_zone = NULL;
659 if (!strcmp(format, "wav49"))
661 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
662 p = popen(SENDMAIL, "w");
664 gethostname(host, sizeof(host));
665 if (strchr(srcemail, '@'))
666 strncpy(who, srcemail, sizeof(who)-1);
668 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
670 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
673 /* Does this user have a timezone specified? */
674 if (strlen(vmu->zonetag)) {
675 /* Find the zone in the list */
679 if (!strcmp(z->name, vmu->zonetag)) {
688 ast_localtime(&t,&tm,the_zone->timezone);
690 ast_localtime(&t,&tm,NULL);
691 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
692 fprintf(p, "Date: %s\n", date);
695 fprintf(p, "From: %s <%s>\n", fromstring, who);
697 fprintf(p, "From: Asterisk PBX <%s>\n", who);
698 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
702 fprintf(p, emailtitle, msgnum, mailbox) ;
707 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
709 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
710 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
711 fprintf(p, "MIME-Version: 1.0\n");
712 if (attach_user_voicemail) {
714 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
716 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
718 fprintf(p, "--%s\n", bound);
720 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
721 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
723 struct ast_channel *ast = ast_channel_alloc(0);
726 int vmlen = strlen(emailbody)*3 + 200;
727 if ((passdata = alloca(vmlen))) {
728 memset(passdata, 0, vmlen);
729 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
730 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
731 sprintf(passdata,"%d",msgnum);
732 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
733 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
734 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
735 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
736 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
737 fprintf(p, "%s\n",passdata);
738 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
739 ast_channel_free(ast);
740 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
742 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
744 "in mailbox %s from %s, on %s so you might\n"
745 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
746 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
748 if (attach_user_voicemail) {
749 fprintf(p, "--%s\n", bound);
750 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
751 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
752 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
753 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
755 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
756 base_encode(fname, p);
757 fprintf(p, "\n\n--%s--\n.\n", bound);
761 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
767 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
776 struct vm_zone *the_zone = NULL;
777 p = popen(SENDMAIL, "w");
780 gethostname(host, sizeof(host));
781 if (strchr(srcemail, '@'))
782 strncpy(who, srcemail, sizeof(who)-1);
784 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
786 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
789 /* Does this user have a timezone specified? */
790 if (strlen(vmu->zonetag)) {
791 /* Find the zone in the list */
795 if (!strcmp(z->name, vmu->zonetag)) {
804 ast_localtime(&t,&tm,the_zone->timezone);
806 ast_localtime(&t,&tm,NULL);
808 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
809 fprintf(p, "Date: %s\n", date);
810 fprintf(p, "From: Asterisk PBX <%s>\n", who);
811 fprintf(p, "To: %s\n", pager);
812 fprintf(p, "Subject: New VM\n\n");
813 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
814 fprintf(p, "New %s long msg in box %s\n"
815 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
818 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
824 static int get_date(char *s, int len)
830 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
833 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
837 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
838 if (ast_fileexists(fn, NULL, NULL) > 0) {
839 res = ast_streamfile(chan, fn, chan->language);
842 res = ast_waitstream(chan, ecodes);
846 res = ast_streamfile(chan, "vm-theperson", chan->language);
849 res = ast_waitstream(chan, ecodes);
852 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
857 res = ast_streamfile(chan, "vm-isonphone", chan->language);
859 res = ast_streamfile(chan, "vm-isunavail", chan->language);
862 res = ast_waitstream(chan, ecodes);
866 static int play_and_wait(struct ast_channel *chan, char *fn)
869 d = ast_streamfile(chan, fn, chan->language);
872 d = ast_waitstream(chan, AST_DIGIT_ANY);
873 ast_stopstream(chan);
877 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
881 int x, fmtcnt=1, res=-1,outmsg=0;
883 struct ast_filestream *others[MAX_OTHER_FORMATS];
884 char *sfmt[MAX_OTHER_FORMATS];
887 struct ast_dsp *sildet; /* silence detector dsp */
888 int totalsilence = 0;
890 int gotsilence = 0; /* did we timeout for silence? */
893 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
894 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
897 d = play_and_wait(chan, playfile);
899 d = ast_streamfile(chan, "beep",chan->language);
901 d = ast_waitstream(chan,"");
906 fmts = ast_strdupa(fmt);
909 strsep(&stringp, "|");
910 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
911 sfmt[0] = ast_strdupa(fmts);
913 while((fmt = strsep(&stringp, "|"))) {
914 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
915 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
918 sfmt[fmtcnt++] = ast_strdupa(fmt);
923 for (x=0;x<fmtcnt;x++) {
924 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
925 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
932 sildet = ast_dsp_new(); //Create the silence detector
934 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
937 ast_dsp_set_threshold(sildet, silencethreshold);
939 if (maxsilence > 0) {
940 rfmt = chan->readformat;
941 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
943 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
949 /* Loop forever, writing the packets we read to the writer(s), until
950 we read a # or get a hangup */
953 res = ast_waitfor(chan, 2000);
955 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
956 /* Try one more time in case of masq */
957 res = ast_waitfor(chan, 2000);
959 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
971 if (f->frametype == AST_FRAME_VOICE) {
972 /* write each format */
973 for (x=0;x<fmtcnt;x++) {
974 res = ast_writestream(others[x], f);
977 /* Silence Detection */
978 if (maxsilence > 0) {
980 ast_dsp_silence(sildet, f, &dspsilence);
982 totalsilence = dspsilence;
986 if (totalsilence > maxsilence) {
987 /* Ended happily with silence */
994 /* Exit on any error */
996 ast_log(LOG_WARNING, "Error writing frame\n");
1000 } else if (f->frametype == AST_FRAME_VIDEO) {
1001 /* Write only once */
1002 ast_writestream(others[0], f);
1003 } else if (f->frametype == AST_FRAME_DTMF) {
1004 if (f->subclass == '#') {
1005 if (option_verbose > 2)
1006 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1015 if (maxtime < (end - start)) {
1016 if (option_verbose > 2)
1017 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1026 if (option_verbose > 2)
1027 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1032 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1035 for (x=0;x<fmtcnt;x++) {
1039 ast_stream_rewind(others[x], totalsilence-200);
1041 ast_stream_rewind(others[x], 200);
1042 ast_truncstream(others[x]);
1043 ast_closestream(others[x]);
1046 if (ast_set_read_format(chan, rfmt)) {
1047 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1052 /* Let them know it worked */
1053 ast_streamfile(chan, "vm-msgsaved", chan->language);
1054 ast_waitstream(chan, "");
1062 static void free_user(struct ast_vm_user *vmu)
1068 static void free_zone(struct vm_zone *z)
1073 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1083 char prefile[256]="";
1091 struct ast_vm_user *vmu;
1092 struct ast_vm_user svm;
1094 strncpy(tmp, ext, sizeof(tmp) - 1);
1096 context = strchr(tmp, '@');
1102 if ((vmu = find_user(&svm, context, ext))) {
1103 /* Setup pre-file if appropriate */
1105 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1107 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1108 make_dir(dir, sizeof(dir), vmu->context, "", "");
1109 /* It's easier just to try to make it than to check for its existence */
1110 if (mkdir(dir, 0700) && (errno != EEXIST))
1111 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1112 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1113 /* It's easier just to try to make it than to check for its existence */
1114 if (mkdir(dir, 0700) && (errno != EEXIST))
1115 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1116 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1117 if (mkdir(dir, 0700) && (errno != EEXIST))
1118 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1119 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1121 /* Play the beginning intro if desired */
1122 if (strlen(prefile)) {
1123 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1124 if (ast_streamfile(chan, prefile, chan->language) > -1)
1125 res = ast_waitstream(chan, "#0");
1127 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1128 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1131 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1137 /* On a '#' we skip the instructions */
1141 if (!res && !silent) {
1142 res = ast_streamfile(chan, INTRO, chan->language);
1144 res = ast_waitstream(chan, ecodes);
1150 /* Check for a '0' here */
1152 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1153 if (strlen(chan->macrocontext))
1154 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1160 /* Unless we're *really* silent, try to send the beep */
1161 res = ast_streamfile(chan, "beep", chan->language);
1163 res = ast_waitstream(chan, "");
1169 /* The meat of recording the message... All the announcements and beeps have been played*/
1170 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1174 make_file(fn, sizeof(fn), dir, msgnum);
1175 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1176 (chan->callerid ? chan->callerid : "Unknown"),
1177 vmu->fullname, ext, chan->name);
1178 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1181 } while(msgnum < MAXMSG);
1182 if (msgnum < MAXMSG) {
1183 /* Store information */
1184 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1185 txt = fopen(txtfile, "w+");
1187 get_date(date, sizeof(date));
1191 "; Message Information file\n"
1207 chan->callerid ? chan->callerid : "Unknown",
1208 date, (long)time(NULL));
1211 ast_log(LOG_WARNING, "Error opening text file for output\n");
1212 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1215 txt = fopen(txtfile, "a");
1218 fprintf(txt, "duration=%ld\n", (long)(end-start));
1222 strsep(&stringp, "|");
1223 /* Send e-mail if applicable */
1224 if (strlen(vmu->email)) {
1225 int attach_user_voicemail = attach_voicemail;
1226 char *myserveremail = serveremail;
1227 if (vmu->attach > -1)
1228 attach_user_voicemail = vmu->attach;
1229 if (strlen(vmu->serveremail))
1230 myserveremail = vmu->serveremail;
1231 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1233 if (strlen(vmu->pager)) {
1234 char *myserveremail = serveremail;
1235 if (strlen(vmu->serveremail))
1236 myserveremail = vmu->serveremail;
1237 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1240 ast_log(LOG_WARNING, "No more messages possible\n");
1242 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1245 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1246 /*Send the call to n+101 priority, where n is the current priority*/
1247 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1248 chan->priority+=100;
1250 /* Leave voicemail for someone */
1251 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1255 static char *mbox(int id)
1283 static int count_messages(char *dir)
1287 for (x=0;x<MAXMSG;x++) {
1288 make_file(fn, sizeof(fn), dir, x);
1289 if (ast_fileexists(fn, NULL, NULL) < 1)
1295 static int say_and_wait(struct ast_channel *chan, int num)
1298 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1302 static int copy(char *infile, char *outfile)
1309 if ((ifd = open(infile, O_RDONLY)) < 0) {
1310 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1313 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1314 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1319 len = read(ifd, buf, sizeof(buf));
1321 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1327 res = write(ofd, buf, len);
1329 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1341 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1348 char *dbox = mbox(box);
1350 make_file(sfn, sizeof(sfn), dir, msg);
1351 make_dir(ddir, sizeof(ddir), context, username, dbox);
1353 for (x=0;x<MAXMSG;x++) {
1354 make_file(dfn, sizeof(dfn), ddir, x);
1355 if (ast_fileexists(dfn, NULL, NULL) < 0)
1360 ast_filecopy(sfn, dfn, NULL);
1361 if (strcmp(sfn, dfn)) {
1362 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1363 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1369 static int adsi_logo(unsigned char *buf)
1372 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1373 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1377 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1385 bytes += adsi_data_mode(buf + bytes);
1386 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1389 bytes += adsi_logo(buf);
1390 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1392 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1394 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1395 bytes += adsi_data_mode(buf + bytes);
1396 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1398 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1400 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1401 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1402 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1403 bytes += adsi_voice_mode(buf + bytes, 0);
1404 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1411 bytes += adsi_logo(buf);
1412 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1413 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1414 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1415 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1418 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1419 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1420 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1421 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1422 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1423 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1424 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1427 /* Add another dot */
1429 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1430 bytes += adsi_voice_mode(buf + bytes, 0);
1432 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1433 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1437 /* These buttons we load but don't use yet */
1438 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1439 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1440 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1441 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1442 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1443 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1444 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1447 /* Add another dot */
1449 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1450 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1451 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1456 snprintf(num, sizeof(num), "%d", x);
1457 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1459 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1460 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1463 /* Add another dot */
1465 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1466 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1467 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1470 if (adsi_end_download(chan)) {
1472 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1473 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1474 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1475 bytes += adsi_voice_mode(buf + bytes, 0);
1476 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1480 bytes += adsi_download_disconnect(buf + bytes);
1481 bytes += adsi_voice_mode(buf + bytes, 0);
1482 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1484 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1489 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1490 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1492 ast_log(LOG_DEBUG, "Restarting session...\n");
1495 /* Load the session now */
1496 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1498 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1500 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1502 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1506 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1509 if (!adsi_available(chan))
1511 x = adsi_load_session(chan, adapp, adver, 1);
1515 if (adsi_load_vmail(chan, useadsi)) {
1516 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1523 static void adsi_login(struct ast_channel *chan)
1527 unsigned char keys[8];
1529 if (!adsi_available(chan))
1534 /* Set one key for next */
1535 keys[3] = ADSI_KEY_APPS + 3;
1537 bytes += adsi_logo(buf + bytes);
1538 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1539 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1540 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1541 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1542 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1543 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1544 bytes += adsi_set_keys(buf + bytes, keys);
1545 bytes += adsi_voice_mode(buf + bytes, 0);
1546 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1549 static void adsi_password(struct ast_channel *chan)
1553 unsigned char keys[8];
1555 if (!adsi_available(chan))
1560 /* Set one key for next */
1561 keys[3] = ADSI_KEY_APPS + 3;
1563 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1564 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1565 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1566 bytes += adsi_set_keys(buf + bytes, keys);
1567 bytes += adsi_voice_mode(buf + bytes, 0);
1568 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1571 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1575 unsigned char keys[8];
1578 if (!adsi_available(chan))
1582 y = ADSI_KEY_APPS + 12 + start + x;
1583 if (y > ADSI_KEY_APPS + 12 + 4)
1585 keys[x] = ADSI_KEY_SKT | y;
1587 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1591 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1592 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1593 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1594 bytes += adsi_set_keys(buf + bytes, keys);
1595 bytes += adsi_voice_mode(buf + bytes, 0);
1597 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1600 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1603 char buf[256], buf1[256], buf2[256];
1609 char datetime[21]="";
1612 unsigned char keys[8];
1616 if (!adsi_available(chan))
1619 /* Retrieve important info */
1620 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1621 f = fopen(fn2, "r");
1624 fgets(buf, sizeof(buf), f);
1628 strsep(&stringp, "=");
1629 val = strsep(&stringp, "=");
1630 if (val && strlen(val)) {
1631 if (!strcmp(buf, "callerid"))
1632 strncpy(cid, val, sizeof(cid) - 1);
1633 if (!strcmp(buf, "origdate"))
1634 strncpy(datetime, val, sizeof(datetime) - 1);
1640 /* New meaning for keys */
1642 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1647 /* No prev key, provide "Folder" instead */
1648 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1651 /* If last message ... */
1653 /* but not only message, provide "Folder" instead */
1654 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1655 bytes += adsi_voice_mode(buf + bytes, 0);
1658 /* Otherwise if only message, leave blank */
1664 ast_callerid_parse(cid, &name, &num);
1668 name = "Unknown Caller";
1670 /* If deleted, show "undeleted" */
1673 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1676 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1677 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1678 strcasecmp(folder, "INBOX") ? " Messages" : "");
1679 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1681 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1682 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1683 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1684 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1685 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1686 bytes += adsi_set_keys(buf + bytes, keys);
1687 bytes += adsi_voice_mode(buf + bytes, 0);
1689 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1692 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1696 unsigned char keys[8];
1700 if (!adsi_available(chan))
1703 /* New meaning for keys */
1705 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1711 /* No prev key, provide "Folder" instead */
1712 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1715 /* If last message ... */
1717 /* but not only message, provide "Folder" instead */
1718 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1720 /* Otherwise if only message, leave blank */
1725 /* If deleted, show "undeleted" */
1727 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1730 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1731 bytes += adsi_set_keys(buf + bytes, keys);
1732 bytes += adsi_voice_mode(buf + bytes, 0);
1734 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1737 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1739 char buf[256], buf1[256], buf2[256];
1741 unsigned char keys[8];
1744 char *newm = (new == 1) ? "message" : "messages";
1745 char *oldm = (old == 1) ? "message" : "messages";
1746 if (!adsi_available(chan))
1749 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1751 strcat(buf1, " and");
1752 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1754 snprintf(buf2, sizeof(buf2), "%s.", newm);
1757 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1758 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1760 strcpy(buf1, "You have no messages.");
1763 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1764 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1765 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1768 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1772 /* Don't let them listen if there are none */
1775 bytes += adsi_set_keys(buf + bytes, keys);
1777 bytes += adsi_voice_mode(buf + bytes, 0);
1779 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1782 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1784 char buf[256], buf1[256], buf2[256];
1786 unsigned char keys[8];
1789 char *mess = (messages == 1) ? "message" : "messages";
1791 if (!adsi_available(chan))
1794 /* Original command keys */
1796 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1804 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1805 strcasecmp(folder, "INBOX") ? " folder" : "");
1808 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1810 strcpy(buf2, "no messages.");
1811 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1812 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1813 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1814 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1815 bytes += adsi_set_keys(buf + bytes, keys);
1817 bytes += adsi_voice_mode(buf + bytes, 0);
1819 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1823 static void adsi_clear(struct ast_channel *chan)
1827 if (!adsi_available(chan))
1829 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1830 bytes += adsi_voice_mode(buf + bytes, 0);
1832 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1835 static void adsi_goodbye(struct ast_channel *chan)
1840 if (!adsi_available(chan))
1842 bytes += adsi_logo(buf + bytes);
1843 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1844 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1845 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1846 bytes += adsi_voice_mode(buf + bytes, 0);
1848 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1851 static int get_folder(struct ast_channel *chan, int start)
1856 d = play_and_wait(chan, "vm-press");
1859 for (x = start; x< 5; x++) {
1860 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1862 d = play_and_wait(chan, "vm-for");
1865 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1866 d = play_and_wait(chan, fn);
1869 d = play_and_wait(chan, "vm-messages");
1872 d = ast_waitfordigit(chan, 500);
1876 d = play_and_wait(chan, "vm-tocancel");
1879 d = ast_waitfordigit(chan, 4000);
1883 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1886 res = play_and_wait(chan, fn);
1887 while (((res < '0') || (res > '9')) &&
1888 (res != '#') && (res >= 0)) {
1889 res = get_folder(chan, 0);
1895 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1902 struct ast_config *mif;
1907 struct ast_vm_user *receiver, srec;
1912 res = ast_streamfile(chan, "vm-extension", chan->language);
1915 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1917 if ((receiver = find_user(&srec, context, username))) {
1918 /* if (play_and_wait(chan, "vm-savedto"))
1922 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1923 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1924 ast_log(LOG_DEBUG, sys);
1927 todircount = count_messages(todir);
1928 strncpy(tmp, fmt, sizeof(tmp));
1930 while((s = strsep(&stringp, "|"))) {
1931 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1932 ast_log(LOG_DEBUG, sys);
1935 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1936 ast_log(LOG_DEBUG, sys);
1938 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1940 /* load the information on the source message so we can send an e-mail like a new message */
1941 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1942 if ((mif=ast_load(miffile))) {
1944 /* set callerid and duration variables */
1945 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1946 s = ast_variable_retrieve(mif, NULL, "duration");
1951 if (strlen(receiver->email)) {
1952 int attach_user_voicemail = attach_voicemail;
1953 char *myserveremail = serveremail;
1954 if (receiver->attach > -1)
1955 attach_user_voicemail = receiver->attach;
1956 if (strlen(receiver->serveremail))
1957 myserveremail = receiver->serveremail;
1958 sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, duration, attach_user_voicemail);
1961 if (strlen(receiver->pager)) {
1962 char *myserveremail = serveremail;
1963 if (strlen(receiver->serveremail))
1964 myserveremail = receiver->serveremail;
1965 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver);
1968 ast_destroy(mif); /* or here */
1970 /* Leave voicemail for someone */
1971 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1973 /* give confirmatopm that the message was saved */
1974 res = play_and_wait(chan, "vm-message");
1976 res = play_and_wait(chan, "vm-saved");
1977 free_user(receiver);
1980 res = play_and_wait(chan, "pbx-invalid");
1993 int deleted[MAXMSG];
2004 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2007 if ((res = ast_streamfile(chan, file, chan->language)))
2008 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2010 res = ast_waitstream(chan, AST_DIGIT_ANY);
2014 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2017 if ((res = ast_streamfile(chan, file, chan->language)))
2018 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2020 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2024 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2027 char filename[256], *origtime;
2028 struct vm_zone *the_zone = NULL;
2029 struct ast_config *msg_cfg;
2033 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2034 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2035 msg_cfg = ast_load(filename);
2037 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2041 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2043 if (sscanf(origtime,"%ld",&tin) < 1) {
2044 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2048 ast_destroy(msg_cfg);
2050 /* Does this user have a timezone specified? */
2051 if (strlen(vmu->zonetag)) {
2052 /* Find the zone in the list */
2056 if (!strcmp(z->name, vmu->zonetag)) {
2064 /* No internal variable parsing for now, so we'll comment it out for the time being */
2066 /* Set the DIFF_* variables */
2067 localtime_r(&t, &time_now);
2068 gettimeofday(&tv_now,NULL);
2069 tnow = tv_now.tv_sec;
2070 localtime_r(&tnow,&time_then);
2072 /* Day difference */
2073 if (time_now.tm_year == time_then.tm_year)
2074 sprintf(temp,"%d",time_now.tm_yday);
2076 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2077 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2079 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2082 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2084 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2086 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2091 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2095 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2096 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2098 res = wait_file2(chan, vms, "vm-first");
2099 else if (msg == vms->lastmsg)
2100 res = wait_file2(chan, vms, "vm-last");
2102 res = wait_file2(chan, vms, "vm-message");
2103 if (msg && (msg != vms->lastmsg)) {
2105 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2110 res = play_message_datetime(chan,vmu,vms);
2113 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2114 vms->heard[msg] = 1;
2115 res = wait_file(chan, vms, vms->fn);
2120 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2122 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2123 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2124 vms->lastmsg = count_messages(vms->curdir) - 1;
2125 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2128 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2131 char ntxt[256] = "";
2133 if (vms->lastmsg > -1) {
2134 /* Get the deleted messages fixed */
2136 for (x=0;x < MAXMSG;x++) {
2137 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2138 /* Save this message. It's not in INBOX or hasn't been heard */
2139 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2140 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2143 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2144 if (strcmp(vms->fn, vms->fn2)) {
2145 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2146 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2147 ast_filerename(vms->fn, vms->fn2, NULL);
2150 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2151 /* Move to old folder before deleting */
2152 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2155 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2156 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2157 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2159 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2160 ast_filedelete(vms->fn, NULL);
2164 memset(vms->deleted, 0, sizeof(vms->deleted));
2165 memset(vms->heard, 0, sizeof(vms->heard));
2168 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2170 /* Introduce messages they have */
2172 res = play_and_wait(chan, "vm-youhave");
2174 if (vms->newmessages) {
2175 res = say_and_wait(chan, vms->newmessages);
2177 res = play_and_wait(chan, "vm-INBOX");
2178 if (vms->oldmessages && !res)
2179 res = play_and_wait(chan, "vm-and");
2181 if ((vms->newmessages == 1))
2182 res = play_and_wait(chan, "vm-message");
2184 res = play_and_wait(chan, "vm-messages");
2188 if (!res && vms->oldmessages) {
2189 res = say_and_wait(chan, vms->oldmessages);
2191 res = play_and_wait(chan, "vm-Old");
2193 if (vms->oldmessages == 1)
2194 res = play_and_wait(chan, "vm-message");
2196 res = play_and_wait(chan, "vm-messages");
2200 if (!vms->oldmessages && !vms->newmessages) {
2201 res = play_and_wait(chan, "vm-no");
2203 res = play_and_wait(chan, "vm-messages");
2210 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2213 /* Play instructions and wait for new command */
2215 if (vms->starting) {
2216 if (vms->lastmsg > -1) {
2217 res = play_and_wait(chan, "vm-onefor");
2219 res = play_and_wait(chan, vms->vmbox);
2221 res = play_and_wait(chan, "vm-messages");
2224 res = play_and_wait(chan, "vm-opts");
2227 res = play_and_wait(chan, "vm-prev");
2229 res = play_and_wait(chan, "vm-repeat");
2230 if (!res && (vms->curmsg != vms->lastmsg))
2231 res = play_and_wait(chan, "vm-next");
2233 if (!vms->deleted[vms->curmsg])
2234 res = play_and_wait(chan, "vm-delete");
2236 res = play_and_wait(chan, "vm-undelete");
2238 res = play_and_wait(chan, "vm-toforward");
2240 res = play_and_wait(chan, "vm-savemessage");
2244 res = play_and_wait(chan, "vm-helpexit");
2246 res = ast_waitfordigit(chan, 6000);
2249 if (vms->repeats > 2) {
2250 res = play_and_wait(chan, "vm-goodbye");
2259 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2263 char newpassword[80] = "";
2264 char newpassword2[80] = "";
2265 char prefile[256]="";
2269 if (adsi_available(chan))
2271 bytes += adsi_logo(buf + bytes);
2272 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2273 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2274 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2275 bytes += adsi_voice_mode(buf + bytes, 0);
2276 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2278 while((cmd >= 0) && (cmd != 't')) {
2283 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2284 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2287 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2288 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2291 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2292 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2295 newpassword[1] = '\0';
2296 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2299 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2302 newpassword2[1] = '\0';
2303 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2307 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2310 if (strcmp(newpassword, newpassword2)) {
2311 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2312 cmd = play_and_wait(chan, "vm-mismatch");
2315 vm_change_password(vmu,newpassword);
2316 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2317 cmd = play_and_wait(chan,"vm-passchanged");
2323 cmd = play_and_wait(chan,"vm-options");
2325 cmd = ast_waitfordigit(chan,6000);
2337 static int vm_execmain(struct ast_channel *chan, void *data)
2339 /* XXX This is, admittedly, some pretty horrendus code. For some
2340 reason it just seemed a lot easier to do with GOTO's. I feel
2341 like I'm back in my GWBASIC days. XXX */
2346 struct localuser *u;
2347 char prefixstr[80] ="";
2348 char empty[80] = "";
2352 char tmp[256], *ext;
2353 char fmtc[256] = "";
2355 struct vm_state vms;
2357 struct ast_vm_user *vmu = NULL, vmus;
2361 memset(&vms, 0, sizeof(vms));
2362 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2363 if (chan->_state != AST_STATE_UP)
2366 if (data && strlen(data)) {
2367 strncpy(tmp, data, sizeof(tmp) - 1);
2372 /* We should skip the user's password */
2377 /* We should prefix the mailbox with the supplied data */
2383 context = strchr(ext, '@');
2390 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2392 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2393 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2400 /* If ADSI is supported, setup login screen */
2401 adsi_begin(chan, &useadsi);
2402 if (!skipuser && useadsi)
2404 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2405 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2409 /* Authenticate them and get their mailbox/password */
2411 while (!valid && (logretries < maxlogins)) {
2412 /* Prompt for, and read in the username */
2413 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2414 ast_log(LOG_WARNING, "Couldn't read username\n");
2417 if (!strlen(vms.username)) {
2418 if (option_verbose > 2)
2419 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2424 adsi_password(chan);
2425 if (ast_streamfile(chan, "vm-password", chan->language)) {
2426 ast_log(LOG_WARNING, "Unable to stream password file\n");
2429 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2430 ast_log(LOG_WARNING, "Unable to read password\n");
2434 char fullusername[80] = "";
2435 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2436 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2437 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2440 vmu = find_user(&vmus, context, vms.username);
2441 if (vmu && !strcmp(vmu->password, password))
2444 if (option_verbose > 2)
2445 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2447 strncpy(vms.username, empty, sizeof(vms.username) -1);
2452 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2457 if (!valid && (logretries >= maxlogins)) {
2458 ast_stopstream(chan);
2459 res = play_and_wait(chan, "vm-goodbye");
2465 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2466 mkdir(vms.curdir, 0700);
2467 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2468 mkdir(vms.curdir, 0700);
2469 /* Retrieve old and new message counts */
2470 open_mailbox(&vms, vmu, 1);
2471 vms.oldmessages = vms.lastmsg + 1;
2472 /* Start in INBOX */
2473 open_mailbox(&vms, vmu, 0);
2474 vms.newmessages = vms.lastmsg + 1;
2477 /* Select proper mailbox FIRST!! */
2478 if (!vms.newmessages && vms.oldmessages) {
2479 /* If we only have old messages start here */
2480 open_mailbox(&vms, vmu, 1);
2484 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2486 cmd = vm_intro(chan, &vms);
2489 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2496 if (vms.lastmsg > -1) {
2497 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2499 cmd = play_and_wait(chan, "vm-youhave");
2501 cmd = play_and_wait(chan, "vm-no");
2503 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2504 cmd = play_and_wait(chan, vms.fn);
2507 cmd = play_and_wait(chan, "vm-messages");
2510 case '2': /* Change folders */
2512 adsi_folders(chan, 0, "Change to folder...");
2513 cmd = get_folder2(chan, "vm-changeto", 0);
2516 } else if (cmd > 0) {
2518 close_mailbox(&vms, vmu);
2519 open_mailbox(&vms, vmu, cmd);
2523 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2525 cmd = play_and_wait(chan, vms.vmbox);
2527 cmd = play_and_wait(chan, "vm-messages");
2533 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2535 cmd = play_and_wait(chan, "vm-nomore");
2539 if (vms.curmsg < vms.lastmsg) {
2541 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2543 cmd = play_and_wait(chan, "vm-nomore");
2547 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2549 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2550 if (vms.deleted[vms.curmsg])
2551 cmd = play_and_wait(chan, "vm-deleted");
2553 cmd = play_and_wait(chan, "vm-undeleted");
2556 if(vms.lastmsg > -1)
2557 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2559 cmd = play_and_wait(chan, "vm-nomore");
2563 adsi_folders(chan, 1, "Save to folder...");
2564 cmd = get_folder2(chan, "vm-savefolder", 1);
2565 box = 0; /* Shut up compiler */
2569 } else if (cmd > 0) {
2570 box = cmd = cmd - '0';
2571 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2572 vms.deleted[vms.curmsg]=1;
2574 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2576 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2578 cmd = play_and_wait(chan, "vm-message");
2580 cmd = say_and_wait(chan, vms.curmsg + 1);
2582 cmd = play_and_wait(chan, "vm-savedto");
2584 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2585 cmd = play_and_wait(chan, vms.fn);
2588 cmd = play_and_wait(chan, "vm-messages");
2591 if (!vms.starting) {
2592 cmd = play_and_wait(chan, "vm-onefor");
2594 cmd = play_and_wait(chan, vms.vmbox);
2596 cmd = play_and_wait(chan, "vm-messages");
2598 cmd = play_and_wait(chan, "vm-opts");
2603 cmd = vm_options(chan, vmu, &vms, vmfmts);
2605 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2607 default: /* Nothing */
2608 cmd = vm_instructions(chan, &vms);
2612 if ((cmd == 't') || (cmd == '#')) {
2622 ast_stopstream(chan);
2625 res = play_and_wait(chan, "vm-goodbye");
2630 adsi_unload_session(chan);
2633 close_mailbox(&vms, vmu);
2637 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2639 LOCAL_USER_REMOVE(u);
2644 static int vm_exec(struct ast_channel *chan, void *data)
2646 int res=0, silent=0, busy=0, unavail=0;
2647 struct localuser *u;
2648 char tmp[256], *ext;
2651 if (chan->_state != AST_STATE_UP)
2653 if (data && strlen(data))
2654 strncpy(tmp, data, sizeof(tmp) - 1);
2656 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2667 } else if (*ext == 'b') {
2670 } else if (*ext == 'u') {
2676 res = leave_voicemail(chan, ext, silent, busy, unavail);
2677 LOCAL_USER_REMOVE(u);
2681 static int append_mailbox(char *context, char *mbox, char *data)
2683 /* Assumes lock is already held */
2687 struct ast_vm_user *vmu;
2688 strncpy(tmp, data, sizeof(tmp));
2689 vmu = malloc(sizeof(struct ast_vm_user));
2691 memset(vmu, 0, sizeof(struct ast_vm_user));
2692 strncpy(vmu->context, context, sizeof(vmu->context));
2693 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2696 if ((s = strsep(&stringp, ",")))
2697 strncpy(vmu->password, s, sizeof(vmu->password));
2698 if (stringp && (s = strsep(&stringp, ",")))
2699 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2700 if (stringp && (s = strsep(&stringp, ",")))
2701 strncpy(vmu->email, s, sizeof(vmu->email));
2702 if (stringp && (s = strsep(&stringp, ",")))
2703 strncpy(vmu->pager, s, sizeof(vmu->pager));
2704 if (stringp && (s = strsep(&stringp, ",")))
2705 apply_options(vmu, s);
2716 static int load_config(void)
2718 struct ast_vm_user *cur, *l;
2719 struct vm_zone *zcur, *zl;
2720 struct ast_config *cfg;
2722 struct ast_variable *var;
2731 cfg = ast_load(VOICEMAIL_CONFIG);
2732 ast_mutex_lock(&vmlock);
2750 /* General settings */
2751 attach_voicemail = 1;
2752 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
2754 attach_voicemail = ast_true(astattach);
2756 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2757 maxsilence = atoi(silencestr);
2762 silencethreshold = 256;
2763 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2764 silencethreshold = atoi(thresholdstr);
2766 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
2767 astemail = ASTERISK_USERNAME;
2768 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2771 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2772 if (sscanf(s, "%d", &x) == 1) {
2775 ast_log(LOG_WARNING, "Invalid max message time length\n");
2778 fmt = ast_variable_retrieve(cfg, "general", "format");
2781 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2784 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2785 if (sscanf(s, "%d", &x) == 1) {
2788 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2792 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2793 if (sscanf(s, "%d", &x) == 1) {
2796 ast_log(LOG_WARNING, "Invalid skipms value\n");
2801 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2802 if (sscanf(s, "%d", &x) == 1) {
2805 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2810 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2811 strcpy(dbuser, "test");
2815 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2816 strcpy(dbpass, "test");
2820 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2825 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2826 strcpy(dbname, "vmdb");
2832 #ifdef USEPOSTGRESVM
2833 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
2834 strcpy(dboption, "dboption not-specified in voicemail.conf");
2836 strcpy(dboption, s);
2839 cat = ast_category_browse(cfg, NULL);
2841 if (strcasecmp(cat, "general")) {
2842 var = ast_variable_browse(cfg, cat);
2843 if (strcasecmp(cat, "zonemessages")) {
2845 /* Process mailboxes in this context */
2847 append_mailbox(cat, var->name, var->value);
2852 /* Timezones in this context */
2855 z = malloc(sizeof(struct vm_zone));
2857 char *msg_format, *timezone;
2858 msg_format = ast_strdupa(var->value);
2859 if (msg_format != NULL) {
2860 timezone = strsep(&msg_format, "|");
2861 strncpy(z->name, var->name, sizeof(z->name) - 1);
2862 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
2863 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
2873 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2878 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2885 cat = ast_category_browse(cfg, cat);
2887 memset(fromstring,0,sizeof(fromstring));
2888 memset(emailtitle,0,sizeof(emailtitle));
2893 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
2894 pbxskip = ast_true(s);
2895 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
2896 strncpy(fromstring,s,sizeof(fromstring)-1);
2897 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
2898 strncpy(emailtitle,s,sizeof(emailtitle)-1);
2899 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
2900 char *tmpread, *tmpwrite;
2901 emailbody = strdup(s);
2903 /* substitute strings \t and \n into the apropriate characters */
2904 tmpread = tmpwrite = emailbody;
2905 while ((tmpwrite = strchr(tmpread,'\\'))) {
2906 int len = strlen("\n");
2907 switch (tmpwrite[1]) {
2909 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2910 strncpy(tmpwrite,"\n",len);
2913 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2914 strncpy(tmpwrite,"\t",len);
2917 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
2919 tmpread = tmpwrite+len;
2923 ast_mutex_unlock(&vmlock);
2926 ast_mutex_unlock(&vmlock);
2927 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2934 return(load_config());
2937 int unload_module(void)
2940 STANDARD_HANGUP_LOCALUSERS;
2941 res = ast_unregister_application(app);
2942 res |= ast_unregister_application(capp);
2943 res |= ast_unregister_application(app2);
2944 res |= ast_unregister_application(capp2);
2949 int load_module(void)
2952 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2953 res |= ast_register_application(capp, vm_exec, synopsis_vm, descrip_vm);
2954 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2955 res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain);
2959 if ((res=load_config())) {
2963 if ((res = sql_init())) {
2964 ast_log(LOG_WARNING, "SQL init\n");
2970 char *description(void)
2978 STANDARD_USECOUNT(res);
2984 return ASTERISK_GPL_KEY;