2 * Asterisk -- A telephony toolkit for Linux.
4 * Voicemail System (did you ever think it could be so easy?)
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/file.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/channel.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/options.h>
19 #include <asterisk/config.h>
20 #include <asterisk/say.h>
21 #include <asterisk/module.h>
22 #include <asterisk/adsi.h>
34 #include "../asterisk.h"
36 #define COMMAND_TIMEOUT 5000
38 #define VOICEMAIL_CONFIG "voicemail.conf"
39 #define ASTERISK_USERNAME "asterisk"
41 #define SENDMAIL "/usr/sbin/sendmail -t"
43 #define INTRO "vm-intro"
47 #define MAX_OTHER_FORMATS 10
49 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
52 static char *tdesc = "Comedian Mail (Voicemail System)";
54 static char *adapp = "CoMa";
56 static char *adsec = "_AST";
58 static char *addesc = "Comedian Mail";
62 static char *synopsis_vm =
63 "Leave a voicemail message";
65 static char *descrip_vm =
66 " VoiceMail([s|u|b]extension): Leaves voicemail for a given extension (must\n"
67 "be configured in voicemail.conf). If the extension is preceeded by an 's'"
68 "then instructions for leaving the message will be skipped. If the extension\n"
69 "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
70 "/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists. If the extension\n"
71 "is preceeded by a 'b' then the the busy message will be played (that is,\n"
72 "busy instead of unavail). At most one of 's', 'u', or 'b' may be specified.\n"
73 "Returns -1 on error or mailbox not found, or if the user hangs up. \n"
74 "Otherwise, it returns 0. \n";
76 static char *synopsis_vmain =
77 "Enter voicemail system";
79 static char *descrip_vmain =
80 " VoiceMailMain(): Enters the main voicemail system for the checking of voicemail. Returns\n"
81 " -1 if the user hangs up or 0 otherwise.\n";
84 static char *app = "VoiceMail";
86 /* Check mail, control, etc */
87 static char *app2 = "VoiceMailMain";
93 static int make_dir(char *dest, int len, char *ext, char *mailbox)
95 return snprintf(dest, len, "%s/%s/%s", VM_SPOOL_DIR, ext, mailbox);
98 static int make_file(char *dest, int len, char *dir, int num)
100 return snprintf(dest, len, "%s/msg%04d", dir, num);
105 static int announce_message(struct ast_channel *chan, char *dir, int msgcnt)
109 res = ast_streamfile(chan, "vm-message", chan->language);
111 res = ast_waitstream(chan, AST_DIGIT_ANY);
113 res = ast_say_number(chan, msgcnt+1, chan->language);
115 fn = get_fn(dir, msgcnt);
117 res = ast_streamfile(chan, fn, chan->language);
124 ast_log(LOG_WARNING, "Unable to announce message\n");
129 static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid)
137 p = popen(SENDMAIL, "w");
139 if (strchr(srcemail, '@'))
140 strncpy(who, srcemail, sizeof(who)-1);
142 gethostname(host, sizeof(host));
143 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
147 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", tm);
148 fprintf(p, "Date: %s\n", date);
149 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
150 fprintf(p, "From: Asterisk PBX <%s>\n", who);
151 fprintf(p, "To: %s <%s>\n", name, email);
152 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n\n", msgnum, mailbox);
153 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", tm);
154 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a message (number %d)\n"
155 "in mailbox %s from %s, on %s so you might\n"
156 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n", name,
157 msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
161 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
167 static int get_date(char *s, int len)
173 return strftime(s, len, "%a %b %e %r %Z %Y", tm);
176 static int invent_message(struct ast_channel *chan, char *ext, int busy)
179 res = ast_streamfile(chan, "vm-theperson", chan->language);
182 res = ast_waitstream(chan, "#");
185 res = ast_say_digit_str(chan, ext, "#", chan->language);
189 res = ast_streamfile(chan, "vm-isonphone", chan->language);
191 res = ast_streamfile(chan, "vm-isunavail", chan->language);
194 res = ast_waitstream(chan, "#");
198 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
200 struct ast_config *cfg;
201 char *copy, *name, *passwd, *email, *fmt, *fmts;
203 struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
204 char *sfmt[MAX_OTHER_FORMATS];
207 int res = -1, fmtcnt=0, x;
214 char prefile[256]="";
217 cfg = ast_load(VOICEMAIL_CONFIG);
219 ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG);
222 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
223 astemail = ASTERISK_USERNAME;
224 if ((copy = ast_variable_retrieve(cfg, NULL, ext))) {
225 /* Setup pre-file if appropriate */
227 snprintf(prefile, sizeof(prefile), "vm/%s/busy", ext);
229 snprintf(prefile, sizeof(prefile), "vm/%s/unavail", ext);
230 /* Make sure they have an entry in the config */
232 passwd = strtok(copy, ",");
233 name = strtok(NULL, ",");
234 email = strtok(NULL, ",");
235 make_dir(dir, sizeof(dir), ext, "");
236 /* It's easier just to try to make it than to check for its existence */
237 if (mkdir(dir, 0700) && (errno != EEXIST))
238 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
239 make_dir(dir, sizeof(dir), ext, "INBOX");
240 if (mkdir(dir, 0700) && (errno != EEXIST))
241 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
242 /* Play the beginning intro if desired */
243 if (strlen(prefile)) {
244 if (ast_fileexists(prefile, NULL, NULL) < 0) {
245 if (ast_streamfile(chan, prefile, chan->language) > -1)
246 silent = ast_waitstream(chan, "#");
248 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
249 silent = invent_message(chan, ext, busy);
252 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
257 /* If they hit "#" we should still play the beep sound */
259 if (!ast_streamfile(chan, "beep", chan->language) < 0)
261 ast_waitstream(chan, "");
263 /* Stream an info message */
264 if (silent || !ast_streamfile(chan, INTRO, chan->language)) {
265 /* Wait for the message to finish */
266 if (silent || !ast_waitstream(chan, "")) {
267 fmt = ast_variable_retrieve(cfg, "general", "format");
270 fmt = strtok(fmts, "|");
273 make_file(fn, sizeof(fn), dir, msgnum);
274 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
275 (chan->callerid ? chan->callerid : "Unknown"),
276 name, ext, chan->name);
277 if (ast_fileexists(fn, NULL, chan->language) > 0) {
281 writer = ast_writefile(fn, fmt, comment, O_EXCL, 1 /* check for other formats */, 0700);
285 } while(!writer && (msgnum < MAXMSG));
287 /* Store information */
288 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
289 txt = fopen(txtfile, "w+");
291 get_date(date, sizeof(date));
294 "# Message Information file\n"
308 chan->callerid ? chan->callerid : "Unknown",
312 ast_log(LOG_WARNING, "Error opening text file for output\n");
314 /* We need to reset these values */
316 fmt = ast_variable_retrieve(cfg, "general", "format");
319 while((fmt = strtok(NULL, "|"))) {
320 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
321 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
324 sfmt[fmtcnt++] = strdup(fmt);
326 for (x=0;x<fmtcnt;x++) {
327 others[x] = ast_writefile(fn, sfmt[x], comment, 0, 0, 0700);
329 /* Ick, the other format didn't work, but be sure not
330 to leak memory here */
332 for(y=x+1;y < fmtcnt;y++)
339 /* Loop forever, writing the packets we read to the writer(s), until
340 we read a # or get a hangup */
341 if (option_verbose > 2)
342 ast_verbose( VERBOSE_PREFIX_3 "Recording to %s\n", fn);
343 while((f = ast_read(chan))) {
344 if (f->frametype == AST_FRAME_VOICE) {
345 /* Write the primary format */
346 res = ast_writestream(writer, f);
348 ast_log(LOG_WARNING, "Error writing primary frame\n");
351 /* And each of the others */
352 for (x=0;x<fmtcnt;x++) {
353 res |= ast_writestream(others[x], f);
356 /* Exit on any error */
358 ast_log(LOG_WARNING, "Error writing frame\n");
362 if (f->frametype == AST_FRAME_DTMF) {
363 if (f->subclass == '#') {
364 if (option_verbose > 2)
365 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
372 if (option_verbose > 2)
373 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
378 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", fn, sfmt[x]);
381 ast_closestream(writer);
382 for (x=0;x<fmtcnt;x++) {
385 ast_closestream(others[x]);
389 /* Let them know it worked */
390 ast_streamfile(chan, "vm-msgsaved", chan->language);
391 ast_waitstream(chan, "");
393 /* Send e-mail if applicable */
395 sendmail(astemail, email, name, msgnum, ext, chan->callerid);
399 ast_log(LOG_WARNING, "Error writing to mailbox %s\n", ext);
401 ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext);
405 ast_log(LOG_WARNING, "No format to save messages in \n");
408 ast_log(LOG_WARNING, "Unable to playback instructions\n");
412 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
414 /* Leave voicemail for someone */
418 static char *mbox(int id)
446 static int count_messages(char *dir)
450 for (x=0;x<MAXMSG;x++) {
451 make_file(fn, sizeof(fn), dir, x);
452 if (ast_fileexists(fn, NULL, NULL) < 1)
458 static int play_and_wait(struct ast_channel *chan, char *fn)
461 d = ast_streamfile(chan, fn, chan->language);
464 d = ast_waitstream(chan, AST_DIGIT_ANY);
468 static int say_and_wait(struct ast_channel *chan, int num)
471 d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
475 static int copy(char *infile, char *outfile)
482 if ((ifd = open(infile, O_RDONLY)) < 0) {
483 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
486 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
487 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
492 len = read(ifd, buf, sizeof(buf));
494 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
500 res = write(ofd, buf, len);
502 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
514 static int save_to_folder(char *dir, int msg, char *username, int box)
521 char *dbox = mbox(box);
523 make_file(sfn, sizeof(sfn), dir, msg);
524 make_dir(ddir, sizeof(ddir), username, dbox);
526 for (x=0;x<MAXMSG;x++) {
527 make_file(dfn, sizeof(dfn), ddir, x);
528 if (ast_fileexists(dfn, NULL, NULL) < 0)
533 ast_filecopy(sfn, dfn, NULL);
534 if (strcmp(sfn, dfn)) {
535 snprintf(txt, sizeof(txt), "%s.txt", sfn);
536 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
542 static int adsi_logo(unsigned char *buf)
545 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
546 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
550 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
558 bytes += adsi_data_mode(buf + bytes);
559 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
562 bytes += adsi_logo(buf);
563 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
565 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
567 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
568 bytes += adsi_data_mode(buf + bytes);
569 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
571 if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
573 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
574 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
575 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
576 bytes += adsi_voice_mode(buf + bytes, 0);
577 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
584 bytes += adsi_logo(buf);
585 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
586 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
587 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
588 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
591 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
592 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
593 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
594 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "4", 1);
595 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
596 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
597 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
600 /* Add another dot */
602 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
603 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
604 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
608 /* These buttons we load but don't use yet */
609 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
610 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
611 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
612 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
613 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
614 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
615 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
618 /* Add another dot */
620 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
621 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
622 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
627 snprintf(num, sizeof(num), "%d", x);
628 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
630 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
631 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
634 /* Add another dot */
636 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
637 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
638 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
641 if (adsi_end_download(chan)) {
643 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
644 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
645 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
646 bytes += adsi_voice_mode(buf + bytes, 0);
647 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
651 bytes += adsi_download_disconnect(buf + bytes);
652 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
654 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
659 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
660 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
662 ast_log(LOG_DEBUG, "Restarting session...\n");
665 /* Load the session now */
666 if (adsi_load_session(chan, adapp, adver, 1) == 1) {
668 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
670 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
672 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
676 static void adsi_begin(struct ast_channel *chan, int *useadsi)
679 x = adsi_load_session(chan, adapp, adver, 1);
683 if (adsi_load_vmail(chan, useadsi)) {
684 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
691 static void adsi_login(struct ast_channel *chan)
695 unsigned char keys[6];
697 if (!adsi_available(chan))
702 /* Set one key for next */
703 keys[3] = ADSI_KEY_APPS + 3;
705 bytes += adsi_logo(buf + bytes);
706 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
707 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
708 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
709 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
710 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
711 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
712 bytes += adsi_set_keys(buf + bytes, keys);
713 bytes += adsi_voice_mode(buf + bytes, 0);
714 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
717 static void adsi_password(struct ast_channel *chan)
721 unsigned char keys[6];
723 if (!adsi_available(chan))
728 /* Set one key for next */
729 keys[3] = ADSI_KEY_APPS + 3;
731 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
732 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
733 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
734 bytes += adsi_set_keys(buf + bytes, keys);
735 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
738 static void adsi_folders(struct ast_channel *chan, int start, char *label)
742 unsigned char keys[6];
745 if (!adsi_available(chan))
749 y = ADSI_KEY_APPS + 12 + start + x;
750 if (y > ADSI_KEY_APPS + 12 + 4)
752 keys[x] = ADSI_KEY_SKT | y;
754 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
756 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
757 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
758 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
759 bytes += adsi_set_keys(buf + bytes, keys);
760 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
763 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
766 char buf[256], buf1[256], buf2[256];
771 char datetime[21]="";
774 unsigned char keys[6];
778 if (!adsi_available(chan))
781 /* Retrieve important info */
782 snprintf(fn2, sizeof(fn2), "%s.txt", fn);
786 fgets(buf, sizeof(buf), f);
789 val = strtok(NULL, "=");
790 if (val && strlen(val)) {
791 if (!strcmp(buf, "callerid"))
792 strncpy(cid, val, sizeof(cid) - 1);
793 if (!strcmp(buf, "origdate"))
794 strncpy(datetime, val, sizeof(datetime) - 1);
800 /* New meaning for keys */
802 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
805 /* No prev key, provide "Folder" instead */
806 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
809 /* If last message ... */
811 /* but not only message, provide "Folder" instead */
812 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
814 /* Otherwise if only message, leave blank */
820 ast_callerid_parse(cid, &name, &num);
824 name = "Unknown Caller";
826 /* If deleted, show "undeleted" */
828 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
831 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
832 snprintf(buf1, sizeof(buf1), "%s%s", folder,
833 strcasecmp(folder, "INBOX") ? " Messages" : "");
834 snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
836 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
837 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
838 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
839 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
840 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
841 bytes += adsi_set_keys(buf + bytes, keys);
842 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
845 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
849 unsigned char keys[6];
853 if (!adsi_available(chan))
856 /* New meaning for keys */
858 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
861 /* No prev key, provide "Folder" instead */
862 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
865 /* If last message ... */
867 /* but not only message, provide "Folder" instead */
868 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
870 /* Otherwise if only message, leave blank */
875 /* If deleted, show "undeleted" */
877 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
880 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
881 bytes += adsi_set_keys(buf + bytes, keys);
882 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
885 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
887 char buf[256], buf1[256], buf2[256];
889 unsigned char keys[6];
892 char *newm = (new == 1) ? "message" : "messages";
893 char *oldm = (old == 1) ? "message" : "messages";
894 if (!adsi_available(chan))
897 snprintf(buf1, sizeof(buf1), "You have %d new", new);
899 strcat(buf1, " and");
900 snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
902 snprintf(buf2, sizeof(buf2), "%s.", newm);
905 snprintf(buf1, sizeof(buf1), "You have %d old", old);
906 snprintf(buf2, sizeof(buf2), "%s.", oldm);
908 strcpy(buf1, "You have no messages.");
911 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
912 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
913 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
916 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
918 /* Don't let them listen if there are none */
921 bytes += adsi_set_keys(buf + bytes, keys);
923 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
926 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
928 char buf[256], buf1[256], buf2[256];
930 unsigned char keys[6];
933 char *mess = (messages == 1) ? "message" : "messages";
935 if (!adsi_available(chan))
938 /* Original command keys */
940 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
945 snprintf(buf1, sizeof(buf1), "%s%s has", folder,
946 strcasecmp(folder, "INBOX") ? " folder" : "");
949 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
951 strcpy(buf2, "no messages.");
952 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
953 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
954 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
955 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
956 bytes += adsi_set_keys(buf + bytes, keys);
958 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
962 static void adsi_clear(struct ast_channel *chan)
966 if (!adsi_available(chan))
968 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
969 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
972 static void adsi_goodbye(struct ast_channel *chan)
976 if (!adsi_available(chan))
978 bytes += adsi_logo(buf + bytes);
979 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
980 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
981 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
982 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
985 static int get_folder(struct ast_channel *chan, int start)
990 d = play_and_wait(chan, "vm-press");
993 for (x = start; x< 5; x++) {
994 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
996 d = play_and_wait(chan, "vm-for");
999 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1000 d = play_and_wait(chan, fn);
1003 d = play_and_wait(chan, "vm-messages");
1006 d = ast_waitfordigit(chan, 500);
1010 d = play_and_wait(chan, "vm-tocancel");
1013 d = ast_waitfordigit(chan, 4000);
1017 #define WAITCMD(a) do { \
1025 #define WAITFILE2(file) do { \
1026 if (ast_streamfile(chan, file, chan->language)) \
1027 ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
1028 d = ast_waitstream(chan, AST_DIGIT_ANY); \
1034 #define WAITFILE(file) do { \
1035 if (ast_streamfile(chan, file, chan->language)) \
1036 ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
1037 d = ast_waitstream(chan, AST_DIGIT_ANY); \
1040 goto instructions; \
1041 } else if (d < 0) { \
1046 #define PLAYMSG(a) do { \
1048 make_file(fn, sizeof(fn), curdir, a); \
1049 adsi_message(chan, curbox, a, lastmsg, deleted[a], fn); \
1051 WAITFILE2("vm-first"); \
1052 else if (a == lastmsg) \
1053 WAITFILE2("vm-last"); \
1054 WAITFILE2("vm-message"); \
1055 if (a && (a != lastmsg)) { \
1056 d = ast_say_number(chan, a + 1, AST_DIGIT_ANY, chan->language); \
1057 if (d < 0) goto out; \
1060 make_file(fn, sizeof(fn), curdir, a); \
1065 #define CLOSE_MAILBOX do { \
1066 if (lastmsg > -1) { \
1067 /* Get the deleted messages fixed */ \
1069 for (x=0;x<=lastmsg;x++) { \
1070 if (!deleted[x] && (strcasecmp(curbox, "INBOX") || !heard[x])) { \
1071 /* Save this message. It's not in INBOX or hasn't been heard */ \
1073 make_file(fn, sizeof(fn), curdir, x); \
1074 make_file(fn2, sizeof(fn2), curdir, curmsg); \
1075 if (strcmp(fn, fn2)) { \
1076 snprintf(txt, sizeof(txt), "%s.txt", fn); \
1077 snprintf(ntxt, sizeof(ntxt), "%s.txt", fn2); \
1078 ast_filerename(fn, fn2, NULL); \
1079 rename(txt, ntxt); \
1081 } else if (!strcasecmp(curbox, "INBOX") && heard[x] && !deleted[x]) { \
1082 /* Move to old folder before deleting */ \
1083 save_to_folder(curdir, x, username, 1); \
1086 for (x = curmsg + 1; x<=lastmsg; x++) { \
1087 make_file(fn, sizeof(fn), curdir, x); \
1088 snprintf(txt, sizeof(txt), "%s.txt", fn); \
1089 ast_filedelete(fn, NULL); \
1093 memset(deleted, 0, sizeof(deleted)); \
1094 memset(heard, 0, sizeof(heard)); \
1097 #define OPEN_MAILBOX(a) do { \
1098 strcpy(curbox, mbox(a)); \
1099 make_dir(curdir, sizeof(curdir), username, curbox); \
1100 lastmsg = count_messages(curdir) - 1; \
1101 snprintf(vmbox, sizeof(vmbox), "vm-%s", curbox); \
1105 static int vm_execmain(struct ast_channel *chan, void *data)
1107 /* XXX This is, admittedly, some pretty horrendus code. For some
1108 reason it just seemed a lot easier to do with GOTO's. I feel
1109 like I'm back in my GWBASIC days. XXX */
1113 struct localuser *u;
1115 char password[80], *copy;
1124 int deleted[MAXMSG] = { 0, };
1125 int heard[MAXMSG] = { 0, };
1134 struct ast_config *cfg;
1137 cfg = ast_load(VOICEMAIL_CONFIG);
1139 ast_log(LOG_WARNING, "No voicemail configuration\n");
1142 if (chan->state != AST_STATE_UP)
1145 /* If ADSI is supported, setup login screen */
1146 adsi_begin(chan, &useadsi);
1149 if (ast_streamfile(chan, "vm-login", chan->language)) {
1150 ast_log(LOG_WARNING, "Couldn't stream login file\n");
1154 /* Authenticate them and get their mailbox/password */
1157 /* Prompt for, and read in the username */
1158 if (ast_readstring(chan, username, sizeof(username), 2000, 10000, "#") < 0) {
1159 ast_log(LOG_WARNING, "Couldn't read username\n");
1162 if (!strlen(username)) {
1163 if (option_verbose > 2)
1164 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
1169 adsi_password(chan);
1170 if (ast_streamfile(chan, "vm-password", chan->language)) {
1171 ast_log(LOG_WARNING, "Unable to stream password file\n");
1174 if (ast_readstring(chan, password, sizeof(password), 2000, 10000, "#") < 0) {
1175 ast_log(LOG_WARNING, "Unable to read password\n");
1178 copy = ast_variable_retrieve(cfg, NULL, username);
1180 copy = strdup(copy);
1182 if (!strcmp(password,copy))
1184 else if (option_verbose > 2)
1185 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s'\n", password, username);
1187 } else if (option_verbose > 2)
1188 ast_verbose( VERBOSE_PREFIX_3 "No such user '%s' in config file\n", username);
1192 if (ast_streamfile(chan, "vm-incorrect", chan->language))
1195 if (ast_waitstream(chan, ""))
1203 oldmessages = lastmsg + 1;
1204 /* Start in INBOX */
1206 newmessages = lastmsg + 1;
1209 /* Select proper mailbox FIRST!! */
1210 if (!newmessages && oldmessages) {
1211 /* If we only have old messages start here */
1216 adsi_status(chan, newmessages, oldmessages, lastmsg);
1218 WAITCMD(play_and_wait(chan, "vm-youhave"));
1220 WAITCMD(say_and_wait(chan, newmessages));
1221 WAITCMD(play_and_wait(chan, "vm-INBOX"));
1223 WAITCMD(play_and_wait(chan, "vm-and"));
1225 if (newmessages == 1)
1226 WAITCMD(play_and_wait(chan, "vm-message"));
1228 WAITCMD(play_and_wait(chan, "vm-messages"));
1233 WAITCMD(say_and_wait(chan, oldmessages));
1234 WAITCMD(play_and_wait(chan, "vm-Old"));
1235 if (oldmessages == 1)
1236 WAITCMD(play_and_wait(chan, "vm-message"));
1238 WAITCMD(play_and_wait(chan, "vm-messages"));
1240 if (!oldmessages && !newmessages) {
1241 WAITCMD(play_and_wait(chan, "vm-no"));
1242 WAITCMD(play_and_wait(chan, "vm-messages"));
1249 WAITCMD(play_and_wait(chan, "vm-onefor"));
1250 WAITCMD(play_and_wait(chan, vmbox));
1251 WAITCMD(play_and_wait(chan, "vm-messages"));
1253 WAITCMD(play_and_wait(chan, "vm-opts"));
1256 WAITCMD(play_and_wait(chan, "vm-prev"));
1257 WAITCMD(play_and_wait(chan, "vm-repeat"));
1258 if (curmsg != lastmsg)
1259 WAITCMD(play_and_wait(chan, "vm-next"));
1260 if (!deleted[curmsg])
1261 WAITCMD(play_and_wait(chan, "vm-delete"));
1263 WAITCMD(play_and_wait(chan, "vm-undelete"));
1264 WAITCMD(play_and_wait(chan, "vm-toforward"));
1265 WAITCMD(play_and_wait(chan, "vm-savemessage"));
1267 WAITCMD(play_and_wait(chan, "vm-helpexit"));
1268 d = ast_waitfordigit(chan, 6000);
1274 play_and_wait(chan, "vm-goodbye");
1283 adsi_folders(chan, 0, "Change to folder...");
1284 box = play_and_wait(chan, "vm-changeto");
1287 while((box < '0') || (box > '9')) {
1288 box = get_folder(chan, 0);
1298 adsi_status2(chan, curbox, lastmsg + 1);
1299 WAITCMD(play_and_wait(chan, vmbox));
1300 WAITCMD(play_and_wait(chan, "vm-messages"));
1308 WAITCMD(play_and_wait(chan, "vm-nomore"));
1318 WAITCMD(play_and_wait(chan, "vm-youhave"));
1319 WAITCMD(play_and_wait(chan, "vm-no"));
1320 snprintf(fn, sizeof(fn), "vm-%s", curbox);
1321 WAITCMD(play_and_wait(chan, fn));
1322 WAITCMD(play_and_wait(chan, "vm-messages"));
1326 if (curmsg < lastmsg) {
1330 WAITCMD(play_and_wait(chan, "vm-nomore"));
1334 deleted[curmsg] = !deleted[curmsg];
1336 adsi_delete(chan, curmsg, lastmsg, deleted[curmsg]);
1337 if (deleted[curmsg])
1338 WAITCMD(play_and_wait(chan, "vm-deleted"));
1340 WAITCMD(play_and_wait(chan, "vm-undeleted"));
1344 adsi_folders(chan, 1, "Save to folder...");
1345 box = play_and_wait(chan, "vm-savefolder");
1348 while((box < '1') || (box > '9')) {
1349 box = get_folder(chan, 1);
1357 ast_log(LOG_DEBUG, "Save to folder: %s (%d)\n", mbox(box), box);
1358 if (save_to_folder(curdir, curmsg, username, box))
1361 make_file(fn, sizeof(fn), curdir, curmsg);
1363 adsi_message(chan, curbox, curmsg, lastmsg, deleted[curmsg], fn);
1364 WAITCMD(play_and_wait(chan, "vm-message"));
1365 WAITCMD(say_and_wait(chan, curmsg + 1) );
1366 WAITCMD(play_and_wait(chan, "vm-savedto"));
1367 snprintf(fn, sizeof(fn), "vm-%s", mbox(box));
1368 WAITCMD(play_and_wait(chan, fn));
1369 WAITCMD(play_and_wait(chan, "vm-messages"));
1373 WAITCMD(play_and_wait(chan, "vm-onefor"));
1374 WAITCMD(play_and_wait(chan, vmbox));
1375 WAITCMD(play_and_wait(chan, "vm-messages"));
1376 WAITCMD(play_and_wait(chan, "vm-opts"));
1380 ast_stopstream(chan);
1382 play_and_wait(chan, "vm-goodbye");
1393 ast_stopstream(chan);
1397 adsi_unload_session(chan);
1398 adsi_channel_init(chan);
1399 LOCAL_USER_REMOVE(u);
1403 static int vm_exec(struct ast_channel *chan, void *data)
1405 int res=0, silent=0, busy=0, unavail=0;
1406 struct localuser *u;
1407 char *ext = (char *)data;
1410 ast_log(LOG_WARNING, "vm requires an argument (extension)\n");
1417 } else if (*ext == 'b') {
1420 } else if (*ext == 'u') {
1424 if (chan->state != AST_STATE_UP)
1426 res = leave_voicemail(chan, ext, silent, busy, unavail);
1427 LOCAL_USER_REMOVE(u);
1431 int unload_module(void)
1434 STANDARD_HANGUP_LOCALUSERS;
1435 res = ast_unregister_application(app);
1436 res |= ast_unregister_application(app2);
1440 int load_module(void)
1443 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
1445 res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
1449 char *description(void)
1457 STANDARD_USECOUNT(res);
1463 return ASTERISK_GPL_KEY;