app_voicemail: fix bugs, imap mm_status log change to debug
authorAlexei Gradinari <alex2grad@gmail.com>
Fri, 20 May 2016 18:56:30 +0000 (14:56 -0400)
committerMatthew Fredrickson <creslin@digium.com>
Thu, 26 May 2016 21:13:33 +0000 (16:13 -0500)
Fixed some bugs:
- create dirpath when save downloading message from IMAP storage.
- create IMAP folder if not exists when saving to IMAP storage
- check if file successfully opened before write to it
- some IMAP checks
- remove non-standard flag 'Unseen'
etc

Change to debug IMAP mm_status log instead of verbose.

Remove unused X-Asterisk-VM-Caller-channel message header
for security reason. The clients should not know name of peer/endpoint.

ASTERISK-26045 #close

Change-Id: I7f83d88b69b36934e2539c114b9fb612deed971b

apps/app_voicemail.c

index e7de8a2..1d5b2dc 100644 (file)
@@ -1088,7 +1088,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
 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, int *sound_duration, const char *unlockdir,
-                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id);
+                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
@@ -2174,6 +2174,7 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
 {
        struct vm_state *vms_p;
        char *file, *filename;
+       char dest[PATH_MAX];
        char *attachment;
        int i;
        BODY *body;
@@ -2237,6 +2238,7 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
                if (!strcmp(filename, file)) {
                        ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
                        vms_p->msgArray[vms_p->curmsg] = i + 1;
+                       create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
                        save_body(body, vms_p, "2", attachment, 0);
                        ret = 0;
                        break;
@@ -2369,7 +2371,8 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
        snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
 
        if (!(text_file_ptr = fopen(text_file, "w"))) {
-               ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+               ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+               goto exit;
        }
 
        fprintf(text_file_ptr, "%s\n", "[message]");
@@ -2709,8 +2712,8 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
        }
 
        make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
-               S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
-               S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
+               chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
+               chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
                fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
        /* read mail file to memory */
        len = ftell(p);
@@ -3018,6 +3021,10 @@ static int init_mailstream(struct vm_state *vms, int box)
        ast_mutex_lock(&vms->lock);
        ast_mutex_lock(&mail_open_lock);
        vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+       /* Create the folder if it dosn't exist */
+       if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
+               mail_create(vms->mailstream, tmp);
+       }
        ast_mutex_unlock(&mail_open_lock);
        ast_mutex_unlock(&vms->lock);
        if (vms->mailstream == NIL) {
@@ -3112,7 +3119,15 @@ static void write_file(char *filename, char *buffer, unsigned long len)
 {
        FILE *output;
 
-       output = fopen (filename, "w");
+       if (!filename || !buffer) {
+               return;
+       }
+
+       if (!(output = fopen(filename, "w"))) {
+               ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
+               return;
+       }
+
        if (fwrite(buffer, len, 1, output) != 1) {
                if (ferror(output)) {
                        ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
@@ -3247,18 +3262,30 @@ void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
 
 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
 {
-       ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
-       if (status->flags & SA_MESSAGES)
-               ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
-       if (status->flags & SA_RECENT)
-               ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
-       if (status->flags & SA_UNSEEN)
-               ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
-       if (status->flags & SA_UIDVALIDITY)
-               ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
-       if (status->flags & SA_UIDNEXT)
-               ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
-       ast_log(AST_LOG_NOTICE, "\n");
+       struct ast_str *str;
+       if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(MAX_OBJECT_FIELD))) {
+           return;
+       }
+
+       ast_str_append(&str, 0, " Mailbox %s", mailbox);
+       if (status->flags & SA_MESSAGES) {
+               ast_str_append(&str, 0, ", %lu messages", status->messages);
+       }
+       if (status->flags & SA_RECENT) {
+               ast_str_append(&str, 0, ", %lu recent", status->recent);
+       }
+       if (status->flags & SA_UNSEEN) {
+               ast_str_append(&str, 0, ", %lu unseen", status->unseen);
+       }
+       if (status->flags & SA_UIDVALIDITY) {
+               ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
+       }
+       if (status->flags & SA_UIDNEXT) {
+               ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
+       }
+       ast_log(LOG_DEBUG, "%s\n", ast_str_buffer(str));
+
+       ast_free(str);
 }
 
 
@@ -3446,8 +3473,9 @@ static struct vm_state *get_vm_state_by_imapuser(const char *user, int interacti
        if (interactive) {
                struct vm_state *vms;
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
-               vms = pthread_getspecific(ts_vmstate.key);
-               return vms;
+               if ((vms = pthread_getspecific(ts_vmstate.key)) && vms->imapuser && !strcmp(vms->imapuser, user)) {
+                       return vms;
+               }
        }
 
        AST_LIST_LOCK(&vmstates);
@@ -3485,8 +3513,10 @@ static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char
        if (interactive) {
                struct vm_state *vms;
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
-               vms = pthread_getspecific(ts_vmstate.key);
-               return vms;
+               if ((vms = pthread_getspecific(ts_vmstate.key)) && vms->username && vms->context &&
+                   !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
+                       return vms;
+               }
        }
 
        AST_LIST_LOCK(&vmstates);
@@ -5133,7 +5163,9 @@ static void make_email_file(FILE *p,
                first_line = 0;
        }
 
-       if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
+       if (msgnum <= -1) {
+               fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
+       } else if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
                char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
                struct ast_channel *ast;
                if ((ast = ast_dummy_channel_alloc())) {
@@ -5186,9 +5218,8 @@ static void make_email_file(FILE *p,
                fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
 #endif
                /* flag added for Urgent */
-               fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
+               fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
                fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
-               fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan ? ast_channel_name(chan) : "");
                fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
                fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
                fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
@@ -5219,7 +5250,11 @@ static void make_email_file(FILE *p,
                fprintf(p, "--%s" ENDL, bound);
        }
        fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
-       if (emailbody || vmu->emailbody) {
+       if (msgnum <= -1) {
+               fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
+                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
+                               greeting_attachment, date);
+       } else if (emailbody || vmu->emailbody) {
                char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
                struct ast_channel *ast;
                if ((ast = ast_dummy_channel_alloc())) {
@@ -5245,7 +5280,7 @@ static void make_email_file(FILE *p,
                } else {
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
                }
-       } else if (msgnum > -1) {
+       } else {
                if (strcmp(vmu->mailbox, mailbox)) {
                        /* Forwarded type */
                        struct ast_config *msg_cfg;
@@ -5290,9 +5325,6 @@ plain_message:
                                ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
                                (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
                }
-       } else {
-               fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
-                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
        }
 
        if (imap || attach_user_voicemail) {
@@ -6851,7 +6883,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                        res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
                        goto leave_vm_out;
                }
-               res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id);
+               res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id, 0);
 
                if (txt) {
                        fprintf(txt, "flag=%s\n", flag);
@@ -7055,6 +7087,7 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
        char sequence[10];
        char mailbox[256];
        int res;
+       int curr_mbox;
 
        /* get the real IMAP message number for this message */
        snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
@@ -7064,26 +7097,35 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
        /* if save to Old folder, put in INBOX as read */
        if (box == OLD_FOLDER) {
                mail_setflag(vms->mailstream, sequence, "\\Seen");
-               mail_clearflag(vms->mailstream, sequence, "\\Unseen");
        } else if (box == NEW_FOLDER) {
-               mail_setflag(vms->mailstream, sequence, "\\Unseen");
                mail_clearflag(vms->mailstream, sequence, "\\Seen");
        }
        if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
                ast_mutex_unlock(&vms->lock);
                return 0;
        }
-       /* Create the folder if it don't exist */
+
+       /* get the current mailbox so that we can point the mailstream back to it later */
+       curr_mbox = get_folder_by_name(vms->curbox);
+
+       /* Create the folder if it dosn't exist */
        imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
-       ast_debug(5, "Checking if folder exists: %s\n", mailbox);
-       if (mail_create(vms->mailstream, mailbox) == NIL)
-               ast_debug(5, "Folder exists.\n");
-       else
-               ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
-       if (move) {
-               res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
+       if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
+               if (mail_create(vms->mailstream, mailbox) != NIL) {
+                       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
+               }
+       }
+
+       /* restore previous mbox stream */
+       if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
+               ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+               res = -1;
        } else {
-               res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
+               if (move) {
+                       res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
+               } else {
+                       res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
+               }
        }
        ast_mutex_unlock(&vms->lock);
        return res;
@@ -7814,9 +7856,9 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                        }
                        make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
                        strncat(vms->introfn, "intro", sizeof(vms->introfn));
-                       ast_play_and_wait(chan, INTRO);
+                       ast_play_and_wait(chan, "vm-record-prepend");
                        ast_play_and_wait(chan, "beep");
-                       cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id);
+                       cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id, 1);
                        if (cmd == -1) {
                                break;
                        }
