2 * Asterisk -- A telephony toolkit for Linux.
4 * Voicemail System (did you ever think it could be so easy?)
6 * Copyright (C) 2003, Digium Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
29 #include <asterisk/cli.h>
38 #include <sys/types.h>
42 /* we define USESQLVM when we have MySQL or POSTGRES */
44 #include <mysql/mysql.h>
50 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
52 #include <postgresql/libpq-fe.h>
57 static inline int sql_init(void) { return 0; }
58 static inline void sql_close(void) { }
62 #include "../asterisk.h"
63 #include "../astconf.h"
65 #define COMMAND_TIMEOUT 5000
67 #define VOICEMAIL_CONFIG "voicemail.conf"
68 #define ASTERISK_USERNAME "asterisk"
70 /* Default mail command to mail voicemail. Change it with the
71 mailcmd= command in voicemail.conf */
72 #define SENDMAIL "/usr/sbin/sendmail -t"
74 #define INTRO "vm-intro"
77 #define MAX_OTHER_FORMATS 10
79 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
81 #define BASEMAXINLINE 256
82 #define BASELINELEN 72
83 #define BASEMAXINLINE 256
86 #define MAX_DATETIME_FORMAT 512
87 #define MAX_NUM_CID_CONTEXTS 10
89 #define DIGITS_DIR AST_SOUNDS "/digits/"
97 unsigned char iobuf[BASEMAXINLINE];
100 /* Structure for linked list of users */
108 char serveremail[80];
109 char mailcmd[160]; /* Configurable mail command */
118 struct ast_vm_user *next;
124 char msg_format[512];
125 struct vm_zone *next;
144 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
145 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
146 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration);
147 static int vm_delete(char *file);
151 static char *tdesc = "Comedian Mail (Voicemail System)";
153 static char *adapp = "CoMa";
155 static char *adsec = "_AST";
157 static char *addesc = "Comedian Mail";
159 static int adver = 1;
161 static char *synopsis_vm =
162 "Leave a voicemail message";
164 static char *descrip_vm =
165 " VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given\n"
166 "extension (must be configured in voicemail.conf).\n"
167 " If the extension is preceded by \n"
168 "* 's' then instructions for leaving the message will be skipped.\n"
169 "* 'u' then the \"unavailable\" message will be played.\n"
170 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
171 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
172 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
173 "priority 'o' in the current context.\n"
174 "If the requested mailbox does not exist, and there exists a priority\n"
175 "n + 101, then that priority will be taken next.\n"
176 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
177 "Otherwise, it returns 0.\n";
179 static char *synopsis_vmain =
180 "Enter voicemail system";
182 static char *descrip_vmain =
183 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
184 "for the checking of voicemail. The mailbox can be passed as the option,\n"
185 "which will stop the voicemail system from prompting the user for the mailbox.\n"
186 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
187 "a context is specified, logins are considered in that voicemail context only.\n"
188 "Returns -1 if the user hangs up or 0 otherwise.\n";
190 static char *synopsis_vm_box_exists =
191 "Check if vmbox exists";
193 static char *descrip_vm_box_exists =
194 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
195 "if the specified voice mailbox exists.\n";
198 /* Leave a message */
199 static char *capp = "VoiceMail2";
200 static char *app = "VoiceMail";
202 /* Check mail, control, etc */
203 static char *capp2 = "VoiceMailMain2";
204 static char *app2 = "VoiceMailMain";
206 static char *app3 = "MailboxExists";
208 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
209 struct ast_vm_user *users;
210 struct ast_vm_user *usersl;
211 struct vm_zone *zones = NULL;
212 struct vm_zone *zonesl = NULL;
213 static int attach_voicemail;
214 static int maxsilence;
215 static int silencethreshold = 128;
216 static char serveremail[80];
217 static char mailcmd[160]; /* Configurable mail cmd */
218 static char externnotify[160];
220 static char vmfmts[80];
221 static int vmminmessage;
222 static int vmmaxmessage;
225 static int maxlogins;
229 static int saycidinfo;
230 static char dialcontext[80];
231 static char callcontext[80];
233 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
236 static char *emailbody = NULL;
237 static int pbxskip = 0;
238 static char fromstring[100];
239 static char emailtitle[100];
246 static void populate_defaults(struct ast_vm_user *vmu)
256 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
258 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
261 static void apply_options(struct ast_vm_user *vmu, char *options)
263 /* Destructively Parse options and apply */
264 char *stringp = ast_strdupa(options);
268 while((s = strsep(&stringp, "|"))) {
270 if ((var = strsep(&value, "=")) && value) {
271 if (!strcasecmp(var, "attach")) {
276 } else if (!strcasecmp(var, "serveremail")) {
277 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
278 } else if (!strcasecmp(var, "tz")) {
279 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
280 } else if (!strcasecmp(var, "saycid")){
285 } else if (!strcasecmp(var, "review")){
290 } else if (!strcasecmp(var, "operator")){
295 } else if (!strcasecmp(var, "callback")) {
296 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
297 } else if (!strcasecmp(var, "dialout")) {
298 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
307 #include "mysql-vm-routines.h"
314 ast_mutex_t postgreslock;
316 static int sql_init(void)
318 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
319 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
321 dbhandler=PQconnectdb(dboption);
322 if (PQstatus(dbhandler) == CONNECTION_BAD) {
323 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
326 ast_mutex_init(&postgreslock);
328 /* fprintf(stderr,"postgres login OK\n"); */
332 static void sql_close(void)
338 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
346 char options[160] = "";
347 struct ast_vm_user *retval;
349 retval=malloc(sizeof(struct ast_vm_user));
351 /* fprintf(stderr,"postgres find_user:\n"); */
354 memset(retval, 0, sizeof(struct ast_vm_user));
357 strcpy(retval->mailbox, mailbox);
360 strcpy(retval->context, context);
364 strcpy(retval->context, "default");
366 populate_defaults(retval);
367 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
369 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
370 ast_mutex_lock(&postgreslock);
371 PGSQLres=PQexec(dbhandler,query);
372 if (PGSQLres!=NULL) {
373 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
374 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
375 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
377 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
380 ast_mutex_unlock(&postgreslock);
384 numFields = PQnfields(PGSQLres);
385 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
386 if (PQntuples(PGSQLres) != 1) {
387 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
389 ast_mutex_unlock(&postgreslock);
393 for (i=0; i<numFields; i++) {
394 fname = PQfname(PGSQLres,i);
395 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
396 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
397 } else if (!strcmp(fname, "fullname")) {
398 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
399 } else if (!strcmp(fname, "email")) {
400 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
401 } else if (!strcmp(fname, "pager")) {
402 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
403 } else if (!strcmp(fname, "options")) {
404 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
405 apply_options(retval, options);
410 ast_mutex_unlock(&postgreslock);
414 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
415 ast_mutex_unlock(&postgreslock);
420 } /* malloc() retval */
425 static void vm_change_password(struct ast_vm_user *vmu, char *password)
430 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);
432 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
434 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
435 ast_mutex_lock(&postgreslock);
436 PQexec(dbhandler, query);
437 strcpy(vmu->password, password);
438 ast_mutex_unlock(&postgreslock);
441 static void reset_user_pw(char *context, char *mailbox, char *password)
446 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
448 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
450 ast_mutex_lock(&postgreslock);
451 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
452 PQexec(dbhandler, query);
453 ast_mutex_unlock(&postgreslock);
456 #endif /* Postgres */
460 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
462 /* This function could be made to generate one from a database, too */
463 struct ast_vm_user *vmu=NULL, *cur;
464 ast_mutex_lock(&vmlock);
467 if ((!context || !strcasecmp(context, cur->context)) &&
468 (!strcasecmp(mailbox, cur->mailbox)))
476 /* Make a copy, so that on a reload, we have no race */
477 vmu = malloc(sizeof(struct ast_vm_user));
479 memcpy(vmu, cur, sizeof(struct ast_vm_user));
487 ast_mutex_unlock(&vmlock);
491 static int reset_user_pw(char *context, char *mailbox, char *newpass)
493 /* This function could be made to generate one from a database, too */
494 struct ast_vm_user *cur;
496 ast_mutex_lock(&vmlock);
499 if ((!context || !strcasecmp(context, cur->context)) &&
500 (!strcasecmp(mailbox, cur->mailbox)))
505 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
508 ast_mutex_unlock(&vmlock);
512 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
514 /* There's probably a better way of doing this. */
515 /* That's why I've put the password change in a separate function. */
516 /* This could also be done with a database function */
523 char currcontext[256] ="";
524 char tmpin[AST_CONFIG_MAX_PATH];
525 char tmpout[AST_CONFIG_MAX_PATH];
526 char *user, *pass, *rest, *trim, *tempcontext;
528 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
529 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
530 configin = fopen((char *)tmpin,"r");
532 configout = fopen((char *)tmpout,"w+");
535 if(!configin || !configout) {
539 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
543 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
547 while (!feof(configin)) {
548 /* Read in the line */
549 fgets(inbuf, sizeof(inbuf), configin);
551 if (!feof(configin)) {
552 /* Make a backup of it */
553 memcpy(orig, inbuf, sizeof(orig));
554 /* Strip trailing \n and comment */
555 inbuf[strlen(inbuf) - 1] = '\0';
556 user = strchr(inbuf, ';');
562 /* check for '[' (opening of context name ) */
563 tempcontext = strchr(user, '[');
565 strncpy(currcontext, tempcontext +1,
566 sizeof(currcontext) - 1);
567 /* now check for ']' */
568 tempcontext = strchr(currcontext, ']');
572 currcontext[0] = '\0';
574 pass = strchr(user, '=');
577 while(*trim && *trim < 33) {
587 while(*pass && *pass < 33)
591 rest = strchr(pass,',');
599 /* Compare user, pass AND context */
600 if (user && *user && !strcmp(user, vmu->mailbox) &&
601 pass && *pass && !strcmp(pass, vmu->password) &&
602 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
603 /* This is the line */
605 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
607 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
610 /* Put it back like it was */
611 fprintf(configout, orig);
618 unlink((char *)tmpin);
619 rename((char *)tmpout,(char *)tmpin);
620 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
621 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
625 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
627 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
630 static int make_file(char *dest, int len, char *dir, int num)
632 return snprintf(dest, len, "%s/msg%04d", dir, num);
636 inbuf(struct baseio *bio, FILE *fi)
643 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
658 inchar(struct baseio *bio, FILE *fi)
660 if(bio->iocp>=bio->iolen)
664 return bio->iobuf[bio->iocp++];
668 ochar(struct baseio *bio, int c, FILE *so)
670 if(bio->linelength>=BASELINELEN) {
671 if(fputs(eol,so)==EOF)
677 if(putc(((unsigned char)c),so)==EOF)
685 static int base_encode(char *filename, FILE *so)
687 unsigned char dtable[BASEMAXINLINE];
692 memset(&bio, 0, sizeof(bio));
693 bio.iocp = BASEMAXINLINE;
695 if ( !(fi = fopen(filename, "rb"))) {
696 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
704 dtable[26+i+9]= 'j'+i;
708 dtable[26+i+18]= 's'+i;
717 unsigned char igroup[3],ogroup[4];
720 igroup[0]= igroup[1]= igroup[2]= 0;
723 if ( (c = inchar(&bio, fi)) == EOF) {
728 igroup[n]= (unsigned char)c;
732 ogroup[0]= dtable[igroup[0]>>2];
733 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
734 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
735 ogroup[3]= dtable[igroup[2]&0x3F];
745 ochar(&bio, ogroup[i], so);
749 if(fputs(eol,so)==EOF)
757 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)
767 char tmp[80] = "/tmp/astmail-XXXXXX";
771 struct vm_zone *the_zone = NULL;
773 if (!strcmp(format, "wav49"))
775 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
776 /* Make a temporary file instead of piping directly to sendmail, in case the mail
780 p = fdopen(pfd, "w");
787 gethostname(host, sizeof(host));
788 if (strchr(srcemail, '@'))
789 strncpy(who, srcemail, sizeof(who)-1);
791 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
793 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
796 /* Does this user have a timezone specified? */
797 if (strlen(vmu->zonetag)) {
798 /* Find the zone in the list */
802 if (!strcmp(z->name, vmu->zonetag)) {
811 ast_localtime(&t,&tm,the_zone->timezone);
813 ast_localtime(&t,&tm,NULL);
814 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
815 fprintf(p, "Date: %s\n", date);
818 fprintf(p, "From: %s <%s>\n", fromstring, who);
820 fprintf(p, "From: Asterisk PBX <%s>\n", who);
821 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
825 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
830 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
832 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
833 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
834 fprintf(p, "MIME-Version: 1.0\n");
835 if (attach_user_voicemail) {
836 /* Something unique. */
837 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
839 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
841 fprintf(p, "--%s\n", bound);
843 fprintf(p, "Content-Type: text/plain; charset=ISO-8859-1\nContent-Transfer-Encoding: 8bit\n\n");
844 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
846 struct ast_channel *ast = ast_channel_alloc(0);
849 int vmlen = strlen(emailbody)*3 + 200;
850 if ((passdata = alloca(vmlen))) {
851 memset(passdata, 0, vmlen);
852 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
853 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
854 sprintf(passdata,"%d",msgnum);
855 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
856 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
857 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
858 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
859 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
860 fprintf(p, "%s\n",passdata);
861 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
862 ast_channel_free(ast);
863 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
865 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
867 "in mailbox %s from %s, on %s so you might\n"
868 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
869 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
871 if (attach_user_voicemail) {
872 fprintf(p, "--%s\n", bound);
873 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
874 fprintf(p, "Content-Transfer-Encoding: base64\n");
875 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
876 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
878 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
879 base_encode(fname, p);
880 fprintf(p, "\n\n--%s--\n.\n", bound);
883 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
884 ast_safe_system(tmp2);
885 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
887 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
893 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, int duration, struct ast_vm_user *vmu)
901 char tmp[80] = "/tmp/astmail-XXXXXX";
905 struct vm_zone *the_zone = NULL;
909 p = fdopen(pfd, "w");
917 gethostname(host, sizeof(host));
918 if (strchr(srcemail, '@'))
919 strncpy(who, srcemail, sizeof(who)-1);
921 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
923 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
926 /* Does this user have a timezone specified? */
927 if (strlen(vmu->zonetag)) {
928 /* Find the zone in the list */
932 if (!strcmp(z->name, vmu->zonetag)) {
941 ast_localtime(&t,&tm,the_zone->timezone);
943 ast_localtime(&t,&tm,NULL);
945 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
946 fprintf(p, "Date: %s\n", date);
947 fprintf(p, "From: Asterisk PBX <%s>\n", who);
948 fprintf(p, "To: %s\n", pager);
949 fprintf(p, "Subject: New VM\n\n");
950 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
951 fprintf(p, "New %s long msg in box %s\n"
952 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
954 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
955 ast_safe_system(tmp2);
956 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
958 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
964 static int get_date(char *s, int len)
970 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
973 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
977 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
978 if (ast_fileexists(fn, NULL, NULL) > 0) {
979 res = ast_streamfile(chan, fn, chan->language);
982 res = ast_waitstream(chan, ecodes);
986 res = ast_streamfile(chan, "vm-theperson", chan->language);
989 res = ast_waitstream(chan, ecodes);
992 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
997 res = ast_streamfile(chan, "vm-isonphone", chan->language);
999 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1002 res = ast_waitstream(chan, ecodes);
1006 static int play_and_wait(struct ast_channel *chan, char *fn)
1009 d = ast_streamfile(chan, fn, chan->language);
1012 d = ast_waitstream(chan, AST_DIGIT_ANY);
1013 ast_stopstream(chan);
1017 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep)
1021 int x, fmtcnt=1, res=-1,outmsg=0;
1022 struct ast_frame *f;
1023 struct ast_filestream *others[MAX_OTHER_FORMATS];
1024 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
1025 char *sfmt[MAX_OTHER_FORMATS];
1028 struct ast_dsp *sildet; /* silence detector dsp */
1029 int totalsilence = 0;
1031 int gotsilence = 0; /* did we timeout for silence? */
1033 char prependfile[80];
1035 /* barf if no pointer passed to store duration in */
1036 if (duration == NULL)
1037 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
1039 ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1040 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1042 if (playfile || beep) {
1044 d = play_and_wait(chan, playfile);
1046 d = ast_streamfile(chan, "beep",chan->language);
1048 d = ast_waitstream(chan,"");
1052 strncpy(prependfile, recordfile, sizeof(prependfile) -1);
1053 strcat(prependfile, "-prepend");
1055 fmts = ast_strdupa(fmt);
1058 strsep(&stringp, "|");
1059 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1060 sfmt[0] = ast_strdupa(fmts);
1062 while((fmt = strsep(&stringp, "|"))) {
1063 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1064 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1067 sfmt[fmtcnt++] = ast_strdupa(fmt);
1071 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1072 for (x=0;x<fmtcnt;x++) {
1073 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1074 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1080 sildet = ast_dsp_new(); /* Create the silence detector */
1082 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1085 ast_dsp_set_threshold(sildet, silencethreshold);
1087 if (maxsilence > 0) {
1088 rfmt = chan->readformat;
1089 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1091 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1097 /* Loop forever, writing the packets we read to the writer(s), until
1098 we read a # or get a hangup */
1101 res = ast_waitfor(chan, 2000);
1103 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1104 /* Try one more time in case of masq */
1105 res = ast_waitfor(chan, 2000);
1107 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1119 if (f->frametype == AST_FRAME_VOICE) {
1120 /* write each format */
1121 for (x=0;x<fmtcnt;x++) {
1124 res = ast_writestream(others[x], f);
1127 /* Silence Detection */
1128 if (maxsilence > 0) {
1130 ast_dsp_silence(sildet, f, &dspsilence);
1132 totalsilence = dspsilence;
1136 if (totalsilence > maxsilence) {
1137 /* Ended happily with silence */
1144 /* Exit on any error */
1146 ast_log(LOG_WARNING, "Error writing frame\n");
1150 } else if (f->frametype == AST_FRAME_VIDEO) {
1151 /* Write only once */
1152 ast_writestream(others[0], f);
1153 } else if (f->frametype == AST_FRAME_DTMF) {
1154 /* stop recording with any digit */
1155 if (option_verbose > 2)
1156 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1164 if (maxtime < (end - start)) {
1165 if (option_verbose > 2)
1166 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1174 if (end == start) time(&end);
1176 if (option_verbose > 2)
1177 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1181 /* delete all the prepend files */
1182 for (x=0;x<fmtcnt;x++) {
1185 ast_closestream(others[x]);
1186 ast_filedelete(prependfile, sfmt[x]);
1191 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
1193 *duration = end - start;
1199 struct ast_frame *fr;
1200 for (x=0;x<fmtcnt;x++) {
1201 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1202 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1203 if (!others[x] || !realfiles[x])
1206 ast_stream_rewind(others[x], totalsilence-200);
1208 ast_stream_rewind(others[x], 200);
1209 ast_truncstream(others[x]);
1210 /* add the original file too */
1211 while ((fr = ast_readframe(realfiles[x]))) {
1212 ast_writestream(others[x],fr);
1214 ast_closestream(others[x]);
1215 ast_closestream(realfiles[x]);
1216 ast_filerename(prependfile, recordfile, sfmt[x]);
1218 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1220 ast_filedelete(prependfile, sfmt[x]);
1224 if (ast_set_read_format(chan, rfmt)) {
1225 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1230 /* Let them know it worked */
1231 ast_streamfile(chan, "auth-thankyou", chan->language);
1232 ast_waitstream(chan, "");
1238 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1242 int x, fmtcnt=1, res=-1,outmsg=0;
1243 struct ast_frame *f;
1244 struct ast_filestream *others[MAX_OTHER_FORMATS];
1245 char *sfmt[MAX_OTHER_FORMATS];
1248 struct ast_dsp *sildet; /* silence detector dsp */
1249 int totalsilence = 0;
1251 int gotsilence = 0; /* did we timeout for silence? */
1254 /* barf if no pointer passed to store duration in */
1255 if (duration == NULL)
1256 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1258 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1259 snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1262 d = play_and_wait(chan, playfile);
1264 d = ast_streamfile(chan, "beep",chan->language);
1266 d = ast_waitstream(chan,"");
1271 fmts = ast_strdupa(fmt);
1274 strsep(&stringp, "|");
1275 ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1276 sfmt[0] = ast_strdupa(fmts);
1278 while((fmt = strsep(&stringp, "|"))) {
1279 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1280 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1283 sfmt[fmtcnt++] = ast_strdupa(fmt);
1287 end=start; /* pre-initialize end to be same as start in case we never get into loop */
1288 for (x=0;x<fmtcnt;x++) {
1289 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1290 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1297 sildet = ast_dsp_new(); /* Create the silence detector */
1299 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1302 ast_dsp_set_threshold(sildet, silencethreshold);
1304 if (maxsilence > 0) {
1305 rfmt = chan->readformat;
1306 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1308 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1314 /* Loop forever, writing the packets we read to the writer(s), until
1315 we read a # or get a hangup */
1318 res = ast_waitfor(chan, 2000);
1320 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1321 /* Try one more time in case of masq */
1322 res = ast_waitfor(chan, 2000);
1324 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1336 if (f->frametype == AST_FRAME_VOICE) {
1337 /* write each format */
1338 for (x=0;x<fmtcnt;x++) {
1339 res = ast_writestream(others[x], f);
1342 /* Silence Detection */
1343 if (maxsilence > 0) {
1345 ast_dsp_silence(sildet, f, &dspsilence);
1347 totalsilence = dspsilence;
1351 if (totalsilence > maxsilence) {
1352 /* Ended happily with silence */
1359 /* Exit on any error */
1361 ast_log(LOG_WARNING, "Error writing frame\n");
1365 } else if (f->frametype == AST_FRAME_VIDEO) {
1366 /* Write only once */
1367 ast_writestream(others[0], f);
1368 } else if (f->frametype == AST_FRAME_DTMF) {
1369 if (f->subclass == '#') {
1370 if (option_verbose > 2)
1371 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1378 if (f->subclass == '0') {
1379 /* Check for a '0' during message recording also, in case caller wants operator */
1380 if (option_verbose > 2)
1381 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1389 if (maxtime < (end - start)) {
1390 if (option_verbose > 2)
1391 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1399 if (end == start) time(&end);
1401 if (option_verbose > 2)
1402 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1407 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1410 *duration = end - start;
1412 for (x=0;x<fmtcnt;x++) {
1416 ast_stream_rewind(others[x], totalsilence-200);
1418 ast_stream_rewind(others[x], 200);
1419 ast_truncstream(others[x]);
1420 ast_closestream(others[x]);
1423 if (ast_set_read_format(chan, rfmt)) {
1424 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1429 /* Let them know recording is stopped */
1430 ast_streamfile(chan, "auth-thankyou", chan->language);
1431 ast_waitstream(chan, "");
1438 static void free_user(struct ast_vm_user *vmu)
1444 static void free_zone(struct vm_zone *z)
1449 static void run_externnotify(char *context, char *extension, int numvoicemails)
1451 char arguments[255];
1453 if(externnotify[0]) {
1454 strncpy(arguments, externnotify, sizeof(arguments));
1455 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1456 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1457 ast_safe_system(arguments);
1462 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1474 char prefile[256]="";
1480 struct ast_vm_user *vmu;
1481 struct ast_vm_user svm;
1484 strncpy(tmp, ext, sizeof(tmp) - 1);
1486 context = strchr(tmp, '@');
1492 if ((vmu = find_user(&svm, context, ext))) {
1493 /* Setup pre-file if appropriate */
1495 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1497 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1498 make_dir(dir, sizeof(dir), vmu->context, "", "");
1499 /* It's easier just to try to make it than to check for its existence */
1500 if (mkdir(dir, 0700) && (errno != EEXIST))
1501 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1502 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1503 /* It's easier just to try to make it than to check for its existence */
1504 if (mkdir(dir, 0700) && (errno != EEXIST))
1505 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1506 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1507 if (mkdir(dir, 0700) && (errno != EEXIST))
1508 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1509 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1511 /* Play the beginning intro if desired */
1512 if (strlen(prefile)) {
1513 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1514 if (ast_streamfile(chan, prefile, chan->language) > -1)
1515 res = ast_waitstream(chan, "#0*");
1517 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1518 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1521 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1527 /* On a '#' we skip the instructions */
1531 if (!res && !silent) {
1532 res = ast_streamfile(chan, INTRO, chan->language);
1534 res = ast_waitstream(chan, ecodes);
1540 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1541 other than the operator -- an automated attendant or mailbox login for example */
1543 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1544 if (strlen(chan->macrocontext))
1545 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1550 /* Check for a '0' here */
1553 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1554 if (strlen(chan->macrocontext))
1555 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1561 /* Unless we're *really* silent, try to send the beep */
1562 res = ast_streamfile(chan, "beep", chan->language);
1564 res = ast_waitstream(chan, "");
1570 /* The meat of recording the message... All the announcements and beeps have been played*/
1571 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1575 make_file(fn, sizeof(fn), dir, msgnum);
1576 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1577 (chan->callerid ? chan->callerid : "Unknown"),
1578 vmu->fullname, ext, chan->name);
1579 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1582 } while(msgnum < MAXMSG);
1583 if (msgnum < MAXMSG) {
1584 /* Store information */
1585 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1586 txt = fopen(txtfile, "w+");
1588 get_date(date, sizeof(date));
1591 "; Message Information file\n"
1609 chan->callerid ? chan->callerid : "Unknown",
1610 date, (long)time(NULL));
1613 ast_log(LOG_WARNING, "Error opening text file for output\n");
1614 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1619 fd = open(txtfile, O_APPEND | O_WRONLY);
1621 txt = fdopen(fd, "a");
1623 fprintf(txt, "duration=%d\n", duration);
1628 if (duration < vmminmessage) {
1633 strsep(&stringp, "|");
1634 /* Send e-mail if applicable */
1635 if (strlen(vmu->email)) {
1636 int attach_user_voicemail = attach_voicemail;
1637 char *myserveremail = serveremail;
1638 if (vmu->attach > -1)
1639 attach_user_voicemail = vmu->attach;
1640 if (strlen(vmu->serveremail))
1641 myserveremail = vmu->serveremail;
1642 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, duration, attach_user_voicemail);
1644 if (strlen(vmu->pager)) {
1645 char *myserveremail = serveremail;
1646 if (strlen(vmu->serveremail))
1647 myserveremail = vmu->serveremail;
1648 sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, duration, vmu);
1651 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1653 res = ast_waitstream(chan, "");
1654 ast_log(LOG_WARNING, "No more messages possible\n");
1657 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1661 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1662 /*Send the call to n+101 priority, where n is the current priority*/
1663 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1664 chan->priority+=100;
1666 /* Leave voicemail for someone */
1667 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1669 /* If an external program is specified to be run after leaving a voicemail */
1670 run_externnotify(chan->context, ext, ast_app_has_voicemail(ext));
1675 static char *mbox(int id)
1703 static int count_messages(char *dir)
1707 for (x=0;x<MAXMSG;x++) {
1708 make_file(fn, sizeof(fn), dir, x);
1709 if (ast_fileexists(fn, NULL, NULL) < 1)
1715 static int say_and_wait(struct ast_channel *chan, int num)
1718 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL);
1722 static int copy(char *infile, char *outfile)
1729 if ((ifd = open(infile, O_RDONLY)) < 0) {
1730 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1733 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1734 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1739 len = read(ifd, buf, sizeof(buf));
1741 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1747 res = write(ofd, buf, len);
1749 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1761 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1768 char *dbox = mbox(box);
1770 make_file(sfn, sizeof(sfn), dir, msg);
1771 make_dir(ddir, sizeof(ddir), context, username, dbox);
1773 for (x=0;x<MAXMSG;x++) {
1774 make_file(dfn, sizeof(dfn), ddir, x);
1775 if (ast_fileexists(dfn, NULL, NULL) < 0)
1780 ast_filecopy(sfn, dfn, NULL);
1781 if (strcmp(sfn, dfn)) {
1782 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1783 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1789 static int adsi_logo(unsigned char *buf)
1792 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1793 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1797 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1805 bytes += adsi_data_mode(buf + bytes);
1806 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1809 bytes += adsi_logo(buf);
1810 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1812 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1814 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1815 bytes += adsi_data_mode(buf + bytes);
1816 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1818 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1820 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1821 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1822 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1823 bytes += adsi_voice_mode(buf + bytes, 0);
1824 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1831 bytes += adsi_logo(buf);
1832 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1833 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1834 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1835 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1838 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1839 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1840 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1841 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1842 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1843 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1844 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1847 /* Add another dot */
1849 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1850 bytes += adsi_voice_mode(buf + bytes, 0);
1852 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1853 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1857 /* These buttons we load but don't use yet */
1858 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1859 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1860 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1861 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1862 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1863 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1864 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1867 /* Add another dot */
1869 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1870 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1871 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1876 snprintf(num, sizeof(num), "%d", x);
1877 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1879 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1880 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1883 /* Add another dot */
1885 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1886 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1887 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1890 if (adsi_end_download(chan)) {
1892 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1893 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1894 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1895 bytes += adsi_voice_mode(buf + bytes, 0);
1896 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1900 bytes += adsi_download_disconnect(buf + bytes);
1901 bytes += adsi_voice_mode(buf + bytes, 0);
1902 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1904 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1909 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1910 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1912 ast_log(LOG_DEBUG, "Restarting session...\n");
1915 /* Load the session now */
1916 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1918 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1920 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1922 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1926 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1929 if (!adsi_available(chan))
1931 x = adsi_load_session(chan, adapp, adver, 1);
1935 if (adsi_load_vmail(chan, useadsi)) {
1936 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1943 static void adsi_login(struct ast_channel *chan)
1947 unsigned char keys[8];
1949 if (!adsi_available(chan))
1954 /* Set one key for next */
1955 keys[3] = ADSI_KEY_APPS + 3;
1957 bytes += adsi_logo(buf + bytes);
1958 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1959 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1960 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1961 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1962 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1963 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1964 bytes += adsi_set_keys(buf + bytes, keys);
1965 bytes += adsi_voice_mode(buf + bytes, 0);
1966 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1969 static void adsi_password(struct ast_channel *chan)
1973 unsigned char keys[8];
1975 if (!adsi_available(chan))
1980 /* Set one key for next */
1981 keys[3] = ADSI_KEY_APPS + 3;
1983 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1984 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1985 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1986 bytes += adsi_set_keys(buf + bytes, keys);
1987 bytes += adsi_voice_mode(buf + bytes, 0);
1988 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1991 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1995 unsigned char keys[8];
1998 if (!adsi_available(chan))
2002 y = ADSI_KEY_APPS + 12 + start + x;
2003 if (y > ADSI_KEY_APPS + 12 + 4)
2005 keys[x] = ADSI_KEY_SKT | y;
2007 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2011 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2012 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2013 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2014 bytes += adsi_set_keys(buf + bytes, keys);
2015 bytes += adsi_voice_mode(buf + bytes, 0);
2017 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2020 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2023 char buf[256], buf1[256], buf2[256];
2029 char datetime[21]="";
2032 unsigned char keys[8];
2036 if (!adsi_available(chan))
2039 /* Retrieve important info */
2040 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2041 f = fopen(fn2, "r");
2044 fgets(buf, sizeof(buf), f);
2048 strsep(&stringp, "=");
2049 val = strsep(&stringp, "=");
2050 if (val && strlen(val)) {
2051 if (!strcmp(buf, "callerid"))
2052 strncpy(cid, val, sizeof(cid) - 1);
2053 if (!strcmp(buf, "origdate"))
2054 strncpy(datetime, val, sizeof(datetime) - 1);
2060 /* New meaning for keys */
2062 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2067 /* No prev key, provide "Folder" instead */
2068 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2071 /* If last message ... */
2073 /* but not only message, provide "Folder" instead */
2074 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2075 bytes += adsi_voice_mode(buf + bytes, 0);
2078 /* Otherwise if only message, leave blank */
2084 ast_callerid_parse(cid, &name, &num);
2088 name = "Unknown Caller";
2090 /* If deleted, show "undeleted" */
2093 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2096 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2097 snprintf(buf1, sizeof(buf1), "%s%s", folder,
2098 strcasecmp(folder, "INBOX") ? " Messages" : "");
2099 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2101 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2102 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2103 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2104 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2105 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2106 bytes += adsi_set_keys(buf + bytes, keys);
2107 bytes += adsi_voice_mode(buf + bytes, 0);
2109 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2112 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2116 unsigned char keys[8];
2120 if (!adsi_available(chan))
2123 /* New meaning for keys */
2125 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2131 /* No prev key, provide "Folder" instead */
2132 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2135 /* If last message ... */
2137 /* but not only message, provide "Folder" instead */
2138 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2140 /* Otherwise if only message, leave blank */
2145 /* If deleted, show "undeleted" */
2147 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2150 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2151 bytes += adsi_set_keys(buf + bytes, keys);
2152 bytes += adsi_voice_mode(buf + bytes, 0);
2154 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2157 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2159 char buf[256], buf1[256], buf2[256];
2161 unsigned char keys[8];
2164 char *newm = (new == 1) ? "message" : "messages";
2165 char *oldm = (old == 1) ? "message" : "messages";
2166 if (!adsi_available(chan))
2169 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2171 strcat(buf1, " and");
2172 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2174 snprintf(buf2, sizeof(buf2), "%s.", newm);
2177 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2178 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2180 strcpy(buf1, "You have no messages.");
2183 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2184 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2185 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2188 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2192 /* Don't let them listen if there are none */
2195 bytes += adsi_set_keys(buf + bytes, keys);
2197 bytes += adsi_voice_mode(buf + bytes, 0);
2199 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2202 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2204 char buf[256], buf1[256], buf2[256];
2206 unsigned char keys[8];
2209 char *mess = (messages == 1) ? "message" : "messages";
2211 if (!adsi_available(chan))
2214 /* Original command keys */
2216 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2224 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2225 strcasecmp(folder, "INBOX") ? " folder" : "");
2228 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2230 strcpy(buf2, "no messages.");
2231 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2232 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2233 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2234 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2235 bytes += adsi_set_keys(buf + bytes, keys);
2237 bytes += adsi_voice_mode(buf + bytes, 0);
2239 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2244 static void adsi_clear(struct ast_channel *chan)
2248 if (!adsi_available(chan))
2250 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2251 bytes += adsi_voice_mode(buf + bytes, 0);
2253 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2257 static void adsi_goodbye(struct ast_channel *chan)
2262 if (!adsi_available(chan))
2264 bytes += adsi_logo(buf + bytes);
2265 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2266 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2267 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2268 bytes += adsi_voice_mode(buf + bytes, 0);
2270 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2273 static int get_folder(struct ast_channel *chan, int start)
2278 d = play_and_wait(chan, "vm-press");
2281 for (x = start; x< 5; x++) {
2282 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2284 d = play_and_wait(chan, "vm-for");
2287 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2288 d = play_and_wait(chan, fn);
2291 d = play_and_wait(chan, "vm-messages");
2294 d = ast_waitfordigit(chan, 500);
2298 d = play_and_wait(chan, "vm-tocancel");
2301 d = ast_waitfordigit(chan, 4000);
2305 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2308 res = play_and_wait(chan, fn);
2309 while (((res < '0') || (res > '9')) &&
2310 (res != '#') && (res >= 0)) {
2311 res = get_folder(chan, 0);
2316 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2322 while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2327 /* prepend a message to the current message and return */
2330 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2331 cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2341 cmd = play_and_wait(chan,"vm-forwardoptions");
2343 cmd = play_and_wait(chan,"vm-starmain");
2345 cmd = ast_waitfordigit(chan,6000);
2357 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2364 struct ast_config *mif;
2368 int res = 0, cmd = 0;
2369 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2372 int saved_messages = 0, found = 0;
2373 int valid_extensions = 0;
2374 while (!res && !valid_extensions) {
2375 res = ast_streamfile(chan, "vm-extension", chan->language);
2378 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2380 /* start all over if no username */
2381 if (!strlen(username))
2384 s = strsep(&stringp, "*");
2385 /* start optimistic */
2386 valid_extensions = 1;
2388 /* find_user is going to malloc since we have a NULL as first argument */
2389 if ((receiver = find_user(NULL, context, s))) {
2391 vmtmp = extensions = receiver;
2393 vmtmp->next = receiver;
2398 valid_extensions = 0;
2401 s = strsep(&stringp, "*");
2403 /* break from the loop of reading the extensions */
2404 if (valid_extensions)
2406 /* invalid extension, try again */
2407 res = play_and_wait(chan, "pbx-invalid");
2409 /* check if we're clear to proceed */
2410 if (!extensions || !valid_extensions)
2413 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2415 while(!res && vmtmp) {
2416 /* if (play_and_wait(chan, "vm-savedto"))
2419 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2420 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2421 ast_log(LOG_DEBUG, sys);
2422 ast_safe_system(sys);
2424 todircount = count_messages(todir);
2425 strncpy(tmp, fmt, sizeof(tmp) - 1);
2427 while((s = strsep(&stringp, "|"))) {
2428 /* XXX This is a hack -- we should use build_filename or similar XXX */
2429 if (!strcasecmp(s, "wav49"))
2431 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2432 ast_log(LOG_DEBUG, sys);
2433 ast_safe_system(sys);
2435 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2436 ast_log(LOG_DEBUG, sys);
2437 ast_safe_system(sys);
2438 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2440 /* load the information on the source message so we can send an e-mail like a new message */
2441 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2442 if ((mif=ast_load(miffile))) {
2444 /* set callerid and duration variables */
2445 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2446 s = ast_variable_retrieve(mif, NULL, "duration");
2451 if (strlen(vmtmp->email)) {
2452 int attach_user_voicemail = attach_voicemail;
2453 char *myserveremail = serveremail;
2454 if (vmtmp->attach > -1)
2455 attach_user_voicemail = vmtmp->attach;
2456 if (strlen(vmtmp->serveremail))
2457 myserveremail = vmtmp->serveremail;
2458 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2461 if (strlen(vmtmp->pager)) {
2462 char *myserveremail = serveremail;
2463 if (strlen(vmtmp->serveremail))
2464 myserveremail = vmtmp->serveremail;
2465 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2468 ast_destroy(mif); /* or here */
2470 /* Leave voicemail for someone */
2471 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2472 run_externnotify(chan->context, vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2476 vmtmp = vmtmp->next;
2479 if (saved_messages > 0) {
2480 /* give confirmatopm that the message was saved */
2481 /* commented out since we can't forward batches yet
2482 if (saved_messages == 1)
2483 res = play_and_wait(chan, "vm-message");
2485 res = play_and_wait(chan, "vm-messages");
2487 res = play_and_wait(chan, "vm-saved"); */
2489 res = play_and_wait(chan, "vm-msgsaved");
2492 return res ? res : cmd;
2496 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2499 if ((res = ast_streamfile(chan, file, chan->language)))
2500 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2502 res = ast_waitstream(chan, AST_DIGIT_ANY);
2506 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2509 if ((res = ast_streamfile(chan, file, chan->language)))
2510 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2512 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2516 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2519 struct vm_zone *the_zone = NULL;
2523 if (sscanf(origtime,"%ld",&tin) < 1) {
2524 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2529 /* Does this user have a timezone specified? */
2530 if (strlen(vmu->zonetag)) {
2531 /* Find the zone in the list */
2535 if (!strcmp(z->name, vmu->zonetag)) {
2543 /* No internal variable parsing for now, so we'll comment it out for the time being */
2545 /* Set the DIFF_* variables */
2546 localtime_r(&t, &time_now);
2547 gettimeofday(&tv_now,NULL);
2548 tnow = tv_now.tv_sec;
2549 localtime_r(&tnow,&time_then);
2551 /* Day difference */
2552 if (time_now.tm_year == time_then.tm_year)
2553 sprintf(temp,"%d",time_now.tm_yday);
2555 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2556 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2558 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2561 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2563 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2565 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2572 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2576 char *callerid, *name;
2577 char prefile[256]="";
2580 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2581 /* 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 */
2582 if((cid == NULL)||(context == NULL))
2585 /* Strip off caller ID number from name */
2586 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2587 ast_callerid_parse(cid, &name, &callerid);
2588 if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2589 /* Check for internal contexts and only */
2590 /* say extension when the call didn't come from an internal context in the list */
2591 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2592 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2593 if((strcmp(cidinternalcontexts[i], context) == 0))
2596 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2598 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2599 if (strlen(prefile)) {
2600 /* See if we can find a recorded name for this person instead of their extension number */
2601 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2602 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2604 res = wait_file2(chan, vms, "vm-from");
2605 res = ast_streamfile(chan, prefile, chan->language) > -1;
2606 res = ast_waitstream(chan, "");
2608 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2609 /* BB: Say "from extension" as one saying to sound smoother */
2611 res = wait_file2(chan, vms, "vm-from-extension");
2612 res = ast_say_digit_str(chan, callerid, "", chan->language);
2619 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2620 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2622 res = wait_file2(chan, vms, "vm-from-phonenumber");
2623 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2627 /* Number unknown */
2628 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2630 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2631 res = wait_file2(chan, vms, "vm-unknown-caller");
2636 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2639 char filename[256],*origtime, *cid, *context;
2640 struct ast_config *msg_cfg;
2643 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2644 adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2646 res = wait_file2(chan, vms, "vm-first");
2647 else if (msg == vms->lastmsg)
2648 res = wait_file2(chan, vms, "vm-last");
2650 res = wait_file2(chan, vms, "vm-message");
2651 if (msg && (msg != vms->lastmsg)) {
2653 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2657 /* Retrieve info from VM attribute file */
2659 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2660 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2661 msg_cfg = ast_load(filename);
2663 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2667 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2670 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2672 context = ast_variable_retrieve(msg_cfg, "message", "context");
2673 if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2674 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2677 res = play_message_datetime(chan, vmu, origtime, filename);
2679 if ((!res)&&(vmu->saycid))
2680 res = play_message_callerid(chan, vms, cid, context, 0);
2682 ast_destroy(msg_cfg);
2685 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2686 vms->heard[msg] = 1;
2687 res = wait_file(chan, vms, vms->fn);
2692 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2694 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2695 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2696 vms->lastmsg = count_messages(vms->curdir) - 1;
2697 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2700 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2703 char ntxt[256] = "";
2705 if (vms->lastmsg > -1) {
2706 /* Get the deleted messages fixed */
2708 for (x=0;x < MAXMSG;x++) {
2709 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2710 /* Save this message. It's not in INBOX or hasn't been heard */
2711 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2712 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2715 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2716 if (strcmp(vms->fn, vms->fn2)) {
2717 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2718 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2719 ast_filerename(vms->fn, vms->fn2, NULL);
2722 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2723 /* Move to old folder before deleting */
2724 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2727 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2728 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2729 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2734 memset(vms->deleted, 0, sizeof(vms->deleted));
2735 memset(vms->heard, 0, sizeof(vms->heard));
2738 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2740 /* Introduce messages they have */
2742 res = play_and_wait(chan, "vm-youhave");
2744 if (vms->newmessages) {
2745 res = say_and_wait(chan, vms->newmessages);
2747 res = play_and_wait(chan, "vm-INBOX");
2748 if (vms->oldmessages && !res)
2749 res = play_and_wait(chan, "vm-and");
2751 if ((vms->newmessages == 1))
2752 res = play_and_wait(chan, "vm-message");
2754 res = play_and_wait(chan, "vm-messages");
2758 if (!res && vms->oldmessages) {
2759 res = say_and_wait(chan, vms->oldmessages);
2761 res = play_and_wait(chan, "vm-Old");
2763 if (vms->oldmessages == 1)
2764 res = play_and_wait(chan, "vm-message");
2766 res = play_and_wait(chan, "vm-messages");
2770 if (!vms->oldmessages && !vms->newmessages) {
2771 res = play_and_wait(chan, "vm-no");
2773 res = play_and_wait(chan, "vm-messages");
2780 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2783 /* Play instructions and wait for new command */
2785 if (vms->starting) {
2786 if (vms->lastmsg > -1) {
2787 res = play_and_wait(chan, "vm-onefor");
2789 res = play_and_wait(chan, vms->vmbox);
2791 res = play_and_wait(chan, "vm-messages");
2794 res = play_and_wait(chan, "vm-opts");
2797 res = play_and_wait(chan, "vm-prev");
2799 res = play_and_wait(chan, "vm-advopts");
2801 res = play_and_wait(chan, "vm-repeat");
2802 if (!res && (vms->curmsg != vms->lastmsg))
2803 res = play_and_wait(chan, "vm-next");
2805 if (!vms->deleted[vms->curmsg])
2806 res = play_and_wait(chan, "vm-delete");
2808 res = play_and_wait(chan, "vm-undelete");
2810 res = play_and_wait(chan, "vm-toforward");
2812 res = play_and_wait(chan, "vm-savemessage");
2816 res = play_and_wait(chan, "vm-helpexit");
2818 res = ast_waitfordigit(chan, 6000);
2821 if (vms->repeats > 2) {
2829 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2834 char newpassword[80] = "";
2835 char newpassword2[80] = "";
2836 char prefile[256]="";
2840 if (adsi_available(chan))
2842 bytes += adsi_logo(buf + bytes);
2843 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2844 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2845 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2846 bytes += adsi_voice_mode(buf + bytes, 0);
2847 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2849 while((cmd >= 0) && (cmd != 't')) {
2854 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2855 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration);
2858 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2859 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration);
2862 snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2863 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration);
2866 newpassword[1] = '\0';
2867 newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2870 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2873 newpassword2[1] = '\0';
2874 newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2878 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2881 if (strcmp(newpassword, newpassword2)) {
2882 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2883 cmd = play_and_wait(chan, "vm-mismatch");
2886 vm_change_password(vmu,newpassword);
2887 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2888 cmd = play_and_wait(chan,"vm-passchanged");
2894 cmd = play_and_wait(chan,"vm-options");
2896 cmd = ast_waitfordigit(chan,6000);
2908 static int vm_execmain(struct ast_channel *chan, void *data)
2910 /* XXX This is, admittedly, some pretty horrendus code. For some
2911 reason it just seemed a lot easier to do with GOTO's. I feel
2912 like I'm back in my GWBASIC days. XXX */
2917 struct localuser *u;
2918 char prefixstr[80] ="";
2919 char empty[80] = "";
2923 char tmp[256], *ext;
2924 char fmtc[256] = "";
2926 struct vm_state vms;
2928 struct ast_vm_user *vmu = NULL, vmus;
2933 memset(&vms, 0, sizeof(vms));
2934 strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2935 if (chan->_state != AST_STATE_UP)
2938 if (data && strlen(data)) {
2939 strncpy(tmp, data, sizeof(tmp) - 1);
2944 /* We should skip the user's password */
2949 /* We should prefix the mailbox with the supplied data */
2955 context = strchr(ext, '@');
2962 strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2964 strncpy(vms.username, ext, sizeof(vms.username) - 1);
2965 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2972 /* If ADSI is supported, setup login screen */
2973 adsi_begin(chan, &useadsi);
2974 if (!skipuser && useadsi)
2976 if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2977 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2981 /* Authenticate them and get their mailbox/password */
2983 while (!valid && (logretries < maxlogins)) {
2984 /* Prompt for, and read in the username */
2985 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2986 ast_log(LOG_WARNING, "Couldn't read username\n");
2989 if (!strlen(vms.username)) {
2990 if (option_verbose > 2)
2991 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2996 adsi_password(chan);
2997 if (ast_streamfile(chan, "vm-password", chan->language)) {
2998 ast_log(LOG_WARNING, "Unable to stream password file\n");
3001 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
3002 ast_log(LOG_WARNING, "Unable to read password\n");
3006 char fullusername[80] = "";
3007 strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
3008 strncat(fullusername, vms.username, sizeof(fullusername) - 1);
3009 strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
3012 vmu = find_user(&vmus, context, vms.username);
3013 if (vmu && !strcmp(vmu->password, password))
3016 if (option_verbose > 2)
3017 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
3019 strncpy(vms.username, empty, sizeof(vms.username) -1);
3024 if (ast_streamfile(chan, "vm-incorrect", chan->language))
3029 if (!valid && (logretries >= maxlogins)) {
3030 ast_stopstream(chan);
3031 res = play_and_wait(chan, "vm-goodbye");
3037 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
3038 mkdir(vms.curdir, 0700);
3039 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
3040 mkdir(vms.curdir, 0700);
3041 /* Retrieve old and new message counts */
3042 open_mailbox(&vms, vmu, 1);
3043 vms.oldmessages = vms.lastmsg + 1;
3044 /* Start in INBOX */
3045 open_mailbox(&vms, vmu, 0);
3046 vms.newmessages = vms.lastmsg + 1;
3049 /* Select proper mailbox FIRST!! */
3050 if (!vms.newmessages && vms.oldmessages) {
3051 /* If we only have old messages start here */
3052 open_mailbox(&vms, vmu, 1);
3056 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
3058 cmd = vm_intro(chan, &vms);
3061 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
3068 if (vms.lastmsg > -1) {
3069 cmd = play_message(chan, vmu, &vms, vms.curmsg);
3071 cmd = play_and_wait(chan, "vm-youhave");
3073 cmd = play_and_wait(chan, "vm-no");
3075 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
3076 cmd = play_and_wait(chan, vms.fn);
3079 cmd = play_and_wait(chan, "vm-messages");
3082 case '2': /* Change folders */
3084 adsi_folders(chan, 0, "Change to folder...");
3085 cmd = get_folder2(chan, "vm-changeto", 0);
3088 } else if (cmd > 0) {
3090 close_mailbox(&vms, vmu);
3091 open_mailbox(&vms, vmu, cmd);
3095 adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
3097 cmd = play_and_wait(chan, vms.vmbox);
3099 cmd = play_and_wait(chan, "vm-messages");
3102 case '3': /* Advanced options */
3105 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
3107 case '1': /* Reply */
3108 if(vms.lastmsg > -1)
3109 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1);
3111 cmd = play_and_wait(chan, "vm-sorry");
3114 case '2': /* Callback */
3115 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
3116 if (strlen(vmu->callback) && vms.lastmsg > -1) {
3117 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2);
3124 cmd = play_and_wait(chan, "vm-sorry");
3127 case '3': /* Envelope */
3128 if(vms.lastmsg > -1)
3129 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3);
3131 cmd = play_and_wait(chan, "vm-sorry");
3134 case '4': /* Dialout */
3135 if (strlen(vmu->dialout)) {
3136 cmd = dialout(chan, vmu, NULL, vmu->dialout);
3143 cmd = play_and_wait(chan, "vm-sorry");
3147 case '*': /* Return to main menu */
3153 if (!vms.starting) {
3154 cmd = play_and_wait(chan, "vm-toreply");
3156 if (strlen(vmu->callback) && !vms.starting && !cmd) {
3157 cmd = play_and_wait(chan, "vm-tocallback");
3160 if (!cmd && !vms.starting) {
3161 cmd = play_and_wait(chan, "vm-tohearenv");
3163 if (strlen(vmu->dialout) && !cmd) {
3164 cmd = play_and_wait(chan, "vm-tomakecall");
3167 cmd = play_and_wait(chan, "vm-starmain");
3169 cmd = ast_waitfordigit(chan,6000);
3172 if (vms.repeats > 3)
3189 cmd = play_message(chan, vmu, &vms, vms.curmsg);
3191 cmd = play_and_wait(chan, "vm-nomore");
3195 if (vms.curmsg < vms.lastmsg) {
3197 cmd = play_message(chan, vmu, &vms, vms.curmsg);
3199 cmd = play_and_wait(chan, "vm-nomore");
3203 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
3205 adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
3206 if (vms.deleted[vms.curmsg])
3207 cmd = play_and_wait(chan, "vm-deleted");
3209 cmd = play_and_wait(chan, "vm-undeleted");
3212 if(vms.lastmsg > -1)
3213 cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
3215 cmd = play_and_wait(chan, "vm-nomore");
3219 adsi_folders(chan, 1, "Save to folder...");
3220 cmd = get_folder2(chan, "vm-savefolder", 1);
3221 box = 0; /* Shut up compiler */
3225 } else if (cmd > 0) {
3226 box = cmd = cmd - '0';
3227 cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
3228 vms.deleted[vms.curmsg]=1;
3230 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
3232 adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
3234 cmd = play_and_wait(chan, "vm-message");
3236 cmd = say_and_wait(chan, vms.curmsg + 1);
3238 cmd = play_and_wait(chan, "vm-savedto");
3240 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
3241 cmd = play_and_wait(chan, vms.fn);
3244 cmd = play_and_wait(chan, "vm-messages");
3247 if (!vms.starting) {
3248 cmd = play_and_wait(chan, "vm-onefor");
3250 cmd = play_and_wait(chan, vms.vmbox);
3252 cmd = play_and_wait(chan, "vm-messages");
3254 cmd = play_and_wait(chan, "vm-opts");
3259 cmd = vm_options(chan, vmu, &vms, vmfmts);
3261 adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
3263 default: /* Nothing */
3264 cmd = vm_instructions(chan, &vms);
3268 if ((cmd == 't') || (cmd == '#')) {
3278 ast_stopstream(chan);
3282 res = play_and_wait(chan, "vm-dialout");
3284 res = play_and_wait(chan, "vm-goodbye");