Skip to content
Snippets Groups Projects
app_voicemail.c 291 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	struct ast_event *event;
    
    
    	/* Strip off @default */
    
    	context = mailbox = ast_strdupa(mbox);
    	strsep(&context, "@");
    	if (ast_strlen_zero(context))
    		context = "default";
    
    
    	if (!(event = ast_event_new(AST_EVENT_MWI,
    			AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
    
    			AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
    
    			AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, new,
    			AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
    			AST_EVENT_IE_END))) {
    		return;
    	}
    
    	ast_event_queue_and_cache(event,
    		AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
    
    		AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
    
    static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
    
    	char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
    
    	int newmsgs = 0, oldmsgs = 0;
    
    	const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
    
    	char *myserveremail = serveremail;
    
    
    	make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
    	make_file(fn, sizeof(fn), todir, msgnum);
    
    	snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
    
    	if (!ast_strlen_zero(vmu->attachfmt)) {
    
    		if (strstr(fmt, vmu->attachfmt))
    
    			ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'.  Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
    
    	/* Attach only the first format */
    
    	fmt = ast_strdupa(fmt);
    	stringp = fmt;
    	strsep(&stringp, "|");
    
    	if (!ast_strlen_zero(vmu->serveremail))
    		myserveremail = vmu->serveremail;
    
    
    		int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
    		if (!attach_user_voicemail)
    			attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
    
    
    		if (attach_user_voicemail)
    			RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
    
    
    		/*XXX possible imap issue, should category be NULL XXX*/
    		sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
    
    
    		if (attach_user_voicemail)
    			DISPOSE(todir, msgnum);
    
    	if (!ast_strlen_zero(vmu->pager))
    
    		sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
    
    	if (ast_test_flag(vmu, VM_DELETE))
    
    		DELETE(todir, msgnum, fn);
    
    #ifdef IMAP_STORAGE
    	DELETE(todir, msgnum, fn);
    #endif
    
    	/* Leave voicemail for someone */
    
    	if (ast_app_has_voicemail(ext_context, NULL)) 
    
    	queue_mwi_event(ext_context, newmsgs, oldmsgs);
    
    
    	manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
    
    	run_externnotify(vmu->context, vmu->mailbox);
    
    static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int flag, signed char record_gain)
    
    #ifdef IMAP_STORAGE
    	BODY *body;
    	char *header_content;
    	char *temp;
    
    	char todir[256];
    	int todircount=0;
    
    	struct vm_state *dstvms;
    
    #endif
    	char username[70]="";
    
    	struct ast_vm_user *receiver = NULL, *vmtmp;
    	AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
    
    	int saved_messages = 0, found = 0;
    	int valid_extensions = 0;
    
    	char *dir;
    	int curmsg;
    
    	if (vms == NULL) return -1;
    	dir = vms->curdir;
    	curmsg = vms->curmsg;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
    
    			int done = 0;
    			int retries = 0;
    			cmd=0;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    			while ((cmd >= 0) && !done ){
    
    				if (cmd)
    					retries = 0;
    				switch (cmd) {
    				case '1': 
    					use_directory = 0;
    					done = 1;
    					break;
    				case '2': 
    					use_directory = 1;
    					done=1;
    					break;
    				case '*': 
    					cmd = 't';
    					done = 1;
    					break;
    				default: 
    					/* Press 1 to enter an extension press 2 to use the directory */
    					cmd = ast_play_and_wait(chan,"vm-forward");
    					if (!cmd)
    						cmd = ast_waitfordigit(chan,3000);
    					if (!cmd)
    						retries++;
    					if (retries > 3)
    					{
    						cmd = 't';
    						done = 1;
    					}
    					
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				}
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    			if (cmd < 0 || cmd == 't')
    
    		if (use_directory) {
    
    			/* use app_directory */
    			
    			char old_context[sizeof(chan->context)];
    			char old_exten[sizeof(chan->exten)];
    			int old_priority;
    			struct ast_app* app;
    
    			
    			app = pbx_findapp("Directory");
    			if (app) {
    
    				char vmcontext[256];
    				/* make backup copies */
    
    				memcpy(old_context, chan->context, sizeof(chan->context));
    				memcpy(old_exten, chan->exten, sizeof(chan->exten));
    				old_priority = chan->priority;
    				
    				/* call the the Directory, changes the channel */
    
    				sprintf(vmcontext, "%s||v", context ? context : "default");
    				res = pbx_exec(chan, app, vmcontext);
    
    				ast_copy_string(username, chan->exten, sizeof(username));
    
    				
    				/* restore the old context, exten, and priority */
    				memcpy(chan->context, old_context, sizeof(chan->context));
    				memcpy(chan->exten, old_exten, sizeof(chan->exten));
    				chan->priority = old_priority;
    				
    			} else {
    				ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
    
    				ast_clear_flag((&globalflags), VM_DIRECFORWARD);	
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		} else {
    
    			/* Ask for an extension */
    			res = ast_streamfile(chan, "vm-extension", chan->language);	/* "extension" */
    			if (res)
    				break;
    			if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
    				break;
    		}
    		
    
    		if (ast_strlen_zero(username))
    
    			continue;
    		stringp = username;
    		s = strsep(&stringp, "*");
    		/* start optimistic */
    		valid_extensions = 1;
    		while (s) {
    
    			/* Don't forward to ourselves.  find_user is going to malloc since we have a NULL as first argument */
    			if (strcmp(s,sender->mailbox) && (receiver = find_user(NULL, context, s))) {
    
    				AST_LIST_INSERT_HEAD(&extensions, receiver, list);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    
    			s = strsep(&stringp, "*");
    		}
    		/* break from the loop of reading the extensions */
    		if (valid_extensions)
    			break;
    
    		/* "I am sorry, that's not a valid extension.  Please try again." */
    
    		res = ast_play_and_wait(chan, "pbx-invalid");
    
    	if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
    
    	if (flag==1) {
    
    		char mailbox[AST_MAX_EXTENSION * 2 + 2];
    		snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
    
    		/* Send VoiceMail */
    
    		memset(&leave_options, 0, sizeof(leave_options));
    		leave_options.record_gain = record_gain;
    
    		cmd = leave_voicemail(chan, mailbox, &leave_options);
    
    		/* Forward VoiceMail */
    
    		long duration = 0;
    
    		char *myserveremail = serveremail;
    		char buf[1024] = "";
    		int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
    
    		cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, vms);
    
    			AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
    
    #ifdef IMAP_STORAGE
     				/* Need to get message content */
    
    	 			ast_debug(3,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
    
     				if (!vms->msgArray[vms->curmsg]) {
    
     					ast_log (LOG_WARNING,"Trying to access unknown message\n");
     					return -1;
     				}
     
     				/* This will only work for new messages... */
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    				header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
    
     				/* empty string means no valid header */
    
     					ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
     					return -1;
     				}
     				/* Get header info needed by sendmail */
    
    				if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))))
    
     					duration = atoi(temp);
     				else
     					duration = 0;
    
     				/* Attach only the first format */
    
    				if ((fmt = ast_strdupa(fmt))) {
    
     					stringp = fmt;
     					strsep(&stringp, "|");
     				} else {
     					ast_log (LOG_ERROR,"audio format not set. Default to WAV\n");
     					fmt = "WAV";
     				}
     				if (!strcasecmp(fmt, "wav49"))
     					fmt = "WAV";
    
    	 			ast_debug(3,"**** format set to %s, vmfmts set to %s\n",fmt,vmfmts);
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
     				/* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
    
     				/* if (!ast_strlen_zero(fmt)) { */
    
    				snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
    
     				make_gsm_file(vms->fn, vms->imapuser, todir, vms->curmsg);
    
    	 			ast_debug(3,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms->msgArray[vms->curmsg], vms->fn);
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    				/*mail_fetchstructure (mailstream, vmArray[0], &body); */
    				mail_fetchstructure (vms->mailstream, vms->msgArray[vms->curmsg], &body);
    				save_body(body,vms,"3","gsm");
    
     				/* should not assume "fmt" here! */
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    				save_body(body,vms,"2",fmt);
    
    				/* get destination mailbox */
    				dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
    				if (dstvms) {
    					init_mailstream(dstvms, 0);
    					if (!dstvms->mailstream) {
    						ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
    					} else {
    						STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
    						run_externnotify(vmtmp->context, vmtmp->mailbox); 
    					}
    				} else {
    					ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
    				}
    
    				if (!ast_strlen_zero(vmtmp->serveremail))
    					myserveremail = vmtmp->serveremail;
    				attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
    				/* NULL category for IMAP storage */
    
    				sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
    
    				copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
    
    				AST_LIST_REMOVE_CURRENT(&extensions, list);
    				free_user(vmtmp);
    				if (res)
    					break;
    
    			AST_LIST_TRAVERSE_SAFE_END;
    
    			if (saved_messages > 0) {
    				/* give confirmation that the message was saved */
    				/* commented out since we can't forward batches yet
    				if (saved_messages == 1)
    
    					res = ast_play_and_wait(chan, "vm-message");
    
    					res = ast_play_and_wait(chan, "vm-messages");
    
    					res = ast_play_and_wait(chan, "vm-saved"); */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				res = ast_play_and_wait(chan, "vm-msgsaved");
    
    
    	/* If anything failed above, we still have this list to free */
    	while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
    		free_user(vmtmp);
    
    static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
    
    	if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
    
    		ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
    	return res;
    }
    
    static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
    {
    
    	return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
    
    static int play_message_category(struct ast_channel *chan, const char *category)
    
    	if (!ast_strlen_zero(category))
    
    		res = ast_play_and_wait(chan, category);
    
    
    	if (res) {
    		ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
    		res = 0;
    	}
    
    
    static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
    
    {
    	int res = 0;
    	struct vm_zone *the_zone = NULL;
    	time_t t;
    
    
    	if (ast_get_time_t(origtime, &t, 0, NULL)) {
    
    		ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
    		return 0;
    	}
    
    	/* Does this user have a timezone specified? */
    
    	if (!ast_strlen_zero(vmu->zonetag)) {
    
    		/* Find the zone in the list */
    		struct vm_zone *z;
    
    		AST_LIST_LOCK(&zones);
    		AST_LIST_TRAVERSE(&zones, z, list) {
    
    			if (!strcmp(z->name, vmu->zonetag)) {
    				the_zone = z;
    				break;
    
    /* No internal variable parsing for now, so we'll comment it out for the time being */
    #if 0
    	/* Set the DIFF_* variables */
    
    	ast_localtime(&t, &time_now, NULL);
    
    	ast_localtime(&tv_now, &time_then, NULL);
    
    
    	/* Day difference */
    	if (time_now.tm_year == time_then.tm_year)
    
    		snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
    
    		snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
    
    	pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
    
    	/* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
    #endif
    	if (the_zone)
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	else if (!strcasecmp(chan->language,"pl"))       /* POLISH syntax */
    
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	else if (!strcasecmp(chan->language,"se"))       /* SWEDISH syntax */
    
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	else if (!strcasecmp(chan->language,"no"))       /* NORWEGIAN syntax */
    
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	else if (!strcasecmp(chan->language,"de"))       /* GERMAN syntax */
    
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	else if (!strcasecmp(chan->language,"nl"))      /* DUTCH syntax */
    
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	else if (!strcasecmp(chan->language,"it"))      /* ITALIAN syntax */
    
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
    
    	else if (!strcasecmp(chan->language,"gr"))
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
    
    	else if (!strcasecmp(chan->language,"pt_BR"))
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
    
    	else if (!strcasecmp(chan->language,"tw"))      /* CHINESE (Taiwan) syntax */
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);		
    
    	else
    		res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
    #if 0
    	pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
    #endif
    	return res;
    }
    
    static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
    
    {
    	int res = 0;
    	int i;
    	char *callerid, *name;
    
    	char prefile[PATH_MAX] = "";
    
    	
    
    	/* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
    	/* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
    
    	if ((cid == NULL)||(context == NULL))
    
    		return res;
    
    	/* Strip off caller ID number from name */
    
    	ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
    
    	ast_callerid_parse(cid, &name, &callerid);
    
    	if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
    
    		/* Check for internal contexts and only */
    		/* say extension when the call didn't come from an internal context in the list */
    
    		for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
    
    			ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
    
    			if ((strcmp(cidinternalcontexts[i], context) == 0))
    
    		if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
    			if (!res) {
    
    				snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
    
    				if (!ast_strlen_zero(prefile)) {
    
    				/* See if we can find a recorded name for this person instead of their extension number */
    					if (ast_fileexists(prefile, NULL, NULL) > 0) {
    
    						ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
    
    						if (!callback)
    							res = wait_file2(chan, vms, "vm-from");
    
    						res = ast_stream_and_wait(chan, prefile, "");
    
    						ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
    
    						/* BB: Say "from extension" as one saying to sound smoother */
    						if (!callback)
    							res = wait_file2(chan, vms, "vm-from-extension");
    						res = ast_say_digit_str(chan, callerid, "", chan->language);
    					}
    				}
    			}
    		}
    
    
    		else if (!res) {
    
    			ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
    
    			/* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
    			if (!callback)
    				res = wait_file2(chan, vms, "vm-from-phonenumber");
    			res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
    		}
    
    		/* Number unknown */
    
    		ast_debug(1, "VM-CID: From an unknown number\n");
    
    		/* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
    		res = wait_file2(chan, vms, "vm-unknown-caller");
    
    static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
    
    {
    	int res = 0;
    	int durationm;
    	int durations;
    	/* Verify that we have a duration for the message */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (duration == NULL)
    
    		return res;
    
    	/* Convert from seconds to minutes */
    	durations=atoi(duration);
    	durationm=(durations / 60);
    
    
    	ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
    
    	if ((!res) && (durationm >= minduration)) {
    		res = wait_file2(chan, vms, "vm-duration");
    
    		/* POLISH syntax */
    		if (!strcasecmp(chan->language, "pl")) {
    			div_t num = div(durationm, 10);
    
    			if (durationm == 1) {
    				res = ast_play_and_wait(chan, "digits/1z");
    				res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
    			} else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
    				if (num.rem == 2) {
    					if (!num.quot) {
    						res = ast_play_and_wait(chan, "digits/2-ie");
    					} else {
    						res = say_and_wait(chan, durationm - 2 , chan->language);
    						res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
    					}
    				} else {
    					res = say_and_wait(chan, durationm, chan->language);
    				}
    				res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
    			} else {
    				res = say_and_wait(chan, durationm, chan->language);
    				res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
    			}
    		/* DEFAULT syntax */
    		} else {
    			res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
    			res = wait_file2(chan, vms, "vm-minutes");
    		}
    
    #ifdef IMAP_STORAGE
    static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
    {
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	BODY *body;
    
    	char *header_content;
    	char cid[256];
    	char context[256];
    	char origtime[32];
    	char duration[16];
    	char category[32];
    
    	char todir[PATH_MAX];
    
    	char *attachedfilefmt;
    
    	ast_debug(3,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
    
    
    	if (!vms->msgArray[vms->curmsg]) {
    
    		ast_log (LOG_WARNING,"Trying to access unknown message\n");
    		return -1;
    	}
    
    	/* This will only work for new messages... */
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
    
    	/* empty string means no valid header */
    
    		ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
    		return -1;
    	}
    
    	snprintf(todir, sizeof(todir), "%s%s/%s/tmp", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
    
    	make_gsm_file(vms->fn, vms->imapuser, todir, vms->curmsg);
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	mail_fetchstructure (vms->mailstream,vms->msgArray[vms->curmsg],&body);
    
    	
    	/* We have the body, now we extract the file name of the first attachment. */
    	if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
    		attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
    	} else {
    		ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
    		return -1;
    	}
    	
    
    	/* Find the format of the attached file */
    
    	strsep(&attachedfilefmt, ".");
    	if (!attachedfilefmt) {
    		ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
    		return -1;
    	}
    	save_body(body, vms, "2", attachedfilefmt);
    
    
    	adsi_message(chan, vms);
    	if (!vms->curmsg)
    		res = wait_file2(chan, vms, "vm-first");	/* "First" */
    	else if (vms->curmsg == vms->lastmsg)
    		res = wait_file2(chan, vms, "vm-last");		/* "last" */
    
    	if (!res) {
    		res = wait_file2(chan, vms, "vm-message");	/* "message" */
    		if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
    			if (!res)
    				res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
    		}
    	}
    
    
    	if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))))
    
    		ast_copy_string(cid, temp, sizeof(cid)); 
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	else 
    		cid[0] = '\0';
    
    	
    	if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))))
    
    		ast_copy_string(context, temp, sizeof(context)); 
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	else
    		context[0] = '\0';
    
    
    	if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))))
    
    		ast_copy_string(origtime, temp, sizeof(origtime));
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	else
    		origtime[0] = '\0';
    
    
    	if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))))
    
    		ast_copy_string(duration,temp, sizeof(duration));
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	else
    		duration[0] = '\0';
    	
    
    	if ((temp = get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))))
    
    		ast_copy_string(category,temp, sizeof(category));
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	else
    		category[0] = '\0';
    
    	/*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
    
    	/*	context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
    	if (res == '1')
    		res = 0;
    
    
    	if ((!res) && !ast_strlen_zero(category)) {
    
    		res = play_message_category(chan, category);
    	}
    
    	if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)) && origtime[0] != '\0')
    		res = play_message_datetime(chan, vmu, origtime, "IMAP_STORAGE");
    	if ((!res) && (ast_test_flag(vmu, VM_SAYCID)) && cid[0] !='\0' && context[0] !='\0')
    		res = play_message_callerid(chan, vms, cid, context, 0);
    
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)) && duration[0] != '\0')
    		res = play_message_duration(chan, vms, duration, vmu->saydurationm);
    
    
    	/* Allow pressing '1' to skip envelope / callerid */
    	/* if (res == '1')
    		res = 0;
    	*/
    	/*ast_config_destroy(msg_cfg);*/
    	res = 0;
    
    	if (!res) {
    		vms->heard[vms->curmsg] = 1;
    		res = wait_file(chan, vms, vms->fn);
    	}
    	DISPOSE(vms->curdir, vms->curmsg);
    
    static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
    
    	char filename[256], *cid;
    	const char *origtime, *context, *category, *duration;
    
    	struct ast_config *msg_cfg;
    
    
    	make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
    	adsi_message(chan, vms);
    	if (!vms->curmsg)
    
    		res = wait_file2(chan, vms, "vm-first");	/* "First" */
    
    	else if (vms->curmsg == vms->lastmsg)
    
    		res = wait_file2(chan, vms, "vm-last");		/* "last" */
    
    		/* POLISH syntax */
    		if (!strcasecmp(chan->language, "pl")) { 
    			if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
    				int ten, one;
    				char nextmsg[256];
    				ten = (vms->curmsg + 1) / 10;
    				one = (vms->curmsg + 1) % 10;
    				
    				if (vms->curmsg < 20) {
    					snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
    					res = wait_file2(chan, vms, nextmsg);
    				} else {
    					snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
    					res = wait_file2(chan, vms, nextmsg);
    					if (one > 0) {
    						if (!res) {
    							snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
    							res = wait_file2(chan, vms, nextmsg);
    						}
    					}
    				}
    			}
    
    				res = wait_file2(chan, vms, "vm-message");
    		} else {
    			if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
    				res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
    			else /* DEFAULT syntax */
    				res = wait_file2(chan, vms, "vm-message");
    			if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
    				if (!res)
    					res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
    			}
    
    	/* Retrieve info from VM attribute file */
    
    	make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
    
    	snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
    
    	RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
    
    	msg_cfg = ast_config_load(filename);
    
    	if (!msg_cfg) {
    		ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
    		return 0;
    	}
    
    	if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
    		ast_log(LOG_WARNING, "No origtime?!\n");
    		DISPOSE(vms->curdir, vms->curmsg);
    
    	cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
    
    	duration = ast_variable_retrieve(msg_cfg, "message", "duration");
    
    	category = ast_variable_retrieve(msg_cfg, "message", "category");
    
    	context = ast_variable_retrieve(msg_cfg, "message", "context");
    
    	if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
    
    		context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
    
    	if (!res)
    		res = play_message_category(chan, category);
    
    	if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
    
    		res = play_message_datetime(chan, vmu, origtime, filename);
    
    	if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
    
    		res = play_message_callerid(chan, vms, cid, context, 0);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
    		res = play_message_duration(chan, vms, duration, vmu->saydurationm);
    
    	/* Allow pressing '1' to skip envelope / callerid */
    	if (res == '1')
    		res = 0;
    
    	ast_config_destroy(msg_cfg);
    
    		make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
    		vms->heard[vms->curmsg] = 1;
    
    		res = wait_file(chan, vms, vms->fn);
    	}
    
    	DISPOSE(vms->curdir, vms->curmsg);
    
    static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int use_folder)
    
    	char tmp[256], *t = tmp;
    	size_t left = sizeof(tmp);
    
    
    	if (box == OLD_FOLDER) {
    		ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
    		sprintf(vms->vmbox, "vm-%s", mbox(OLD_FOLDER));
    
    		ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
    
    		snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
    	}
    
    	/* Build up server information */
    	ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
    
    	/* Add authentication user if present */
    	if (!ast_strlen_zero(authuser))
    
    		ast_build_string(&t, &left, "/authuser=%s", authuser);
    
    
    	/* Add flags if present */
    	if (!ast_strlen_zero(imapflags))
    		ast_build_string(&t, &left, "/%s", imapflags);
    
    	/* End with username */
    	ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
    
    
    		sprintf(spec, "%s%s", tmp, use_folder? imapfolder: "INBOX");
    
    	else if (box == GREETINGS_FOLDER)
    		sprintf(spec, "%s%s", tmp, greetingfolder);
    
    	else
    		sprintf(spec, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
    }
    
    static int init_mailstream(struct vm_state *vms, int box)
    {
    	MAILSTREAM *stream = NIL;
    	long debug;
    	char tmp[255];
    	
    	if (!vms) {
    		ast_log (LOG_ERROR,"vm_state is NULL!\n");
    		return -1;
    	}
    
    	ast_debug(3,"vm_state user is:%s\n",vms->imapuser);
    
    	if (vms->mailstream == NIL || !vms->mailstream) {
    
    		ast_debug(1,"mailstream not set.\n");
    
    	} else {
    		stream = vms->mailstream;
    	}
    	/* debug = T;  user wants protocol telemetry? */
    	debug = NIL;  /* NO protocol telemetry? */
    
    	if (delimiter == '\0') {		/* did not probe the server yet */
    		char *cp;
    
    #ifdef USE_SYSTEM_IMAP
    #include <imap/linkage.c>
    #else
    
    		/* Connect to INBOX first to get folders delimiter */
    
    		stream = mail_open(stream, tmp, debug ? OP_DEBUG : NIL);
    
    		if (stream == NIL) {
    			ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
    			return NIL;
    		}
    		get_mailbox_delimiter(stream);
    		/* update delimiter in imapfolder */
    
    		for(cp = imapfolder; *cp; cp++) {
    
    	}
    	/* Now connect to the target folder */
    	imap_mailbox_name(tmp, vms, box, 1);
    
    	ast_debug(3,"Before mail_open, server: %s, box:%d\n", tmp, box);
    
    	vms->mailstream = mail_open(stream, tmp, debug ? OP_DEBUG : NIL);
    
    	if (vms->mailstream == NIL) {
    		return -1;
    	} else {
    		return 0;
    	}
    }
    
    static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
    {
    	SEARCHPGM *pgm;
    	SEARCHHEADER *hdr;
    	int ret;
    	char dbox[256];
    
    	ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
    
    	ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
    
    
    	if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    		ast_log (LOG_ERROR,"Could not initialize mailstream\n");
    
    	/* Check Quota (here for now to test) */
    	mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
    
    	imap_mailbox_name(dbox, vms, box, 1);
    	imap_getquotaroot(vms->mailstream, dbox);
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	pgm = mail_newsearchpgm();
    
    
    	/* Check IMAP folder for Asterisk messages only... */
    
    	hdr = mail_newsearchheader("X-Asterisk-VM-Extension", vmu->mailbox);
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	pgm->header = hdr;
    
    	pgm->deleted = 0;
    	pgm->undeleted = 1;
    
    
    	/* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
    	if (box == NEW_FOLDER) {
    
    		pgm->unseen = 1;
    		pgm->seen = 0;
    
    	ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
    
    	vms->vmArrayIndex = 0;
    	mail_search_full (vms->mailstream, NULL, pgm, NIL);
    
    	vms->lastmsg = vms->vmArrayIndex - 1;
    
    static int imap_remove_file(char *dir, int msgnum)
    {
    	char fn[PATH_MAX];
    	char full_fn[PATH_MAX];
    	char msgnums[80];
    	
    	if (msgnum > -1) {
    		snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
    		make_file(fn, sizeof(fn), dir, msgnum);
    	} else
    		ast_copy_string(fn, dir, sizeof(fn));
    	
    	if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
    		ast_filedelete(fn, NULL);	
    		snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
    		unlink(full_fn);
    	}
    	return 0;
    }
    
    static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context)
    {
    	struct ast_vm_user *vmu;
    	struct vm_state *vms_p;
    	char *file, *filename;
    	char *attachment;
    	/*char *mb, *cur;*/
    	int ret = 0, i;
    	BODY *body;
    
    	/* This function is only used for retrieval of IMAP greetings
    	 * regular messages are not retrieved this way, nor are greetings
    	 * if they are stored locally*/
    	if (msgnum > -1 || !imapgreetings) {
    		return 0;
    	} else {
    		file = strrchr(ast_strdupa(dir), '/');
    		if (file)
    			*file++ = '\0';
    		else {
    			ast_debug (1, "Failed to procure file name from directory passed.\n");
    			return -1;
    		}
    	}
    	/* We have to get the user before we can open the stream! */
    	/* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
    	vmu = find_user(NULL, context, mailbox);
    	if (!vmu) {
    		ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
    		return -1;
    	} else {
    		/* No IMAP account available */
    		if (vmu->imapuser[0] == '\0') {
    			ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
    			free_user(vmu);
    			return -1;
    		}
    	}
    
    	/* check if someone is accessing this box right now... */
    	vms_p = get_vm_state_by_mailbox(mailbox,0);
    	if (!vms_p) {
    		ast_log(LOG_ERROR, "Voicemail state not found!\n");
    		return -1;
    	}
    	
    	ret = init_mailstream(vms_p, GREETINGS_FOLDER);
    	if (!vms_p->mailstream) {
    		ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
    		free_user(vmu);
    		return -1;
    	}
    
    
    	for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
    
    		mail_fetchstructure(vms_p->mailstream, i + 1, &body);
    		/* We have the body, now we extract the file name of the first attachment. */
    		if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
    			attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
    		} else {
    			ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
    			return -1;
    		}
    		filename = strsep(&attachment, ".");
    
    		if (!strcmp(filename, file)) {
    
    			ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
    			vms_p->msgArray[vms_p->curmsg] = i + 1;
    			save_body(body, vms_p, "2", attachment);
    			free_user(vmu);
    			return 0;
    		}
    	}
    
    	free_user(vmu);
    	return -1;
    }
    
    static int imap_delete_old_greeting (char *dir, struct vm_state *vms)