Version 0.1.9 from FTP
authorMark Spencer <markster@digium.com>
Sun, 5 Aug 2001 21:46:13 +0000 (21:46 +0000)
committerMark Spencer <markster@digium.com>
Sun, 5 Aug 2001 21:46:13 +0000 (21:46 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@349 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_directory.c
apps/app_voicemail.c
include/asterisk/app.h [new file with mode: 0755]

index 551f61f..bea7902 100755 (executable)
@@ -30,11 +30,12 @@ static char *app = "Directory";
 
 static char *synopsis = "Provide directory of voicemail extensions";
 static char *descrip =
-"  Directory(context): Presents the user with a directory of extensions from which\n"
-"  they may select by name.  The list of names and extensions is discovered from\n"
-"  voicemail.conf.  The context argument is required, and specifies the context\n"
-"  in which to interpret the extensions\n.  Returns 0 unless the user hangs up.  It\n"
-"  also sets up the channel on exit to enter the extension the user selected.\n";
+"  Directory(context): Presents the user with a directory of extensions from\n"
+"which they  may  select  by name. The  list  of  names  and  extensions  is\n"
+"discovered from  voicemail.conf. The  context  argument  is  required,  and\n"
+"specifies  the  context  in  which to interpret the extensions\n. Returns 0\n"
+"unless the user hangs up. It  also sets up the channel on exit to enter the\n"
+"extension the user selected.\n";
 
 /* For simplicity, I'm keeping the format compatible with the voicemail config,
    but i'm open to suggestions for isolating it */
@@ -129,7 +130,8 @@ static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *
        char fn[256];
        memset(ext, 0, sizeof(ext));
        ext[0] = digit;
-       res = ast_readstring(chan, ext + 1, NUMDIGITS, 3000, 3000, "#");
+       res = 0;
+       if (ast_readstring(chan, ext + 1, NUMDIGITS, 3000, 3000, "#") < 0) res = -1;
        if (!res) {
                /* Search for all names which start with those digits */
                v = ast_variable_browse(cfg, context);
index 356e101..5321466 100755 (executable)
 #define VOICEMAIL_CONFIG "voicemail.conf"
 #define ASTERISK_USERNAME "asterisk"
 
-/*
-#define HOSTNAME_OVERRIDE "linux-support.net"
-*/
-
 #define SENDMAIL "/usr/sbin/sendmail -t"
 
 #define INTRO "vm-intro"
@@ -58,10 +54,10 @@ static char *synopsis_vm =
 "Leave a voicemail message";
 
 static char *descrip_vm =
-"  VoiceMail([s]extension): Leaves voicemail for a given extension (must be configured in\n"
-"  voicemail.conf).  If the extension is preceeded by an 's' then instructions for leaving\n"
-"  the message will be skipped.  Returns -1 on error or mailbox not found, or if the user\n"
-"  hangs up.  Otherwise, it returns 0. \n";
+"  VoiceMail([s]extension): Leaves voicemail for a given  extension (must be\n"
+"configured in voicemail.conf). If the extension is preceeded by an 's' then\n"
+"instructions for leaving the message will be skipped. Returns  -1 on  error\n"
+"or mailbox not found, or if the user hangs up. Otherwise, it returns 0. \n";
 
 static char *synopsis_vmain =
 "Enter voicemail system";
@@ -80,19 +76,18 @@ STANDARD_LOCAL_USER;
 
 LOCAL_USER_DECL;
 
-static char *get_dir(char *ext, char *mailbox)
+static int make_dir(char *dest, int len, char *ext, char *mailbox)
 {
-       char *tmp = malloc(strlen(ext) + strlen(VM_SPOOL_DIR) + 3 + strlen(mailbox));
-       sprintf(tmp, "%s/%s/%s", VM_SPOOL_DIR, ext, mailbox);
-       return tmp;
+       return snprintf(dest, len, "%s/%s/%s", VM_SPOOL_DIR, ext, mailbox);
 }
-static char *get_fn(char *dir, int num)
+
+static int make_file(char *dest, int len, char *dir, int num)
 {
-       char *tmp = malloc(strlen(dir) + 10);
-       sprintf(tmp, "%s/msg%04d", dir, num);
-       return tmp;
+       return snprintf(dest, len, "%s/msg%04d", dir, num);
 }
 
+#if 0
+
 static int announce_message(struct ast_channel *chan, char *dir, int msgcnt)
 {
        char *fn;
@@ -115,34 +110,37 @@ static int announce_message(struct ast_channel *chan, char *dir, int msgcnt)
                ast_log(LOG_WARNING, "Unable to announce message\n");
        return res;
 }
-static int sendmail(char *email, char *name, int msgnum, char *mailbox)
+#endif
+
+static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid)
 {
        FILE *p;
        char date[256];
        char host[256];
+       char who[256];
        time_t t;
        struct tm *tm;
        p = popen(SENDMAIL, "w");
        if (p) {
-               gethostname(host, sizeof(host));
+               if (strchr(srcemail, '@'))
+                       strncpy(who, srcemail, sizeof(who));
+               else {
+                       gethostname(host, sizeof(host));
+                       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+               }
                time(&t);
                tm = localtime(&t);
                strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", tm);
                fprintf(p, "Date: %s\n", date);
                fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
-               fprintf(p, "From: Asterisk PBX <%s@%s>\n", ASTERISK_USERNAME,
-#ifdef HOSTNAME_OVERRIDE
-                               HOSTNAME_OVERRIDE
-#else
-                               host
-#endif
-                               );
+               fprintf(p, "From: Asterisk PBX <%s>\n", who);
                fprintf(p, "To: %s <%s>\n", name, email);
                fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n\n", msgnum, mailbox);
                strftime(date, sizeof(date), "%A, %B %d, %Y at %r", tm);
                fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a message (number %d)\n"
-                          "in mailbox %s, on %s so you might\n"
-                                  "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n", name, msgnum, mailbox, date);
+                          "in mailbox %s from %s, on %s so you might\n"
+                                  "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n", name, 
+                       msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
                fprintf(p, ".\n");
                pclose(p);
        } else {
@@ -164,7 +162,7 @@ static int get_date(char *s, int len)
 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 {
        struct ast_config *cfg;
-       char *copy, *name, *passwd, *email, *dir, *fmt, *fmts, *fn=NULL;
+       char *copy, *name, *passwd, *email, *fmt, *fmts;
        char comment[256];
        struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
        char *sfmt[MAX_OTHER_FORMATS];
@@ -175,23 +173,28 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
        int outmsg=0;
        struct ast_frame *f;
        char date[256];
+       char dir[256];
+       char fn[256];
+       char *astemail;
        
        cfg = ast_load(VOICEMAIL_CONFIG);
        if (!cfg) {
                ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG);
                return -1;
        }
+       if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
+               astemail = ASTERISK_USERNAME;
        if ((copy = ast_variable_retrieve(cfg, NULL, ext))) {
                /* Make sure they have an entry in the config */
                copy = strdup(copy);
                passwd = strtok(copy, ",");
                name = strtok(NULL, ",");
                email = strtok(NULL, ",");
-               dir = get_dir(ext, "");
+               make_dir(dir, sizeof(dir), ext, "");
                /* It's easier just to try to make it than to check for its existence */
                if (mkdir(dir, 0700) && (errno != EEXIST))
                        ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
-               dir = get_dir(ext, "INBOX");
+               make_dir(dir, sizeof(dir), ext, "INBOX");
                if (mkdir(dir, 0700) && (errno != EEXIST))
                        ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
                /* Stream an info message */
@@ -204,9 +207,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
                                        fmt = strtok(fmts, "|");
                                        msgnum = 0;
                                        do {
-                                               if (fn)
-                                                       free(fn);
-                                               fn = get_fn(dir, msgnum);
+                                               make_file(fn, sizeof(fn), dir, msgnum);
                                                snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
                                                                                        (chan->callerid ? chan->callerid : "Unknown"), 
                                                                                        name, ext, chan->name);
@@ -328,7 +329,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
                                                        }
                                                        /* Send e-mail if applicable */
                                                        if (email) 
-                                                               sendmail(email, name, msgnum, ext);
+                                                               sendmail(astemail, email, name, msgnum, ext, chan->callerid);
                                                }
                                        } else {
                                                if (msgnum < MAXMSG)
@@ -336,8 +337,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
                                                else
                                                        ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext);
                                        }
