Skip to content
Snippets Groups Projects
channel.c 328 KiB
Newer Older
  • Learn to ignore specific revisions
  • 					/*
    					 * Mark when outgoing channel answered so we can know how
    					 * long the channel has been up.
    					 */
    					set_channel_answer_time(chan);
    
    
    			} else if (f->subclass.integer == AST_CONTROL_READ_ACTION) {
    				read_action_payload = f->data.ptr;
    				switch (read_action_payload->action) {
    				case AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO:
    
    					ast_party_connected_line_init(&connected);
    
    					ast_party_connected_line_copy(&connected, ast_channel_connected(chan));
    
    					if (ast_connected_line_parse_data(read_action_payload->payload,
    
    						read_action_payload->payload_size, &connected)) {
    						ast_party_connected_line_free(&connected);
    
    					if (ast_channel_connected_line_sub(NULL, chan, &connected, 0) &&
    						ast_channel_connected_line_macro(NULL, chan, &connected, 1, 0)) {
    
    						ast_indicate_data(chan, AST_CONTROL_CONNECTED_LINE,
    
    							read_action_payload->payload,
    							read_action_payload->payload_size);
    
    					ast_party_connected_line_free(&connected);
    
    					break;
    				}
    				ast_frfree(f);
    				f = &ast_null_frame;
    
    			send_dtmf_end_event(chan, DTMF_RECEIVED, f->subclass.integer, f->len);
    
    			ast_log(LOG_DTMF, "DTMF end '%c' received on %s, duration %ld ms\n", f->subclass.integer, ast_channel_name(chan), f->len);
    
    			/* Queue it up if DTMF is deferred, or if DTMF emulation is forced. */
    
    			if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF) || ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF)) {
    
    				queue_dtmf_readq(chan, f);
    
    			} else if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF | AST_FLAG_END_DTMF_ONLY)) {
    
    				if (!ast_tvzero(*ast_channel_dtmf_tv(chan)) &&
    
    				    ast_tvdiff_ms(ast_tvnow(), *ast_channel_dtmf_tv(chan)) < AST_MIN_DTMF_GAP) {
    
    					/* If it hasn't been long enough, defer this digit */
    
    					queue_dtmf_readq(chan, f);
    
    					ast_frfree(f);
    					f = &ast_null_frame;
    				} else {
    					/* There was no begin, turn this into a begin and send the end later */
    
    					struct timeval tv = ast_tvnow();
    
    					f->frametype = AST_FRAME_DTMF_BEGIN;
    
    					ast_set_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
    
    					ast_channel_dtmf_digit_to_emulate_set(chan, f->subclass.integer);
    
    					ast_channel_dtmf_tv_set(chan, &tv);
    
    						if (f->len > option_dtmfminduration)
    
    							ast_channel_emulate_dtmf_duration_set(chan, f->len);
    
    							ast_channel_emulate_dtmf_duration_set(chan, option_dtmfminduration);
    
    						ast_channel_emulate_dtmf_duration_set(chan, AST_DEFAULT_EMULATE_DTMF_DURATION);
    					ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %u queued on %s\n", f->subclass.integer, ast_channel_emulate_dtmf_duration(chan), ast_channel_name(chan));
    
    				if (ast_channel_audiohooks(chan)) {
    
    					/*!
    					 * \todo XXX It is possible to write a digit to the audiohook twice
    					 * if the digit was originally read while the channel was in autoservice. */
    
    					f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
    
    				struct timeval now = ast_tvnow();
    
    				if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF)) {
    
    					ast_log(LOG_DTMF, "DTMF end accepted with begin '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    					ast_clear_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF);
    
    						f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
    
    
    					/* detect tones that were received on
    					 * the wire with durations shorter than
    
    					 * option_dtmfminduration and set f->len
    
    					 * to the actual duration of the DTMF
    					 * frames on the wire.  This will cause
    					 * dtmf emulation to be triggered later
    					 * on.
    					 */
    
    					if (ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan)) < option_dtmfminduration) {
    
    						f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
    
    						ast_log(LOG_DTMF, "DTMF end '%c' detected to have actual duration %ld on the wire, emulation will be triggered on %s\n", f->subclass.integer, f->len, ast_channel_name(chan));
    
    				} else if (!f->len) {
    
    					ast_log(LOG_DTMF, "DTMF end accepted without begin '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    				if (f->len < option_dtmfminduration && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY)) {
    
    					ast_log(LOG_DTMF, "DTMF end '%c' has duration %ld but want minimum %u, emulating on %s\n", f->subclass.integer, f->len, option_dtmfminduration, ast_channel_name(chan));
    
    					ast_set_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
    
    					ast_channel_dtmf_digit_to_emulate_set(chan, f->subclass.integer);
    
    					ast_channel_emulate_dtmf_duration_set(chan, option_dtmfminduration - f->len);
    
    					f = &ast_null_frame;
    
    					ast_log(LOG_DTMF, "DTMF end passthrough '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    					if (f->len < option_dtmfminduration) {
    						f->len = option_dtmfminduration;
    
    					ast_channel_dtmf_tv_set(chan, &now);
    
    				if (ast_channel_audiohooks(chan)) {
    
    					f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
    
    			send_dtmf_begin_event(chan, DTMF_RECEIVED, f->subclass.integer);
    
    			ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    			if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY | AST_FLAG_EMULATE_DTMF) ||
    			    (!ast_tvzero(*ast_channel_dtmf_tv(chan)) &&
    
    			      ast_tvdiff_ms(ast_tvnow(), *ast_channel_dtmf_tv(chan)) < AST_MIN_DTMF_GAP) ) {
    
    				ast_log(LOG_DTMF, "DTMF begin ignored '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    				struct timeval now = ast_tvnow();
    
    				ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF);
    
    				ast_channel_dtmf_tv_set(chan, &now);
    
    				ast_log(LOG_DTMF, "DTMF begin passthrough '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    		case AST_FRAME_NULL:
    
    			/* The EMULATE_DTMF flag must be cleared here as opposed to when the duration
    			 * is reached , because we want to make sure we pass at least one
    			 * voice frame through before starting the next digit, to ensure a gap
    			 * between DTMF digits. */
    
    			if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF)) {
    
    				struct timeval now = ast_tvnow();
    
    				if (!ast_channel_emulate_dtmf_duration(chan)) {
    
    					ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
    
    					ast_channel_dtmf_digit_to_emulate_set(chan, 0);
    
    				} else if (ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan)) >= ast_channel_emulate_dtmf_duration(chan)) {
    
    					ast_channel_emulate_dtmf_duration_set(chan, 0);
    
    					ast_frfree(f);
    
    					f = ast_channel_dtmff(chan);
    
    					f->frametype = AST_FRAME_DTMF_END;
    
    					f->subclass.integer = ast_channel_dtmf_digit_to_emulate(chan);
    
    					f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
    					ast_channel_dtmf_tv_set(chan, &now);
    
    					ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
    
    					ast_channel_dtmf_digit_to_emulate_set(chan, 0);
    
    					ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    					if (ast_channel_audiohooks(chan)) {
    
    						struct ast_frame *old_frame = f;
    
    						f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
    
    						if (old_frame != f) {
    							ast_frfree(old_frame);
    						}
    					}
    
    			/* The EMULATE_DTMF flag must be cleared here as opposed to when the duration
    			 * is reached , because we want to make sure we pass at least one
    
    			 * voice frame through before starting the next digit, to ensure a gap
    			 * between DTMF digits. */
    
    			if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF) && !ast_channel_emulate_dtmf_duration(chan)) {
    				ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF);
    
    				ast_channel_dtmf_digit_to_emulate_set(chan, 0);
    
    			if (dropaudio || ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF)) {
    
    				if (dropaudio)
    					ast_read_generator_actions(chan, f);
    
    			if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_EMULATE_DTMF) && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_DTMF)) {
    
    				struct timeval now = ast_tvnow();
    
    				if (ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan)) >= ast_channel_emulate_dtmf_duration(chan)) {
    
    					ast_channel_emulate_dtmf_duration_set(chan, 0);
    
    					ast_frfree(f);
    
    					f = ast_channel_dtmff(chan);
    
    					f->subclass.integer = ast_channel_dtmf_digit_to_emulate(chan);
    
    					f->len = ast_tvdiff_ms(now, *ast_channel_dtmf_tv(chan));
    					ast_channel_dtmf_tv_set(chan, &now);
    
    					if (ast_channel_audiohooks(chan)) {
    
    						f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
    
    					ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass.integer, ast_channel_name(chan));
    
    					/* Drop voice frames while we're still in the middle of the digit */
    
    				break;
    			}
    			if (f->frametype != AST_FRAME_VOICE) {
    				break;
    			}
    			if (ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) != AST_FORMAT_CMP_EQUAL
    				&& ast_format_cmp(f->subclass.format, ast_channel_readformat(chan)) != AST_FORMAT_CMP_EQUAL) {
    				struct ast_format *core_format;
    
    				/*
    				 * Note: This frame may not be one of the current native
    				 * formats.  We may have gotten it out of the read queue from
    				 * a previous multi-frame translation, from a framehook
    				 * injected frame, or the device we're talking to isn't
    				 * respecting negotiated formats.  Regardless we will accept
    				 * all frames.
    				 *
    				 * Update the read translation path to handle the new format
    				 * that just came in.  If the core wants slinear we need to
    				 * setup a new translation path because the core is usually
    				 * doing something with the audio itself and may not handle
    				 * any other format.  e.g., Softmix bridge, holding bridge
    				 * announcer channel, recording, AMD...  Otherwise, we'll
    				 * setup to pass the frame as is to the core.  In this case
    				 * the core doesn't care.  The channel is likely in
    				 * autoservice, safesleep, or the channel is in a bridge.
    				 * Let the bridge technology deal with format compatibility
    				 * between the channels in the bridge.
    				 *
    				 * Beware of the transcode_via_slin and genericplc options as
    				 * they force any transcoding to go through slin on a bridge.
    				 * Unfortunately transcode_via_slin is enabled by default and
    				 * genericplc is enabled in the codecs.conf.sample file.
    				 *
    				 * XXX Only updating translation to slinear frames has some
    				 * corner cases if slinear is one of the native formats and
    				 * there are different sample rates involved.  We might wind
    				 * up with conflicting translation paths between channels
    				 * where the read translation path on this channel reduces
    				 * the sample rate followed by a write translation path on
    				 * the peer channel that increases the sample rate.
    				 */
    				core_format = ast_channel_readformat(chan);
    				if (!ast_format_cache_is_slinear(core_format)) {
    					core_format = f->subclass.format;
    				}
    				if (ast_set_read_format_path(chan, f->subclass.format, core_format)) {
    					/* Drop frame.  We couldn't make it compatible with the core. */
    					ast_frfree(f);
    					f = &ast_null_frame;
    					break;
    
    			/*
    			 * Send frame to audiohooks if present, if frametype is linear, to preserve
    			 * functional compatibility with previous behavior. If not linear, hold off
    			 * until transcoding is done where we are more likely to have a linear frame
    			 */
    			if (ast_channel_audiohooks(chan) && ast_format_cache_is_slinear(f->subclass.format)) {
    				/* Place hooked after declaration */
    
    
    				f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
    				if (old_frame != f) {
    					ast_frfree(old_frame);
    				}
    			}
    
    			if (ast_channel_monitor(chan) && ast_channel_monitor(chan)->read_stream) {
    				/* XXX what does this do ? */
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #ifndef MONITOR_CONSTANT_DELAY
    
    				int jump = ast_channel_outsmpl(chan) - ast_channel_insmpl(chan) - 4 * f->samples;
    				if (jump >= 0) {
    					jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
    						ast_format_get_sample_rate(f->subclass.format),
    						ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
    					if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump, SEEK_FORCECUR) == -1) {
    						ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
    
    					ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + (ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)) + f->samples);
    				} else {
    					ast_channel_insmpl_set(chan, ast_channel_insmpl(chan) + f->samples);
    				}
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #else
    
    				int jump = calc_monitor_jump((ast_channel_outsmpl(chan) - ast_channel_insmpl(chan)),
    
    					ast_format_get_sample_rate(f->subclass.format),
    
    					ast_format_get_sample_rate(ast_channel_monitor(chan)->read_stream->fmt->format));
    				if (jump - MONITOR_DELAY >= 0) {
    					if (ast_seekstream(ast_channel_monitor(chan)->read_stream, jump - f->samples, SEEK_FORCECUR) == -1) {
    						ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
    
    					ast_channel_insmpl(chan) += ast_channel_outsmpl(chan) - ast_channel_insmpl(chan);
    				} else {
    					ast_channel_insmpl(chan) += f->samples;
    
    #endif
    				if (ast_channel_monitor(chan)->state == AST_MONITOR_RUNNING) {
    					if (ast_writestream(ast_channel_monitor(chan)->read_stream, f) < 0)
    						ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
    				}
    			}
    
    			if (ast_channel_readtrans(chan)
    				&& ast_format_cmp(f->subclass.format, ast_channel_rawreadformat(chan)) == AST_FORMAT_CMP_EQUAL) {
    				f = ast_translate(ast_channel_readtrans(chan), f, 1);
    				if (!f) {
    
    					f = &ast_null_frame;
    
    			/* Second chance at hooking a linear frame, also the last chance */
    			if (ast_channel_audiohooks(chan) && !hooked) {
    				struct ast_frame *old_frame = f;
    
    				f = ast_audiohook_write_list(chan, ast_channel_audiohooks(chan), AST_AUDIOHOOK_DIRECTION_READ, f);
    				if (old_frame != f) {
    					ast_frfree(old_frame);
    				}
    			}
    
    
    			/*
    			 * It is possible for the translation process on the channel to have
    			 * produced multiple frames from the single input frame we passed it; if
    			 * this happens, queue the additional frames *before* the frames we may
    			 * have queued earlier. if the readq was empty, put them at the head of
    			 * the queue, and if it was not, put them just after the frame that was
    			 * at the end of the queue.
    			 */
    			if (AST_LIST_NEXT(f, frame_list)) {
    
    				struct ast_frame *cur, *multi_frame = AST_LIST_NEXT(f, frame_list);
    
    				/* Mark these frames as being re-queued */
    				for (cur = multi_frame; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
    					ast_set_flag(cur, AST_FRFLAG_REQUEUED);
    				}
    
    
    					ast_queue_frame_head(chan, multi_frame);
    
    					__ast_queue_frame(chan, multi_frame, 0, readq_tail);
    
    
    			/*
    			 * Run generator sitting on the line if timing device not available
    			 * and synchronous generation of outgoing frames is necessary
    			 */
    			ast_read_generator_actions(chan, f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    	} else {
    		/* Make sure we always return NULL in the future */
    
    		if (!ast_channel_softhangup_internal_flag(chan)) {
    			ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_DEV);
    
    			ast_channel_hangupcause_set(chan, cause);
    		if (ast_channel_generator(chan))
    
    			ast_deactivate_generator(chan);
    
    		/* We no longer End the CDR here */
    
    	/* High bit prints debugging */
    
    	if (ast_channel_fin(chan) & DEBUGCHAN_FLAG)
    
    		ast_frame_dump(ast_channel_name(chan), f, "<<");
    
    	ast_channel_fin_set(chan, FRAMECOUNT_INC(ast_channel_fin(chan)));
    
    	if (ast_channel_music_state(chan) && ast_channel_generator(chan) && ast_channel_generator(chan)->digit && f && f->frametype == AST_FRAME_DTMF_END)
    		ast_channel_generator(chan)->digit(chan, f->subclass.integer);
    
    	if (ast_channel_audiohooks(chan) && ast_audiohook_write_list_empty(ast_channel_audiohooks(chan))) {
    
    		/* The list gets recreated if audiohooks are added again later */
    
    		ast_audiohook_detach_list(ast_channel_audiohooks(chan));
    		ast_channel_audiohooks_set(chan, NULL);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return f;
    }
    
    
    struct ast_frame *ast_read(struct ast_channel *chan)
    {
    	return __ast_read(chan, 0);
    }
    
    struct ast_frame *ast_read_noaudio(struct ast_channel *chan)
    {
    	return __ast_read(chan, 1);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_indicate(struct ast_channel *chan, int condition)
    
    static int attribute_const is_visible_indication(enum ast_control_frame_type condition)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* Don't include a default case here so that we get compiler warnings
    	 * when a new type is added. */
    
    	switch (condition) {
    	case AST_CONTROL_PROGRESS:
    	case AST_CONTROL_PROCEEDING:
    	case AST_CONTROL_VIDUPDATE:
    	case AST_CONTROL_SRCUPDATE:
    
    	case AST_CONTROL_SRCCHANGE:
    
    	case AST_CONTROL_RADIO_KEY:
    	case AST_CONTROL_RADIO_UNKEY:
    	case AST_CONTROL_OPTION:
    	case AST_CONTROL_WINK:
    	case AST_CONTROL_FLASH:
    	case AST_CONTROL_OFFHOOK:
    	case AST_CONTROL_TAKEOFFHOOK:
    	case AST_CONTROL_ANSWER:
    	case AST_CONTROL_HANGUP:
    
    	case AST_CONTROL_CONNECTED_LINE:
    	case AST_CONTROL_REDIRECTING:
    
    	case AST_CONTROL_CC:
    
    	case AST_CONTROL_READ_ACTION:
    
    	case AST_CONTROL_AOC:
    
    	case AST_CONTROL_END_OF_Q:
    
    	case AST_CONTROL_MCID:
    
    	case AST_CONTROL_UPDATE_RTP_PEER:
    
    	case AST_CONTROL_PVT_CAUSE_CODE:
    
    	case AST_CONTROL_MASQUERADE_NOTIFY:
    
    	case AST_CONTROL_STREAM_STOP:
    	case AST_CONTROL_STREAM_SUSPEND:
    	case AST_CONTROL_STREAM_REVERSE:
    	case AST_CONTROL_STREAM_FORWARD:
    	case AST_CONTROL_STREAM_RESTART:
    
    	case AST_CONTROL_RECORD_CANCEL:
    	case AST_CONTROL_RECORD_STOP:
    	case AST_CONTROL_RECORD_SUSPEND:
    	case AST_CONTROL_RECORD_MUTE:
    
    	case AST_CONTROL_INCOMPLETE:
    
    	case AST_CONTROL_CONGESTION:
    	case AST_CONTROL_BUSY:
    	case AST_CONTROL_RINGING:
    	case AST_CONTROL_RING:
    	case AST_CONTROL_HOLD:
    
    		/* You can hear these */
    
    
    	case AST_CONTROL_UNHOLD:
    		/* This is a special case.  You stop hearing this. */
    		break;
    
    void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code, int datalen)
    
    	if (ast_channel_dialed_causes_add(chan, cause_code, datalen)) {
    		ast_log(LOG_WARNING, "Unable to store hangup cause for %s on %s\n", cause_code->chan_name, ast_channel_name(chan));
    	}
    
    
    	if (cause_code->emulate_sip_cause) {
    		snprintf(causevar, sizeof(causevar), "HASH(SIP_CAUSE,%s)", cause_code->chan_name);
    		ast_func_write(chan, causevar, cause_code->code);
    	}
    }
    
    
    enum ama_flags ast_channel_string2amaflag(const char *flag)
    {
    	if (!strcasecmp(flag, "default"))
    		return DEFAULT_AMA_FLAGS;
    	if (!strcasecmp(flag, "omit"))
    		return AST_AMA_OMIT;
    	if (!strcasecmp(flag, "billing"))
    		return AST_AMA_BILLING;
    	if (!strcasecmp(flag, "documentation"))
    		return AST_AMA_DOCUMENTATION;
    	return AST_AMA_NONE;
    }
    
    const char *ast_channel_amaflags2string(enum ama_flags flag)
    {
    	switch (flag) {
    	case AST_AMA_OMIT:
    		return "OMIT";
    	case AST_AMA_BILLING:
    		return "BILLING";
    	case AST_AMA_DOCUMENTATION:
    		return "DOCUMENTATION";
    	default:
    		return "Unknown";
    	}
    }
    
    
    /*!
     * \internal
     * \brief Preprocess connected line update.
     * \since 12.0.0
     *
     * \param chan channel to change the indication
     * \param data pointer to payload data
     * \param datalen size of payload data
     *
     * \note This function assumes chan is locked.
     *
     * \retval 0 keep going.
     * \retval -1 quit now.
     */
    static int indicate_connected_line(struct ast_channel *chan, const void *data, size_t datalen)
    {
    	struct ast_party_connected_line *chan_connected = ast_channel_connected(chan);
    	struct ast_party_connected_line *chan_indicated = ast_channel_connected_indicated(chan);
    	struct ast_party_connected_line connected;
    	unsigned char current[1024];
    	unsigned char proposed[1024];
    	int current_size;
    	int proposed_size;
    	int res;
    
    	ast_party_connected_line_set_init(&connected, chan_connected);
    	res = ast_connected_line_parse_data(data, datalen, &connected);
    	if (!res) {
    		ast_channel_set_connected_line(chan, &connected, NULL);
    	}
    	ast_party_connected_line_free(&connected);
    	if (res) {
    		return -1;
    	}
    
    	current_size = ast_connected_line_build_data(current, sizeof(current),
    		chan_indicated, NULL);
    	proposed_size = ast_connected_line_build_data(proposed, sizeof(proposed),
    		chan_connected, NULL);
    	if (current_size == -1 || proposed_size == -1) {
    		return -1;
    	}
    
    	if (current_size == proposed_size && !memcmp(current, proposed, current_size)) {
    		ast_debug(1, "%s: Dropping redundant connected line update \"%s\" <%s>.\n",
    			ast_channel_name(chan),
    			S_COR(chan_connected->id.name.valid, chan_connected->id.name.str, ""),
    			S_COR(chan_connected->id.number.valid, chan_connected->id.number.str, ""));
    		return -1;
    	}
    
    	ast_party_connected_line_copy(chan_indicated, chan_connected);
    	return 0;
    }
    
    /*!
     * \internal
     * \brief Preprocess redirecting update.
     * \since 12.0.0
     *
     * \param chan channel to change the indication
     * \param data pointer to payload data
     * \param datalen size of payload data
     *
     * \note This function assumes chan is locked.
     *
     * \retval 0 keep going.
     * \retval -1 quit now.
     */
    static int indicate_redirecting(struct ast_channel *chan, const void *data, size_t datalen)
    {
    	struct ast_party_redirecting redirecting;
    	int res;
    
    	ast_party_redirecting_set_init(&redirecting, ast_channel_redirecting(chan));
    	res = ast_redirecting_parse_data(data, datalen, &redirecting);
    	if (!res) {
    		ast_channel_set_redirecting(chan, &redirecting, NULL);
    	}
    	ast_party_redirecting_free(&redirecting);
    	return res ? -1 : 0;
    }
    
    
    static int indicate_data_internal(struct ast_channel *chan, int _condition, const void *data, size_t datalen)
    
    	/* By using an enum, we'll get compiler warnings for values not handled
    
    	 * in switch statements. */
    	enum ast_control_frame_type condition = _condition;
    
    	struct ast_tone_zone_sound *ts = NULL;
    
    	const struct ast_control_t38_parameters *t38_parameters;
    
    	switch (condition) {
    	case AST_CONTROL_CONNECTED_LINE:
    
    		if (indicate_connected_line(chan, data, datalen)) {
    			res = 0;
    
    		if (indicate_redirecting(chan, data, datalen)) {
    			res = 0;
    
    	case AST_CONTROL_HOLD:
    	case AST_CONTROL_UNHOLD:
    
    		ast_channel_hold_state_set(chan, _condition);
    
    	case AST_CONTROL_T38_PARAMETERS:
    		t38_parameters = data;
    		switch (t38_parameters->request_response) {
    		case AST_T38_REQUEST_NEGOTIATE:
    		case AST_T38_NEGOTIATED:
    			ast_channel_set_is_t38_active_nolock(chan, 1);
    			break;
    		case AST_T38_REQUEST_TERMINATE:
    		case AST_T38_TERMINATED:
    		case AST_T38_REFUSED:
    			ast_channel_set_is_t38_active_nolock(chan, 0);
    			break;
    		default:
    			break;
    		}
    		break;
    
    	if (is_visible_indication(condition)) {
    		/* A new visible indication is requested. */
    
    		ast_channel_visible_indication_set(chan, _condition);
    
    	} else if (condition == AST_CONTROL_UNHOLD || _condition < 0) {
    		/* Visible indication is cleared/stopped. */
    
    		ast_channel_visible_indication_set(chan, 0);
    
    	if (ast_channel_tech(chan)->indicate) {
    
    		/* See if the channel driver can handle this condition. */
    
    		res = ast_channel_tech(chan)->indicate(chan, _condition, data, datalen);
    
    		/* The channel driver successfully handled this indication */
    
    	}
    
    	/* The channel driver does not support this indication, let's fake
    	 * it by doing our own tone generation if applicable. */
    
    
    	/*!\note If we compare the enumeration type, which does not have any
    	 * negative constants, the compiler may optimize this code away.
    	 * Therefore, we must perform an integer comparison here. */
    	if (_condition < 0) {
    
    		/* Stop any tones that are playing */
    		ast_playtones_stop(chan);
    
    	}
    
    	/* Handle conditions that we have tones for. */
    	switch (condition) {
    
    	case _XXX_AST_CONTROL_T38:
    		/* deprecated T.38 control frame */
    
    	case AST_CONTROL_T38_PARAMETERS:
    		/* there is no way to provide 'default' behavior for these
    		 * control frames, so we need to return failure, but there
    		 * is also no value in the log message below being emitted
    		 * since failure to handle these frames is not an 'error'
    
    		 * so just return right now. in addition, we want to return
    		 * whatever value the channel driver returned, in case it
    		 * has some meaning.*/
    
    	case AST_CONTROL_RINGING:
    
    		ts = ast_get_indication_tone(ast_channel_zone(chan), "ring");
    
    		/* It is common practice for channel drivers to return -1 if trying
    		 * to indicate ringing on a channel which is up. The idea is to let the
    		 * core generate the ringing inband. However, we don't want the
    		 * warning message about not being able to handle the specific indication
    		 * to print nor do we want ast_indicate_data to return an "error" for this
    		 * condition
    		 */
    
    		if (ast_channel_state(chan) == AST_STATE_UP) {
    
    		break;
    	case AST_CONTROL_BUSY:
    
    		ts = ast_get_indication_tone(ast_channel_zone(chan), "busy");
    
    	case AST_CONTROL_INCOMPLETE:
    
    	case AST_CONTROL_CONGESTION:
    
    		ts = ast_get_indication_tone(ast_channel_zone(chan), "congestion");
    
    	case AST_CONTROL_PVT_CAUSE_CODE:
    
    		ast_channel_hangupcause_hash_set(chan, data, datalen);
    
    	case AST_CONTROL_PROGRESS:
    	case AST_CONTROL_PROCEEDING:
    	case AST_CONTROL_VIDUPDATE:
    	case AST_CONTROL_SRCUPDATE:
    
    	case AST_CONTROL_SRCCHANGE:
    
    	case AST_CONTROL_RADIO_KEY:
    	case AST_CONTROL_RADIO_UNKEY:
    	case AST_CONTROL_OPTION:
    	case AST_CONTROL_WINK:
    	case AST_CONTROL_FLASH:
    	case AST_CONTROL_OFFHOOK:
    	case AST_CONTROL_TAKEOFFHOOK:
    	case AST_CONTROL_ANSWER:
    	case AST_CONTROL_HANGUP:
    	case AST_CONTROL_RING:
    	case AST_CONTROL_HOLD:
    	case AST_CONTROL_UNHOLD:
    
    	case AST_CONTROL_CONNECTED_LINE:
    	case AST_CONTROL_REDIRECTING:
    
    	case AST_CONTROL_CC:
    
    	case AST_CONTROL_READ_ACTION:
    
    	case AST_CONTROL_AOC:
    
    	case AST_CONTROL_END_OF_Q:
    
    	case AST_CONTROL_MCID:
    
    	case AST_CONTROL_MASQUERADE_NOTIFY:
    
    	case AST_CONTROL_UPDATE_RTP_PEER:
    
    	case AST_CONTROL_STREAM_STOP:
    	case AST_CONTROL_STREAM_SUSPEND:
    	case AST_CONTROL_STREAM_REVERSE:
    	case AST_CONTROL_STREAM_FORWARD:
    	case AST_CONTROL_STREAM_RESTART:
    
    	case AST_CONTROL_RECORD_CANCEL:
    	case AST_CONTROL_RECORD_STOP:
    	case AST_CONTROL_RECORD_SUSPEND:
    	case AST_CONTROL_RECORD_MUTE:
    
    		/* Nothing left to do for these. */
    		res = 0;
    		break;
    	}
    
    
    		/* We have a tone to play, yay. */
    
    		ast_debug(1, "Driver for channel '%s' does not support indication %u, emulating it\n", ast_channel_name(chan), condition);
    
    		res = ast_playtones_start(chan, 0, ts->data, 1);
    
    		if (!res) {
    			ast_test_suite_event_notify("RINGING_INBAND",
    					"Channel: %s\r\n",
    					ast_channel_name(chan));
    		}
    
    		ts = ast_tone_zone_sound_unref(ts);
    
    		ast_log(LOG_WARNING, "Unable to handle indication %u for '%s'\n", condition, ast_channel_name(chan));
    
    	return res;
    }
    
    int ast_indicate_data(struct ast_channel *chan, int _condition, const void *data, size_t datalen)
    {
    	int res;
    	/* this frame is used by framehooks. if it is set, we must free it at the end of this function */
    	struct ast_frame *awesome_frame = NULL;
    
    	ast_channel_lock(chan);
    
    	/* Don't bother if the channel is about to go away, anyway. */
    	if ((ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
    			|| (ast_check_hangup(chan) && !ast_channel_is_leaving_bridge(chan)))
    		&& _condition != AST_CONTROL_MASQUERADE_NOTIFY) {
    		res = -1;
    		goto indicate_cleanup;
    	}
    
    	if (!ast_framehook_list_is_empty(ast_channel_framehooks(chan))) {
    		/* Do framehooks now, do it, go, go now */
    		struct ast_frame frame = {
    			.frametype = AST_FRAME_CONTROL,
    			.subclass.integer = _condition,
    			.data.ptr = (void *) data, /* this cast from const is only okay because we do the ast_frdup below */
    			.datalen = datalen
    		};
    
    		/* we have now committed to freeing this frame */
    		awesome_frame = ast_frdup(&frame);
    
    		/* who knows what we will get back! the anticipation is killing me. */
    		awesome_frame = ast_framehook_list_write_event(ast_channel_framehooks(chan),
    			awesome_frame);
    		if (!awesome_frame
    			|| awesome_frame->frametype != AST_FRAME_CONTROL) {
    			res = 0;
    			goto indicate_cleanup;
    		}
    
    		_condition = awesome_frame->subclass.integer;
    		data = awesome_frame->data.ptr;
    		datalen = awesome_frame->datalen;
    	}
    
    	res = indicate_data_internal(chan, _condition, data, datalen);
    
    
    indicate_cleanup:
    
    	ast_channel_unlock(chan);
    
    	if (awesome_frame) {
    		ast_frfree(awesome_frame);
    	}
    
    
    	return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_recvchar(struct ast_channel *chan, int timeout)
    {
    
    	int c;
    	char *buf = ast_recvtext(chan, timeout);
    	if (buf == NULL)
    		return -1;	/* error or timeout */
    	c = *(unsigned char *)buf;
    
    char *ast_recvtext(struct ast_channel *chan, int timeout)
    {
    
    	struct timeval start = ast_tvnow();
    	int ms;
    
    	while ((ms = ast_remaining_ms(start, timeout))) {
    
    		struct ast_frame *f;
    
    
    		if (ast_check_hangup(chan)) {
    
    		}
    		res = ast_waitfor(chan, ms);
    		if (res <= 0)  {/* timeout or error */
    
    		f = ast_read(chan);
    
    		if (f == NULL) {
    
    			break; /* no frame */
    
    		}
    		if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP) {
    			ast_frfree(f);
    			break;
    		} else if (f->frametype == AST_FRAME_TEXT) {		/* what we want */
    
    			buf = ast_strndup((char *) f->data.ptr, f->datalen);	/* dup and break */
    
    			ast_frfree(f);
    			break;
    
    int ast_sendtext(struct ast_channel *chan, const char *text)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res = 0;
    
    
    	ast_channel_lock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
    
    		ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    
    	if (ast_strlen_zero(text)) {
    		ast_channel_unlock(chan);
    		return 0;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	CHECK_BLOCKING(chan);
    
    	if (ast_channel_tech(chan)->write_text && (ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_TEXT))) {
    
    		memset(&f, 0, sizeof(f));
    
    		f.frametype = AST_FRAME_TEXT;
    		f.src = "DIALPLAN";
    		f.mallocd = AST_MALLOCD_DATA;
    		f.datalen = strlen(text);
    		f.data.ptr = ast_strdup(text);
    
    
    		if (f.data.ptr) {
    			res = ast_channel_tech(chan)->write_text(chan, &f);
    			ast_frfree(&f);
    		}
    
    	} else if (ast_channel_tech(chan)->send_text) {
    		res = ast_channel_tech(chan)->send_text(chan, text);
    
    	ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    int ast_senddigit_begin(struct ast_channel *chan, char digit)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* Device does not support DTMF tones, lets fake
    	 * it by doing our own generation. */
    
    	static const char * const dtmf_tones[] = {
    
    		"941+1336", /* 0 */
    		"697+1209", /* 1 */
    		"697+1336", /* 2 */
    		"697+1477", /* 3 */
    		"770+1209", /* 4 */
    		"770+1336", /* 5 */
    		"770+1477", /* 6 */
    		"852+1209", /* 7 */
    		"852+1336", /* 8 */
    		"852+1477", /* 9 */
    		"697+1633", /* A */
    		"770+1633", /* B */
    		"852+1633", /* C */
    		"941+1633", /* D */
    		"941+1209", /* * */
    		"941+1477"  /* # */
    	};
    
    	if (!ast_channel_tech(chan)->send_digit_begin)
    
    	ast_channel_lock(chan);
    	ast_channel_sending_dtmf_digit_set(chan, digit);
    	ast_channel_sending_dtmf_tv_set(chan, ast_tvnow());
    	ast_channel_unlock(chan);
    
    
    	if (!ast_channel_tech(chan)->send_digit_begin(chan, digit))
    
    		return 0;
    
    	if (digit >= '0' && digit <='9')
    		ast_playtones_start(chan, 0, dtmf_tones[digit-'0'], 0);
    	else if (digit >= 'A' && digit <= 'D')
    		ast_playtones_start(chan, 0, dtmf_tones[digit-'A'+10], 0);
    	else if (digit == '*')
    		ast_playtones_start(chan, 0, dtmf_tones[14], 0);
    	else if (digit == '#')
    		ast_playtones_start(chan, 0, dtmf_tones[15], 0);
    	else {
    		/* not handled */
    
    		ast_debug(1, "Unable to generate DTMF tone '%c' for '%s'\n", digit, ast_channel_name(chan));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration)
    
    	if (ast_channel_tech(chan)->send_digit_end)
    		res = ast_channel_tech(chan)->send_digit_end(chan, digit, duration);
    
    	ast_channel_lock(chan);
    	if (ast_channel_sending_dtmf_digit(chan) == digit) {
    		ast_channel_sending_dtmf_digit_set(chan, 0);
    	}
    	ast_channel_unlock(chan);
    
    
    	if (res && ast_channel_generator(chan))
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration)
    
    	if (ast_channel_tech(chan)->send_digit_begin) {
    
    		ast_senddigit_begin(chan, digit);
    
    		ast_safe_sleep(chan, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
    
    	return ast_senddigit_end(chan, digit, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
    
    int ast_prod(struct ast_channel *chan)
    {
    	struct ast_frame a = { AST_FRAME_VOICE };
    	char nothing[128];
    
    	/* Send an empty audio frame to get things moving */
    
    	if (ast_channel_state(chan) != AST_STATE_UP) {
    
    		ast_debug(1, "Prodding channel '%s'\n", ast_channel_name(chan));