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;
129 static char *tdesc = "Comedian Mail (Voicemail System)";
131 static char *adapp = "CoMa";
133 static char *adsec = "_AST";
135 static char *addesc = "Comedian Mail";
137 static int adver = 1;
139 static char *synopsis_vm =
140 "Leave a voicemail message";
142 static char *descrip_vm =
143 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
144 "extension (must be configured in voicemail.conf). If the extension is\n"
145 "preceded by an 's' then instructions for leaving the message will be\n"
146 "skipped. If the extension is preceeded by 'u' then the \"unavailable\"\n"
147 "message will be played (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it\n"
148 "exists. If the extension is preceeded by a 'b' then the the busy message\n"
149 "will be played (that is, busy instead of unavail).\n"
150 "If the requested mailbox does not exist, and there exists a priority\n"
151 "n + 101, then that priority will be taken next.\n"
152 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
153 "Otherwise, it returns 0.\n";
155 static char *synopsis_vmain =
156 "Enter voicemail system";
158 static char *descrip_vmain =
159 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
160 "for the checking of voicemail. The mailbox can be passed as the option,\n"
161 "which will stop the voicemail system from prompting the user for the mailbox.\n"
162 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
163 "a context is specified, logins are considered in that context only.\n"
164 "Returns -1 if the user hangs up or 0 otherwise.\n";
166 /* Leave a message */
167 static char *capp = "VoiceMail2";
168 static char *app = "VoiceMail";
170 /* Check mail, control, etc */
171 static char *capp2 = "VoiceMailMain2";
172 static char *app2 = "VoiceMailMain";
174 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
175 struct ast_vm_user *users;
176 struct ast_vm_user *usersl;
177 struct vm_zone *zones = NULL;
178 struct vm_zone *zonesl = NULL;
179 static int attach_voicemail;
180 static int maxsilence;
181 static int silencethreshold = 128;
182 static char serveremail[80];
183 static char vmfmts[80];
184 static int vmmaxmessage;
187 static int maxlogins;
189 static char *emailbody = NULL;
190 static int pbxskip = 0;
191 static char fromstring[100];
192 static char emailtitle[100];
198 static void apply_options(struct ast_vm_user *vmu, char *options)
200 /* Destructively Parse options and apply */
201 char *stringp = ast_strdupa(options);
204 while((s = strsep(&stringp, "|"))) {
206 if ((var = strsep(&value, "=")) && value) {
207 if (!strcasecmp(var, "attach")) {
212 } else if (!strcasecmp(var, "serveremail")) {
213 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
214 } else if (!strcasecmp(var, "tz")) {
215 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
223 #include "mysql-vm-routines.h"
230 ast_mutex_t postgreslock;
232 static int sql_init(void)
234 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
235 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
237 dbhandler=PQconnectdb(dboption);
238 if (PQstatus(dbhandler) == CONNECTION_BAD) {
239 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
242 ast_mutex_init(&postgreslock);
244 /* fprintf(stderr,"postgres login OK\n"); */
248 static void sql_close(void)
254 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
262 char options[160] = "";
263 struct ast_vm_user *retval;
265 retval=malloc(sizeof(struct ast_vm_user));
267 /* fprintf(stderr,"postgres find_user:\n"); */
270 *retval->mailbox='\0';
271 *retval->context='\0';
272 *retval->password='\0';
273 *retval->fullname='\0';
276 *retval->serveremail='\0';
281 strcpy(retval->mailbox, mailbox);
284 strcpy(retval->context, context);
288 strcpy(retval->context, "default");
291 if (*retval->context) {
292 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", context, mailbox);
294 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='default' AND mailbox='%s'", mailbox);
296 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
297 ast_mutex_lock(&postgreslock);
298 PGSQLres=PQexec(dbhandler,query);
299 if (PGSQLres!=NULL) {
300 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
301 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
302 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
304 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
307 ast_mutex_unlock(&postgreslock);
311 numFields = PQnfields(PGSQLres);
312 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
313 if (PQntuples(PGSQLres) != 1) {
314 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
316 ast_mutex_unlock(&postgreslock);
320 for (i=0; i<numFields; i++) {
321 fname = PQfname(PGSQLres,i);
322 if (!strcmp(fname, "password")) {
323 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
324 } else if (!strcmp(fname, "fullname")) {
325 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
326 } else if (!strcmp(fname, "email")) {
327 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
328 } else if (!strcmp(fname, "pager")) {
329 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
330 } else if (!strcmp(fname, "options")) {
331 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
332 apply_options(retval, options);
337 ast_mutex_unlock(&postgreslock);
341 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
342 ast_mutex_unlock(&postgreslock);
347 } /* malloc() retval */
352 static void vm_change_password(struct ast_vm_user *vmu, char *password)
357 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);
359 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
361 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
362 ast_mutex_lock(&postgreslock);
363 PQexec(dbhandler, query);
364 strcpy(vmu->password, password);
365 ast_mutex_unlock(&postgreslock);
368 static void reset_user_pw(char *context, char *mailbox, char *password)
373 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
375 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
377 ast_mutex_lock(&postgreslock);
378 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
379 PQexec(dbhandler, query);
380 ast_mutex_unlock(&postgreslock);
383 #endif /* Postgres */
386 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
388 /* This function could be made to generate one from a database, too */
389 struct ast_vm_user *vmu=NULL, *cur;
390 ast_mutex_lock(&vmlock);
393 if ((!context || !strcasecmp(context, cur->context)) &&
394 (!strcasecmp(mailbox, cur->mailbox)))
402 /* Make a copy, so that on a reload, we have no race */
403 vmu = malloc(sizeof(struct ast_vm_user));
405 memcpy(vmu, cur, sizeof(struct ast_vm_user));
413 ast_mutex_unlock(&vmlock);
417 static int reset_user_pw(char *context, char *mailbox, char *newpass)
419 /* This function could be made to generate one from a database, too */
420 struct ast_vm_user *cur;
422 ast_mutex_lock(&vmlock);
425 if ((!context || !strcasecmp(context, cur->context)) &&
426 (!strcasecmp(mailbox, cur->mailbox)))
431 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
434 ast_mutex_unlock(&vmlock);
438 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
440 /* There's probably a better way of doing this. */
441 /* That's why I've put the password change in a separate function. */
442 /* This could also be done with a database function */
448 char tmpin[AST_CONFIG_MAX_PATH];
449 char tmpout[AST_CONFIG_MAX_PATH];
450 char *user, *pass, *rest, *trim;
451 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
452 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
453 configin = fopen((char *)tmpin,"r");
455 configout = fopen((char *)tmpout,"w+");
458 if(!configin || !configout) {
462 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
466 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
470 while (!feof(configin)) {
471 /* Read in the line */
472 fgets(inbuf, sizeof(inbuf), configin);
473 if (!feof(configin)) {
474 /* Make a backup of it */
475 memcpy(orig, inbuf, sizeof(orig));
476 /* Strip trailing \n and comment */
477 inbuf[strlen(inbuf) - 1] = '\0';
478 user = strchr(inbuf, ';');
484 pass = strchr(user, '=');
487 while(*trim && *trim < 33) {
497 while(*pass && *pass < 33)
501 rest = strchr(pass,',');
508 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
509 /* This is the line */
511 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
513 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
516 /* Put it back like it was */
517 fprintf(configout, orig);
524 unlink((char *)tmpin);
525 rename((char *)tmpout,(char *)tmpin);
526 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
527 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
531 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
533 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
536 static int make_file(char *dest, int len, char *dir, int num)
538 return snprintf(dest, len, "%s/msg%04d", dir, num);
542 inbuf(struct baseio *bio, FILE *fi)
549 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
564 inchar(struct baseio *bio, FILE *fi)
566 if(bio->iocp>=bio->iolen)
570 return bio->iobuf[bio->iocp++];
574 ochar(struct baseio *bio, int c, FILE *so)
576 if(bio->linelength>=BASELINELEN) {
577 if(fputs(eol,so)==EOF)
583 if(putc(((unsigned char)c),so)==EOF)
591 static int base_encode(char *filename, FILE *so)
593 unsigned char dtable[BASEMAXINLINE];
598 memset(&bio, 0, sizeof(bio));
599 bio.iocp = BASEMAXINLINE;
601 if ( !(fi = fopen(filename, "rb"))) {
602 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
610 dtable[26+i+9]= 'j'+i;
614 dtable[26+i+18]= 's'+i;
623 unsigned char igroup[3],ogroup[4];
626 igroup[0]= igroup[1]= igroup[2]= 0;
629 if ( (c = inchar(&bio, fi)) == EOF) {
634 igroup[n]= (unsigned char)c;
638 ogroup[0]= dtable[igroup[0]>>2];
639 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
640 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
641 ogroup[3]= dtable[igroup[2]&0x3F];
651 ochar(&bio, ogroup[i], so);
655 if(fputs(eol,so)==EOF)
663 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)
674 struct vm_zone *the_zone = NULL;
676 if (!strcmp(format, "wav49"))
678 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
679 p = popen(SENDMAIL, "w");
681 gethostname(host, sizeof(host));
682 if (strchr(srcemail, '@'))
683 strncpy(who, srcemail, sizeof(who)-1);
685 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
687 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
690 /* Does this user have a timezone specified? */
691 if (strlen(vmu->zonetag)) {
692 /* Find the zone in the list */
696 if (!strcmp(z->name, vmu->zonetag)) {
705 ast_localtime(&t,&tm,the_zone->timezone);
707 ast_localtime(&t,&tm,NULL);
708 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
709 fprintf(p, "Date: %s\n", date);
712 fprintf(p, "From: %s <%s>\n", fromstring, who);
714 fprintf(p, "From: Asterisk PBX <%s>\n", who);
715 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
719 fprintf(p, emailtitle, msgnum, mailbox) ;
724 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
726 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
727 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
728 fprintf(p, "MIME-Version: 1.0\n");
729 if (attach_user_voicemail) {
731 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
733 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
735 fprintf(p, "--%s\n", bound);
737 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
738 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
740 struct ast_channel *ast = ast_channel_alloc(0);
743 int vmlen = strlen(emailbody)*3 + 200;
744 if ((passdata = alloca(vmlen))) {
745 memset(passdata, 0, vmlen);
746 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
747 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
748 sprintf(passdata,"%d",msgnum);
749 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
750 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
751 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
752 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
753 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
754 fprintf(p, "%s\n",passdata);
755 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
756 ast_channel_free(ast);
757 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
759 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
761 "in mailbox %s from %s, on %s so you might\n"
762 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
763 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
765 if (attach_user_voicemail) {
766 fprintf(p, "--%s\n", bound);
767 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
768 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
769 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
770 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
772 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
773 base_encode(fname, p);
774 fprintf(p, "\n\n--%s--\n.\n", bound);
778 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
784 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
793 struct vm_zone *the_zone = NULL;
794 p = popen(SENDMAIL, "w");
797 gethostname(host, sizeof(host));
798 if (strchr(srcemail, '@'))
799 strncpy(who, srcemail, sizeof(who)-1);
801 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
803 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
806 /* Does this user have a timezone specified? */
807 if (strlen(vmu->zonetag)) {
808 /* Find the zone in the list */
812 if (!strcmp(z->name, vmu->zonetag)) {
821 ast_localtime(&t,&tm,the_zone->timezone);
823 ast_localtime(&t,&tm,NULL);
825 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
826 fprintf(p, "Date: %s\n", date);
827 fprintf(p, "From: Asterisk PBX <%s>\n", who);
828 fprintf(p, "To: %s\n", pager);
829 fprintf(p, "Subject: New VM\n\n");
830 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
831 fprintf(p, "New %s long msg in box %s\n"
832 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
835 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
841 static int get_date(char *s, int len)
847 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
850 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
854 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
855 if (ast_fileexists(fn, NULL, NULL) > 0) {
856 res = ast_streamfile(chan, fn, chan->language);
859 res = ast_waitstream(chan, ecodes);
863 res = ast_streamfile(chan, "vm-theperson", chan->language);
866 res = ast_waitstream(chan, ecodes);
869 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
874 res = ast_streamfile(chan, "vm-isonphone", chan->language);
876 res = ast_streamfile(chan, "vm-isunavail", chan->language);
879 res = ast_waitstream(chan, ecodes);
883 static int play_and_wait(struct ast_channel *chan, char *fn)
886 d = ast_streamfile(chan, fn, chan->language);
889 d = ast_waitstream(chan, AST_DIGIT_ANY);
890 ast_stopstream(chan);
894 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
898 int x, fmtcnt=1, res=-1,outmsg=0;
900 struct ast_filestream *others[MAX_OTHER_FORMATS];
901 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
902 char *sfmt[MAX_OTHER_FORMATS];
905 struct ast_dsp *sildet; /* silence detector dsp */
906 int totalsilence = 0;
908 int gotsilence = 0; /* did we timeout for silence? */
910 char prependfile[80];
912 ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
913 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
916 d = play_and_wait(chan, playfile);
918 d = ast_streamfile(chan, "beep",chan->language);
920 d = ast_waitstream(chan,"");
924 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
925 strcat(prependfile, "-prepend");
927 fmts = ast_strdupa(fmt);
930 strsep(&stringp, "|");
931 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
932 sfmt[0] = ast_strdupa(fmts);
934 while((fmt = strsep(&stringp, "|"))) {
935 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
936 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
939 sfmt[fmtcnt++] = ast_strdupa(fmt);
944 for (x=0;x<fmtcnt;x++) {
945 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
946 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
952 sildet = ast_dsp_new(); //Create the silence detector
954 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
957 ast_dsp_set_threshold(sildet, silencethreshold);
959 if (maxsilence > 0) {
960 rfmt = chan->readformat;
961 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
963 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
969 /* Loop forever, writing the packets we read to the writer(s), until
970 we read a # or get a hangup */
973 res = ast_waitfor(chan, 2000);
975 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
976 /* Try one more time in case of masq */
977 res = ast_waitfor(chan, 2000);
979 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
991 if (f->frametype == AST_FRAME_VOICE) {
992 /* write each format */
993 for (x=0;x<fmtcnt;x++) {
996 res = ast_writestream(others[x], f);
999 /* Silence Detection */
1000 if (maxsilence > 0) {
1002 ast_dsp_silence(sildet, f, &dspsilence);
1004 totalsilence = dspsilence;
1008 if (totalsilence > maxsilence) {
1009 /* Ended happily with silence */
1016 /* Exit on any error */
1018 ast_log(LOG_WARNING, "Error writing frame\n");
1022 } else if (f->frametype == AST_FRAME_VIDEO) {
1023 /* Write only once */
1024 ast_writestream(others[0], f);
1025 } else if (f->frametype == AST_FRAME_DTMF) {
1026 /* stop recording with any digit */
1027 if (option_verbose > 2)
1028 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1036 if (maxtime < (end - start)) {
1037 if (option_verbose > 2)
1038 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1047 if (option_verbose > 2)
1048 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1051 /* delete all the prepend files */
1052 for (x=0;x<fmtcnt;x++) {
1055 ast_closestream(others[x]);
1056 ast_filedelete(prependfile, sfmt[x]);
1060 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1063 struct ast_frame *fr;
1064 for (x=0;x<fmtcnt;x++) {
1065 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1066 realfiles[x] = ast_writefile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1067 if (!others[x] || !realfiles[x])
1070 ast_stream_rewind(others[x], totalsilence-200);
1072 ast_stream_rewind(others[x], 200);
1073 ast_truncstream(others[x]);
1074 /* add the original file too */
1075 while ((fr = ast_readframe(realfiles[x]))) {
1076 ast_writestream(others[x],fr);
1078 ast_closestream(others[x]);
1079 ast_closestream(realfiles[x]);
1080 ast_filerename(prependfile, recordfile, sfmt[x]);
1082 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1084 ast_filedelete(prependfile, sfmt[x]);
1088 if (ast_set_read_format(chan, rfmt)) {
1089 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1094 /* Let them know it worked */
1095 ast_streamfile(chan, "vm-msgsaved", chan->language);
1096 ast_waitstream(chan, "");
1102 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
1106 int x, fmtcnt=1, res=-1,outmsg=0;
1107 struct ast_frame *f;
1108 struct ast_filestream *others[MAX_OTHER_FORMATS];
1109 char *sfmt[MAX_OTHER_FORMATS];
1112 struct ast_dsp *sildet; /* silence detector dsp */
1113 int totalsilence = 0;
1115 int gotsilence = 0; /* did we timeout for silence? */
1118 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1119 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1122 d = play_and_wait(chan, playfile);
1124 d = ast_streamfile(chan, "beep",chan->language);
1126 d = ast_waitstream(chan,"");
1131 fmts = ast_strdupa(fmt);
1134 strsep(&stringp, "|");
1135 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1136 sfmt[0] = ast_strdupa(fmts);
1138 while((fmt = strsep(&stringp, "|"))) {
1139 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1140 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1143 sfmt[fmtcnt++] = ast_strdupa(fmt);
1148 for (x=0;x<fmtcnt;x++) {
1149 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1150 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1157 sildet = ast_dsp_new(); //Create the silence detector
1159 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1162 ast_dsp_set_threshold(sildet, silencethreshold);
1164 if (maxsilence > 0) {
1165 rfmt = chan->readformat;
1166 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1168 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1174 /* Loop forever, writing the packets we read to the writer(s), until
1175 we read a # or get a hangup */
1178 res = ast_waitfor(chan, 2000);
1180 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1181 /* Try one more time in case of masq */
1182 res = ast_waitfor(chan, 2000);
1184 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1196 if (f->frametype == AST_FRAME_VOICE) {
1197 /* write each format */
1198 for (x=0;x<fmtcnt;x++) {
1199 res = ast_writestream(others[x], f);
1202 /* Silence Detection */
1203 if (maxsilence > 0) {
1205 ast_dsp_silence(sildet, f, &dspsilence);
1207 totalsilence = dspsilence;
1211 if (totalsilence > maxsilence) {
1212 /* Ended happily with silence */
1219 /* Exit on any error */
1221 ast_log(LOG_WARNING, "Error writing frame\n");
1225 } else if (f->frametype == AST_FRAME_VIDEO) {
1226 /* Write only once */
1227 ast_writestream(others[0], f);
1228 } else if (f->frametype == AST_FRAME_DTMF) {
1229 if (f->subclass == '#') {
1230 if (option_verbose > 2)
1231 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1240 if (maxtime < (end - start)) {
1241 if (option_verbose > 2)
1242 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1251 if (option_verbose > 2)
1252 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1257 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1260 for (x=0;x<fmtcnt;x++) {
1264 ast_stream_rewind(others[x], totalsilence-200);
1266 ast_stream_rewind(others[x], 200);
1267 ast_truncstream(others[x]);
1268 ast_closestream(others[x]);
1271 if (ast_set_read_format(chan, rfmt)) {
1272 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1277 /* Let them know it worked */
1278 ast_streamfile(chan, "vm-msgsaved", chan->language);
1279 ast_waitstream(chan, "");
1287 static void free_user(struct ast_vm_user *vmu)
1293 static void free_zone(struct vm_zone *z)
1298 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1308 char prefile[256]="";
1316 struct ast_vm_user *vmu;
1317 struct ast_vm_user svm;
1319 strncpy(tmp, ext, sizeof(tmp) - 1);
1321 context = strchr(tmp, '@');
1327 if ((vmu = find_user(&svm, context, ext))) {
1328 /* Setup pre-file if appropriate */
1330 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1332 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1333 make_dir(dir, sizeof(dir), vmu->context, "", "");
1334 /* It's easier just to try to make it than to check for its existence */
1335 if (mkdir(dir, 0700) && (errno != EEXIST))
1336 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1337 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1338 /* It's easier just to try to make it than to check for its existence */
1339 if (mkdir(dir, 0700) && (errno != EEXIST))
1340 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1341 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1342 if (mkdir(dir, 0700) && (errno != EEXIST))
1343 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1344 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1346 /* Play the beginning intro if desired */
1347 if (strlen(prefile)) {
1348 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1349 if (ast_streamfile(chan, prefile, chan->language) > -1)
1350 res = ast_waitstream(chan, "#0");
1352 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1353 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1356 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1362 /* On a '#' we skip the instructions */
1366 if (!res && !silent) {
1367 res = ast_streamfile(chan, INTRO, chan->language);
1369 res = ast_waitstream(chan, ecodes);
1375 /* Check for a '0' here */
1377 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1378 if (strlen(chan->macrocontext))
1379 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1385 /* Unless we're *really* silent, try to send the beep */
1386 res = ast_streamfile(chan, "beep", chan->language);
1388 res = ast_waitstream(chan, "");
1394 /* The meat of recording the message... All the announcements and beeps have been played*/
1395 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1399 make_file(fn, sizeof(fn), dir, msgnum);
1400 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1401 (chan->callerid ? chan->callerid : "Unknown"),
1402 vmu->fullname, ext, chan->name);
1403 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1406 } while(msgnum < MAXMSG);
1407 if (msgnum < MAXMSG) {
1408 /* Store information */
1409 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1410 txt = fopen(txtfile, "w+");
1412 get_date(date, sizeof(date));
1416 "; Message Information file\n"
1432 chan->callerid ? chan->callerid : "Unknown",
1433 date, (long)time(NULL));
1436 ast_log(LOG_WARNING, "Error opening text file for output\n");
1437 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1440 txt = fopen(txtfile, "a");
1443 fprintf(txt, "duration=%ld\n", (long)(end-start));
1447 strsep(&stringp, "|");
1448 /* Send e-mail if applicable */
1449 if (strlen(vmu->email)) {
1450 int attach_user_voicemail = attach_voicemail;
1451 char *myserveremail = serveremail;
1452 if (vmu->attach > -1)
1453 attach_user_voicemail = vmu->attach;
1454 if (strlen(vmu->serveremail))
1455 myserveremail = vmu->serveremail;
1456 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1458 if (strlen(vmu->pager)) {
1459 char *myserveremail = serveremail;
1460 if (strlen(vmu->serveremail))
1461 myserveremail = vmu->serveremail;
1462 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1465 ast_log(LOG_WARNING, "No more messages possible\n");
1467 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1470 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1471 /*Send the call to n+101 priority, where n is the current priority*/
1472 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1473 chan->priority+=100;
1475 /* Leave voicemail for someone */
1476 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1480 static char *mbox(int id)
1508 static int count_messages(char *dir)
1512 for (x=0;x<MAXMSG;x++) {
1513 make_file(fn, sizeof(fn), dir, x);
1514 if (ast_fileexists(fn, NULL, NULL) < 1)
1520 static int say_and_wait(struct ast_channel *chan, int num)
1523 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1527 static int copy(char *infile, char *outfile)
1534 if ((ifd = open(infile, O_RDONLY)) < 0) {
1535 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1538 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1539 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1544 len = read(ifd, buf, sizeof(buf));
1546 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1552 res = write(ofd, buf, len);
1554 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1566 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1573 char *dbox = mbox(box);
1575 make_file(sfn, sizeof(sfn), dir, msg);
1576 make_dir(ddir, sizeof(ddir), context, username, dbox);
1578 for (x=0;x<MAXMSG;x++) {
1579 make_file(dfn, sizeof(dfn), ddir, x);
1580 if (ast_fileexists(dfn, NULL, NULL) < 0)
1585 ast_filecopy(sfn, dfn, NULL);
1586 if (strcmp(sfn, dfn)) {
1587 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1588 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1594 static int adsi_logo(unsigned char *buf)
1597 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1598 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1602 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1610 bytes += adsi_data_mode(buf + bytes);
1611 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1614 bytes += adsi_logo(buf);
1615 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1617 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1619 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1620 bytes += adsi_data_mode(buf + bytes);
1621 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1623 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1625 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1626 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1627 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1628 bytes += adsi_voice_mode(buf + bytes, 0);
1629 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1636 bytes += adsi_logo(buf);
1637 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1638 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1639 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1640 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1643 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1644 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1645 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1646 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1647 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1648 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1649 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1652 /* Add another dot */
1654 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1655 bytes += adsi_voice_mode(buf + bytes, 0);
1657 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1658 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1662 /* These buttons we load but don't use yet */
1663 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1664 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1665 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1666 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1667 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1668 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1669 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1672 /* Add another dot */
1674 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1675 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1676 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1681 snprintf(num, sizeof(num), "%d", x);
1682 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1684 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1685 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1688 /* Add another dot */
1690 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1691 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1692 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1695 if (adsi_end_download(chan)) {
1697 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1698 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1699 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1700 bytes += adsi_voice_mode(buf + bytes, 0);
1701 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1705 bytes += adsi_download_disconnect(buf + bytes);
1706 bytes += adsi_voice_mode(buf + bytes, 0);
1707 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1709 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1714 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1715 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1717 ast_log(LOG_DEBUG, "Restarting session...\n");
1720 /* Load the session now */
1721 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1723 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1725 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1727 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1731 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1734 if (!adsi_available(chan))
1736 x = adsi_load_session(chan, adapp, adver, 1);
1740 if (adsi_load_vmail(chan, useadsi)) {
1741 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1748 static void adsi_login(struct ast_channel *chan)
1752 unsigned char keys[8];
1754 if (!adsi_available(chan))
1759 /* Set one key for next */
1760 keys[3] = ADSI_KEY_APPS + 3;
1762 bytes += adsi_logo(buf + bytes);
1763 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1764 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1765 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1766 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1767 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1768 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1769 bytes += adsi_set_keys(buf + bytes, keys);
1770 bytes += adsi_voice_mode(buf + bytes, 0);
1771 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1774 static void adsi_password(struct ast_channel *chan)
1778 unsigned char keys[8];
1780 if (!adsi_available(chan))
1785 /* Set one key for next */
1786 keys[3] = ADSI_KEY_APPS + 3;
1788 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1789 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1790 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1791 bytes += adsi_set_keys(buf + bytes, keys);
1792 bytes += adsi_voice_mode(buf + bytes, 0);
1793 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1796 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1800 unsigned char keys[8];
1803 if (!adsi_available(chan))
1807 y = ADSI_KEY_APPS + 12 + start + x;
1808 if (y > ADSI_KEY_APPS + 12 + 4)
1810 keys[x] = ADSI_KEY_SKT | y;
1812 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1816 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1817 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1818 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1819 bytes += adsi_set_keys(buf + bytes, keys);
1820 bytes += adsi_voice_mode(buf + bytes, 0);
1822 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1825 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1828 char buf[256], buf1[256], buf2[256];
1834 char datetime[21]="";
1837 unsigned char keys[8];
1841 if (!adsi_available(chan))
1844 /* Retrieve important info */
1845 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1846 f = fopen(fn2, "r");
1849 fgets(buf, sizeof(buf), f);
1853 strsep(&stringp, "=");
1854 val = strsep(&stringp, "=");
1855 if (val && strlen(val)) {
1856 if (!strcmp(buf, "callerid"))
1857 strncpy(cid, val, sizeof(cid) - 1);
1858 if (!strcmp(buf, "origdate"))
1859 strncpy(datetime, val, sizeof(datetime) - 1);
1865 /* New meaning for keys */
1867 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1872 /* No prev key, provide "Folder" instead */
1873 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1876 /* If last message ... */
1878 /* but not only message, provide "Folder" instead */
1879 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1880 bytes += adsi_voice_mode(buf + bytes, 0);
1883 /* Otherwise if only message, leave blank */
1889 ast_callerid_parse(cid, &name, &num);
1893 name = "Unknown Caller";
1895 /* If deleted, show "undeleted" */
1898 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1901 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1902 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1903 strcasecmp(folder, "INBOX") ? " Messages" : "");
1904 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1906 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1907 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1908 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1909 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1910 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1911 bytes += adsi_set_keys(buf + bytes, keys);
1912 bytes += adsi_voice_mode(buf + bytes, 0);
1914 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1917 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1921 unsigned char keys[8];
1925 if (!adsi_available(chan))
1928 /* New meaning for keys */
1930 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1936 /* No prev key, provide "Folder" instead */
1937 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1940 /* If last message ... */
1942 /* but not only message, provide "Folder" instead */
1943 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1945 /* Otherwise if only message, leave blank */
1950 /* If deleted, show "undeleted" */
1952 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1955 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1956 bytes += adsi_set_keys(buf + bytes, keys);
1957 bytes += adsi_voice_mode(buf + bytes, 0);
1959 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1962 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1964 char buf[256], buf1[256], buf2[256];
1966 unsigned char keys[8];
1969 char *newm = (new == 1) ? "message" : "messages";
1970 char *oldm = (old == 1) ? "message" : "messages";
1971 if (!adsi_available(chan))
1974 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1976 strcat(buf1, " and");
1977 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1979 snprintf(buf2, sizeof(buf2), "%s.", newm);
1982 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1983 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1985 strcpy(buf1, "You have no messages.");
1988 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1989 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1990 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1993 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1997 /* Don't let them listen if there are none */
2000 bytes += adsi_set_keys(buf + bytes, keys);
2002 bytes += adsi_voice_mode(buf + bytes, 0);
2004 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2007 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2009 char buf[256], buf1[256], buf2[256];
2011 unsigned char keys[8];
2014 char *mess = (messages == 1) ? "message" : "messages";
2016 if (!adsi_available(chan))
2019 /* Original command keys */
2021 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2029 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2030 strcasecmp(folder, "INBOX") ? " folder" : "");
2033 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2035 strcpy(buf2, "no messages.");
2036 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2037 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2038 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2039 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2040 bytes += adsi_set_keys(buf + bytes, keys);
2042 bytes += adsi_voice_mode(buf + bytes, 0);
2044 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2048 static void adsi_clear(struct ast_channel *chan)
2052 if (!adsi_available(chan))
2054 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2055 bytes += adsi_voice_mode(buf + bytes, 0);
2057 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2060 static void adsi_goodbye(struct ast_channel *chan)
2065 if (!adsi_available(chan))
2067 bytes += adsi_logo(buf + bytes);
2068 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2069 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2070 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2071 bytes += adsi_voice_mode(buf + bytes, 0);
2073 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2076 static int get_folder(struct ast_channel *chan, int start)
2081 d = play_and_wait(chan, "vm-press");
2084 for (x = start; x< 5; x++) {
2085 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
2087 d = play_and_wait(chan, "vm-for");
2090 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2091 d = play_and_wait(chan, fn);
2094 d = play_and_wait(chan, "vm-messages");
2097 d = ast_waitfordigit(chan, 500);
2101 d = play_and_wait(chan, "vm-tocancel");
2104 d = ast_waitfordigit(chan, 4000);
2108 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2111 res = play_and_wait(chan, fn);
2112 while (((res < '0') || (res > '9')) &&
2113 (res != '#') && (res >= 0)) {
2114 res = get_folder(chan, 0);
2119 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2124 while((cmd >= 0) && (cmd != 't') && (cmd != '#')) {
2129 /* prepend a message to the current message and return */
2132 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2133 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts);
2143 cmd = play_and_wait(chan,"vm-forwardoptions");
2145 cmd = ast_waitfordigit(chan,6000);
2157 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2164 struct ast_config *mif;
2168 int res = 0, cmd = 0;
2169 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2172 int saved_messages = 0, found = 0;
2173 int valid_extensions = 0;
2174 while (!res && !valid_extensions) {
2175 res = ast_streamfile(chan, "vm-extension", chan->language);
2178 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2180 /* start all over if no username */
2181 if (!strlen(username))
2184 s = strsep(&stringp, "*");
2185 /* start optimistic */
2186 valid_extensions = 1;
2188 /* find_user is going to malloc since we have a NULL as first argument */
2189 if ((receiver = find_user(NULL, context, s))) {
2191 vmtmp = extensions = receiver;
2193 vmtmp->next = receiver;
2198 valid_extensions = 0;
2201 s = strsep(&stringp, "*");
2203 /* break from the loop of reading the extensions */
2204 if (valid_extensions)
2206 /* invalid extension, try again */
2207 res = play_and_wait(chan, "pbx-invalid");
2209 /* check if we're clear to proceed */
2210 if (!extensions || !valid_extensions)
2213 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2215 while(!res && vmtmp) {
2216 /* if (play_and_wait(chan, "vm-savedto"))
2219 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2220 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2221 ast_log(LOG_DEBUG, sys);
2224 todircount = count_messages(todir);
2225 strncpy(tmp, fmt, sizeof(tmp));
2227 while((s = strsep(&stringp, "|"))) {
2228 /* XXX This is a hack -- we should use build_filename or similar XXX */
2229 if (!strcasecmp(s, "wav49"))
2231 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2232 ast_log(LOG_DEBUG, sys);
2235 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2236 ast_log(LOG_DEBUG, sys);
2238 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2240 /* load the information on the source message so we can send an e-mail like a new message */
2241 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2242 if ((mif=ast_load(miffile))) {
2244 /* set callerid and duration variables */
2245 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2246 s = ast_variable_retrieve(mif, NULL, "duration");
2251 if (strlen(vmtmp->email)) {
2252 int attach_user_voicemail = attach_voicemail;
2253 char *myserveremail = serveremail;
2254 if (vmtmp->attach > -1)
2255 attach_user_voicemail = vmtmp->attach;
2256 if (strlen(vmtmp->serveremail))
2257 myserveremail = vmtmp->serveremail;
2258 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2261 if (strlen(vmtmp->pager)) {
2262 char *myserveremail = serveremail;
2263 if (strlen(vmtmp->serveremail))
2264 myserveremail = vmtmp->serveremail;
2265 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2268 ast_destroy(mif); /* or here */
2270 /* Leave voicemail for someone */
2271 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2275 vmtmp = vmtmp->next;
2278 if (saved_messages > 0) {
2279 /* give confirmatopm that the message was saved */
2280 if (saved_messages == 1)
2281 res = play_and_wait(chan, "vm-message");
2283 res = play_and_wait(chan, "vm-messages");
2285 res = play_and_wait(chan, "vm-saved");
2287 return res ? res : cmd;
2291 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2294 if ((res = ast_streamfile(chan, file, chan->language)))
2295 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2297 res = ast_waitstream(chan, AST_DIGIT_ANY);
2301 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2304 if ((res = ast_streamfile(chan, file, chan->language)))
2305 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2307 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2311 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2314 char filename[256], *origtime;
2315 struct vm_zone *the_zone = NULL;
2316 struct ast_config *msg_cfg;
2320 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2321 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2322 msg_cfg = ast_load(filename);
2324 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2328 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2330 if (sscanf(origtime,"%ld",&tin) < 1) {
2331 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2335 ast_destroy(msg_cfg);
2337 /* Does this user have a timezone specified? */
2338 if (strlen(vmu->zonetag)) {
2339 /* Find the zone in the list */
2343 if (!strcmp(z->name, vmu->zonetag)) {
2351 /* No internal variable parsing for now, so we'll comment it out for the time being */
2353 /* Set the DIFF_* variables */
2354 localtime_r(&t, &time_now);
2355 gettimeofday(&tv_now,NULL);
2356 tnow = tv_now.tv_sec;
2357 localtime_r(&tnow,&time_then);
2359 /* Day difference */
2360 if (time_now.tm_year == time_then.tm_year)
2361 sprintf(temp,"%d",time_now.tm_yday);
2363 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2364 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2366 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2369 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2371 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2373 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2378 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2382 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2383 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2385 res = wait_file2(chan, vms, "vm-first");
2386 else if (msg == vms->lastmsg)
2387 res = wait_file2(chan, vms, "vm-last");
2389 res = wait_file2(chan, vms, "vm-message");
2390 if (msg && (msg != vms->lastmsg)) {
2392 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2397 res = play_message_datetime(chan,vmu,vms);
2400 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2401 vms->heard[msg] = 1;
2402 res = wait_file(chan, vms, vms->fn);
2407 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2409 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2410 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2411 vms->lastmsg = count_messages(vms->curdir) - 1;
2412 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2415 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2418 char ntxt[256] = "";
2420 if (vms->lastmsg > -1) {
2421 /* Get the deleted messages fixed */
2423 for (x=0;x < MAXMSG;x++) {
2424 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2425 /* Save this message. It's not in INBOX or hasn't been heard */
2426 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2427 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2430 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2431 if (strcmp(vms->fn, vms->fn2)) {
2432 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2433 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2434 ast_filerename(vms->fn, vms->fn2, NULL);
2437 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2438 /* Move to old folder before deleting */
2439 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2442 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2443 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2444 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2446 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2447 ast_filedelete(vms->fn, NULL);
2451 memset(vms->deleted, 0, sizeof(vms->deleted));
2452 memset(vms->heard, 0, sizeof(vms->heard));
2455 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2457 /* Introduce messages they have */
2459 res = play_and_wait(chan, "vm-youhave");
2461 if (vms->newmessages) {
2462 res = say_and_wait(chan, vms->newmessages);
2464 res = play_and_wait(chan, "vm-INBOX");
2465 if (vms->oldmessages && !res)
2466 res = play_and_wait(chan, "vm-and");
2468 if ((vms->newmessages == 1))
2469 res = play_and_wait(chan, "vm-message");
2471 res = play_and_wait(chan, "vm-messages");
2475 if (!res && vms->oldmessages) {
2476 res = say_and_wait(chan, vms->oldmessages);
2478 res = play_and_wait(chan, "vm-Old");
2480 if (vms->oldmessages == 1)
2481 res = play_and_wait(chan, "vm-message");
2483 res = play_and_wait(chan, "vm-messages");
2487 if (!vms->oldmessages && !vms->newmessages) {
2488 res = play_and_wait(chan, "vm-no");
2490 res = play_and_wait(chan, "vm-messages");
2497 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2500 /* Play instructions and wait for new command */
2502 if (vms->starting) {
2503 if (vms->lastmsg > -1) {
2504 res = play_and_wait(chan, "vm-onefor");
2506 res = play_and_wait(chan, vms->vmbox);
2508 res = play_and_wait(chan, "vm-messages");
2511 res = play_and_wait(chan, "vm-opts");
2514 res = play_and_wait(chan, "vm-prev");
2516 res = play_and_wait(chan, "vm-repeat");
2517 if (!res && (vms->curmsg != vms->lastmsg))
2518 res = play_and_wait(chan, "vm-next");
2520 if (!vms->deleted[vms->curmsg])
2521 res = play_and_wait(chan, "vm-delete");
2523 res = play_and_wait(chan, "vm-undelete");
2525 res = play_and_wait(chan, "vm-toforward");
2527 res = play_and_wait(chan, "vm-savemessage");
2531 res = play_and_wait(chan, "vm-helpexit");
2533 res = ast_waitfordigit(chan, 6000);
2536 if (vms->repeats > 2) {
2537 res = play_and_wait(chan, "vm-goodbye");
2546 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2550 char newpassword[80] = "";
2551 char newpassword2[80] = "";
2552 char prefile[256]="";
2556 if (adsi_available(chan))
2558 bytes += adsi_logo(buf + bytes);
2559 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2560 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2561 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2562 bytes += adsi_voice_mode(buf + bytes, 0);
2563 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2565 while((cmd >= 0) && (cmd != 't')) {
2570 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2571 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2574 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2575 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2578 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2579 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2582 newpassword[1] = '\0';
2583 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2586 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2589 newpassword2[1] = '\0';
2590 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2594 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2597 if (strcmp(newpassword, newpassword2)) {
2598 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2599 cmd = play_and_wait(chan, "vm-mismatch");
2602 vm_change_password(vmu,newpassword);
2603 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2604 cmd = play_and_wait(chan,"vm-passchanged");
2610 cmd = play_and_wait(chan,"vm-options");
2612 cmd = ast_waitfordigit(chan,6000);
2624 static int vm_execmain(struct ast_channel *chan, void *data)
2626 /* XXX This is, admittedly, some pretty horrendus code. For some
2627 reason it just seemed a lot easier to do with GOTO's. I feel
2628 like I'm back in my GWBASIC days. XXX */
2633 struct localuser *u;
2634 char prefixstr[80] ="";
2635 char empty[80] = "";
2639 char tmp[256], *ext;
2640 char fmtc[256] = "";
2642 struct vm_state vms;
2644 struct ast_vm_user *vmu = NULL, vmus;
2648 memset(&vms, 0, sizeof(vms));
2649 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2650 if (chan->_state != AST_STATE_UP)
2653 if (data && strlen(data)) {
2654 strncpy(tmp, data, sizeof(tmp) - 1);
2659 /* We should skip the user's password */
2664 /* We should prefix the mailbox with the supplied data */
2670 context = strchr(ext, '@');
2677 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2679 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2680 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2687 /* If ADSI is supported, setup login screen */
2688 adsi_begin(chan, &useadsi);
2689 if (!skipuser && useadsi)
2691 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2692 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2696 /* Authenticate them and get their mailbox/password */
2698 while (!valid && (logretries < maxlogins)) {
2699 /* Prompt for, and read in the username */
2700 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2701 ast_log(LOG_WARNING, "Couldn't read username\n");
2704 if (!strlen(vms.username)) {
2705 if (option_verbose > 2)
2706 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2711 adsi_password(chan);
2712 if (ast_streamfile(chan, "vm-password", chan->language)) {
2713 ast_log(LOG_WARNING, "Unable to stream password file\n");
2716 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2717 ast_log(LOG_WARNING, "Unable to read password\n");
2721 char fullusername[80] = "";
2722 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2723 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2724 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2727 vmu = find_user(&vmus, context, vms.username);
2728 if (vmu && !strcmp(vmu->password, password))
2731 if (option_verbose > 2)
2732 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2734 strncpy(vms.username, empty, sizeof(vms.username) -1);
2739 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2744 if (!valid && (logretries >= maxlogins)) {
2745 ast_stopstream(chan);
2746 res = play_and_wait(chan, "vm-goodbye");
2752 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2753 mkdir(vms.curdir, 0700);
2754 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2755 mkdir(vms.curdir, 0700);
2756 /* Retrieve old and new message counts */
2757 open_mailbox(&vms, vmu, 1);
2758 vms.oldmessages = vms.lastmsg + 1;
2759 /* Start in INBOX */
2760 open_mailbox(&vms, vmu, 0);
2761 vms.newmessages = vms.lastmsg + 1;
2764 /* Select proper mailbox FIRST!! */
2765 if (!vms.newmessages && vms.oldmessages) {
2766 /* If we only have old messages start here */
2767 open_mailbox(&vms, vmu, 1);
2771 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2773 cmd = vm_intro(chan, &vms);
2776 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2783 if (vms.lastmsg > -1) {
2784 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2786 cmd = play_and_wait(chan, "vm-youhave");
2788 cmd = play_and_wait(chan, "vm-no");
2790 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2791 cmd = play_and_wait(chan, vms.fn);
2794 cmd = play_and_wait(chan, "vm-messages");
2797 case '2': /* Change folders */
2799 adsi_folders(chan, 0, "Change to folder...");
2800 cmd = get_folder2(chan, "vm-changeto", 0);
2803 } else if (cmd > 0) {
2805 close_mailbox(&vms, vmu);
2806 open_mailbox(&vms, vmu, cmd);
2810 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2812 cmd = play_and_wait(chan, vms.vmbox);
2814 cmd = play_and_wait(chan, "vm-messages");
2820 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2822 cmd = play_and_wait(chan, "vm-nomore");
2826 if (vms.curmsg < vms.lastmsg) {
2828 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2830 cmd = play_and_wait(chan, "vm-nomore");
2834 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2836 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2837 if (vms.deleted[vms.curmsg])
2838 cmd = play_and_wait(chan, "vm-deleted");
2840 cmd = play_and_wait(chan, "vm-undeleted");
2843 if(vms.lastmsg > -1)
2844 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2846 cmd = play_and_wait(chan, "vm-nomore");
2850 adsi_folders(chan, 1, "Save to folder...");
2851 cmd = get_folder2(chan, "vm-savefolder", 1);
2852 box = 0; /* Shut up compiler */
2856 } else if (cmd > 0) {
2857 box = cmd = cmd - '0';
2858 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2859 vms.deleted[vms.curmsg]=1;
2861 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2863 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2865 cmd = play_and_wait(chan, "vm-message");
2867 cmd = say_and_wait(chan, vms.curmsg + 1);
2869 cmd = play_and_wait(chan, "vm-savedto");
2871 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2872 cmd = play_and_wait(chan, vms.fn);
2875 cmd = play_and_wait(chan, "vm-messages");
2878 if (!vms.starting) {
2879 cmd = play_and_wait(chan, "vm-onefor");
2881 cmd = play_and_wait(chan, vms.vmbox);
2883 cmd = play_and_wait(chan, "vm-messages");
2885 cmd = play_and_wait(chan, "vm-opts");
2890 cmd = vm_options(chan, vmu, &vms, vmfmts);
2892 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2894 default: /* Nothing */
2895 cmd = vm_instructions(chan, &vms);
2899 if ((cmd == 't') || (cmd == '#')) {
2909 ast_stopstream(chan);
2912 res = play_and_wait(chan, "vm-goodbye");
2917 adsi_unload_session(chan);
2920 close_mailbox(&vms, vmu);
2924 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2926 LOCAL_USER_REMOVE(u);
2931 static int vm_exec(struct ast_channel *chan, void *data)
2933 int res=0, silent=0, busy=0, unavail=0;
2934 struct localuser *u;
2935 char tmp[256], *ext;
2938 if (chan->_state != AST_STATE_UP)
2940 if (data && strlen(data))
2941 strncpy(tmp, data, sizeof(tmp) - 1);
2943 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2954 } else if (*ext == 'b') {
2957 } else if (*ext == 'u') {
2963 res = leave_voicemail(chan, ext, silent, busy, unavail);
2964 LOCAL_USER_REMOVE(u);
2968 static int append_mailbox(char *context, char *mbox, char *data)
2970 /* Assumes lock is already held */
2974 struct ast_vm_user *vmu;
2975 strncpy(tmp, data, sizeof(tmp));
2976 vmu = malloc(sizeof(struct ast_vm_user));
2978 memset(vmu, 0, sizeof(struct ast_vm_user));
2979 strncpy(vmu->context, context, sizeof(vmu->context));
2980 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2983 if ((s = strsep(&stringp, ",")))
2984 strncpy(vmu->password, s, sizeof(vmu->password));
2985 if (stringp && (s = strsep(&stringp, ",")))
2986 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2987 if (stringp && (s = strsep(&stringp, ",")))
2988 strncpy(vmu->email, s, sizeof(vmu->email));
2989 if (stringp && (s = strsep(&stringp, ",")))
2990 strncpy(vmu->pager, s, sizeof(vmu->pager));
2991 if (stringp && (s = strsep(&stringp, ",")))
2992 apply_options(vmu, s);
3003 static int load_config(void)
3005 struct ast_vm_user *cur, *l;
3006 struct vm_zone *zcur, *zl;
3007 struct ast_config *cfg;
3009 struct ast_variable *var;
3018 cfg = ast_load(VOICEMAIL_CONFIG);
3019 ast_mutex_lock(&vmlock);
3037 /* General settings */
3038 attach_voicemail = 1;
3039 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
3041 attach_voicemail = ast_true(astattach);
3043 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
3044 maxsilence = atoi(silencestr);
3049 silencethreshold = 256;
3050 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
3051 silencethreshold = atoi(thresholdstr);
3053 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
3054 astemail = ASTERISK_USERNAME;
3055 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
3058 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
3059 if (sscanf(s, "%d", &x) == 1) {
3062 ast_log(LOG_WARNING, "Invalid max message time length\n");
3065 fmt = ast_variable_retrieve(cfg, "general", "format");
3068 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
3071 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
3072 if (sscanf(s, "%d", &x) == 1) {
3075 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
3079 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
3080 if (sscanf(s, "%d", &x) == 1) {
3083 ast_log(LOG_WARNING, "Invalid skipms value\n");
3088 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
3089 if (sscanf(s, "%d", &x) == 1) {
3092 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
3097 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
3098 strcpy(dbuser, "test");
3102 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
3103 strcpy(dbpass, "test");
3107 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
3112 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
3113 strcpy(dbname, "vmdb");
3119 #ifdef USEPOSTGRESVM
3120 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
3121 strcpy(dboption, "dboption not-specified in voicemail.conf");
3123 strcpy(dboption, s);
3126 cat = ast_category_browse(cfg, NULL);
3128 if (strcasecmp(cat, "general")) {
3129 var = ast_variable_browse(cfg, cat);
3130 if (strcasecmp(cat, "zonemessages")) {
3132 /* Process mailboxes in this context */
3134 append_mailbox(cat, var->name, var->value);
3139 /* Timezones in this context */
3142 z = malloc(sizeof(struct vm_zone));
3144 char *msg_format, *timezone;
3145 msg_format = ast_strdupa(var->value);
3146 if (msg_format != NULL) {
3147 timezone = strsep(&msg_format, "|");
3148 strncpy(z->name, var->name, sizeof(z->name) - 1);
3149 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
3150 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
3160 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3165 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3172 cat = ast_category_browse(cfg, cat);
3174 memset(fromstring,0,sizeof(fromstring));
3175 memset(emailtitle,0,sizeof(emailtitle));
3180 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
3181 pbxskip = ast_true(s);
3182 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
3183 strncpy(fromstring,s,sizeof(fromstring)-1);
3184 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
3185 strncpy(emailtitle,s,sizeof(emailtitle)-1);
3186 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
3187 char *tmpread, *tmpwrite;
3188 emailbody = strdup(s);
3190 /* substitute strings \t and \n into the apropriate characters */
3191 tmpread = tmpwrite = emailbody;
3192 while ((tmpwrite = strchr(tmpread,'\\'))) {
3193 int len = strlen("\n");
3194 switch (tmpwrite[1]) {
3196 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3197 strncpy(tmpwrite,"\n",len);
3200 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3201 strncpy(tmpwrite,"\t",len);
3204 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
3206 tmpread = tmpwrite+len;
3210 ast_mutex_unlock(&vmlock);
3213 ast_mutex_unlock(&vmlock);
3214 ast_log(LOG_WARNING, "Error reading voicemail config\n");
3221 return(load_config());
3224 int unload_module(void)
3227 STANDARD_HANGUP_LOCALUSERS;
3228 res = ast_unregister_application(app);
3229 res |= ast_unregister_application(capp);
3230 res |= ast_unregister_application(app2);
3231 res |= ast_unregister_application(capp2);
3236 int load_module(void)
3239 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
3240 res |= ast_register_application(capp, vm_exec, synopsis_vm, descrip_vm);
3241 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
3242 res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain);
3246 if ((res=load_config())) {
3250 if ((res = sql_init())) {
3251 ast_log(LOG_WARNING, "SQL init\n");
3257 char *description(void)
3265 STANDARD_USECOUNT(res);
3271 return ASTERISK_GPL_KEY;