2 * Asterisk -- A telephony toolkit for Linux.
4 * Voicemail System (did you ever think it could be so easy?)
6 * Copyright (C) 2003-2004, Digium Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
29 #include <asterisk/cli.h>
30 #include <asterisk/utils.h>
39 #include <sys/types.h>
43 /* we define USESQLVM when we have MySQL or POSTGRES */
45 #include <mysql/mysql.h>
51 * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
53 #include <postgresql/libpq-fe.h>
58 static inline int sql_init(void) { return 0; }
59 static inline void sql_close(void) { }
62 #include "../asterisk.h"
63 #include "../astconf.h"
65 #define COMMAND_TIMEOUT 5000
67 #define VOICEMAIL_CONFIG "voicemail.conf"
68 #define ASTERISK_USERNAME "asterisk"
70 /* Default mail command to mail voicemail. Change it with the
71 mailcmd= command in voicemail.conf */
72 #define SENDMAIL "/usr/sbin/sendmail -t"
74 #define INTRO "vm-intro"
78 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
80 #define BASEMAXINLINE 256
81 #define BASELINELEN 72
82 #define BASEMAXINLINE 256
85 #define MAX_DATETIME_FORMAT 512
86 #define MAX_NUM_CID_CONTEXTS 10
88 static int load_config(void);
90 /* Syntaxes supported, not really language codes.
98 German requires the following additional soundfile:
101 Spanish requires the following additional soundfile:
104 Dutch, Portuguese & Spanish require the following additional soundfiles:
105 vm-INBOXs singular of 'new'
106 vm-Olds singular of 'old/heard/read'
125 unsigned char iobuf[BASEMAXINLINE];
128 /* Structure for linked list of users */
130 char context[80]; /* Voicemail context */
131 char mailbox[80]; /* Mailbox id, unique within vm context */
132 char password[80]; /* Secret pin code, numbers only */
133 char fullname[80]; /* Full name, for directory app */
134 char email[80]; /* E-mail address */
135 char pager[80]; /* E-mail address to pager (no attachment) */
136 char serveremail[80]; /* From: Mail address */
137 char mailcmd[160]; /* Configurable mail command */
138 char language[MAX_LANGUAGE]; /* Config: Language setting */
139 char zonetag[80]; /* Time zone */
151 struct ast_vm_user *next;
157 char msg_format[512];
158 struct vm_zone *next;
177 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
178 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
179 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);
180 static int vm_delete(char *file);
182 static char ext_pass_cmd[128];
184 static char *tdesc = "Comedian Mail (Voicemail System)";
186 static char *addesc = "Comedian Mail";
188 static char *synopsis_vm =
189 "Leave a voicemail message";
191 static char *descrip_vm =
192 " VoiceMail([s|u|b]extension[@context][&extension[@context]][...]): Leaves"
193 "voicemail for a given extension (must be configured in voicemail.conf).\n"
194 " If the extension is preceded by \n"
195 "* 's' then instructions for leaving the message will be skipped.\n"
196 "* 'u' then the \"unavailable\" message will be played.\n"
197 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
198 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
199 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
200 "extension 'o' in the current context.\n"
201 "If the caller presses '*' during the prompt, the call jumps to\n"
202 "extension 'a' in the current context.\n"
203 "If the requested mailbox does not exist, and there exists a priority\n"
204 "n + 101, then that priority will be taken next.\n"
205 "When multiple mailboxes are specified, the unavailable or busy message\n"
206 "will be taken from the first mailbox specified.\n"
207 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
208 "Otherwise, it returns 0.\n";
210 static char *synopsis_vmain =
211 "Enter voicemail system";
213 static char *descrip_vmain =
214 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
215 "for the checking of voicemail. The mailbox can be passed as the option,\n"
216 "which will stop the voicemail system from prompting the user for the mailbox.\n"
217 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
218 "a context is specified, logins are considered in that voicemail context only.\n"
219 "Returns -1 if the user hangs up or 0 otherwise.\n";
221 static char *synopsis_vm_box_exists =
222 "Check if vmbox exists";
224 static char *descrip_vm_box_exists =
225 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
226 "if the specified voice mailbox exists.\n";
229 /* Leave a message */
230 static char *capp = "VoiceMail2";
231 static char *app = "VoiceMail";
233 /* Check mail, control, etc */
234 static char *capp2 = "VoiceMailMain2";
235 static char *app2 = "VoiceMailMain";
237 static char *app3 = "MailboxExists";
239 AST_MUTEX_DEFINE_STATIC(vmlock);
240 struct ast_vm_user *users;
241 struct ast_vm_user *usersl;
242 struct vm_zone *zones = NULL;
243 struct vm_zone *zonesl = NULL;
244 static int attach_voicemail;
245 static int maxsilence;
246 static int silencethreshold = 128;
247 static char serveremail[80];
248 static char mailcmd[160]; /* Configurable mail cmd */
249 static char externnotify[160];
251 static char vmfmts[80];
252 static int vmminmessage;
253 static int vmmaxmessage;
256 static int maxlogins;
260 static int saycidinfo;
261 static int svmailinfo;
263 static int skipaftercmd;
264 static char dialcontext[80];
265 static char callcontext[80];
266 static char exitcontext[80];
268 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
271 static char *emailbody = NULL;
272 static int pbxskip = 0;
273 static char *emailsubject = NULL;
274 static char fromstring[100];
275 static char pagerfromstring[100];
276 static char emailtitle[100];
277 static char charset[32] = "ISO-8859-1";
279 static char adsifdn[4] = "\x00\x00\x00\x0F";
280 static char adsisec[4] = "\x9B\xDB\xF7\xAC";
281 static int adsiver = 1;
288 static void populate_defaults(struct ast_vm_user *vmu)
302 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
304 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
306 strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
309 static void apply_options(struct ast_vm_user *vmu, char *options)
311 /* Destructively Parse options and apply */
312 char *stringp = ast_strdupa(options);
316 while((s = strsep(&stringp, "|"))) {
318 if ((var = strsep(&value, "=")) && value) {
319 if (!strcasecmp(var, "attach")) {
324 } else if (!strcasecmp(var, "serveremail")) {
325 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
326 } else if (!strcasecmp(var, "language")) {
327 strncpy(vmu->language, value, sizeof(vmu->language) - 1);
328 } else if (!strcasecmp(var, "tz")) {
329 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
330 } else if (!strcasecmp(var, "delete")) {
331 vmu->delete = ast_true(value);
332 } else if (!strcasecmp(var, "saycid")){
337 } else if (!strcasecmp(var,"sendvoicemail")){
342 } else if (!strcasecmp(var, "review")){
347 } else if (!strcasecmp(var, "operator")){
352 } else if (!strcasecmp(var, "envelope")){
357 } else if (!strcasecmp(var, "callback")) {
358 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
359 } else if (!strcasecmp(var, "dialout")) {
360 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
361 } else if (!strcasecmp(var, "exitcontext")) {
362 strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
371 #include "mysql-vm-routines.h"
377 char dboption[256] = "";
378 AST_MUTEX_DEFINE_STATIC(postgreslock);
380 static int sql_init(void)
382 ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
383 /* fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
385 dbhandler=PQconnectdb(dboption);
386 if (PQstatus(dbhandler) == CONNECTION_BAD) {
387 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
390 /* fprintf(stderr,"postgres login OK\n"); */
394 static void sql_close(void)
400 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
408 char options[160] = "";
409 struct ast_vm_user *retval;
411 retval=malloc(sizeof(struct ast_vm_user));
413 /* fprintf(stderr,"postgres find_user:\n"); */
416 memset(retval, 0, sizeof(struct ast_vm_user));
419 strncpy(retval->mailbox, mailbox, sizeof(retval->mailbox) - 1);
422 strncpy(retval->context, context, sizeof(retval->context) - 1);
426 strncpy(retval->context, "default", sizeof(retval->context) - 1);
428 populate_defaults(retval);
429 snprintf(query, sizeof(query), "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
431 /* fprintf(stderr,"postgres find_user: query = %s\n",query); */
432 ast_mutex_lock(&postgreslock);
433 PGSQLres=PQexec(dbhandler,query);
434 if (PGSQLres!=NULL) {
435 if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
436 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
437 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
439 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
442 ast_mutex_unlock(&postgreslock);
446 numFields = PQnfields(PGSQLres);
447 /* fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
448 if (PQntuples(PGSQLres) != 1) {
449 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
451 ast_mutex_unlock(&postgreslock);
455 for (i=0; i<numFields; i++) {
456 fname = PQfname(PGSQLres,i);
457 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
458 strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
459 } else if (!strcmp(fname, "fullname")) {
460 strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
461 } else if (!strcmp(fname, "email")) {
462 strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
463 } else if (!strcmp(fname, "pager")) {
464 strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
465 } else if (!strcmp(fname, "options")) {
466 strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
467 apply_options(retval, options);
472 ast_mutex_unlock(&postgreslock);
476 ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
477 ast_mutex_unlock(&postgreslock);
482 } /* malloc() retval */
487 static void vm_change_password(struct ast_vm_user *vmu, char *password)
492 snprintf(query, sizeof(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);
494 snprintf(query, sizeof(query), "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
496 /* fprintf(stderr,"postgres change_password: query = %s\n",query); */
497 ast_mutex_lock(&postgreslock);
498 PQexec(dbhandler, query);
499 strncpy(vmu->password, password, sizeof(vmu->password) - 1);
500 ast_mutex_unlock(&postgreslock);
503 static void reset_user_pw(char *context, char *mailbox, char *password)
508 snprintf(query, sizeof(query), "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
510 snprintf(query, sizeof(query), "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
512 ast_mutex_lock(&postgreslock);
513 /* fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
514 PQexec(dbhandler, query);
515 ast_mutex_unlock(&postgreslock);
518 #endif /* Postgres */
522 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
524 /* This function could be made to generate one from a database, too */
525 struct ast_vm_user *vmu=NULL, *cur;
526 ast_mutex_lock(&vmlock);
529 if ((!context || !strcasecmp(context, cur->context)) &&
530 (!strcasecmp(mailbox, cur->mailbox)))
538 /* Make a copy, so that on a reload, we have no race */
539 vmu = malloc(sizeof(struct ast_vm_user));
541 memcpy(vmu, cur, sizeof(struct ast_vm_user));
549 ast_mutex_unlock(&vmlock);
553 static int reset_user_pw(char *context, char *mailbox, char *newpass)
555 /* This function could be made to generate one from a database, too */
556 struct ast_vm_user *cur;
558 ast_mutex_lock(&vmlock);
561 if ((!context || !strcasecmp(context, cur->context)) &&
562 (!strcasecmp(mailbox, cur->mailbox)))
567 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
570 ast_mutex_unlock(&vmlock);
574 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
576 /* There's probably a better way of doing this. */
577 /* That's why I've put the password change in a separate function. */
578 /* This could also be done with a database function */
585 char currcontext[256] ="";
586 char tmpin[AST_CONFIG_MAX_PATH];
587 char tmpout[AST_CONFIG_MAX_PATH];
588 char *user, *pass, *rest, *trim, *tempcontext;
591 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
592 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
593 configin = fopen(tmpin,"r");
595 configout = fopen(tmpout,"w+");
598 if(!configin || !configout) {
602 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
606 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
610 while (!feof(configin)) {
611 /* Read in the line */
612 fgets(inbuf, sizeof(inbuf), configin);
614 if (!feof(configin)) {
615 /* Make a backup of it */
616 memcpy(orig, inbuf, sizeof(orig));
617 /* Strip trailing \n and comment */
618 inbuf[strlen(inbuf) - 1] = '\0';
619 user = strchr(inbuf, ';');
625 /* check for '[' (opening of context name ) */
626 tempcontext = strchr(user, '[');
628 strncpy(currcontext, tempcontext +1,
629 sizeof(currcontext) - 1);
630 /* now check for ']' */
631 tempcontext = strchr(currcontext, ']');
635 currcontext[0] = '\0';
637 pass = strchr(user, '=');
640 while(*trim && *trim < 33) {
650 while(*pass && *pass < 33)
654 rest = strchr(pass,',');
662 /* Compare user, pass AND context */
663 if (user && *user && !strcmp(user, vmu->mailbox) &&
664 pass && !strcmp(pass, vmu->password) &&
665 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
666 /* This is the line */
668 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
670 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
673 /* Put it back like it was */
674 fprintf(configout, orig);
681 stat((char *)tmpin, &statbuf);
682 chmod((char *)tmpout, statbuf.st_mode);
683 chown((char *)tmpout, statbuf.st_uid, statbuf.st_gid);
684 unlink((char *)tmpin);
685 rename((char *)tmpout,(char *)tmpin);
686 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
687 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
691 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
694 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
695 ast_safe_system(buf);
698 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
700 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
703 static int make_file(char *dest, int len, char *dir, int num)
705 return snprintf(dest, len, "%s/msg%04d", dir, num);
709 inbuf(struct baseio *bio, FILE *fi)
716 if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
731 inchar(struct baseio *bio, FILE *fi)
733 if(bio->iocp>=bio->iolen)
737 return bio->iobuf[bio->iocp++];
741 ochar(struct baseio *bio, int c, FILE *so)
743 if(bio->linelength>=BASELINELEN) {
744 if(fputs(eol,so)==EOF)
750 if(putc(((unsigned char)c),so)==EOF)
758 static int base_encode(char *filename, FILE *so)
760 unsigned char dtable[BASEMAXINLINE];
765 memset(&bio, 0, sizeof(bio));
766 bio.iocp = BASEMAXINLINE;
768 if ( !(fi = fopen(filename, "rb"))) {
769 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
777 dtable[26+i+9]= 'j'+i;
781 dtable[26+i+18]= 's'+i;
790 unsigned char igroup[3],ogroup[4];
793 igroup[0]= igroup[1]= igroup[2]= 0;
796 if ( (c = inchar(&bio, fi)) == EOF) {
801 igroup[n]= (unsigned char)c;
805 ogroup[0]= dtable[igroup[0]>>2];
806 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
807 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
808 ogroup[3]= dtable[igroup[2]&0x3F];
818 ochar(&bio, ogroup[i], so);
822 if(fputs(eol,so)==EOF)
830 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, size_t passdatasize)
832 /* Prepare variables for substition in email body and subject */
833 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
834 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
835 snprintf(passdata, passdatasize, "%d", msgnum);
836 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
837 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
838 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
839 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
842 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)
852 char tmp[80] = "/tmp/astmail-XXXXXX";
856 struct vm_zone *the_zone = NULL;
857 if (vmu && ast_strlen_zero(vmu->email)) {
858 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
861 if (!strcmp(format, "wav49"))
863 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
864 /* Make a temporary file instead of piping directly to sendmail, in case the mail
868 p = fdopen(pfd, "w");
875 gethostname(host, sizeof(host));
876 if (strchr(srcemail, '@'))
877 strncpy(who, srcemail, sizeof(who)-1);
879 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
881 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
884 /* Does this user have a timezone specified? */
885 if (!ast_strlen_zero(vmu->zonetag)) {
886 /* Find the zone in the list */
890 if (!strcmp(z->name, vmu->zonetag)) {
899 ast_localtime(&t,&tm,the_zone->timezone);
901 ast_localtime(&t,&tm,NULL);
902 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
903 fprintf(p, "Date: %s\n", date);
906 struct ast_channel *ast = ast_channel_alloc(0);
909 int vmlen = strlen(fromstring)*3 + 200;
910 if ((passdata = alloca(vmlen))) {
911 memset(passdata, 0, vmlen);
912 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata, vmlen);
913 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
914 fprintf(p, "From: %s <%s>\n",passdata,who);
915 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
916 ast_channel_free(ast);
917 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
919 fprintf(p, "From: Asterisk PBX <%s>\n", who);
920 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
923 struct ast_channel *ast = ast_channel_alloc(0);
926 int vmlen = strlen(emailsubject)*3 + 200;
927 if ((passdata = alloca(vmlen))) {
928 memset(passdata, 0, vmlen);
929 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata, vmlen);
930 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
931 fprintf(p, "Subject: %s\n",passdata);
932 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
933 ast_channel_free(ast);
934 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
938 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
943 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
945 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
946 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
947 fprintf(p, "MIME-Version: 1.0\n");
948 if (attach_user_voicemail) {
949 /* Something unique. */
950 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
952 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
954 fprintf(p, "--%s\n", bound);
956 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
957 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
959 struct ast_channel *ast = ast_channel_alloc(0);
962 int vmlen = strlen(emailbody)*3 + 200;
963 if ((passdata = alloca(vmlen))) {
964 memset(passdata, 0, vmlen);
965 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata, vmlen);
966 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
967 fprintf(p, "%s\n",passdata);
968 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
969 ast_channel_free(ast);
970 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
972 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
974 "in mailbox %s from %s, on %s so you might\n"
975 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
976 dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
978 if (attach_user_voicemail) {
979 fprintf(p, "--%s\n", bound);
980 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
981 fprintf(p, "Content-Transfer-Encoding: base64\n");
982 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
983 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
985 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
986 base_encode(fname, p);
987 fprintf(p, "\n\n--%s--\n.\n", bound);
990 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
991 ast_safe_system(tmp2);
992 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
994 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1000 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, int duration, struct ast_vm_user *vmu)
1008 char tmp[80] = "/tmp/astmail-XXXXXX";
1012 struct vm_zone *the_zone = NULL;
1016 p = fdopen(pfd, "w");
1024 gethostname(host, sizeof(host));
1025 if (strchr(srcemail, '@'))
1026 strncpy(who, srcemail, sizeof(who)-1);
1028 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1030 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1033 /* Does this user have a timezone specified? */
1034 if (!ast_strlen_zero(vmu->zonetag)) {
1035 /* Find the zone in the list */
1039 if (!strcmp(z->name, vmu->zonetag)) {
1048 ast_localtime(&t,&tm,the_zone->timezone);
1050 ast_localtime(&t,&tm,NULL);
1052 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1053 fprintf(p, "Date: %s\n", date);
1055 if (*pagerfromstring) {
1056 struct ast_channel *ast = ast_channel_alloc(0);
1059 int vmlen = strlen(fromstring)*3 + 200;
1060 if ((passdata = alloca(vmlen))) {
1061 memset(passdata, 0, vmlen);
1062 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata, vmlen);
1063 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1064 fprintf(p, "From: %s <%s>\n",passdata,who);
1066 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1067 ast_channel_free(ast);
1068 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1070 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1071 fprintf(p, "To: %s\n", pager);
1072 fprintf(p, "Subject: New VM\n\n");
1073 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1074 fprintf(p, "New %s long msg in box %s\n"
1075 "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
1077 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1078 ast_safe_system(tmp2);
1079 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
1081 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1087 static int get_date(char *s, int len)
1092 localtime_r(&t,&tm);
1093 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1096 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1100 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1101 if (ast_fileexists(fn, NULL, NULL) > 0) {
1102 res = ast_streamfile(chan, fn, chan->language);
1105 res = ast_waitstream(chan, ecodes);
1109 res = ast_streamfile(chan, "vm-theperson", chan->language);
1112 res = ast_waitstream(chan, ecodes);
1115 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1120 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1122 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1125 res = ast_waitstream(chan, ecodes);
1129 static void free_user(struct ast_vm_user *vmu)
1135 static void free_zone(struct vm_zone *z)
1140 static char *mbox(int id)
1168 static int copy(char *infile, char *outfile)
1176 #ifdef HARDLINK_WHEN_POSSIBLE
1177 /* Hard link if possible; saves disk space & is faster */
1178 if (link(infile, outfile)) {
1180 if ((ifd = open(infile, O_RDONLY)) < 0) {
1181 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1184 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1185 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1190 len = read(ifd, buf, sizeof(buf));
1192 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1198 res = write(ofd, buf, len);
1200 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1210 #ifdef HARDLINK_WHEN_POSSIBLE
1212 /* Hard link succeeded */
1218 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid);
1220 static void copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt)
1222 char fromdir[256], todir[256], frompath[256], topath[256];
1223 char *frombox = mbox(imbox);
1226 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1228 make_dir(todir, sizeof(todir), recip->context, "", "");
1229 /* It's easier just to try to make it than to check for its existence */
1230 if (mkdir(todir, 0700) && (errno != EEXIST))
1231 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1232 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1233 /* It's easier just to try to make it than to check for its existence */
1234 if (mkdir(todir, 0700) && (errno != EEXIST))
1235 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1236 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1237 if (mkdir(todir, 0700) && (errno != EEXIST))
1238 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1240 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1241 make_file(frompath, sizeof(frompath), fromdir, msgnum);
1244 make_file(topath, sizeof(topath), todir, recipmsgnum);
1245 if (ast_fileexists(topath, NULL, chan->language) <= 0)
1248 } while(recipmsgnum < MAXMSG);
1249 if (recipmsgnum < MAXMSG) {
1250 char frompath2[256],topath2[256];
1251 ast_filecopy(frompath, topath, NULL);
1252 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1253 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1254 copy(frompath2, topath2);
1256 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1259 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->callerid);
1262 static void run_externnotify(char *context, char *extension)
1264 char arguments[255];
1265 int newvoicemails = 0, oldvoicemails = 0;
1267 if(!ast_strlen_zero(externnotify)) {
1268 if (ast_app_messagecount(extension, &newvoicemails, &oldvoicemails)) {
1269 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
1271 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
1272 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1273 ast_safe_system(arguments);
1279 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1292 char prefile[256]="";
1293 char ext_context[256] = "";
1296 char ecodes[16] = "#";
1297 char tmp[256] = "", *tmpptr;
1298 struct ast_vm_user *vmu;
1299 struct ast_vm_user svm;
1301 strncpy(tmp, ext, sizeof(tmp) - 1);
1303 context = strchr(tmp, '@');
1307 tmpptr = strchr(context, '&');
1309 tmpptr = strchr(ext, '&');
1317 if ((vmu = find_user(&svm, context, ext))) {
1318 /* Setup pre-file if appropriate */
1319 if (strcmp(vmu->context, "default"))
1320 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1322 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1324 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1326 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1327 make_dir(dir, sizeof(dir), vmu->context, "", "");
1328 /* It's easier just to try to make it than to check for its existence */
1329 if (mkdir(dir, 0700) && (errno != EEXIST))
1330 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1331 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1332 /* It's easier just to try to make it than to check for its existence */
1333 if (mkdir(dir, 0700) && (errno != EEXIST))
1334 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1335 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1336 if (mkdir(dir, 0700) && (errno != EEXIST))
1337 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1339 /* Check current or macro-calling context for special extensions */
1340 if (!ast_strlen_zero(vmu->exit)) {
1341 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->callerid))
1342 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
1343 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->callerid))
1344 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
1345 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->callerid)) {
1346 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
1350 if (!ast_strlen_zero(vmu->exit)) {
1351 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->callerid))
1352 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
1353 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->callerid))
1354 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
1355 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->callerid)) {
1356 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
1360 /* Play the beginning intro if desired */
1361 if (!ast_strlen_zero(prefile)) {
1362 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1363 if (ast_streamfile(chan, prefile, chan->language) > -1)
1364 res = ast_waitstream(chan, ecodes);
1366 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1367 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1370 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1376 /* On a '#' we skip the instructions */
1380 if (!res && !silent) {
1381 res = ast_streamfile(chan, INTRO, chan->language);
1383 res = ast_waitstream(chan, ecodes);
1390 ast_stopstream(chan);
1391 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1392 other than the operator -- an automated attendant or mailbox login for example */
1394 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1395 if (!ast_strlen_zero(vmu->exit)) {
1396 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1397 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
1398 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1404 /* Check for a '0' here */
1407 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1408 if (!ast_strlen_zero(vmu->exit)) {
1409 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1410 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
1411 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1418 /* Unless we're *really* silent, try to send the beep */
1419 res = ast_streamfile(chan, "beep", chan->language);
1421 res = ast_waitstream(chan, "");
1427 /* The meat of recording the message... All the announcements and beeps have been played*/
1428 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1429 if (!ast_strlen_zero(fmt)) {
1432 make_file(fn, sizeof(fn), dir, msgnum);
1433 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1436 } while(msgnum < MAXMSG);
1437 if (msgnum < MAXMSG) {
1438 /* Store information */
1439 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1440 txt = fopen(txtfile, "w+");
1442 get_date(date, sizeof(date));
1445 "; Message Information file\n"
1463 chan->callerid ? chan->callerid : "Unknown",
1464 date, (long)time(NULL));
1467 ast_log(LOG_WARNING, "Error opening text file for output\n");
1468 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1473 fd = open(txtfile, O_APPEND | O_WRONLY);
1475 txt = fdopen(fd, "a");
1477 fprintf(txt, "duration=%d\n", duration);
1482 if (duration < vmminmessage) {
1483 if (option_verbose > 2)
1484 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
1486 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
1489 /* Are there to be more recipients of this message? */
1491 struct ast_vm_user recipu, *recip;
1492 char *exten, *context;
1494 exten = strsep(&tmpptr, "&");
1495 context = strchr(exten, '@');
1500 if ((recip = find_user(&recipu, context, exten))) {
1501 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
1505 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->callerid);
1507 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1509 res = ast_waitstream(chan, "");
1510 ast_log(LOG_WARNING, "No more messages possible\n");
1513 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1517 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1518 /*Send the call to n+101 priority, where n is the current priority*/
1519 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1520 chan->priority+=100;
1526 static int count_messages(char *dir)
1530 for (x=0;x<MAXMSG;x++) {
1531 make_file(fn, sizeof(fn), dir, x);
1532 if (ast_fileexists(fn, NULL, NULL) < 1)
1538 static int say_and_wait(struct ast_channel *chan, int num, char *language)
1541 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
1545 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1552 char *dbox = mbox(box);
1554 make_file(sfn, sizeof(sfn), dir, msg);
1555 make_dir(ddir, sizeof(ddir), context, username, dbox);
1557 for (x=0;x<MAXMSG;x++) {
1558 make_file(dfn, sizeof(dfn), ddir, x);
1559 if (ast_fileexists(dfn, NULL, NULL) < 0)
1564 ast_filecopy(sfn, dfn, NULL);
1565 if (strcmp(sfn, dfn)) {
1566 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1567 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1573 static int adsi_logo(unsigned char *buf)
1576 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1577 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1581 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1589 bytes += adsi_data_mode(buf + bytes);
1590 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1593 bytes += adsi_logo(buf);
1594 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1596 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1598 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1599 bytes += adsi_data_mode(buf + bytes);
1600 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1602 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
1604 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1605 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1606 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1607 bytes += adsi_voice_mode(buf + bytes, 0);
1608 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1615 bytes += adsi_logo(buf);
1616 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1617 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1618 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1619 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1622 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1623 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1624 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1625 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1626 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1627 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1628 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1631 /* Add another dot */
1633 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1634 bytes += adsi_voice_mode(buf + bytes, 0);
1636 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1637 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1641 /* These buttons we load but don't use yet */
1642 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1643 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1644 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1645 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1646 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1647 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1648 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1651 /* Add another dot */
1653 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1654 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1655 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1660 snprintf(num, sizeof(num), "%d", x);
1661 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1663 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1664 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1667 /* Add another dot */
1669 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1670 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1671 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1674 if (adsi_end_download(chan)) {
1676 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1677 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1678 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1679 bytes += adsi_voice_mode(buf + bytes, 0);
1680 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1684 bytes += adsi_download_disconnect(buf + bytes);
1685 bytes += adsi_voice_mode(buf + bytes, 0);
1686 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1688 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1693 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1694 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1696 ast_log(LOG_DEBUG, "Restarting session...\n");
1699 /* Load the session now */
1700 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
1702 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1704 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1706 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1710 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1713 if (!adsi_available(chan))
1715 x = adsi_load_session(chan, adsifdn, adsiver, 1);
1719 if (adsi_load_vmail(chan, useadsi)) {
1720 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1727 static void adsi_login(struct ast_channel *chan)
1731 unsigned char keys[8];
1733 if (!adsi_available(chan))
1738 /* Set one key for next */
1739 keys[3] = ADSI_KEY_APPS + 3;
1741 bytes += adsi_logo(buf + bytes);
1742 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1743 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1744 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1745 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1746 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1747 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1748 bytes += adsi_set_keys(buf + bytes, keys);
1749 bytes += adsi_voice_mode(buf + bytes, 0);
1750 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1753 static void adsi_password(struct ast_channel *chan)
1757 unsigned char keys[8];
1759 if (!adsi_available(chan))
1764 /* Set one key for next */
1765 keys[3] = ADSI_KEY_APPS + 3;
1767 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1768 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1769 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1770 bytes += adsi_set_keys(buf + bytes, keys);
1771 bytes += adsi_voice_mode(buf + bytes, 0);
1772 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1775 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1779 unsigned char keys[8];
1782 if (!adsi_available(chan))
1786 y = ADSI_KEY_APPS + 12 + start + x;
1787 if (y > ADSI_KEY_APPS + 12 + 4)
1789 keys[x] = ADSI_KEY_SKT | y;
1791 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1795 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1796 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1797 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1798 bytes += adsi_set_keys(buf + bytes, keys);
1799 bytes += adsi_voice_mode(buf + bytes, 0);
1801 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1804 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
1807 char buf[256], buf1[256], buf2[256];
1813 char datetime[21]="";
1816 unsigned char keys[8];
1820 if (!adsi_available(chan))
1823 /* Retrieve important info */
1824 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
1825 f = fopen(fn2, "r");
1828 fgets(buf, sizeof(buf), f);
1832 strsep(&stringp, "=");
1833 val = strsep(&stringp, "=");
1834 if (val && !ast_strlen_zero(val)) {
1835 if (!strcmp(buf, "callerid"))
1836 strncpy(cid, val, sizeof(cid) - 1);
1837 if (!strcmp(buf, "origdate"))
1838 strncpy(datetime, val, sizeof(datetime) - 1);
1844 /* New meaning for keys */
1846 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1851 /* No prev key, provide "Folder" instead */
1852 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1854 if (vms->curmsg >= vms->lastmsg) {
1855 /* If last message ... */
1857 /* but not only message, provide "Folder" instead */
1858 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1859 bytes += adsi_voice_mode(buf + bytes, 0);
1862 /* Otherwise if only message, leave blank */
1867 if (!ast_strlen_zero(cid)) {
1868 ast_callerid_parse(cid, &name, &num);
1872 name = "Unknown Caller";
1874 /* If deleted, show "undeleted" */
1876 if (vms->deleted[vms->curmsg])
1877 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1880 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1881 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
1882 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
1883 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
1885 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1886 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1887 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1888 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1889 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1890 bytes += adsi_set_keys(buf + bytes, keys);
1891 bytes += adsi_voice_mode(buf + bytes, 0);
1893 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1896 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
1900 unsigned char keys[8];
1904 if (!adsi_available(chan))
1907 /* New meaning for keys */
1909 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1915 /* No prev key, provide "Folder" instead */
1916 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1918 if (vms->curmsg >= vms->lastmsg) {
1919 /* If last message ... */
1921 /* but not only message, provide "Folder" instead */
1922 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1924 /* Otherwise if only message, leave blank */
1929 /* If deleted, show "undeleted" */
1930 if (vms->deleted[vms->curmsg])
1931 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1934 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1935 bytes += adsi_set_keys(buf + bytes, keys);
1936 bytes += adsi_voice_mode(buf + bytes, 0);
1938 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1941 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
1943 char buf[256] = "", buf1[256] = "", buf2[256] = "";
1945 unsigned char keys[8];
1948 char *newm = (vms->newmessages == 1) ? "message" : "messages";
1949 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
1950 if (!adsi_available(chan))
1952 if (vms->newmessages) {
1953 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
1954 if (vms->oldmessages) {
1955 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
1956 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
1958 snprintf(buf2, sizeof(buf2), "%s.", newm);
1960 } else if (vms->oldmessages) {
1961 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
1962 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1964 strncpy(buf1, "You have no messages.", sizeof(buf1) - 1);
1968 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1969 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1970 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1973 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1977 /* Don't let them listen if there are none */
1978 if (vms->lastmsg < 0)
1980 bytes += adsi_set_keys(buf + bytes, keys);
1982 bytes += adsi_voice_mode(buf + bytes, 0);
1984 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1987 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
1989 char buf[256] = "", buf1[256] = "", buf2[256] = "";
1991 unsigned char keys[8];
1994 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
1996 if (!adsi_available(chan))
1999 /* Original command keys */
2001 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2006 if ((vms->lastmsg + 1) < 1)
2009 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
2010 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
2012 if (vms->lastmsg + 1)
2013 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
2015 strncpy(buf2, "no messages.", sizeof(buf2) - 1);
2016 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2017 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2018 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2019 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2020 bytes += adsi_set_keys(buf + bytes, keys);
2022 bytes += adsi_voice_mode(buf + bytes, 0);
2024 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2029 static void adsi_clear(struct ast_channel *chan)
2033 if (!adsi_available(chan))
2035 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2036 bytes += adsi_voice_mode(buf + bytes, 0);
2038 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2042 static void adsi_goodbye(struct ast_channel *chan)
2047 if (!adsi_available(chan))
2049 bytes += adsi_logo(buf + bytes);
2050 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2051 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2052 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2053 bytes += adsi_voice_mode(buf + bytes, 0);
2055 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2058 /*--- get_folder: Folder menu ---*/
2059 /* Plays "press 1 for INBOX messages" etc
2060 Should possibly be internationalized
2062 static int get_folder(struct ast_channel *chan, int start)
2067 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
2070 for (x = start; x< 5; x++) { /* For all folders */
2071 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2073 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
2076 if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Spanish, French or Portuguese syntax */
2077 d = ast_play_and_wait(chan, "vm-messages"); /* "messages */
2080 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2081 d = ast_play_and_wait(chan, fn);
2084 } else { /* Default English */
2085 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2086 d = ast_play_and_wait(chan, fn);
2089 d = ast_play_and_wait(chan, "vm-messages"); /* "messages */
2093 d = ast_waitfordigit(chan, 500);
2097 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2100 d = ast_waitfordigit(chan, 4000);
2104 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2107 res = ast_play_and_wait(chan, fn); /* Folder name */
2108 while (((res < '0') || (res > '9')) &&
2109 (res != '#') && (res >= 0)) {
2110 res = get_folder(chan, 0);
2115 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2121 while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2126 /* prepend a message to the current message and return */
2129 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2130 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
2140 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
2141 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2143 cmd = ast_play_and_wait(chan,"vm-starmain");
2144 /* "press star to return to the main menu" */
2146 cmd = ast_waitfordigit(chan,6000);
2158 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid)
2160 char todir[256], fn[256], ext_context[256], *stringp;
2162 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2163 make_file(fn, sizeof(fn), todir, msgnum);
2164 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
2166 /* Attach only the first format */
2167 fmt = ast_strdupa(fmt);
2170 strsep(&stringp, "|");
2172 if (!ast_strlen_zero(vmu->email)) {
2173 int attach_user_voicemail = attach_voicemail;
2174 char *myserveremail = serveremail;
2175 if (vmu->attach > -1)
2176 attach_user_voicemail = vmu->attach;
2177 if (!ast_strlen_zero(vmu->serveremail))
2178 myserveremail = vmu->serveremail;
2179 sendmail(myserveremail, vmu, msgnum, vmu->mailbox, callerid, fn, fmt, duration, attach_user_voicemail);
2182 if (!ast_strlen_zero(vmu->pager)) {
2183 char *myserveremail = serveremail;
2184 if (!ast_strlen_zero(vmu->serveremail))
2185 myserveremail = vmu->serveremail;
2186 sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, callerid, duration, vmu);
2189 ast_log(LOG_ERROR, "Out of memory\n");
2196 /* Leave voicemail for someone */
2197 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context));
2198 run_externnotify(chan->context, ext_context);
2202 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt,int flag)
2209 struct ast_config *mif;
2213 char ext_context[256]="";
2214 int res = 0, cmd = 0;
2215 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2218 int saved_messages = 0, found = 0;
2219 int valid_extensions = 0;
2220 while (!res && !valid_extensions) {
2221 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
2224 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2226 /* start all over if no username */
2227 if (ast_strlen_zero(username))
2230 s = strsep(&stringp, "*");
2231 /* start optimistic */
2232 valid_extensions = 1;
2234 /* find_user is going to malloc since we have a NULL as first argument */
2235 if ((receiver = find_user(NULL, context, s))) {
2237 vmtmp = extensions = receiver;
2239 vmtmp->next = receiver;
2244 valid_extensions = 0;
2247 s = strsep(&stringp, "*");
2249 /* break from the loop of reading the extensions */
2250 if (valid_extensions)
2252 /* "I am sorry, that's not a valid extension. Please try again." */
2253 res = ast_play_and_wait(chan, "pbx-invalid");
2255 /* check if we're clear to proceed */
2256 if (!extensions || !valid_extensions)
2259 if(flag==1)/*Send VoiceMail*/
2261 cmd=leave_voicemail(chan,username,0,0,0);
2264 else /*Forward VoiceMail*/
2266 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2268 while(!res && vmtmp) {
2269 /* if (ast_play_and_wait(chan, "vm-savedto"))
2272 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2273 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2274 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2275 ast_log(LOG_DEBUG, sys);
2276 ast_safe_system(sys);
2278 todircount = count_messages(todir);
2279 strncpy(tmp, fmt, sizeof(tmp) - 1);
2281 while((s = strsep(&stringp, "|"))) {
2282 /* XXX This is a hack -- we should use build_filename or similar XXX */
2283 if (!strcasecmp(s, "wav49"))
2285 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2286 ast_log(LOG_DEBUG, sys);
2287 ast_safe_system(sys);
2289 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2290 ast_log(LOG_DEBUG, sys);
2291 ast_safe_system(sys);
2292 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2294 /* load the information on the source message so we can send an e-mail like a new message */
2295 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2296 if ((mif=ast_load(miffile))) {
2298 /* set callerid and duration variables */
2299 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2300 s = ast_variable_retrieve(mif, NULL, "duration");
2305 if (!ast_strlen_zero(vmtmp->email)) {
2306 int attach_user_voicemail = attach_voicemail;
2307 char *myserveremail = serveremail;
2308 if (vmtmp->attach > -1)
2309 attach_user_voicemail = vmtmp->attach;
2310 if (!ast_strlen_zero(vmtmp->serveremail))
2311 myserveremail = vmtmp->serveremail;
2312 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2315 if (!ast_strlen_zero(vmtmp->pager)) {
2316 char *myserveremail = serveremail;
2317 if (!ast_strlen_zero(vmtmp->serveremail))
2318 myserveremail = vmtmp->serveremail;
2319 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2322 ast_destroy(mif); /* or here */
2324 /* Leave voicemail for someone */
2325 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2326 run_externnotify(chan->context, ext_context);
2330 vmtmp = vmtmp->next;
2333 if (saved_messages > 0) {
2334 /* give confirmation that the message was saved */
2335 /* commented out since we can't forward batches yet
2336 if (saved_messages == 1)
2337 res = ast_play_and_wait(chan, "vm-message");
2339 res = ast_play_and_wait(chan, "vm-messages");
2341 res = ast_play_and_wait(chan, "vm-saved"); */
2343 res = ast_play_and_wait(chan, "vm-msgsaved");
2347 return res ? res : cmd;
2350 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2353 if ((res = ast_streamfile(chan, file, chan->language)))
2354 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2356 res = ast_waitstream(chan, AST_DIGIT_ANY);
2360 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2362 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
2365 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2368 struct vm_zone *the_zone = NULL;
2372 if (sscanf(origtime,"%ld",&tin) < 1) {
2373 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2378 /* Does this user have a timezone specified? */
2379 if (!ast_strlen_zero(vmu->zonetag)) {
2380 /* Find the zone in the list */
2384 if (!strcmp(z->name, vmu->zonetag)) {
2392 /* No internal variable parsing for now, so we'll comment it out for the time being */
2394 /* Set the DIFF_* variables */
2395 localtime_r(&t, &time_now);
2396 gettimeofday(&tv_now,NULL);
2397 tnow = tv_now.tv_sec;
2398 localtime_r(&tnow,&time_then);
2400 /* Day difference */
2401 if (time_now.tm_year == time_then.tm_year)
2402 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
2404 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2405 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2407 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2410 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2411 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
2412 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
2414 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2416 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2423 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2427 char *callerid, *name;
2428 char prefile[256]="";
2431 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2432 /* 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 */
2433 if((cid == NULL)||(context == NULL))
2436 /* Strip off caller ID number from name */
2437 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2438 ast_callerid_parse(cid, &name, &callerid);
2439 if((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
2440 /* Check for internal contexts and only */
2441 /* say extension when the call didn't come from an internal context in the list */
2442 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2443 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2444 if((strcmp(cidinternalcontexts[i], context) == 0))
2447 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2449 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2450 if (!ast_strlen_zero(prefile)) {
2451 /* See if we can find a recorded name for this person instead of their extension number */
2452 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2453 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2455 res = wait_file2(chan, vms, "vm-from");
2456 res = ast_streamfile(chan, prefile, chan->language) > -1;
2457 res = ast_waitstream(chan, "");
2459 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2460 /* BB: Say "from extension" as one saying to sound smoother */
2462 res = wait_file2(chan, vms, "vm-from-extension");
2463 res = ast_say_digit_str(chan, callerid, "", chan->language);
2470 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2471 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2473 res = wait_file2(chan, vms, "vm-from-phonenumber");
2474 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2478 /* Number unknown */
2479 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2481 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2482 res = wait_file2(chan, vms, "vm-unknown-caller");
2487 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2490 char filename[256],*origtime, *cid, *context;
2491 struct ast_config *msg_cfg;
2494 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
2495 adsi_message(chan, vms);
2497 res = wait_file2(chan, vms, "vm-first"); /* "First" */
2498 else if (vms->curmsg == vms->lastmsg)
2499 res = wait_file2(chan, vms, "vm-last"); /* "last" */
2501 res = wait_file2(chan, vms, "vm-message"); /* "message" */
2502 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
2504 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2508 /* Retrieve info from VM attribute file */
2509 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2510 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2511 msg_cfg = ast_load(filename);
2513 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2517 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2520 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2522 context = ast_variable_retrieve(msg_cfg, "message", "context");
2523 if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2524 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2526 if ((!res)&&(vmu->envelope))
2527 res = play_message_datetime(chan, vmu, origtime, filename);
2528 if ((!res)&&(vmu->saycid))
2529 res = play_message_callerid(chan, vms, cid, context, 0);
2530 /* Allow pressing '1' to skip envelope / callerid */
2533 ast_destroy(msg_cfg);
2536 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
2537 vms->heard[vms->curmsg] = 1;
2538 res = wait_file(chan, vms, vms->fn);
2543 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2545 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2546 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2547 vms->lastmsg = count_messages(vms->curdir) - 1;
2548 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2551 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2554 char ntxt[256] = "";
2556 if (vms->lastmsg > -1) {
2557 /* Get the deleted messages fixed */
2559 for (x=0;x < MAXMSG;x++) {
2560 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2561 /* Save this message. It's not in INBOX or hasn't been heard */
2562 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2563 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2566 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2567 if (strcmp(vms->fn, vms->fn2)) {
2568 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2569 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2570 ast_filerename(vms->fn, vms->fn2, NULL);
2573 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2574 /* Move to old folder before deleting */
2575 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2578 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2579 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2580 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2585 memset(vms->deleted, 0, sizeof(vms->deleted));
2586 memset(vms->heard, 0, sizeof(vms->heard));
2589 /* Default English syntax */
2590 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2592 /* Introduce messages they have */
2594 res = ast_play_and_wait(chan, "vm-youhave");
2596 if (vms->newmessages) {
2597 res = say_and_wait(chan, vms->newmessages, chan->language);
2599 res = ast_play_and_wait(chan, "vm-INBOX");
2600 if (vms->oldmessages && !res)
2601 res = ast_play_and_wait(chan, "vm-and");
2603 if ((vms->newmessages == 1))
2604 res = ast_play_and_wait(chan, "vm-message");
2606 res = ast_play_and_wait(chan, "vm-messages");
2610 if (!res && vms->oldmessages) {
2611 res = say_and_wait(chan, vms->oldmessages, chan->language);
2613 res = ast_play_and_wait(chan, "vm-Old");
2615 if (vms->oldmessages == 1)
2616 res = ast_play_and_wait(chan, "vm-message");
2618 res = ast_play_and_wait(chan, "vm-messages");
2622 if (!vms->oldmessages && !vms->newmessages) {
2623 res = ast_play_and_wait(chan, "vm-no");
2625 res = ast_play_and_wait(chan, "vm-messages");
2633 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
2635 /* Introduce messages they have */
2637 res = ast_play_and_wait(chan, "vm-youhave");
2639 if (vms->newmessages) {
2640 if ((vms->newmessages == 1))
2641 res = ast_play_and_wait(chan, "digits/1F");
2643 res = say_and_wait(chan, vms->newmessages, chan->language);
2645 res = ast_play_and_wait(chan, "vm-INBOX");
2646 if (vms->oldmessages && !res)
2647 res = ast_play_and_wait(chan, "vm-and");
2649 if ((vms->newmessages == 1))
2650 res = ast_play_and_wait(chan, "vm-message");
2652 res = ast_play_and_wait(chan, "vm-messages");
2656 if (!res && vms->oldmessages) {
2657 if (vms->oldmessages == 1)
2658 res = ast_play_and_wait(chan, "digits/1F");
2660 res = say_and_wait(chan, vms->oldmessages, chan->language);
2662 res = ast_play_and_wait(chan, "vm-Old");
2664 if (vms->oldmessages == 1)
2665 res = ast_play_and_wait(chan, "vm-message");
2667 res = ast_play_and_wait(chan, "vm-messages");
2671 if (!vms->oldmessages && !vms->newmessages) {
2672 res = ast_play_and_wait(chan, "vm-no");
2674 res = ast_play_and_wait(chan, "vm-messages");
2681 /* SPANISH syntax */
2682 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
2684 /* Introduce messages they have */
2686 if (!vms->oldmessages && !vms->newmessages) {
2687 res = ast_play_and_wait(chan, "vm-youhaveno");
2689 res = ast_play_and_wait(chan, "vm-messages");
2691 res = ast_play_and_wait(chan, "vm-youhave");
2694 if (vms->newmessages) {
2696 if ((vms->newmessages == 1)) {
2697 res = ast_play_and_wait(chan, "digits/1M");
2699 res = ast_play_and_wait(chan, "vm-message");
2701 res = ast_play_and_wait(chan, "vm-INBOXs");
2703 res = say_and_wait(chan, vms->newmessages, chan->language);
2705 res = ast_play_and_wait(chan, "vm-messages");
2707 res = ast_play_and_wait(chan, "vm-INBOX");
2710 if (vms->oldmessages && !res)
2711 res = ast_play_and_wait(chan, "vm-and");
2713 if (vms->oldmessages) {
2715 if (vms->oldmessages == 1) {
2716 res = ast_play_and_wait(chan, "digits/1M");
2718 res = ast_play_and_wait(chan, "vm-message");
2720 res = ast_play_and_wait(chan, "vm-Olds");
2722 res = say_and_wait(chan, vms->oldmessages, chan->language);
2724 res = ast_play_and_wait(chan, "vm-messages");
2726 res = ast_play_and_wait(chan, "vm-Old");
2735 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
2737 /* Introduce messages they have */
2739 res = ast_play_and_wait(chan, "vm-youhave");
2741 if (vms->newmessages) {
2742 res = say_and_wait(chan, vms->newmessages, chan->language);
2744 res = ast_play_and_wait(chan, "vm-INBOX");
2745 if (vms->oldmessages && !res)
2746 res = ast_play_and_wait(chan, "vm-and");
2748 if ((vms->newmessages == 1))
2749 res = ast_play_and_wait(chan, "vm-message");
2751 res = ast_play_and_wait(chan, "vm-messages");
2755 if (!res && vms->oldmessages) {
2756 res = say_and_wait(chan, vms->oldmessages, chan->language);
2758 if (vms->oldmessages == 1)
2759 res = ast_play_and_wait(chan, "vm-message");
2761 res = ast_play_and_wait(chan, "vm-messages");
2764 res = ast_play_and_wait(chan, "vm-Old");
2767 if (!vms->oldmessages && !vms->newmessages) {
2768 res = ast_play_and_wait(chan, "vm-no");
2770 res = ast_play_and_wait(chan, "vm-messages");
2778 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
2780 /* Introduce messages they have */
2782 res = ast_play_and_wait(chan, "vm-youhave");
2784 if (vms->newmessages) {
2785 res = say_and_wait(chan, vms->newmessages, chan->language);
2787 if (vms->oldmessages == 1)
2788 res = ast_play_and_wait(chan, "vm-INBOXs");
2790 res = ast_play_and_wait(chan, "vm-INBOX");
2792 if (vms->oldmessages && !res)
2793 res = ast_play_and_wait(chan, "vm-and");
2795 if ((vms->newmessages == 1))
2796 res = ast_play_and_wait(chan, "vm-message");
2798 res = ast_play_and_wait(chan, "vm-messages");
2802 if (!res && vms->oldmessages) {
2803 res = say_and_wait(chan, vms->oldmessages, chan->language);
2805 if (vms->oldmessages == 1)
2806 res = ast_play_and_wait(chan, "vm-Olds");
2808 res = ast_play_and_wait(chan, "vm-Old");
2811 if (vms->oldmessages == 1)
2812 res = ast_play_and_wait(chan, "vm-message");
2814 res = ast_play_and_wait(chan, "vm-messages");
2818 if (!vms->oldmessages && !vms->newmessages) {
2819 res = ast_play_and_wait(chan, "vm-no");
2821 res = ast_play_and_wait(chan, "vm-messages");
2828 /* PORTUGUESE syntax */
2829 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
2831 /* Introduce messages they have */
2833 res = ast_play_and_wait(chan, "vm-youhave");
2835 if (vms->newmessages) {
2836 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
2838 if ((vms->newmessages == 1)) {
2839 res = ast_play_and_wait(chan, "vm-message");
2841 res = ast_play_and_wait(chan, "vm-INBOXs");
2843 res = ast_play_and_wait(chan, "vm-messages");
2845 res = ast_play_and_wait(chan, "vm-INBOX");
2848 if (vms->oldmessages && !res)
2849 res = ast_play_and_wait(chan, "vm-and");
2851 if (!res && vms->oldmessages) {
2852 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
2854 if (vms->oldmessages == 1) {
2855 res = ast_play_and_wait(chan, "vm-message");
2857 res = ast_play_and_wait(chan, "vm-Olds");
2859 res = ast_play_and_wait(chan, "vm-messages");
2861 res = ast_play_and_wait(chan, "vm-Old");
2866 if (!vms->oldmessages && !vms->newmessages) {
2867 res = ast_play_and_wait(chan, "vm-no");
2869 res = ast_play_and_wait(chan, "vm-messages");
2878 /* in czech there must be declension of word new and message
2879 * czech : english : czech : english
2880 * --------------------------------------------------------
2881 * vm-youhave : you have
2882 * vm-novou : one new : vm-zpravu : message
2883 * vm-nove : 2-4 new : vm-zpravy : messages
2884 * vm-novych : 5-infinite new : vm-zprav : messages
2885 * vm-starou : one old
2886 * vm-stare : 2-4 old
2887 * vm-starych : 5-infinite old
2888 * jednu : one - falling 4.
2889 * vm-no : no ( no messages )
2892 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
2895 res = ast_play_and_wait(chan, "vm-youhave");
2897 if (vms->newmessages) {
2898 if (vms->newmessages == 1) {
2899 res = ast_play_and_wait(chan, "digits/jednu");
2901 res = say_and_wait(chan, vms->newmessages, chan->language);
2904 if ((vms->newmessages == 1))
2905 res = ast_play_and_wait(chan, "vm-novou");
2906 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
2907 res = ast_play_and_wait(chan, "vm-nove");
2908 if (vms->newmessages > 4)
2909 res = ast_play_and_wait(chan, "vm-novych");
2911 if (vms->oldmessages && !res)
2912 res = ast_play_and_wait(chan, "vm-and");
2914 if ((vms->newmessages == 1))
2915 res = ast_play_and_wait(chan, "vm-zpravu");
2916 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
2917 res = ast_play_and_wait(chan, "vm-zpravy");
2918 if (vms->newmessages > 4)
2919 res = ast_play_and_wait(chan, "vm-zprav");
2922 if (!res && vms->oldmessages) {
2923 res = say_and_wait(chan, vms->oldmessages, chan->language);
2925 if ((vms->oldmessages == 1))
2926 res = ast_play_and_wait(chan, "vm-starou");
2927 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
2928 res = ast_play_and_wait(chan, "vm-stare");
2929 if (vms->oldmessages > 4)
2930 res = ast_play_and_wait(chan, "vm-starych");