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 */
119 struct ast_vm_user *next;
125 char msg_format[512];
126 struct vm_zone *next;
145 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
146 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
147 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);
148 static int vm_delete(char *file);
152 static char *tdesc = "Comedian Mail (Voicemail System)";
154 static char *adapp = "CoMa";
156 static char *adsec = "_AST";
158 static char *addesc = "Comedian Mail";
160 static int adver = 1;
162 static char *synopsis_vm =
163 "Leave a voicemail message";
165 static char *descrip_vm =
166 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
167 "extension (must be configured in voicemail.conf).\n"
168 " If the extension is preceded by \n"
169 "* 's' then instructions for leaving the message will be skipped.\n"
170 "* 'u' then the \"unavailable\" message will be played.\n"
171 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
172 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
173 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
174 "priority 'o' in the current context.\n"
175 "If the requested mailbox does not exist, and there exists a priority\n"
176 "n + 101, then that priority will be taken next.\n"
177 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
178 "Otherwise, it returns 0.\n";
180 static char *synopsis_vmain =
181 "Enter voicemail system";
183 static char *descrip_vmain =
184 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
185 "for the checking of voicemail. The mailbox can be passed as the option,\n"
186 "which will stop the voicemail system from prompting the user for the mailbox.\n"
187 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
188 "a context is specified, logins are considered in that voicemail context only.\n"
189 "Returns -1 if the user hangs up or 0 otherwise.\n";
191 static char *synopsis_vm_box_exists =
192 "Check if vmbox exists";
194 static char *descrip_vm_box_exists =
195 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
196 "if the specified voice mailbox exists.\n";
199 /* Leave a message */
200 static char *capp = "VoiceMail2";
201 static char *app = "VoiceMail";
203 /* Check mail, control, etc */
204 static char *capp2 = "VoiceMailMain2";
205 static char *app2 = "VoiceMailMain";
207 static char *app3 = "MailboxExists";
209 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
210 struct ast_vm_user *users;
211 struct ast_vm_user *usersl;
212 struct vm_zone *zones = NULL;
213 struct vm_zone *zonesl = NULL;
214 static int attach_voicemail;
215 static int maxsilence;
216 static int silencethreshold = 128;
217 static char serveremail[80];
218 static char mailcmd[160]; /* Configurable mail cmd */
219 static char externnotify[160];
221 static char vmfmts[80];
222 static int vmminmessage;
223 static int vmmaxmessage;
226 static int maxlogins;
230 static int saycidinfo;
232 static char dialcontext[80];
233 static char callcontext[80];
235 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
238 static char *emailbody = NULL;
239 static int pbxskip = 0;
240 static char *emailsubject = NULL;
241 static char fromstring[100];
242 static char emailtitle[100];
243 static char charset[32] = "ISO-8859-1";
250 static void populate_defaults(struct ast_vm_user *vmu)
262 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
264 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
267 static void apply_options(struct ast_vm_user *vmu, char *options)
269 /* Destructively Parse options and apply */
270 char *stringp = ast_strdupa(options);
274 while((s = strsep(&stringp, "|"))) {
276 if ((var = strsep(&value, "=")) && value) {
277 if (!strcasecmp(var, "attach")) {
282 } else if (!strcasecmp(var, "serveremail")) {
283 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
284 } else if (!strcasecmp(var, "tz")) {
285 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
286 } else if (!strcasecmp(var, "saycid")){
291 } else if (!strcasecmp(var, "review")){
296 } else if (!strcasecmp(var, "operator")){
301 } else if (!strcasecmp(var, "envelope")){
306 } else if (!strcasecmp(var, "callback")) {
307 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
308 } else if (!strcasecmp(var, "dialout")) {
309 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
318 #include "mysql-vm-routines.h"
325 ast_mutex_t postgreslock;
327 static int sql_init(void)
329 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
330 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
332 dbhandler=PQconnectdb(dboption);
333 if (PQstatus(dbhandler) == CONNECTION_BAD) {
334 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
337 ast_mutex_init(&postgreslock);
339 /* fprintf(stderr,"postgres login OK\n"); */
343 static void sql_close(void)
349 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
357 char options[160] = "";
358 struct ast_vm_user *retval;
360 retval=malloc(sizeof(struct ast_vm_user));
362 /* fprintf(stderr,"postgres find_user:\n"); */
365 memset(retval, 0, sizeof(struct ast_vm_user));
368 strcpy(retval->mailbox, mailbox);
371 strcpy(retval->context, context);
375 strcpy(retval->context, "default");
377 populate_defaults(retval);
378 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
380 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
381 ast_mutex_lock(&postgreslock);
382 PGSQLres=PQexec(dbhandler,query);
383 if (PGSQLres!=NULL) {
384 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
385 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
386 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
388 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
391 ast_mutex_unlock(&postgreslock);
395 numFields = PQnfields(PGSQLres);
396 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
397 if (PQntuples(PGSQLres) != 1) {
398 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
400 ast_mutex_unlock(&postgreslock);
404 for (i=0; i<numFields; i++) {
405 fname = PQfname(PGSQLres,i);
406 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
407 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
408 } else if (!strcmp(fname, "fullname")) {
409 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
410 } else if (!strcmp(fname, "email")) {
411 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
412 } else if (!strcmp(fname, "pager")) {
413 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
414 } else if (!strcmp(fname, "options")) {
415 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
416 apply_options(retval, options);
421 ast_mutex_unlock(&postgreslock);
425 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
426 ast_mutex_unlock(&postgreslock);
431 } /* malloc() retval */
436 static void vm_change_password(struct ast_vm_user *vmu, char *password)
441 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);
443 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
445 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
446 ast_mutex_lock(&postgreslock);
447 PQexec(dbhandler, query);
448 strcpy(vmu->password, password);
449 ast_mutex_unlock(&postgreslock);
452 static void reset_user_pw(char *context, char *mailbox, char *password)
457 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
459 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
461 ast_mutex_lock(&postgreslock);
462 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
463 PQexec(dbhandler, query);
464 ast_mutex_unlock(&postgreslock);
467 #endif /* Postgres */
471 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
473 /* This function could be made to generate one from a database, too */
474 struct ast_vm_user *vmu=NULL, *cur;
475 ast_mutex_lock(&vmlock);
478 if ((!context || !strcasecmp(context, cur->context)) &&
479 (!strcasecmp(mailbox, cur->mailbox)))
487 /* Make a copy, so that on a reload, we have no race */
488 vmu = malloc(sizeof(struct ast_vm_user));
490 memcpy(vmu, cur, sizeof(struct ast_vm_user));
498 ast_mutex_unlock(&vmlock);
502 static int reset_user_pw(char *context, char *mailbox, char *newpass)
504 /* This function could be made to generate one from a database, too */
505 struct ast_vm_user *cur;
507 ast_mutex_lock(&vmlock);
510 if ((!context || !strcasecmp(context, cur->context)) &&
511 (!strcasecmp(mailbox, cur->mailbox)))
516 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
519 ast_mutex_unlock(&vmlock);
523 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
525 /* There's probably a better way of doing this. */
526 /* That's why I've put the password change in a separate function. */
527 /* This could also be done with a database function */
534 char currcontext[256] ="";
535 char tmpin[AST_CONFIG_MAX_PATH];
536 char tmpout[AST_CONFIG_MAX_PATH];
537 char *user, *pass, *rest, *trim, *tempcontext;
539 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
540 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
541 configin = fopen((char *)tmpin,"r");
543 configout = fopen((char *)tmpout,"w+");
546 if(!configin || !configout) {
550 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
554 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
558 while (!feof(configin)) {
559 /* Read in the line */
560 fgets(inbuf, sizeof(inbuf), configin);
562 if (!feof(configin)) {
563 /* Make a backup of it */
564 memcpy(orig, inbuf, sizeof(orig));
565 /* Strip trailing \n and comment */
566 inbuf[strlen(inbuf) - 1] = '\0';
567 user = strchr(inbuf, ';');
573 /* check for '[' (opening of context name ) */
574 tempcontext = strchr(user, '[');
576 strncpy(currcontext, tempcontext +1,
577 sizeof(currcontext) - 1);
578 /* now check for ']' */
579 tempcontext = strchr(currcontext, ']');
583 currcontext[0] = '\0';
585 pass = strchr(user, '=');
588 while(*trim && *trim < 33) {
598 while(*pass && *pass < 33)
602 rest = strchr(pass,',');
610 /* Compare user, pass AND context */
611 if (user && *user && !strcmp(user, vmu->mailbox) &&
612 pass && *pass && !strcmp(pass, vmu->password) &&
613 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
614 /* This is the line */
616 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
618 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
621 /* Put it back like it was */
622 fprintf(configout, orig);
629 unlink((char *)tmpin);
630 rename((char *)tmpout,(char *)tmpin);
631 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
632 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
636 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
638 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
641 static int make_file(char *dest, int len, char *dir, int num)
643 return snprintf(dest, len, "%s/msg%04d", dir, num);
647 inbuf(struct baseio *bio, FILE *fi)
654 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
669 inchar(struct baseio *bio, FILE *fi)
671 if(bio->iocp>=bio->iolen)
675 return bio->iobuf[bio->iocp++];
679 ochar(struct baseio *bio, int c, FILE *so)
681 if(bio->linelength>=BASELINELEN) {
682 if(fputs(eol,so)==EOF)
688 if(putc(((unsigned char)c),so)==EOF)
696 static int base_encode(char *filename, FILE *so)
698 unsigned char dtable[BASEMAXINLINE];
703 memset(&bio, 0, sizeof(bio));
704 bio.iocp = BASEMAXINLINE;
706 if ( !(fi = fopen(filename, "rb"))) {
707 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
715 dtable[26+i+9]= 'j'+i;
719 dtable[26+i+18]= 's'+i;
728 unsigned char igroup[3],ogroup[4];
731 igroup[0]= igroup[1]= igroup[2]= 0;
734 if ( (c = inchar(&bio, fi)) == EOF) {
739 igroup[n]= (unsigned char)c;
743 ogroup[0]= dtable[igroup[0]>>2];
744 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
745 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
746 ogroup[3]= dtable[igroup[2]&0x3F];
756 ochar(&bio, ogroup[i], so);
760 if(fputs(eol,so)==EOF)
768 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *callerid, char *dur, char *date, char *passdata)
770 /* Prepare variables for substition in email body and subject */
771 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
772 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
773 sprintf(passdata,"%d",msgnum);
774 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
775 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
776 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
777 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
780 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *callerid, char *attach, char *format, int duration, int attach_user_voicemail)
790 char tmp[80] = "/tmp/astmail-XXXXXX";
794 struct vm_zone *the_zone = NULL;
796 if (!strcmp(format, "wav49"))
798 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
799 /* Make a temporary file instead of piping directly to sendmail, in case the mail
803 p = fdopen(pfd, "w");
810 gethostname(host, sizeof(host));
811 if (strchr(srcemail, '@'))
812 strncpy(who, srcemail, sizeof(who)-1);
814 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
816 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
819 /* Does this user have a timezone specified? */
820 if (strlen(vmu->zonetag)) {
821 /* Find the zone in the list */
825 if (!strcmp(z->name, vmu->zonetag)) {
834 ast_localtime(&t,&tm,the_zone->timezone);
836 ast_localtime(&t,&tm,NULL);
837 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
838 fprintf(p, "Date: %s\n", date);
841 fprintf(p, "From: %s <%s>\n", fromstring, who);
843 fprintf(p, "From: Asterisk PBX <%s>\n", who);
844 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
847 struct ast_channel *ast = ast_channel_alloc(0);
850 int vmlen = strlen(emailsubject)*3 + 200;
851 if ((passdata = alloca(vmlen))) {
852 memset(passdata, 0, vmlen);
853 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
854 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
855 fprintf(p, "Subject: %s\n",passdata);
856 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
857 ast_channel_free(ast);
858 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
862 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
867 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
869 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
870 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
871 fprintf(p, "MIME-Version: 1.0\n");
872 if (attach_user_voicemail) {
873 /* Something unique. */
874 snprintf(bound, sizeof(bound), "voicemail_%d%s%d", msgnum, mailbox, getpid());
876 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
878 fprintf(p, "--%s\n", bound);
880 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
881 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
883 struct ast_channel *ast = ast_channel_alloc(0);
886 int vmlen = strlen(emailbody)*3 + 200;
887 if ((passdata = alloca(vmlen))) {
888 memset(passdata, 0, vmlen);
889 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
890 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
891 fprintf(p, "%s\n",passdata);
892 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
893 ast_channel_free(ast);
894 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
896 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
898 "in mailbox %s from %s, on %s so you might\n"
899 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
900 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
902 if (attach_user_voicemail) {
903 fprintf(p, "--%s\n", bound);
904 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
905 fprintf(p, "Content-Transfer-Encoding: base64\n");
906 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
907 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
909 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
910 base_encode(fname, p);
911 fprintf(p, "\n\n--%s--\n.\n", bound);
914 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
915 ast_safe_system(tmp2);
916 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
918 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
924 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, int duration, struct ast_vm_user *vmu)
932 char tmp[80] = "/tmp/astmail-XXXXXX";
936 struct vm_zone *the_zone = NULL;
940 p = fdopen(pfd, "w");
948 gethostname(host, sizeof(host));
949 if (strchr(srcemail, '@'))
950 strncpy(who, srcemail, sizeof(who)-1);
952 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
954 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
957 /* Does this user have a timezone specified? */
958 if (strlen(vmu->zonetag)) {
959 /* Find the zone in the list */
963 if (!strcmp(z->name, vmu->zonetag)) {
972 ast_localtime(&t,&tm,the_zone->timezone);
974 ast_localtime(&t,&tm,NULL);
976 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
977 fprintf(p, "Date: %s\n", date);
978 fprintf(p, "From: Asterisk PBX <%s>\n", who);
979 fprintf(p, "To: %s\n", pager);
980 fprintf(p, "Subject: New VM\n\n");
981 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
982 fprintf(p, "New %s long msg in box %s\n"
983 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
985 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
986 ast_safe_system(tmp2);
987 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
989 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
995 static int get_date(char *s, int len)
1000 localtime_r(&t,&tm);
1001 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1004 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1008 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1009 if (ast_fileexists(fn, NULL, NULL) > 0) {
1010 res = ast_streamfile(chan, fn, chan->language);
1013 res = ast_waitstream(chan, ecodes);
1017 res = ast_streamfile(chan, "vm-theperson", chan->language);
1020 res = ast_waitstream(chan, ecodes);
1023 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1028 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1030 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1033 res = ast_waitstream(chan, ecodes);
1037 static int play_and_wait(struct ast_channel *chan, char *fn)
1040 d = ast_streamfile(chan, fn, chan->language);
1043 d = ast_waitstream(chan, AST_DIGIT_ANY);
1044 ast_stopstream(chan);
1048 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep)
1052 int x, fmtcnt=1, res=-1,outmsg=0;
1053 struct ast_frame *f;
1054 struct ast_filestream *others[MAX_OTHER_FORMATS];
1055 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
1056 char *sfmt[MAX_OTHER_FORMATS];
1059 struct ast_dsp *sildet; /* silence detector dsp */
1060 int totalsilence = 0;
1062 int gotsilence = 0; /* did we timeout for silence? */
1064 char prependfile[80];
1066 /* barf if no pointer passed to store duration in */
1067 if (duration == NULL) {
1068 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
1072 ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1073 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1075 if (playfile || beep) {
1077 d = play_and_wait(chan, playfile);
1079 d = ast_streamfile(chan, "beep",chan->language);
1081 d = ast_waitstream(chan,"");
1085 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
1086 strcat(prependfile, "-prepend");
1088 fmts = ast_strdupa(fmt);
1091 strsep(&stringp, "|");
1092 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1093 sfmt[0] = ast_strdupa(fmts);
1095 while((fmt = strsep(&stringp, "|"))) {
1096 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1097 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1100 sfmt[fmtcnt++] = ast_strdupa(fmt);
1104 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1105 for (x=0;x<fmtcnt;x++) {
1106 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1107 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1113 sildet = ast_dsp_new(); /* Create the silence detector */
1115 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1118 ast_dsp_set_threshold(sildet, silencethreshold);
1120 if (maxsilence > 0) {
1121 rfmt = chan->readformat;
1122 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1124 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1130 /* Loop forever, writing the packets we read to the writer(s), until
1131 we read a # or get a hangup */
1134 res = ast_waitfor(chan, 2000);
1136 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1137 /* Try one more time in case of masq */
1138 res = ast_waitfor(chan, 2000);
1140 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1152 if (f->frametype == AST_FRAME_VOICE) {
1153 /* write each format */
1154 for (x=0;x<fmtcnt;x++) {
1157 res = ast_writestream(others[x], f);
1160 /* Silence Detection */
1161 if (maxsilence > 0) {
1163 ast_dsp_silence(sildet, f, &dspsilence);
1165 totalsilence = dspsilence;
1169 if (totalsilence > maxsilence) {
1170 /* Ended happily with silence */
1177 /* Exit on any error */
1179 ast_log(LOG_WARNING, "Error writing frame\n");
1183 } else if (f->frametype == AST_FRAME_VIDEO) {
1184 /* Write only once */
1185 ast_writestream(others[0], f);
1186 } else if (f->frametype == AST_FRAME_DTMF) {
1187 /* stop recording with any digit */
1188 if (option_verbose > 2)
1189 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1197 if (maxtime < (end - start)) {
1198 if (option_verbose > 2)
1199 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1207 if (end == start) time(&end);
1209 if (option_verbose > 2)
1210 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1214 /* delete all the prepend files */
1215 for (x=0;x<fmtcnt;x++) {
1218 ast_closestream(others[x]);
1219 ast_filedelete(prependfile, sfmt[x]);
1224 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1226 *duration = end - start;
1232 struct ast_frame *fr;
1233 for (x=0;x<fmtcnt;x++) {
1234 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1235 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1236 if (!others[x] || !realfiles[x])
1239 ast_stream_rewind(others[x], totalsilence-200);
1241 ast_stream_rewind(others[x], 200);
1242 ast_truncstream(others[x]);
1243 /* add the original file too */
1244 while ((fr = ast_readframe(realfiles[x]))) {
1245 ast_writestream(others[x],fr);
1247 ast_closestream(others[x]);
1248 ast_closestream(realfiles[x]);
1249 ast_filerename(prependfile, recordfile, sfmt[x]);
1251 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1253 ast_filedelete(prependfile, sfmt[x]);
1257 if (ast_set_read_format(chan, rfmt)) {
1258 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1263 /* Let them know it worked */
1264 ast_streamfile(chan, "auth-thankyou", chan->language);
1265 ast_waitstream(chan, "");
1271 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1275 int x, fmtcnt=1, res=-1,outmsg=0;
1276 struct ast_frame *f;
1277 struct ast_filestream *others[MAX_OTHER_FORMATS];
1278 char *sfmt[MAX_OTHER_FORMATS];
1281 struct ast_dsp *sildet; /* silence detector dsp */
1282 int totalsilence = 0;
1284 int gotsilence = 0; /* did we timeout for silence? */
1287 /* barf if no pointer passed to store duration in */
1288 if (duration == NULL) {
1289 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1293 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1294 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1297 d = play_and_wait(chan, playfile);
1299 d = ast_streamfile(chan, "beep",chan->language);
1301 d = ast_waitstream(chan,"");
1306 fmts = ast_strdupa(fmt);
1309 strsep(&stringp, "|");
1310 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1311 sfmt[0] = ast_strdupa(fmts);
1313 while((fmt = strsep(&stringp, "|"))) {
1314 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1315 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1318 sfmt[fmtcnt++] = ast_strdupa(fmt);
1322 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1323 for (x=0;x<fmtcnt;x++) {
1324 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1325 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1332 sildet = ast_dsp_new(); /* Create the silence detector */
1334 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1337 ast_dsp_set_threshold(sildet, silencethreshold);
1339 if (maxsilence > 0) {
1340 rfmt = chan->readformat;
1341 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1343 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1349 /* Loop forever, writing the packets we read to the writer(s), until
1350 we read a # or get a hangup */
1353 res = ast_waitfor(chan, 2000);
1355 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1356 /* Try one more time in case of masq */
1357 res = ast_waitfor(chan, 2000);
1359 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1371 if (f->frametype == AST_FRAME_VOICE) {
1372 /* write each format */
1373 for (x=0;x<fmtcnt;x++) {
1374 res = ast_writestream(others[x], f);
1377 /* Silence Detection */
1378 if (maxsilence > 0) {
1380 ast_dsp_silence(sildet, f, &dspsilence);
1382 totalsilence = dspsilence;
1386 if (totalsilence > maxsilence) {
1387 /* Ended happily with silence */
1394 /* Exit on any error */
1396 ast_log(LOG_WARNING, "Error writing frame\n");
1400 } else if (f->frametype == AST_FRAME_VIDEO) {
1401 /* Write only once */
1402 ast_writestream(others[0], f);
1403 } else if (f->frametype == AST_FRAME_DTMF) {
1404 if (f->subclass == '#') {
1405 if (option_verbose > 2)
1406 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1413 if (f->subclass == '0') {
1414 /* Check for a '0' during message recording also, in case caller wants operator */
1415 if (option_verbose > 2)
1416 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1424 if (maxtime < (end - start)) {
1425 if (option_verbose > 2)
1426 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1434 if (end == start) time(&end);
1436 if (option_verbose > 2)
1437 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1442 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1445 *duration = end - start;
1447 for (x=0;x<fmtcnt;x++) {
1451 ast_stream_rewind(others[x], totalsilence-200);
1453 ast_stream_rewind(others[x], 200);
1454 ast_truncstream(others[x]);
1455 ast_closestream(others[x]);
1458 if (ast_set_read_format(chan, rfmt)) {
1459 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1464 /* Let them know recording is stopped */
1465 ast_streamfile(chan, "auth-thankyou", chan->language);
1466 ast_waitstream(chan, "");
1473 static void free_user(struct ast_vm_user *vmu)
1479 static void free_zone(struct vm_zone *z)
1484 static void run_externnotify(char *context, char *extension, int numvoicemails)
1486 char arguments[255];
1488 if(externnotify[0]) {
1489 strncpy(arguments, externnotify, sizeof(arguments));
1490 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1491 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1492 ast_safe_system(arguments);
1497 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1509 char prefile[256]="";
1510 char ext_context[256] = "";
1516 struct ast_vm_user *vmu;
1517 struct ast_vm_user svm;
1520 strncpy(tmp, ext, sizeof(tmp) - 1);
1522 context = strchr(tmp, '@');
1528 if ((vmu = find_user(&svm, context, ext))) {
1529 /* Setup pre-file if appropriate */
1530 if (strcmp(vmu->context, "default"))
1531 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1533 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1535 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1537 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1538 make_dir(dir, sizeof(dir), vmu->context, "", "");
1539 /* It's easier just to try to make it than to check for its existence */
1540 if (mkdir(dir, 0700) && (errno != EEXIST))
1541 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1542 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1543 /* It's easier just to try to make it than to check for its existence */
1544 if (mkdir(dir, 0700) && (errno != EEXIST))
1545 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1546 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1547 if (mkdir(dir, 0700) && (errno != EEXIST))
1548 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1549 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1551 /* Play the beginning intro if desired */
1552 if (strlen(prefile)) {
1553 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1554 if (ast_streamfile(chan, prefile, chan->language) > -1)
1555 res = ast_waitstream(chan, "#0*");
1557 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1558 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1561 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1567 /* On a '#' we skip the instructions */
1571 if (!res && !silent) {
1572 res = ast_streamfile(chan, INTRO, chan->language);
1574 res = ast_waitstream(chan, ecodes);
1581 ast_stopstream(chan);
1582 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1583 other than the operator -- an automated attendant or mailbox login for example */
1585 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1586 if (strlen(chan->macrocontext))
1587 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1592 /* Check for a '0' here */
1595 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1596 if (strlen(chan->macrocontext))
1597 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1603 /* Unless we're *really* silent, try to send the beep */
1604 res = ast_streamfile(chan, "beep", chan->language);
1606 res = ast_waitstream(chan, "");
1612 /* The meat of recording the message... All the announcements and beeps have been played*/
1613 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1617 make_file(fn, sizeof(fn), dir, msgnum);
1618 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1619 (chan->callerid ? chan->callerid : "Unknown"),
1620 vmu->fullname, ext, chan->name);
1621 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1624 } while(msgnum < MAXMSG);
1625 if (msgnum < MAXMSG) {
1626 /* Store information */
1627 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1628 txt = fopen(txtfile, "w+");
1630 get_date(date, sizeof(date));
1633 "; Message Information file\n"
1651 chan->callerid ? chan->callerid : "Unknown",
1652 date, (long)time(NULL));
1655 ast_log(LOG_WARNING, "Error opening text file for output\n");
1656 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1661 fd = open(txtfile, O_APPEND | O_WRONLY);
1663 txt = fdopen(fd, "a");
1665 fprintf(txt, "duration=%d\n", duration);
1670 if (duration < vmminmessage) {
1675 strsep(&stringp, "|");
1676 /* Send e-mail if applicable */
1677 if (strlen(vmu->email)) {
1678 int attach_user_voicemail = attach_voicemail;
1679 char *myserveremail = serveremail;
1680 if (vmu->attach > -1)
1681 attach_user_voicemail = vmu->attach;
1682 if (strlen(vmu->serveremail))
1683 myserveremail = vmu->serveremail;
1684 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, duration, attach_user_voicemail);
1686 if (strlen(vmu->pager)) {
1687 char *myserveremail = serveremail;
1688 if (strlen(vmu->serveremail))
1689 myserveremail = vmu->serveremail;
1690 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, duration, vmu);
1693 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1695 res = ast_waitstream(chan, "");
1696 ast_log(LOG_WARNING, "No more messages possible\n");
1699 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1703 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1704 /*Send the call to n+101 priority, where n is the current priority*/
1705 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1706 chan->priority+=100;
1708 /* Leave voicemail for someone */
1709 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
1711 /* If an external program is specified to be run after leaving a voicemail */
1712 run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
1717 static char *mbox(int id)
1745 static int count_messages(char *dir)
1749 for (x=0;x<MAXMSG;x++) {
1750 make_file(fn, sizeof(fn), dir, x);
1751 if (ast_fileexists(fn, NULL, NULL) < 1)
1757 static int say_and_wait(struct ast_channel *chan, int num)
1760 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL);
1764 static int copy(char *infile, char *outfile)
1771 if ((ifd = open(infile, O_RDONLY)) < 0) {
1772 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1775 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1776 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1781 len = read(ifd, buf, sizeof(buf));
1783 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1789 res = write(ofd, buf, len);
1791 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1803 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1810 char *dbox = mbox(box);
1812 make_file(sfn, sizeof(sfn), dir, msg);
1813 make_dir(ddir, sizeof(ddir), context, username, dbox);
1815 for (x=0;x<MAXMSG;x++) {
1816 make_file(dfn, sizeof(dfn), ddir, x);
1817 if (ast_fileexists(dfn, NULL, NULL) < 0)
1822 ast_filecopy(sfn, dfn, NULL);
1823 if (strcmp(sfn, dfn)) {
1824 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1825 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1831 static int adsi_logo(unsigned char *buf)
1834 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1835 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1839 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1847 bytes += adsi_data_mode(buf + bytes);
1848 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1851 bytes += adsi_logo(buf);
1852 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1854 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1856 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1857 bytes += adsi_data_mode(buf + bytes);
1858 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1860 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1862 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1863 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1864 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1865 bytes += adsi_voice_mode(buf + bytes, 0);
1866 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1873 bytes += adsi_logo(buf);
1874 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
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 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1881 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1882 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1883 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1884 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1885 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1886 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1889 /* Add another dot */
1891 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1892 bytes += adsi_voice_mode(buf + bytes, 0);
1894 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1895 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1899 /* These buttons we load but don't use yet */
1900 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1901 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1902 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1903 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1904 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1905 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1906 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1909 /* Add another dot */
1911 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1912 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1913 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1918 snprintf(num, sizeof(num), "%d", x);
1919 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1921 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1922 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1925 /* Add another dot */
1927 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1928 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1929 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1932 if (adsi_end_download(chan)) {
1934 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1935 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1936 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1937 bytes += adsi_voice_mode(buf + bytes, 0);
1938 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1942 bytes += adsi_download_disconnect(buf + bytes);
1943 bytes += adsi_voice_mode(buf + bytes, 0);
1944 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1946 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1951 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1952 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1954 ast_log(LOG_DEBUG, "Restarting session...\n");
1957 /* Load the session now */
1958 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1960 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1962 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1964 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1968 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1971 if (!adsi_available(chan))
1973 x = adsi_load_session(chan, adapp, adver, 1);
1977 if (adsi_load_vmail(chan, useadsi)) {
1978 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1985 static void adsi_login(struct ast_channel *chan)
1989 unsigned char keys[8];
1991 if (!adsi_available(chan))
1996 /* Set one key for next */
1997 keys[3] = ADSI_KEY_APPS + 3;
1999 bytes += adsi_logo(buf + bytes);
2000 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2001 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2002 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2003 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2004 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2005 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2006 bytes += adsi_set_keys(buf + bytes, keys);
2007 bytes += adsi_voice_mode(buf + bytes, 0);
2008 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2011 static void adsi_password(struct ast_channel *chan)
2015 unsigned char keys[8];
2017 if (!adsi_available(chan))
2022 /* Set one key for next */
2023 keys[3] = ADSI_KEY_APPS + 3;
2025 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2026 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2027 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2028 bytes += adsi_set_keys(buf + bytes, keys);
2029 bytes += adsi_voice_mode(buf + bytes, 0);
2030 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2033 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2037 unsigned char keys[8];
2040 if (!adsi_available(chan))
2044 y = ADSI_KEY_APPS + 12 + start + x;
2045 if (y > ADSI_KEY_APPS + 12 + 4)
2047 keys[x] = ADSI_KEY_SKT | y;
2049 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2053 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2054 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2055 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2056 bytes += adsi_set_keys(buf + bytes, keys);
2057 bytes += adsi_voice_mode(buf + bytes, 0);
2059 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2062 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2065 char buf[256], buf1[256], buf2[256];
2071 char datetime[21]="";
2074 unsigned char keys[8];
2078 if (!adsi_available(chan))
2081 /* Retrieve important info */
2082 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2083 f = fopen(fn2, "r");
2086 fgets(buf, sizeof(buf), f);
2090 strsep(&stringp, "=");
2091 val = strsep(&stringp, "=");
2092 if (val && strlen(val)) {
2093 if (!strcmp(buf, "callerid"))
2094 strncpy(cid, val, sizeof(cid) - 1);
2095 if (!strcmp(buf, "origdate"))
2096 strncpy(datetime, val, sizeof(datetime) - 1);
2102 /* New meaning for keys */
2104 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2109 /* No prev key, provide "Folder" instead */
2110 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2113 /* If last message ... */
2115 /* but not only message, provide "Folder" instead */
2116 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2117 bytes += adsi_voice_mode(buf + bytes, 0);
2120 /* Otherwise if only message, leave blank */
2126 ast_callerid_parse(cid, &name, &num);
2130 name = "Unknown Caller";
2132 /* If deleted, show "undeleted" */
2135 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2138 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2139 snprintf(buf1, sizeof(buf1), "%s%s", folder,
2140 strcasecmp(folder, "INBOX") ? " Messages" : "");
2141 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2143 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2144 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2145 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2146 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2147 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2148 bytes += adsi_set_keys(buf + bytes, keys);
2149 bytes += adsi_voice_mode(buf + bytes, 0);
2151 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2154 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2158 unsigned char keys[8];
2162 if (!adsi_available(chan))
2165 /* New meaning for keys */
2167 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2173 /* No prev key, provide "Folder" instead */
2174 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2177 /* If last message ... */
2179 /* but not only message, provide "Folder" instead */
2180 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2182 /* Otherwise if only message, leave blank */
2187 /* If deleted, show "undeleted" */
2189 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2192 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2193 bytes += adsi_set_keys(buf + bytes, keys);
2194 bytes += adsi_voice_mode(buf + bytes, 0);
2196 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2199 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2201 char buf[256], buf1[256], buf2[256];
2203 unsigned char keys[8];
2206 char *newm = (new == 1) ? "message" : "messages";
2207 char *oldm = (old == 1) ? "message" : "messages";
2208 if (!adsi_available(chan))
2211 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2213 strcat(buf1, " and");
2214 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2216 snprintf(buf2, sizeof(buf2), "%s.", newm);
2219 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2220 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2222 strcpy(buf1, "You have no messages.");
2225 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2226 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2227 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2230 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2234 /* Don't let them listen if there are none */
2237 bytes += adsi_set_keys(buf + bytes, keys);
2239 bytes += adsi_voice_mode(buf + bytes, 0);
2241 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2244 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2246 char buf[256], buf1[256], buf2[256];
2248 unsigned char keys[8];
2251 char *mess = (messages == 1) ? "message" : "messages";
2253 if (!adsi_available(chan))
2256 /* Original command keys */
2258 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2266 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2267 strcasecmp(folder, "INBOX") ? " folder" : "");
2270 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2272 strcpy(buf2, "no messages.");
2273 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2274 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2275 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2276 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2277 bytes += adsi_set_keys(buf + bytes, keys);
2279 bytes += adsi_voice_mode(buf + bytes, 0);
2281 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2286 static void adsi_clear(struct ast_channel *chan)
2290 if (!adsi_available(chan))
2292 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2293 bytes += adsi_voice_mode(buf + bytes, 0);
2295 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2299 static void adsi_goodbye(struct ast_channel *chan)
2304 if (!adsi_available(chan))
2306 bytes += adsi_logo(buf + bytes);
2307 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2308 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2309 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2310 bytes += adsi_voice_mode(buf + bytes, 0);
2312 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2315 static int get_folder(struct ast_channel *chan, int start)
2320 d = play_and_wait(chan, "vm-press");
2323 for (x = start; x< 5; x++) {
2324 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2326 d = play_and_wait(chan, "vm-for");
2329 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2330 d = play_and_wait(chan, fn);
2333 d = play_and_wait(chan, "vm-messages");
2336 d = ast_waitfordigit(chan, 500);
2340 d = play_and_wait(chan, "vm-tocancel");
2343 d = ast_waitfordigit(chan, 4000);
2347 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2350 res = play_and_wait(chan, fn);
2351 while (((res < '0') || (res > '9')) &&
2352 (res != '#') && (res >= 0)) {
2353 res = get_folder(chan, 0);
2358 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2364 while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2369 /* prepend a message to the current message and return */
2372 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2373 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2383 cmd = play_and_wait(chan,"vm-forwardoptions");
2385 cmd = play_and_wait(chan,"vm-starmain");
2387 cmd = ast_waitfordigit(chan,6000);
2399 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2406 struct ast_config *mif;
2410 char ext_context[256]="";
2411 int res = 0, cmd = 0;
2412 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2415 int saved_messages = 0, found = 0;
2416 int valid_extensions = 0;
2417 while (!res && !valid_extensions) {
2418 res = ast_streamfile(chan, "vm-extension", chan->language);
2421 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2423 /* start all over if no username */
2424 if (!strlen(username))
2427 s = strsep(&stringp, "*");
2428 /* start optimistic */
2429 valid_extensions = 1;
2431 /* find_user is going to malloc since we have a NULL as first argument */
2432 if ((receiver = find_user(NULL, context, s))) {
2434 vmtmp = extensions = receiver;
2436 vmtmp->next = receiver;
2441 valid_extensions = 0;
2444 s = strsep(&stringp, "*");
2446 /* break from the loop of reading the extensions */
2447 if (valid_extensions)
2449 /* invalid extension, try again */
2450 res = play_and_wait(chan, "pbx-invalid");
2452 /* check if we're clear to proceed */
2453 if (!extensions || !valid_extensions)
2456 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2458 while(!res && vmtmp) {
2459 /* if (play_and_wait(chan, "vm-savedto"))
2462 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2463 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2464 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2465 ast_log(LOG_DEBUG, sys);
2466 ast_safe_system(sys);
2468 todircount = count_messages(todir);
2469 strncpy(tmp, fmt, sizeof(tmp) - 1);
2471 while((s = strsep(&stringp, "|"))) {
2472 /* XXX This is a hack -- we should use build_filename or similar XXX */
2473 if (!strcasecmp(s, "wav49"))
2475 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2476 ast_log(LOG_DEBUG, sys);
2477 ast_safe_system(sys);
2479 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2480 ast_log(LOG_DEBUG, sys);
2481 ast_safe_system(sys);
2482 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2484 /* load the information on the source message so we can send an e-mail like a new message */
2485 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2486 if ((mif=ast_load(miffile))) {
2488 /* set callerid and duration variables */
2489 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2490 s = ast_variable_retrieve(mif, NULL, "duration");
2495 if (strlen(vmtmp->email)) {
2496 int attach_user_voicemail = attach_voicemail;
2497 char *myserveremail = serveremail;
2498 if (vmtmp->attach > -1)
2499 attach_user_voicemail = vmtmp->attach;
2500 if (strlen(vmtmp->serveremail))
2501 myserveremail = vmtmp->serveremail;
2502 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2505 if (strlen(vmtmp->pager)) {
2506 char *myserveremail = serveremail;
2507 if (strlen(vmtmp->serveremail))
2508 myserveremail = vmtmp->serveremail;
2509 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2512 ast_destroy(mif); /* or here */
2514 /* Leave voicemail for someone */
2515 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2516 run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2520 vmtmp = vmtmp->next;
2523 if (saved_messages > 0) {
2524 /* give confirmatopm that the message was saved */
2525 /* commented out since we can't forward batches yet
2526 if (saved_messages == 1)
2527 res = play_and_wait(chan, "vm-message");
2529 res = play_and_wait(chan, "vm-messages");
2531 res = play_and_wait(chan, "vm-saved"); */
2533 res = play_and_wait(chan, "vm-msgsaved");
2536 return res ? res : cmd;
2540 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2543 if ((res = ast_streamfile(chan, file, chan->language)))
2544 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2546 res = ast_waitstream(chan, AST_DIGIT_ANY);
2550 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2553 if ((res = ast_streamfile(chan, file, chan->language)))
2554 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2556 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2560 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2563 struct vm_zone *the_zone = NULL;
2567 if (sscanf(origtime,"%ld",&tin) < 1) {
2568 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2573 /* Does this user have a timezone specified? */
2574 if (strlen(vmu->zonetag)) {
2575 /* Find the zone in the list */
2579 if (!strcmp(z->name, vmu->zonetag)) {
2587 /* No internal variable parsing for now, so we'll comment it out for the time being */
2589 /* Set the DIFF_* variables */
2590 localtime_r(&t, &time_now);
2591 gettimeofday(&tv_now,NULL);
2592 tnow = tv_now.tv_sec;
2593 localtime_r(&tnow,&time_then);
2595 /* Day difference */
2596 if (time_now.tm_year == time_then.tm_year)
2597 sprintf(temp,"%d",time_now.tm_yday);
2599 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2600 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2602 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2605 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2607 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2609 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2616 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2620 char *callerid, *name;
2621 char prefile[256]="";
2624 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2625 /* 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 */
2626 if((cid == NULL)||(context == NULL))
2629 /* Strip off caller ID number from name */
2630 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2631 ast_callerid_parse(cid, &name, &callerid);
2632 if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2633 /* Check for internal contexts and only */
2634 /* say extension when the call didn't come from an internal context in the list */
2635 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2636 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2637 if((strcmp(cidinternalcontexts[i], context) == 0))
2640 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2642 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2643 if (strlen(prefile)) {
2644 /* See if we can find a recorded name for this person instead of their extension number */
2645 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2646 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2648 res = wait_file2(chan, vms, "vm-from");
2649 res = ast_streamfile(chan, prefile, chan->language) > -1;
2650 res = ast_waitstream(chan, "");
2652 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2653 /* BB: Say "from extension" as one saying to sound smoother */
2655 res = wait_file2(chan, vms, "vm-from-extension");
2656 res = ast_say_digit_str(chan, callerid, "", chan->language);
2663 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2664 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2666 res = wait_file2(chan, vms, "vm-from-phonenumber");
2667 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2671 /* Number unknown */
2672 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2674 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2675 res = wait_file2(chan, vms, "vm-unknown-caller");
2680 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2683 char filename[256],*origtime, *cid, *context;
2684 struct ast_config *msg_cfg;
2687 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2688 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2690 res = wait_file2(chan, vms, "vm-first");
2691 else if (msg == vms->lastmsg)
2692 res = wait_file2(chan, vms, "vm-last");
2694 res = wait_file2(chan, vms, "vm-message");
2695 if (msg && (msg != vms->lastmsg)) {
2697 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2701 /* Retrieve info from VM attribute file */
2703 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2704 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2705 msg_cfg = ast_load(filename);
2707 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2711 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2714 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2716 context = ast_variable_retrieve(msg_cfg, "message", "context");
2717 if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2718 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2720 if ((!res)&&(vmu->envelope))
2721 res = play_message_datetime(chan, vmu, origtime, filename);
2723 if ((!res)&&(vmu->saycid))
2724 res = play_message_callerid(chan, vms, cid, context, 0);
2726 ast_destroy(msg_cfg);
2729 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2730 vms->heard[msg] = 1;
2731 res = wait_file(chan, vms, vms->fn);
2736 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2738 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2739 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2740 vms->lastmsg = count_messages(vms->curdir) - 1;
2741 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2744 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2747 char ntxt[256] = "";
2749 if (vms->lastmsg > -1) {
2750 /* Get the deleted messages fixed */
2752 for (x=0;x < MAXMSG;x++) {
2753 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2754 /* Save this message. It's not in INBOX or hasn't been heard */
2755 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2756 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2759 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2760 if (strcmp(vms->fn, vms->fn2)) {
2761 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2762 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2763 ast_filerename(vms->fn, vms->fn2, NULL);
2766 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2767 /* Move to old folder before deleting */
2768 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2771 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2772 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2773 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2778 memset(vms->deleted, 0, sizeof(vms->deleted));
2779 memset(vms->heard, 0, sizeof(vms->heard));
2782 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2784 /* Introduce messages they have */
2786 res = play_and_wait(chan, "vm-youhave");
2788 if (vms->newmessages) {
2789 res = say_and_wait(chan, vms->newmessages);
2791 res = play_and_wait(chan, "vm-INBOX");
2792 if (vms->oldmessages && !res)
2793 res = play_and_wait(chan, "vm-and");
2795 if ((vms->newmessages == 1))
2796 res = play_and_wait(chan, "vm-message");
2798 res = play_and_wait(chan, "vm-messages");
2802 if (!res && vms->oldmessages) {
2803 res = say_and_wait(chan, vms->oldmessages);
2805 res = play_and_wait(chan, "vm-Old");
2807 if (vms->oldmessages == 1)
2808 res = play_and_wait(chan, "vm-message");
2810 res = play_and_wait(chan, "vm-messages");
2814 if (!vms->oldmessages && !vms->newmessages) {
2815 res = play_and_wait(chan, "vm-no");
2817 res = play_and_wait(chan, "vm-messages");
2824 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2827 /* Play instructions and wait for new command */
2829 if (vms->starting) {
2830 if (vms->lastmsg > -1) {
2831 res = play_and_wait(chan, "vm-onefor");
2833 res = play_and_wait(chan, vms->vmbox);
2835 res = play_and_wait(chan, "vm-messages");
2838 res = play_and_wait(chan, "vm-opts");
2841 res = play_and_wait(chan, "vm-prev");
2843 res = play_and_wait(chan, "vm-advopts");
2845 res = play_and_wait(chan, "vm-repeat");
2846 if (!res && (vms->curmsg != vms->lastmsg))
2847 res = play_and_wait(chan, "vm-next");
2849 if (!vms->deleted[vms->curmsg])
2850 res = play_and_wait(chan, "vm-delete");
2852 res = play_and_wait(chan, "vm-undelete");
2854 res = play_and_wait(chan, "vm-toforward");
2856 res = play_and_wait(chan, "vm-savemessage");
2860 res = play_and_wait(chan, "vm-helpexit");
2862 res = ast_waitfordigit(chan, 6000);
2865 if (vms->repeats > 2) {
2873 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2878 char newpassword[80] = "";
2879 char newpassword2[80] = "";
2880 char prefile[256]="";
2884 if (adsi_available(chan))
2886 bytes += adsi_logo(buf + bytes);
2887 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2888 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2889 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2890 bytes += adsi_voice_mode(buf + bytes, 0);
2891 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2893 while((cmd >= 0) && (cmd != 't')) {
2898 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2899 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration);
2902 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2903 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration);
2906 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2907 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration);
2910 newpassword[1] = '\0';
2911 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2914 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2917 newpassword2[1] = '\0';
2918 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2922 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2925 if (strcmp(newpassword, newpassword2)) {
2926 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2927 cmd = play_and_wait(chan, "vm-mismatch");
2930 vm_change_password(vmu,newpassword);
2931 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2932 cmd = play_and_wait(chan,"vm-passchanged");
2938 cmd = play_and_wait(chan,"vm-options");
2940 cmd = ast_waitfordigit(chan,6000);
2952 static int vm_execmain(struct ast_channel *chan, void *data)
2954 /* XXX This is, admittedly, some pretty horrendus code. For some
2955 reason it just seemed a lot easier to do with GOTO's. I feel