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 #include "../asterisk.h"
44 #include "../astconf.h"
46 #define COMMAND_TIMEOUT 5000
48 #define VOICEMAIL_CONFIG "voicemail.conf"
49 #define ASTERISK_USERNAME "asterisk"
51 /* Default mail command to mail voicemail. Change it with the
52 mailcmd= command in voicemail.conf */
53 #define SENDMAIL "/usr/sbin/sendmail -t"
55 #define INTRO "vm-intro"
59 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
61 #define BASEMAXINLINE 256
62 #define BASELINELEN 72
63 #define BASEMAXINLINE 256
66 #define MAX_DATETIME_FORMAT 512
67 #define MAX_NUM_CID_CONTEXTS 10
69 static int load_config(void);
71 /* Syntaxes supported, not really language codes.
79 German requires the following additional soundfile:
82 Spanish requires the following additional soundfile:
85 Dutch, Portuguese & Spanish require the following additional soundfiles:
86 vm-INBOXs singular of 'new'
87 vm-Olds singular of 'old/heard/read'
106 unsigned char iobuf[BASEMAXINLINE];
109 /* Structure for linked list of users */
111 char context[80]; /* Voicemail context */
112 char mailbox[80]; /* Mailbox id, unique within vm context */
113 char password[80]; /* Secret pin code, numbers only */
114 char fullname[80]; /* Full name, for directory app */
115 char email[80]; /* E-mail address */
116 char pager[80]; /* E-mail address to pager (no attachment) */
117 char serveremail[80]; /* From: Mail address */
118 char mailcmd[160]; /* Configurable mail command */
119 char language[MAX_LANGUAGE]; /* Config: Language setting */
120 char zonetag[80]; /* Time zone */
123 char uniqueid[20]; /* Unique integer identifier */
137 struct ast_vm_user *next;
143 char msg_format[512];
144 struct vm_zone *next;
163 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
164 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
165 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);
166 static int vm_delete(char *file);
168 static char ext_pass_cmd[128];
170 static char *tdesc = "Comedian Mail (Voicemail System)";
172 static char *addesc = "Comedian Mail";
174 static char *synopsis_vm =
175 "Leave a voicemail message";
177 static char *descrip_vm =
178 " VoiceMail([s|u|b]extension[@context][&extension[@context]][...]): Leaves"
179 "voicemail for a given extension (must be configured in voicemail.conf).\n"
180 " If the extension is preceded by \n"
181 "* 's' then instructions for leaving the message will be skipped.\n"
182 "* 'u' then the \"unavailable\" message will be played.\n"
183 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
184 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
185 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
186 "extension 'o' in the current context.\n"
187 "If the caller presses '*' during the prompt, the call jumps to\n"
188 "extension 'a' in the current context.\n"
189 "If the requested mailbox does not exist, and there exists a priority\n"
190 "n + 101, then that priority will be taken next.\n"
191 "When multiple mailboxes are specified, the unavailable or busy message\n"
192 "will be taken from the first mailbox specified.\n"
193 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
194 "Otherwise, it returns 0.\n";
196 static char *synopsis_vmain =
197 "Enter voicemail system";
199 static char *descrip_vmain =
200 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
201 "for the checking of voicemail. The mailbox can be passed as the option,\n"
202 "which will stop the voicemail system from prompting the user for the mailbox.\n"
203 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
204 "the mailbox is preceded by 'p' then the supplied mailbox is prepended to the\n"
205 "user's entry and the resulting string is used as the mailbox number. This is\n"
206 "useful for virtual hosting of voicemail boxes. If a context is specified,\n"
207 "logins are considered in that voicemail context only.\n"
208 "Returns -1 if the user hangs up or 0 otherwise.\n";
210 static char *synopsis_vm_box_exists =
211 "Check if vmbox exists";
213 static char *descrip_vm_box_exists =
214 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
215 "if the specified voice mailbox exists.\n";
217 static char *synopsis_vmauthenticate =
218 "Authenticate off voicemail passwords";
220 static char *descrip_vmauthenticate =
221 " VMAuthenticate([mailbox][@context]): Behaves identically to the Authenticate\n"
222 "application, with the exception that the passwords are taken from\n"
224 " If the mailbox is specified, only that mailbox's password will be considered\n"
225 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
226 "be set with the authenticated mailbox.\n";
228 /* Leave a message */
229 static char *app = "VoiceMail";
231 /* Check mail, control, etc */
232 static char *app2 = "VoiceMailMain";
234 static char *app3 = "MailboxExists";
235 static char *app4 = "VMAuthenticate";
237 AST_MUTEX_DEFINE_STATIC(vmlock);
238 struct ast_vm_user *users;
239 struct ast_vm_user *usersl;
240 struct vm_zone *zones = NULL;
241 struct vm_zone *zonesl = NULL;
242 static int attach_voicemail;
243 static int maxsilence;
244 static int silencethreshold = 128;
245 static char serveremail[80];
246 static char mailcmd[160]; /* Configurable mail cmd */
247 static char externnotify[160];
249 static char vmfmts[80];
250 static int vmminmessage;
251 static int vmmaxmessage;
254 static int maxlogins;
258 static int saycidinfo;
259 static int svmailinfo;
261 static int saydurationinfo;
262 static int saydurationminfo;
263 static int skipaftercmd;
266 static char dialcontext[80];
267 static char callcontext[80];
268 static char exitcontext[80];
270 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
273 static char *emailbody = NULL;
274 static int pbxskip = 0;
275 static char *emailsubject = NULL;
276 static char fromstring[100];
277 static char pagerfromstring[100];
278 static char emailtitle[100];
279 static char charset[32] = "ISO-8859-1";
281 static char adsifdn[4] = "\x00\x00\x00\x0F";
282 static char adsisec[4] = "\x9B\xDB\xF7\xAC";
283 static int adsiver = 1;
290 static void populate_defaults(struct ast_vm_user *vmu)
304 vmu->sayduration = 1;
305 if (saydurationminfo>0)
306 vmu->saydurationm = saydurationminfo;
310 vmu->forcegreetings = 1;
312 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
314 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
316 strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
319 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
322 if (!strcasecmp(var, "attach")) {
327 } else if (!strcasecmp(var, "serveremail")) {
328 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
329 } else if (!strcasecmp(var, "language")) {
330 strncpy(vmu->language, value, sizeof(vmu->language) - 1);
331 } else if (!strcasecmp(var, "tz")) {
332 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
333 } else if (!strcasecmp(var, "delete")) {
334 vmu->delete = ast_true(value);
335 } else if (!strcasecmp(var, "saycid")){
340 } else if (!strcasecmp(var,"sendvoicemail")){
345 } else if (!strcasecmp(var, "review")){
350 } else if (!strcasecmp(var, "operator")){
355 } else if (!strcasecmp(var, "envelope")){
360 } else if (!strcasecmp(var, "sayduration")){
362 vmu->sayduration = 1;
364 vmu->sayduration = 0;
365 } else if (!strcasecmp(var, "saydurationm")){
366 if (sscanf(value, "%d", &x) == 1) {
367 vmu->saydurationm = x;
369 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
371 } else if (!strcasecmp(var, "forcename")){
376 } else if (!strcasecmp(var, "forcegreetings")){
378 vmu->forcegreetings = 1;
380 vmu->forcegreetings = 0;
381 } else if (!strcasecmp(var, "callback")) {
382 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
383 } else if (!strcasecmp(var, "dialout")) {
384 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
385 } else if (!strcasecmp(var, "exitcontext")) {
386 strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
390 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
393 if (!ast_strlen_zero(vmu->uniqueid)) {
394 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password);
396 strncpy(vmu->password, password, sizeof(vmu->password) - 1);
402 static void apply_options(struct ast_vm_user *vmu, const char *options)
403 { /* Destructively Parse options and apply */
407 stringp = ast_strdupa(options);
408 while ((s = strsep(&stringp, "|"))) {
410 if ((var = strsep(&value, "=")) && value) {
411 apply_option(vmu, var, value);
416 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
418 struct ast_variable *var, *tmp;
419 struct ast_vm_user *retval;
424 retval=malloc(sizeof(struct ast_vm_user));
427 memset(retval, 0, sizeof(struct ast_vm_user));
431 strncpy(retval->mailbox, mailbox, sizeof(retval->mailbox) - 1);
433 strncpy(retval->context, context, sizeof(retval->context) - 1);
435 strncpy(retval->context, "default", sizeof(retval->context) - 1);
436 populate_defaults(retval);
437 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", retval->context, NULL);
441 printf("%s => %s\n", tmp->name, tmp->value);
442 if (!strcasecmp(tmp->name, "password")) {
443 strncpy(retval->password, tmp->value, sizeof(retval->password) - 1);
444 } else if (!strcasecmp(tmp->name, "uniqueid")) {
445 strncpy(retval->uniqueid, tmp->value, sizeof(retval->uniqueid) - 1);
446 } else if (!strcasecmp(tmp->name, "pager")) {
447 strncpy(retval->pager, tmp->value, sizeof(retval->pager) - 1);
448 } else if (!strcasecmp(tmp->name, "email")) {
449 strncpy(retval->email, tmp->value, sizeof(retval->email) - 1);
450 } else if (!strcasecmp(tmp->name, "fullname")) {
451 strncpy(retval->fullname, tmp->value, sizeof(retval->fullname) - 1);
453 apply_option(retval, tmp->name, tmp->value);
465 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
467 /* This function could be made to generate one from a database, too */
468 struct ast_vm_user *vmu=NULL, *cur;
469 ast_mutex_lock(&vmlock);
472 if ((!context || !strcasecmp(context, cur->context)) &&
473 (!strcasecmp(mailbox, cur->mailbox)))
481 /* Make a copy, so that on a reload, we have no race */
482 vmu = malloc(sizeof(struct ast_vm_user));
484 memcpy(vmu, cur, sizeof(struct ast_vm_user));
492 vmu = find_user_realtime(ivm, context, mailbox);
493 ast_mutex_unlock(&vmlock);
497 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
499 /* This function could be made to generate one from a database, too */
500 struct ast_vm_user *cur;
502 ast_mutex_lock(&vmlock);
505 if ((!context || !strcasecmp(context, cur->context)) &&
506 (!strcasecmp(mailbox, cur->mailbox)))
511 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
514 ast_mutex_unlock(&vmlock);
518 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
520 /* There's probably a better way of doing this. */
521 /* That's why I've put the password change in a separate function. */
522 /* This could also be done with a database function */
529 char currcontext[256] ="";
530 char tmpin[AST_CONFIG_MAX_PATH];
531 char tmpout[AST_CONFIG_MAX_PATH];
532 char *user, *pass, *rest, *trim, *tempcontext;
535 if (!change_password_realtime(vmu, newpassword))
539 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
540 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
541 configin = fopen(tmpin,"r");
543 configout = fopen(tmpout,"w+");
546 if (!configin || !configout) {
550 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
554 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
558 while (!feof(configin)) {
559 /* Read in the line */
560 fgets(inbuf, sizeof(inbuf), configin);
562 if (!feof(configin)) {
563 /* Make a backup of it */
564 memcpy(orig, inbuf, sizeof(orig));
565 /* Strip trailing \n and comment */
566 inbuf[strlen(inbuf) - 1] = '\0';
567 user = strchr(inbuf, ';');
573 /* check for '[' (opening of context name ) */
574 tempcontext = strchr(user, '[');
576 strncpy(currcontext, tempcontext +1, sizeof(currcontext) - 1);
577 /* now check for ']' */
578 tempcontext = strchr(currcontext, ']');
582 currcontext[0] = '\0';
584 pass = strchr(user, '=');
587 while (*trim && *trim < 33) {
597 while (*pass && *pass < 33)
601 rest = strchr(pass,',');
609 /* Compare user, pass AND context */
610 if (user && *user && !strcmp(user, vmu->mailbox) &&
611 pass && !strcmp(pass, vmu->password) &&
612 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
613 /* This is the line */
615 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
617 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
620 /* Put it back like it was */
621 fprintf(configout, orig);
628 stat((char *)tmpin, &statbuf);
629 chmod((char *)tmpout, statbuf.st_mode);
630 chown((char *)tmpout, statbuf.st_uid, statbuf.st_gid);
631 unlink((char *)tmpin);
632 rename((char *)tmpout,(char *)tmpin);
633 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
634 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
637 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
640 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
641 ast_safe_system(buf);
644 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
646 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
649 static int make_file(char *dest, int len, char *dir, int num)
651 return snprintf(dest, len, "%s/msg%04d", dir, num);
654 static int last_message_index(char *dir)
658 for (x=0;x<MAXMSG;x++) {
659 make_file(fn, sizeof(fn), dir, x);
660 if (ast_fileexists(fn, NULL, NULL) < 1)
667 inbuf(struct baseio *bio, FILE *fi)
674 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
689 inchar(struct baseio *bio, FILE *fi)
691 if (bio->iocp>=bio->iolen) {
696 return bio->iobuf[bio->iocp++];
700 ochar(struct baseio *bio, int c, FILE *so)
702 if (bio->linelength>=BASELINELEN) {
703 if (fputs(eol,so)==EOF)
709 if (putc(((unsigned char)c),so)==EOF)
717 static int base_encode(char *filename, FILE *so)
719 unsigned char dtable[BASEMAXINLINE];
724 memset(&bio, 0, sizeof(bio));
725 bio.iocp = BASEMAXINLINE;
727 if (!(fi = fopen(filename, "rb"))) {
728 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
736 dtable[26+i+9]= 'j'+i;
740 dtable[26+i+18]= 's'+i;
742 for (i= 0;i<10;i++) {
749 unsigned char igroup[3],ogroup[4];
752 igroup[0]= igroup[1]= igroup[2]= 0;
755 if ((c = inchar(&bio, fi)) == EOF) {
760 igroup[n]= (unsigned char)c;
764 ogroup[0]= dtable[igroup[0]>>2];
765 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
766 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
767 ogroup[3]= dtable[igroup[2]&0x3F];
777 ochar(&bio, ogroup[i], so);
781 if (fputs(eol,so)==EOF)
789 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize)
792 /* Prepare variables for substition in email body and subject */
793 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
794 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
795 snprintf(passdata, passdatasize, "%d", msgnum);
796 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
797 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
798 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
799 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
800 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
801 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
804 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail)
814 char tmp[80] = "/tmp/astmail-XXXXXX";
818 struct vm_zone *the_zone = NULL;
819 if (vmu && ast_strlen_zero(vmu->email)) {
820 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
823 if (!strcmp(format, "wav49"))
825 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
826 /* Make a temporary file instead of piping directly to sendmail, in case the mail
830 p = fdopen(pfd, "w");
837 gethostname(host, sizeof(host));
838 if (strchr(srcemail, '@'))
839 strncpy(who, srcemail, sizeof(who)-1);
841 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
843 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
846 /* Does this user have a timezone specified? */
847 if (!ast_strlen_zero(vmu->zonetag)) {
848 /* Find the zone in the list */
852 if (!strcmp(z->name, vmu->zonetag)) {
861 ast_localtime(&t,&tm,the_zone->timezone);
863 ast_localtime(&t,&tm,NULL);
864 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
865 fprintf(p, "Date: %s\n", date);
868 struct ast_channel *ast = ast_channel_alloc(0);
871 int vmlen = strlen(fromstring)*3 + 200;
872 if ((passdata = alloca(vmlen))) {
873 memset(passdata, 0, vmlen);
874 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
875 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
876 fprintf(p, "From: %s <%s>\n",passdata,who);
877 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
878 ast_channel_free(ast);
879 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
881 fprintf(p, "From: Asterisk PBX <%s>\n", who);
882 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
885 struct ast_channel *ast = ast_channel_alloc(0);
888 int vmlen = strlen(emailsubject)*3 + 200;
889 if ((passdata = alloca(vmlen))) {
890 memset(passdata, 0, vmlen);
891 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
892 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
893 fprintf(p, "Subject: %s\n",passdata);
894 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
895 ast_channel_free(ast);
896 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
899 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
902 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
904 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
905 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
906 fprintf(p, "MIME-Version: 1.0\n");
907 if (attach_user_voicemail) {
908 /* Something unique. */
909 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
911 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
913 fprintf(p, "--%s\n", bound);
915 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
916 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
918 struct ast_channel *ast = ast_channel_alloc(0);
921 int vmlen = strlen(emailbody)*3 + 200;
922 if ((passdata = alloca(vmlen))) {
923 memset(passdata, 0, vmlen);
924 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
925 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
926 fprintf(p, "%s\n",passdata);
927 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
928 ast_channel_free(ast);
929 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
931 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
933 "in mailbox %s from %s, on %s so you might\n"
934 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
935 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
937 if (attach_user_voicemail) {
938 fprintf(p, "--%s\n", bound);
939 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
940 fprintf(p, "Content-Transfer-Encoding: base64\n");
941 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
942 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
944 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
945 base_encode(fname, p);
946 fprintf(p, "\n\n--%s--\n.\n", bound);
949 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
950 ast_safe_system(tmp2);
951 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
953 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
959 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
967 char tmp[80] = "/tmp/astmail-XXXXXX";
971 struct vm_zone *the_zone = NULL;
975 p = fdopen(pfd, "w");
983 gethostname(host, sizeof(host));
984 if (strchr(srcemail, '@'))
985 strncpy(who, srcemail, sizeof(who)-1);
987 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
989 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
992 /* Does this user have a timezone specified? */
993 if (!ast_strlen_zero(vmu->zonetag)) {
994 /* Find the zone in the list */
998 if (!strcmp(z->name, vmu->zonetag)) {
1007 ast_localtime(&t,&tm,the_zone->timezone);
1009 ast_localtime(&t,&tm,NULL);
1011 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1012 fprintf(p, "Date: %s\n", date);
1014 if (*pagerfromstring) {
1015 struct ast_channel *ast = ast_channel_alloc(0);
1018 int vmlen = strlen(fromstring)*3 + 200;
1019 if ((passdata = alloca(vmlen))) {
1020 memset(passdata, 0, vmlen);
1021 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1022 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1023 fprintf(p, "From: %s <%s>\n",passdata,who);
1025 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1026 ast_channel_free(ast);
1027 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1029 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1030 fprintf(p, "To: %s\n", pager);
1031 fprintf(p, "Subject: New VM\n\n");
1032 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1033 fprintf(p, "New %s long msg in box %s\n"
1034 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1036 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1037 ast_safe_system(tmp2);
1038 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
1040 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1046 static int get_date(char *s, int len)
1051 localtime_r(&t,&tm);
1052 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1055 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1059 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1060 if (ast_fileexists(fn, NULL, NULL) > 0) {
1061 res = ast_streamfile(chan, fn, chan->language);
1064 res = ast_waitstream(chan, ecodes);
1068 res = ast_streamfile(chan, "vm-theperson", chan->language);
1071 res = ast_waitstream(chan, ecodes);
1074 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1079 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1081 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1084 res = ast_waitstream(chan, ecodes);
1088 static void free_user(struct ast_vm_user *vmu)
1094 static void free_zone(struct vm_zone *z)
1099 static char *mbox(int id)
1127 static int copy(char *infile, char *outfile)
1135 #ifdef HARDLINK_WHEN_POSSIBLE
1136 /* Hard link if possible; saves disk space & is faster */
1137 if (link(infile, outfile)) {
1139 if ((ifd = open(infile, O_RDONLY)) < 0) {
1140 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1143 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1144 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1149 len = read(ifd, buf, sizeof(buf));
1151 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1157 res = write(ofd, buf, len);
1159 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1169 #ifdef HARDLINK_WHEN_POSSIBLE
1171 /* Hard link succeeded */
1177 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
1179 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)
1181 char fromdir[256], todir[256], frompath[256], topath[256];
1182 char *frombox = mbox(imbox);
1185 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1187 make_dir(todir, sizeof(todir), recip->context, "", "");
1188 /* It's easier just to try to make it than to check for its existence */
1189 if (mkdir(todir, 0700) && (errno != EEXIST))
1190 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1191 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1192 /* It's easier just to try to make it than to check for its existence */
1193 if (mkdir(todir, 0700) && (errno != EEXIST))
1194 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1195 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1196 if (mkdir(todir, 0700) && (errno != EEXIST))
1197 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1199 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1200 make_file(frompath, sizeof(frompath), fromdir, msgnum);
1203 make_file(topath, sizeof(topath), todir, recipmsgnum);
1204 if (ast_fileexists(topath, NULL, chan->language) <= 0)
1207 } while (recipmsgnum < MAXMSG);
1208 if (recipmsgnum < MAXMSG) {
1209 char frompath2[256],topath2[256];
1210 ast_filecopy(frompath, topath, NULL);
1211 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1212 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1213 copy(frompath2, topath2);
1215 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1218 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
1221 static void run_externnotify(char *context, char *extension)
1223 char arguments[255];
1224 int newvoicemails = 0, oldvoicemails = 0;
1226 if (!ast_strlen_zero(externnotify)) {
1227 if (ast_app_messagecount(extension, &newvoicemails, &oldvoicemails)) {
1228 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
1230 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
1231 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
1232 ast_safe_system(arguments);
1238 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1252 char prefile[256]="";
1253 char ext_context[256] = "";
1256 char ecodes[16] = "#";
1257 char tmp[256] = "", *tmpptr;
1258 struct ast_vm_user *vmu;
1259 struct ast_vm_user svm;
1261 strncpy(tmp, ext, sizeof(tmp) - 1);
1263 context = strchr(tmp, '@');
1267 tmpptr = strchr(context, '&');
1269 tmpptr = strchr(ext, '&');
1277 if ((vmu = find_user(&svm, context, ext))) {
1278 /* Setup pre-file if appropriate */
1279 if (strcmp(vmu->context, "default"))
1280 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1282 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1284 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1286 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1287 make_dir(dir, sizeof(dir), vmu->context, "", "");
1288 /* It's easier just to try to make it than to check for its existence */
1289 if (mkdir(dir, 0700) && (errno != EEXIST))
1290 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1291 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1292 /* It's easier just to try to make it than to check for its existence */
1293 if (mkdir(dir, 0700) && (errno != EEXIST))
1294 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1295 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1296 if (mkdir(dir, 0700) && (errno != EEXIST))
1297 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1299 /* Check current or macro-calling context for special extensions */
1300 if (!ast_strlen_zero(vmu->exit)) {
1301 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
1302 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
1303 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
1304 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
1305 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
1306 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
1310 if (!ast_strlen_zero(vmu->exit)) {
1311 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
1312 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
1313 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
1314 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
1315 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
1316 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
1320 /* Play the beginning intro if desired */
1321 if (!ast_strlen_zero(prefile)) {
1322 if (ast_fileexists(prefile, NULL, NULL) > 0) {
1323 if (ast_streamfile(chan, prefile, chan->language) > -1)
1324 res = ast_waitstream(chan, ecodes);
1326 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1327 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1330 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1336 /* On a '#' we skip the instructions */
1340 if (!res && !silent) {
1341 res = ast_streamfile(chan, INTRO, chan->language);
1343 res = ast_waitstream(chan, ecodes);
1350 ast_stopstream(chan);
1351 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1352 other than the operator -- an automated attendant or mailbox login for example */
1354 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1355 if (!ast_strlen_zero(vmu->exit)) {
1356 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1357 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
1358 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1364 /* Check for a '0' here */
1367 if (vmu->operator) {
1368 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1369 if (!ast_strlen_zero(vmu->exit)) {
1370 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1371 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
1372 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1374 ast_play_and_wait(chan, "transfer");
1379 ast_play_and_wait(chan, "vm-sorry");
1387 /* The meat of recording the message... All the announcements and beeps have been played*/
1388 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1389 if (!ast_strlen_zero(fmt)) {
1392 make_file(fn, sizeof(fn), dir, msgnum);
1393 if (ast_fileexists(fn, NULL, chan->language) <= 0)
1396 } while (msgnum < MAXMSG);
1398 /* Unless we're *really* silent, try to send the beep */
1399 res = ast_streamfile(chan, "beep", chan->language);
1401 res = ast_waitstream(chan, "");
1403 if (msgnum < MAXMSG) {
1404 /* Store information */
1405 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1406 txt = fopen(txtfile, "w+");
1408 get_date(date, sizeof(date));
1411 "; Message Information file\n"
1429 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
1430 date, (long)time(NULL));
1433 ast_log(LOG_WARNING, "Error opening text file for output\n");
1434 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1439 fd = open(txtfile, O_APPEND | O_WRONLY);
1441 txt = fdopen(fd, "a");
1443 fprintf(txt, "duration=%d\n", duration);
1448 if (duration < vmminmessage) {
1449 if (option_verbose > 2)
1450 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
1452 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
1455 /* Are there to be more recipients of this message? */
1457 struct ast_vm_user recipu, *recip;
1458 char *exten, *context;
1460 exten = strsep(&tmpptr, "&");
1461 context = strchr(exten, '@');
1466 if ((recip = find_user(&recipu, context, exten))) {
1467 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
1471 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
1473 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1475 res = ast_waitstream(chan, "");
1476 ast_log(LOG_WARNING, "No more messages possible\n");
1479 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1483 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1484 /*Send the call to n+101 priority, where n is the current priority*/
1485 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num))
1486 chan->priority+=100;
1492 static int count_messages(char *dir)
1494 // Find all .txt files - even if they are not in sequence from 0000
1498 struct dirent *vment = NULL;
1500 if ((vmdir = opendir(dir))) {
1501 while ((vment = readdir(vmdir)))
1503 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
1514 static void resequence_mailbox(char * dir)
1516 // we know max messages, so stop process when number is hit
1524 for (x=0,dest=0;x<MAXMSG;x++) {
1525 make_file(sfn, sizeof(sfn), dir, x);
1526 if (ast_fileexists(sfn, NULL, NULL) > 0) {
1529 make_file(dfn, sizeof(dfn), dir, dest);
1530 ast_filerename(sfn,dfn,NULL);
1532 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1533 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1543 static int say_and_wait(struct ast_channel *chan, int num, char *language)
1546 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
1550 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1557 char *dbox = mbox(box);
1559 make_file(sfn, sizeof(sfn), dir, msg);
1560 make_dir(ddir, sizeof(ddir), context, username, dbox);
1562 for (x=0;x<MAXMSG;x++) {
1563 make_file(dfn, sizeof(dfn), ddir, x);
1564 if (ast_fileexists(dfn, NULL, NULL) < 0)
1569 ast_filecopy(sfn, dfn, NULL);
1570 if (strcmp(sfn, dfn)) {
1571 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1572 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1578 static int adsi_logo(unsigned char *buf)
1581 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1582 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1586 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1594 bytes += adsi_data_mode(buf + bytes);
1595 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1598 bytes += adsi_logo(buf);
1599 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1601 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
1603 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1604 bytes += adsi_data_mode(buf + bytes);
1605 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1607 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
1609 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1610 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1611 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1612 bytes += adsi_voice_mode(buf + bytes, 0);
1613 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1620 bytes += adsi_logo(buf);
1621 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1622 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
1623 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1624 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1627 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1628 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1629 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1630 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1631 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1632 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1633 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1636 /* Add another dot */
1638 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
1639 bytes += adsi_voice_mode(buf + bytes, 0);
1641 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1642 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1646 /* These buttons we load but don't use yet */
1647 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1648 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1649 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1650 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1651 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1652 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1653 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1656 /* Add another dot */
1658 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
1659 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1660 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1665 snprintf(num, sizeof(num), "%d", x);
1666 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1668 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1669 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1672 /* Add another dot */
1674 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
1675 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1676 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1679 if (adsi_end_download(chan)) {
1681 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1682 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1683 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1684 bytes += adsi_voice_mode(buf + bytes, 0);
1685 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1689 bytes += adsi_download_disconnect(buf + bytes);
1690 bytes += adsi_voice_mode(buf + bytes, 0);
1691 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1693 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1698 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
1699 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1701 ast_log(LOG_DEBUG, "Restarting session...\n");
1704 /* Load the session now */
1705 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
1707 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1709 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1711 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1715 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1718 if (!adsi_available(chan))
1720 x = adsi_load_session(chan, adsifdn, adsiver, 1);
1724 if (adsi_load_vmail(chan, useadsi)) {
1725 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1732 static void adsi_login(struct ast_channel *chan)
1736 unsigned char keys[8];
1738 if (!adsi_available(chan))
1743 /* Set one key for next */
1744 keys[3] = ADSI_KEY_APPS + 3;
1746 bytes += adsi_logo(buf + bytes);
1747 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1748 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1749 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1750 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1751 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1752 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1753 bytes += adsi_set_keys(buf + bytes, keys);
1754 bytes += adsi_voice_mode(buf + bytes, 0);
1755 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1758 static void adsi_password(struct ast_channel *chan)
1762 unsigned char keys[8];
1764 if (!adsi_available(chan))
1769 /* Set one key for next */
1770 keys[3] = ADSI_KEY_APPS + 3;
1772 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1773 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1774 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1775 bytes += adsi_set_keys(buf + bytes, keys);
1776 bytes += adsi_voice_mode(buf + bytes, 0);
1777 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1780 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1784 unsigned char keys[8];
1787 if (!adsi_available(chan))
1791 y = ADSI_KEY_APPS + 12 + start + x;
1792 if (y > ADSI_KEY_APPS + 12 + 4)
1794 keys[x] = ADSI_KEY_SKT | y;
1796 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1800 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1801 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1802 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1803 bytes += adsi_set_keys(buf + bytes, keys);
1804 bytes += adsi_voice_mode(buf + bytes, 0);
1806 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1809 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
1812 char buf[256], buf1[256], buf2[256];
1818 char datetime[21]="";
1821 unsigned char keys[8];
1825 if (!adsi_available(chan))
1828 /* Retrieve important info */
1829 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
1830 f = fopen(fn2, "r");
1833 fgets(buf, sizeof(buf), f);
1837 strsep(&stringp, "=");
1838 val = strsep(&stringp, "=");
1839 if (val && !ast_strlen_zero(val)) {
1840 if (!strcmp(buf, "callerid"))
1841 strncpy(cid, val, sizeof(cid) - 1);
1842 if (!strcmp(buf, "origdate"))
1843 strncpy(datetime, val, sizeof(datetime) - 1);
1849 /* New meaning for keys */
1851 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1856 /* No prev key, provide "Folder" instead */
1857 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1859 if (vms->curmsg >= vms->lastmsg) {
1860 /* If last message ... */
1862 /* but not only message, provide "Folder" instead */
1863 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1864 bytes += adsi_voice_mode(buf + bytes, 0);
1867 /* Otherwise if only message, leave blank */
1872 if (!ast_strlen_zero(cid)) {
1873 ast_callerid_parse(cid, &name, &num);
1877 name = "Unknown Caller";
1879 /* If deleted, show "undeleted" */
1881 if (vms->deleted[vms->curmsg])
1882 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1885 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1886 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
1887 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
1888 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
1890 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1891 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1892 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1893 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1894 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1895 bytes += adsi_set_keys(buf + bytes, keys);
1896 bytes += adsi_voice_mode(buf + bytes, 0);
1898 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1901 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
1905 unsigned char keys[8];
1909 if (!adsi_available(chan))
1912 /* New meaning for keys */
1914 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1920 /* No prev key, provide "Folder" instead */
1921 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1923 if (vms->curmsg >= vms->lastmsg) {
1924 /* If last message ... */
1926 /* but not only message, provide "Folder" instead */
1927 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1929 /* Otherwise if only message, leave blank */
1934 /* If deleted, show "undeleted" */
1935 if (vms->deleted[vms->curmsg])
1936 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1939 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1940 bytes += adsi_set_keys(buf + bytes, keys);
1941 bytes += adsi_voice_mode(buf + bytes, 0);
1943 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1946 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
1948 char buf[256] = "", buf1[256] = "", buf2[256] = "";
1950 unsigned char keys[8];
1953 char *newm = (vms->newmessages == 1) ? "message" : "messages";
1954 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
1955 if (!adsi_available(chan))
1957 if (vms->newmessages) {
1958 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
1959 if (vms->oldmessages) {
1960 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
1961 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
1963 snprintf(buf2, sizeof(buf2), "%s.", newm);
1965 } else if (vms->oldmessages) {
1966 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
1967 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1969 strncpy(buf1, "You have no messages.", sizeof(buf1) - 1);
1973 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1974 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1975 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1978 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1982 /* Don't let them listen if there are none */
1983 if (vms->lastmsg < 0)
1985 bytes += adsi_set_keys(buf + bytes, keys);
1987 bytes += adsi_voice_mode(buf + bytes, 0);
1989 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1992 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
1994 char buf[256] = "", buf1[256] = "", buf2[256] = "";
1996 unsigned char keys[8];
1999 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
2001 if (!adsi_available(chan))
2004 /* Original command keys */
2006 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2011 if ((vms->lastmsg + 1) < 1)
2014 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
2015 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
2017 if (vms->lastmsg + 1)
2018 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
2020 strncpy(buf2, "no messages.", sizeof(buf2) - 1);
2021 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2022 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2023 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2024 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2025 bytes += adsi_set_keys(buf + bytes, keys);
2027 bytes += adsi_voice_mode(buf + bytes, 0);
2029 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2034 static void adsi_clear(struct ast_channel *chan)
2038 if (!adsi_available(chan))
2040 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2041 bytes += adsi_voice_mode(buf + bytes, 0);
2043 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2047 static void adsi_goodbye(struct ast_channel *chan)
2052 if (!adsi_available(chan))
2054 bytes += adsi_logo(buf + bytes);
2055 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2056 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2057 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2058 bytes += adsi_voice_mode(buf + bytes, 0);
2060 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2063 /*--- get_folder: Folder menu ---*/
2064 /* Plays "press 1 for INBOX messages" etc
2065 Should possibly be internationalized
2067 static int get_folder(struct ast_channel *chan, int start)
2072 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
2075 for (x = start; x< 5; x++) { /* For all folders */
2076 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2078 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
2081 if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Spanish, French or Portuguese syntax */
2082 d = ast_play_and_wait(chan, "vm-messages"); /* "messages */
2085 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2086 d = ast_play_and_wait(chan, fn);
2089 } else { /* Default English */
2090 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2091 d = ast_play_and_wait(chan, fn);
2094 d = ast_play_and_wait(chan, "vm-messages"); /* "messages */
2098 d = ast_waitfordigit(chan, 500);
2102 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2105 d = ast_waitfordigit(chan, 4000);
2109 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2112 res = ast_play_and_wait(chan, fn); /* Folder name */
2113 while (((res < '0') || (res > '9')) &&
2114 (res != '#') && (res >= 0)) {
2115 res = get_folder(chan, 0);
2120 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2126 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2131 /* prepend a message to the current message and return */
2134 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2135 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
2145 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
2146 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2148 cmd = ast_play_and_wait(chan,"vm-starmain");
2149 /* "press star to return to the main menu" */
2151 cmd = ast_waitfordigit(chan,6000);
2163 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
2165 char todir[256], fn[256], ext_context[256], *stringp;
2167 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2168 make_file(fn, sizeof(fn), todir, msgnum);
2169 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
2171 /* Attach only the first format */
2172 fmt = ast_strdupa(fmt);
2175 strsep(&stringp, "|");
2177 if (!ast_strlen_zero(vmu->email)) {
2178 int attach_user_voicemail = attach_voicemail;
2179 char *myserveremail = serveremail;
2180 if (vmu->attach > -1)
2181 attach_user_voicemail = vmu->attach;
2182 if (!ast_strlen_zero(vmu->serveremail))
2183 myserveremail = vmu->serveremail;
2184 sendmail(myserveremail, vmu, msgnum, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
2187 if (!ast_strlen_zero(vmu->pager)) {
2188 char *myserveremail = serveremail;
2189 if (!ast_strlen_zero(vmu->serveremail))
2190 myserveremail = vmu->serveremail;
2191 sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, cidnum, cidname, duration, vmu);
2194 ast_log(LOG_ERROR, "Out of memory\n");
2201 /* Leave voicemail for someone */
2202 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL));
2203 run_externnotify(chan->context, ext_context);
2207 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt,int flag)
2214 struct ast_config *mif;
2218 char ext_context[256]="";
2219 int res = 0, cmd = 0;
2220 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2223 int saved_messages = 0, found = 0;
2224 int valid_extensions = 0;
2225 while (!res && !valid_extensions) {
2226 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
2229 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2231 /* start all over if no username */
2232 if (ast_strlen_zero(username))
2235 s = strsep(&stringp, "*");
2236 /* start optimistic */
2237 valid_extensions = 1;
2239 /* find_user is going to malloc since we have a NULL as first argument */
2240 if ((receiver = find_user(NULL, context, s))) {
2242 vmtmp = extensions = receiver;
2244 vmtmp->next = receiver;
2249 valid_extensions = 0;
2252 s = strsep(&stringp, "*");
2254 /* break from the loop of reading the extensions */
2255 if (valid_extensions)
2257 /* "I am sorry, that's not a valid extension. Please try again." */
2258 res = ast_play_and_wait(chan, "pbx-invalid");
2260 /* check if we're clear to proceed */
2261 if (!extensions || !valid_extensions)
2265 /* Send VoiceMail */
2266 cmd=leave_voicemail(chan,username,0,0,0);
2268 /* Forward VoiceMail */
2269 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2271 while (!res && vmtmp) {
2272 /* if (ast_play_and_wait(chan, "vm-savedto"))
2275 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2276 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2277 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2278 ast_log(LOG_DEBUG, sys);
2279 ast_safe_system(sys);
2281 todircount = count_messages(todir);
2282 strncpy(tmp, fmt, sizeof(tmp) - 1);
2284 while ((s = strsep(&stringp, "|"))) {
2285 /* XXX This is a hack -- we should use build_filename or similar XXX */
2286 if (!strcasecmp(s, "wav49"))
2288 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2289 ast_log(LOG_DEBUG, sys);
2290 ast_safe_system(sys);
2292 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2293 ast_log(LOG_DEBUG, sys);
2294 ast_safe_system(sys);
2295 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2297 /* load the information on the source message so we can send an e-mail like a new message */
2298 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2299 if ((mif=ast_load(miffile))) {
2301 /* set callerid and duration variables */
2302 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2303 s = ast_variable_retrieve(mif, NULL, "duration");
2308 if (!ast_strlen_zero(vmtmp->email)) {
2309 int attach_user_voicemail = attach_voicemail;
2310 char *myserveremail = serveremail;
2311 if (vmtmp->attach > -1)
2312 attach_user_voicemail = vmtmp->attach;
2313 if (!ast_strlen_zero(vmtmp->serveremail))
2314 myserveremail = vmtmp->serveremail;
2315 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, fn, tmp, duration, attach_user_voicemail);
2318 if (!ast_strlen_zero(vmtmp->pager)) {
2319 char *myserveremail = serveremail;
2320 if (!ast_strlen_zero(vmtmp->serveremail))
2321 myserveremail = vmtmp->serveremail;
2322 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, duration, vmtmp);
2325 ast_destroy(mif); /* or here */
2327 /* Leave voicemail for someone */
2328 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context, NULL));
2329 run_externnotify(chan->context, ext_context);
2333 vmtmp = vmtmp->next;
2336 if (saved_messages > 0) {
2337 /* give confirmation that the message was saved */
2338 /* commented out since we can't forward batches yet
2339 if (saved_messages == 1)
2340 res = ast_play_and_wait(chan, "vm-message");
2342 res = ast_play_and_wait(chan, "vm-messages");
2344 res = ast_play_and_wait(chan, "vm-saved"); */
2346 res = ast_play_and_wait(chan, "vm-msgsaved");
2350 return res ? res : cmd;
2353 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2356 if ((res = ast_streamfile(chan, file, chan->language)))
2357 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2359 res = ast_waitstream(chan, AST_DIGIT_ANY);
2363 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
2365 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
2368 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2371 struct vm_zone *the_zone = NULL;
2375 if (sscanf(origtime,"%ld",&tin) < 1) {
2376 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2381 /* Does this user have a timezone specified? */
2382 if (!ast_strlen_zero(vmu->zonetag)) {
2383 /* Find the zone in the list */
2387 if (!strcmp(z->name, vmu->zonetag)) {
2395 /* No internal variable parsing for now, so we'll comment it out for the time being */
2397 /* Set the DIFF_* variables */
2398 localtime_r(&t, &time_now);
2399 gettimeofday(&tv_now,NULL);
2400 tnow = tv_now.tv_sec;
2401 localtime_r(&tnow,&time_then);
2403 /* Day difference */
2404 if (time_now.tm_year == time_then.tm_year)
2405 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
2407 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2408 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2410 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2413 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2414 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
2415 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
2417 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2419 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2426 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2430 char *callerid, *name;
2431 char prefile[256]="";
2434 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2435 /* 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 */
2436 if ((cid == NULL)||(context == NULL))
2439 /* Strip off caller ID number from name */
2440 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2441 ast_callerid_parse(cid, &name, &callerid);
2442 if ((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
2443 /* Check for internal contexts and only */
2444 /* say extension when the call didn't come from an internal context in the list */
2445 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2446 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2447 if ((strcmp(cidinternalcontexts[i], context) == 0))
2450 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2452 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2453 if (!ast_strlen_zero(prefile)) {
2454 /* See if we can find a recorded name for this person instead of their extension number */
2455 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2456 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2458 res = wait_file2(chan, vms, "vm-from");
2459 res = ast_streamfile(chan, prefile, chan->language) > -1;
2460 res = ast_waitstream(chan, "");
2462 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2463 /* BB: Say "from extension" as one saying to sound smoother */
2465 res = wait_file2(chan, vms, "vm-from-extension");
2466 res = ast_say_digit_str(chan, callerid, "", chan->language);
2473 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2474 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2476 res = wait_file2(chan, vms, "vm-from-phonenumber");
2477 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2480 /* Number unknown */
2481 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
2483 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2484 res = wait_file2(chan, vms, "vm-unknown-caller");
2489 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
2494 /* Verify that we have a duration for the message */
2495 if((duration == NULL))
2498 /* Convert from seconds to minutes */
2499 durations=atoi(duration);
2500 durationm=(durations / 60);
2502 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
2504 if((!res)&&(durationm>=minduration)) {
2505 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, (char *) NULL);
2506 res = wait_file2(chan, vms, "vm-minutes");
2511 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2514 char filename[256],*origtime, *cid, *context, *duration;
2515 struct ast_config *msg_cfg;
2518 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
2519 adsi_message(chan, vms);
2521 res = wait_file2(chan, vms, "vm-first"); /* "First" */
2522 else if (vms->curmsg == vms->lastmsg)
2523 res = wait_file2(chan, vms, "vm-last"); /* "last" */
2525 res = wait_file2(chan, vms, "vm-message"); /* "message" */
2526 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
2528 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2532 /* Retrieve info from VM attribute file */
2533 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2534 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2535 msg_cfg = ast_load(filename);
2537 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2541 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2544 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2545 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
2547 context = ast_variable_retrieve(msg_cfg, "message", "context");
2548 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2549 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2551 if ((!res)&&(vmu->envelope))
2552 res = play_message_datetime(chan, vmu, origtime, filename);
2553 if ((!res)&&(vmu->saycid))
2554 res = play_message_callerid(chan, vms, cid, context, 0);
2555 if ((!res)&&(vmu->sayduration))
2556 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
2557 /* Allow pressing '1' to skip envelope / callerid */
2560 ast_destroy(msg_cfg);
2563 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
2564 vms->heard[vms->curmsg] = 1;
2565 res = wait_file(chan, vms, vms->fn);
2570 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2572 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2573 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2574 vms->lastmsg = count_messages(vms->curdir) - 1;
2577 The following test is needed in case sequencing gets messed up.
2578 There appears to be more than one way to mess up sequence, so
2579 we will not try to find all of the root causes--just fix it when
2583 if(vms->lastmsg != last_message_index(vms->curdir))
2585 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
2586 resequence_mailbox(vms->curdir);
2589 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2592 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2595 char ntxt[256] = "";
2597 if (vms->lastmsg > -1) {
2598 /* Get the deleted messages fixed */
2600 for (x=0;x < MAXMSG;x++) {
2601 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
2602 /* Save this message. It's not in INBOX or hasn't been heard */
2603 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2604 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2607 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2608 if (strcmp(vms->fn, vms->fn2)) {
2609 snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
2610 snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
2611 ast_filerename(vms->fn, vms->fn2, NULL);
2614 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
2615 /* Move to old folder before deleting */
2616 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
2619 for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
2620 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
2621 if (ast_fileexists(vms->fn, NULL, NULL) < 1)
2626 memset(vms->deleted, 0, sizeof(vms->deleted));
2627 memset(vms->heard, 0, sizeof(vms->heard));
2630 /* Default English syntax */
2631 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2633 /* Introduce messages they have */
2635 res = ast_play_and_wait(chan, "vm-youhave");
2637 if (vms->newmessages) {
2638 res = say_and_wait(chan, vms->newmessages, chan->language);
2640 res = ast_play_and_wait(chan, "vm-INBOX");
2641 if (vms->oldmessages && !res)
2642 res = ast_play_and_wait(chan, "vm-and");
2644 if ((vms->newmessages == 1))
2645 res = ast_play_and_wait(chan, "vm-message");
2647 res = ast_play_and_wait(chan, "vm-messages");
2651 if (!res && vms->oldmessages) {
2652 res = say_and_wait(chan, vms->oldmessages, chan->language);
2654 res = ast_play_and_wait(chan, "vm-Old");
2656 if (vms->oldmessages == 1)
2657 res = ast_play_and_wait(chan, "vm-message");
2659 res = ast_play_and_wait(chan, "vm-messages");
2663 if (!vms->oldmessages && !vms->newmessages) {
2664 res = ast_play_and_wait(chan, "vm-no");
2666 res = ast_play_and_wait(chan, "vm-messages");
2674 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
2676 /* Introduce messages they have */
2678 res = ast_play_and_wait(chan, "vm-youhave");
2680 if (vms->newmessages) {
2681 if ((vms->newmessages == 1))
2682 res = ast_play_and_wait(chan, "digits/1F");
2684 res = say_and_wait(chan, vms->newmessages, chan->language);
2686 res = ast_play_and_wait(chan, "vm-INBOX");
2687 if (vms->oldmessages && !res)
2688 res = ast_play_and_wait(chan, "vm-and");
2690 if ((vms->newmessages == 1))
2691 res = ast_play_and_wait(chan, "vm-message");
2693 res = ast_play_and_wait(chan, "vm-messages");
2697 if (!res && vms->oldmessages) {
2698 if (vms->oldmessages == 1)
2699 res = ast_play_and_wait(chan, "digits/1F");
2701 res = say_and_wait(chan, vms->oldmessages, chan->language);
2703 res = ast_play_and_wait(chan, "vm-Old");
2705 if (vms->oldmessages == 1)
2706 res = ast_play_and_wait(chan, "vm-message");
2708 res = ast_play_and_wait(chan, "vm-messages");
2712 if (!vms->oldmessages && !vms->newmessages) {
2713 res = ast_play_and_wait(chan, "vm-no");
2715 res = ast_play_and_wait(chan, "vm-messages");
2722 /* SPANISH syntax */
2723 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
2725 /* Introduce messages they have */
2727 if (!vms->oldmessages && !vms->newmessages) {
2728 res = ast_play_and_wait(chan, "vm-youhaveno");
2730 res = ast_play_and_wait(chan, "vm-messages");
2732 res = ast_play_and_wait(chan, "vm-youhave");
2735 if (vms->newmessages) {
2737 if ((vms->newmessages == 1)) {
2738 res = ast_play_and_wait(chan, "digits/1M");
2740 res = ast_play_and_wait(chan, "vm-message");
2742 res = ast_play_and_wait(chan, "vm-INBOXs");
2744 res = say_and_wait(chan, vms->newmessages, chan->language);
2746 res = ast_play_and_wait(chan, "vm-messages");
2748 res = ast_play_and_wait(chan, "vm-INBOX");
2751 if (vms->oldmessages && !res)
2752 res = ast_play_and_wait(chan, "vm-and");
2754 if (vms->oldmessages) {
2756 if (vms->oldmessages == 1) {
2757 res = ast_play_and_wait(chan, "digits/1M");
2759 res = ast_play_and_wait(chan, "vm-message");
2761 res = ast_play_and_wait(chan, "vm-Olds");
2763 res = say_and_wait(chan, vms->oldmessages, chan->language);
2765 res = ast_play_and_wait(chan, "vm-messages");
2767 res = ast_play_and_wait(chan, "vm-Old");
2776 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
2778 /* Introduce messages they have */
2780 res = ast_play_and_wait(chan, "vm-youhave");
2782 if (vms->newmessages) {
2783 res = say_and_wait(chan, vms->newmessages, chan->language);
2785 res = ast_play_and_wait(chan, "vm-INBOX");
2786 if (vms->oldmessages && !res)
2787 res = ast_play_and_wait(chan, "vm-and");
2789 if ((vms->newmessages == 1))
2790 res = ast_play_and_wait(chan, "vm-message");
2792 res = ast_play_and_wait(chan, "vm-messages");
2796 if (!res && vms->oldmessages) {
2797 res = say_and_wait(chan, vms->oldmessages, chan->language);
2799 if (vms->oldmessages == 1)
2800 res = ast_play_and_wait(chan, "vm-message");
2802 res = ast_play_and_wait(chan, "vm-messages");
2805 res = ast_play_and_wait(chan, "vm-Old");
2808 if (!vms->oldmessages && !vms->newmessages) {
2809 res = ast_play_and_wait(chan, "vm-no");
2811 res = ast_play_and_wait(chan, "vm-messages");
2819 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
2821 /* Introduce messages they have */
2823 res = ast_play_and_wait(chan, "vm-youhave");
2825 if (vms->newmessages) {
2826 res = say_and_wait(chan, vms->newmessages, chan->language);
2828 if (vms->oldmessages == 1)
2829 res = ast_play_and_wait(chan, "vm-INBOXs");
2831 res = ast_play_and_wait(chan, "vm-INBOX");
2833 if (vms->oldmessages && !res)
2834 res = ast_play_and_wait(chan, "vm-and");
2836 if ((vms->newmessages == 1))
2837 res = ast_play_and_wait(chan, "vm-message");
2839 res = ast_play_and_wait(chan, "vm-messages");
2843 if (!res && vms->oldmessages) {
2844 res = say_and_wait(chan, vms->oldmessages, chan->language);
2846 if (vms->oldmessages == 1)
2847 res = ast_play_and_wait(chan, "vm-Olds");
2849 res = ast_play_and_wait(chan, "vm-Old");
2852 if (vms->oldmessages == 1)
2853 res = ast_play_and_wait(chan, "vm-message");
2855 res = ast_play_and_wait(chan, "vm-messages");
2859 if (!vms->oldmessages && !vms->newmessages) {
2860 res = ast_play_and_wait(chan, "vm-no");
2862 res = ast_play_and_wait(chan, "vm-messages");
2869 /* PORTUGUESE syntax */
2870 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
2872 /* Introduce messages they have */
2874 res = ast_play_and_wait(chan, "vm-youhave");
2876 if (vms->newmessages) {
2877 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
2879 if ((vms->newmessages == 1)) {
2880 res = ast_play_and_wait(chan, "vm-message");
2882 res = ast_play_and_wait(chan, "vm-INBOXs");
2884 res = ast_play_and_wait(chan, "vm-messages");
2886 res = ast_play_and_wait(chan, "vm-INBOX");
2889 if (vms->oldmessages && !res)
2890 res = ast_play_and_wait(chan, "vm-and");
2892 if (!res && vms->oldmessages) {
2893 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
2895 if (vms->oldmessages == 1) {
2896 res = ast_play_and_wait(chan, "vm-message");
2898 res = ast_play_and_wait(chan, "vm-Olds");
2900 res = ast_play_and_wait(chan, "vm-messages");
2902 res = ast_play_and_wait(chan, "vm-Old");
2907 if (!vms->oldmessages && !vms->newmessages) {
2908 res = ast_play_and_wait(chan, "vm-no");
2910 res = ast_play_and_wait(chan, "vm-messages");
2919 /* in czech there must be declension of word new and message
2920 * czech : english : czech : english
2921 * --------------------------------------------------------
2922 * vm-youhave : you have
2923 * vm-novou : one new : vm-zpravu : message
2924 * vm-nove : 2-4 new : vm-zpravy : messages
2925 * vm-novych : 5-infinite new : vm-zprav : messages
2926 * vm-starou : one old
2927 * vm-stare : 2-4 old
2928 * vm-starych : 5-infinite old
2929 * jednu : one - falling 4.
2930 * vm-no : no ( no messages )
2933 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
2936 res = ast_play_and_wait(chan, "vm-youhave");
2938 if (vms->newmessages) {
2939 if (vms->newmessages == 1) {
2940 res = ast_play_and_wait(chan, "digits/jednu");
2942 res = say_and_wait(chan, vms->newmessages, chan->language);
2945 if ((vms->newmessages == 1))
2946 res = ast_play_and_wait(chan, "vm-novou");
2947 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
2948 res = ast_play_and_wait(chan, "vm-nove");
2949 if (vms->newmessages > 4)
2950 res = ast_play_and_wait(chan, "vm-novych");
2952 if (vms->oldmessages && !res)
2953 res = ast_play_and_wait(chan, "vm-and");
2955 if ((vms->newmessages == 1))
2956 res = ast_play_and_wait(chan, "vm-zpravu"