-                                       if (fn)
-                                               free(fn);
                                        free(fmts);
                                } else 
                                        ast_log(LOG_WARNING, "No format to save messages in \n");
@@ -345,7 +344,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
                } else
                        ast_log(LOG_WARNING, "Unable to playback instructions\n");
                        
-               free(dir);
                free(copy);
        } else
                ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
@@ -354,25 +352,276 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
        return res;
 }
 
+static char *mbox(int id)
+{
+       switch(id) {
+       case 0:
+               return "INBOX";
+       case 1:
+               return "Old";
+       case 2:
+               return "Work";
+       case 3:
+               return "Family";
+       case 4:
+               return "Friends";
+       case 5:
+               return "Cust1";
+       case 6:
+               return "Cust2";
+       case 7:
+               return "Cust3";
+       case 8:
+               return "Cust4";
+       case 9:
+               return "Cust5";
+       default:
+               return "Unknown";
+       }
+}
+
+static int count_messages(char *dir)
+{
+       int x;
+       char fn[256];
+       for (x=0;x<MAXMSG;x++) {
+               make_file(fn, sizeof(fn), dir, x);
+               if (ast_fileexists(fn, NULL, NULL) < 1)
+                       break;
+       }
+       return x;
+}
+
+static int play_and_wait(struct ast_channel *chan, char *fn)
+{
+       int d;
+       d = ast_streamfile(chan, fn, chan->language);
+       if (d)
+               return d;
+       d = ast_waitstream(chan, AST_DIGIT_ANY);
+       return d;
+}
+
+static int say_and_wait(struct ast_channel *chan, int num)
+{
+       int d;
+       d = ast_say_number(chan, num, chan->language);
+       return d;
+}
+
+static int copy(char *infile, char *outfile)
+{
+       int ifd;
+       int ofd;
+       int res;
+       int len;
+       char buf[4096];
+       if ((ifd = open(infile, O_RDONLY)) < 0) {
+               ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+               return -1;
+       }
+       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
+               ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+               close(ifd);
+               return -1;
+       }
+       do {
+               len = read(ifd, buf, sizeof(buf));
+               if (len < 0) {
+                       ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+                       close(ifd);
+                       close(ofd);
+                       unlink(outfile);
+               }
+               if (len) {
+                       res = write(ofd, buf, len);
+                       if (res != len) {
+                               ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+                               close(ifd);
+                               close(ofd);
+                               unlink(outfile);
+                       }
+               }
+       } while(len);
+       close(ifd);
+       close(ofd);
+       return 0;
+}
+
+static int save_to_folder(char *dir, int msg, char *username, int box)
+{
+       char sfn[256];
+       char dfn[256];
+       char ddir[256];
+       char txt[256];
+       char ntxt[256];
+       char *dbox = mbox(box);
+       int x;
+       make_file(sfn, sizeof(sfn), dir, msg);
+       make_dir(ddir, sizeof(ddir), username, dbox);
+       mkdir(ddir, 0700);
+       for (x=0;x<MAXMSG;x++) {
+               make_file(dfn, sizeof(dfn), ddir, x);
+               if (ast_fileexists(dfn, NULL, NULL) < 0)
+                       break;
+       }
+       if (x >= MAXMSG)
+               return -1;
+       ast_filecopy(sfn, dfn, NULL);
+       if (strcmp(sfn, dfn)) {
+               snprintf(txt, sizeof(txt), "%s.txt", sfn);
+               snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
+               copy(txt, ntxt);
+       }
+       return 0;
+}
+
+static int get_folder(struct ast_channel *chan, int start)
+{
+       int x;
+       int d;
+       char fn[256];
+       d = play_and_wait(chan, "vm-press");
+       if (d)
+               return d;
+       for (x = start; x< 5; x++) {
+               if ((d = ast_say_number(chan, x, chan->language)))
+                       return d;
+               d = play_and_wait(chan, "vm-for");
+               if (d)
+                       return d;
+               snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
+               d = play_and_wait(chan, fn);
+               if (d)
+                       return d;
+               d = play_and_wait(chan, "vm-messages");
+               if (d)
+                       return d;
+               d = ast_waitfordigit(chan, 500);
+               if (d)
+                       return d;
+       }
+       d = play_and_wait(chan, "vm-tocancel");
+       if (d)
+               return d;
+       d = ast_waitfordigit(chan, 4000);
+       return d;
+}
+
+#define WAITCMD(a) do { \
+       d = (a); \
+       if (d < 0) \
+               goto out; \
+       if (d) \
+               goto cmd; \
+} while(0)
+
+#define WAITFILE2(file) do { \
+       if (ast_streamfile(chan, file, chan->language)) \
+               ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
+       d = ast_waitstream(chan, AST_DIGIT_ANY); \
+       if (d < 0) { \
+               goto out; \
+       }\
+} while(0)
+
+#define WAITFILE(file) do { \
+       if (ast_streamfile(chan, file, chan->language)) \
+               ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
+       d = ast_waitstream(chan, AST_DIGIT_ANY); \
+       if (!d) { \
+               repeats = 0; \
+               goto instructions; \
+       } else if (d < 0) { \
+               goto out; \
+       } else goto cmd;\
+} while(0)
+
+#define PLAYMSG(a) do { \
+       starting = 0; \
+       if (!a) \
+               WAITFILE2("vm-first"); \
+       else if (a == lastmsg) \
+               WAITFILE2("vm-last"); \
+       WAITFILE2("vm-message"); \
+       if (a && (a != lastmsg)) { \
+               d = ast_say_number(chan, a + 1, chan->language); \
+               if (d < 0) goto out; \
+               if (d) goto cmd; \
+       } \
+       make_file(fn, sizeof(fn), curdir, a); \
+       heard[a] = 1; \
+       WAITFILE(fn); \
+} while(0)
+
+#define CLOSE_MAILBOX do { \
+       if (lastmsg > -1) { \
+               /* Get the deleted messages fixed */ \
+               curmsg = -1; \
+               for (x=0;x<=lastmsg;x++) { \
+                       if (!deleted[x] && (strcasecmp(curbox, "INBOX") || !heard[x])) { \
+                               /* Save this message.  It's not in INBOX or hasn't been heard */ \
+                               curmsg++; \
+                               make_file(fn, sizeof(fn), curdir, x); \
+                               make_file(fn2, sizeof(fn2), curdir, curmsg); \
+                               if (strcmp(fn, fn2)) { \
+                                       snprintf(txt, sizeof(txt), "%s.txt", fn); \
+                                       snprintf(ntxt, sizeof(ntxt), "%s.txt", fn2); \
+                                       ast_filerename(fn, fn2, NULL); \
+                                       rename(txt, ntxt); \
+                               } \
+                       } else if (!strcasecmp(curbox, "INBOX") && heard[x] && !deleted[x]) { \
+                               /* Move to old folder before deleting */ \
+                               save_to_folder(curdir, x, username, 1); \
+                       } \
+               } \
+               for (x = curmsg + 1; x<=lastmsg; x++) { \
+                       make_file(fn, sizeof(fn), curdir, x); \
+                       snprintf(txt, sizeof(txt), "%s.txt", fn); \
+                       ast_filedelete(fn, NULL); \
+                       unlink(txt); \
+               } \
+       } \
+       memset(deleted, 0, sizeof(deleted)); \
+       memset(heard, 0, sizeof(heard)); \
+} while(0)
+
+#define OPEN_MAILBOX(a) do { \
+       strcpy(curbox, mbox(a)); \
+       make_dir(curdir, sizeof(curdir), username, curbox); \
+       lastmsg = count_messages(curdir) - 1; \
+       snprintf(vmbox, sizeof(vmbox), "vm-%s", curbox); \
+} while (0)
+
 static int vm_execmain(struct ast_channel *chan, void *data)
 {
-       /* XXX This is, admittedly, some pretty horrendus code XXX */
+       /* XXX This is, admittedly, some pretty horrendus code.  For some
+          reason it just seemed a lot easier to do with GOTO's.  I feel
+          like I'm back in my GWBASIC days. XXX */
        int res=-1;
        int valid = 0;
-       int curmsg = 0;
-       int maxmsg = 0;
-       int x;
-       char *fn, *nfn;
        char d;
        struct localuser *u;
        char username[80];
        char password[80], *copy;
-       int deleted[MAXMSG];
+       char curbox[80];
+       char curdir[256];
+       char vmbox[256];
+       char fn[256];
+       char fn2[256];
+       int x;
        char ntxt[256];
        char txt[256];
+       int deleted[MAXMSG] = { 0, };
+       int heard[MAXMSG] = { 0, };
+       int newmessages;
+       int oldmessages;
+       int repeats = 0;
+       int curmsg = 0;
+       int lastmsg = 0;
+       int starting = 1;
+       int box;
        struct ast_config *cfg;
-       int state;
-       char *dir=NULL;
        
        LOCAL_USER_ADD(u);
        cfg = ast_load(VOICEMAIL_CONFIG);
@@ -386,9 +635,12 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                ast_log(LOG_WARNING, "Couldn't stream login file\n");
                goto out;
        }