@@ -8085,6 +8127,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
        AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
        char *stringp;
        const char *s;
+       const char mailbox_context[256];
        int saved_messages = 0;
        int valid_extensions = 0;
        char *dir;
@@ -8193,20 +8236,44 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                /* start optimistic */
                valid_extensions = 1;
                while (s) {
+                       snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
                        if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
                                int oldmsgs;
                                int newmsgs;
                                int capacity;
-                               if (inboxcount(s, &newmsgs, &oldmsgs)) {
-                                       ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
+
+                               if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
+                                       ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
                                        /* Shouldn't happen, but allow trying another extension if it does */
                                        res = ast_play_and_wait(chan, "pbx-invalid");
                                        valid_extensions = 0;
                                        break;
                                }
+#ifdef IMAP_STORAGE
+                               if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
+                                       if (!(dstvms = create_vm_state_from_user(receiver))) {
+                                               ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
+                                               /* Shouldn't happen, but allow trying another extension if it does */
+                                               res = ast_play_and_wait(chan, "pbx-invalid");
+                                               valid_extensions = 0;
+                                               break;
+                                       }
+                               }
+                               check_quota(dstvms, imapfolder);
+                               if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
+                                       ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
+                                       res = ast_play_and_wait(chan, "vm-mailboxfull");
+                                       valid_extensions = 0;
+                                       while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
+                                               inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
+                                               free_user(vmtmp);
+                                       }
+                                       break;
+                               }
+#endif
                                capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
                                if ((newmsgs + oldmsgs) >= capacity) {
-                                       ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
+                                       ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
                                        res = ast_play_and_wait(chan, "vm-mailboxfull");
                                        valid_extensions = 0;
                                        while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
@@ -8226,7 +8293,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
                                        free_user(receiver);
                                }
