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>
30 #include <asterisk/utils.h>
39 #include <sys/types.h>
43 /* we define USESQLVM when we have MySQL or POSTGRES */
45 #include <mysql/mysql.h>
51 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
53 #include <postgresql/libpq-fe.h>
58 static inline int sql_init(void) { return 0; }
59 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 /* Syntaxes supported, not really language codes.
97 German requires the following additional soundfile:
100 Spanish requires the following additional soundfile:
103 Dutch, Portuguese & Spanish require the following additional soundfiles:
104 vm-INBOXs singular of 'new'
105 vm-Olds singular of 'old/heard/read'
124 unsigned char iobuf[BASEMAXINLINE];
127 /* Structure for linked list of users */
129 char context[80]; /* Voicemail context */
130 char mailbox[80]; /* Mailbox id, unique within vm context */
131 char password[80]; /* Secret pin code, numbers only */
132 char fullname[80]; /* Full name, for directory app */
133 char email[80]; /* E-mail address */
134 char pager[80]; /* E-mail address to pager (no attachment) */
135 char serveremail[80]; /* From: Mail address */
136 char mailcmd[160]; /* Configurable mail command */
137 char language[MAX_LANGUAGE]; /* Config: Language setting */
138 char zonetag[80]; /* Time zone */
149 struct ast_vm_user *next;
155 char msg_format[512];
156 struct vm_zone *next;
175 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
176 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
177 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);
178 static int vm_delete(char *file);
182 static char *tdesc = "Comedian Mail (Voicemail System)";
184 static char *adapp = "\x00\x00\x00\x0F";
186 static char *adsec = "\x9B\xDB\xF7\xAC";
188 static char *addesc = "Comedian Mail";
190 static int adver = 1;
192 static char *synopsis_vm =
193 "Leave a voicemail message";
195 static char *descrip_vm =
196 " VoiceMail([s|u|b]extension[@context][&extension[@context]][...]): Leaves"
197 "voicemail for a given extension (must be configured in voicemail.conf).\n"
198 " If the extension is preceded by \n"
199 "* 's' then instructions for leaving the message will be skipped.\n"
200 "* 'u' then the \"unavailable\" message will be played.\n"
201 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
202 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
203 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
204 "extension 'o' in the current context.\n"
205 "If the caller presses '*' during the prompt, the call jumps to\n"
206 "extension 'a' in the current context.\n"
207 "If the requested mailbox does not exist, and there exists a priority\n"
208 "n + 101, then that priority will be taken next.\n"
209 "When multiple mailboxes are specified, the unavailable or busy message\n"
210 "will be taken from the first mailbox specified.\n"
211 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
212 "Otherwise, it returns 0.\n";
214 static char *synopsis_vmain =
215 "Enter voicemail system";
217 static char *descrip_vmain =
218 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
219 "for the checking of voicemail. The mailbox can be passed as the option,\n"
220 "which will stop the voicemail system from prompting the user for the mailbox.\n"
221 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
222 "a context is specified, logins are considered in that voicemail context only.\n"
223 "Returns -1 if the user hangs up or 0 otherwise.\n";
225 static char *synopsis_vm_box_exists =
226 "Check if vmbox exists";
228 static char *descrip_vm_box_exists =
229 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
230 "if the specified voice mailbox exists.\n";
233 /* Leave a message */
234 static char *capp = "VoiceMail2";
235 static char *app = "VoiceMail";
237 /* Check mail, control, etc */
238 static char *capp2 = "VoiceMailMain2";
239 static char *app2 = "VoiceMailMain";
241 static char *app3 = "MailboxExists";
243 AST_MUTEX_DEFINE_STATIC(vmlock);
244 struct ast_vm_user *users;
245 struct ast_vm_user *usersl;
246 struct vm_zone *zones = NULL;
247 struct vm_zone *zonesl = NULL;
248 static int attach_voicemail;
249 static int maxsilence;
250 static int silencethreshold = 128;
251 static char serveremail[80];
252 static char mailcmd[160]; /* Configurable mail cmd */
253 static char externnotify[160];
255 static char vmfmts[80];
256 static int vmminmessage;
257 static int vmmaxmessage;
260 static int maxlogins;
264 static int saycidinfo;
266 static char dialcontext[80];
267 static char callcontext[80];
268 static char exitcontext[80];
270 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
273 static char *emailbody = NULL;
274 static int pbxskip = 0;
275 static char *emailsubject = NULL;
276 static char fromstring[100];
277 static char emailtitle[100];
278 static char charset[32] = "ISO-8859-1";
285 static void populate_defaults(struct ast_vm_user *vmu)
297 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
299 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
301 strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
304 static void apply_options(struct ast_vm_user *vmu, char *options)
306 /* Destructively Parse options and apply */
307 char *stringp = ast_strdupa(options);
311 while((s = strsep(&stringp, "|"))) {
313 if ((var = strsep(&value, "=")) && value) {
314 if (!strcasecmp(var, "attach")) {
319 } else if (!strcasecmp(var, "serveremail")) {
320 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
321 } else if (!strcasecmp(var, "language")) {
322 strncpy(vmu->language, value, sizeof(vmu->language) - 1);
323 } else if (!strcasecmp(var, "tz")) {
324 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
325 } else if (!strcasecmp(var, "delete")) {
326 vmu->delete = ast_true(value);
327 } else if (!strcasecmp(var, "saycid")){
332 } else if (!strcasecmp(var, "review")){
337 } else if (!strcasecmp(var, "operator")){
342 } else if (!strcasecmp(var, "envelope")){
347 } else if (!strcasecmp(var, "callback")) {
348 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
349 } else if (!strcasecmp(var, "dialout")) {
350 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
351 } else if (!strcasecmp(var, "exitcontext")) {
352 strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
361 #include "mysql-vm-routines.h"
368 AST_MUTEX_DEFINE_STATIC(postgreslock);
370 static int sql_init(void)
372 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
373 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
375 dbhandler=PQconnectdb(dboption);
376 if (PQstatus(dbhandler) == CONNECTION_BAD) {
377 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
380 /* fprintf(stderr,"postgres login OK\n"); */
384 static void sql_close(void)
390 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
398 char options[160] = "";
399 struct ast_vm_user *retval;
401 retval=malloc(sizeof(struct ast_vm_user));
403 /* fprintf(stderr,"postgres find_user:\n"); */
406 memset(retval, 0, sizeof(struct ast_vm_user));
409 strcpy(retval->mailbox, mailbox);
412 strcpy(retval->context, context);
416 strcpy(retval->context, "default");
418 populate_defaults(retval);
419 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
421 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
422 ast_mutex_lock(&postgreslock);
423 PGSQLres=PQexec(dbhandler,query);
424 if (PGSQLres!=NULL) {
425 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
426 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
427 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
429 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
432 ast_mutex_unlock(&postgreslock);
436 numFields = PQnfields(PGSQLres);
437 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
438 if (PQntuples(PGSQLres) != 1) {
439 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
441 ast_mutex_unlock(&postgreslock);
445 for (i=0; i<numFields; i++) {
446 fname = PQfname(PGSQLres,i);
447 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
448 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
449 } else if (!strcmp(fname, "fullname")) {
450 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
451 } else if (!strcmp(fname, "email")) {
452 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
453 } else if (!strcmp(fname, "pager")) {
454 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
455 } else if (!strcmp(fname, "options")) {
456 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
457 apply_options(retval, options);
462 ast_mutex_unlock(&postgreslock);
466 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
467 ast_mutex_unlock(&postgreslock);
472 } /* malloc() retval */
477 static void vm_change_password(struct ast_vm_user *vmu, char *password)
482 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);
484 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
486 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
487 ast_mutex_lock(&postgreslock);
488 PQexec(dbhandler, query);
489 strcpy(vmu->password, password);
490 ast_mutex_unlock(&postgreslock);
493 static void reset_user_pw(char *context, char *mailbox, char *password)
498 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
500 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
502 ast_mutex_lock(&postgreslock);
503 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
504 PQexec(dbhandler, query);
505 ast_mutex_unlock(&postgreslock);
508 #endif /* Postgres */
512 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
514 /* This function could be made to generate one from a database, too */
515 struct ast_vm_user *vmu=NULL, *cur;
516 ast_mutex_lock(&vmlock);
519 if ((!context || !strcasecmp(context, cur->context)) &&
520 (!strcasecmp(mailbox, cur->mailbox)))
528 /* Make a copy, so that on a reload, we have no race */
529 vmu = malloc(sizeof(struct ast_vm_user));
531 memcpy(vmu, cur, sizeof(struct ast_vm_user));
539 ast_mutex_unlock(&vmlock);
543 static int reset_user_pw(char *context, char *mailbox, char *newpass)
545 /* This function could be made to generate one from a database, too */
546 struct ast_vm_user *cur;
548 ast_mutex_lock(&vmlock);
551 if ((!context || !strcasecmp(context, cur->context)) &&
552 (!strcasecmp(mailbox, cur->mailbox)))
557 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
560 ast_mutex_unlock(&vmlock);
564 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
566 /* There's probably a better way of doing this. */
567 /* That's why I've put the password change in a separate function. */
568 /* This could also be done with a database function */
575 char currcontext[256] ="";
576 char tmpin[AST_CONFIG_MAX_PATH];
577 char tmpout[AST_CONFIG_MAX_PATH];
578 char *user, *pass, *rest, *trim, *tempcontext;
580 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
581 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
582 configin = fopen((char *)tmpin,"r");
584 configout = fopen((char *)tmpout,"w+");
587 if(!configin || !configout) {
591 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
595 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
599 while (!feof(configin)) {
600 /* Read in the line */
601 fgets(inbuf, sizeof(inbuf), configin);
603 if (!feof(configin)) {
604 /* Make a backup of it */
605 memcpy(orig, inbuf, sizeof(orig));
606 /* Strip trailing \n and comment */
607 inbuf[strlen(inbuf) - 1] = '\0';
608 user = strchr(inbuf, ';');
614 /* check for '[' (opening of context name ) */
615 tempcontext = strchr(user, '[');
617 strncpy(currcontext, tempcontext +1,
618 sizeof(currcontext) - 1);
619 /* now check for ']' */
620 tempcontext = strchr(currcontext, ']');
624 currcontext[0] = '\0';
626 pass = strchr(user, '=');
629 while(*trim && *trim < 33) {
639 while(*pass && *pass < 33)
643 rest = strchr(pass,',');
651 /* Compare user, pass AND context */
652 if (user && *user && !strcmp(user, vmu->mailbox) &&
653 pass && *pass && !strcmp(pass, vmu->password) &&
654 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
655 /* This is the line */
657 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
659 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
662 /* Put it back like it was */
663 fprintf(configout, orig);
670 unlink((char *)tmpin);
671 rename((char *)tmpout,(char *)tmpin);
672 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
673 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
677 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
679 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
682 static int make_file(char *dest, int len, char *dir, int num)
684 return snprintf(dest, len, "%s/msg%04d", dir, num);
688 inbuf(struct baseio *bio, FILE *fi)
695 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
710 inchar(struct baseio *bio, FILE *fi)
712 if(bio->iocp>=bio->iolen)
716 return bio->iobuf[bio->iocp++];
720 ochar(struct baseio *bio, int c, FILE *so)
722 if(bio->linelength>=BASELINELEN) {
723 if(fputs(eol,so)==EOF)
729 if(putc(((unsigned char)c),so)==EOF)
737 static int base_encode(char *filename, FILE *so)
739 unsigned char dtable[BASEMAXINLINE];
744 memset(&bio, 0, sizeof(bio));
745 bio.iocp = BASEMAXINLINE;
747 if ( !(fi = fopen(filename, "rb"))) {
748 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
756 dtable[26+i+9]= 'j'+i;
760 dtable[26+i+18]= 's'+i;
769 unsigned char igroup[3],ogroup[4];
772 igroup[0]= igroup[1]= igroup[2]= 0;
775 if ( (c = inchar(&bio, fi)) == EOF) {
780 igroup[n]= (unsigned char)c;
784 ogroup[0]= dtable[igroup[0]>>2];
785 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
786 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
787 ogroup[3]= dtable[igroup[2]&0x3F];
797 ochar(&bio, ogroup[i], so);
801 if(fputs(eol,so)==EOF)
809 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)
811 /* Prepare variables for substition in email body and subject */
812 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
813 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
814 sprintf(passdata,"%d",msgnum);
815 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
816 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
817 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
818 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
821 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)
831 char tmp[80] = "/tmp/astmail-XXXXXX";
835 struct vm_zone *the_zone = NULL;
836 if (vmu && ast_strlen_zero(vmu->email)) {
837 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
840 if (!strcmp(format, "wav49"))
842 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
843 /* Make a temporary file instead of piping directly to sendmail, in case the mail
847 p = fdopen(pfd, "w");
854 gethostname(host, sizeof(host));
855 if (strchr(srcemail, '@'))
856 strncpy(who, srcemail, sizeof(who)-1);
858 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
860 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
863 /* Does this user have a timezone specified? */
864 if (!ast_strlen_zero(vmu->zonetag)) {
865 /* Find the zone in the list */
869 if (!strcmp(z->name, vmu->zonetag)) {
878 ast_localtime(&t,&tm,the_zone->timezone);
880 ast_localtime(&t,&tm,NULL);
881 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
882 fprintf(p, "Date: %s\n", date);
885 struct ast_channel *ast = ast_channel_alloc(0);
888 int vmlen = strlen(fromstring)*3 + 200;
889 if ((passdata = alloca(vmlen))) {
890 memset(passdata, 0, vmlen);
891 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
892 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
893 fprintf(p, "From: %s <%s>\n",passdata,who);
894 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
895 ast_channel_free(ast);
896 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
898 fprintf(p, "From: Asterisk PBX <%s>\n", who);
899 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
902 struct ast_channel *ast = ast_channel_alloc(0);
905 int vmlen = strlen(emailsubject)*3 + 200;
906 if ((passdata = alloca(vmlen))) {
907 memset(passdata, 0, vmlen);
908 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
909 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
910 fprintf(p, "Subject: %s\n",passdata);
911 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
912 ast_channel_free(ast);
913 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
917 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
922 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
924 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
925 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
926 fprintf(p, "MIME-Version: 1.0\n");
927 if (attach_user_voicemail) {
928 /* Something unique. */
929 snprintf(bound, sizeof(bound), "voicemail_%d%s%d", msgnum, mailbox, getpid());
931 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
933 fprintf(p, "--%s\n", bound);
935 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
936 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
938 struct ast_channel *ast = ast_channel_alloc(0);
941 int vmlen = strlen(emailbody)*3 + 200;
942 if ((passdata = alloca(vmlen))) {
943 memset(passdata, 0, vmlen);
944 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
945 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
946 fprintf(p, "%s\n",passdata);
947 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
948 ast_channel_free(ast);
949 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
951 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
953 "in mailbox %s from %s, on %s so you might\n"
954 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
955 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
957 if (attach_user_voicemail) {
958 fprintf(p, "--%s\n", bound);
959 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
960 fprintf(p, "Content-Transfer-Encoding: base64\n");
961 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
962 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
964 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
965 base_encode(fname, p);
966 fprintf(p, "\n\n--%s--\n.\n", bound);
969 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
970 ast_safe_system(tmp2);
971 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
973 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
979 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, int duration, struct ast_vm_user *vmu)
987 char tmp[80] = "/tmp/astmail-XXXXXX";
991 struct vm_zone *the_zone = NULL;
995 p = fdopen(pfd, "w");
1003 gethostname(host, sizeof(host));
1004 if (strchr(srcemail, '@'))
1005 strncpy(who, srcemail, sizeof(who)-1);
1007 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1009 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1012 /* Does this user have a timezone specified? */
1013 if (!ast_strlen_zero(vmu->zonetag)) {
1014 /* Find the zone in the list */
1018 if (!strcmp(z->name, vmu->zonetag)) {
1027 ast_localtime(&t,&tm,the_zone->timezone);
1029 ast_localtime(&t,&tm,NULL);
1031 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1032 fprintf(p, "Date: %s\n", date);
1033 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1034 fprintf(p, "To: %s\n", pager);
1035 fprintf(p, "Subject: New VM\n\n");
1036 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1037 fprintf(p, "New %s long msg in box %s\n"
1038 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
1040 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1041 ast_safe_system(tmp2);
1042 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
1044 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1050 static int get_date(char *s, int len)
1055 localtime_r(&t,&tm);
1056 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1059 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1063 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1064 if (ast_fileexists(fn, NULL, NULL) > 0) {
1065 res = ast_streamfile(chan, fn, chan->language);
1068 res = ast_waitstream(chan, ecodes);
1072 res = ast_streamfile(chan, "vm-theperson", chan->language);
1075 res = ast_waitstream(chan, ecodes);
1078 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1083 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1085 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1088 res = ast_waitstream(chan, ecodes);
1092 static int play_and_wait(struct ast_channel *chan, char *fn)
1095 d = ast_streamfile(chan, fn, chan->language);
1098 d = ast_waitstream(chan, AST_DIGIT_ANY);
1099 ast_stopstream(chan);
1103 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep)
1107 int x, fmtcnt=1, res=-1,outmsg=0;
1108 struct ast_frame *f;
1109 struct ast_filestream *others[MAX_OTHER_FORMATS];
1110 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
1111 char *sfmt[MAX_OTHER_FORMATS];
1114 struct ast_dsp *sildet; /* silence detector dsp */
1115 int totalsilence = 0;
1117 int gotsilence = 0; /* did we timeout for silence? */
1119 char prependfile[80];
1121 /* barf if no pointer passed to store duration in */
1122 if (duration == NULL) {
1123 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
1127 ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1128 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1130 if (playfile || beep) {
1132 d = play_and_wait(chan, playfile);
1134 d = ast_streamfile(chan, "beep",chan->language);
1136 d = ast_waitstream(chan,"");
1140 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
1141 strcat(prependfile, "-prepend");
1143 fmts = ast_strdupa(fmt);
1146 strsep(&stringp, "|");
1147 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1148 sfmt[0] = ast_strdupa(fmts);
1150 while((fmt = strsep(&stringp, "|"))) {
1151 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1152 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1155 sfmt[fmtcnt++] = ast_strdupa(fmt);
1159 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1160 for (x=0;x<fmtcnt;x++) {
1161 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1162 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1168 sildet = ast_dsp_new(); /* Create the silence detector */
1170 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1173 ast_dsp_set_threshold(sildet, silencethreshold);
1175 if (maxsilence > 0) {
1176 rfmt = chan->readformat;
1177 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1179 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1185 /* Loop forever, writing the packets we read to the writer(s), until
1186 we read a # or get a hangup */
1189 res = ast_waitfor(chan, 2000);
1191 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1192 /* Try one more time in case of masq */
1193 res = ast_waitfor(chan, 2000);
1195 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1207 if (f->frametype == AST_FRAME_VOICE) {
1208 /* write each format */
1209 for (x=0;x<fmtcnt;x++) {
1212 res = ast_writestream(others[x], f);
1215 /* Silence Detection */
1216 if (maxsilence > 0) {
1218 ast_dsp_silence(sildet, f, &dspsilence);
1220 totalsilence = dspsilence;
1224 if (totalsilence > maxsilence) {
1225 /* Ended happily with silence */
1232 /* Exit on any error */
1234 ast_log(LOG_WARNING, "Error writing frame\n");
1238 } else if (f->frametype == AST_FRAME_VIDEO) {
1239 /* Write only once */
1240 ast_writestream(others[0], f);
1241 } else if (f->frametype == AST_FRAME_DTMF) {
1242 /* stop recording with any digit */
1243 if (option_verbose > 2)
1244 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1252 if (maxtime < (end - start)) {
1253 if (option_verbose > 2)
1254 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1263 if (end == start) time(&end);
1265 if (option_verbose > 2)
1266 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1270 /* delete all the prepend files */
1271 for (x=0;x<fmtcnt;x++) {
1274 ast_closestream(others[x]);
1275 ast_filedelete(prependfile, sfmt[x]);
1280 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1282 *duration = end - start;
1288 struct ast_frame *fr;
1289 for (x=0;x<fmtcnt;x++) {
1290 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1291 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1292 if (!others[x] || !realfiles[x])
1295 ast_stream_rewind(others[x], totalsilence-200);
1297 ast_stream_rewind(others[x], 200);
1298 ast_truncstream(others[x]);
1299 /* add the original file too */
1300 while ((fr = ast_readframe(realfiles[x]))) {
1301 ast_writestream(others[x],fr);
1303 ast_closestream(others[x]);
1304 ast_closestream(realfiles[x]);
1305 ast_filerename(prependfile, recordfile, sfmt[x]);
1307 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1309 ast_filedelete(prependfile, sfmt[x]);
1313 if (ast_set_read_format(chan, rfmt)) {
1314 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1319 /* Let them know it worked */
1320 ast_streamfile(chan, "auth-thankyou", chan->language);
1321 ast_waitstream(chan, "");
1327 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1331 int x, fmtcnt=1, res=-1,outmsg=0;
1332 struct ast_frame *f;
1333 struct ast_filestream *others[MAX_OTHER_FORMATS];
1334 char *sfmt[MAX_OTHER_FORMATS];
1337 struct ast_dsp *sildet; /* silence detector dsp */
1338 int totalsilence = 0;
1340 int gotsilence = 0; /* did we timeout for silence? */
1343 /* barf if no pointer passed to store duration in */
1344 if (duration == NULL) {
1345 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1349 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1350 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1353 d = play_and_wait(chan, playfile);
1355 d = ast_streamfile(chan, "beep",chan->language);
1357 d = ast_waitstream(chan,"");
1362 fmts = ast_strdupa(fmt);
1365 strsep(&stringp, "|");
1366 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1367 sfmt[0] = ast_strdupa(fmts);
1369 while((fmt = strsep(&stringp, "|"))) {
1370 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1371 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1374 sfmt[fmtcnt++] = ast_strdupa(fmt);
1378 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1379 for (x=0;x<fmtcnt;x++) {
1380 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1381 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1388 sildet = ast_dsp_new(); /* Create the silence detector */
1390 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1393 ast_dsp_set_threshold(sildet, silencethreshold);
1395 if (maxsilence > 0) {
1396 rfmt = chan->readformat;
1397 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1399 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1405 /* Loop forever, writing the packets we read to the writer(s), until
1406 we read a # or get a hangup */
1409 res = ast_waitfor(chan, 2000);
1411 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1412 /* Try one more time in case of masq */
1413 res = ast_waitfor(chan, 2000);
1415 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1427 if (f->frametype == AST_FRAME_VOICE) {
1428 /* write each format */
1429 for (x=0;x<fmtcnt;x++) {
1430 res = ast_writestream(others[x], f);
1433 /* Silence Detection */
1434 if (maxsilence > 0) {
1436 ast_dsp_silence(sildet, f, &dspsilence);
1438 totalsilence = dspsilence;
1442 if (totalsilence > maxsilence) {
1443 /* Ended happily with silence */
1450 /* Exit on any error */
1452 ast_log(LOG_WARNING, "Error writing frame\n");
1456 } else if (f->frametype == AST_FRAME_VIDEO) {
1457 /* Write only once */
1458 ast_writestream(others[0], f);
1459 } else if (f->frametype == AST_FRAME_DTMF) {
1460 if (f->subclass == '#') {
1461 if (option_verbose > 2)
1462 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1469 if (f->subclass == '0') {
1470 /* Check for a '0' during message recording also, in case caller wants operator */
1471 if (option_verbose > 2)
1472 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1480 if (maxtime < (end - start)) {
1481 if (option_verbose > 2)
1482 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1491 if (end == start) time(&end);
1493 if (option_verbose > 2)
1494 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1499 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1502 *duration = end - start;
1504 for (x=0;x<fmtcnt;x++) {
1508 ast_stream_rewind(others[x], totalsilence-200);
1510 ast_stream_rewind(others[x], 200);
1511 ast_truncstream(others[x]);
1512 ast_closestream(others[x]);
1515 if (ast_set_read_format(chan, rfmt)) {
1516 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1521 /* Let them know recording is stopped */
1522 ast_streamfile(chan, "auth-thankyou", chan->language);
1523 ast_waitstream(chan, "");
1530 static void free_user(struct ast_vm_user *vmu)
1536 static void free_zone(struct vm_zone *z)
1541 static char *mbox(int id)
1569 static int copy(char *infile, char *outfile)
1577 #ifdef HARDLINK_WHEN_POSSIBLE
1578 /* Hard link if possible; saves disk space & is faster */
1579 if (link(infile, outfile)) {
1581 if ((ifd = open(infile, O_RDONLY)) < 0) {
1582 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1585 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1586 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1591 len = read(ifd, buf, sizeof(buf));
1593 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1599 res = write(ofd, buf, len);
1601 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1611 #ifdef HARDLINK_WHEN_POSSIBLE
1613 /* Hard link succeeded */
1619 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid);
1621 static void copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt)
1623 char fromdir[256], todir[256], frompath[256], topath[256];
1624 char *frombox = mbox(imbox);
1627 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1629 make_dir(todir, sizeof(todir), recip->context, "", "");
1630 /* It's easier just to try to make it than to check for its existence */
1631 if (mkdir(todir, 0700) && (errno != EEXIST))
1632 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1633 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1634 /* It's easier just to try to make it than to check for its existence */
1635 if (mkdir(todir, 0700) && (errno != EEXIST))
1636 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1637 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1638 if (mkdir(todir, 0700) && (errno != EEXIST))
1639 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1641 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1642 make_file(frompath, sizeof(frompath), fromdir, msgnum);
1645 make_file(topath, sizeof(topath), todir, recipmsgnum);
1646 if (ast_fileexists(topath, NULL, chan->language) <= 0)
1649 } while(recipmsgnum < MAXMSG);
1650 if (recipmsgnum < MAXMSG) {
1651 char frompath2[256],topath2[256];
1652 ast_filecopy(frompath, topath, NULL);
1653 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1654 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1655 copy(frompath2, topath2);
1657 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1660 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->callerid);
1663 static void run_externnotify(char *context, char *extension, int numvoicemails)
1665 char arguments[255];
1667 if(externnotify[0]) {
1668 strncpy(arguments, externnotify, sizeof(arguments));
1669 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1670 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1671 ast_safe_system(arguments);
1676 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1689 char prefile[256]="";
1690 char ext_context[256] = "";
1693 char ecodes[16] = "#";
1694 char tmp[256] = "", *tmpptr;
1695 struct ast_vm_user *vmu;
1696 struct ast_vm_user svm;
1698 strncpy(tmp, ext, sizeof(tmp) - 1);
1700 context = strchr(tmp, '@');
1704 tmpptr = strchr(context, '&');
1706 tmpptr = strchr(ext, '&');
1714 if ((vmu = find_user(&svm, context, ext))) {
1715 /* Setup pre-file if appropriate */
1716 if (strcmp(vmu->context, "default"))
1717 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1719 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1721 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1723 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1724 make_dir(dir, sizeof(dir), vmu->context, "", "");
1725 /* It's easier just to try to make it than to check for its existence */
1726 if (mkdir(dir, 0700) && (errno != EEXIST))
1727 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1728 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1729 /* It's easier just to try to make it than to check for its existence */
1730 if (mkdir(dir, 0700) && (errno != EEXIST))
1731 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1732 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1733 if (mkdir(dir, 0700) && (errno != EEXIST))
1734 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1736 /* Check current or macro-calling context for special extensions */
1737 if (ast_exists_extension(chan, chan->context, "o", 1, chan->callerid))
1738 strcat(ecodes, "0");
1739 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->callerid)) {
1740 strcat(ecodes, "0");
1744 if (ast_exists_extension(chan, chan->context, "a", 1, chan->callerid))
1745 strcat(ecodes, "*");
1746 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->callerid)) {
1747 strcat(ecodes, "*");
1751 /* Play the beginning intro if desired */
1752 if (!ast_strlen_zero(prefile)) {
1753 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1754 if (ast_streamfile(chan, prefile, chan->language) > -1)
1755 res = ast_waitstream(chan, ecodes);
1757 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1758 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1761 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1767 /* On a '#' we skip the instructions */
1771 if (!res && !silent) {
1772 res = ast_streamfile(chan, INTRO, chan->language);
1774 res = ast_waitstream(chan, ecodes);
1781 ast_stopstream(chan);
1782 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1783 other than the operator -- an automated attendant or mailbox login for example */
1785 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1786 if (!ast_strlen_zero(vmu->exit)) {
1787 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1788 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
1789 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1795 /* Check for a '0' here */
1798 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1799 if (!ast_strlen_zero(vmu->exit)) {
1800 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1801 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
1802 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1809 /* Unless we're *really* silent, try to send the beep */
1810 res = ast_streamfile(chan, "beep", chan->language);
1812 res = ast_waitstream(chan, "");
1818 /* The meat of recording the message... All the announcements and beeps have been played*/
1819 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1820 if (!ast_strlen_zero(fmt)) {
1823 make_file(fn, sizeof(fn), dir, msgnum);
1824 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1827 } while(msgnum < MAXMSG);
1828 if (msgnum < MAXMSG) {
1829 /* Store information */
1830 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1831 txt = fopen(txtfile, "w+");
1833 get_date(date, sizeof(date));
1836 "; Message Information file\n"
1854 chan->callerid ? chan->callerid : "Unknown",
1855 date, (long)time(NULL));
1858 ast_log(LOG_WARNING, "Error opening text file for output\n");
1859 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1864 fd = open(txtfile, O_APPEND | O_WRONLY);
1866 txt = fdopen(fd, "a");
1868 fprintf(txt, "duration=%d\n", duration);
1873 if (duration < vmminmessage) {
1877 /* Are there to be more recipients of this message? */
1879 struct ast_vm_user recipu, *recip;
1880 char *exten, *context;
1882 exten = strsep(&tmpptr, "&");
1883 context = strchr(exten, '@');
1888 if ((recip = find_user(&recipu, context, exten))) {
1889 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
1893 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->callerid);
1895 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1897 res = ast_waitstream(chan, "");
1898 ast_log(LOG_WARNING, "No more messages possible\n");
1901 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1905 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1906 /*Send the call to n+101 priority, where n is the current priority*/
1907 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1908 chan->priority+=100;
1914 static int count_messages(char *dir)
1918 for (x=0;x<MAXMSG;x++) {
1919 make_file(fn, sizeof(fn), dir, x);
1920 if (ast_fileexists(fn, NULL, NULL) < 1)
1926 static int say_and_wait(struct ast_channel *chan, int num, char *language)
1929 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
1933 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1940 char *dbox = mbox(box);
1942 make_file(sfn, sizeof(sfn), dir, msg);
1943 make_dir(ddir, sizeof(ddir), context, username, dbox);
1945 for (x=0;x<MAXMSG;x++) {
1946 make_file(dfn, sizeof(dfn), ddir, x);
1947 if (ast_fileexists(dfn, NULL, NULL) < 0)
1952 ast_filecopy(sfn, dfn, NULL);
1953 if (strcmp(sfn, dfn)) {
1954 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1955 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1961 static int adsi_logo(unsigned char *buf)
1964 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1965 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1969 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1977 bytes += adsi_data_mode(buf + bytes);
1978 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1981 bytes += adsi_logo(buf);
1982 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1984 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1986 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1987 bytes += adsi_data_mode(buf + bytes);
1988 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1990 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1992 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1993 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1994 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1995 bytes += adsi_voice_mode(buf + bytes, 0);
1996 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2003 bytes += adsi_logo(buf);
2004 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2005 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2006 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2007 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2010 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2011 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2012 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2013 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2014 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2015 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2016 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2019 /* Add another dot */
2021 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2022 bytes += adsi_voice_mode(buf + bytes, 0);
2024 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2025 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2029 /* These buttons we load but don't use yet */
2030 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2031 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2032 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2033 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2034 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2035 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2036 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2039 /* Add another dot */
2041 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2042 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2043 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2048 snprintf(num, sizeof(num), "%d", x);
2049 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2051 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2052 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2055 /* Add another dot */
2057 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2058 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2059 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2062 if (adsi_end_download(chan)) {
2064 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2065 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2066 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2067 bytes += adsi_voice_mode(buf + bytes, 0);
2068 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2072 bytes += adsi_download_disconnect(buf + bytes);
2073 bytes += adsi_voice_mode(buf + bytes, 0);
2074 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2076 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2081 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2082 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2084 ast_log(LOG_DEBUG, "Restarting session...\n");
2087 /* Load the session now */
2088 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
2090 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2092 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2094 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2098 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2101 if (!adsi_available(chan))
2103 x = adsi_load_session(chan, adapp, adver, 1);
2107 if (adsi_load_vmail(chan, useadsi)) {
2108 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2115 static void adsi_login(struct ast_channel *chan)
2119 unsigned char keys[8];
2121 if (!adsi_available(chan))
2126 /* Set one key for next */
2127 keys[3] = ADSI_KEY_APPS + 3;
2129 bytes += adsi_logo(buf + bytes);
2130 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2131 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2132 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2133 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2134 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2135 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2136 bytes += adsi_set_keys(buf + bytes, keys);
2137 bytes += adsi_voice_mode(buf + bytes, 0);
2138 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2141 static void adsi_password(struct ast_channel *chan)
2145 unsigned char keys[8];
2147 if (!adsi_available(chan))
2152 /* Set one key for next */
2153 keys[3] = ADSI_KEY_APPS + 3;
2155 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2156 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2157 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2158 bytes += adsi_set_keys(buf + bytes, keys);
2159 bytes += adsi_voice_mode(buf + bytes, 0);
2160 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2163 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2167 unsigned char keys[8];
2170 if (!adsi_available(chan))
2174 y = ADSI_KEY_APPS + 12 + start + x;
2175 if (y > ADSI_KEY_APPS + 12 + 4)
2177 keys[x] = ADSI_KEY_SKT | y;
2179 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2183 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2184 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2185 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2186 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_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2195 char buf[256], buf1[256], buf2[256];
2201 char datetime[21]="";
2204 unsigned char keys[8];
2208 if (!adsi_available(chan))
2211 /* Retrieve important info */
2212 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2213 f = fopen(fn2, "r");
2216 fgets(buf, sizeof(buf), f);
2220 strsep(&stringp, "=");
2221 val = strsep(&stringp, "=");
2222 if (val && !ast_strlen_zero(val)) {
2223 if (!strcmp(buf, "callerid"))
2224 strncpy(cid, val, sizeof(cid) - 1);
2225 if (!strcmp(buf, "origdate"))
2226 strncpy(datetime, val, sizeof(datetime) - 1);
2232 /* New meaning for keys */
2234 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2239 /* No prev key, provide "Folder" instead */
2240 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2243 /* If last message ... */
2245 /* but not only message, provide "Folder" instead */
2246 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2247 bytes += adsi_voice_mode(buf + bytes, 0);
2250 /* Otherwise if only message, leave blank */
2255 if (!ast_strlen_zero(cid)) {
2256 ast_callerid_parse(cid, &name, &num);
2260 name = "Unknown Caller";
2262 /* If deleted, show "undeleted" */
2265 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2268 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2269 snprintf(buf1, sizeof(buf1), "%s%s", folder,
2270 strcasecmp(folder, "INBOX") ? " Messages" : "");
2271 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
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, name, "");
2276 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2277 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2278 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);
2284 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2288 unsigned char keys[8];
2292 if (!adsi_available(chan))
2295 /* New meaning for keys */
2297 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2303 /* No prev key, provide "Folder" instead */
2304 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2307 /* If last message ... */
2309 /* but not only message, provide "Folder" instead */
2310 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2312 /* Otherwise if only message, leave blank */
2317 /* If deleted, show "undeleted" */
2319 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2322 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2323 bytes += adsi_set_keys(buf + bytes, keys);
2324 bytes += adsi_voice_mode(buf + bytes, 0);
2326 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2329 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2331 char buf[256], buf1[256], buf2[256];
2333 unsigned char keys[8];
2336 char *newm = (new == 1) ? "message" : "messages";
2337 char *oldm = (old == 1) ? "message" : "messages";
2338 if (!adsi_available(chan))
2341 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2343 strcat(buf1, " and");
2344 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2346 snprintf(buf2, sizeof(buf2), "%s.", newm);
2349 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2350 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2352 strcpy(buf1, "You have no messages.");
2355 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2356 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2357 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2360 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2364 /* Don't let them listen if there are none */
2367 bytes += adsi_set_keys(buf + bytes, keys);
2369 bytes += adsi_voice_mode(buf + bytes, 0);
2371 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2374 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2376 char buf[256], buf1[256], buf2[256];
2378 unsigned char keys[8];
2381 char *mess = (messages == 1) ? "message" : "messages";
2383 if (!adsi_available(chan))
2386 /* Original command keys */
2388 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2396 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2397 strcasecmp(folder, "INBOX") ? " folder" : "");
2400 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2402 strcpy(buf2, "no messages.");
2403 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2404 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2405 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2406 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2407 bytes += adsi_set_keys(buf + bytes, keys);
2409 bytes += adsi_voice_mode(buf + bytes, 0);
2411 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2416 static void adsi_clear(struct ast_channel *chan)
2420 if (!adsi_available(chan))
2422 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2423 bytes += adsi_voice_mode(buf + bytes, 0);
2425 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2429 static void adsi_goodbye(struct ast_channel *chan)
2434 if (!adsi_available(chan))
2436 bytes += adsi_logo(buf + bytes);
2437 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2438 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2439 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2440 bytes += adsi_voice_mode(buf + bytes, 0);
2442 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2445 /*--- get_folder: Folder menu ---*/
2446 /* Plays "press 1 for INBOX messages" etc
2447 Should possibly be internationalized
2449 static int get_folder(struct ast_channel *chan, int start)
2454 d = play_and_wait(chan, "vm-press"); /* "Press" */
2457 for (x = start; x< 5; x++) { /* For all folders */
2458 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2460 d = play_and_wait(chan, "vm-for"); /* "for" */
2463 if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Spanish, French or Portuguese syntax */
2464 d = play_and_wait(chan, "vm-messages"); /* "messages */
2467 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2468 d = play_and_wait(chan, fn);
2471 } else { /* Default English */
2472 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2473 d = play_and_wait(chan, fn);
2476 d = play_and_wait(chan, "vm-messages"); /* "messages */
2480 d = ast_waitfordigit(chan, 500);
2484 d = play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2487 d = ast_waitfordigit(chan, 4000);
2491 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2494 res = play_and_wait(chan, fn); /* Folder name */
2495 while (((res < '0') || (res > '9')) &&
2496 (res != '#') && (res >= 0)) {
2497 res = get_folder(chan, 0);
2502 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2508 while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2513 /* prepend a message to the current message and return */
2516 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2517 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2527 cmd = play_and_wait(chan,"vm-forwardoptions");
2528 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2530 cmd = play_and_wait(chan,"vm-starmain");
2531 /* "press star to return to the main menu" */
2533 cmd = ast_waitfordigit(chan,6000);
2545 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid)
2547 char todir[256], fn[256], ext_context[256], *stringp;
2549 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2550 make_file(fn, sizeof(fn), todir, msgnum);
2551 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
2553 /* Attach only the first format */
2554 fmt = ast_strdupa(fmt);
2557 strsep(&stringp, "|");
2559 if (!ast_strlen_zero(vmu->email)) {
2560 int attach_user_voicemail = attach_voicemail;
2561 char *myserveremail = serveremail;
2562 if (vmu->attach > -1)
2563 attach_user_voicemail = vmu->attach;
2564 if (!ast_strlen_zero(vmu->serveremail))
2565 myserveremail = vmu->serveremail;
2566 sendmail(myserveremail, vmu, msgnum, vmu->mailbox, callerid, fn, fmt, duration, attach_user_voicemail);
2569 if (!ast_strlen_zero(vmu->pager)) {
2570 char *myserveremail = serveremail;
2571 if (!ast_strlen_zero(vmu->serveremail))
2572 myserveremail = vmu->serveremail;
2573 sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, callerid, duration, vmu);
2576 ast_log(LOG_ERROR, "Out of memory\n");
2583 /* Leave voicemail for someone */
2584 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context));
2585 run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2589 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2596 struct ast_config *mif;
2600 char ext_context[256]="";
2601 int res = 0, cmd = 0;
2602 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2605 int saved_messages = 0, found = 0;
2606 int valid_extensions = 0;
2607 while (!res && !valid_extensions) {
2608 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
2611 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2613 /* start all over if no username */
2614 if (ast_strlen_zero(username))
2617 s = strsep(&stringp, "*");
2618 /* start optimistic */
2619 valid_extensions = 1;
2621 /* find_user is going to malloc since we have a NULL as first argument */
2622 if ((receiver = find_user(NULL, context, s))) {
2624 vmtmp = extensions = receiver;
2626 vmtmp->next = receiver;
2631 valid_extensions = 0;
2634 s = strsep(&stringp, "*");
2636 /* break from the loop of reading the extensions */
2637 if (valid_extensions)
2639 /* "I am sorry, that's not a valid extension. Please try again." */
2640 res = play_and_wait(chan, "pbx-invalid");
2642 /* check if we're clear to proceed */
2643 if (!extensions || !valid_extensions)
2646 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2648 while(!res && vmtmp) {
2649 /* if (play_and_wait(chan, "vm-savedto"))
2652 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2653 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2654 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2655 ast_log(LOG_DEBUG, sys);
2656 ast_safe_system(sys);
2658 todircount = count_messages(todir);
2659 strncpy(tmp, fmt, sizeof(tmp) - 1);
2661 while((s = strsep(&stringp, "|"))) {
2662 /* XXX This is a hack -- we should use build_filename or similar XXX */
2663 if (!strcasecmp(s, "wav49"))
2665 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2666 ast_log(LOG_DEBUG, sys);
2667 ast_safe_system(sys);
2669 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2670 ast_log(LOG_DEBUG, sys);
2671 ast_safe_system(sys);
2672 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2674 /* load the information on the source message so we can send an e-mail like a new message */
2675 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2676 if ((mif=ast_load(miffile))) {
2678 /* set callerid and duration variables */
2679 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2680 s = ast_variable_retrieve(mif, NULL, "duration");
2685 if (!ast_strlen_zero(vmtmp->email)) {
2686 int attach_user_voicemail = attach_voicemail;
2687 char *myserveremail = serveremail;
2688 if (vmtmp->attach > -1)
2689 attach_user_voicemail = vmtmp->attach;
2690 if (!ast_strlen_zero(vmtmp->serveremail))
2691 myserveremail = vmtmp->serveremail;
2692 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2695 if (!ast_strlen_zero(vmtmp->pager)) {
2696 char *myserveremail = serveremail;
2697 if (!ast_strlen_zero(vmtmp->serveremail))
2698 myserveremail = vmtmp->serveremail;
2699 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2702 ast_destroy(mif); /* or here */
2704 /* Leave voicemail for someone */
2705 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2706 run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2710 vmtmp = vmtmp->next;
2713 if (saved_messages > 0) {
2714 /* give confirmation that the message was saved */
2715 /* commented out since we can't forward batches yet
2716 if (saved_messages == 1)
2717 res = play_and_wait(chan, "vm-message");
2719 res = play_and_wait(chan, "vm-messages");
2721 res = play_and_wait(chan, "vm-saved"); */
2723 res = play_and_wait(chan, "vm-msgsaved");
2726 return res ? res : cmd;
2730 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2733 if ((res = ast_streamfile(chan, file, chan->language)))
2734 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2736 res = ast_waitstream(chan, AST_DIGIT_ANY);
2740 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2742 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
2745 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2748 struct vm_zone *the_zone = NULL;
2752 if (sscanf(origtime,"%ld",&tin) < 1) {
2753 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2758 /* Does this user have a timezone specified? */
2759 if (!ast_strlen_zero(vmu->zonetag)) {
2760 /* Find the zone in the list */
2764 if (!strcmp(z->name, vmu->zonetag)) {
2772 /* No internal variable parsing for now, so we'll comment it out for the time being */
2774 /* Set the DIFF_* variables */
2775 localtime_r(&t, &time_now);
2776 gettimeofday(&tv_now,NULL);
2777 tnow = tv_now.tv_sec;
2778 localtime_r(&tnow,&time_then);
2780 /* Day difference */
2781 if (time_now.tm_year == time_then.tm_year)
2782 sprintf(temp,"%d",time_now.tm_yday);
2784 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2785 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2787 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2790 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2791 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
2792 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
2794 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2796 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2803 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2807 char *callerid, *name;
2808 char prefile[256]="";
2811 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2812 /* 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 */
2813 if((cid == NULL)||(context == NULL))
2816 /* Strip off caller ID number from name */
2817 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2818 ast_callerid_parse(cid, &name, &callerid);
2819 if((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
2820 /* Check for internal contexts and only */
2821 /* say extension when the call didn't come from an internal context in the list */
2822 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2823 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2824 if((strcmp(cidinternalcontexts[i], context) == 0))
2827 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2829 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2830 if (!ast_strlen_zero(prefile)) {
2831 /* See if we can find a recorded name for this person instead of their extension number */
2832 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2833 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2835 res = wait_file2(chan, vms, "vm-from");
2836 res = ast_streamfile(chan, prefile, chan->language) > -1;
2837 res = ast_waitstream(chan, "");
2839 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2840 /* BB: Say "from extension" as one saying to sound smoother */
2842 res = wait_file2(chan, vms, "vm-from-extension");
2843 res = ast_say_digit_str(chan, callerid, "", chan->language);
2850 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2851 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2853 res = wait_file2(chan, vms, "vm-from-phonenumber");
2854 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2858 /* Number unknown */
2859 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2861 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2862 res = wait_file2(chan, vms, "vm-unknown-caller");
2867 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2870 char filename[256],*origtime, *cid, *context;
2871 struct ast_config *msg_cfg;
2874 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2875 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2877 res = wait_file2(chan, vms, "vm-first"); /* "First" */
2878 else if (msg == vms->lastmsg)
2879 res = wait_file2(chan, vms, "vm-last"); /* "last" */
2881 res = wait_file2(chan, vms, "vm-message"); /* "message" */
2882 if (msg && (msg != vms->lastmsg)) {
2884 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2888 /* Retrieve info from VM attribute file */
2889 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2890 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2891 msg_cfg = ast_load(filename);
2893 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2897 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2900 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2902 context = ast_variable_retrieve(msg_cfg, "message", "context");
2903 if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2904 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2906 if ((!res)&&(vmu->envelope))
2907 res = play_message_datetime(chan, vmu, origtime, filename);
2908 if ((!res)&&(vmu->saycid))
2909 res = play_message_callerid(chan, vms, cid, context, 0);
2910 /* Allow pressing '1' to skip envelope / callerid */
2913 ast_destroy(msg_cfg);
2916 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2917 vms->heard[msg] = 1;
2918 res = wait_file(chan, vms, vms->fn);
2923 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2925 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2926 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2927 vms->lastmsg = count_messages(vms->curdir) - 1;
2928 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2931 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2934 char ntxt[256] = "";
2936 if (vms->lastmsg > -1) {
2937 /* Get the deleted messages fixed */
2939 for (x=0;x < MAXMSG;x++) {
2940 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2941 /* Save this message. It's not in INBOX or hasn't been heard */
2942 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2943 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2946 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2947 if (strcmp(vms->fn, vms->fn2)) {
2948 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2949 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2950 ast_filerename(vms->fn, vms->fn2, NULL);
2953 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2954 /* Move to old folder before deleting */
2955 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2958 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2959 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2960 if (ast_fileexists(vms->fn, NULL, NULL) < 1)