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 /* Default mail command to mail voicemail. Change it with the
71 mailcmd= command in voicemail.conf */
72 #define SENDMAIL "/usr/sbin/sendmail -t"
74 #define INTRO "vm-intro"
77 #define MAX_OTHER_FORMATS 10
79 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
81 #define BASEMAXINLINE 256
82 #define BASELINELEN 72
83 #define BASEMAXINLINE 256
86 #define MAX_DATETIME_FORMAT 512
87 #define MAX_NUM_CID_CONTEXTS 10
89 #define DIGITS_DIR AST_SOUNDS "/digits/"
97 unsigned char iobuf[BASEMAXINLINE];
100 /* Structure for linked list of users */
108 char serveremail[80];
109 char mailcmd[160]; /* Configurable mail command */
118 struct ast_vm_user *next;
124 char msg_format[512];
125 struct vm_zone *next;
144 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
145 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
146 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration);
150 static char *tdesc = "Comedian Mail (Voicemail System)";
152 static char *adapp = "CoMa";
154 static char *adsec = "_AST";
156 static char *addesc = "Comedian Mail";
158 static int adver = 1;
160 static char *synopsis_vm =
161 "Leave a voicemail message";
163 static char *descrip_vm =
164 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
165 "extension (must be configured in voicemail.conf).\n"
166 " If the extension is preceded by \n"
167 "* 's' then instructions for leaving the message will be skipped.\n"
168 "* 'u' then the \"unavailable\" message will be played.\n"
169 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
170 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
171 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
172 "priority 'o' in the current context.\n"
173 "If the requested mailbox does not exist, and there exists a priority\n"
174 "n + 101, then that priority will be taken next.\n"
175 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
176 "Otherwise, it returns 0.\n";
178 static char *synopsis_vmain =
179 "Enter voicemail system";
181 static char *descrip_vmain =
182 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
183 "for the checking of voicemail. The mailbox can be passed as the option,\n"
184 "which will stop the voicemail system from prompting the user for the mailbox.\n"
185 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
186 "a context is specified, logins are considered in that voicemail context only.\n"
187 "Returns -1 if the user hangs up or 0 otherwise.\n";
189 static char *synopsis_vm_box_exists =
190 "Check if vmbox exists";
192 static char *descrip_vm_box_exists =
193 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
194 "if the specified voice mailbox exists.\n";
197 /* Leave a message */
198 static char *capp = "VoiceMail2";
199 static char *app = "VoiceMail";
201 /* Check mail, control, etc */
202 static char *capp2 = "VoiceMailMain2";
203 static char *app2 = "VoiceMailMain";
205 static char *app3 = "MailboxExists";
207 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
208 struct ast_vm_user *users;
209 struct ast_vm_user *usersl;
210 struct vm_zone *zones = NULL;
211 struct vm_zone *zonesl = NULL;
212 static int attach_voicemail;
213 static int maxsilence;
214 static int silencethreshold = 128;
215 static char serveremail[80];
216 static char mailcmd[160]; /* Configurable mail cmd */
217 static char externnotify[160];
219 static char vmfmts[80];
220 static int vmminmessage;
221 static int vmmaxmessage;
224 static int maxlogins;
228 static int saycidinfo;
229 static char dialcontext[80];
230 static char callcontext[80];
232 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
235 static char *emailbody = NULL;
236 static int pbxskip = 0;
237 static char fromstring[100];
238 static char emailtitle[100];
245 static void populate_defaults(struct ast_vm_user *vmu)
255 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
257 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
260 static void apply_options(struct ast_vm_user *vmu, char *options)
262 /* Destructively Parse options and apply */
263 char *stringp = ast_strdupa(options);
267 while((s = strsep(&stringp, "|"))) {
269 if ((var = strsep(&value, "=")) && value) {
270 if (!strcasecmp(var, "attach")) {
275 } else if (!strcasecmp(var, "serveremail")) {
276 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
277 } else if (!strcasecmp(var, "tz")) {
278 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
279 } else if (!strcasecmp(var, "saycid")){
284 } else if (!strcasecmp(var, "review")){
289 } else if (!strcasecmp(var, "operator")){
294 } else if (!strcasecmp(var, "callback")) {
295 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
296 } else if (!strcasecmp(var, "dialout")) {
297 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
306 #include "mysql-vm-routines.h"
313 ast_mutex_t postgreslock;
315 static int sql_init(void)
317 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
318 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
320 dbhandler=PQconnectdb(dboption);
321 if (PQstatus(dbhandler) == CONNECTION_BAD) {
322 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
325 ast_mutex_init(&postgreslock);
327 /* fprintf(stderr,"postgres login OK\n"); */
331 static void sql_close(void)
337 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
345 char options[160] = "";
346 struct ast_vm_user *retval;
348 retval=malloc(sizeof(struct ast_vm_user));
350 /* fprintf(stderr,"postgres find_user:\n"); */
353 memset(retval, 0, sizeof(struct ast_vm_user));
356 strcpy(retval->mailbox, mailbox);
359 strcpy(retval->context, context);
363 strcpy(retval->context, "default");
365 populate_defaults(retval);
366 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
368 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
369 ast_mutex_lock(&postgreslock);
370 PGSQLres=PQexec(dbhandler,query);
371 if (PGSQLres!=NULL) {
372 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
373 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
374 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
376 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
379 ast_mutex_unlock(&postgreslock);
383 numFields = PQnfields(PGSQLres);
384 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
385 if (PQntuples(PGSQLres) != 1) {
386 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
388 ast_mutex_unlock(&postgreslock);
392 for (i=0; i<numFields; i++) {
393 fname = PQfname(PGSQLres,i);
394 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
395 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
396 } else if (!strcmp(fname, "fullname")) {
397 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
398 } else if (!strcmp(fname, "email")) {
399 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
400 } else if (!strcmp(fname, "pager")) {
401 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
402 } else if (!strcmp(fname, "options")) {
403 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
404 apply_options(retval, options);
409 ast_mutex_unlock(&postgreslock);
413 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
414 ast_mutex_unlock(&postgreslock);
419 } /* malloc() retval */
424 static void vm_change_password(struct ast_vm_user *vmu, char *password)
429 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);
431 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
433 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
434 ast_mutex_lock(&postgreslock);
435 PQexec(dbhandler, query);
436 strcpy(vmu->password, password);
437 ast_mutex_unlock(&postgreslock);
440 static void reset_user_pw(char *context, char *mailbox, char *password)
445 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
447 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
449 ast_mutex_lock(&postgreslock);
450 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
451 PQexec(dbhandler, query);
452 ast_mutex_unlock(&postgreslock);
455 #endif /* Postgres */
459 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
461 /* This function could be made to generate one from a database, too */
462 struct ast_vm_user *vmu=NULL, *cur;
463 ast_mutex_lock(&vmlock);
466 if ((!context || !strcasecmp(context, cur->context)) &&
467 (!strcasecmp(mailbox, cur->mailbox)))
475 /* Make a copy, so that on a reload, we have no race */
476 vmu = malloc(sizeof(struct ast_vm_user));
478 memcpy(vmu, cur, sizeof(struct ast_vm_user));
486 ast_mutex_unlock(&vmlock);
490 static int reset_user_pw(char *context, char *mailbox, char *newpass)
492 /* This function could be made to generate one from a database, too */
493 struct ast_vm_user *cur;
495 ast_mutex_lock(&vmlock);
498 if ((!context || !strcasecmp(context, cur->context)) &&
499 (!strcasecmp(mailbox, cur->mailbox)))
504 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
507 ast_mutex_unlock(&vmlock);
511 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
513 /* There's probably a better way of doing this. */
514 /* That's why I've put the password change in a separate function. */
515 /* This could also be done with a database function */
522 char currcontext[256] ="";
523 char tmpin[AST_CONFIG_MAX_PATH];
524 char tmpout[AST_CONFIG_MAX_PATH];
525 char *user, *pass, *rest, *trim, *tempcontext;
527 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
528 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
529 configin = fopen((char *)tmpin,"r");
531 configout = fopen((char *)tmpout,"w+");
534 if(!configin || !configout) {
538 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
542 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
546 while (!feof(configin)) {
547 /* Read in the line */
548 fgets(inbuf, sizeof(inbuf), configin);
550 if (!feof(configin)) {
551 /* Make a backup of it */
552 memcpy(orig, inbuf, sizeof(orig));
553 /* Strip trailing \n and comment */
554 inbuf[strlen(inbuf) - 1] = '\0';
555 user = strchr(inbuf, ';');
561 /* check for '[' (opening of context name ) */
562 tempcontext = strchr(user, '[');
564 strncpy(currcontext, tempcontext +1,
565 sizeof(currcontext) - 1);
566 /* now check for ']' */
567 tempcontext = strchr(currcontext, ']');
571 currcontext[0] = '\0';
573 pass = strchr(user, '=');
576 while(*trim && *trim < 33) {
586 while(*pass && *pass < 33)
590 rest = strchr(pass,',');
598 /* Compare user, pass AND context */
599 if (user && *user && !strcmp(user, vmu->mailbox) &&
600 pass && *pass && !strcmp(pass, vmu->password) &&
601 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
602 /* This is the line */
604 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
606 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
609 /* Put it back like it was */
610 fprintf(configout, orig);
617 unlink((char *)tmpin);
618 rename((char *)tmpout,(char *)tmpin);
619 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
620 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
624 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
626 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
629 static int make_file(char *dest, int len, char *dir, int num)
631 return snprintf(dest, len, "%s/msg%04d", dir, num);
635 inbuf(struct baseio *bio, FILE *fi)
642 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
657 inchar(struct baseio *bio, FILE *fi)
659 if(bio->iocp>=bio->iolen)
663 return bio->iobuf[bio->iocp++];
667 ochar(struct baseio *bio, int c, FILE *so)
669 if(bio->linelength>=BASELINELEN) {
670 if(fputs(eol,so)==EOF)
676 if(putc(((unsigned char)c),so)==EOF)
684 static int base_encode(char *filename, FILE *so)
686 unsigned char dtable[BASEMAXINLINE];
691 memset(&bio, 0, sizeof(bio));
692 bio.iocp = BASEMAXINLINE;
694 if ( !(fi = fopen(filename, "rb"))) {
695 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
703 dtable[26+i+9]= 'j'+i;
707 dtable[26+i+18]= 's'+i;
716 unsigned char igroup[3],ogroup[4];
719 igroup[0]= igroup[1]= igroup[2]= 0;
722 if ( (c = inchar(&bio, fi)) == EOF) {
727 igroup[n]= (unsigned char)c;
731 ogroup[0]= dtable[igroup[0]>>2];
732 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
733 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
734 ogroup[3]= dtable[igroup[2]&0x3F];
744 ochar(&bio, ogroup[i], so);
748 if(fputs(eol,so)==EOF)
756 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)
766 char tmp[80] = "/tmp/astmail-XXXXXX";
770 struct vm_zone *the_zone = NULL;
772 if (!strcmp(format, "wav49"))
774 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
775 /* Make a temporary file instead of piping directly to sendmail, in case the mail
779 p = fdopen(pfd, "w");
786 gethostname(host, sizeof(host));
787 if (strchr(srcemail, '@'))
788 strncpy(who, srcemail, sizeof(who)-1);
790 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
792 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
795 /* Does this user have a timezone specified? */
796 if (strlen(vmu->zonetag)) {
797 /* Find the zone in the list */
801 if (!strcmp(z->name, vmu->zonetag)) {
810 ast_localtime(&t,&tm,the_zone->timezone);
812 ast_localtime(&t,&tm,NULL);
813 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
814 fprintf(p, "Date: %s\n", date);
817 fprintf(p, "From: %s <%s>\n", fromstring, who);
819 fprintf(p, "From: Asterisk PBX <%s>\n", who);
820 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
824 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
829 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
831 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
832 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
833 fprintf(p, "MIME-Version: 1.0\n");
834 if (attach_user_voicemail) {
835 /* Something unique. */
836 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
838 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
840 fprintf(p, "--%s\n", bound);
842 fprintf(p, "Content-Type: text/plain; charset=ISO-8859-1\nContent-Transfer-Encoding: 8bit\n\n");
843 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
845 struct ast_channel *ast = ast_channel_alloc(0);
848 int vmlen = strlen(emailbody)*3 + 200;
849 if ((passdata = alloca(vmlen))) {
850 memset(passdata, 0, vmlen);
851 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
852 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
853 sprintf(passdata,"%d",msgnum);
854 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
855 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
856 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
857 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
858 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
859 fprintf(p, "%s\n",passdata);
860 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
861 ast_channel_free(ast);
862 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
864 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
866 "in mailbox %s from %s, on %s so you might\n"
867 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
868 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
870 if (attach_user_voicemail) {
871 fprintf(p, "--%s\n", bound);
872 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
873 fprintf(p, "Content-Transfer-Encoding: base64\n");
874 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
875 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
877 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
878 base_encode(fname, p);
879 fprintf(p, "\n\n--%s--\n.\n", bound);
882 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
883 ast_safe_system(tmp2);
884 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
886 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
892 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
900 char tmp[80] = "/tmp/astmail-XXXXXX";
904 struct vm_zone *the_zone = NULL;
908 p = fdopen(pfd, "w");
916 gethostname(host, sizeof(host));
917 if (strchr(srcemail, '@'))
918 strncpy(who, srcemail, sizeof(who)-1);
920 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
922 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
925 /* Does this user have a timezone specified? */
926 if (strlen(vmu->zonetag)) {
927 /* Find the zone in the list */
931 if (!strcmp(z->name, vmu->zonetag)) {
940 ast_localtime(&t,&tm,the_zone->timezone);
942 ast_localtime(&t,&tm,NULL);
944 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
945 fprintf(p, "Date: %s\n", date);
946 fprintf(p, "From: Asterisk PBX <%s>\n", who);
947 fprintf(p, "To: %s\n", pager);
948 fprintf(p, "Subject: New VM\n\n");
949 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
950 fprintf(p, "New %s long msg in box %s\n"
951 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
953 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
954 ast_safe_system(tmp2);
955 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
957 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
963 static int get_date(char *s, int len)
969 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
972 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
976 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
977 if (ast_fileexists(fn, NULL, NULL) > 0) {
978 res = ast_streamfile(chan, fn, chan->language);
981 res = ast_waitstream(chan, ecodes);
985 res = ast_streamfile(chan, "vm-theperson", chan->language);
988 res = ast_waitstream(chan, ecodes);
991 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
996 res = ast_streamfile(chan, "vm-isonphone", chan->language);
998 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1001 res = ast_waitstream(chan, ecodes);
1005 static int play_and_wait(struct ast_channel *chan, char *fn)
1008 d = ast_streamfile(chan, fn, chan->language);
1011 d = ast_waitstream(chan, AST_DIGIT_ANY);
1012 ast_stopstream(chan);
1016 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int beep)
1020 int x, fmtcnt=1, res=-1,outmsg=0;
1021 struct ast_frame *f;
1022 struct ast_filestream *others[MAX_OTHER_FORMATS];
1023 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
1024 char *sfmt[MAX_OTHER_FORMATS];
1027 struct ast_dsp *sildet; /* silence detector dsp */
1028 int totalsilence = 0;
1030 int gotsilence = 0; /* did we timeout for silence? */
1032 char prependfile[80];
1034 ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1035 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1037 if (playfile || beep) {
1039 d = play_and_wait(chan, playfile);
1041 d = ast_streamfile(chan, "beep",chan->language);
1043 d = ast_waitstream(chan,"");
1047 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
1048 strcat(prependfile, "-prepend");
1050 fmts = ast_strdupa(fmt);
1053 strsep(&stringp, "|");
1054 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1055 sfmt[0] = ast_strdupa(fmts);
1057 while((fmt = strsep(&stringp, "|"))) {
1058 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1059 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1062 sfmt[fmtcnt++] = ast_strdupa(fmt);
1067 for (x=0;x<fmtcnt;x++) {
1068 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1069 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1075 sildet = ast_dsp_new(); /* Create the silence detector */
1077 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1080 ast_dsp_set_threshold(sildet, silencethreshold);
1082 if (maxsilence > 0) {
1083 rfmt = chan->readformat;
1084 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1086 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1092 /* Loop forever, writing the packets we read to the writer(s), until
1093 we read a # or get a hangup */
1096 res = ast_waitfor(chan, 2000);
1098 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1099 /* Try one more time in case of masq */
1100 res = ast_waitfor(chan, 2000);
1102 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1114 if (f->frametype == AST_FRAME_VOICE) {
1115 /* write each format */
1116 for (x=0;x<fmtcnt;x++) {
1119 res = ast_writestream(others[x], f);
1122 /* Silence Detection */
1123 if (maxsilence > 0) {
1125 ast_dsp_silence(sildet, f, &dspsilence);
1127 totalsilence = dspsilence;
1131 if (totalsilence > maxsilence) {
1132 /* Ended happily with silence */
1139 /* Exit on any error */
1141 ast_log(LOG_WARNING, "Error writing frame\n");
1145 } else if (f->frametype == AST_FRAME_VIDEO) {
1146 /* Write only once */
1147 ast_writestream(others[0], f);
1148 } else if (f->frametype == AST_FRAME_DTMF) {
1149 /* stop recording with any digit */
1150 if (option_verbose > 2)
1151 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1159 if (maxtime < (end - start)) {
1160 if (option_verbose > 2)
1161 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1170 if (option_verbose > 2)
1171 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1175 /* delete all the prepend files */
1176 for (x=0;x<fmtcnt;x++) {
1179 ast_closestream(others[x]);
1180 ast_filedelete(prependfile, sfmt[x]);
1185 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1192 struct ast_frame *fr;
1193 for (x=0;x<fmtcnt;x++) {
1194 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1195 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1196 if (!others[x] || !realfiles[x])
1199 ast_stream_rewind(others[x], totalsilence-200);
1201 ast_stream_rewind(others[x], 200);
1202 ast_truncstream(others[x]);
1203 /* add the original file too */
1204 while ((fr = ast_readframe(realfiles[x]))) {
1205 ast_writestream(others[x],fr);
1207 ast_closestream(others[x]);
1208 ast_closestream(realfiles[x]);
1209 ast_filerename(prependfile, recordfile, sfmt[x]);
1211 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1213 ast_filedelete(prependfile, sfmt[x]);
1217 if (ast_set_read_format(chan, rfmt)) {
1218 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1223 /* Let them know it worked */
1224 ast_streamfile(chan, "auth-thankyou", chan->language);
1225 ast_waitstream(chan, "");
1231 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
1235 int x, fmtcnt=1, res=-1,outmsg=0;
1236 struct ast_frame *f;
1237 struct ast_filestream *others[MAX_OTHER_FORMATS];
1238 char *sfmt[MAX_OTHER_FORMATS];
1241 struct ast_dsp *sildet; /* silence detector dsp */
1242 int totalsilence = 0;
1244 int gotsilence = 0; /* did we timeout for silence? */
1247 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1248 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1251 d = play_and_wait(chan, playfile);
1253 d = ast_streamfile(chan, "beep",chan->language);
1255 d = ast_waitstream(chan,"");
1260 fmts = ast_strdupa(fmt);
1263 strsep(&stringp, "|");
1264 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1265 sfmt[0] = ast_strdupa(fmts);
1267 while((fmt = strsep(&stringp, "|"))) {
1268 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1269 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1272 sfmt[fmtcnt++] = ast_strdupa(fmt);
1277 for (x=0;x<fmtcnt;x++) {
1278 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1279 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1286 sildet = ast_dsp_new(); /* Create the silence detector */
1288 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1291 ast_dsp_set_threshold(sildet, silencethreshold);
1293 if (maxsilence > 0) {
1294 rfmt = chan->readformat;
1295 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1297 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1303 /* Loop forever, writing the packets we read to the writer(s), until
1304 we read a # or get a hangup */
1307 res = ast_waitfor(chan, 2000);
1309 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1310 /* Try one more time in case of masq */
1311 res = ast_waitfor(chan, 2000);
1313 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1325 if (f->frametype == AST_FRAME_VOICE) {
1326 /* write each format */
1327 for (x=0;x<fmtcnt;x++) {
1328 res = ast_writestream(others[x], f);
1331 /* Silence Detection */
1332 if (maxsilence > 0) {
1334 ast_dsp_silence(sildet, f, &dspsilence);
1336 totalsilence = dspsilence;
1340 if (totalsilence > maxsilence) {
1341 /* Ended happily with silence */
1348 /* Exit on any error */
1350 ast_log(LOG_WARNING, "Error writing frame\n");
1354 } else if (f->frametype == AST_FRAME_VIDEO) {
1355 /* Write only once */
1356 ast_writestream(others[0], f);
1357 } else if (f->frametype == AST_FRAME_DTMF) {
1358 if (f->subclass == '#') {
1359 if (option_verbose > 2)
1360 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1367 if (f->subclass == '0') {
1368 /* Check for a '0' during message recording also, in case caller wants operator */
1369 if (option_verbose > 2)
1370 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1378 if (maxtime < (end - start)) {
1379 if (option_verbose > 2)
1380 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1389 if (option_verbose > 2)
1390 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1395 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1398 for (x=0;x<fmtcnt;x++) {
1402 ast_stream_rewind(others[x], totalsilence-200);
1404 ast_stream_rewind(others[x], 200);
1405 ast_truncstream(others[x]);
1406 ast_closestream(others[x]);
1409 if (ast_set_read_format(chan, rfmt)) {
1410 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1415 /* Let them know recording is stopped */
1416 ast_streamfile(chan, "auth-thankyou", chan->language);
1417 ast_waitstream(chan, "");
1424 static void free_user(struct ast_vm_user *vmu)
1430 static void free_zone(struct vm_zone *z)
1435 static void run_externnotify(char *context, char *extension, int numvoicemails)
1437 char arguments[255];
1439 if(externnotify[0]) {
1440 strncpy(arguments, externnotify, sizeof(arguments));
1441 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1442 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1443 ast_safe_system(arguments);
1448 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1460 char prefile[256]="";
1468 struct ast_vm_user *vmu;
1469 struct ast_vm_user svm;
1472 strncpy(tmp, ext, sizeof(tmp) - 1);
1474 context = strchr(tmp, '@');
1480 if ((vmu = find_user(&svm, context, ext))) {
1481 /* Setup pre-file if appropriate */
1483 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1485 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1486 make_dir(dir, sizeof(dir), vmu->context, "", "");
1487 /* It's easier just to try to make it than to check for its existence */
1488 if (mkdir(dir, 0700) && (errno != EEXIST))
1489 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1490 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1491 /* It's easier just to try to make it than to check for its existence */
1492 if (mkdir(dir, 0700) && (errno != EEXIST))
1493 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1494 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1495 if (mkdir(dir, 0700) && (errno != EEXIST))
1496 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1497 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1499 /* Play the beginning intro if desired */
1500 if (strlen(prefile)) {
1501 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1502 if (ast_streamfile(chan, prefile, chan->language) > -1)
1503 res = ast_waitstream(chan, "#0*");
1505 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1506 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1509 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1515 /* On a '#' we skip the instructions */
1519 if (!res && !silent) {
1520 res = ast_streamfile(chan, INTRO, chan->language);
1522 res = ast_waitstream(chan, ecodes);
1528 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1529 other than the operator -- an automated attendant or mailbox login for example */
1531 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1532 if (strlen(chan->macrocontext))
1533 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1538 /* Check for a '0' here */
1541 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1542 if (strlen(chan->macrocontext))
1543 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1549 /* Unless we're *really* silent, try to send the beep */
1550 res = ast_streamfile(chan, "beep", chan->language);
1552 res = ast_waitstream(chan, "");
1558 /* The meat of recording the message... All the announcements and beeps have been played*/
1559 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1563 make_file(fn, sizeof(fn), dir, msgnum);
1564 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1565 (chan->callerid ? chan->callerid : "Unknown"),
1566 vmu->fullname, ext, chan->name);
1567 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1570 } while(msgnum < MAXMSG);
1571 if (msgnum < MAXMSG) {
1572 /* Store information */
1573 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1574 txt = fopen(txtfile, "w+");
1576 get_date(date, sizeof(date));
1580 "; Message Information file\n"
1598 chan->callerid ? chan->callerid : "Unknown",
1599 date, (long)time(NULL));
1602 ast_log(LOG_WARNING, "Error opening text file for output\n");
1603 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1608 fd = open(txtfile, O_APPEND | O_WRONLY);
1610 txt = fdopen(fd, "a");
1613 fprintf(txt, "duration=%ld\n", (long)(duration));
1618 if (end - start < vmminmessage) {
1619 ast_filedelete(fn, NULL);
1623 strsep(&stringp, "|");
1624 /* Send e-mail if applicable */
1625 if (strlen(vmu->email)) {
1626 int attach_user_voicemail = attach_voicemail;
1627 char *myserveremail = serveremail;
1628 if (vmu->attach > -1)
1629 attach_user_voicemail = vmu->attach;
1630 if (strlen(vmu->serveremail))
1631 myserveremail = vmu->serveremail;
1632 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1634 if (strlen(vmu->pager)) {
1635 char *myserveremail = serveremail;
1636 if (strlen(vmu->serveremail))
1637 myserveremail = vmu->serveremail;
1638 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1641 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1643 res = ast_waitstream(chan, "");
1644 ast_log(LOG_WARNING, "No more messages possible\n");
1647 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1651 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1652 /*Send the call to n+101 priority, where n is the current priority*/
1653 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1654 chan->priority+=100;
1656 /* Leave voicemail for someone */
1657 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1659 /* If an external program is specified to be run after leaving a voicemail */
1660 run_externnotify(chan->context, ext, ast_app_has_voicemail(ext));
1665 static char *mbox(int id)
1693 static int count_messages(char *dir)
1697 for (x=0;x<MAXMSG;x++) {
1698 make_file(fn, sizeof(fn), dir, x);
1699 if (ast_fileexists(fn, NULL, NULL) < 1)
1705 static int say_and_wait(struct ast_channel *chan, int num)
1708 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1712 static int copy(char *infile, char *outfile)
1719 if ((ifd = open(infile, O_RDONLY)) < 0) {
1720 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1723 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1724 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1729 len = read(ifd, buf, sizeof(buf));
1731 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1737 res = write(ofd, buf, len);
1739 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1751 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1758 char *dbox = mbox(box);
1760 make_file(sfn, sizeof(sfn), dir, msg);
1761 make_dir(ddir, sizeof(ddir), context, username, dbox);
1763 for (x=0;x<MAXMSG;x++) {
1764 make_file(dfn, sizeof(dfn), ddir, x);
1765 if (ast_fileexists(dfn, NULL, NULL) < 0)
1770 ast_filecopy(sfn, dfn, NULL);
1771 if (strcmp(sfn, dfn)) {
1772 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1773 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1779 static int adsi_logo(unsigned char *buf)
1782 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1783 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1787 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1795 bytes += adsi_data_mode(buf + bytes);
1796 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1799 bytes += adsi_logo(buf);
1800 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1802 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1804 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1805 bytes += adsi_data_mode(buf + bytes);
1806 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1808 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1810 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1811 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1812 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1813 bytes += adsi_voice_mode(buf + bytes, 0);
1814 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1821 bytes += adsi_logo(buf);
1822 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1823 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1824 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1825 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1828 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1829 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1830 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1831 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1832 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1833 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1834 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1837 /* Add another dot */
1839 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1840 bytes += adsi_voice_mode(buf + bytes, 0);
1842 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1843 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1847 /* These buttons we load but don't use yet */
1848 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1849 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1850 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1851 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1852 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1853 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1854 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1857 /* Add another dot */
1859 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1860 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1861 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1866 snprintf(num, sizeof(num), "%d", x);
1867 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1869 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1870 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1873 /* Add another dot */
1875 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1876 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1877 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1880 if (adsi_end_download(chan)) {
1882 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1883 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1884 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1885 bytes += adsi_voice_mode(buf + bytes, 0);
1886 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1890 bytes += adsi_download_disconnect(buf + bytes);
1891 bytes += adsi_voice_mode(buf + bytes, 0);
1892 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1894 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1899 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1900 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1902 ast_log(LOG_DEBUG, "Restarting session...\n");
1905 /* Load the session now */
1906 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1908 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1910 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1912 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1916 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1919 if (!adsi_available(chan))
1921 x = adsi_load_session(chan, adapp, adver, 1);
1925 if (adsi_load_vmail(chan, useadsi)) {
1926 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1933 static void adsi_login(struct ast_channel *chan)
1937 unsigned char keys[8];
1939 if (!adsi_available(chan))
1944 /* Set one key for next */
1945 keys[3] = ADSI_KEY_APPS + 3;
1947 bytes += adsi_logo(buf + bytes);
1948 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1949 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1950 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1951 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1952 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1953 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1954 bytes += adsi_set_keys(buf + bytes, keys);
1955 bytes += adsi_voice_mode(buf + bytes, 0);
1956 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1959 static void adsi_password(struct ast_channel *chan)
1963 unsigned char keys[8];
1965 if (!adsi_available(chan))
1970 /* Set one key for next */
1971 keys[3] = ADSI_KEY_APPS + 3;
1973 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1974 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1975 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1976 bytes += adsi_set_keys(buf + bytes, keys);
1977 bytes += adsi_voice_mode(buf + bytes, 0);
1978 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1981 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1985 unsigned char keys[8];
1988 if (!adsi_available(chan))
1992 y = ADSI_KEY_APPS + 12 + start + x;
1993 if (y > ADSI_KEY_APPS + 12 + 4)
1995 keys[x] = ADSI_KEY_SKT | y;
1997 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2001 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2002 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2003 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2004 bytes += adsi_set_keys(buf + bytes, keys);
2005 bytes += adsi_voice_mode(buf + bytes, 0);
2007 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2010 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2013 char buf[256], buf1[256], buf2[256];
2019 char datetime[21]="";
2022 unsigned char keys[8];
2026 if (!adsi_available(chan))
2029 /* Retrieve important info */
2030 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2031 f = fopen(fn2, "r");
2034 fgets(buf, sizeof(buf), f);
2038 strsep(&stringp, "=");
2039 val = strsep(&stringp, "=");
2040 if (val && strlen(val)) {
2041 if (!strcmp(buf, "callerid"))
2042 strncpy(cid, val, sizeof(cid) - 1);
2043 if (!strcmp(buf, "origdate"))
2044 strncpy(datetime, val, sizeof(datetime) - 1);
2050 /* New meaning for keys */
2052 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2057 /* No prev key, provide "Folder" instead */
2058 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2061 /* If last message ... */
2063 /* but not only message, provide "Folder" instead */
2064 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2065 bytes += adsi_voice_mode(buf + bytes, 0);
2068 /* Otherwise if only message, leave blank */
2074 ast_callerid_parse(cid, &name, &num);
2078 name = "Unknown Caller";
2080 /* If deleted, show "undeleted" */
2083 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2086 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2087 snprintf(buf1, sizeof(buf1), "%s%s", folder,
2088 strcasecmp(folder, "INBOX") ? " Messages" : "");
2089 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2091 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2092 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2093 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2094 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2095 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2096 bytes += adsi_set_keys(buf + bytes, keys);
2097 bytes += adsi_voice_mode(buf + bytes, 0);
2099 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2102 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2106 unsigned char keys[8];
2110 if (!adsi_available(chan))
2113 /* New meaning for keys */
2115 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2121 /* No prev key, provide "Folder" instead */
2122 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2125 /* If last message ... */
2127 /* but not only message, provide "Folder" instead */
2128 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2130 /* Otherwise if only message, leave blank */
2135 /* If deleted, show "undeleted" */
2137 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2140 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2141 bytes += adsi_set_keys(buf + bytes, keys);
2142 bytes += adsi_voice_mode(buf + bytes, 0);
2144 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2147 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2149 char buf[256], buf1[256], buf2[256];
2151 unsigned char keys[8];
2154 char *newm = (new == 1) ? "message" : "messages";
2155 char *oldm = (old == 1) ? "message" : "messages";
2156 if (!adsi_available(chan))
2159 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2161 strcat(buf1, " and");
2162 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2164 snprintf(buf2, sizeof(buf2), "%s.", newm);
2167 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2168 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2170 strcpy(buf1, "You have no messages.");
2173 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2174 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2175 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2178 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2182 /* Don't let them listen if there are none */
2185 bytes += adsi_set_keys(buf + bytes, keys);
2187 bytes += adsi_voice_mode(buf + bytes, 0);
2189 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2192 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2194 char buf[256], buf1[256], buf2[256];
2196 unsigned char keys[8];
2199 char *mess = (messages == 1) ? "message" : "messages";
2201 if (!adsi_available(chan))
2204 /* Original command keys */
2206 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2214 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2215 strcasecmp(folder, "INBOX") ? " folder" : "");
2218 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2220 strcpy(buf2, "no messages.");
2221 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2222 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2223 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2224 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2225 bytes += adsi_set_keys(buf + bytes, keys);
2227 bytes += adsi_voice_mode(buf + bytes, 0);
2229 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2234 static void adsi_clear(struct ast_channel *chan)
2238 if (!adsi_available(chan))
2240 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2241 bytes += adsi_voice_mode(buf + bytes, 0);
2243 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2247 static void adsi_goodbye(struct ast_channel *chan)
2252 if (!adsi_available(chan))
2254 bytes += adsi_logo(buf + bytes);
2255 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2256 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2257 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2258 bytes += adsi_voice_mode(buf + bytes, 0);
2260 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2263 static int get_folder(struct ast_channel *chan, int start)
2268 d = play_and_wait(chan, "vm-press");
2271 for (x = start; x< 5; x++) {
2272 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
2274 d = play_and_wait(chan, "vm-for");
2277 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2278 d = play_and_wait(chan, fn);
2281 d = play_and_wait(chan, "vm-messages");
2284 d = ast_waitfordigit(chan, 500);
2288 d = play_and_wait(chan, "vm-tocancel");
2291 d = ast_waitfordigit(chan, 4000);
2295 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2298 res = play_and_wait(chan, fn);
2299 while (((res < '0') || (res > '9')) &&
2300 (res != '#') && (res >= 0)) {
2301 res = get_folder(chan, 0);
2306 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2311 while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2316 /* prepend a message to the current message and return */
2319 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2320 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, 1);
2330 cmd = play_and_wait(chan,"vm-forwardoptions");
2332 cmd = play_and_wait(chan,"vm-starmain");
2334 cmd = ast_waitfordigit(chan,6000);
2346 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2353 struct ast_config *mif;
2357 int res = 0, cmd = 0;
2358 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2361 int saved_messages = 0, found = 0;
2362 int valid_extensions = 0;
2363 while (!res && !valid_extensions) {
2364 res = ast_streamfile(chan, "vm-extension", chan->language);
2367 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2369 /* start all over if no username */
2370 if (!strlen(username))
2373 s = strsep(&stringp, "*");
2374 /* start optimistic */
2375 valid_extensions = 1;
2377 /* find_user is going to malloc since we have a NULL as first argument */
2378 if ((receiver = find_user(NULL, context, s))) {
2380 vmtmp = extensions = receiver;
2382 vmtmp->next = receiver;
2387 valid_extensions = 0;
2390 s = strsep(&stringp, "*");
2392 /* break from the loop of reading the extensions */
2393 if (valid_extensions)
2395 /* invalid extension, try again */
2396 res = play_and_wait(chan, "pbx-invalid");
2398 /* check if we're clear to proceed */
2399 if (!extensions || !valid_extensions)
2402 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2404 while(!res && vmtmp) {
2405 /* if (play_and_wait(chan, "vm-savedto"))
2408 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2409 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2410 ast_log(LOG_DEBUG, sys);
2411 ast_safe_system(sys);
2413 todircount = count_messages(todir);
2414 strncpy(tmp, fmt, sizeof(tmp) - 1);
2416 while((s = strsep(&stringp, "|"))) {
2417 /* XXX This is a hack -- we should use build_filename or similar XXX */
2418 if (!strcasecmp(s, "wav49"))
2420 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2421 ast_log(LOG_DEBUG, sys);
2422 ast_safe_system(sys);
2424 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2425 ast_log(LOG_DEBUG, sys);
2426 ast_safe_system(sys);
2427 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2429 /* load the information on the source message so we can send an e-mail like a new message */
2430 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2431 if ((mif=ast_load(miffile))) {
2433 /* set callerid and duration variables */
2434 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2435 s = ast_variable_retrieve(mif, NULL, "duration");
2440 if (strlen(vmtmp->email)) {
2441 int attach_user_voicemail = attach_voicemail;
2442 char *myserveremail = serveremail;
2443 if (vmtmp->attach > -1)
2444 attach_user_voicemail = vmtmp->attach;
2445 if (strlen(vmtmp->serveremail))
2446 myserveremail = vmtmp->serveremail;
2447 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2450 if (strlen(vmtmp->pager)) {
2451 char *myserveremail = serveremail;
2452 if (strlen(vmtmp->serveremail))
2453 myserveremail = vmtmp->serveremail;
2454 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2457 ast_destroy(mif); /* or here */
2459 /* Leave voicemail for someone */
2460 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2461 run_externnotify(chan->context, vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2465 vmtmp = vmtmp->next;
2468 if (saved_messages > 0) {
2469 /* give confirmatopm that the message was saved */
2470 /* commented out since we can't forward batches yet
2471 if (saved_messages == 1)
2472 res = play_and_wait(chan, "vm-message");
2474 res = play_and_wait(chan, "vm-messages");
2476 res = play_and_wait(chan, "vm-saved"); */
2478 res = play_and_wait(chan, "vm-msgsaved");
2481 return res ? res : cmd;
2485 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2488 if ((res = ast_streamfile(chan, file, chan->language)))
2489 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2491 res = ast_waitstream(chan, AST_DIGIT_ANY);
2495 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2498 if ((res = ast_streamfile(chan, file, chan->language)))
2499 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2501 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2505 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2508 struct vm_zone *the_zone = NULL;
2512 if (sscanf(origtime,"%ld",&tin) < 1) {
2513 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2518 /* Does this user have a timezone specified? */
2519 if (strlen(vmu->zonetag)) {
2520 /* Find the zone in the list */
2524 if (!strcmp(z->name, vmu->zonetag)) {
2532 /* No internal variable parsing for now, so we'll comment it out for the time being */
2534 /* Set the DIFF_* variables */
2535 localtime_r(&t, &time_now);
2536 gettimeofday(&tv_now,NULL);
2537 tnow = tv_now.tv_sec;
2538 localtime_r(&tnow,&time_then);
2540 /* Day difference */
2541 if (time_now.tm_year == time_then.tm_year)
2542 sprintf(temp,"%d",time_now.tm_yday);
2544 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2545 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2547 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2550 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2552 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2554 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2561 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2565 char *callerid, *name;
2566 char prefile[256]="";
2569 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2570 /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
2571 if((cid == NULL)||(context == NULL))
2574 /* Strip off caller ID number from name */
2575 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2576 ast_callerid_parse(cid, &name, &callerid);
2577 if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2578 /* Check for internal contexts and only */
2579 /* say extension when the call didn't come from an internal context in the list */
2580 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2581 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2582 if((strcmp(cidinternalcontexts[i], context) == 0))
2585 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2587 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2588 if (strlen(prefile)) {
2589 /* See if we can find a recorded name for this person instead of their extension number */
2590 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2591 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2593 res = wait_file2(chan, vms, "vm-from");
2594 res = ast_streamfile(chan, prefile, chan->language) > -1;
2595 res = ast_waitstream(chan, "");
2597 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2598 /* BB: Say "from extension" as one saying to sound smoother */
2600 res = wait_file2(chan, vms, "vm-from-extension");
2601 res = ast_say_digit_str(chan, callerid, "", chan->language);
2608 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2609 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2611 res = wait_file2(chan, vms, "vm-from-phonenumber");
2612 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2616 /* Number unknown */
2617 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2619 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2620 res = wait_file2(chan, vms, "vm-unknown-caller");
2625 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2628 char filename[256],*origtime, *cid, *context;
2629 struct ast_config *msg_cfg;
2632 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2633 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2635 res = wait_file2(chan, vms, "vm-first");
2636 else if (msg == vms->lastmsg)
2637 res = wait_file2(chan, vms, "vm-last");
2639 res = wait_file2(chan, vms, "vm-message");
2640 if (msg && (msg != vms->lastmsg)) {
2642 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2646 /* Retrieve info from VM attribute file */
2648 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2649 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2650 msg_cfg = ast_load(filename);
2652 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2656 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2659 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2661 context = ast_variable_retrieve(msg_cfg, "message", "context");
2662 if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2663 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2666 res = play_message_datetime(chan, vmu, origtime, filename);
2668 if ((!res)&&(vmu->saycid))
2669 res = play_message_callerid(chan, vms, cid, context, 0);
2671 ast_destroy(msg_cfg);
2674 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2675 vms->heard[msg] = 1;
2676 res = wait_file(chan, vms, vms->fn);
2681 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2683 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2684 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2685 vms->lastmsg = count_messages(vms->curdir) - 1;
2686 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2689 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2692 char ntxt[256] = "";
2694 if (vms->lastmsg > -1) {
2695 /* Get the deleted messages fixed */
2697 for (x=0;x < MAXMSG;x++) {
2698 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2699 /* Save this message. It's not in INBOX or hasn't been heard */
2700 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2701 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2704 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2705 if (strcmp(vms->fn, vms->fn2)) {
2706 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2707 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2708 ast_filerename(vms->fn, vms->fn2, NULL);
2711 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2712 /* Move to old folder before deleting */
2713 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2716 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2717 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2718 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2720 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2721 ast_filedelete(vms->fn, NULL);
2725 memset(vms->deleted, 0, sizeof(vms->deleted));
2726 memset(vms->heard, 0, sizeof(vms->heard));
2729 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2731 /* Introduce messages they have */
2733 res = play_and_wait(chan, "vm-youhave");
2735 if (vms->newmessages) {
2736 res = say_and_wait(chan, vms->newmessages);
2738 res = play_and_wait(chan, "vm-INBOX");
2739 if (vms->oldmessages && !res)
2740 res = play_and_wait(chan, "vm-and");
2742 if ((vms->newmessages == 1))
2743 res = play_and_wait(chan, "vm-message");
2745 res = play_and_wait(chan, "vm-messages");
2749 if (!res && vms->oldmessages) {
2750 res = say_and_wait(chan, vms->oldmessages);
2752 res = play_and_wait(chan, "vm-Old");
2754 if (vms->oldmessages == 1)
2755 res = play_and_wait(chan, "vm-message");
2757 res = play_and_wait(chan, "vm-messages");
2761 if (!vms->oldmessages && !vms->newmessages) {
2762 res = play_and_wait(chan, "vm-no");
2764 res = play_and_wait(chan, "vm-messages");
2771 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2774 /* Play instructions and wait for new command */
2776 if (vms->starting) {
2777 if (vms->lastmsg > -1) {
2778 res = play_and_wait(chan, "vm-onefor");
2780 res = play_and_wait(chan, vms->vmbox);
2782 res = play_and_wait(chan, "vm-messages");
2785 res = play_and_wait(chan, "vm-opts");
2788 res = play_and_wait(chan, "vm-prev");
2790 res = play_and_wait(chan, "vm-advopts");
2792 res = play_and_wait(chan, "vm-repeat");
2793 if (!res && (vms->curmsg != vms->lastmsg))
2794 res = play_and_wait(chan, "vm-next");
2796 if (!vms->deleted[vms->curmsg])
2797 res = play_and_wait(chan, "vm-delete");
2799 res = play_and_wait(chan, "vm-undelete");
2801 res = play_and_wait(chan, "vm-toforward");
2803 res = play_and_wait(chan, "vm-savemessage");
2807 res = play_and_wait(chan, "vm-helpexit");
2809 res = ast_waitfordigit(chan, 6000);
2812 if (vms->repeats > 2) {
2820 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2824 char newpassword[80] = "";
2825 char newpassword2[80] = "";
2826 char prefile[256]="";
2830 if (adsi_available(chan))
2832 bytes += adsi_logo(buf + bytes);
2833 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2834 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2835 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2836 bytes += adsi_voice_mode(buf + bytes, 0);
2837 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2839 while((cmd >= 0) && (cmd != 't')) {
2844 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2845 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, 0);
2848 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2849 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, 0);
2852 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2853 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, 0);
2856 newpassword[1] = '\0';
2857 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2860 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2863 newpassword2[1] = '\0';
2864 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2868 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2871 if (strcmp(newpassword, newpassword2)) {
2872 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2873 cmd = play_and_wait(chan, "vm-mismatch");
2876 vm_change_password(vmu,newpassword);
2877 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2878 cmd = play_and_wait(chan,"vm-passchanged");
2884 cmd = play_and_wait(chan,"vm-options");
2886 cmd = ast_waitfordigit(chan,6000);
2898 static int vm_execmain(struct ast_channel *chan, void *data)
2900 /* XXX This is, admittedly, some pretty horrendus code. For some
2901 reason it just seemed a lot easier to do with GOTO's. I feel
2902 like I'm back in my GWBASIC days. XXX */
2907 struct localuser *u;
2908 char prefixstr[80] ="";
2909 char empty[80] = "";
2913 char tmp[256], *ext;
2914 char fmtc[256] = "";
2916 struct vm_state vms;
2918 struct ast_vm_user *vmu = NULL, vmus;
2923 memset(&vms, 0, sizeof(vms));
2924 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2925 if (chan->_state != AST_STATE_UP)
2928 if (data && strlen(data)) {
2929 strncpy(tmp, data, sizeof(tmp) - 1);
2934 /* We should skip the user's password */
2939 /* We should prefix the mailbox with the supplied data */
2945 context = strchr(ext, '@');
2952 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2954 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2955 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))