diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index a7b43a88d7ec4c16979bef79b7c9b3bbd57f0509..ced94c639363fa01094a3fd9b43ddc7d2edb8a96 100755 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -109,6 +109,23 @@ struct vm_zone { struct vm_zone *next; }; +struct vm_state { + char curbox[80]; + char username[80]; + char curdir[256]; + char vmbox[256]; + char fn[256]; + char fn2[256]; + int deleted[MAXMSG]; + int heard[MAXMSG]; + int curmsg; + int lastmsg; + int newmessages; + int oldmessages; + int starting; + int repeats; +}; + static char *tdesc = "Comedian Mail (Voicemail System)"; static char *adapp = "CoMa"; @@ -874,6 +891,214 @@ static int play_and_wait(struct ast_channel *chan, char *fn) return d; } +static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt) +{ + char d, *fmts; + char comment[256]; + int x, fmtcnt=1, res=-1,outmsg=0; + struct ast_frame *f; + struct ast_filestream *others[MAX_OTHER_FORMATS]; + struct ast_filestream *realfiles[MAX_OTHER_FORMATS]; + char *sfmt[MAX_OTHER_FORMATS]; + char *stringp=NULL; + time_t start, end; + struct ast_dsp *sildet; /* silence detector dsp */ + int totalsilence = 0; + int dspsilence = 0; + int gotsilence = 0; /* did we timeout for silence? */ + int rfmt=0; + char prependfile[80]; + + ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt); + snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name); + + if (playfile) { + d = play_and_wait(chan, playfile); + if (d > -1) + d = ast_streamfile(chan, "beep",chan->language); + if (!d) + d = ast_waitstream(chan,""); + if (d < 0) + return -1; + } + strncpy(prependfile, recordfile, sizeof(prependfile) -1); + strcat(prependfile, "-prepend"); + + fmts = ast_strdupa(fmt); + + stringp=fmts; + strsep(&stringp, "|"); + ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts); + sfmt[0] = ast_strdupa(fmts); + + while((fmt = strsep(&stringp, "|"))) { + if (fmtcnt > MAX_OTHER_FORMATS - 1) { + ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n"); + break; + } + sfmt[fmtcnt++] = ast_strdupa(fmt); + } + + if (maxtime) + time(&start); + for (x=0;x<fmtcnt;x++) { + others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700); + ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]); + if (!others[x]) { + break; + } + } + + sildet = ast_dsp_new(); //Create the silence detector + if (!sildet) { + ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); + return -1; + } + ast_dsp_set_threshold(sildet, silencethreshold); + + if (maxsilence > 0) { + rfmt = chan->readformat; + res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); + return -1; + } + } + + if (x == fmtcnt) { + /* Loop forever, writing the packets we read to the writer(s), until + we read a # or get a hangup */ + f = NULL; + for(;;) { + res = ast_waitfor(chan, 2000); + if (!res) { + ast_log(LOG_DEBUG, "One waitfor failed, trying another\n"); + /* Try one more time in case of masq */ + res = ast_waitfor(chan, 2000); + if (!res) { + ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name); + res = -1; + } + } + + if (res < 0) { + f = NULL; + break; + } + f = ast_read(chan); + if (!f) + break; + if (f->frametype == AST_FRAME_VOICE) { + /* write each format */ + for (x=0;x<fmtcnt;x++) { + if (!others[x]) + break; + res = ast_writestream(others[x], f); + } + + /* Silence Detection */ + if (maxsilence > 0) { + dspsilence = 0; + ast_dsp_silence(sildet, f, &dspsilence); + if (dspsilence) + totalsilence = dspsilence; + else + totalsilence = 0; + + if (totalsilence > maxsilence) { + /* Ended happily with silence */ + ast_frfree(f); + gotsilence = 1; + outmsg=2; + break; + } + } + /* Exit on any error */ + if (res) { + ast_log(LOG_WARNING, "Error writing frame\n"); + ast_frfree(f); + break; + } + } else if (f->frametype == AST_FRAME_VIDEO) { + /* Write only once */ + ast_writestream(others[0], f); + } else if (f->frametype == AST_FRAME_DTMF) { + /* stop recording with any digit */ + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass); + res = f->subclass; + outmsg = 2; + ast_frfree(f); + break; + } + if (maxtime) { + time(&end); + if (maxtime < (end - start)) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n"); + res = 't'; + ast_frfree(f); + break; + } + } + ast_frfree(f); + } + if (!f) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "User hung up\n"); + res = -1; + outmsg=1; + /* delete all the prepend files */ + for (x=0;x<fmtcnt;x++) { + if (!others[x]) + break; + ast_closestream(others[x]); + ast_filedelete(prependfile, sfmt[x]); + } + } + } else { + ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); + } + if (outmsg > 1) { + struct ast_frame *fr; + for (x=0;x<fmtcnt;x++) { + snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]); + realfiles[x] = ast_writefile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0); + if (!others[x] || !realfiles[x]) + break; + if (totalsilence) + ast_stream_rewind(others[x], totalsilence-200); + else + ast_stream_rewind(others[x], 200); + ast_truncstream(others[x]); + /* add the original file too */ + while ((fr = ast_readframe(realfiles[x]))) { + ast_writestream(others[x],fr); + } + ast_closestream(others[x]); + ast_closestream(realfiles[x]); + ast_filerename(prependfile, recordfile, sfmt[x]); +#if 0 + ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile); +#endif + ast_filedelete(prependfile, sfmt[x]); + } + } + if (rfmt) { + if (ast_set_read_format(chan, rfmt)) { + ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name); + } + } + if (outmsg) { + if (outmsg > 1) { + /* Let them know it worked */ + ast_streamfile(chan, "vm-msgsaved", chan->language); + ast_waitstream(chan, ""); + } + } + return res; +} + static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt) { char d, *fmts; @@ -1891,8 +2116,45 @@ static int get_folder2(struct ast_channel *chan, char *fn, int start) return res; } -static int -forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt) +static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context) +{ + int cmd = 0; + int retries = 0; + + while((cmd >= 0) && (cmd != 't') && (cmd != '#')) { + if (cmd) + retries = 0; + switch (cmd) { + case '1': + /* prepend a message to the current message and return */ + { + char file[200]; + snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg); + cmd = play_and_prepend(chan, NULL, file, 0, vmfmts); + break; + } + case '2': + cmd = 't'; + break; + case '#': + cmd = '#'; + break; + default: + cmd = play_and_wait(chan,"vm-forwardoptions"); + if (!cmd) + cmd = ast_waitfordigit(chan,6000); + if (!cmd) + retries++; + if (retries > 3) + cmd = 't'; + } + } + if (cmd == 't') + cmd = 0; + return cmd; +} + +static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt) { char username[70]; char sys[256]; @@ -1903,106 +2165,127 @@ forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, char miffile[256]; char fn[256]; char callerid[512]; - int res = 0; - struct ast_vm_user *receiver, srec; + int res = 0, cmd = 0; + struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL; char tmp[256]; char *stringp, *s; - - while(!res) { + int saved_messages = 0, found = 0; + int valid_extensions = 0; + while (!res && !valid_extensions) { res = ast_streamfile(chan, "vm-extension", chan->language); if (res) break; if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0)) break; - if ((receiver = find_user(&srec, context, username))) { - /* if (play_and_wait(chan, "vm-savedto")) + /* start all over if no username */ + if (!strlen(username)) + continue; + stringp = username; + s = strsep(&stringp, "*"); + /* start optimistic */ + valid_extensions = 1; + while (s) { + /* find_user is going to malloc since we have a NULL as first argument */ + if ((receiver = find_user(NULL, context, s))) { + if (!extensions) + vmtmp = extensions = receiver; + else { + vmtmp->next = receiver; + vmtmp = receiver; + } + found++; + } else { + valid_extensions = 0; break; - */ + } + s = strsep(&stringp, "*"); + } + /* break from the loop of reading the extensions */ + if (valid_extensions) + break; + /* invalid extension, try again */ + res = play_and_wait(chan, "pbx-invalid"); + } + /* check if we're clear to proceed */ + if (!extensions || !valid_extensions) + return res; + vmtmp = extensions; + cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context); - snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, receiver->context, username); - snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir); + while(!res && vmtmp) { + /* if (play_and_wait(chan, "vm-savedto")) + break; + */ + snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox); + snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir); + ast_log(LOG_DEBUG, sys); + system(sys); + + todircount = count_messages(todir); + strncpy(tmp, fmt, sizeof(tmp)); + stringp = tmp; + while((s = strsep(&stringp, "|"))) { + /* XXX This is a hack -- we should use build_filename or similar XXX */ + if (!strcasecmp(s, "wav49")) + s = "WAV"; + snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s); ast_log(LOG_DEBUG, sys); system(sys); + } + snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount); + ast_log(LOG_DEBUG, sys); + system(sys); + snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount); - todircount = count_messages(todir); - strncpy(tmp, fmt, sizeof(tmp)); - stringp = tmp; - while((s = strsep(&stringp, "|"))) { - /* XXX This is a hack -- we should use build_filename or similar XXX */ - if (!strcasecmp(s, "wav49")) - s = "WAV"; - snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s); - ast_log(LOG_DEBUG, sys); - system(sys); - } - snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount); - ast_log(LOG_DEBUG, sys); - system(sys); - snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount); - - /* load the information on the source message so we can send an e-mail like a new message */ - snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg); - if ((mif=ast_load(miffile))) { - - /* set callerid and duration variables */ - snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid")); - s = ast_variable_retrieve(mif, NULL, "duration"); - if (s) - duration = atol(s); - else - duration = 0; - if (strlen(receiver->email)) { + /* load the information on the source message so we can send an e-mail like a new message */ + snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg); + if ((mif=ast_load(miffile))) { + + /* set callerid and duration variables */ + snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid")); + s = ast_variable_retrieve(mif, NULL, "duration"); + if (s) + duration = atol(s); + else + duration = 0; + if (strlen(vmtmp->email)) { int attach_user_voicemail = attach_voicemail; char *myserveremail = serveremail; - if (receiver->attach > -1) - attach_user_voicemail = receiver->attach; - if (strlen(receiver->serveremail)) - myserveremail = receiver->serveremail; - sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, duration, attach_user_voicemail); - } - - if (strlen(receiver->pager)) { + if (vmtmp->attach > -1) + attach_user_voicemail = vmtmp->attach; + if (strlen(vmtmp->serveremail)) + myserveremail = vmtmp->serveremail; + sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail); + } + + if (strlen(vmtmp->pager)) { char *myserveremail = serveremail; - if (strlen(receiver->serveremail)) - myserveremail = receiver->serveremail; - sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver); + if (strlen(vmtmp->serveremail)) + myserveremail = vmtmp->serveremail; + sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp); } - ast_destroy(mif); /* or here */ - } - /* Leave voicemail for someone */ - manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username)); + ast_destroy(mif); /* or here */ + } + /* Leave voicemail for someone */ + manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox)); - /* give confirmatopm that the message was saved */ + free_user(vmtmp); + saved_messages++; + vmtmp = vmtmp->next; + } + if (saved_messages > 0) { + /* give confirmatopm that the message was saved */ + if (saved_messages == 1) res = play_and_wait(chan, "vm-message"); - if (!res) - res = play_and_wait(chan, "vm-saved"); - free_user(receiver); - break; - } else { - res = play_and_wait(chan, "pbx-invalid"); - } + else + res = play_and_wait(chan, "vm-messages"); + if (!res) + res = play_and_wait(chan, "vm-saved"); } - return res; + return res ? res : cmd; } -struct vm_state { - char curbox[80]; - char username[80]; - char curdir[256]; - char vmbox[256]; - char fn[256]; - char fn2[256]; - int deleted[MAXMSG]; - int heard[MAXMSG]; - int curmsg; - int lastmsg; - int newmessages; - int oldmessages; - int starting; - int repeats; -}; - static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file) { diff --git a/file.c b/file.c index 4213a23e2dc1c98e70f0d513199f52e02673be3d..9869eb325f8e6f7c389a4fb027556b8292314d1c 100755 --- a/file.c +++ b/file.c @@ -499,6 +499,15 @@ struct ast_filestream *ast_openvstream(struct ast_channel *chan, char *filename, return NULL; } +struct ast_frame *ast_readframe(struct ast_filestream *s) +{ + struct ast_frame *f = NULL; + int whennext = 0; + if (s && s->fmt) + f = s->fmt->read(s, &whennext); + return f; +} + static int ast_readaudio_callback(void *data) { struct ast_filestream *s = data; @@ -726,10 +735,58 @@ int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang) return -1; } +struct ast_filestream *ast_readfile(char *filename, char *type, char *comment, int flags, int check, mode_t mode) +{ + int fd,myflags = 0; + struct ast_format *f; + struct ast_filestream *fs=NULL; + char *fn; + char *ext; + if (ast_mutex_lock(&formatlock)) { + ast_log(LOG_WARNING, "Unable to lock format list\n"); + return NULL; + } + f = formats; + while(f) { + if (!strcasecmp(f->name, type)) { + char *stringp=NULL; + /* XXX Implement check XXX */ + ext = strdup(f->exts); + stringp=ext; + ext = strsep(&stringp, "|"); + fn = build_filename(filename, ext); + fd = open(fn, flags | myflags); + if (fd >= 0) { + errno = 0; + if ((fs = f->open(fd))) { + fs->trans = NULL; + fs->fmt = f; + fs->flags = flags; + fs->mode = mode; + fs->filename = strdup(filename); + fs->vfs = NULL; + } else { + ast_log(LOG_WARNING, "Unable to open %s\n", fn); + close(fd); + unlink(fn); + } + } else if (errno != EEXIST) + ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno)); + free(fn); + free(ext); + break; + } + f = f->next; + } + ast_mutex_unlock(&formatlock); + if (!f) + ast_log(LOG_WARNING, "No such format '%s'\n", type); + return fs; +} struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode) { - int fd,myflags; + int fd,myflags = 0; struct ast_format *f; struct ast_filestream *fs=NULL; char *fn; @@ -738,9 +795,12 @@ struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, ast_log(LOG_WARNING, "Unable to lock format list\n"); return NULL; } - myflags = 0; /* set the O_TRUNC flag if and only if there is no O_APPEND specified */ - if (!(flags & O_APPEND)) myflags = O_TRUNC; + if (!(flags & O_APPEND)) + myflags = O_TRUNC; + + myflags |= O_WRONLY | O_CREAT; + f = formats; while(f) { if (!strcasecmp(f->name, type)) { @@ -750,7 +810,7 @@ struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, stringp=ext; ext = strsep(&stringp, "|"); fn = build_filename(filename, ext); - fd = open(fn, flags | myflags | O_WRONLY | O_CREAT, mode); + fd = open(fn, flags | myflags, mode); if (fd >= 0) { errno = 0; if ((fs = f->rewrite(fd, comment))) { diff --git a/include/asterisk/file.h b/include/asterisk/file.h index f7808e435c2b9fc5fe2f8ad848e111f22e95a8dd..316e4711219a89ee4258061ffc1efdd9bc37f438 100755 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -140,6 +140,22 @@ char ast_waitstream_fr(struct ast_channel *c, char *breakon, char *forward, char 1 if monfd is ready for reading */ char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int monfd); +//! Starts reading from a file +/*! + * \param filename the name of the file to write to + * \param type format of file you wish to write out to + * \param comment comment to go with + * \param oflags output file flags + * \param check (unimplemented, hence negligible) + * \param mode Open mode + * Open an incoming file stream. oflags are flags for the open() command, and + * if check is non-zero, then it will not write a file if there are any files that + * start with that name and have an extension + * Please note, this is a blocking function. Program execution will not return until ast_waitstream completes it's execution. + * Returns a struct ast_filestream on success, NULL on failure + */ +struct ast_filestream *ast_readfile(char *filename, char *type, char *comment, int oflags, int check, mode_t mode); + //! Starts writing a file /*! * \param filename the name of the file to write to @@ -261,6 +277,13 @@ int ast_stream_rewind(struct ast_filestream *fs, long ms); */ long ast_tellstream(struct ast_filestream *fs); +//! Read a frame from a filestream +/*! + * \param ast_filestream fs to act on + * Returns a frame or NULL if read failed + */ +struct ast_frame *ast_readframe(struct ast_filestream *s); + #define AST_RESERVED_POINTERS 20 #if defined(__cplusplus) || defined(c_plusplus) diff --git a/sounds.txt b/sounds.txt index 86655bc47820aa1266b7ff95c2ace21a560959c3..71b5c1655625341e86769af6a1f5a0ec3e25943b 100755 --- a/sounds.txt +++ b/sounds.txt @@ -392,3 +392,5 @@ %minutes.gsm%minutes +%vm-forwardoptions.gsm%press 1 to prepend a message or 2 to forward the + message without prepending diff --git a/sounds/vm-forwardoptions.gsm b/sounds/vm-forwardoptions.gsm new file mode 100755 index 0000000000000000000000000000000000000000..b9ccb7f98002c9b899db20eac904530744db273a Binary files /dev/null and b/sounds/vm-forwardoptions.gsm differ