-                               ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
+                               ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
                                /* "I am sorry, that's not a valid extension.  Please try again." */
                                res = ast_play_and_wait(chan, "pbx-invalid");
                                valid_extensions = 0;
@@ -8306,7 +8373,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                        if (!dstvms->mailstream) {
                                                ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
                                        } else {
-                                               copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
+                                               copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
                                                run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
                                        }
                                } else {
@@ -8342,10 +8409,6 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                        res = ast_play_and_wait(chan, "vm-messages");
                                if (!res)
                                        res = ast_play_and_wait(chan, "vm-saved"); */
-#ifdef IMAP_STORAGE
-                               /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
-                               if (ast_strlen_zero(vmstmp.introfn))
-#endif
                                res = ast_play_and_wait(chan, "vm-msgforwarded");
                        }
 #ifndef IMAP_STORAGE
@@ -8814,6 +8877,7 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
        char arg[10];
        int i;
        BODY* body;
+       int curr_mbox;
 
        file = strrchr(ast_strdupa(dir), '/');
        if (file) {
@@ -8824,6 +8888,16 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
        }
 
        ast_mutex_lock(&vms->lock);
+
+       /* get the current mailbox so that we can point the mailstream back to it later */
+       curr_mbox = get_folder_by_name(vms->curbox);
+
+       if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
+               ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+               ast_mutex_unlock(&vms->lock);
+               return -1;
+       }
+
        for (i = 0; i < vms->mailstream->nmsgs; i++) {
                mail_fetchstructure(vms->mailstream, i + 1, &body);
                /* We have the body, now we extract the file name of the first attachment. */
@@ -8841,6 +8915,14 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
                }
        }
        mail_expunge(vms->mailstream);
+
+       if (curr_mbox != -1) {
+               /* restore previous mbox stream */
+               if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
+                       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+               }
+       }
+
        ast_mutex_unlock(&vms->lock);
        return 0;
 }
@@ -10348,7 +10430,7 @@ static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct
        if (ast_test_flag(vmu, VM_FORCENAME)) {
                snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
                if (ast_fileexists(prefile, NULL, NULL) < 1) {
-                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd < 0 || cmd == 't' || cmd == '#')
                                return cmd;
                }
@@ -10358,14 +10440,14 @@ static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct
        if (ast_test_flag(vmu, VM_FORCEGREET)) {
                snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
                if (ast_fileexists(prefile, NULL, NULL) < 1) {
-                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd < 0 || cmd == 't' || cmd == '#')
                                return cmd;
                }
 
                snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
                if (ast_fileexists(prefile, NULL, NULL) < 1) {
-                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd < 0 || cmd == 't' || cmd == '#')
                                return cmd;
                }
@@ -10447,15 +10529,15 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct
                switch (cmd) {
                case '1': /* Record your unavailable message */
                        snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
-                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        break;
                case '2':  /* Record your busy message */
                        snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
-                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        break;
                case '3': /* Record greeting */
                        snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
-                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        break;
                case '4':  /* manage the temporary greeting */
                        cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
@@ -10589,7 +10671,7 @@ static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, st
                        retries = 0;
                RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
                if (ast_fileexists(prefile, NULL, NULL) <= 0) {
-                       cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd == -1) {
                                break;
                        }
@@ -10597,7 +10679,7 @@ static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, st
                } else {
                        switch (cmd) {
                        case '1':
-                               cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                               cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                                break;
                        case '2':
                                DELETE(prefile, -1, prefile, vmu);
@@ -11876,7 +11958,13 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
                        cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
                        if (useadsi)
                                adsi_status(chan, &vms);
-                       break;
+                       /* Reopen play_folder */
+                       res = open_mailbox(&vms, vmu, play_folder);
+                       if (res < 0) {
+                               goto out;
+                       }
+                       vms.starting = 1;
+                       break;
                default:        /* Nothing */
                        ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
                        cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
@@ -15109,7 +15197,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
 
 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, int *sound_duration, const char *unlockdir,
-                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id)
+                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
 {
        /* Record message & let caller review or re-record it, or set options if applicable */
        int res = 0;
@@ -15151,7 +15239,9 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                                ast_verb(3, "Saving message as is\n");
                                if (!outsidecaller) 
                                        ast_filerename(tempfile, recordfile, NULL);
-                               ast_stream_and_wait(chan, "vm-msgsaved", "");
+                               if (!forwardintro) {
+                                       ast_stream_and_wait(chan, "vm-msgsaved", "");
+                               }
                                if (!outsidecaller) {
                                        /* Saves to IMAP server only if imapgreeting=yes */
                                        STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
@@ -15174,7 +15264,11 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                                ast_verb(3, "Recording the message\n");
                        
                        if (recorded && outsidecaller) {
-                               cmd = ast_play_and_wait(chan, INTRO);
+                               if (forwardintro) {
+                                       cmd = ast_play_and_wait(chan, "vm-record-prepend");
+                               } else {
+                                       cmd = ast_play_and_wait(chan, INTRO);
+                               }
                                cmd = ast_play_and_wait(chan, "beep");
                        }
                        recorded = 1;
@@ -15295,10 +15389,10 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                        }
                        return cmd;
                default:
-                       /* If the caller is an ouside caller, and the review option is enabled,
+                       /* If the caller is an ouside caller and the review option is enabled or it's forward intro
                           allow them to review the message, but let the owner of the box review
                           their OGM's */
-                       if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
+                       if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro)
                                return cmd;
                        if (msg_exists) {
                                cmd = ast_play_and_wait(chan, "vm-review");