+       
+       /* Authenticate them and get their mailbox/password */
+       
        do {
                /* Prompt for, and read in the username */
-               if (ast_readstring(chan, username, sizeof(username), 2000, 10000, "#")) {
+               if (ast_readstring(chan, username, sizeof(username), 2000, 10000, "#") < 0) {
                        ast_log(LOG_WARNING, "Couldn't read username\n");
                        goto out;
                }                       
@@ -402,7 +654,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                        ast_log(LOG_WARNING, "Unable to stream password file\n");
                        goto out;
                }
-               if (ast_readstring(chan, password, sizeof(password), 2000, 10000, "#")) {
+               if (ast_readstring(chan, password, sizeof(password), 2000, 10000, "#") < 0) {
                        ast_log(LOG_WARNING, "Unable to read password\n");
                        goto out;
                }
@@ -424,208 +676,175 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                break;
                }
        } while (!valid);
-       if (valid) {
-               dir = get_dir(username, "INBOX");
-               if (!dir) 
-                       goto out;
 
-               deleted[0] = 0;
-               /* Find out how many messages are there, mark all as
-                  not deleted. */
-               do {
-                       fn = get_fn(dir, maxmsg);
-                       if ((res = ast_fileexists(fn, NULL, chan->language))>0) {
-                               maxmsg++;
-                               deleted[maxmsg] = 0;
+       if (valid) {
+               OPEN_MAILBOX(1);
+               oldmessages = lastmsg + 1;
+               /* Start in INBOX */
+               OPEN_MAILBOX(0);
+               newmessages = lastmsg + 1;
+               
+               WAITCMD(play_and_wait(chan, "vm-youhave"));
+               if (newmessages) {
+                       WAITCMD(say_and_wait(chan, newmessages));
+                       WAITCMD(play_and_wait(chan, "vm-INBOX"));
+                       if (newmessages == 1)
+                               WAITCMD(play_and_wait(chan, "vm-message"));
+                       else
+                               WAITCMD(play_and_wait(chan, "vm-messages"));
+                               
+                       if (oldmessages)
+                               WAITCMD(play_and_wait(chan, "vm-and"));
+               }
+               if (oldmessages) {
+                       WAITCMD(say_and_wait(chan, oldmessages));
+                       WAITCMD(play_and_wait(chan, "vm-Old"));
+                       if (oldmessages == 1)
+                               WAITCMD(play_and_wait(chan, "vm-message"));
+                       else
+                               WAITCMD(play_and_wait(chan, "vm-messages"));
+               }
+               if (!oldmessages && !newmessages) {
+                       WAITCMD(play_and_wait(chan, "vm-no"));
+                       WAITCMD(play_and_wait(chan, "vm-messages"));
+               }
+               if (!newmessages && oldmessages) {
+                       /* If we only have old messages start here */
+                       OPEN_MAILBOX(1);
+               }
+               repeats = 0;
+               starting = 1;
+instructions:
+               if (starting) {
+                       if (lastmsg > -1) {
+                               WAITCMD(play_and_wait(chan, "vm-onefor"));
+                               WAITCMD(play_and_wait(chan, vmbox));
+                               WAITCMD(play_and_wait(chan, "vm-messages"));
                        }
-                       free(fn);
-               } while(res > 0);
-               if (ast_streamfile(chan, "vm-youhave", chan->language))
-                       goto out;
-               if ((d=ast_waitstream(chan, AST_DIGIT_ANY)) < 0)
+                       WAITCMD(play_and_wait(chan, "vm-opts"));
+               } else {
+                       if (curmsg)
+                               WAITCMD(play_and_wait(chan, "vm-prev"));
+                       WAITCMD(play_and_wait(chan, "vm-repeat"));
+                       if (curmsg != lastmsg)
+                               WAITCMD(play_and_wait(chan, "vm-next"));
+                       if (!deleted[curmsg])
+                               WAITCMD(play_and_wait(chan, "vm-delete"));
+                       else
+                               WAITCMD(play_and_wait(chan, "vm-undelete"));
+                       WAITCMD(play_and_wait(chan, "vm-toforward"));
+                       WAITCMD(play_and_wait(chan, "vm-savemessage"));
+               }
+               WAITCMD(play_and_wait(chan, "vm-helpexit"));
+               d = ast_waitfordigit(chan, 6000);
+               if (d < 0)
                        goto out;
-               ast_stopstream(chan);
                if (!d) {
-                       /* If they haven't interrupted us, play the message count */
-                       if (maxmsg > 0) {
-                               if ((d = ast_say_number(chan, maxmsg, chan->language)) < 0)
+                       repeats++;
+                       if (repeats > 2) {
+                               play_and_wait(chan, "vm-goodbye");
+                               goto out;
+                       }
+                       goto instructions;
+               }
+cmd:
+               switch(d) {
+               case '2':
+                       box = play_and_wait(chan, "vm-changeto");
+                       if (box < 0)
+                               goto out;
+                       while((box < '0') || (box > '9')) {
+                               box = get_folder(chan, 0);
+                               if (box < 0)
                                        goto out;
+                               if (box == '#')
+                                       goto instructions;
+                       } 
+                       box = box - '0';
+                       CLOSE_MAILBOX;
+                       OPEN_MAILBOX(box);
+                       WAITCMD(play_and_wait(chan, vmbox));
+                       WAITCMD(play_and_wait(chan, "vm-messages"));
+                       starting = 1;
+                       goto instructions;
+               case '4':
+                       if (curmsg) {
+                               curmsg--;
+                               PLAYMSG(curmsg);
                        } else {
-                               if (ast_streamfile(chan, "vm-no", chan->language))
-                                       goto out;
-                               if ((d=ast_waitstream(chan, AST_DIGIT_ANY)) < 0)
-                                       goto out;
-                               ast_stopstream(chan);
+                               WAITCMD(play_and_wait(chan, "vm-nomore"));
+                               goto instructions;
                        }
-                       if (!d) {
-                               /* And if they still haven't, give them the last word */
-                               if (ast_streamfile(chan, ((maxmsg == 1) ? "vm-message" : "vm-messages"), chan->language))
-                                       goto out;
-                               if (ast_waitstream(chan, AST_DIGIT_ANY) < 0)
-                                       goto out;
-                               ast_stopstream(chan);
+               case '1':
+                               curmsg = 0;
+                               /* Fall through */
+               case '5':
+                       if (lastmsg > -1) {
+                               PLAYMSG(curmsg);
+                       } else {
+                               WAITCMD(play_and_wait(chan, "vm-youhave"));
+                               WAITCMD(play_and_wait(chan, "vm-no"));
+                               snprintf(fn, sizeof(fn), "vm-%s", curbox);
+                               WAITCMD(play_and_wait(chan, fn));
+                               WAITCMD(play_and_wait(chan, "vm-messages"));
+                               goto instructions;
                        }
-               }
-               res = -1;
-                               
-#define STATE_STARTING 1
-#define STATE_MESSAGE 2
-#define STATE_MESSAGE_PLAYING 3
-               state = STATE_STARTING;
-               ast_log(LOG_EVENT, "User '%s' logged in on channel '%s' with %d message(s).\n", username, chan->name, maxmsg);
-               if (option_verbose > 2)
-                       ast_verbose( VERBOSE_PREFIX_3 "User '%s' logged in on channel %s with %d messages\n", username, chan->name, maxmsg);
-               if (!ast_streamfile(chan, "vm-instructions", chan->language)) {
-                       for(;;) {
-                               if (chan->stream) {
-                                       d = ast_waitstream(chan, AST_DIGIT_ANY);
-                                       ast_stopstream(chan);
-                                       if (!d && (state == STATE_MESSAGE_PLAYING)) {
-                                               state  = STATE_MESSAGE;
-                                               /* If it runs out playing a message, then give directions */
-                                               if (!(d = ast_streamfile(chan, "vm-msginstruct", chan->language)))
-                                                       d = ast_waitstream(chan, AST_DIGIT_ANY);
-                                               ast_stopstream(chan);
-                                       }
-                                       if (!d)
-                                               d = ast_waitfordigit(chan, COMMAND_TIMEOUT);
-                               } else
-                                       d = ast_waitfordigit(chan, COMMAND_TIMEOUT);
-                               if (d < 0)
+               case '6':
+                       if (curmsg < lastmsg) {
+                               curmsg++;
+                               PLAYMSG(curmsg);
+                       } else {
+                               WAITCMD(play_and_wait(chan, "vm-nomore"));
+                               goto instructions;
+                       }
+               case '7':
+                       deleted[curmsg] = !deleted[curmsg];
+                       if (deleted[curmsg]) 
+                               WAITCMD(play_and_wait(chan, "vm-deleted"));
+                       else
+                               WAITCMD(play_and_wait(chan, "vm-undeleted"));
+                       goto instructions;
+               case '9':
+                       box = play_and_wait(chan, "vm-savefolder");
+                       if (box < 0)
+                               goto out;
+                       while((box < '1') || (box > '9')) {
+                               box = get_folder(chan, 1);
+                               if (box < 0)
                                        goto out;
-restart:
-                               if (!d || (d == '*')) {
-                                       /* If they don't say anything, play back a message.  We'll decide which one is
-                                          best based up on where they are.  Ditto if they press the '*' key. */
-                                       switch(state) {
-                                       case STATE_STARTING:
-                                               if (ast_streamfile(chan, "vm-instructions", chan->language))
-                                                       goto out;
-                                               break;
-                                       case STATE_MESSAGE:
-                                       case STATE_MESSAGE_PLAYING:
-                                               if (ast_streamfile(chan, "vm-msginstruct", chan->language))
-                                                       goto out;
-                                               break;
-                                       default:
-                                               ast_log(LOG_WARNING, "What do I do when they timeout/* in state %d?\n", state);
-                                       }
-                               } else {
-                                       /* XXX Should we be command-compatible with Meridian mail?  Their system seems
-                                              very confusing, but also widely used XXX */
-                                       /* They've entered (or started to enter) a command */
-                                       switch(d) {
-                                       case '0':
-                                               if (curmsg < maxmsg) {
-                                                       deleted[curmsg] = !deleted[curmsg];
-                                                       if (deleted[curmsg]) {
-                                                               if (ast_streamfile(chan, "vm-deleted", chan->language))
-                                                                       goto out;
-                                                       } else {
-                                                               if (ast_streamfile(chan, "vm-undeleted", chan->language))
-                                                                       goto out;
-                                                       }
-                                               } else {
-                                                       if (ast_streamfile(chan, "vm-nomore", chan->language))
-                                                               goto out;
-                                               }
-                                               break;
-                                       case '1':
-                                               curmsg = 0;
-                                               if (maxmsg > 0) {
-                                                       /* Yuck */
-                                                       if ((d = announce_message(chan, dir, curmsg)) > 0)
-                                                               goto restart;
-                                                       else if (d < 0)
-                                                               goto out;
-                                               } else {
-                                                       if (ast_streamfile(chan, "vm-nomore", chan->language))
-                                                               goto out;
-                                               }
-                                               state = STATE_MESSAGE_PLAYING;
-                                               break;
-                                       case '4':
-                                               if (curmsg > 0)
-                                                       curmsg--;
-                                               /* Yuck */
-                                               if ((d = announce_message(chan, dir, curmsg)) > 0)
-                                                       goto restart;
-                                               else if (d < 0)
-                                                       goto out;
-                                               state = STATE_MESSAGE_PLAYING;
-                                               break;
-                                       case '5':
-                                               if ((d = announce_message(chan, dir, curmsg)) > 0)
-                                                       goto restart;
-                                               else if (d < 0)
-                                                       goto out;
-                                               state = STATE_MESSAGE_PLAYING;
-                                               break;
-                                       case '6':
-                                               if (curmsg < maxmsg - 1) {
-                                                       curmsg++;
-                                                       if ((d = announce_message(chan, dir, curmsg)) > 0)
-                                                               goto restart;
-                                                       else if (d < 0)
-                                                               goto out;
-                                               } else {
-                                                       if (ast_streamfile(chan, "vm-nomore", chan->language))
-                                                               goto out;
-                                               }
-                                               state = STATE_MESSAGE_PLAYING;
-                                               break;
-                                       /* XXX Message compose? It's easy!  Just read their # and, assuming it's in the config, 
-                                              call the routine as if it were called from the PBX proper XXX */
-                                       case '#':
-                                               if (ast_streamfile(chan, "vm-goodbye", chan->language))
-                                                       goto out;
-                                               if (ast_waitstream(chan, ""))
-                                                       goto out;
-                                               res = 0;
-                                               goto out;
-                                               break;
-                                       default:
-                                               /* Double yuck */
-                                               d = '*';
-                                               goto restart;
-                                       }
-                               }
+                               if (box == '#')
+                                       goto instructions;
+                       } 
+                       box = box - '0';
+                       ast_log(LOG_DEBUG, "Save to folder: %s (%d)\n", mbox(box), box);
+                       if (save_to_folder(curdir, curmsg, username, box))
+                               goto out;
+                       deleted[curmsg]=1;
+                       WAITCMD(play_and_wait(chan, "vm-message"));
+                       WAITCMD(say_and_wait(chan, curmsg + 1) );
+                       WAITCMD(play_and_wait(chan, "vm-savedto"));
+                       snprintf(fn, sizeof(fn), "vm-%s", mbox(box));
+                       WAITCMD(play_and_wait(chan, fn));
+                       WAITCMD(play_and_wait(chan, "vm-messages"));
+                       goto instructions;
+               case '*':
+                       if (!starting) {
+                               WAITCMD(play_and_wait(chan, "vm-onefor"));
+                               WAITCMD(play_and_wait(chan, vmbox));
+                               WAITCMD(play_and_wait(chan, "vm-messages"));
+                               WAITCMD(play_and_wait(chan, "vm-opts"));
                        }
+                       goto instructions;
+               case '#':
+                       play_and_wait(chan, "vm-goodbye");
+                       goto out;
+               default:
+                       goto instructions;
                }
        }
-       
 out:
+       CLOSE_MAILBOX;
        ast_stopstream(chan);
-       if (maxmsg) {
-               /* Get the deleted messages fixed */
-               curmsg = -1;
-               for (x=0;x<maxmsg;x++) {
-                       if (!deleted[x]) {
-                               curmsg++;
-                               fn = get_fn(dir, x);
-                               nfn = get_fn(dir, curmsg);
-                               if (strcmp(fn, nfn)) {
-                                       snprintf(txt, sizeof(txt), "%s.txt", fn);
-                                       snprintf(ntxt, sizeof(ntxt), "%s.txt", nfn);
-                                       ast_filerename(fn, nfn, NULL);
-                                       rename(txt, ntxt);
-                               }
-                               free(fn);
-                               free(nfn);
-                       }
-               }
-               for (x = curmsg + 1; x<maxmsg; x++) {
-                       fn = get_fn(dir, x);
-                       if (fn) {
-                               snprintf(txt, sizeof(txt), "%s.txt", fn);
-                               ast_filedelete(fn, NULL);
-                               unlink(txt);
-                               free(fn);
-                       }
-               }
-       }
-       if (dir)
-               free(dir);
        if (cfg)
                ast_destroy(cfg);
        LOCAL_USER_REMOVE(u);
@@ -647,6 +866,8 @@ static int vm_exec(struct ast_channel *chan, void *data)
                silent++;
                ext++;
        }
+       if (chan->state != AST_STATE_UP)
+               ast_answer(chan);
        res = leave_voicemail(chan, ext, silent);
        LOCAL_USER_REMOVE(u);
        return res;
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
new file mode 100755 (executable)
index 0000000..ecb1ab1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Application convenience functions, designed to give consistent
+ * look and feel to asterisk apps.
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#ifndef _ASTERISK_APP_H
+#define _ASTERISK_APP_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+extern int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif