2 * Asterisk -- A telephony toolkit for Linux.
4 * Voicemail System (did you ever think it could be so easy?)
6 * Copyright (C) 2003, Digium Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
29 #include <asterisk/cli.h>
38 #include <sys/types.h>
42 /* we define USESQLVM when we have MySQL or POSTGRES */
44 #include <mysql/mysql.h>
50 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
52 #include <postgresql/libpq-fe.h>
57 static inline int sql_init(void) { return 0; }
58 static inline void sql_close(void) { }
62 #include "../asterisk.h"
63 #include "../astconf.h"
65 #define COMMAND_TIMEOUT 5000
67 #define VOICEMAIL_CONFIG "voicemail.conf"
68 #define ASTERISK_USERNAME "asterisk"
70 /* Default mail command to mail voicemail. Change it with the
71 mailcmd= command in voicemail.conf */
72 #define SENDMAIL "/usr/sbin/sendmail -t"
74 #define INTRO "vm-intro"
77 #define MAX_OTHER_FORMATS 10
79 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
81 #define BASEMAXINLINE 256
82 #define BASELINELEN 72
83 #define BASEMAXINLINE 256
86 #define MAX_DATETIME_FORMAT 512
87 #define MAX_NUM_CID_CONTEXTS 10
89 #define DIGITS_DIR AST_SOUNDS "/digits/"
97 unsigned char iobuf[BASEMAXINLINE];
100 /* Structure for linked list of users */
108 char serveremail[80];
109 char mailcmd[160]; /* Configurable mail command */
120 struct ast_vm_user *next;
126 char msg_format[512];
127 struct vm_zone *next;
146 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
147 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
148 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);
149 static int vm_delete(char *file);
153 static char *tdesc = "Comedian Mail (Voicemail System)";
155 static char *adapp = "\x00\x00\x00\x0F";
157 static char *adsec = "\x9B\xDB\xF7\xAC";
159 static char *addesc = "Comedian Mail";
161 static int adver = 1;
163 static char *synopsis_vm =
164 "Leave a voicemail message";
166 static char *descrip_vm =
167 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
168 "extension (must be configured in voicemail.conf).\n"
169 " If the extension is preceded by \n"
170 "* 's' then instructions for leaving the message will be skipped.\n"
171 "* 'u' then the \"unavailable\" message will be played.\n"
172 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
173 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
174 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
175 "priority 'o' in the current context.\n"
176 "If the caller presses '*' during the prompt, the call jumps to\n"
177 "priority 'a' in the current context.\n"
178 "If the requested mailbox does not exist, and there exists a priority\n"
179 "n + 101, then that priority will be taken next.\n"
180 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
181 "Otherwise, it returns 0.\n";
183 static char *synopsis_vmain =
184 "Enter voicemail system";
186 static char *descrip_vmain =
187 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
188 "for the checking of voicemail. The mailbox can be passed as the option,\n"
189 "which will stop the voicemail system from prompting the user for the mailbox.\n"
190 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
191 "a context is specified, logins are considered in that voicemail context only.\n"
192 "Returns -1 if the user hangs up or 0 otherwise.\n";
194 static char *synopsis_vm_box_exists =
195 "Check if vmbox exists";
197 static char *descrip_vm_box_exists =
198 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
199 "if the specified voice mailbox exists.\n";
202 /* Leave a message */
203 static char *capp = "VoiceMail2";
204 static char *app = "VoiceMail";
206 /* Check mail, control, etc */
207 static char *capp2 = "VoiceMailMain2";
208 static char *app2 = "VoiceMailMain";
210 static char *app3 = "MailboxExists";
212 AST_MUTEX_DEFINE_STATIC(vmlock);
213 struct ast_vm_user *users;
214 struct ast_vm_user *usersl;
215 struct vm_zone *zones = NULL;
216 struct vm_zone *zonesl = NULL;
217 static int attach_voicemail;
218 static int maxsilence;
219 static int silencethreshold = 128;
220 static char serveremail[80];
221 static char mailcmd[160]; /* Configurable mail cmd */
222 static char externnotify[160];
224 static char vmfmts[80];
225 static int vmminmessage;
226 static int vmmaxmessage;
229 static int maxlogins;
233 static int saycidinfo;
235 static char dialcontext[80];
236 static char callcontext[80];
237 static char exitcontext[80];
239 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
242 static char *emailbody = NULL;
243 static int pbxskip = 0;
244 static char *emailsubject = NULL;
245 static char fromstring[100];
246 static char emailtitle[100];
247 static char charset[32] = "ISO-8859-1";
254 static void populate_defaults(struct ast_vm_user *vmu)
266 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
268 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
270 strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
273 static void apply_options(struct ast_vm_user *vmu, char *options)
275 /* Destructively Parse options and apply */
276 char *stringp = ast_strdupa(options);
280 while((s = strsep(&stringp, "|"))) {
282 if ((var = strsep(&value, "=")) && value) {
283 if (!strcasecmp(var, "attach")) {
288 } else if (!strcasecmp(var, "serveremail")) {
289 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
290 } else if (!strcasecmp(var, "tz")) {
291 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
292 } else if (!strcasecmp(var, "saycid")){
297 } else if (!strcasecmp(var, "review")){
302 } else if (!strcasecmp(var, "operator")){
307 } else if (!strcasecmp(var, "envelope")){
312 } else if (!strcasecmp(var, "callback")) {
313 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
314 } else if (!strcasecmp(var, "dialout")) {
315 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
316 } else if (!strcasecmp(var, "exitcontext")) {
317 strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
326 #include "mysql-vm-routines.h"
333 ast_mutex_t postgreslock;
335 static int sql_init(void)
337 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
338 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
340 dbhandler=PQconnectdb(dboption);
341 if (PQstatus(dbhandler) == CONNECTION_BAD) {
342 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
345 ast_mutex_init(&postgreslock);
347 /* fprintf(stderr,"postgres login OK\n"); */
351 static void sql_close(void)
357 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
365 char options[160] = "";
366 struct ast_vm_user *retval;
368 retval=malloc(sizeof(struct ast_vm_user));
370 /* fprintf(stderr,"postgres find_user:\n"); */
373 memset(retval, 0, sizeof(struct ast_vm_user));
376 strcpy(retval->mailbox, mailbox);
379 strcpy(retval->context, context);
383 strcpy(retval->context, "default");
385 populate_defaults(retval);
386 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
388 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
389 ast_mutex_lock(&postgreslock);
390 PGSQLres=PQexec(dbhandler,query);
391 if (PGSQLres!=NULL) {
392 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
393 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
394 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
396 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
399 ast_mutex_unlock(&postgreslock);
403 numFields = PQnfields(PGSQLres);
404 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
405 if (PQntuples(PGSQLres) != 1) {
406 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
408 ast_mutex_unlock(&postgreslock);
412 for (i=0; i<numFields; i++) {
413 fname = PQfname(PGSQLres,i);
414 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
415 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
416 } else if (!strcmp(fname, "fullname")) {
417 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
418 } else if (!strcmp(fname, "email")) {
419 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
420 } else if (!strcmp(fname, "pager")) {
421 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
422 } else if (!strcmp(fname, "options")) {
423 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
424 apply_options(retval, options);
429 ast_mutex_unlock(&postgreslock);
433 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
434 ast_mutex_unlock(&postgreslock);
439 } /* malloc() retval */
444 static void vm_change_password(struct ast_vm_user *vmu, char *password)
449 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);
451 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
453 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
454 ast_mutex_lock(&postgreslock);
455 PQexec(dbhandler, query);
456 strcpy(vmu->password, password);
457 ast_mutex_unlock(&postgreslock);
460 static void reset_user_pw(char *context, char *mailbox, char *password)
465 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
467 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
469 ast_mutex_lock(&postgreslock);
470 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
471 PQexec(dbhandler, query);
472 ast_mutex_unlock(&postgreslock);
475 #endif /* Postgres */
479 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
481 /* This function could be made to generate one from a database, too */
482 struct ast_vm_user *vmu=NULL, *cur;
483 ast_mutex_lock(&vmlock);
486 if ((!context || !strcasecmp(context, cur->context)) &&
487 (!strcasecmp(mailbox, cur->mailbox)))
495 /* Make a copy, so that on a reload, we have no race */
496 vmu = malloc(sizeof(struct ast_vm_user));
498 memcpy(vmu, cur, sizeof(struct ast_vm_user));
506 ast_mutex_unlock(&vmlock);
510 static int reset_user_pw(char *context, char *mailbox, char *newpass)
512 /* This function could be made to generate one from a database, too */
513 struct ast_vm_user *cur;
515 ast_mutex_lock(&vmlock);
518 if ((!context || !strcasecmp(context, cur->context)) &&
519 (!strcasecmp(mailbox, cur->mailbox)))
524 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
527 ast_mutex_unlock(&vmlock);
531 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
533 /* There's probably a better way of doing this. */
534 /* That's why I've put the password change in a separate function. */
535 /* This could also be done with a database function */
542 char currcontext[256] ="";
543 char tmpin[AST_CONFIG_MAX_PATH];
544 char tmpout[AST_CONFIG_MAX_PATH];
545 char *user, *pass, *rest, *trim, *tempcontext;
547 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
548 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
549 configin = fopen((char *)tmpin,"r");
551 configout = fopen((char *)tmpout,"w+");
554 if(!configin || !configout) {
558 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
562 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
566 while (!feof(configin)) {
567 /* Read in the line */
568 fgets(inbuf, sizeof(inbuf), configin);
570 if (!feof(configin)) {
571 /* Make a backup of it */
572 memcpy(orig, inbuf, sizeof(orig));
573 /* Strip trailing \n and comment */
574 inbuf[strlen(inbuf) - 1] = '\0';
575 user = strchr(inbuf, ';');
581 /* check for '[' (opening of context name ) */
582 tempcontext = strchr(user, '[');
584 strncpy(currcontext, tempcontext +1,
585 sizeof(currcontext) - 1);
586 /* now check for ']' */
587 tempcontext = strchr(currcontext, ']');
591 currcontext[0] = '\0';
593 pass = strchr(user, '=');
596 while(*trim && *trim < 33) {
606 while(*pass && *pass < 33)
610 rest = strchr(pass,',');
618 /* Compare user, pass AND context */
619 if (user && *user && !strcmp(user, vmu->mailbox) &&
620 pass && *pass && !strcmp(pass, vmu->password) &&
621 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
622 /* This is the line */
624 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
626 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
629 /* Put it back like it was */
630 fprintf(configout, orig);
637 unlink((char *)tmpin);
638 rename((char *)tmpout,(char *)tmpin);
639 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
640 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
644 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
646 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
649 static int make_file(char *dest, int len, char *dir, int num)
651 return snprintf(dest, len, "%s/msg%04d", dir, num);
655 inbuf(struct baseio *bio, FILE *fi)
662 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
677 inchar(struct baseio *bio, FILE *fi)
679 if(bio->iocp>=bio->iolen)
683 return bio->iobuf[bio->iocp++];
687 ochar(struct baseio *bio, int c, FILE *so)
689 if(bio->linelength>=BASELINELEN) {
690 if(fputs(eol,so)==EOF)
696 if(putc(((unsigned char)c),so)==EOF)
704 static int base_encode(char *filename, FILE *so)
706 unsigned char dtable[BASEMAXINLINE];
711 memset(&bio, 0, sizeof(bio));
712 bio.iocp = BASEMAXINLINE;
714 if ( !(fi = fopen(filename, "rb"))) {
715 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
723 dtable[26+i+9]= 'j'+i;
727 dtable[26+i+18]= 's'+i;
736 unsigned char igroup[3],ogroup[4];
739 igroup[0]= igroup[1]= igroup[2]= 0;
742 if ( (c = inchar(&bio, fi)) == EOF) {
747 igroup[n]= (unsigned char)c;
751 ogroup[0]= dtable[igroup[0]>>2];
752 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
753 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
754 ogroup[3]= dtable[igroup[2]&0x3F];
764 ochar(&bio, ogroup[i], so);
768 if(fputs(eol,so)==EOF)
776 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)
778 /* Prepare variables for substition in email body and subject */
779 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
780 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
781 sprintf(passdata,"%d",msgnum);
782 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
783 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
784 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
785 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
788 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)
798 char tmp[80] = "/tmp/astmail-XXXXXX";
802 struct vm_zone *the_zone = NULL;
804 if (!strcmp(format, "wav49"))
806 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
807 /* Make a temporary file instead of piping directly to sendmail, in case the mail
811 p = fdopen(pfd, "w");
818 gethostname(host, sizeof(host));
819 if (strchr(srcemail, '@'))
820 strncpy(who, srcemail, sizeof(who)-1);
822 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
824 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
827 /* Does this user have a timezone specified? */
828 if (strlen(vmu->zonetag)) {
829 /* Find the zone in the list */
833 if (!strcmp(z->name, vmu->zonetag)) {
842 ast_localtime(&t,&tm,the_zone->timezone);
844 ast_localtime(&t,&tm,NULL);
845 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
846 fprintf(p, "Date: %s\n", date);
849 struct ast_channel *ast = ast_channel_alloc(0);
852 int vmlen = strlen(fromstring)*3 + 200;
853 if ((passdata = alloca(vmlen))) {
854 memset(passdata, 0, vmlen);
855 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
856 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
857 fprintf(p, "From: %s <%s>\n",passdata,who);
858 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
859 ast_channel_free(ast);
860 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
862 fprintf(p, "From: Asterisk PBX <%s>\n", who);
863 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
866 struct ast_channel *ast = ast_channel_alloc(0);
869 int vmlen = strlen(emailsubject)*3 + 200;
870 if ((passdata = alloca(vmlen))) {
871 memset(passdata, 0, vmlen);
872 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
873 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
874 fprintf(p, "Subject: %s\n",passdata);
875 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
876 ast_channel_free(ast);
877 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
881 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
886 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
888 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
889 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
890 fprintf(p, "MIME-Version: 1.0\n");
891 if (attach_user_voicemail) {
892 /* Something unique. */
893 snprintf(bound, sizeof(bound), "voicemail_%d%s%d", msgnum, mailbox, getpid());
895 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
897 fprintf(p, "--%s\n", bound);
899 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
900 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
902 struct ast_channel *ast = ast_channel_alloc(0);
905 int vmlen = strlen(emailbody)*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,emailbody,passdata,vmlen);
910 fprintf(p, "%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");
915 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
917 "in mailbox %s from %s, on %s so you might\n"
918 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
919 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
921 if (attach_user_voicemail) {
922 fprintf(p, "--%s\n", bound);
923 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
924 fprintf(p, "Content-Transfer-Encoding: base64\n");
925 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
926 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
928 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
929 base_encode(fname, p);
930 fprintf(p, "\n\n--%s--\n.\n", bound);
933 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
934 ast_safe_system(tmp2);
935 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
937 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
943 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, int duration, struct ast_vm_user *vmu)
951 char tmp[80] = "/tmp/astmail-XXXXXX";
955 struct vm_zone *the_zone = NULL;
959 p = fdopen(pfd, "w");
967 gethostname(host, sizeof(host));
968 if (strchr(srcemail, '@'))
969 strncpy(who, srcemail, sizeof(who)-1);
971 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
973 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
976 /* Does this user have a timezone specified? */
977 if (strlen(vmu->zonetag)) {
978 /* Find the zone in the list */
982 if (!strcmp(z->name, vmu->zonetag)) {
991 ast_localtime(&t,&tm,the_zone->timezone);
993 ast_localtime(&t,&tm,NULL);
995 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
996 fprintf(p, "Date: %s\n", date);
997 fprintf(p, "From: Asterisk PBX <%s>\n", who);
998 fprintf(p, "To: %s\n", pager);
999 fprintf(p, "Subject: New VM\n\n");
1000 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1001 fprintf(p, "New %s long msg in box %s\n"
1002 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
1004 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1005 ast_safe_system(tmp2);
1006 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
1008 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1014 static int get_date(char *s, int len)
1019 localtime_r(&t,&tm);
1020 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1023 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1027 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1028 if (ast_fileexists(fn, NULL, NULL) > 0) {
1029 res = ast_streamfile(chan, fn, chan->language);
1032 res = ast_waitstream(chan, ecodes);
1036 res = ast_streamfile(chan, "vm-theperson", chan->language);
1039 res = ast_waitstream(chan, ecodes);
1042 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1047 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1049 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1052 res = ast_waitstream(chan, ecodes);
1056 static int play_and_wait(struct ast_channel *chan, char *fn)
1059 d = ast_streamfile(chan, fn, chan->language);
1062 d = ast_waitstream(chan, AST_DIGIT_ANY);
1063 ast_stopstream(chan);
1067 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep)
1071 int x, fmtcnt=1, res=-1,outmsg=0;
1072 struct ast_frame *f;
1073 struct ast_filestream *others[MAX_OTHER_FORMATS];
1074 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
1075 char *sfmt[MAX_OTHER_FORMATS];
1078 struct ast_dsp *sildet; /* silence detector dsp */
1079 int totalsilence = 0;
1081 int gotsilence = 0; /* did we timeout for silence? */
1083 char prependfile[80];
1085 /* barf if no pointer passed to store duration in */
1086 if (duration == NULL) {
1087 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
1091 ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1092 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1094 if (playfile || beep) {
1096 d = play_and_wait(chan, playfile);
1098 d = ast_streamfile(chan, "beep",chan->language);
1100 d = ast_waitstream(chan,"");
1104 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
1105 strcat(prependfile, "-prepend");
1107 fmts = ast_strdupa(fmt);
1110 strsep(&stringp, "|");
1111 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1112 sfmt[0] = ast_strdupa(fmts);
1114 while((fmt = strsep(&stringp, "|"))) {
1115 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1116 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1119 sfmt[fmtcnt++] = ast_strdupa(fmt);
1123 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1124 for (x=0;x<fmtcnt;x++) {
1125 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1126 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1132 sildet = ast_dsp_new(); /* Create the silence detector */
1134 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1137 ast_dsp_set_threshold(sildet, silencethreshold);
1139 if (maxsilence > 0) {
1140 rfmt = chan->readformat;
1141 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1143 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1149 /* Loop forever, writing the packets we read to the writer(s), until
1150 we read a # or get a hangup */
1153 res = ast_waitfor(chan, 2000);
1155 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1156 /* Try one more time in case of masq */
1157 res = ast_waitfor(chan, 2000);
1159 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1171 if (f->frametype == AST_FRAME_VOICE) {
1172 /* write each format */
1173 for (x=0;x<fmtcnt;x++) {
1176 res = ast_writestream(others[x], f);
1179 /* Silence Detection */
1180 if (maxsilence > 0) {
1182 ast_dsp_silence(sildet, f, &dspsilence);
1184 totalsilence = dspsilence;
1188 if (totalsilence > maxsilence) {
1189 /* Ended happily with silence */
1196 /* Exit on any error */
1198 ast_log(LOG_WARNING, "Error writing frame\n");
1202 } else if (f->frametype == AST_FRAME_VIDEO) {
1203 /* Write only once */
1204 ast_writestream(others[0], f);
1205 } else if (f->frametype == AST_FRAME_DTMF) {
1206 /* stop recording with any digit */
1207 if (option_verbose > 2)
1208 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1216 if (maxtime < (end - start)) {
1217 if (option_verbose > 2)
1218 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1227 if (end == start) time(&end);
1229 if (option_verbose > 2)
1230 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1234 /* delete all the prepend files */
1235 for (x=0;x<fmtcnt;x++) {
1238 ast_closestream(others[x]);
1239 ast_filedelete(prependfile, sfmt[x]);
1244 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1246 *duration = end - start;
1252 struct ast_frame *fr;
1253 for (x=0;x<fmtcnt;x++) {
1254 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1255 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1256 if (!others[x] || !realfiles[x])
1259 ast_stream_rewind(others[x], totalsilence-200);
1261 ast_stream_rewind(others[x], 200);
1262 ast_truncstream(others[x]);
1263 /* add the original file too */
1264 while ((fr = ast_readframe(realfiles[x]))) {
1265 ast_writestream(others[x],fr);
1267 ast_closestream(others[x]);
1268 ast_closestream(realfiles[x]);
1269 ast_filerename(prependfile, recordfile, sfmt[x]);
1271 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1273 ast_filedelete(prependfile, sfmt[x]);
1277 if (ast_set_read_format(chan, rfmt)) {
1278 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1283 /* Let them know it worked */
1284 ast_streamfile(chan, "auth-thankyou", chan->language);
1285 ast_waitstream(chan, "");
1291 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1295 int x, fmtcnt=1, res=-1,outmsg=0;
1296 struct ast_frame *f;
1297 struct ast_filestream *others[MAX_OTHER_FORMATS];
1298 char *sfmt[MAX_OTHER_FORMATS];
1301 struct ast_dsp *sildet; /* silence detector dsp */
1302 int totalsilence = 0;
1304 int gotsilence = 0; /* did we timeout for silence? */
1307 /* barf if no pointer passed to store duration in */
1308 if (duration == NULL) {
1309 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1313 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1314 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1317 d = play_and_wait(chan, playfile);
1319 d = ast_streamfile(chan, "beep",chan->language);
1321 d = ast_waitstream(chan,"");
1326 fmts = ast_strdupa(fmt);
1329 strsep(&stringp, "|");
1330 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1331 sfmt[0] = ast_strdupa(fmts);
1333 while((fmt = strsep(&stringp, "|"))) {
1334 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1335 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1338 sfmt[fmtcnt++] = ast_strdupa(fmt);
1342 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1343 for (x=0;x<fmtcnt;x++) {
1344 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1345 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1352 sildet = ast_dsp_new(); /* Create the silence detector */
1354 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1357 ast_dsp_set_threshold(sildet, silencethreshold);
1359 if (maxsilence > 0) {
1360 rfmt = chan->readformat;
1361 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1363 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1369 /* Loop forever, writing the packets we read to the writer(s), until
1370 we read a # or get a hangup */
1373 res = ast_waitfor(chan, 2000);
1375 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1376 /* Try one more time in case of masq */
1377 res = ast_waitfor(chan, 2000);
1379 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1391 if (f->frametype == AST_FRAME_VOICE) {
1392 /* write each format */
1393 for (x=0;x<fmtcnt;x++) {
1394 res = ast_writestream(others[x], f);
1397 /* Silence Detection */
1398 if (maxsilence > 0) {
1400 ast_dsp_silence(sildet, f, &dspsilence);
1402 totalsilence = dspsilence;
1406 if (totalsilence > maxsilence) {
1407 /* Ended happily with silence */
1414 /* Exit on any error */
1416 ast_log(LOG_WARNING, "Error writing frame\n");
1420 } else if (f->frametype == AST_FRAME_VIDEO) {
1421 /* Write only once */
1422 ast_writestream(others[0], f);
1423 } else if (f->frametype == AST_FRAME_DTMF) {
1424 if (f->subclass == '#') {
1425 if (option_verbose > 2)
1426 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1433 if (f->subclass == '0') {
1434 /* Check for a '0' during message recording also, in case caller wants operator */
1435 if (option_verbose > 2)
1436 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1444 if (maxtime < (end - start)) {
1445 if (option_verbose > 2)
1446 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1455 if (end == start) time(&end);
1457 if (option_verbose > 2)
1458 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1463 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1466 *duration = end - start;
1468 for (x=0;x<fmtcnt;x++) {
1472 ast_stream_rewind(others[x], totalsilence-200);
1474 ast_stream_rewind(others[x], 200);
1475 ast_truncstream(others[x]);
1476 ast_closestream(others[x]);
1479 if (ast_set_read_format(chan, rfmt)) {
1480 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1485 /* Let them know recording is stopped */
1486 ast_streamfile(chan, "auth-thankyou", chan->language);
1487 ast_waitstream(chan, "");
1494 static void free_user(struct ast_vm_user *vmu)
1500 static void free_zone(struct vm_zone *z)
1505 static void run_externnotify(char *context, char *extension, int numvoicemails)
1507 char arguments[255];
1509 if(externnotify[0]) {
1510 strncpy(arguments, externnotify, sizeof(arguments));
1511 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1512 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1513 ast_safe_system(arguments);
1518 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1530 char prefile[256]="";
1531 char ext_context[256] = "";
1534 char ecodes[16] = "#";
1537 struct ast_vm_user *vmu;
1538 struct ast_vm_user svm;
1541 strncpy(tmp, ext, sizeof(tmp) - 1);
1543 context = strchr(tmp, '@');
1549 if ((vmu = find_user(&svm, context, ext))) {
1550 /* Setup pre-file if appropriate */
1551 if (strcmp(vmu->context, "default"))
1552 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1554 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1556 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1558 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1559 make_dir(dir, sizeof(dir), vmu->context, "", "");
1560 /* It's easier just to try to make it than to check for its existence */
1561 if (mkdir(dir, 0700) && (errno != EEXIST))
1562 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1563 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1564 /* It's easier just to try to make it than to check for its existence */
1565 if (mkdir(dir, 0700) && (errno != EEXIST))
1566 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1567 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1568 if (mkdir(dir, 0700) && (errno != EEXIST))
1569 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1570 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1571 strcat(ecodes, "0");
1572 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "a", 1, chan->callerid))
1573 strcat(ecodes, "*");
1574 /* Play the beginning intro if desired */
1575 if (strlen(prefile)) {
1576 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1577 if (ast_streamfile(chan, prefile, chan->language) > -1)
1578 res = ast_waitstream(chan, ecodes);
1580 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1581 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1584 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1590 /* On a '#' we skip the instructions */
1594 if (!res && !silent) {
1595 res = ast_streamfile(chan, INTRO, chan->language);
1597 res = ast_waitstream(chan, ecodes);
1604 ast_stopstream(chan);
1605 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1606 other than the operator -- an automated attendant or mailbox login for example */
1608 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1609 if (strlen(vmu->exit)) {
1610 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1611 } else if (strlen(chan->macrocontext)) {
1612 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1618 /* Check for a '0' here */
1621 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1622 if (strlen(vmu->exit)) {
1623 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1624 } else if (strlen(chan->macrocontext)) {
1625 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1632 /* Unless we're *really* silent, try to send the beep */
1633 res = ast_streamfile(chan, "beep", chan->language);
1635 res = ast_waitstream(chan, "");
1641 /* The meat of recording the message... All the announcements and beeps have been played*/
1642 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1646 make_file(fn, sizeof(fn), dir, msgnum);
1647 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1648 (chan->callerid ? chan->callerid : "Unknown"),
1649 vmu->fullname, ext, chan->name);
1650 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1653 } while(msgnum < MAXMSG);
1654 if (msgnum < MAXMSG) {
1655 /* Store information */
1656 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1657 txt = fopen(txtfile, "w+");
1659 get_date(date, sizeof(date));
1662 "; Message Information file\n"
1680 chan->callerid ? chan->callerid : "Unknown",
1681 date, (long)time(NULL));
1684 ast_log(LOG_WARNING, "Error opening text file for output\n");
1685 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1690 fd = open(txtfile, O_APPEND | O_WRONLY);
1692 txt = fdopen(fd, "a");
1694 fprintf(txt, "duration=%d\n", duration);
1699 if (duration < vmminmessage) {
1704 strsep(&stringp, "|");
1705 /* Send e-mail if applicable */
1706 if (strlen(vmu->email)) {
1707 int attach_user_voicemail = attach_voicemail;
1708 char *myserveremail = serveremail;
1709 if (vmu->attach > -1)
1710 attach_user_voicemail = vmu->attach;
1711 if (strlen(vmu->serveremail))
1712 myserveremail = vmu->serveremail;
1713 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, duration, attach_user_voicemail);
1715 if (strlen(vmu->pager)) {
1716 char *myserveremail = serveremail;
1717 if (strlen(vmu->serveremail))
1718 myserveremail = vmu->serveremail;
1719 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, duration, vmu);
1722 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1724 res = ast_waitstream(chan, "");
1725 ast_log(LOG_WARNING, "No more messages possible\n");
1728 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1732 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1733 /*Send the call to n+101 priority, where n is the current priority*/
1734 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1735 chan->priority+=100;
1737 /* Leave voicemail for someone */
1738 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
1740 /* If an external program is specified to be run after leaving a voicemail */
1741 run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
1746 static char *mbox(int id)
1774 static int count_messages(char *dir)
1778 for (x=0;x<MAXMSG;x++) {
1779 make_file(fn, sizeof(fn), dir, x);
1780 if (ast_fileexists(fn, NULL, NULL) < 1)
1786 static int say_and_wait(struct ast_channel *chan, int num)
1789 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL);
1793 static int copy(char *infile, char *outfile)
1800 if ((ifd = open(infile, O_RDONLY)) < 0) {
1801 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1804 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1805 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1810 len = read(ifd, buf, sizeof(buf));
1812 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1818 res = write(ofd, buf, len);
1820 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1832 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1839 char *dbox = mbox(box);
1841 make_file(sfn, sizeof(sfn), dir, msg);
1842 make_dir(ddir, sizeof(ddir), context, username, dbox);
1844 for (x=0;x<MAXMSG;x++) {
1845 make_file(dfn, sizeof(dfn), ddir, x);
1846 if (ast_fileexists(dfn, NULL, NULL) < 0)
1851 ast_filecopy(sfn, dfn, NULL);
1852 if (strcmp(sfn, dfn)) {
1853 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1854 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1860 static int adsi_logo(unsigned char *buf)
1863 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1864 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1868 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1876 bytes += adsi_data_mode(buf + bytes);
1877 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1880 bytes += adsi_logo(buf);
1881 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1883 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1885 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1886 bytes += adsi_data_mode(buf + bytes);
1887 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1889 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1891 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1892 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1893 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1894 bytes += adsi_voice_mode(buf + bytes, 0);
1895 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1902 bytes += adsi_logo(buf);
1903 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1904 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1905 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1906 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1909 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1910 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1911 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1912 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1913 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1914 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1915 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1918 /* Add another dot */
1920 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1921 bytes += adsi_voice_mode(buf + bytes, 0);
1923 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1924 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1928 /* These buttons we load but don't use yet */
1929 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1930 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1931 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1932 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1933 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1934 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1935 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1938 /* Add another dot */
1940 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1941 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1942 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1947 snprintf(num, sizeof(num), "%d", x);
1948 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1950 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1951 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1954 /* Add another dot */
1956 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1957 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1958 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1961 if (adsi_end_download(chan)) {
1963 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1964 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1965 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1966 bytes += adsi_voice_mode(buf + bytes, 0);
1967 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1971 bytes += adsi_download_disconnect(buf + bytes);
1972 bytes += adsi_voice_mode(buf + bytes, 0);
1973 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1975 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1980 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1981 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1983 ast_log(LOG_DEBUG, "Restarting session...\n");
1986 /* Load the session now */
1987 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1989 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1991 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1993 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1997 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2000 if (!adsi_available(chan))
2002 x = adsi_load_session(chan, adapp, adver, 1);
2006 if (adsi_load_vmail(chan, useadsi)) {
2007 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2014 static void adsi_login(struct ast_channel *chan)
2018 unsigned char keys[8];
2020 if (!adsi_available(chan))
2025 /* Set one key for next */
2026 keys[3] = ADSI_KEY_APPS + 3;
2028 bytes += adsi_logo(buf + bytes);
2029 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2030 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2031 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2032 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2033 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2034 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2035 bytes += adsi_set_keys(buf + bytes, keys);
2036 bytes += adsi_voice_mode(buf + bytes, 0);
2037 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2040 static void adsi_password(struct ast_channel *chan)
2044 unsigned char keys[8];
2046 if (!adsi_available(chan))
2051 /* Set one key for next */
2052 keys[3] = ADSI_KEY_APPS + 3;
2054 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2055 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2056 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2057 bytes += adsi_set_keys(buf + bytes, keys);
2058 bytes += adsi_voice_mode(buf + bytes, 0);
2059 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2062 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2066 unsigned char keys[8];
2069 if (!adsi_available(chan))
2073 y = ADSI_KEY_APPS + 12 + start + x;
2074 if (y > ADSI_KEY_APPS + 12 + 4)
2076 keys[x] = ADSI_KEY_SKT | y;
2078 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2082 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2083 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2084 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2085 bytes += adsi_set_keys(buf + bytes, keys);
2086 bytes += adsi_voice_mode(buf + bytes, 0);
2088 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2091 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2094 char buf[256], buf1[256], buf2[256];
2100 char datetime[21]="";
2103 unsigned char keys[8];
2107 if (!adsi_available(chan))
2110 /* Retrieve important info */
2111 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2112 f = fopen(fn2, "r");
2115 fgets(buf, sizeof(buf), f);
2119 strsep(&stringp, "=");
2120 val = strsep(&stringp, "=");
2121 if (val && strlen(val)) {
2122 if (!strcmp(buf, "callerid"))
2123 strncpy(cid, val, sizeof(cid) - 1);
2124 if (!strcmp(buf, "origdate"))
2125 strncpy(datetime, val, sizeof(datetime) - 1);
2131 /* New meaning for keys */
2133 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2138 /* No prev key, provide "Folder" instead */
2139 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2142 /* If last message ... */
2144 /* but not only message, provide "Folder" instead */
2145 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2146 bytes += adsi_voice_mode(buf + bytes, 0);
2149 /* Otherwise if only message, leave blank */
2155 ast_callerid_parse(cid, &name, &num);
2159 name = "Unknown Caller";
2161 /* If deleted, show "undeleted" */
2164 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2167 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2168 snprintf(buf1, sizeof(buf1), "%s%s", folder,
2169 strcasecmp(folder, "INBOX") ? " Messages" : "");
2170 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2172 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2173 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2174 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2175 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2176 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2177 bytes += adsi_set_keys(buf + bytes, keys);
2178 bytes += adsi_voice_mode(buf + bytes, 0);
2180 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2183 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2187 unsigned char keys[8];
2191 if (!adsi_available(chan))
2194 /* New meaning for keys */
2196 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2202 /* No prev key, provide "Folder" instead */
2203 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2206 /* If last message ... */
2208 /* but not only message, provide "Folder" instead */
2209 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2211 /* Otherwise if only message, leave blank */
2216 /* If deleted, show "undeleted" */
2218 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2221 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2222 bytes += adsi_set_keys(buf + bytes, keys);
2223 bytes += adsi_voice_mode(buf + bytes, 0);
2225 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2228 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2230 char buf[256], buf1[256], buf2[256];
2232 unsigned char keys[8];
2235 char *newm = (new == 1) ? "message" : "messages";
2236 char *oldm = (old == 1) ? "message" : "messages";
2237 if (!adsi_available(chan))
2240 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2242 strcat(buf1, " and");
2243 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2245 snprintf(buf2, sizeof(buf2), "%s.", newm);
2248 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2249 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2251 strcpy(buf1, "You have no messages.");
2254 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2255 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2256 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2259 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2263 /* Don't let them listen if there are none */
2266 bytes += adsi_set_keys(buf + bytes, keys);
2268 bytes += adsi_voice_mode(buf + bytes, 0);
2270 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2273 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2275 char buf[256], buf1[256], buf2[256];
2277 unsigned char keys[8];
2280 char *mess = (messages == 1) ? "message" : "messages";
2282 if (!adsi_available(chan))
2285 /* Original command keys */
2287 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2295 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2296 strcasecmp(folder, "INBOX") ? " folder" : "");
2299 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2301 strcpy(buf2, "no messages.");
2302 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2303 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2304 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2305 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2306 bytes += adsi_set_keys(buf + bytes, keys);
2308 bytes += adsi_voice_mode(buf + bytes, 0);
2310 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2315 static void adsi_clear(struct ast_channel *chan)
2319 if (!adsi_available(chan))
2321 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2322 bytes += adsi_voice_mode(buf + bytes, 0);
2324 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2328 static void adsi_goodbye(struct ast_channel *chan)
2333 if (!adsi_available(chan))
2335 bytes += adsi_logo(buf + bytes);
2336 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2337 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2338 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2339 bytes += adsi_voice_mode(buf + bytes, 0);
2341 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2344 static int get_folder(struct ast_channel *chan, int start)
2349 d = play_and_wait(chan, "vm-press");
2352 for (x = start; x< 5; x++) {
2353 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2355 d = play_and_wait(chan, "vm-for");
2358 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2359 d = play_and_wait(chan, fn);
2362 d = play_and_wait(chan, "vm-messages");
2365 d = ast_waitfordigit(chan, 500);
2369 d = play_and_wait(chan, "vm-tocancel");
2372 d = ast_waitfordigit(chan, 4000);
2376 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2379 res = play_and_wait(chan, fn);
2380 while (((res < '0') || (res > '9')) &&
2381 (res != '#') && (res >= 0)) {
2382 res = get_folder(chan, 0);
2387 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2393 while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2398 /* prepend a message to the current message and return */
2401 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2402 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2412 cmd = play_and_wait(chan,"vm-forwardoptions");
2414 cmd = play_and_wait(chan,"vm-starmain");
2416 cmd = ast_waitfordigit(chan,6000);
2428 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2435 struct ast_config *mif;
2439 char ext_context[256]="";
2440 int res = 0, cmd = 0;
2441 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2444 int saved_messages = 0, found = 0;
2445 int valid_extensions = 0;
2446 while (!res && !valid_extensions) {
2447 res = ast_streamfile(chan, "vm-extension", chan->language);
2450 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2452 /* start all over if no username */
2453 if (!strlen(username))
2456 s = strsep(&stringp, "*");
2457 /* start optimistic */
2458 valid_extensions = 1;
2460 /* find_user is going to malloc since we have a NULL as first argument */
2461 if ((receiver = find_user(NULL, context, s))) {
2463 vmtmp = extensions = receiver;
2465 vmtmp->next = receiver;
2470 valid_extensions = 0;
2473 s = strsep(&stringp, "*");
2475 /* break from the loop of reading the extensions */
2476 if (valid_extensions)
2478 /* invalid extension, try again */
2479 res = play_and_wait(chan, "pbx-invalid");
2481 /* check if we're clear to proceed */
2482 if (!extensions || !valid_extensions)
2485 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2487 while(!res && vmtmp) {
2488 /* if (play_and_wait(chan, "vm-savedto"))
2491 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2492 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2493 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2494 ast_log(LOG_DEBUG, sys);
2495 ast_safe_system(sys);
2497 todircount = count_messages(todir);
2498 strncpy(tmp, fmt, sizeof(tmp) - 1);
2500 while((s = strsep(&stringp, "|"))) {
2501 /* XXX This is a hack -- we should use build_filename or similar XXX */
2502 if (!strcasecmp(s, "wav49"))
2504 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2505 ast_log(LOG_DEBUG, sys);
2506 ast_safe_system(sys);
2508 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2509 ast_log(LOG_DEBUG, sys);
2510 ast_safe_system(sys);
2511 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2513 /* load the information on the source message so we can send an e-mail like a new message */
2514 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2515 if ((mif=ast_load(miffile))) {
2517 /* set callerid and duration variables */
2518 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2519 s = ast_variable_retrieve(mif, NULL, "duration");
2524 if (strlen(vmtmp->email)) {
2525 int attach_user_voicemail = attach_voicemail;
2526 char *myserveremail = serveremail;
2527 if (vmtmp->attach > -1)
2528 attach_user_voicemail = vmtmp->attach;
2529 if (strlen(vmtmp->serveremail))
2530 myserveremail = vmtmp->serveremail;
2531 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2534 if (strlen(vmtmp->pager)) {
2535 char *myserveremail = serveremail;
2536 if (strlen(vmtmp->serveremail))
2537 myserveremail = vmtmp->serveremail;
2538 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2541 ast_destroy(mif); /* or here */
2543 /* Leave voicemail for someone */
2544 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2545 run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2549 vmtmp = vmtmp->next;
2552 if (saved_messages > 0) {
2553 /* give confirmatopm that the message was saved */
2554 /* commented out since we can't forward batches yet
2555 if (saved_messages == 1)
2556 res = play_and_wait(chan, "vm-message");
2558 res = play_and_wait(chan, "vm-messages");
2560 res = play_and_wait(chan, "vm-saved"); */
2562 res = play_and_wait(chan, "vm-msgsaved");
2565 return res ? res : cmd;
2569 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2572 if ((res = ast_streamfile(chan, file, chan->language)))
2573 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2575 res = ast_waitstream(chan, AST_DIGIT_ANY);
2579 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2581 return ast_control_streamfile(chan, file, "#", "*", "14679", "0", skipms);
2584 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2587 struct vm_zone *the_zone = NULL;
2591 if (sscanf(origtime,"%ld",&tin) < 1) {
2592 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2597 /* Does this user have a timezone specified? */
2598 if (strlen(vmu->zonetag)) {
2599 /* Find the zone in the list */
2603 if (!strcmp(z->name, vmu->zonetag)) {
2611 /* No internal variable parsing for now, so we'll comment it out for the time being */
2613 /* Set the DIFF_* variables */
2614 localtime_r(&t, &time_now);
2615 gettimeofday(&tv_now,NULL);
2616 tnow = tv_now.tv_sec;
2617 localtime_r(&tnow,&time_then);
2619 /* Day difference */
2620 if (time_now.tm_year == time_then.tm_year)
2621 sprintf(temp,"%d",time_now.tm_yday);
2623 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2624 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2626 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2629 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2631 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2633 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2640 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2644 char *callerid, *name;
2645 char prefile[256]="";
2648 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2649 /* 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 */
2650 if((cid == NULL)||(context == NULL))
2653 /* Strip off caller ID number from name */
2654 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2655 ast_callerid_parse(cid, &name, &callerid);
2656 if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2657 /* Check for internal contexts and only */
2658 /* say extension when the call didn't come from an internal context in the list */
2659 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2660 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2661 if((strcmp(cidinternalcontexts[i], context) == 0))
2664 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2666 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2667 if (strlen(prefile)) {
2668 /* See if we can find a recorded name for this person instead of their extension number */
2669 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2670 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2672 res = wait_file2(chan, vms, "vm-from");
2673 res = ast_streamfile(chan, prefile, chan->language) > -1;
2674 res = ast_waitstream(chan, "");
2676 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2677 /* BB: Say "from extension" as one saying to sound smoother */
2679 res = wait_file2(chan, vms, "vm-from-extension");
2680 res = ast_say_digit_str(chan, callerid, "", chan->language);
2687 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2688 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2690 res = wait_file2(chan, vms, "vm-from-phonenumber");
2691 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2695 /* Number unknown */
2696 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2698 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2699 res = wait_file2(chan, vms, "vm-unknown-caller");
2704 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2707 char filename[256],*origtime, *cid, *context;
2708 struct ast_config *msg_cfg;
2711 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2712 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2714 res = wait_file2(chan, vms, "vm-first");
2715 else if (msg == vms->lastmsg)
2716 res = wait_file2(chan, vms, "vm-last");
2718 res = wait_file2(chan, vms, "vm-message");
2719 if (msg && (msg != vms->lastmsg)) {
2721 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2725 /* Retrieve info from VM attribute file */
2727 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2728 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2729 msg_cfg = ast_load(filename);
2731 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2735 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2738 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2740 context = ast_variable_retrieve(msg_cfg, "message", "context");
2741 if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2742 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2744 if ((!res)&&(vmu->envelope))
2745 res = play_message_datetime(chan, vmu, origtime, filename);
2746 if ((!res)&&(vmu->saycid))
2747 res = play_message_callerid(chan, vms, cid, context, 0);
2748 /* Allow pressing '1' to skip envelope / callerid */
2751 ast_destroy(msg_cfg);
2754 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2755 vms->heard[msg] = 1;
2756 res = wait_file(chan, vms, vms->fn);
2761 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2763 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2764 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2765 vms->lastmsg = count_messages(vms->curdir) - 1;
2766 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2769 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2772 char ntxt[256] = "";
2774 if (vms->lastmsg > -1) {
2775 /* Get the deleted messages fixed */
2777 for (x=0;x < MAXMSG;x++) {
2778 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2779 /* Save this message. It's not in INBOX or hasn't been heard */
2780 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2781 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2784 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2785 if (strcmp(vms->fn, vms->fn2)) {
2786 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2787 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2788 ast_filerename(vms->fn, vms->fn2, NULL);
2791 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2792 /* Move to old folder before deleting */
2793 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2796 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2797 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2798 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2803 memset(vms->deleted, 0, sizeof(vms->deleted));
2804 memset(vms->heard, 0, sizeof(vms->heard));
2807 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2809 /* Introduce messages they have */
2811 res = play_and_wait(chan, "vm-youhave");
2813 if (vms->newmessages) {
2814 res = say_and_wait(chan, vms->newmessages);
2816 res = play_and_wait(chan, "vm-INBOX");
2817 if (vms->oldmessages && !res)
2818 res = play_and_wait(chan, "vm-and");
2820 if ((vms->newmessages == 1))
2821 res = play_and_wait(chan, "vm-message");
2823 res = play_and_wait(chan, "vm-messages");
2827 if (!res && vms->oldmessages) {
2828 res = say_and_wait(chan, vms->oldmessages);
2830 res = play_and_wait(chan, "vm-Old");
2832 if (vms->oldmessages == 1)
2833 res = play_and_wait(chan, "vm-message");
2835 res = play_and_wait(chan, "vm-messages");
2839 if (!vms->oldmessages && !vms->newmessages) {
2840 res = play_and_wait(chan, "vm-no");
2842 res = play_and_wait(chan, "vm-messages");
2849 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2852 /* Play instructions and wait for new command */
2854 if (vms->starting) {
2855 if (vms->lastmsg > -1) {
2856 res = play_and_wait(chan, "vm-onefor");
2858 res = play_and_wait(chan, vms->vmbox);
2860 res = play_and_wait(chan, "vm-messages");
2863 res = play_and_wait(chan, "vm-opts");
2866 res = play_and_wait(chan, "vm-prev");
2868 res = play_and_wait(chan, "vm-advopts");
2870 res = play_and_wait(chan, "vm-repeat");
2871 if (!res && (vms->curmsg != vms->lastmsg))
2872 res = play_and_wait(chan, "vm-next");
2874 if (!vms->deleted[vms->curmsg])
2875 res = play_and_wait(chan, "vm-delete");
2877 res = play_and_wait(chan, "vm-undelete");
2879 res = play_and_wait(chan, "vm-toforward");
2881 res = play_and_wait(chan, "vm-savemessage");
2885 res = play_and_wait(chan, "vm-helpexit");
2887 res = ast_waitfordigit(chan, 6000);
2890 if (vms->repeats > 2) {
2898 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2903 char newpassword[80] = "";
2904 char newpassword2[80] = "";
2905 char prefile[256]="";
2909 if (adsi_available(chan))
2911 bytes += adsi_logo(buf + bytes);
2912 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2913 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2914 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2915 bytes += adsi_voice_mode(buf + bytes, 0);
2916 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2918 while((cmd >= 0) && (cmd != 't')) {
2923 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2924 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration);
2927 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2928 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration);
2931 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2932 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration);
2935 newpassword[1] = '\0';
2936 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2939 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2942 newpassword2[1] = '\0';
2943 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2947 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {