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>
29 #include <asterisk/cli.h>
38 #include <sys/types.h>
42 /* we define USESQLVM when we have MySQL or POSTGRES */
44 #include <mysql/mysql.h>
50 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
52 #include <postgresql/libpq-fe.h>
57 static inline int sql_init(void) { return 0; }
58 static inline void sql_close(void) { }
62 #include "../asterisk.h"
63 #include "../astconf.h"
65 #define COMMAND_TIMEOUT 5000
67 #define VOICEMAIL_CONFIG "voicemail.conf"
68 #define ASTERISK_USERNAME "asterisk"
70 #define SENDMAIL "/usr/sbin/sendmail -t"
72 #define INTRO "vm-intro"
75 #define MAX_OTHER_FORMATS 10
77 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
79 #define BASEMAXINLINE 256
80 #define BASELINELEN 72
81 #define BASEMAXINLINE 256
84 #define MAX_DATETIME_FORMAT 512
85 #define DIGITS_DIR AST_SOUNDS "/digits/"
91 unsigned char iobuf[BASEMAXINLINE];
101 char serveremail[80];
105 struct ast_vm_user *next;
111 char msg_format[512];
112 struct vm_zone *next;
132 static char *tdesc = "Comedian Mail (Voicemail System)";
134 static char *adapp = "CoMa";
136 static char *adsec = "_AST";
138 static char *addesc = "Comedian Mail";
140 static int adver = 1;
142 static char *synopsis_vm =
143 "Leave a voicemail message";
145 static char *descrip_vm =
146 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
147 "extension (must be configured in voicemail.conf). If the extension is\n"
148 "preceded by an 's' then instructions for leaving the message will be\n"
149 "skipped. If the extension is preceeded by 'u' then the \"unavailable\"\n"
150 "message will be played (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it\n"
151 "exists. If the extension is preceeded by a 'b' then the the busy message\n"
152 "will be played (that is, busy instead of unavail).\n"
153 "If the requested mailbox does not exist, and there exists a priority\n"
154 "n + 101, then that priority will be taken next.\n"
155 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
156 "Otherwise, it returns 0.\n";
158 static char *synopsis_vmain =
159 "Enter voicemail system";
161 static char *descrip_vmain =
162 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
163 "for the checking of voicemail. The mailbox can be passed as the option,\n"
164 "which will stop the voicemail system from prompting the user for the mailbox.\n"
165 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
166 "a context is specified, logins are considered in that context only.\n"
167 "Returns -1 if the user hangs up or 0 otherwise.\n";
169 /* Leave a message */
170 static char *capp = "VoiceMail2";
171 static char *app = "VoiceMail";
173 /* Check mail, control, etc */
174 static char *capp2 = "VoiceMailMain2";
175 static char *app2 = "VoiceMailMain";
177 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
178 struct ast_vm_user *users;
179 struct ast_vm_user *usersl;
180 struct vm_zone *zones = NULL;
181 struct vm_zone *zonesl = NULL;
182 static int attach_voicemail;
183 static int maxsilence;
184 static int silencethreshold = 128;
185 static char serveremail[80];
186 static char vmfmts[80];
187 static int vmmaxmessage;
190 static int maxlogins;
192 static char *emailbody = NULL;
193 static int pbxskip = 0;
194 static char fromstring[100];
195 static char emailtitle[100];
201 static void apply_options(struct ast_vm_user *vmu, char *options)
203 /* Destructively Parse options and apply */
204 char *stringp = ast_strdupa(options);
207 while((s = strsep(&stringp, "|"))) {
209 if ((var = strsep(&value, "=")) && value) {
210 if (!strcasecmp(var, "attach")) {
215 } else if (!strcasecmp(var, "serveremail")) {
216 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
217 } else if (!strcasecmp(var, "tz")) {
218 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
226 #include "mysql-vm-routines.h"
233 ast_mutex_t postgreslock;
235 static int sql_init(void)
237 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
238 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
240 dbhandler=PQconnectdb(dboption);
241 if (PQstatus(dbhandler) == CONNECTION_BAD) {
242 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
245 ast_mutex_init(&postgreslock);
247 /* fprintf(stderr,"postgres login OK\n"); */
251 static void sql_close(void)
257 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
265 char options[160] = "";
266 struct ast_vm_user *retval;
268 retval=malloc(sizeof(struct ast_vm_user));
270 /* fprintf(stderr,"postgres find_user:\n"); */
273 *retval->mailbox='\0';
274 *retval->context='\0';
275 strcpy(retval->password, "NULL");
276 *retval->fullname='\0';
279 *retval->serveremail='\0';
284 strcpy(retval->mailbox, mailbox);
287 strcpy(retval->context, context);
291 strcpy(retval->context, "default");
293 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
295 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
296 ast_mutex_lock(&postgreslock);
297 PGSQLres=PQexec(dbhandler,query);
298 if (PGSQLres!=NULL) {
299 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
300 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
301 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
303 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
306 ast_mutex_unlock(&postgreslock);
310 numFields = PQnfields(PGSQLres);
311 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
312 if (PQntuples(PGSQLres) != 1) {
313 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
315 ast_mutex_unlock(&postgreslock);
319 for (i=0; i<numFields; i++) {
320 fname = PQfname(PGSQLres,i);
321 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
322 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
323 } else if (!strcmp(fname, "fullname")) {
324 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
325 } else if (!strcmp(fname, "email")) {
326 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
327 } else if (!strcmp(fname, "pager")) {
328 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
329 } else if (!strcmp(fname, "options")) {
330 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
331 apply_options(retval, options);
336 ast_mutex_unlock(&postgreslock);
340 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
341 ast_mutex_unlock(&postgreslock);
346 } /* malloc() retval */
351 static void vm_change_password(struct ast_vm_user *vmu, char *password)
356 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);
358 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
360 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
361 ast_mutex_lock(&postgreslock);
362 PQexec(dbhandler, query);
363 strcpy(vmu->password, password);
364 ast_mutex_unlock(&postgreslock);
367 static void reset_user_pw(char *context, char *mailbox, char *password)
372 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
374 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
376 ast_mutex_lock(&postgreslock);
377 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
378 PQexec(dbhandler, query);
379 ast_mutex_unlock(&postgreslock);
382 #endif /* Postgres */
385 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
387 /* This function could be made to generate one from a database, too */
388 struct ast_vm_user *vmu=NULL, *cur;
389 ast_mutex_lock(&vmlock);
392 if ((!context || !strcasecmp(context, cur->context)) &&
393 (!strcasecmp(mailbox, cur->mailbox)))
401 /* Make a copy, so that on a reload, we have no race */
402 vmu = malloc(sizeof(struct ast_vm_user));
404 memcpy(vmu, cur, sizeof(struct ast_vm_user));
412 ast_mutex_unlock(&vmlock);
416 static int reset_user_pw(char *context, char *mailbox, char *newpass)
418 /* This function could be made to generate one from a database, too */
419 struct ast_vm_user *cur;
421 ast_mutex_lock(&vmlock);
424 if ((!context || !strcasecmp(context, cur->context)) &&
425 (!strcasecmp(mailbox, cur->mailbox)))
430 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
433 ast_mutex_unlock(&vmlock);
437 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
439 /* There's probably a better way of doing this. */
440 /* That's why I've put the password change in a separate function. */
441 /* This could also be done with a database function */
447 char tmpin[AST_CONFIG_MAX_PATH];
448 char tmpout[AST_CONFIG_MAX_PATH];
449 char *user, *pass, *rest, *trim;
450 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
451 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
452 configin = fopen((char *)tmpin,"r");
454 configout = fopen((char *)tmpout,"w+");
457 if(!configin || !configout) {
461 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
465 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
469 while (!feof(configin)) {
470 /* Read in the line */
471 fgets(inbuf, sizeof(inbuf), configin);
472 if (!feof(configin)) {
473 /* Make a backup of it */
474 memcpy(orig, inbuf, sizeof(orig));
475 /* Strip trailing \n and comment */
476 inbuf[strlen(inbuf) - 1] = '\0';
477 user = strchr(inbuf, ';');
483 pass = strchr(user, '=');
486 while(*trim && *trim < 33) {
496 while(*pass && *pass < 33)
500 rest = strchr(pass,',');
507 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
508 /* This is the line */
510 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
512 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
515 /* Put it back like it was */
516 fprintf(configout, orig);
523 unlink((char *)tmpin);
524 rename((char *)tmpout,(char *)tmpin);
525 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
526 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
530 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
532 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
535 static int make_file(char *dest, int len, char *dir, int num)
537 return snprintf(dest, len, "%s/msg%04d", dir, num);
541 inbuf(struct baseio *bio, FILE *fi)
548 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
563 inchar(struct baseio *bio, FILE *fi)
565 if(bio->iocp>=bio->iolen)
569 return bio->iobuf[bio->iocp++];
573 ochar(struct baseio *bio, int c, FILE *so)
575 if(bio->linelength>=BASELINELEN) {
576 if(fputs(eol,so)==EOF)
582 if(putc(((unsigned char)c),so)==EOF)
590 static int base_encode(char *filename, FILE *so)
592 unsigned char dtable[BASEMAXINLINE];
597 memset(&bio, 0, sizeof(bio));
598 bio.iocp = BASEMAXINLINE;
600 if ( !(fi = fopen(filename, "rb"))) {
601 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
609 dtable[26+i+9]= 'j'+i;
613 dtable[26+i+18]= 's'+i;
622 unsigned char igroup[3],ogroup[4];
625 igroup[0]= igroup[1]= igroup[2]= 0;
628 if ( (c = inchar(&bio, fi)) == EOF) {
633 igroup[n]= (unsigned char)c;
637 ogroup[0]= dtable[igroup[0]>>2];
638 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
639 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
640 ogroup[3]= dtable[igroup[2]&0x3F];
650 ochar(&bio, ogroup[i], so);
654 if(fputs(eol,so)==EOF)
662 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)
673 struct vm_zone *the_zone = NULL;
675 if (!strcmp(format, "wav49"))
677 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
678 p = popen(SENDMAIL, "w");
680 gethostname(host, sizeof(host));
681 if (strchr(srcemail, '@'))
682 strncpy(who, srcemail, sizeof(who)-1);
684 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
686 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
689 /* Does this user have a timezone specified? */
690 if (strlen(vmu->zonetag)) {
691 /* Find the zone in the list */
695 if (!strcmp(z->name, vmu->zonetag)) {
704 ast_localtime(&t,&tm,the_zone->timezone);
706 ast_localtime(&t,&tm,NULL);
707 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
708 fprintf(p, "Date: %s\n", date);
711 fprintf(p, "From: %s <%s>\n", fromstring, who);
713 fprintf(p, "From: Asterisk PBX <%s>\n", who);
714 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
718 fprintf(p, emailtitle, msgnum, mailbox) ;
723 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
725 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
726 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
727 fprintf(p, "MIME-Version: 1.0\n");
728 if (attach_user_voicemail) {
730 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
732 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
734 fprintf(p, "--%s\n", bound);
736 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
737 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
739 struct ast_channel *ast = ast_channel_alloc(0);
742 int vmlen = strlen(emailbody)*3 + 200;
743 if ((passdata = alloca(vmlen))) {
744 memset(passdata, 0, vmlen);
745 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
746 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
747 sprintf(passdata,"%d",msgnum);
748 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
749 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
750 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
751 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
752 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
753 fprintf(p, "%s\n",passdata);
754 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
755 ast_channel_free(ast);
756 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
758 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
760 "in mailbox %s from %s, on %s so you might\n"
761 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
762 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
764 if (attach_user_voicemail) {
765 fprintf(p, "--%s\n", bound);
766 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
767 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
768 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
769 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
771 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
772 base_encode(fname, p);
773 fprintf(p, "\n\n--%s--\n.\n", bound);
777 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
783 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
792 struct vm_zone *the_zone = NULL;
793 p = popen(SENDMAIL, "w");
796 gethostname(host, sizeof(host));
797 if (strchr(srcemail, '@'))
798 strncpy(who, srcemail, sizeof(who)-1);
800 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
802 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
805 /* Does this user have a timezone specified? */
806 if (strlen(vmu->zonetag)) {
807 /* Find the zone in the list */
811 if (!strcmp(z->name, vmu->zonetag)) {
820 ast_localtime(&t,&tm,the_zone->timezone);
822 ast_localtime(&t,&tm,NULL);
824 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
825 fprintf(p, "Date: %s\n", date);
826 fprintf(p, "From: Asterisk PBX <%s>\n", who);
827 fprintf(p, "To: %s\n", pager);
828 fprintf(p, "Subject: New VM\n\n");
829 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
830 fprintf(p, "New %s long msg in box %s\n"
831 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
834 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
840 static int get_date(char *s, int len)
846 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
849 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
853 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
854 if (ast_fileexists(fn, NULL, NULL) > 0) {
855 res = ast_streamfile(chan, fn, chan->language);
858 res = ast_waitstream(chan, ecodes);
862 res = ast_streamfile(chan, "vm-theperson", chan->language);
865 res = ast_waitstream(chan, ecodes);
868 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
873 res = ast_streamfile(chan, "vm-isonphone", chan->language);
875 res = ast_streamfile(chan, "vm-isunavail", chan->language);
878 res = ast_waitstream(chan, ecodes);
882 static int play_and_wait(struct ast_channel *chan, char *fn)
885 d = ast_streamfile(chan, fn, chan->language);
888 d = ast_waitstream(chan, AST_DIGIT_ANY);
889 ast_stopstream(chan);
893 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int beep)
897 int x, fmtcnt=1, res=-1,outmsg=0;
899 struct ast_filestream *others[MAX_OTHER_FORMATS];
900 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
901 char *sfmt[MAX_OTHER_FORMATS];
904 struct ast_dsp *sildet; /* silence detector dsp */
905 int totalsilence = 0;
907 int gotsilence = 0; /* did we timeout for silence? */
909 char prependfile[80];
911 ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
912 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
914 if (playfile || beep) {
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");
1052 /* delete all the prepend files */
1053 for (x=0;x<fmtcnt;x++) {
1056 ast_closestream(others[x]);
1057 ast_filedelete(prependfile, sfmt[x]);
1062 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1069 struct ast_frame *fr;
1070 for (x=0;x<fmtcnt;x++) {
1071 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1072 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1073 if (!others[x] || !realfiles[x])
1076 ast_stream_rewind(others[x], totalsilence-200);
1078 ast_stream_rewind(others[x], 200);
1079 ast_truncstream(others[x]);
1080 /* add the original file too */
1081 while ((fr = ast_readframe(realfiles[x]))) {
1082 ast_writestream(others[x],fr);
1084 ast_closestream(others[x]);
1085 ast_closestream(realfiles[x]);
1086 ast_filerename(prependfile, recordfile, sfmt[x]);
1088 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1090 ast_filedelete(prependfile, sfmt[x]);
1094 if (ast_set_read_format(chan, rfmt)) {
1095 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1100 /* Let them know it worked */
1101 ast_streamfile(chan, "vm-msgsaved", chan->language);
1102 ast_waitstream(chan, "");
1108 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
1112 int x, fmtcnt=1, res=-1,outmsg=0;
1113 struct ast_frame *f;
1114 struct ast_filestream *others[MAX_OTHER_FORMATS];
1115 char *sfmt[MAX_OTHER_FORMATS];
1118 struct ast_dsp *sildet; /* silence detector dsp */
1119 int totalsilence = 0;
1121 int gotsilence = 0; /* did we timeout for silence? */
1124 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1125 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1128 d = play_and_wait(chan, playfile);
1130 d = ast_streamfile(chan, "beep",chan->language);
1132 d = ast_waitstream(chan,"");
1137 fmts = ast_strdupa(fmt);
1140 strsep(&stringp, "|");
1141 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1142 sfmt[0] = ast_strdupa(fmts);
1144 while((fmt = strsep(&stringp, "|"))) {
1145 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1146 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1149 sfmt[fmtcnt++] = ast_strdupa(fmt);
1154 for (x=0;x<fmtcnt;x++) {
1155 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1156 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1163 sildet = ast_dsp_new(); //Create the silence detector
1165 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1168 ast_dsp_set_threshold(sildet, silencethreshold);
1170 if (maxsilence > 0) {
1171 rfmt = chan->readformat;
1172 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1174 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1180 /* Loop forever, writing the packets we read to the writer(s), until
1181 we read a # or get a hangup */
1184 res = ast_waitfor(chan, 2000);
1186 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1187 /* Try one more time in case of masq */
1188 res = ast_waitfor(chan, 2000);
1190 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1202 if (f->frametype == AST_FRAME_VOICE) {
1203 /* write each format */
1204 for (x=0;x<fmtcnt;x++) {
1205 res = ast_writestream(others[x], f);
1208 /* Silence Detection */
1209 if (maxsilence > 0) {
1211 ast_dsp_silence(sildet, f, &dspsilence);
1213 totalsilence = dspsilence;
1217 if (totalsilence > maxsilence) {
1218 /* Ended happily with silence */
1225 /* Exit on any error */
1227 ast_log(LOG_WARNING, "Error writing frame\n");
1231 } else if (f->frametype == AST_FRAME_VIDEO) {
1232 /* Write only once */
1233 ast_writestream(others[0], f);
1234 } else if (f->frametype == AST_FRAME_DTMF) {
1235 if (f->subclass == '#') {
1236 if (option_verbose > 2)
1237 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1246 if (maxtime < (end - start)) {
1247 if (option_verbose > 2)
1248 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1257 if (option_verbose > 2)
1258 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1263 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1266 for (x=0;x<fmtcnt;x++) {
1270 ast_stream_rewind(others[x], totalsilence-200);
1272 ast_stream_rewind(others[x], 200);
1273 ast_truncstream(others[x]);
1274 ast_closestream(others[x]);
1277 if (ast_set_read_format(chan, rfmt)) {
1278 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1283 /* Let them know it worked */
1284 ast_streamfile(chan, "vm-msgsaved", chan->language);
1285 ast_waitstream(chan, "");
1293 static void free_user(struct ast_vm_user *vmu)
1299 static void free_zone(struct vm_zone *z)
1304 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1314 char prefile[256]="";
1322 struct ast_vm_user *vmu;
1323 struct ast_vm_user svm;
1325 strncpy(tmp, ext, sizeof(tmp) - 1);
1327 context = strchr(tmp, '@');
1333 if ((vmu = find_user(&svm, context, ext))) {
1334 /* Setup pre-file if appropriate */
1336 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1338 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1339 make_dir(dir, sizeof(dir), vmu->context, "", "");
1340 /* It's easier just to try to make it than to check for its existence */
1341 if (mkdir(dir, 0700) && (errno != EEXIST))
1342 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1343 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1344 /* It's easier just to try to make it than to check for its existence */
1345 if (mkdir(dir, 0700) && (errno != EEXIST))
1346 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1347 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1348 if (mkdir(dir, 0700) && (errno != EEXIST))
1349 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1350 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1352 /* Play the beginning intro if desired */
1353 if (strlen(prefile)) {
1354 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1355 if (ast_streamfile(chan, prefile, chan->language) > -1)
1356 res = ast_waitstream(chan, "#0");
1358 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1359 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1362 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1368 /* On a '#' we skip the instructions */
1372 if (!res && !silent) {
1373 res = ast_streamfile(chan, INTRO, chan->language);
1375 res = ast_waitstream(chan, ecodes);
1381 /* Check for a '0' here */
1383 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1384 if (strlen(chan->macrocontext))
1385 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1391 /* Unless we're *really* silent, try to send the beep */
1392 res = ast_streamfile(chan, "beep", chan->language);
1394 res = ast_waitstream(chan, "");
1400 /* The meat of recording the message... All the announcements and beeps have been played*/
1401 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1405 make_file(fn, sizeof(fn), dir, msgnum);
1406 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1407 (chan->callerid ? chan->callerid : "Unknown"),
1408 vmu->fullname, ext, chan->name);
1409 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1412 } while(msgnum < MAXMSG);
1413 if (msgnum < MAXMSG) {
1414 /* Store information */
1415 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1416 txt = fopen(txtfile, "w+");
1418 get_date(date, sizeof(date));
1422 "; Message Information file\n"
1438 chan->callerid ? chan->callerid : "Unknown",
1439 date, (long)time(NULL));
1442 ast_log(LOG_WARNING, "Error opening text file for output\n");
1443 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1446 txt = fopen(txtfile, "a");
1449 fprintf(txt, "duration=%ld\n", (long)(end-start));
1453 strsep(&stringp, "|");
1454 /* Send e-mail if applicable */
1455 if (strlen(vmu->email)) {
1456 int attach_user_voicemail = attach_voicemail;
1457 char *myserveremail = serveremail;
1458 if (vmu->attach > -1)
1459 attach_user_voicemail = vmu->attach;
1460 if (strlen(vmu->serveremail))
1461 myserveremail = vmu->serveremail;
1462 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1464 if (strlen(vmu->pager)) {
1465 char *myserveremail = serveremail;
1466 if (strlen(vmu->serveremail))
1467 myserveremail = vmu->serveremail;
1468 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1471 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1473 res = ast_waitstream(chan, "");
1474 ast_log(LOG_WARNING, "No more messages possible\n");
1477 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1480 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1481 /*Send the call to n+101 priority, where n is the current priority*/
1482 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1483 chan->priority+=100;
1485 /* Leave voicemail for someone */
1486 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1490 static char *mbox(int id)
1518 static int count_messages(char *dir)
1522 for (x=0;x<MAXMSG;x++) {
1523 make_file(fn, sizeof(fn), dir, x);
1524 if (ast_fileexists(fn, NULL, NULL) < 1)
1530 static int say_and_wait(struct ast_channel *chan, int num)
1533 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1537 static int copy(char *infile, char *outfile)
1544 if ((ifd = open(infile, O_RDONLY)) < 0) {
1545 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1548 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1549 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1554 len = read(ifd, buf, sizeof(buf));
1556 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1562 res = write(ofd, buf, len);
1564 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1576 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1583 char *dbox = mbox(box);
1585 make_file(sfn, sizeof(sfn), dir, msg);
1586 make_dir(ddir, sizeof(ddir), context, username, dbox);
1588 for (x=0;x<MAXMSG;x++) {
1589 make_file(dfn, sizeof(dfn), ddir, x);
1590 if (ast_fileexists(dfn, NULL, NULL) < 0)
1595 ast_filecopy(sfn, dfn, NULL);
1596 if (strcmp(sfn, dfn)) {
1597 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1598 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1604 static int adsi_logo(unsigned char *buf)
1607 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1608 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1612 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1620 bytes += adsi_data_mode(buf + bytes);
1621 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1624 bytes += adsi_logo(buf);
1625 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1627 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1629 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1630 bytes += adsi_data_mode(buf + bytes);
1631 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1633 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1635 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1636 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1637 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1638 bytes += adsi_voice_mode(buf + bytes, 0);
1639 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1646 bytes += adsi_logo(buf);
1647 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1648 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1649 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1650 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1653 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1654 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1655 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1656 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1657 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1658 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1659 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1662 /* Add another dot */
1664 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1665 bytes += adsi_voice_mode(buf + bytes, 0);
1667 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1668 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1672 /* These buttons we load but don't use yet */
1673 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1674 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1675 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1676 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1677 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1678 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1679 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1682 /* Add another dot */
1684 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1685 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1686 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1691 snprintf(num, sizeof(num), "%d", x);
1692 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1694 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1695 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1698 /* Add another dot */
1700 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1701 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1702 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1705 if (adsi_end_download(chan)) {
1707 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1708 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1709 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1710 bytes += adsi_voice_mode(buf + bytes, 0);
1711 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1715 bytes += adsi_download_disconnect(buf + bytes);
1716 bytes += adsi_voice_mode(buf + bytes, 0);
1717 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1719 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1724 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1725 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1727 ast_log(LOG_DEBUG, "Restarting session...\n");
1730 /* Load the session now */
1731 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1733 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1735 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1737 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1741 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1744 if (!adsi_available(chan))
1746 x = adsi_load_session(chan, adapp, adver, 1);
1750 if (adsi_load_vmail(chan, useadsi)) {
1751 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1758 static void adsi_login(struct ast_channel *chan)
1762 unsigned char keys[8];
1764 if (!adsi_available(chan))
1769 /* Set one key for next */
1770 keys[3] = ADSI_KEY_APPS + 3;
1772 bytes += adsi_logo(buf + bytes);
1773 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1774 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1775 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1776 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1777 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1778 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1779 bytes += adsi_set_keys(buf + bytes, keys);
1780 bytes += adsi_voice_mode(buf + bytes, 0);
1781 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1784 static void adsi_password(struct ast_channel *chan)
1788 unsigned char keys[8];
1790 if (!adsi_available(chan))
1795 /* Set one key for next */
1796 keys[3] = ADSI_KEY_APPS + 3;
1798 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1799 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1800 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1801 bytes += adsi_set_keys(buf + bytes, keys);
1802 bytes += adsi_voice_mode(buf + bytes, 0);
1803 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1806 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1810 unsigned char keys[8];
1813 if (!adsi_available(chan))
1817 y = ADSI_KEY_APPS + 12 + start + x;
1818 if (y > ADSI_KEY_APPS + 12 + 4)
1820 keys[x] = ADSI_KEY_SKT | y;
1822 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1826 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1827 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1828 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1829 bytes += adsi_set_keys(buf + bytes, keys);
1830 bytes += adsi_voice_mode(buf + bytes, 0);
1832 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1835 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1838 char buf[256], buf1[256], buf2[256];
1844 char datetime[21]="";
1847 unsigned char keys[8];
1851 if (!adsi_available(chan))
1854 /* Retrieve important info */
1855 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1856 f = fopen(fn2, "r");
1859 fgets(buf, sizeof(buf), f);
1863 strsep(&stringp, "=");
1864 val = strsep(&stringp, "=");
1865 if (val && strlen(val)) {
1866 if (!strcmp(buf, "callerid"))
1867 strncpy(cid, val, sizeof(cid) - 1);
1868 if (!strcmp(buf, "origdate"))
1869 strncpy(datetime, val, sizeof(datetime) - 1);
1875 /* New meaning for keys */
1877 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1882 /* No prev key, provide "Folder" instead */
1883 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1886 /* If last message ... */
1888 /* but not only message, provide "Folder" instead */
1889 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1890 bytes += adsi_voice_mode(buf + bytes, 0);
1893 /* Otherwise if only message, leave blank */
1899 ast_callerid_parse(cid, &name, &num);
1903 name = "Unknown Caller";
1905 /* If deleted, show "undeleted" */
1908 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1911 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1912 snprintf(buf1, sizeof(buf1), "%s%s", folder,
1913 strcasecmp(folder, "INBOX") ? " Messages" : "");
1914 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1916 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1917 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1918 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1919 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1920 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1921 bytes += adsi_set_keys(buf + bytes, keys);
1922 bytes += adsi_voice_mode(buf + bytes, 0);
1924 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1927 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1931 unsigned char keys[8];
1935 if (!adsi_available(chan))
1938 /* New meaning for keys */
1940 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1946 /* No prev key, provide "Folder" instead */
1947 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1950 /* If last message ... */
1952 /* but not only message, provide "Folder" instead */
1953 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1955 /* Otherwise if only message, leave blank */
1960 /* If deleted, show "undeleted" */
1962 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1965 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1966 bytes += adsi_set_keys(buf + bytes, keys);
1967 bytes += adsi_voice_mode(buf + bytes, 0);
1969 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1972 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1974 char buf[256], buf1[256], buf2[256];
1976 unsigned char keys[8];
1979 char *newm = (new == 1) ? "message" : "messages";
1980 char *oldm = (old == 1) ? "message" : "messages";
1981 if (!adsi_available(chan))
1984 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1986 strcat(buf1, " and");
1987 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1989 snprintf(buf2, sizeof(buf2), "%s.", newm);
1992 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1993 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1995 strcpy(buf1, "You have no messages.");
1998 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1999 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2000 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2003 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2007 /* Don't let them listen if there are none */
2010 bytes += adsi_set_keys(buf + bytes, keys);
2012 bytes += adsi_voice_mode(buf + bytes, 0);
2014 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2017 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2019 char buf[256], buf1[256], buf2[256];
2021 unsigned char keys[8];
2024 char *mess = (messages == 1) ? "message" : "messages";
2026 if (!adsi_available(chan))
2029 /* Original command keys */
2031 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2039 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2040 strcasecmp(folder, "INBOX") ? " folder" : "");
2043 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2045 strcpy(buf2, "no messages.");
2046 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2047 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2048 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2049 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2050 bytes += adsi_set_keys(buf + bytes, keys);
2052 bytes += adsi_voice_mode(buf + bytes, 0);
2054 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2058 static void adsi_clear(struct ast_channel *chan)
2062 if (!adsi_available(chan))
2064 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2065 bytes += adsi_voice_mode(buf + bytes, 0);
2067 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2070 static void adsi_goodbye(struct ast_channel *chan)
2075 if (!adsi_available(chan))
2077 bytes += adsi_logo(buf + bytes);
2078 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2079 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2080 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2081 bytes += adsi_voice_mode(buf + bytes, 0);
2083 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2086 static int get_folder(struct ast_channel *chan, int start)
2091 d = play_and_wait(chan, "vm-press");
2094 for (x = start; x< 5; x++) {
2095 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
2097 d = play_and_wait(chan, "vm-for");
2100 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2101 d = play_and_wait(chan, fn);
2104 d = play_and_wait(chan, "vm-messages");
2107 d = ast_waitfordigit(chan, 500);
2111 d = play_and_wait(chan, "vm-tocancel");
2114 d = ast_waitfordigit(chan, 4000);
2118 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2121 res = play_and_wait(chan, fn);
2122 while (((res < '0') || (res > '9')) &&
2123 (res != '#') && (res >= 0)) {
2124 res = get_folder(chan, 0);
2129 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2134 while((cmd >= 0) && (cmd != 't') && (cmd != '#')) {
2139 /* prepend a message to the current message and return */
2142 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2143 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, 1);
2153 cmd = play_and_wait(chan,"vm-forwardoptions");
2155 cmd = ast_waitfordigit(chan,6000);
2167 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2174 struct ast_config *mif;
2178 int res = 0, cmd = 0;
2179 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2182 int saved_messages = 0, found = 0;
2183 int valid_extensions = 0;
2184 while (!res && !valid_extensions) {
2185 res = ast_streamfile(chan, "vm-extension", chan->language);
2188 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2190 /* start all over if no username */
2191 if (!strlen(username))
2194 s = strsep(&stringp, "*");
2195 /* start optimistic */
2196 valid_extensions = 1;
2198 /* find_user is going to malloc since we have a NULL as first argument */
2199 if ((receiver = find_user(NULL, context, s))) {
2201 vmtmp = extensions = receiver;
2203 vmtmp->next = receiver;
2208 valid_extensions = 0;
2211 s = strsep(&stringp, "*");
2213 /* break from the loop of reading the extensions */
2214 if (valid_extensions)
2216 /* invalid extension, try again */
2217 res = play_and_wait(chan, "pbx-invalid");
2219 /* check if we're clear to proceed */
2220 if (!extensions || !valid_extensions)
2223 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2225 while(!res && vmtmp) {
2226 /* if (play_and_wait(chan, "vm-savedto"))
2229 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2230 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2231 ast_log(LOG_DEBUG, sys);
2234 todircount = count_messages(todir);
2235 strncpy(tmp, fmt, sizeof(tmp));
2237 while((s = strsep(&stringp, "|"))) {
2238 /* XXX This is a hack -- we should use build_filename or similar XXX */
2239 if (!strcasecmp(s, "wav49"))
2241 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2242 ast_log(LOG_DEBUG, sys);
2245 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2246 ast_log(LOG_DEBUG, sys);
2248 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2250 /* load the information on the source message so we can send an e-mail like a new message */
2251 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2252 if ((mif=ast_load(miffile))) {
2254 /* set callerid and duration variables */
2255 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2256 s = ast_variable_retrieve(mif, NULL, "duration");
2261 if (strlen(vmtmp->email)) {
2262 int attach_user_voicemail = attach_voicemail;
2263 char *myserveremail = serveremail;
2264 if (vmtmp->attach > -1)
2265 attach_user_voicemail = vmtmp->attach;
2266 if (strlen(vmtmp->serveremail))
2267 myserveremail = vmtmp->serveremail;
2268 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2271 if (strlen(vmtmp->pager)) {
2272 char *myserveremail = serveremail;
2273 if (strlen(vmtmp->serveremail))
2274 myserveremail = vmtmp->serveremail;
2275 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2278 ast_destroy(mif); /* or here */
2280 /* Leave voicemail for someone */
2281 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2285 vmtmp = vmtmp->next;
2288 if (saved_messages > 0) {
2289 /* give confirmatopm that the message was saved */
2290 if (saved_messages == 1)
2291 res = play_and_wait(chan, "vm-message");
2293 res = play_and_wait(chan, "vm-messages");
2295 res = play_and_wait(chan, "vm-saved");
2297 return res ? res : cmd;
2301 static int wait_file2(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(chan, AST_DIGIT_ANY);
2311 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2314 if ((res = ast_streamfile(chan, file, chan->language)))
2315 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2317 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2321 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2324 char filename[256], *origtime;
2325 struct vm_zone *the_zone = NULL;
2326 struct ast_config *msg_cfg;
2330 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2331 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2332 msg_cfg = ast_load(filename);
2334 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2338 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2340 if (sscanf(origtime,"%ld",&tin) < 1) {
2341 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2345 ast_destroy(msg_cfg);
2347 /* Does this user have a timezone specified? */
2348 if (strlen(vmu->zonetag)) {
2349 /* Find the zone in the list */
2353 if (!strcmp(z->name, vmu->zonetag)) {
2361 /* No internal variable parsing for now, so we'll comment it out for the time being */
2363 /* Set the DIFF_* variables */
2364 localtime_r(&t, &time_now);
2365 gettimeofday(&tv_now,NULL);
2366 tnow = tv_now.tv_sec;
2367 localtime_r(&tnow,&time_then);
2369 /* Day difference */
2370 if (time_now.tm_year == time_then.tm_year)
2371 sprintf(temp,"%d",time_now.tm_yday);
2373 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2374 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2376 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2379 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2381 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2383 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2388 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2392 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2393 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2395 res = wait_file2(chan, vms, "vm-first");
2396 else if (msg == vms->lastmsg)
2397 res = wait_file2(chan, vms, "vm-last");
2399 res = wait_file2(chan, vms, "vm-message");
2400 if (msg && (msg != vms->lastmsg)) {
2402 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2407 res = play_message_datetime(chan,vmu,vms);
2410 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2411 vms->heard[msg] = 1;
2412 res = wait_file(chan, vms, vms->fn);
2417 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2419 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2420 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2421 vms->lastmsg = count_messages(vms->curdir) - 1;
2422 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2425 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2428 char ntxt[256] = "";
2430 if (vms->lastmsg > -1) {
2431 /* Get the deleted messages fixed */
2433 for (x=0;x < MAXMSG;x++) {
2434 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2435 /* Save this message. It's not in INBOX or hasn't been heard */
2436 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2437 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2440 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2441 if (strcmp(vms->fn, vms->fn2)) {
2442 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2443 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2444 ast_filerename(vms->fn, vms->fn2, NULL);
2447 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2448 /* Move to old folder before deleting */
2449 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2452 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2453 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2454 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2456 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2457 ast_filedelete(vms->fn, NULL);
2461 memset(vms->deleted, 0, sizeof(vms->deleted));
2462 memset(vms->heard, 0, sizeof(vms->heard));
2465 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2467 /* Introduce messages they have */
2469 res = play_and_wait(chan, "vm-youhave");
2471 if (vms->newmessages) {
2472 res = say_and_wait(chan, vms->newmessages);
2474 res = play_and_wait(chan, "vm-INBOX");
2475 if (vms->oldmessages && !res)
2476 res = play_and_wait(chan, "vm-and");
2478 if ((vms->newmessages == 1))
2479 res = play_and_wait(chan, "vm-message");
2481 res = play_and_wait(chan, "vm-messages");
2485 if (!res && vms->oldmessages) {
2486 res = say_and_wait(chan, vms->oldmessages);
2488 res = play_and_wait(chan, "vm-Old");
2490 if (vms->oldmessages == 1)
2491 res = play_and_wait(chan, "vm-message");
2493 res = play_and_wait(chan, "vm-messages");
2497 if (!vms->oldmessages && !vms->newmessages) {
2498 res = play_and_wait(chan, "vm-no");
2500 res = play_and_wait(chan, "vm-messages");
2507 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2510 /* Play instructions and wait for new command */
2512 if (vms->starting) {
2513 if (vms->lastmsg > -1) {
2514 res = play_and_wait(chan, "vm-onefor");
2516 res = play_and_wait(chan, vms->vmbox);
2518 res = play_and_wait(chan, "vm-messages");
2521 res = play_and_wait(chan, "vm-opts");
2524 res = play_and_wait(chan, "vm-prev");
2526 res = play_and_wait(chan, "vm-repeat");
2527 if (!res && (vms->curmsg != vms->lastmsg))
2528 res = play_and_wait(chan, "vm-next");
2530 if (!vms->deleted[vms->curmsg])
2531 res = play_and_wait(chan, "vm-delete");
2533 res = play_and_wait(chan, "vm-undelete");
2535 res = play_and_wait(chan, "vm-toforward");
2537 res = play_and_wait(chan, "vm-savemessage");
2541 res = play_and_wait(chan, "vm-helpexit");
2543 res = ast_waitfordigit(chan, 6000);
2546 if (vms->repeats > 2) {
2547 res = play_and_wait(chan, "vm-goodbye");
2556 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2560 char newpassword[80] = "";
2561 char newpassword2[80] = "";
2562 char prefile[256]="";
2566 if (adsi_available(chan))
2568 bytes += adsi_logo(buf + bytes);
2569 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2570 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2571 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2572 bytes += adsi_voice_mode(buf + bytes, 0);
2573 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2575 while((cmd >= 0) && (cmd != 't')) {
2580 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2581 cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2584 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2585 cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2588 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2589 cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2592 newpassword[1] = '\0';
2593 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2596 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2599 newpassword2[1] = '\0';
2600 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2604 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2607 if (strcmp(newpassword, newpassword2)) {
2608 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2609 cmd = play_and_wait(chan, "vm-mismatch");
2612 vm_change_password(vmu,newpassword);
2613 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2614 cmd = play_and_wait(chan,"vm-passchanged");
2620 cmd = play_and_wait(chan,"vm-options");
2622 cmd = ast_waitfordigit(chan,6000);
2634 static int vm_execmain(struct ast_channel *chan, void *data)
2636 /* XXX This is, admittedly, some pretty horrendus code. For some
2637 reason it just seemed a lot easier to do with GOTO's. I feel
2638 like I'm back in my GWBASIC days. XXX */
2643 struct localuser *u;
2644 char prefixstr[80] ="";
2645 char empty[80] = "";
2649 char tmp[256], *ext;
2650 char fmtc[256] = "";
2652 struct vm_state vms;
2654 struct ast_vm_user *vmu = NULL, vmus;
2658 memset(&vms, 0, sizeof(vms));
2659 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2660 if (chan->_state != AST_STATE_UP)
2663 if (data && strlen(data)) {
2664 strncpy(tmp, data, sizeof(tmp) - 1);
2669 /* We should skip the user's password */
2674 /* We should prefix the mailbox with the supplied data */
2680 context = strchr(ext, '@');
2687 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2689 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2690 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2697 /* If ADSI is supported, setup login screen */
2698 adsi_begin(chan, &useadsi);
2699 if (!skipuser && useadsi)
2701 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2702 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2706 /* Authenticate them and get their mailbox/password */
2708 while (!valid && (logretries < maxlogins)) {
2709 /* Prompt for, and read in the username */
2710 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2711 ast_log(LOG_WARNING, "Couldn't read username\n");
2714 if (!strlen(vms.username)) {
2715 if (option_verbose > 2)
2716 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2721 adsi_password(chan);
2722 if (ast_streamfile(chan, "vm-password", chan->language)) {
2723 ast_log(LOG_WARNING, "Unable to stream password file\n");
2726 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2727 ast_log(LOG_WARNING, "Unable to read password\n");
2731 char fullusername[80] = "";
2732 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2733 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2734 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2737 vmu = find_user(&vmus, context, vms.username);
2738 if (vmu && !strcmp(vmu->password, password))
2741 if (option_verbose > 2)
2742 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2744 strncpy(vms.username, empty, sizeof(vms.username) -1);
2749 if (ast_streamfile(chan, "vm-incorrect", chan->language))
2754 if (!valid && (logretries >= maxlogins)) {
2755 ast_stopstream(chan);
2756 res = play_and_wait(chan, "vm-goodbye");
2762 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2763 mkdir(vms.curdir, 0700);
2764 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2765 mkdir(vms.curdir, 0700);
2766 /* Retrieve old and new message counts */
2767 open_mailbox(&vms, vmu, 1);
2768 vms.oldmessages = vms.lastmsg + 1;
2769 /* Start in INBOX */
2770 open_mailbox(&vms, vmu, 0);
2771 vms.newmessages = vms.lastmsg + 1;
2774 /* Select proper mailbox FIRST!! */
2775 if (!vms.newmessages && vms.oldmessages) {
2776 /* If we only have old messages start here */
2777 open_mailbox(&vms, vmu, 1);
2781 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2783 cmd = vm_intro(chan, &vms);
2786 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2793 if (vms.lastmsg > -1) {
2794 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2796 cmd = play_and_wait(chan, "vm-youhave");
2798 cmd = play_and_wait(chan, "vm-no");
2800 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2801 cmd = play_and_wait(chan, vms.fn);
2804 cmd = play_and_wait(chan, "vm-messages");
2807 case '2': /* Change folders */
2809 adsi_folders(chan, 0, "Change to folder...");
2810 cmd = get_folder2(chan, "vm-changeto", 0);
2813 } else if (cmd > 0) {
2815 close_mailbox(&vms, vmu);
2816 open_mailbox(&vms, vmu, cmd);
2820 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2822 cmd = play_and_wait(chan, vms.vmbox);
2824 cmd = play_and_wait(chan, "vm-messages");
2830 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2832 cmd = play_and_wait(chan, "vm-nomore");
2836 if (vms.curmsg < vms.lastmsg) {
2838 cmd = play_message(chan, vmu, &vms, vms.curmsg);
2840 cmd = play_and_wait(chan, "vm-nomore");
2844 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2846 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2847 if (vms.deleted[vms.curmsg])
2848 cmd = play_and_wait(chan, "vm-deleted");
2850 cmd = play_and_wait(chan, "vm-undeleted");
2853 if(vms.lastmsg > -1)
2854 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2856 cmd = play_and_wait(chan, "vm-nomore");
2860 adsi_folders(chan, 1, "Save to folder...");
2861 cmd = get_folder2(chan, "vm-savefolder", 1);
2862 box = 0; /* Shut up compiler */
2866 } else if (cmd > 0) {
2867 box = cmd = cmd - '0';
2868 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2869 vms.deleted[vms.curmsg]=1;
2871 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2873 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2875 cmd = play_and_wait(chan, "vm-message");
2877 cmd = say_and_wait(chan, vms.curmsg + 1);
2879 cmd = play_and_wait(chan, "vm-savedto");
2881 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2882 cmd = play_and_wait(chan, vms.fn);
2885 cmd = play_and_wait(chan, "vm-messages");
2888 if (!vms.starting) {
2889 cmd = play_and_wait(chan, "vm-onefor");
2891 cmd = play_and_wait(chan, vms.vmbox);
2893 cmd = play_and_wait(chan, "vm-messages");
2895 cmd = play_and_wait(chan, "vm-opts");
2900 cmd = vm_options(chan, vmu, &vms, vmfmts);
2902 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2904 default: /* Nothing */
2905 cmd = vm_instructions(chan, &vms);
2909 if ((cmd == 't') || (cmd == '#')) {
2919 ast_stopstream(chan);
2922 res = play_and_wait(chan, "vm-goodbye");
2927 adsi_unload_session(chan);
2930 close_mailbox(&vms, vmu);
2934 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2936 LOCAL_USER_REMOVE(u);
2941 static int vm_exec(struct ast_channel *chan, void *data)
2943 int res=0, silent=0, busy=0, unavail=0;
2944 struct localuser *u;
2945 char tmp[256], *ext;
2948 if (chan->_state != AST_STATE_UP)
2950 if (data && strlen(data))
2951 strncpy(tmp, data, sizeof(tmp) - 1);
2953 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2964 } else if (*ext == 'b') {
2967 } else if (*ext == 'u') {
2973 res = leave_voicemail(chan, ext, silent, busy, unavail);
2974 LOCAL_USER_REMOVE(u);
2978 static int append_mailbox(char *context, char *mbox, char *data)
2980 /* Assumes lock is already held */
2984 struct ast_vm_user *vmu;
2985 strncpy(tmp, data, sizeof(tmp));
2986 vmu = malloc(sizeof(struct ast_vm_user));
2988 memset(vmu, 0, sizeof(struct ast_vm_user));
2989 strncpy(vmu->context, context, sizeof(vmu->context));
2990 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2993 if ((s = strsep(&stringp, ",")))
2994 strncpy(vmu->password, s, sizeof(vmu->password));
2995 if (stringp && (s = strsep(&stringp, ",")))
2996 strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2997 if (stringp && (s = strsep(&stringp, ",")))
2998 strncpy(vmu->email, s, sizeof(vmu->email));
2999 if (stringp && (s = strsep(&stringp, ",")))
3000 strncpy(vmu->pager, s, sizeof(vmu->pager));
3001 if (stringp && (s = strsep(&stringp, ",")))
3002 apply_options(vmu, s);
3013 /* XXX TL Bug 690 */
3014 static char show_voicemail_users_help[] =
3015 "Usage: show voicemail users [for <context>]\n"
3016 " Lists all mailboxes currently set up\n";
3018 static char show_voicemail_zones_help[] =
3019 "Usage: show voicemail zones\n"
3020 " Lists zone message formats\n";
3022 static int handle_show_voicemail_users(int fd, int argc, char *argv[])
3024 struct ast_vm_user *vmu = users;
3025 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
3027 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
3028 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
3032 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
3036 if (!strcmp(argv[4],vmu->context))
3042 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
3044 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
3045 return RESULT_FAILURE;
3051 struct dirent *vment;
3055 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
3056 make_dir(dirname, 255, vmu->context, vmu->mailbox, "INBOX");
3057 if ((vmdir = opendir(dirname))) {
3058 /* No matter what the format of VM, there will always be a .txt file for each message. */
3059 while ((vment = readdir(vmdir)))
3060 if (!strncmp(vment->d_name + 7,".txt",4))
3064 snprintf(count,11,"%d",vmcount);
3065 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
3070 ast_cli(fd, "There are no voicemail users currently defined\n");
3071 return RESULT_FAILURE;
3073 return RESULT_SUCCESS;
3076 static int handle_show_voicemail_zones(int fd, int argc, char *argv[])
3078 struct vm_zone *zone = zones;
3079 char *output_format = "%-15s %-20s %-45s\n";
3081 if (argc != 3) return RESULT_SHOWUSAGE;
3084 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
3086 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
3090 ast_cli(fd, "There are no voicemail zones currently defined\n");
3091 return RESULT_FAILURE;
3093 return RESULT_SUCCESS;
3096 static char *complete_show_voicemail_users(char *line, char *word, int pos, int state)
3099 struct ast_vm_user *vmu = users;
3102 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
3107 return strdup("for");
3112 if (!strncasecmp(word, vmu->context, strlen(word))) {
3113 if (context && strcmp(context, vmu->context)) {
3114 if (++which > state) {
3115 return strdup(vmu->context);
3117 context = vmu->context;
3125 static struct ast_cli_entry show_voicemail_users_cli =
3126 { { "show", "voicemail", "users", NULL },
3127 handle_show_voicemail_users, "List defined voicemail boxes",
3128 show_voicemail_users_help, complete_show_voicemail_users };
3130 static struct ast_cli_entry show_voicemail_zones_cli =
3131 { { "show", "voicemail", "zones", NULL },
3132 handle_show_voicemail_zones, "List zone message formats",
3133 show_voicemail_zones_help, NULL };
3136 static int load_config(void)
3138 struct ast_vm_user *cur, *l;
3139 struct vm_zone *zcur, *zl;
3140 struct ast_config *cfg;
3142 struct ast_variable *var;
3151 cfg = ast_load(VOICEMAIL_CONFIG);
3152 ast_mutex_lock(&vmlock);
3170 /* General settings */
3171 attach_voicemail = 1;
3172 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
3174 attach_voicemail = ast_true(astattach);
3176 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
3177 maxsilence = atoi(silencestr);
3182 silencethreshold = 256;
3183 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
3184 silencethreshold = atoi(thresholdstr);
3186 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
3187 astemail = ASTERISK_USERNAME;
3188 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
3191 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
3192 if (sscanf(s, "%d", &x) == 1) {
3195 ast_log(LOG_WARNING, "Invalid max message time length\n");
3198 fmt = ast_variable_retrieve(cfg, "general", "format");
3201 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
3204 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
3205 if (sscanf(s, "%d", &x) == 1) {
3208 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
3212 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
3213 if (sscanf(s, "%d", &x) == 1) {
3216 ast_log(LOG_WARNING, "Invalid skipms value\n");
3221 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
3222 if (sscanf(s, "%d", &x) == 1) {
3225 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
3230 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
3231 strcpy(dbuser, "test");
3235 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
3236 strcpy(dbpass, "test");
3240 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
3245 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
3246 strcpy(dbname, "vmdb");
3252 #ifdef USEPOSTGRESVM
3253 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
3254 strcpy(dboption, "dboption not-specified in voicemail.conf");
3256 strcpy(dboption, s);
3259 cat = ast_category_browse(cfg, NULL);
3261 if (strcasecmp(cat, "general")) {
3262 var = ast_variable_browse(cfg, cat);
3263 if (strcasecmp(cat, "zonemessages")) {
3265 /* Process mailboxes in this context */
3267 append_mailbox(cat, var->name, var->value);
3272 /* Timezones in this context */
3275 z = malloc(sizeof(struct vm_zone));
3277 char *msg_format, *timezone;
3278 msg_format = ast_strdupa(var->value);
3279 if (msg_format != NULL) {
3280 timezone = strsep(&msg_format, "|");
3281 strncpy(z->name, var->name, sizeof(z->name) - 1);
3282 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
3283 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
3293 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3298 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3305 cat = ast_category_browse(cfg, cat);
3307 memset(fromstring,0,sizeof(fromstring));
3308 memset(emailtitle,0,sizeof(emailtitle));
3313 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
3314 pbxskip = ast_true(s);
3315 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
3316 strncpy(fromstring,s,sizeof(fromstring)-1);
3317 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
3318 strncpy(emailtitle,s,sizeof(emailtitle)-1);
3319 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
3320 char *tmpread, *tmpwrite;
3321 emailbody = strdup(s);