--- ../test/asterisk-1.0.6/apps/app_voicemail.c Sat Jan 29 00:53:33 2005 +++ apps/app_voicemail.c Mon Mar 14 10:36:02 2005 @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include /* we define USESQLVM when we have MySQL or POSTGRES */ #ifdef USEMYSQLVM @@ -174,12 +177,41 @@ int starting; int repeats; }; + +struct vm_dist_ext { + char extension[256]; + char context[256]; + struct vm_dist_ext *next; +}; + +struct vm_dist_list { + char name[256]; + char voice_file[256]; // Points to the file name of our recorded list name + struct vm_dist_ext *extensions; // Points to our first ext in this list + struct vm_dist_list *lists; // Points to our sublists + struct vm_dist_list *next; // Points to the next list in our set +}; + +static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox); static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option); 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, const char *unlockdir); static int vm_delete(char *file); static int vm_play_folder_name(struct ast_channel *chan, char *mbox); +static struct vm_dist_list *vm_dist_list_read(struct ast_vm_user *vmu, char *listfile); +static struct vm_dist_list *vm_dist_list_loop(struct ast_vm_user *vmu, struct ast_channel *chan); +static int vm_dist_list_add(struct ast_vm_user *vmu, struct ast_channel *chan, struct vm_dist_list *list); +static void vm_dist_list_free( struct vm_dist_list *list); +static int vm_dist_list_write( struct vm_dist_list *list, char *file); +static int vm_dist_leave_distributed_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail, struct vm_dist_list *list); +static struct vm_dist_list *vm_dist_list_delete( struct ast_channel *chan, struct vm_dist_list *list); +static int vm_dist_validate_list(struct vm_dist_list *list, char *listname); +static void vm_dist_leave_voicemail( struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, int duration, char *fmt, struct vm_dist_list *list); + + +static void copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt); + static char ext_pass_cmd[128]; static char *tdesc = "Comedian Mail (Voicemail System)"; @@ -289,6 +321,534 @@ LOCAL_USER_DECL; +static void vm_dist_leave_voicemail( struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, int duration, char *fmt, struct vm_dist_list *list) { + struct ast_vm_user *tmpvmu, recipu; + struct vm_dist_ext *tmpext; + + ast_log(LOG_WARNING, "List: %s\n", list->name); + + // First send to all the extensions of the main list + tmpext = list->extensions; + + while(tmpext != NULL) { + if((tmpvmu = find_user(&recipu, vmu->context, tmpext->extension))) { + ast_log(LOG_WARNING, "Here: %s\n", tmpext->extension); + copy_message(chan, vmu, 0, msgnum, duration, tmpvmu, fmt); + } + + tmpext = tmpext->next; + } + + // Child lists + if(list->lists != NULL) + vm_dist_leave_voicemail(chan, vmu, msgnum, duration, fmt, list->lists); + + // Sibling lists + list = list->next; + while(list != NULL) { + vm_dist_leave_voicemail(chan, vmu, msgnum, duration, fmt, list); + list = list->next; + } +} + +// Returns 0 on error, -1 on valid list +static int vm_dist_validate_list(struct vm_dist_list *list, char *listname) { + struct vm_dist_list *tmplist; + + if(!list) + return 0; + + // Are these one in the same? + if(listname) + if(!strcmp(list->name,listname)) + return 0; + + + // Do we have any sublists to try + if(list->lists) { + tmplist = list->lists; + while(tmplist) { + if(!listname) { // First level + if(!vm_dist_validate_list(tmplist,list->name)) { + return 0; + } + } + else { + if(!vm_dist_validate_list(tmplist, listname)) { + return 0; + } + } + tmplist = tmplist->next; + } + } + return -1; +} + +static void vm_dist_list_free( struct vm_dist_list *list) { + struct vm_dist_ext *ext = NULL, *tmp = NULL; + + if(list != NULL) { + // First loop through all the extensions and free them all + if(list->extensions != NULL) { + ext = list->extensions; + + while(ext->next != NULL) { + tmp = ext->next; + free(ext); + ext = tmp; + } + free(ext); + } + + // Now free each list below this + if(list->lists != NULL) { + vm_dist_list_free(list->lists); + } + + // See if we point to another list + if(list->next != NULL) { + vm_dist_list_free(list->next); + } + + // Finally free this list + free(list); + } +} +static int vm_dist_list_add(struct ast_vm_user *vmu, struct ast_channel *chan, struct vm_dist_list *list) { + int res, cmd=0; + char tmp[256]; + struct vm_dist_list *tmplist = NULL, *tmplist2 = NULL; + struct vm_dist_ext *distext = NULL; + + // Give our instructions + ast_streamfile(chan, "vm-dist-add-ext", vmu->language); + + res = ast_readstring(chan, tmp, 255, 5000, 6000, "#"); + while((strlen(tmp) != 0) && (res == 0)) { + // Are we dealing with extensions or lists + if(strlen(tmp) <= 2) { + tmplist = vm_dist_list_read(vmu, tmp); + + if((tmplist != NULL) && vm_dist_validate_list(tmplist, list->name)) { + if(list->lists == NULL) { + list->lists = tmplist; + } + else { + tmplist2 = list->lists; + while(tmplist2->next != NULL) + tmplist2 = tmplist2->next; + + tmplist2->next = tmplist; + } + ast_log(LOG_WARNING, "List %s successfully added\n", tmp); + } + else { + ast_play_and_wait(chan, "vm-dist-list-invalid"); + ast_streamfile(chan, "vm-dist-add-ext", vmu->language); + } + } + else { + // Create our extension + if(list->extensions == NULL) { // First extension for the list + list->extensions = malloc(sizeof(struct vm_dist_ext)); + bzero(list->extensions, sizeof(struct vm_dist_ext)); + distext = list->extensions; + } + else { + distext = list->extensions; + + while(distext->next != NULL) + distext = distext->next; + + distext->next = malloc(sizeof(struct vm_dist_ext)); + bzero(distext->next, sizeof(struct vm_dist_ext)); + distext = distext->next; + } + + // Now populate our extension + strncpy(distext->extension, tmp, 255); + ast_log(LOG_WARNING, "Extension %s successfully added\n", tmp); + } + + bzero(tmp, 256); + res = ast_readstring(chan, tmp, 255, 5000, 6000, "#"); + } + + // Verify we want to save these changes + while(!cmd) { + cmd = ast_play_and_wait(chan, "vm-dist-add-confirm"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + + if(cmd == '1') + return 0; + if((cmd == '2') || (cmd == '#')) + return -1; + } + + return -1; +} + +// list: The list to write +// file: The file to write to +static int vm_dist_list_write( struct vm_dist_list *list, char *file) { + FILE *fd; + struct vm_dist_ext *ext = NULL; + struct vm_dist_list *tmplist = NULL; + + // The first thing we need to do is validate our list to make sure we haven't included ourselves + if(!vm_dist_validate_list(list, NULL)) + return -1; + + if((list != NULL) && (file != NULL)) { + if((fd = fopen(file, "w")) == NULL) { + ast_log(LOG_WARNING, "Error writing distribution list: %s\n", file); + return -1; + } + + if(list->voice_file == NULL) { + ast_log(LOG_WARNING, "No voice file given for distribution list\n"); + return -1; + } + + // This should point to the file that is the recorded name of the list + fprintf(fd, "file=%s\n", list->voice_file); + + ast_log(LOG_WARNING, "file=%s\n", list->voice_file); + + // Now loop through all the extensions + if(list->extensions != NULL) { + ext = list->extensions; + + while(ext->next != NULL) { + if(ext->extension != NULL) { + ast_log(LOG_WARNING, "ext=>%s\n", ext->extension); + fprintf(fd, "ext=>%s\n", ext->extension); + } + + ext = ext->next; + } + if(ext->extension != NULL) { + ast_log(LOG_WARNING, "ext=>%s\n", ext->extension); + fprintf(fd, "ext=>%s\n", ext->extension); + } + } + + // And now all of the sublists + if(list->lists != NULL) { + tmplist = list->lists; + + while(tmplist->next != NULL) { + if(tmplist->name != NULL) { + ast_log(LOG_WARNING, "list=>%s\n", tmplist->name); + fprintf(fd, "list=>%s\n", tmplist->name); + } + + tmplist = tmplist->next; + } + if(tmplist->name != NULL) { + ast_log(LOG_WARNING, "list=>%s\n", tmplist->name); + fprintf(fd, "list=>%s\n", tmplist->name); + } + } + + ast_log(LOG_WARNING, "Distribution list successfully created\n"); + + fclose(fd); + return 0; + } + + // Shouldn't be here + return -1; +} + +static struct vm_dist_list *vm_dist_list_loop(struct ast_vm_user *vmu, struct ast_channel *chan) { + DIR *dp = NULL; + struct dirent *ep = NULL; + int res = 0, cmd=0; + char choice[2]; + char dir[256]; + struct vm_dist_list *list = NULL; + + // Build our directory name + snprintf(dir, 255, "%s/%s/dist/", VM_SPOOL_DIR, vmu->mailbox); + + // Get our directory list + if((dp = opendir(dir)) == NULL) + return NULL; + + // Now loop through the files, play the name, and wait for * + while((ep = readdir(dp)) != NULL) { + + if(ep->d_type == DT_REG) { + if( (strcmp(ep->d_name, ".") == 0) || + (strcmp(ep->d_name, "..") == 0) || + (strcmp(ep->d_name, "names") == 0)) + continue; + + if((list = vm_dist_list_read(vmu, ep->d_name)) != NULL) { + + // Play and wait for # + cmd = ast_play_and_wait(chan, list->voice_file); + + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + + if(cmd == '#') + return list; + } + + // Clean up if this isn't the one we want. + vm_dist_list_free(list); + cmd = 0; + } + } + + // Should never make it here; Nothing was selected + return NULL; +} + +static struct vm_dist_list *vm_dist_list_delete(struct ast_channel *chan, struct vm_dist_list *list) { + struct vm_dist_ext *prev, *next; + struct vm_dist_list *next_list = NULL, *prev_list = NULL; + int cmd=0,ctr=0; + + // First loop through our extensions + next = prev = list->extensions; + + while(next) { + // Play our number + cmd = 0; + for(ctr=0; ctr < strlen(next->extension); ctr++) { + switch(next->extension[ctr]) { + case '0': + cmd = ast_play_and_wait(chan, "digits/0"); + break; + case '1': + cmd = ast_play_and_wait(chan, "digits/1"); + break; + case '2': + cmd = ast_play_and_wait(chan, "digits/2"); + break; + case '3': + cmd = ast_play_and_wait(chan, "digits/3"); + break; + case '4': + cmd = ast_play_and_wait(chan, "digits/4"); + break; + case '5': + cmd = ast_play_and_wait(chan, "digits/5"); + break; + case '6': + cmd = ast_play_and_wait(chan, "digits/6"); + break; + case '7': + cmd = ast_play_and_wait(chan, "digits/7"); + break; + case '8': + cmd = ast_play_and_wait(chan, "digits/8"); + break; + case '9': + cmd = ast_play_and_wait(chan, "digits/9"); + break; + } + if(cmd == '#') + break; + } + // Play a short pause after each number + if(cmd != '#') + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + + if(cmd == '#') { + if(next != prev) { // Is this our first extension + if(next->next) { // Is this the last extension + prev->next = next->next; + free(next); + next = prev->next; + } + else { + free(next); + prev->next = NULL; + } + } + else { + if(next->next) { // One of many + prev = next->next; + free(next); + next = prev; + } + else { // One and only + free(prev); + prev = next = list->extensions = NULL; + } + } + } + else { + if(prev != next) { // This our first one in the list + prev = next; + } + if(next->next) { + next = prev->next; + } + else { + next = NULL; + } + + } + } + + if(list->lists) { + prev_list = next_list = list->lists; + + // Now we need to loop through each named list + while(next_list) { + cmd = 0; + + // Play our list name and wait for a # + cmd = ast_play_and_wait(chan, next_list->voice_file); + + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + + if(cmd == '#') { + if(next_list != prev_list) { // Is this our first extension + if(next_list->next) { // Is this the last extension + prev_list->next = next_list->next; + free(next_list); + next_list = prev_list->next; + } + else { + free(next_list); + prev_list->next = NULL; + } + } + else { + if(next_list->next) { // One of many + prev_list = next_list->next; + free(next_list); + next_list = prev_list; + } + else { // One and only + free(prev); + prev_list = next_list = list->lists = NULL; + } + } + } + else { + if(prev_list != next_list) { // This our first one in the list + prev_list = next_list; + } + if(next_list->next) { + next_list = prev_list->next; + } + else { + next_list = NULL; + } + } + } + } + return list; +} + +// list: The filename of the stored distribution list +// vmu: Our voicemail user structure +static struct vm_dist_list *vm_dist_list_read(struct ast_vm_user *vmu, char *listfile) { + FILE *fd = NULL; + char chDistFile[256]; // Will hold our distribution list file name + char *chLine = NULL; // Holds our line from the config file + struct vm_dist_list *list = NULL, *curList = NULL; // Our distribution list + struct vm_dist_ext *ext; + size_t len = 0; + char file[256]; + + // Make sure we were given something to work with + if((listfile == NULL) || (vmu == NULL)) + return NULL; + + // Our distribution list name + snprintf(chDistFile, 255, "%s/%s/dist/%s", VM_SPOOL_DIR, vmu->mailbox, listfile); + + // Open file + if((fd = fopen(chDistFile, "r")) == NULL) + return NULL; + + // Create our list for storage + list = malloc(sizeof(struct vm_dist_list)); + bzero(list, sizeof(struct vm_dist_list)); + + // Set our list name + strncpy(list->name, listfile, 255); + + // Loop through our file creating our lists as we go + while(getline(&chLine, &len, fd) != -1) { + + // Get rid of any newline characters + *(strchr(chLine, '\n')) = '\0'; + + // File location + if(strstr(chLine, "file=")) { + // Copy our filename into file + strncpy(list->voice_file, chLine + 5, 255); + } + + // Extensions + else if(strstr(chLine, "ext=>")) { + if(list->extensions == NULL) { + list->extensions = malloc(sizeof(struct vm_dist_ext)); + bzero(list->extensions, sizeof(struct vm_dist_ext)); + + // Create our current working extensions + ext = list->extensions; + } + else { + // We need to iterate through our already existing list until we find the end + ext = list->extensions; + while(ext->next != NULL) { + ext = ext->next; + } + + // We need to create a new one + ext->next = malloc(sizeof(struct vm_dist_ext)); + bzero(ext->next, sizeof(struct vm_dist_ext)); + + // Work with our new extension + ext = ext->next; + } + + strncpy(ext->extension, chLine + 5, 255); + strncpy(ext->context, vmu->context, strlen(vmu->context)); + } + + // Sub lists + else if(strstr(chLine, "list=>")) { + // Get our listname + strncpy(file, chLine + 6, 255); + + if(list->lists == NULL) { + list->lists = vm_dist_list_read(vmu, file); + } + else { + curList = list->lists; + while(curList->next != NULL) { + curList = curList->next; + } + + curList->next = vm_dist_list_read(vmu, file); + } + + } + + // Clean up + free(chLine); + chLine = NULL; + } + + // Clean up + fclose(fd); + + return list; +} + static void populate_defaults(struct ast_vm_user *vmu) { vmu->attach = -1; @@ -3297,6 +3857,10 @@ int silentexit = 0; char cid[256]=""; char *passptr; + char chDistExt[3], file[256], distnamefile[256]; + int duration = 0; + FILE *fd; + struct vm_dist_list *list = NULL; LOCAL_USER_ADD(u); memset(&vms, 0, sizeof(vms)); @@ -3536,7 +4100,288 @@ cmd = ast_play_and_wait(chan,"vm-sorry"); cmd='t'; break; - + + case '6': /* Distribution list management */ + /* Proposed file format + * file=Recorded_name.wav + * ext=>1234 + * list=>another_list (Allows for inclusion of other lists) + */ + + cmd = 0; + while(( cmd > -1) && (cmd != '#')) { + switch(cmd) { + case '1': // Create a distribution list + + // Play message that says to enter a two digit name for this list + ast_streamfile(chan, "vm-dist-enterid", vmu->language); + + // Get the name for our new list + ast_readstring(chan, chDistExt, 2, 5000, 6000, ""); + + // Check to see if this list already exists + snprintf(file, sizeof(file) - 1, "%s/%s/dist/%s", VM_SPOOL_DIR, vmu->mailbox, + chDistExt); + + if((fd = fopen(file, "r")) != NULL) { + // Play message stating this list already exists and to delete it first + ast_log(LOG_WARNING, "Distribution list %s already exists\n", + file); + + ast_play_and_wait(chan, "vm-dist-already-exists"); + fclose(fd); + break; + } + + // Make sure all of the directories exist + snprintf(tmp, sizeof(tmp) - 1, "%s/%s/dist/", + VM_SPOOL_DIR, vmu->mailbox); + + if(mkdir(tmp, S_IRWXG | S_IRWXU) != 0) { + if(errno != EEXIST) { + ast_log(LOG_WARNING, "Error while creating directory\n"); + break; + } + } + + snprintf(tmp, sizeof(tmp) - 1, "%s/%s/dist/names/", + VM_SPOOL_DIR, vmu->mailbox); + + if(mkdir(tmp, S_IRWXG | S_IRWXU) != 0) { + if(errno != EEXIST) { + ast_log(LOG_WARNING, "Error while creating directory\n"); + break; + } + } + + // Play message stating to record a name + ast_play_and_wait(chan, "vm-dist-record-name"); + ast_play_and_wait(chan, "beep"); + + // Record a name for this list + snprintf(distnamefile, sizeof(distnamefile) - 1, "%s/%s/dist/names/%s", + VM_SPOOL_DIR, vmu->mailbox, chDistExt); + + cmd = ast_play_and_record(chan, NULL, distnamefile, 10, vmfmts, &duration, -1, 1500, NULL); + if((cmd == -1) || (cmd == '0')) { + ast_log(LOG_WARNING, "Error while recording distribution file: %s", + distnamefile); + vm_delete(distnamefile); + break; + } + + // We're ready to start retrieving exts so create our list + list = malloc(sizeof(struct vm_dist_list)); + bzero(list, sizeof(struct vm_dist_list)); + + strncpy(list->voice_file,distnamefile, 255); + + // Now we have our list, now lets save it to file + if(vm_dist_list_add(vmu, chan, list) == 0) { + snprintf(tmp, 255, "%s/%s/dist/%s", VM_SPOOL_DIR, vmu->mailbox, chDistExt); + if(!vm_dist_list_write(list, tmp)) + ast_play_and_wait(chan, "vm-dist-success"); + else + ast_play_and_wait(chan, "vm-dist-add-invalid"); + } + else { + vm_delete(list->voice_file); + } + + // Remember to clean up, memory leaks bad!!! :) + vm_dist_list_free(list); + + // So we drop back to the menu + cmd = 0; + + break; + + case '2': // Edit distribution list + // Play a message that says to enter the code, or press * to loop + ast_streamfile(chan, "vm-dist-edit-choice", vmu->language); + + // Read our choice + if(ast_readstring(chan, tmp, 255, 5000, 6000, "#") != 0) + break; + + // Loop if just # was entered + if(strlen(tmp) == 0) + list = vm_dist_list_loop(vmu, chan); + else + list = vm_dist_list_read(vmu, tmp); + + if(list == NULL) { + ast_play_and_wait(chan, "vm-dist-edit-invalid"); + break; + } + + // Let them choose + cmd = 0; res = -1; + while(!cmd) { + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-dist-edit-addition"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-dist-edit-deletion"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + switch(cmd) { + case '1': // Additions + res = vm_dist_list_add(vmu, chan, list); + break; + case '2': // Deletions + snprintf(tmp, 255, "%s/%s/dist/%s", VM_SPOOL_DIR, vmu->mailbox, list->name); + list = vm_dist_list_delete(chan, list); + // Confirm the delete + cmd = 0; + cmd = ast_play_and_wait(chan, "vm-dist-add-confirm"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + + if(cmd == '1') + snprintf(tmp, 255, "%s/%s/dist/%s", VM_SPOOL_DIR, vmu->mailbox, list->name); + vm_dist_list_write(list, tmp); + break; + case '#': + break; + } + + if(res == 0) { + snprintf(tmp, 255, "%s/%s/dist/%s", VM_SPOOL_DIR, vmu->mailbox, list->name); + if(vm_dist_list_write(list, tmp) == 0) + ast_play_and_wait(chan, "vm-dist-success"); + // Note: Add code to message saying something went wrong + } + } + + // Cleanup + vm_dist_list_free(list); + + break; + case '3': // Delete distribution list + + // Play a message that says to enter the code, or press * to loop + ast_streamfile(chan, "vm-dist-edit-choice", vmu->language); + + // Read our choice + if(ast_readstring(chan, tmp, 255, 5000, 6000, "#") != 0) + break; + + // Loop if just # was entered + if(strlen(tmp) == 0) + list = vm_dist_list_loop(vmu, chan); + else + list = vm_dist_list_read(vmu, tmp); + + if(list == NULL) { + ast_play_and_wait(chan, "vm-dist-edit-invalid"); + break; + } + + cmd = ast_play_and_wait(chan, "vm-dist-add-confirm"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + + if(cmd == '1') { // Yes + // Delete the list file and the saved name + snprintf(tmp, 255, "%s/%s/dist/%s", VM_SPOOL_DIR, vmu->mailbox, list->name); + + // Delete our list file + if(remove(tmp) != 0) + ast_log(LOG_WARNING, "Error deleting file: %s\n", tmp); + + // Continue trying to remove the voice files + if(strstr(vmfmts, "wav") != NULL) { + // wav + snprintf(tmp, 255, "%s.wav", list->voice_file); + + if(remove(tmp) != 0) + ast_log(LOG_WARNING, "Error deleting file: %s\n", tmp); + + // WAV + snprintf(tmp, 255, "%s.WAV", list->voice_file); + + if(remove(tmp) != 0) + ast_log(LOG_WARNING, "Error deleting file: %s\n", tmp); + } + + if(strstr(vmfmts, "gsm") != NULL) { + // GSM + snprintf(tmp, 255, "%s.gsm", list->voice_file); + + if(remove(tmp) != 0) + ast_log(LOG_WARNING, "Error deleting file: %s\n", tmp); + + } + + // Play our success message + ast_play_and_wait(chan, "vm-dist-deleted"); + + } + + // Cleanup + if(list) + vm_dist_list_free(list); + + break; + + case '4': // Send distributed message + + // Play a message that says to enter the code, or press * to loop + ast_streamfile(chan, "vm-dist-edit-choice", vmu->language); + + // Read our choice + if(ast_readstring(chan, tmp, 255, 5000, 6000, "#") != 0) + break; + + // Loop if just # was entered + if(strlen(tmp) == 0) + list = vm_dist_list_loop(vmu, chan); + else + list = vm_dist_list_read(vmu, tmp); + + if(list == NULL) { + ast_play_and_wait(chan, "vm-dist-edit-invalid"); + break; + } + + + // Confirm the deletion + cmd = ast_play_and_wait(chan, "vm-dist-add-confirm"); + if(!cmd) + ast_play_and_wait(chan, "vm-pause-0.5"); + + if(cmd == '1') { // Yes + // Record and send our message + vm_dist_leave_distributed_voicemail(chan, vmu->mailbox, 0, 0, 0, list); + + } + + // Cleanup + vm_dist_list_free(list); + + break; + } + + cmd = 0; + + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-dist-add"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-dist-edit"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-dist-delete"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-dist-send"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-pause-0.5"); + + // Drop out of this if the # was pressed + if(cmd == '#') { + cmd = 0; + break; + } + } + break; case '*': /* Return to main menu */ cmd = 't'; break; @@ -3558,6 +4403,8 @@ } if(vmu->svmail&&!cmd) cmd=ast_play_and_wait(chan, "vm-leavemsg"); + if(!cmd) + cmd = ast_play_and_wait(chan, "vm-dist"); if (!cmd) cmd = ast_play_and_wait(chan, "vm-starmain"); if (!cmd) @@ -4789,4 +5636,281 @@ char *key() { return ASTERISK_GPL_KEY; +} + +static int vm_dist_leave_distributed_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail, struct vm_dist_list *list) +{ + char txtfile[256]; + FILE *txt; + int res = 0; + int msgnum; + int fd; + int duration = 0; + int ausemacro = 0; + int ousemacro = 0; + char date[256]; + char dir[256]; + char fn[256]; + char prefile[256]=""; + char ext_context[256] = ""; + char fmt[80]; + char *context; + char ecodes[16] = "#"; + char tmp[256] = "", *tmpptr; + struct ast_vm_user *vmu; + struct ast_vm_user svm; + + strncpy(tmp, ext, sizeof(tmp) - 1); + ext = tmp; + context = strchr(tmp, '@'); + if (context) { + *context = '\0'; + context++; + tmpptr = strchr(context, '&'); + } else { + tmpptr = strchr(ext, '&'); + } + + if (tmpptr) { + *tmpptr = '\0'; + tmpptr++; + } + + if ((vmu = find_user(&svm, context, ext))) { + /* Setup pre-file if appropriate */ + if (strcmp(vmu->context, "default")) + snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context); + else + strncpy(ext_context, vmu->context, sizeof(ext_context) - 1); + if (busy) + snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext); + else if (unavail) + snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext); + make_dir(dir, sizeof(dir), vmu->context, "", ""); + /* 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)); + make_dir(dir, sizeof(dir), vmu->context, 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)); + make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX"); + if (mkdir(dir, 0700) && (errno != EEXIST)) + ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno)); + + /* Check current or macro-calling context for special extensions */ + if (!ast_strlen_zero(vmu->exit)) { + if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->callerid)) + strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1); + } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->callerid)) + strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1); + else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->callerid)) { + strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1); + ousemacro = 1; + } + + if (!ast_strlen_zero(vmu->exit)) { + if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->callerid)) + strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1); + } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->callerid)) + strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1); + else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->callerid)) { + strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1); + ausemacro = 1; + } + + /* Play the beginning intro if desired */ + if (!ast_strlen_zero(prefile)) { + if (ast_fileexists(prefile, NULL, NULL) > 0) { + if (ast_streamfile(chan, prefile, chan->language) > -1) + res = ast_waitstream(chan, ecodes); + } else { + ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile); + res = invent_message(chan, vmu->context, ext, busy, ecodes); + } + if (res < 0) { + ast_log(LOG_DEBUG, "Hang up during prefile playback\n"); + free_user(vmu); + return -1; + } + } + if (res == '#') { + /* On a '#' we skip the instructions */ + silent = 1; + res = 0; + } + if (!res && !silent) { + res = ast_streamfile(chan, INTRO, chan->language); + if (!res) + res = ast_waitstream(chan, ecodes); + if (res == '#') { + silent = 1; + res = 0; + } + } + if (res > 0) + ast_stopstream(chan); + /* Check for a '*' here in case the caller wants to escape from voicemail to something + other than the operator -- an automated attendant or mailbox login for example */ + if (res == '*') { + strncpy(chan->exten, "a", sizeof(chan->exten) - 1); + if (!ast_strlen_zero(vmu->exit)) { + strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1); + } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) { + strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1); + } + chan->priority = 0; + free_user(vmu); + return 0; + } + /* Check for a '0' here */ + if (res == '0') { + transfer: + if (vmu->operator) { + strncpy(chan->exten, "o", sizeof(chan->exten) - 1); + if (!ast_strlen_zero(vmu->exit)) { + strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1); + } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) { + strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1); + } + ast_play_and_wait(chan, "transfer"); + chan->priority = 0; + free_user(vmu); + return 0; + } else { + ast_play_and_wait(chan, "vm-sorry"); + return 0; + } + } + if (res < 0) { + free_user(vmu); + return -1; + } + /* The meat of recording the message... All the announcements and beeps have been played*/ + strncpy(fmt, vmfmts, sizeof(fmt) - 1); + if (!ast_strlen_zero(fmt)) { + msgnum = 0; + do { + make_file(fn, sizeof(fn), dir, msgnum); + if (ast_fileexists(fn, NULL, chan->language) <= 0) + break; + msgnum++; + } while(msgnum < MAXMSG); + if (res >= 0) { + /* Unless we're *really* silent, try to send the beep */ + res = ast_streamfile(chan, "beep", chan->language); + if (!res) + res = ast_waitstream(chan, ""); + } + if (msgnum < MAXMSG) { + /* Store information */ + snprintf(txtfile, sizeof(txtfile), "%s.txt", fn); + txt = fopen(txtfile, "w+"); + if (txt) { + get_date(date, sizeof(date)); + fprintf(txt, +";\n" +"; Message Information file\n" +";\n" +"[message]\n" +"origmailbox=%s\n" +"context=%s\n" +"macrocontext=%s\n" +"exten=%s\n" +"priority=%d\n" +"callerchan=%s\n" +"callerid=%s\n" +"origdate=%s\n" +"origtime=%ld\n", + ext, + chan->context, + chan->macrocontext, + chan->exten, + chan->priority, + chan->name, + chan->callerid ? chan->callerid : "Unknown", + date, (long)time(NULL)); + fclose(txt); + } else + ast_log(LOG_WARNING, "Error opening text file for output\n"); + res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir); + if (res == '0') + goto transfer; + if (res > 0) + res = 0; + fd = open(txtfile, O_APPEND | O_WRONLY); + if (fd > -1) { + txt = fdopen(fd, "a"); + if (txt) { + fprintf(txt, "duration=%d\n", duration); + fclose(txt); + } else + close(fd); + } + if (duration < vmminmessage) { + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage); + vm_delete(fn); + /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */ + + goto leave_vm_out; + } + vm_dist_leave_voicemail(chan, vmu, msgnum, duration, fmt, list); + + // Try to remove all possible formats + char vmfile[256]; + + snprintf(vmfile, 255, "%s/%s/INBOX/msg%04d.wav", VM_SPOOL_DIR, vmu->mailbox, msgnum); + remove(vmfile); + bzero(vmfile, 256); + + snprintf(vmfile, 255, "%s/%s/INBOX/msg%04d.WAV", VM_SPOOL_DIR, vmu->mailbox, msgnum); + remove(vmfile); + bzero(vmfile, 256); + + snprintf(vmfile, 255, "%s/%s/INBOX/msg%04d.gsm", VM_SPOOL_DIR, vmu->mailbox, msgnum); + remove(vmfile); + bzero(vmfile, 256); + + snprintf(vmfile, 255, "%s/%s/INBOX/msg%04d.txt", VM_SPOOL_DIR, vmu->mailbox, msgnum); + remove(vmfile); + bzero(vmfile, 256); + + snprintf(dir, 255, "%s/%s/dist/", VM_SPOOL_DIR, vmu->mailbox); + /* Are there to be more recipients of this message? */ + while (tmpptr) { + struct ast_vm_user recipu, *recip; + char *exten, *context; + + exten = strsep(&tmpptr, "&"); + context = strchr(exten, '@'); + if (context) { + *context = '\0'; + context++; + } + if ((recip = find_user(&recipu, context, exten))) { + copy_message(chan, vmu, 0, msgnum, duration, recip, fmt); + free_user(recip); + } + } + if (ast_fileexists(fn, NULL, NULL)) + notify_new_message(chan, vmu, msgnum, duration, fmt, chan->callerid); + } else { + res = ast_streamfile(chan, "vm-mailboxfull", chan->language); + if (!res) + res = ast_waitstream(chan, ""); + ast_log(LOG_WARNING, "No more messages possible\n"); + } + } else + ast_log(LOG_WARNING, "No format for saving voicemail?\n"); +leave_vm_out: + free_user(vmu); + } else { + ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext); + /*Send the call to n+101 priority, where n is the current priority*/ + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) + chan->priority+=100; + } + + return res; }