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;
837 if (!strcmp(format, "wav49"))
839 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
840 /* Make a temporary file instead of piping directly to sendmail, in case the mail
844 p = fdopen(pfd, "w");
851 gethostname(host, sizeof(host));
852 if (strchr(srcemail, '@'))
853 strncpy(who, srcemail, sizeof(who)-1);
855 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
857 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
860 /* Does this user have a timezone specified? */
861 if (!ast_strlen_zero(vmu->zonetag)) {
862 /* Find the zone in the list */
866 if (!strcmp(z->name, vmu->zonetag)) {
875 ast_localtime(&t,&tm,the_zone->timezone);
877 ast_localtime(&t,&tm,NULL);
878 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
879 fprintf(p, "Date: %s\n", date);
882 struct ast_channel *ast = ast_channel_alloc(0);
885 int vmlen = strlen(fromstring)*3 + 200;
886 if ((passdata = alloca(vmlen))) {
887 memset(passdata, 0, vmlen);
888 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
889 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
890 fprintf(p, "From: %s <%s>\n",passdata,who);
891 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
892 ast_channel_free(ast);
893 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
895 fprintf(p, "From: Asterisk PBX <%s>\n", who);
896 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
899 struct ast_channel *ast = ast_channel_alloc(0);
902 int vmlen = strlen(emailsubject)*3 + 200;
903 if ((passdata = alloca(vmlen))) {
904 memset(passdata, 0, vmlen);
905 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
906 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
907 fprintf(p, "Subject: %s\n",passdata);
908 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
909 ast_channel_free(ast);
910 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
914 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
919 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
921 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
922 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
923 fprintf(p, "MIME-Version: 1.0\n");
924 if (attach_user_voicemail) {
925 /* Something unique. */
926 snprintf(bound, sizeof(bound), "voicemail_%d%s%d", msgnum, mailbox, getpid());
928 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
930 fprintf(p, "--%s\n", bound);
932 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
933 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
935 struct ast_channel *ast = ast_channel_alloc(0);
938 int vmlen = strlen(emailbody)*3 + 200;
939 if ((passdata = alloca(vmlen))) {
940 memset(passdata, 0, vmlen);
941 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
942 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
943 fprintf(p, "%s\n",passdata);
944 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
945 ast_channel_free(ast);
946 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
948 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
950 "in mailbox %s from %s, on %s so you might\n"
951 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
952 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
954 if (attach_user_voicemail) {
955 fprintf(p, "--%s\n", bound);
956 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
957 fprintf(p, "Content-Transfer-Encoding: base64\n");
958 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
959 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
961 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
962 base_encode(fname, p);
963 fprintf(p, "\n\n--%s--\n.\n", bound);
966 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
967 ast_safe_system(tmp2);
968 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
970 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
976 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, int duration, struct ast_vm_user *vmu)
984 char tmp[80] = "/tmp/astmail-XXXXXX";
988 struct vm_zone *the_zone = NULL;
992 p = fdopen(pfd, "w");
1000 gethostname(host, sizeof(host));
1001 if (strchr(srcemail, '@'))
1002 strncpy(who, srcemail, sizeof(who)-1);
1004 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1006 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1009 /* Does this user have a timezone specified? */
1010 if (!ast_strlen_zero(vmu->zonetag)) {
1011 /* Find the zone in the list */
1015 if (!strcmp(z->name, vmu->zonetag)) {
1024 ast_localtime(&t,&tm,the_zone->timezone);
1026 ast_localtime(&t,&tm,NULL);
1028 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1029 fprintf(p, "Date: %s\n", date);
1030 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1031 fprintf(p, "To: %s\n", pager);
1032 fprintf(p, "Subject: New VM\n\n");
1033 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1034 fprintf(p, "New %s long msg in box %s\n"
1035 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
1037 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1038 ast_safe_system(tmp2);
1039 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
1041 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1047 static int get_date(char *s, int len)
1052 localtime_r(&t,&tm);
1053 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1056 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1060 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1061 if (ast_fileexists(fn, NULL, NULL) > 0) {
1062 res = ast_streamfile(chan, fn, chan->language);
1065 res = ast_waitstream(chan, ecodes);
1069 res = ast_streamfile(chan, "vm-theperson", chan->language);
1072 res = ast_waitstream(chan, ecodes);
1075 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1080 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1082 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1085 res = ast_waitstream(chan, ecodes);
1089 static int play_and_wait(struct ast_channel *chan, char *fn)
1092 d = ast_streamfile(chan, fn, chan->language);
1095 d = ast_waitstream(chan, AST_DIGIT_ANY);
1096 ast_stopstream(chan);
1100 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep)
1104 int x, fmtcnt=1, res=-1,outmsg=0;
1105 struct ast_frame *f;
1106 struct ast_filestream *others[MAX_OTHER_FORMATS];
1107 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
1108 char *sfmt[MAX_OTHER_FORMATS];
1111 struct ast_dsp *sildet; /* silence detector dsp */
1112 int totalsilence = 0;
1114 int gotsilence = 0; /* did we timeout for silence? */
1116 char prependfile[80];
1118 /* barf if no pointer passed to store duration in */
1119 if (duration == NULL) {
1120 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
1124 ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1125 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1127 if (playfile || beep) {
1129 d = play_and_wait(chan, playfile);
1131 d = ast_streamfile(chan, "beep",chan->language);
1133 d = ast_waitstream(chan,"");
1137 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
1138 strcat(prependfile, "-prepend");
1140 fmts = ast_strdupa(fmt);
1143 strsep(&stringp, "|");
1144 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1145 sfmt[0] = ast_strdupa(fmts);
1147 while((fmt = strsep(&stringp, "|"))) {
1148 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1149 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1152 sfmt[fmtcnt++] = ast_strdupa(fmt);
1156 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1157 for (x=0;x<fmtcnt;x++) {
1158 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1159 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1165 sildet = ast_dsp_new(); /* Create the silence detector */
1167 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1170 ast_dsp_set_threshold(sildet, silencethreshold);
1172 if (maxsilence > 0) {
1173 rfmt = chan->readformat;
1174 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1176 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1182 /* Loop forever, writing the packets we read to the writer(s), until
1183 we read a # or get a hangup */
1186 res = ast_waitfor(chan, 2000);
1188 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1189 /* Try one more time in case of masq */
1190 res = ast_waitfor(chan, 2000);
1192 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1204 if (f->frametype == AST_FRAME_VOICE) {
1205 /* write each format */
1206 for (x=0;x<fmtcnt;x++) {
1209 res = ast_writestream(others[x], f);
1212 /* Silence Detection */
1213 if (maxsilence > 0) {
1215 ast_dsp_silence(sildet, f, &dspsilence);
1217 totalsilence = dspsilence;
1221 if (totalsilence > maxsilence) {
1222 /* Ended happily with silence */
1229 /* Exit on any error */
1231 ast_log(LOG_WARNING, "Error writing frame\n");
1235 } else if (f->frametype == AST_FRAME_VIDEO) {
1236 /* Write only once */
1237 ast_writestream(others[0], f);
1238 } else if (f->frametype == AST_FRAME_DTMF) {
1239 /* stop recording with any digit */
1240 if (option_verbose > 2)
1241 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1249 if (maxtime < (end - start)) {
1250 if (option_verbose > 2)
1251 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1260 if (end == start) time(&end);
1262 if (option_verbose > 2)
1263 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1267 /* delete all the prepend files */
1268 for (x=0;x<fmtcnt;x++) {
1271 ast_closestream(others[x]);
1272 ast_filedelete(prependfile, sfmt[x]);
1277 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1279 *duration = end - start;
1285 struct ast_frame *fr;
1286 for (x=0;x<fmtcnt;x++) {
1287 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1288 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1289 if (!others[x] || !realfiles[x])
1292 ast_stream_rewind(others[x], totalsilence-200);
1294 ast_stream_rewind(others[x], 200);
1295 ast_truncstream(others[x]);
1296 /* add the original file too */
1297 while ((fr = ast_readframe(realfiles[x]))) {
1298 ast_writestream(others[x],fr);
1300 ast_closestream(others[x]);
1301 ast_closestream(realfiles[x]);
1302 ast_filerename(prependfile, recordfile, sfmt[x]);
1304 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1306 ast_filedelete(prependfile, sfmt[x]);
1310 if (ast_set_read_format(chan, rfmt)) {
1311 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1316 /* Let them know it worked */
1317 ast_streamfile(chan, "auth-thankyou", chan->language);
1318 ast_waitstream(chan, "");
1324 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1328 int x, fmtcnt=1, res=-1,outmsg=0;
1329 struct ast_frame *f;
1330 struct ast_filestream *others[MAX_OTHER_FORMATS];
1331 char *sfmt[MAX_OTHER_FORMATS];
1334 struct ast_dsp *sildet; /* silence detector dsp */
1335 int totalsilence = 0;
1337 int gotsilence = 0; /* did we timeout for silence? */
1340 /* barf if no pointer passed to store duration in */
1341 if (duration == NULL) {
1342 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1346 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1347 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1350 d = play_and_wait(chan, playfile);
1352 d = ast_streamfile(chan, "beep",chan->language);
1354 d = ast_waitstream(chan,"");
1359 fmts = ast_strdupa(fmt);
1362 strsep(&stringp, "|");
1363 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1364 sfmt[0] = ast_strdupa(fmts);
1366 while((fmt = strsep(&stringp, "|"))) {
1367 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1368 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1371 sfmt[fmtcnt++] = ast_strdupa(fmt);
1375 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1376 for (x=0;x<fmtcnt;x++) {
1377 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1378 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1385 sildet = ast_dsp_new(); /* Create the silence detector */
1387 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1390 ast_dsp_set_threshold(sildet, silencethreshold);
1392 if (maxsilence > 0) {
1393 rfmt = chan->readformat;
1394 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1396 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1402 /* Loop forever, writing the packets we read to the writer(s), until
1403 we read a # or get a hangup */
1406 res = ast_waitfor(chan, 2000);
1408 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1409 /* Try one more time in case of masq */
1410 res = ast_waitfor(chan, 2000);
1412 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1424 if (f->frametype == AST_FRAME_VOICE) {
1425 /* write each format */
1426 for (x=0;x<fmtcnt;x++) {
1427 res = ast_writestream(others[x], f);
1430 /* Silence Detection */
1431 if (maxsilence > 0) {
1433 ast_dsp_silence(sildet, f, &dspsilence);
1435 totalsilence = dspsilence;
1439 if (totalsilence > maxsilence) {
1440 /* Ended happily with silence */
1447 /* Exit on any error */
1449 ast_log(LOG_WARNING, "Error writing frame\n");
1453 } else if (f->frametype == AST_FRAME_VIDEO) {
1454 /* Write only once */
1455 ast_writestream(others[0], f);
1456 } else if (f->frametype == AST_FRAME_DTMF) {
1457 if (f->subclass == '#') {
1458 if (option_verbose > 2)
1459 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1466 if (f->subclass == '0') {
1467 /* Check for a '0' during message recording also, in case caller wants operator */
1468 if (option_verbose > 2)
1469 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1477 if (maxtime < (end - start)) {
1478 if (option_verbose > 2)
1479 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1488 if (end == start) time(&end);
1490 if (option_verbose > 2)
1491 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1496 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1499 *duration = end - start;
1501 for (x=0;x<fmtcnt;x++) {
1505 ast_stream_rewind(others[x], totalsilence-200);
1507 ast_stream_rewind(others[x], 200);
1508 ast_truncstream(others[x]);
1509 ast_closestream(others[x]);
1512 if (ast_set_read_format(chan, rfmt)) {
1513 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1518 /* Let them know recording is stopped */
1519 ast_streamfile(chan, "auth-thankyou", chan->language);
1520 ast_waitstream(chan, "");
1527 static void free_user(struct ast_vm_user *vmu)
1533 static void free_zone(struct vm_zone *z)
1538 static char *mbox(int id)
1566 static int copy(char *infile, char *outfile)
1574 #ifdef HARDLINK_WHEN_POSSIBLE
1575 /* Hard link if possible; saves disk space & is faster */
1576 if (link(infile, outfile)) {
1578 if ((ifd = open(infile, O_RDONLY)) < 0) {
1579 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1582 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1583 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1588 len = read(ifd, buf, sizeof(buf));
1590 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1596 res = write(ofd, buf, len);
1598 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1608 #ifdef HARDLINK_WHEN_POSSIBLE
1610 /* Hard link succeeded */
1616 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid);
1618 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)
1620 char fromdir[256], todir[256], frompath[256], topath[256];
1621 char *frombox = mbox(imbox);
1624 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1626 make_dir(todir, sizeof(todir), recip->context, "", "");
1627 /* It's easier just to try to make it than to check for its existence */
1628 if (mkdir(todir, 0700) && (errno != EEXIST))
1629 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1630 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1631 /* It's easier just to try to make it than to check for its existence */
1632 if (mkdir(todir, 0700) && (errno != EEXIST))
1633 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1634 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1635 if (mkdir(todir, 0700) && (errno != EEXIST))
1636 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1638 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1639 make_file(frompath, sizeof(frompath), fromdir, msgnum);
1642 make_file(topath, sizeof(topath), todir, recipmsgnum);
1643 if (ast_fileexists(topath, NULL, chan->language) <= 0)
1646 } while(recipmsgnum < MAXMSG);
1647 if (recipmsgnum < MAXMSG) {
1648 char frompath2[256],topath2[256];
1649 ast_filecopy(frompath, topath, NULL);
1650 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1651 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1652 copy(frompath2, topath2);
1654 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1657 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->callerid);
1660 static void run_externnotify(char *context, char *extension, int numvoicemails)
1662 char arguments[255];
1664 if(externnotify[0]) {
1665 strncpy(arguments, externnotify, sizeof(arguments));
1666 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1667 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1668 ast_safe_system(arguments);
1673 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1686 char prefile[256]="";
1687 char ext_context[256] = "";
1690 char ecodes[16] = "#";
1691 char tmp[256] = "", *tmpptr;
1692 struct ast_vm_user *vmu;
1693 struct ast_vm_user svm;
1695 strncpy(tmp, ext, sizeof(tmp) - 1);
1697 context = strchr(tmp, '@');
1701 tmpptr = strchr(context, '&');
1703 tmpptr = strchr(ext, '&');
1711 if ((vmu = find_user(&svm, context, ext))) {
1712 /* Setup pre-file if appropriate */
1713 if (strcmp(vmu->context, "default"))
1714 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1716 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1718 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1720 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1721 make_dir(dir, sizeof(dir), vmu->context, "", "");
1722 /* It's easier just to try to make it than to check for its existence */
1723 if (mkdir(dir, 0700) && (errno != EEXIST))
1724 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1725 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1726 /* It's easier just to try to make it than to check for its existence */
1727 if (mkdir(dir, 0700) && (errno != EEXIST))
1728 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1729 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1730 if (mkdir(dir, 0700) && (errno != EEXIST))
1731 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1733 /* Check current or macro-calling context for special extensions */
1734 if (ast_exists_extension(chan, chan->context, "o", 1, chan->callerid))
1735 strcat(ecodes, "0");
1736 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->callerid)) {
1737 strcat(ecodes, "0");
1741 if (ast_exists_extension(chan, chan->context, "a", 1, chan->callerid))
1742 strcat(ecodes, "*");
1743 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->callerid)) {
1744 strcat(ecodes, "*");
1748 /* Play the beginning intro if desired */
1749 if (!ast_strlen_zero(prefile)) {
1750 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1751 if (ast_streamfile(chan, prefile, chan->language) > -1)
1752 res = ast_waitstream(chan, ecodes);
1754 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1755 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1758 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1764 /* On a '#' we skip the instructions */
1768 if (!res && !silent) {
1769 res = ast_streamfile(chan, INTRO, chan->language);
1771 res = ast_waitstream(chan, ecodes);
1778 ast_stopstream(chan);
1779 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1780 other than the operator -- an automated attendant or mailbox login for example */
1782 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1783 if (!ast_strlen_zero(vmu->exit)) {
1784 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1785 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
1786 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1792 /* Check for a '0' here */
1795 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1796 if (!ast_strlen_zero(vmu->exit)) {
1797 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1798 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
1799 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1806 /* Unless we're *really* silent, try to send the beep */
1807 res = ast_streamfile(chan, "beep", chan->language);
1809 res = ast_waitstream(chan, "");
1815 /* The meat of recording the message... All the announcements and beeps have been played*/
1816 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1817 if (!ast_strlen_zero(fmt)) {
1820 make_file(fn, sizeof(fn), dir, msgnum);
1821 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1824 } while(msgnum < MAXMSG);
1825 if (msgnum < MAXMSG) {
1826 /* Store information */
1827 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1828 txt = fopen(txtfile, "w+");
1830 get_date(date, sizeof(date));
1833 "; Message Information file\n"
1851 chan->callerid ? chan->callerid : "Unknown",
1852 date, (long)time(NULL));
1855 ast_log(LOG_WARNING, "Error opening text file for output\n");
1856 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1861 fd = open(txtfile, O_APPEND | O_WRONLY);
1863 txt = fdopen(fd, "a");
1865 fprintf(txt, "duration=%d\n", duration);
1870 if (duration < vmminmessage) {
1874 /* Are there to be more recipients of this message? */
1876 struct ast_vm_user recipu, *recip;
1877 char *exten, *context;
1879 exten = strsep(&tmpptr, "&");
1880 context = strchr(exten, '@');
1885 if ((recip = find_user(&recipu, context, exten))) {
1886 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
1890 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->callerid);
1892 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1894 res = ast_waitstream(chan, "");
1895 ast_log(LOG_WARNING, "No more messages possible\n");
1898 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1902 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1903 /*Send the call to n+101 priority, where n is the current priority*/
1904 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1905 chan->priority+=100;
1911 static int count_messages(char *dir)
1915 for (x=0;x<MAXMSG;x++) {
1916 make_file(fn, sizeof(fn), dir, x);
1917 if (ast_fileexists(fn, NULL, NULL) < 1)
1923 static int say_and_wait(struct ast_channel *chan, int num, char *language)
1926 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
1930 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1937 char *dbox = mbox(box);
1939 make_file(sfn, sizeof(sfn), dir, msg);
1940 make_dir(ddir, sizeof(ddir), context, username, dbox);
1942 for (x=0;x<MAXMSG;x++) {
1943 make_file(dfn, sizeof(dfn), ddir, x);
1944 if (ast_fileexists(dfn, NULL, NULL) < 0)
1949 ast_filecopy(sfn, dfn, NULL);
1950 if (strcmp(sfn, dfn)) {
1951 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1952 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1958 static int adsi_logo(unsigned char *buf)
1961 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1962 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1966 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1974 bytes += adsi_data_mode(buf + bytes);
1975 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1978 bytes += adsi_logo(buf);
1979 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1981 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1983 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1984 bytes += adsi_data_mode(buf + bytes);
1985 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1987 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1989 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1990 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1991 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1992 bytes += adsi_voice_mode(buf + bytes, 0);
1993 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2000 bytes += adsi_logo(buf);
2001 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2002 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2003 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2004 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2007 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2008 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2009 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2010 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2011 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2012 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2013 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2016 /* Add another dot */
2018 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2019 bytes += adsi_voice_mode(buf + bytes, 0);
2021 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2022 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2026 /* These buttons we load but don't use yet */
2027 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2028 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2029 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2030 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2031 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2032 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2033 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2036 /* Add another dot */
2038 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2039 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2040 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2045 snprintf(num, sizeof(num), "%d", x);
2046 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2048 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2049 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2052 /* Add another dot */
2054 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2055 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2056 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2059 if (adsi_end_download(chan)) {
2061 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2062 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2063 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2064 bytes += adsi_voice_mode(buf + bytes, 0);
2065 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2069 bytes += adsi_download_disconnect(buf + bytes);
2070 bytes += adsi_voice_mode(buf + bytes, 0);
2071 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2073 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2078 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2079 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2081 ast_log(LOG_DEBUG, "Restarting session...\n");
2084 /* Load the session now */
2085 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
2087 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2089 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2091 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2095 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2098 if (!adsi_available(chan))
2100 x = adsi_load_session(chan, adapp, adver, 1);
2104 if (adsi_load_vmail(chan, useadsi)) {
2105 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2112 static void adsi_login(struct ast_channel *chan)
2116 unsigned char keys[8];
2118 if (!adsi_available(chan))
2123 /* Set one key for next */
2124 keys[3] = ADSI_KEY_APPS + 3;
2126 bytes += adsi_logo(buf + bytes);
2127 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2128 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2129 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2130 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2131 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2132 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2133 bytes += adsi_set_keys(buf + bytes, keys);
2134 bytes += adsi_voice_mode(buf + bytes, 0);
2135 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2138 static void adsi_password(struct ast_channel *chan)
2142 unsigned char keys[8];
2144 if (!adsi_available(chan))
2149 /* Set one key for next */
2150 keys[3] = ADSI_KEY_APPS + 3;
2152 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2153 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2154 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2155 bytes += adsi_set_keys(buf + bytes, keys);
2156 bytes += adsi_voice_mode(buf + bytes, 0);
2157 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2160 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2164 unsigned char keys[8];
2167 if (!adsi_available(chan))
2171 y = ADSI_KEY_APPS + 12 + start + x;
2172 if (y > ADSI_KEY_APPS + 12 + 4)
2174 keys[x] = ADSI_KEY_SKT | y;
2176 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2180 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2181 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2182 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2183 bytes += adsi_set_keys(buf + bytes, keys);
2184 bytes += adsi_voice_mode(buf + bytes, 0);
2186 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2189 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2192 char buf[256], buf1[256], buf2[256];
2198 char datetime[21]="";
2201 unsigned char keys[8];
2205 if (!adsi_available(chan))
2208 /* Retrieve important info */
2209 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2210 f = fopen(fn2, "r");
2213 fgets(buf, sizeof(buf), f);
2217 strsep(&stringp, "=");
2218 val = strsep(&stringp, "=");
2219 if (val && !ast_strlen_zero(val)) {
2220 if (!strcmp(buf, "callerid"))
2221 strncpy(cid, val, sizeof(cid) - 1);
2222 if (!strcmp(buf, "origdate"))
2223 strncpy(datetime, val, sizeof(datetime) - 1);
2229 /* New meaning for keys */
2231 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2236 /* No prev key, provide "Folder" instead */
2237 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2240 /* If last message ... */
2242 /* but not only message, provide "Folder" instead */
2243 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2244 bytes += adsi_voice_mode(buf + bytes, 0);
2247 /* Otherwise if only message, leave blank */
2252 if (!ast_strlen_zero(cid)) {
2253 ast_callerid_parse(cid, &name, &num);
2257 name = "Unknown Caller";
2259 /* If deleted, show "undeleted" */
2262 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2265 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2266 snprintf(buf1, sizeof(buf1), "%s%s", folder,
2267 strcasecmp(folder, "INBOX") ? " Messages" : "");
2268 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2270 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2271 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2272 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2273 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2274 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2275 bytes += adsi_set_keys(buf + bytes, keys);
2276 bytes += adsi_voice_mode(buf + bytes, 0);
2278 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2281 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2285 unsigned char keys[8];
2289 if (!adsi_available(chan))
2292 /* New meaning for keys */
2294 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2300 /* No prev key, provide "Folder" instead */
2301 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2304 /* If last message ... */
2306 /* but not only message, provide "Folder" instead */
2307 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2309 /* Otherwise if only message, leave blank */
2314 /* If deleted, show "undeleted" */
2316 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2319 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2320 bytes += adsi_set_keys(buf + bytes, keys);
2321 bytes += adsi_voice_mode(buf + bytes, 0);
2323 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2326 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2328 char buf[256], buf1[256], buf2[256];
2330 unsigned char keys[8];
2333 char *newm = (new == 1) ? "message" : "messages";
2334 char *oldm = (old == 1) ? "message" : "messages";
2335 if (!adsi_available(chan))
2338 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2340 strcat(buf1, " and");
2341 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2343 snprintf(buf2, sizeof(buf2), "%s.", newm);
2346 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2347 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2349 strcpy(buf1, "You have no messages.");
2352 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2353 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2354 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2357 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2361 /* Don't let them listen if there are none */
2364 bytes += adsi_set_keys(buf + bytes, keys);
2366 bytes += adsi_voice_mode(buf + bytes, 0);
2368 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2371 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2373 char buf[256], buf1[256], buf2[256];
2375 unsigned char keys[8];
2378 char *mess = (messages == 1) ? "message" : "messages";
2380 if (!adsi_available(chan))
2383 /* Original command keys */
2385 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2393 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2394 strcasecmp(folder, "INBOX") ? " folder" : "");
2397 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2399 strcpy(buf2, "no messages.");
2400 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2401 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2402 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2403 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2404 bytes += adsi_set_keys(buf + bytes, keys);
2406 bytes += adsi_voice_mode(buf + bytes, 0);
2408 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2413 static void adsi_clear(struct ast_channel *chan)
2417 if (!adsi_available(chan))
2419 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2420 bytes += adsi_voice_mode(buf + bytes, 0);
2422 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2426 static void adsi_goodbye(struct ast_channel *chan)
2431 if (!adsi_available(chan))
2433 bytes += adsi_logo(buf + bytes);
2434 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2435 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2436 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2437 bytes += adsi_voice_mode(buf + bytes, 0);
2439 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2442 /*--- get_folder: Folder menu ---*/
2443 /* Plays "press 1 for INBOX messages" etc
2444 Should possibly be internationalized
2446 static int get_folder(struct ast_channel *chan, int start)
2451 d = play_and_wait(chan, "vm-press"); /* "Press" */
2454 for (x = start; x< 5; x++) { /* For all folders */
2455 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2457 d = play_and_wait(chan, "vm-for"); /* "for" */
2460 if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Spanish, French or Portuguese syntax */
2461 d = play_and_wait(chan, "vm-messages"); /* "messages */
2464 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2465 d = play_and_wait(chan, fn);
2468 } else { /* Default English */
2469 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2470 d = play_and_wait(chan, fn);
2473 d = play_and_wait(chan, "vm-messages"); /* "messages */
2477 d = ast_waitfordigit(chan, 500);
2481 d = play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2484 d = ast_waitfordigit(chan, 4000);
2488 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2491 res = play_and_wait(chan, fn); /* Folder name */
2492 while (((res < '0') || (res > '9')) &&
2493 (res != '#') && (res >= 0)) {
2494 res = get_folder(chan, 0);
2499 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2505 while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2510 /* prepend a message to the current message and return */
2513 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2514 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2524 cmd = play_and_wait(chan,"vm-forwardoptions");
2525 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2527 cmd = play_and_wait(chan,"vm-starmain");
2528 /* "press star to return to the main menu" */
2530 cmd = ast_waitfordigit(chan,6000);
2542 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid)
2544 char todir[256], fn[256], *stringp;
2546 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2547 make_file(fn, sizeof(fn), todir, msgnum);
2549 /* Attach only the first format */
2550 fmt = ast_strdupa(fmt);
2553 strsep(&stringp, "|");
2555 if (!ast_strlen_zero(vmu->email)) {
2556 int attach_user_voicemail = attach_voicemail;
2557 char *myserveremail = serveremail;
2558 if (vmu->attach > -1)
2559 attach_user_voicemail = vmu->attach;
2560 if (!ast_strlen_zero(vmu->serveremail))
2561 myserveremail = vmu->serveremail;
2562 sendmail(myserveremail, vmu, msgnum, vmu->mailbox, callerid, fn, fmt, duration, attach_user_voicemail);
2565 if (!ast_strlen_zero(vmu->pager)) {
2566 char *myserveremail = serveremail;
2567 if (!ast_strlen_zero(vmu->serveremail))
2568 myserveremail = vmu->serveremail;
2569 sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, callerid, duration, vmu);
2572 ast_log(LOG_ERROR, "Out of memory\n");
2579 /* Leave voicemail for someone */
2580 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmu->mailbox, ast_app_has_voicemail(vmu->mailbox));
2581 run_externnotify(chan->context, vmu->mailbox, ast_app_has_voicemail(vmu->mailbox));
2585 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2592 struct ast_config *mif;
2596 char ext_context[256]="";
2597 int res = 0, cmd = 0;
2598 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2601 int saved_messages = 0, found = 0;
2602 int valid_extensions = 0;
2603 while (!res && !valid_extensions) {
2604 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
2607 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2609 /* start all over if no username */
2610 if (ast_strlen_zero(username))
2613 s = strsep(&stringp, "*");
2614 /* start optimistic */
2615 valid_extensions = 1;
2617 /* find_user is going to malloc since we have a NULL as first argument */
2618 if ((receiver = find_user(NULL, context, s))) {
2620 vmtmp = extensions = receiver;
2622 vmtmp->next = receiver;
2627 valid_extensions = 0;
2630 s = strsep(&stringp, "*");
2632 /* break from the loop of reading the extensions */
2633 if (valid_extensions)
2635 /* "I am sorry, that's not a valid extension. Please try again." */
2636 res = play_and_wait(chan, "pbx-invalid");
2638 /* check if we're clear to proceed */
2639 if (!extensions || !valid_extensions)
2642 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2644 while(!res && vmtmp) {
2645 /* if (play_and_wait(chan, "vm-savedto"))
2648 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2649 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2650 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2651 ast_log(LOG_DEBUG, sys);
2652 ast_safe_system(sys);
2654 todircount = count_messages(todir);
2655 strncpy(tmp, fmt, sizeof(tmp) - 1);
2657 while((s = strsep(&stringp, "|"))) {
2658 /* XXX This is a hack -- we should use build_filename or similar XXX */
2659 if (!strcasecmp(s, "wav49"))
2661 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2662 ast_log(LOG_DEBUG, sys);
2663 ast_safe_system(sys);
2665 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2666 ast_log(LOG_DEBUG, sys);
2667 ast_safe_system(sys);
2668 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2670 /* load the information on the source message so we can send an e-mail like a new message */
2671 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2672 if ((mif=ast_load(miffile))) {
2674 /* set callerid and duration variables */
2675 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2676 s = ast_variable_retrieve(mif, NULL, "duration");
2681 if (!ast_strlen_zero(vmtmp->email)) {
2682 int attach_user_voicemail = attach_voicemail;
2683 char *myserveremail = serveremail;
2684 if (vmtmp->attach > -1)
2685 attach_user_voicemail = vmtmp->attach;
2686 if (!ast_strlen_zero(vmtmp->serveremail))
2687 myserveremail = vmtmp->serveremail;
2688 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2691 if (!ast_strlen_zero(vmtmp->pager)) {
2692 char *myserveremail = serveremail;
2693 if (!ast_strlen_zero(vmtmp->serveremail))
2694 myserveremail = vmtmp->serveremail;
2695 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2698 ast_destroy(mif); /* or here */
2700 /* Leave voicemail for someone */
2701 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2702 run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2706 vmtmp = vmtmp->next;
2709 if (saved_messages > 0) {
2710 /* give confirmation that the message was saved */
2711 /* commented out since we can't forward batches yet
2712 if (saved_messages == 1)
2713 res = play_and_wait(chan, "vm-message");
2715 res = play_and_wait(chan, "vm-messages");
2717 res = play_and_wait(chan, "vm-saved"); */
2719 res = play_and_wait(chan, "vm-msgsaved");
2722 return res ? res : cmd;
2726 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2729 if ((res = ast_streamfile(chan, file, chan->language)))
2730 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2732 res = ast_waitstream(chan, AST_DIGIT_ANY);
2736 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2738 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
2741 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2744 struct vm_zone *the_zone = NULL;
2748 if (sscanf(origtime,"%ld",&tin) < 1) {
2749 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2754 /* Does this user have a timezone specified? */
2755 if (!ast_strlen_zero(vmu->zonetag)) {
2756 /* Find the zone in the list */
2760 if (!strcmp(z->name, vmu->zonetag)) {
2768 /* No internal variable parsing for now, so we'll comment it out for the time being */
2770 /* Set the DIFF_* variables */
2771 localtime_r(&t, &time_now);
2772 gettimeofday(&tv_now,NULL);
2773 tnow = tv_now.tv_sec;
2774 localtime_r(&tnow,&time_then);
2776 /* Day difference */
2777 if (time_now.tm_year == time_then.tm_year)
2778 sprintf(temp,"%d",time_now.tm_yday);
2780 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2781 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2783 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2786 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2787 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
2788 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
2790 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2792 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2799 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2803 char *callerid, *name;
2804 char prefile[256]="";
2807 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2808 /* 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 */
2809 if((cid == NULL)||(context == NULL))
2812 /* Strip off caller ID number from name */
2813 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2814 ast_callerid_parse(cid, &name, &callerid);
2815 if((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
2816 /* Check for internal contexts and only */
2817 /* say extension when the call didn't come from an internal context in the list */
2818 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2819 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2820 if((strcmp(cidinternalcontexts[i], context) == 0))
2823 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2825 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2826 if (!ast_strlen_zero(prefile)) {
2827 /* See if we can find a recorded name for this person instead of their extension number */
2828 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2829 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2831 res = wait_file2(chan, vms, "vm-from");
2832 res = ast_streamfile(chan, prefile, chan->language) > -1;
2833 res = ast_waitstream(chan, "");
2835 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2836 /* BB: Say "from extension" as one saying to sound smoother */
2838 res = wait_file2(chan, vms, "vm-from-extension");
2839 res = ast_say_digit_str(chan, callerid, "", chan->language);
2846 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2847 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2849 res = wait_file2(chan, vms, "vm-from-phonenumber");
2850 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2854 /* Number unknown */
2855 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2857 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2858 res = wait_file2(chan, vms, "vm-unknown-caller");
2863 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2866 char filename[256],*origtime, *cid, *context;
2867 struct ast_config *msg_cfg;
2870 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2871 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2873 res = wait_file2(chan, vms, "vm-first"); /* "First" */
2874 else if (msg == vms->lastmsg)
2875 res = wait_file2(chan, vms, "vm-last"); /* "last" */
2877 res = wait_file2(chan, vms, "vm-message"); /* "message" */
2878 if (msg && (msg != vms->lastmsg)) {
2880 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2884 /* Retrieve info from VM attribute file */
2885 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2886 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2887 msg_cfg = ast_load(filename);
2889 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2893 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2896 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2898 context = ast_variable_retrieve(msg_cfg, "message", "context");
2899 if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2900 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2902 if ((!res)&&(vmu->envelope))
2903 res = play_message_datetime(chan, vmu, origtime, filename);
2904 if ((!res)&&(vmu->saycid))
2905 res = play_message_callerid(chan, vms, cid, context, 0);
2906 /* Allow pressing '1' to skip envelope / callerid */
2909 ast_destroy(msg_cfg);
2912 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2913 vms->heard[msg] = 1;
2914 res = wait_file(chan, vms, vms->fn);
2919 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2921 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2922 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2923 vms->lastmsg = count_messages(vms->curdir) - 1;
2924 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2927 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2930 char ntxt[256] = "";
2932 if (vms->lastmsg > -1) {
2933 /* Get the deleted messages fixed */
2935 for (x=0;x < MAXMSG;x++) {
2936 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2937 /* Save this message. It's not in INBOX or hasn't been heard */
2938 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2939 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2942 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2943 if (strcmp(vms->fn, vms->fn2)) {
2944 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2945 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2946 ast_filerename(vms->fn, vms->fn2, NULL);
2949 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2950 /* Move to old folder before deleting */
2951 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2954 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2955 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2956 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2961 memset(vms->deleted, 0, sizeof(vms->deleted));
2962 memset(vms->heard, 0, sizeof(vms->heard));