Skip to content
Snippets Groups Projects
app_dial.c 127 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			/* When a call is forwarded, we don't want to track new interfaces
    			 * dialed for CC purposes. Setting the done flag will ensure that
    			 * any Dial operations that happen later won't record CC interfaces.
    			 */
    			ast_ignore_cc(o->chan);
    
    			ast_log(LOG_NOTICE, "Not accepting call completion offers from call-forward recipient %s\n", ast_channel_name(o->chan));
    
    			ast_log(LOG_NOTICE,
    				"Forwarding failed to create channel to dial '%s/%s' (cause = %d)\n",
    				tech, stuff, cause);
    
    		ast_channel_publish_dial(in, original, stuff, "BUSY");
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		ast_clear_flag64(o, DIAL_STILLGOING);
    
    		handle_cause(cause, num);
    
    		ast_channel_lock_both(c, original);
    		ast_party_redirecting_copy(ast_channel_redirecting(c),
    			ast_channel_redirecting(original));
    		ast_channel_unlock(c);
    		ast_channel_unlock(original);
    
    		if (single && !caller_entertained && CAN_EARLY_BRIDGE(peerflags, c, in)) {
    
    			ast_rtp_instance_early_bridge_make_compatible(c, in);
    		}
    
    		if (!ast_channel_redirecting(c)->from.number.valid
    			|| ast_strlen_zero(ast_channel_redirecting(c)->from.number.str)) {
    
    			/*
    			 * The call was not previously redirected so it is
    			 * now redirected from this number.
    			 */
    
    			ast_party_number_free(&ast_channel_redirecting(c)->from.number);
    			ast_party_number_init(&ast_channel_redirecting(c)->from.number);
    			ast_channel_redirecting(c)->from.number.valid = 1;
    			ast_channel_redirecting(c)->from.number.str =
    
    				ast_strdup(S_OR(ast_channel_macroexten(in), ast_channel_exten(in)));
    
    		ast_channel_dialed(c)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
    
    		/* Determine CallerID to store in outgoing channel. */
    
    		ast_party_caller_set_init(&caller, ast_channel_caller(c));
    
    		if (ast_test_flag64(peerflags, OPT_ORIGINAL_CLID)) {
    			caller.id = *stored_clid;
    			ast_channel_set_caller_event(c, &caller, NULL);
    
    			ast_set_flag64(o, DIAL_CALLERID_ABSENT);
    
    		} else if (ast_strlen_zero(S_COR(ast_channel_caller(c)->id.number.valid,
    			ast_channel_caller(c)->id.number.str, NULL))) {
    
    			/*
    			 * The new channel has no preset CallerID number by the channel
    			 * driver.  Use the dialplan extension and hint name.
    			 */
    			caller.id = *stored_clid;
    			ast_channel_set_caller_event(c, &caller, NULL);
    
    			ast_set_flag64(o, DIAL_CALLERID_ABSENT);
    		} else {
    			ast_clear_flag64(o, DIAL_CALLERID_ABSENT);
    
    		}
    
    		/* Determine CallerID for outgoing channel to send. */
    
    		if (ast_test_flag64(o, OPT_FORCECLID)) {
    
    			struct ast_party_connected_line connected;
    
    			ast_party_connected_line_init(&connected);
    			connected.id = *forced_clid;
    
    			ast_party_connected_line_copy(ast_channel_connected(c), &connected);
    
    			ast_connected_line_copy_from_caller(ast_channel_connected(c), ast_channel_caller(in));
    
    		ast_channel_req_accountcodes(c, in, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
    
    		ast_channel_appl_set(c, "AppDial");
    		ast_channel_data_set(c, "(Outgoing Line)");
    
    		ast_channel_publish_snapshot(c);
    
    		ast_channel_unlock(in);
    
    		if (single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
    			struct ast_party_redirecting redirecting;
    
    			/*
    			 * Redirecting updates to the caller make sense only on single
    			 * calls.
    			 *
    			 * We must unlock c before calling
    			 * ast_channel_redirecting_macro, because we put c into
    			 * autoservice there.  That is pretty much a guaranteed
    			 * deadlock.  This is why the handling of c's lock may seem a
    			 * bit unusual here.
    			 */
    			ast_party_redirecting_init(&redirecting);
    			ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(c));
    			ast_channel_unlock(c);
    			if (ast_channel_redirecting_sub(c, in, &redirecting, 0) &&
    				ast_channel_redirecting_macro(c, in, &redirecting, 1, 0)) {
    				ast_channel_update_redirecting(in, &redirecting, NULL);
    			}
    			ast_party_redirecting_free(&redirecting);
    		} else {
    			ast_channel_unlock(c);
    		}
    
    		if (ast_test_flag64(peerflags, OPT_CANCEL_TIMEOUT)) {
    			*to = -1;
    		}
    
    		if (ast_call(c, stuff, 0)) {
    			ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
    				tech, stuff);
    
    			ast_channel_publish_dial(in, original, stuff, "CONGESTION");
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			ast_clear_flag64(o, DIAL_STILLGOING);
    
    			c = o->chan = NULL;
    			num->nochan++;
    		} else {
    
    			ast_channel_publish_dial_forward(in, original, c, NULL, "CANCEL",
    
    
    			ast_channel_publish_dial(in, c, stuff, NULL);
    
    			/* Hangup the original channel now, in case we needed it */
    
    		if (single && !caller_entertained) {
    
    			ast_indicate(in, -1);
    		}
    
    /* argument used for some functions. */
    struct privacy_args {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	int sentringing;
    	int privdb_val;
    	char privcid[256];
    	char privintro[1024];
    	char status[256];
    
    static void publish_dial_end_event(struct ast_channel *in, struct dial_head *out_chans, struct ast_channel *exception, const char *status)
    {
    	struct chanlist *outgoing;
    	AST_LIST_TRAVERSE(out_chans, outgoing, node) {
    		if (!outgoing->chan || outgoing->chan == exception) {
    			continue;
    		}
    		ast_channel_publish_dial(in, outgoing->chan, NULL, status);
    	}
    }
    
    
    /*!
     * \internal
     * \brief Update connected line on chan from peer.
     * \since 13.6.0
     *
     * \param chan Channel to get connected line updated.
     * \param peer Channel providing connected line information.
     * \param is_caller Non-zero if chan is the calling channel.
     *
     * \return Nothing
     */
    static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
    {
    	struct ast_party_connected_line connected_caller;
    
    	ast_party_connected_line_init(&connected_caller);
    
    	ast_channel_lock(peer);
    	ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
    	ast_channel_unlock(peer);
    	connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
    	if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
    		&& ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
    		ast_channel_update_connected_line(chan, &connected_caller, NULL);
    	}
    	ast_party_connected_line_free(&connected_caller);
    }
    
    
    /*!
     * \internal
     * \pre chan is locked
     */
    static void set_duration_var(struct ast_channel *chan, const char *var_base, int64_t duration)
    {
    	char buf[32];
    	char full_var_name[128];
    
    	snprintf(buf, sizeof(buf), "%" PRId64, duration / 1000);
    	pbx_builtin_setvar_helper(chan, var_base, buf);
    
    	snprintf(full_var_name, sizeof(full_var_name), "%s_MS", var_base);
    	snprintf(buf, sizeof(buf), "%" PRId64, duration);
    	pbx_builtin_setvar_helper(chan, full_var_name, buf);
    }
    
    
    static struct ast_channel *wait_for_answer(struct ast_channel *in,
    
    	struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
    
    	const struct cause_args *num_in, int *result, char *dtmf_progress,
    
    	const int ignore_cc,
    	struct ast_party_id *forced_clid, struct ast_party_id *stored_clid)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct cause_args num = *num_in;
    	int prestart = num.busy + num.congestion + num.nochan;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int orig = *to;
    	struct ast_channel *peer = NULL;
    
    	struct chanlist *outgoing = AST_LIST_FIRST(out_chans);
    	/* single is set if only one destination is enabled */
    	int single = outgoing && !AST_LIST_NEXT(outgoing, node);
    	int caller_entertained = outgoing
    		&& ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
    
    	struct ast_str *featurecode = ast_str_alloca(AST_FEATURE_MAX_LEN + 1);
    
    	int cc_recall_core_id;
    	int is_cc_recall;
    	int cc_frame_received = 0;
    	int num_ringing = 0;
    
    	int sent_ring = 0;
    	int sent_progress = 0;
    
    	struct timeval start = ast_tvnow();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (single) {
    
    		/* Turn off hold music, etc */
    
    			/* If we are calling a single channel, and not providing ringback or music, */
    			/* then, make them compatible for in-band tone purpose */
    
    			if (ast_channel_make_compatible(in, outgoing->chan) < 0) {
    
    				/* If these channels can not be made compatible,
    
    				 * there is no point in continuing.  The bridge
    				 * will just fail if it gets that far.
    				 */
    				*to = -1;
    				strcpy(pa->status, "CONGESTION");
    
    				ast_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
    
    		if (!ast_test_flag64(outgoing, OPT_IGNORE_CONNECTEDLINE)
    			&& !ast_test_flag64(outgoing, DIAL_CALLERID_ABSENT)) {
    
    			update_connected_line_from_peer(in, outgoing->chan, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL);
    
    
    	while ((*to = ast_remaining_ms(start, orig)) && !peer) {
    
    		struct chanlist *o;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		int pos = 0; /* how many channels do we handle */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		struct ast_channel *winner;
    		struct ast_channel *watchers[AST_MAX_WATCHERS];
    
    
    		AST_LIST_TRAVERSE(out_chans, o, node) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Keep track of important channels */
    
    			if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				watchers[pos++] = o->chan;
    			numlines++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		if (pos == 1) { /* only the input channel is available */
    
    			if (numlines == (num.busy + num.congestion + num.nochan)) {
    
    				ast_verb(2, "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
    
    				if (num.busy)
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					strcpy(pa->status, "BUSY");
    
    				else if (num.congestion)
    
    					strcpy(pa->status, "CONGESTION");
    
    				else if (num.nochan)
    
    					strcpy(pa->status, "CHANUNAVAIL");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    
    				ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*to = 0;
    
    			if (is_cc_recall) {
    				ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		winner = ast_waitfor_n(watchers, pos, to);
    
    		AST_LIST_TRAVERSE(out_chans, o, node) {
    
    			struct ast_frame *f;
    			struct ast_channel *c = o->chan;
    
    			if (c == NULL)
    				continue;
    
    			if (ast_test_flag64(o, DIAL_STILLGOING) && ast_channel_state(c) == AST_STATE_UP) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (!peer) {
    
    					ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
    
    					if (o->orig_chan_name
    						&& strcmp(o->orig_chan_name, ast_channel_name(c))) {
    						/*
    						 * The channel name changed so we must generate COLP update.
    						 * Likely because a call pickup channel masqueraded in.
    						 */
    						update_connected_line_from_peer(in, c, 1);
    					} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
    
    							if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
    								ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
    
    								ast_channel_update_connected_line(in, &o->connected, NULL);
    
    						} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
    
    							update_connected_line_from_peer(in, c, 1);
    
    					if (o->aoc_s_rate_list) {
    						size_t encoded_size;
    						struct ast_aoc_encoded *encoded;
    						if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
    							ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
    							ast_aoc_destroy_encoded(encoded);
    						}
    					}
    
    					publish_dial_end_event(in, out_chans, peer, "CANCEL");
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    						OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
    						OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
    						OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
    						OPT_CALLEE_PARK | OPT_CALLER_PARK |
    						OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
    						DIAL_NOFORWARDHTML);
    
    					ast_channel_dialcontext_set(c, "");
    
    					ast_channel_exten_set(c, "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    			/* here, o->chan == c == winner */
    
    			if (!ast_strlen_zero(ast_channel_call_forward(c))) {
    
    Mark Michelson's avatar
    Mark Michelson committed
    				pa->sentringing = 0;
    
    				if (!ignore_cc && (f = ast_read(c))) {
    					if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_CC) {
    						/* This channel is forwarding the call, and is capable of CC, so
    						 * be sure to add the new device interface to the list
    						 */
    						ast_handle_cc_control_frame(in, c, f->data.ptr);
    					}
    					ast_frfree(f);
    				}
    
    
    				if (o->pending_connected_update) {
    					/*
    					 * Re-seed the chanlist's connected line information with
    					 * previously acquired connected line info from the incoming
    					 * channel.  The previously acquired connected line info could
    					 * have been set through the CONNECTED_LINE dialplan function.
    					 */
    					o->pending_connected_update = 0;
    					ast_channel_lock(in);
    					ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
    					ast_channel_unlock(in);
    				}
    
    
    				do_forward(o, &num, peerflags, single, caller_entertained, &orig,
    
    				if (o->chan) {
    					ast_free(o->orig_chan_name);
    					o->orig_chan_name = ast_strdup(ast_channel_name(o->chan));
    					if (single
    						&& !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)
    						&& !ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
    						update_connected_line_from_peer(in, o->chan, 1);
    					}
    
    				ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
    
    				ast_channel_publish_dial(in, c, NULL, ast_hangup_cause_to_dial_status(ast_channel_hangupcause(c)));
    
    				ast_hangup(c);
    				c = o->chan = NULL;
    
    				ast_clear_flag64(o, DIAL_STILLGOING);
    
    				handle_cause(ast_channel_hangupcause(in), &num);
    
    			switch (f->frametype) {
    			case AST_FRAME_CONTROL:
    
    				switch (f->subclass.integer) {
    
    				case AST_CONTROL_ANSWER:
    					/* This is our guy if someone answered. */
    					if (!peer) {
    
    						ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
    
    						if (o->orig_chan_name
    							&& strcmp(o->orig_chan_name, ast_channel_name(c))) {
    							/*
    							 * The channel name changed so we must generate COLP update.
    							 * Likely because a call pickup channel masqueraded in.
    							 */
    							update_connected_line_from_peer(in, c, 1);
    						} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
    
    								if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
    									ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
    
    									ast_channel_update_connected_line(in, &o->connected, NULL);
    
    							} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
    
    								update_connected_line_from_peer(in, c, 1);
    
    						if (o->aoc_s_rate_list) {
    							size_t encoded_size;
    							struct ast_aoc_encoded *encoded;
    							if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
    								ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
    								ast_aoc_destroy_encoded(encoded);
    							}
    						}
    
    						/* Inform everyone else that they've been canceled.
    						 * The dial end event for the peer will be sent out after
    						 * other Dial options have been handled.
    						 */
    
    						publish_dial_end_event(in, out_chans, peer, "CANCEL");
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    							OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
    							OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
    							OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
    							OPT_CALLEE_PARK | OPT_CALLER_PARK |
    							OPT_CALLEE_MIXMONITOR | OPT_CALLER_MIXMONITOR |
    							DIAL_NOFORWARDHTML);
    
    						ast_channel_dialcontext_set(c, "");
    
    						ast_channel_exten_set(c, "");
    
    						if (CAN_EARLY_BRIDGE(peerflags, in, peer)) {
    
    							/* Setup early bridge if appropriate */
    							ast_channel_early_bridge(in, peer);
    
    					}
    					/* If call has been answered, then the eventual hangup is likely to be normal hangup */
    
    					ast_channel_hangupcause_set(in, AST_CAUSE_NORMAL_CLEARING);
    					ast_channel_hangupcause_set(c, AST_CAUSE_NORMAL_CLEARING);
    
    					ast_verb(3, "%s is busy\n", ast_channel_name(c));
    
    					ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
    
    					ast_channel_publish_dial(in, c, NULL, "BUSY");
    
    					ast_hangup(c);
    					c = o->chan = NULL;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					ast_clear_flag64(o, DIAL_STILLGOING);
    
    					handle_cause(AST_CAUSE_BUSY, &num);
    
    					break;
    				case AST_CONTROL_CONGESTION:
    
    					ast_verb(3, "%s is circuit-busy\n", ast_channel_name(c));
    
    					ast_channel_hangupcause_set(in, ast_channel_hangupcause(c));
    
    					ast_channel_publish_dial(in, c, NULL, "CONGESTION");
    
    					ast_hangup(c);
    					c = o->chan = NULL;
    
    					ast_clear_flag64(o, DIAL_STILLGOING);
    
    					handle_cause(AST_CAUSE_CONGESTION, &num);
    
    					break;
    				case AST_CONTROL_RINGING:
    
    					/* This is a tricky area to get right when using a native
    					 * CC agent. The reason is that we do the best we can to send only a
    					 * single ringing notification to the caller.
    					 *
    					 * Call completion complicates the logic used here. CCNR is typically
    					 * offered during a ringing message. Let's say that party A calls
    					 * parties B, C, and D. B and C do not support CC requests, but D
    					 * does. If we were to receive a ringing notification from B before
    					 * the others, then we would end up sending a ringing message to
    					 * A with no CCNR offer present.
    					 *
    					 * The approach that we have taken is that if we receive a ringing
    					 * response from a party and no CCNR offer is present, we need to
    					 * wait. Specifically, we need to wait until either a) a called party
    					 * offers CCNR in its ringing response or b) all called parties have
    					 * responded in some way to our call and none offers CCNR.
    					 *
    					 * The drawback to this is that if one of the parties has a delayed
    					 * response or, god forbid, one just plain doesn't respond to our
    					 * outgoing call, then this will result in a significant delay between
    					 * when the caller places the call and hears ringback.
    					 *
    					 * Note also that if CC is disabled for this call, then it is perfectly
    					 * fine for ringing frames to get sent through.
    					 */
    					++num_ringing;
    					if (ignore_cc || cc_frame_received || num_ringing == numlines) {
    
    						ast_verb(3, "%s is ringing\n", ast_channel_name(c));
    
    						/* Setup early media if appropriate */
    
    						if (single && !caller_entertained
    							&& CAN_EARLY_BRIDGE(peerflags, in, c)) {
    
    							ast_channel_early_bridge(in, c);
    
    						if (!(pa->sentringing) && !ast_test_flag64(outgoing, OPT_MUSICBACK) && ast_strlen_zero(opt_args[OPT_ARG_RINGBACK])) {
    							ast_indicate(in, AST_CONTROL_RINGING);
    							pa->sentringing++;
    						}
    
    						if (!sent_ring) {
    							struct timeval now, then;
    							int64_t diff;
    
    							now = ast_tvnow();
    
    							ast_channel_lock(in);
    							ast_channel_stage_snapshot(in);
    
    							then = ast_channel_creationtime(c);
    							diff = ast_tvzero(then) ? 0 : ast_tvdiff_ms(now, then);
    							set_duration_var(in, "RINGTIME", diff);
    
    							ast_channel_stage_snapshot_done(in);
    							ast_channel_unlock(in);
    							sent_ring = 1;
    						}
    
    					ast_channel_publish_dial(in, c, NULL, "RINGING");
    
    					break;
    				case AST_CONTROL_PROGRESS:
    
    					ast_verb(3, "%s is making progress passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
    
    					/* Setup early media if appropriate */
    
    					if (single && !caller_entertained
    						&& CAN_EARLY_BRIDGE(peerflags, in, c)) {
    
    					if (!ast_test_flag64(outgoing, OPT_RINGBACK)) {
    
    						if (single || (!single && !pa->sentringing)) {
    							ast_indicate(in, AST_CONTROL_PROGRESS);
    						}
    
    					if (!sent_progress) {
    						struct timeval now, then;
    						int64_t diff;
    
    						now = ast_tvnow();
    
    						ast_channel_lock(in);
    						ast_channel_stage_snapshot(in);
    
    						then = ast_channel_creationtime(c);
    						diff = ast_tvzero(then) ? 0 : ast_tvdiff_ms(now, then);
    						set_duration_var(in, "PROGRESSTIME", diff);
    
    						ast_channel_stage_snapshot_done(in);
    						ast_channel_unlock(in);
    						sent_progress = 1;
    					}
    
    					if (!ast_strlen_zero(dtmf_progress)) {
    						ast_verb(3,
    							"Sending DTMF '%s' to the called party as result of receiving a PROGRESS message.\n",
    							dtmf_progress);
    						ast_dtmf_stream(c, in, dtmf_progress, 250, 0);
    					}
    
    					ast_channel_publish_dial(in, c, NULL, "PROGRESS");
    
    					break;
    				case AST_CONTROL_VIDUPDATE:
    
    				case AST_CONTROL_SRCUPDATE:
    
    				case AST_CONTROL_SRCCHANGE:
    					if (!single || caller_entertained) {
    						break;
    					}
    					ast_verb(3, "%s requested media update control %d, passing it to %s\n",
    						ast_channel_name(c), f->subclass.integer, ast_channel_name(in));
    					ast_indicate(in, f->subclass.integer);
    
    					if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
    
    						ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(in));
    
    						struct ast_party_connected_line connected;
    
    
    						ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n",
    							ast_channel_name(c), ast_channel_name(in));
    
    						ast_party_connected_line_set_init(&connected, &o->connected);
    						ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
    
    						ast_party_connected_line_set(&o->connected, &connected, NULL);
    
    						ast_party_connected_line_free(&connected);
    
    						break;
    					}
    					if (ast_channel_connected_line_sub(c, in, f, 1) &&
    						ast_channel_connected_line_macro(c, in, f, 1, 1)) {
    						ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
    
    				case AST_CONTROL_AOC:
    					{
    						struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
    						if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
    							ast_aoc_destroy_decoded(o->aoc_s_rate_list);
    							o->aoc_s_rate_list = decoded;
    						} else {
    							ast_aoc_destroy_decoded(decoded);
    						}
    					}
    					break;
    
    					if (!single) {
    						/*
    						 * Redirecting updates to the caller make sense only on single
    						 * calls.
    						 */
    						break;
    					}
    					if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
    
    						ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(in));
    
    					ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
    						ast_channel_name(c), ast_channel_name(in));
    					if (ast_channel_redirecting_sub(c, in, f, 1) &&
    						ast_channel_redirecting_macro(c, in, f, 1, 1)) {
    						ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
    					}
    					pa->sentringing = 0;
    
    				case AST_CONTROL_PROCEEDING:
    
    					ast_verb(3, "%s is proceeding passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
    
    					if (single && !caller_entertained
    						&& CAN_EARLY_BRIDGE(peerflags, in, c)) {
    
    					if (!ast_test_flag64(outgoing, OPT_RINGBACK))
    
    						ast_indicate(in, AST_CONTROL_PROCEEDING);
    
    					ast_channel_publish_dial(in, c, NULL, "PROCEEDING");
    
    					/* XXX this should be saved like AST_CONTROL_CONNECTED_LINE for !single || caller_entertained */
    
    					ast_verb(3, "Call on %s placed on hold\n", ast_channel_name(c));
    
    					ast_indicate_data(in, AST_CONTROL_HOLD, f->data.ptr, f->datalen);
    
    					break;
    				case AST_CONTROL_UNHOLD:
    
    					/* XXX this should be saved like AST_CONTROL_CONNECTED_LINE for !single || caller_entertained */
    
    					ast_verb(3, "Call on %s left from hold\n", ast_channel_name(c));
    
    					ast_indicate(in, AST_CONTROL_UNHOLD);
    					break;
    				case AST_CONTROL_OFFHOOK:
    				case AST_CONTROL_FLASH:
    					/* Ignore going off hook and flash */
    					break;
    
    				case AST_CONTROL_CC:
    					if (!ignore_cc) {
    						ast_handle_cc_control_frame(in, c, f->data.ptr);
    						cc_frame_received = 1;
    					}
    					break;
    
    				case AST_CONTROL_PVT_CAUSE_CODE:
    					ast_indicate_data(in, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen);
    					break;
    
    						ast_verb(3, "%s stopped sounds\n", ast_channel_name(c));
    
    					ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
    
    			case AST_FRAME_VIDEO:
    
    			case AST_FRAME_VOICE:
    			case AST_FRAME_IMAGE:
    				if (caller_entertained) {
    
    				/* Fall through */
    			case AST_FRAME_TEXT:
    				if (single && ast_write(in, f)) {
    
    					ast_log(LOG_WARNING, "Unable to write frametype: %u\n",
    
    						f->frametype);
    				}
    				break;
    			case AST_FRAME_HTML:
    				if (single && !ast_test_flag64(outgoing, DIAL_NOFORWARDHTML)
    					&& ast_channel_sendhtml(in, f->subclass.integer, f->data.ptr, f->datalen) == -1) {
    					ast_log(LOG_WARNING, "Unable to send URL\n");
    				}
    				break;
    			default:
    				break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (winner == in) {
    
    			struct ast_frame *f = ast_read(in);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #if 0
    			if (f && (f->frametype != AST_FRAME_VOICE))
    
    				printf("Frame type: %d, %d\n", f->frametype, f->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else if (!f || (f->frametype != AST_FRAME_VOICE))
    				printf("Hangup received on %s\n", in->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    			if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Got hung up */
    
    Olle Johansson's avatar
    Olle Johansson committed
    				*to = -1;
    
    				strcpy(pa->status, "CANCEL");
    
    				publish_dial_end_event(in, out_chans, NULL, pa->status);
    
    						ast_channel_hangupcause_set(in, f->data.uint32);
    
    				if (is_cc_recall) {
    					ast_cc_completed(in, "CC completed, although the caller hung up (cancelled)");
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			/* now f is guaranteed non-NULL */
    			if (f->frametype == AST_FRAME_DTMF) {
    
    				if (ast_test_flag64(peerflags, OPT_DTMF_EXIT)) {
    
    					const char *context;
    					ast_channel_lock(in);
    					context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
    
    					if (onedigit_goto(in, context, (char) f->subclass.integer, 1)) {
    						ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
    
    						*result = f->subclass.integer;
    
    						strcpy(pa->status, "CANCEL");
    
    						publish_dial_end_event(in, out_chans, NULL, pa->status);
    
    						if (is_cc_recall) {
    							ast_cc_completed(in, "CC completed, but the caller used DTMF to exit");
    						}
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    				if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
    
    					detect_disconnect(in, f->subclass.integer, &featurecode)) {
    
    					ast_verb(3, "User requested call disconnect.\n");
    
    					strcpy(pa->status, "CANCEL");
    
    					publish_dial_end_event(in, out_chans, NULL, pa->status);
    
    					if (is_cc_recall) {
    						ast_cc_completed(in, "CC completed, but the caller hung up with DTMF");
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			/* Send the frame from the in channel to all outgoing channels. */
    
    			AST_LIST_TRAVERSE(out_chans, o, node) {
    
    				if (!o->chan || !ast_test_flag64(o, DIAL_STILLGOING)) {
    					/* This outgoing channel has died so don't send the frame to it. */
    					continue;
    				}
    
    				switch (f->frametype) {
    				case AST_FRAME_HTML:
    					/* Forward HTML stuff */
    
    					if (!ast_test_flag64(o, DIAL_NOFORWARDHTML)
    						&& ast_channel_sendhtml(o->chan, f->subclass.integer, f->data.ptr, f->datalen) == -1) {
    
    						ast_log(LOG_WARNING, "Unable to send URL\n");
    
    				case AST_FRAME_VIDEO:
    
    				case AST_FRAME_VOICE:
    				case AST_FRAME_IMAGE:
    
    					if (!single || caller_entertained) {
    						/*
    						 * We are calling multiple parties or caller is being
    						 * entertained and has thus not been made compatible.
    						 * No need to check any other called parties.
    						 */
    						goto skip_frame;
    					}
    					/* Fall through */
    
    				case AST_FRAME_TEXT:
    				case AST_FRAME_DTMF_BEGIN:
    				case AST_FRAME_DTMF_END:
    
    					if (ast_write(o->chan, f)) {
    
    						ast_log(LOG_WARNING, "Unable to forward frametype: %u\n",
    
    							f->frametype);
    					}
    					break;
    				case AST_FRAME_CONTROL:
    					switch (f->subclass.integer) {
    					case AST_CONTROL_HOLD:
    
    						ast_verb(3, "Call on %s placed on hold\n", ast_channel_name(o->chan));
    						ast_indicate_data(o->chan, AST_CONTROL_HOLD, f->data.ptr, f->datalen);
    						break;
    
    						ast_verb(3, "Call on %s left from hold\n", ast_channel_name(o->chan));
    						ast_indicate(o->chan, AST_CONTROL_UNHOLD);
    						break;
    
    					case AST_CONTROL_VIDUPDATE:
    					case AST_CONTROL_SRCUPDATE:
    
    					case AST_CONTROL_SRCCHANGE:
    						if (!single || caller_entertained) {
    							/*
    							 * We are calling multiple parties or caller is being
    							 * entertained and has thus not been made compatible.
    							 * No need to check any other called parties.
    							 */
    							goto skip_frame;
    						}
    						ast_verb(3, "%s requested media update control %d, passing it to %s\n",
    
    							ast_channel_name(in), f->subclass.integer, ast_channel_name(o->chan));
    
    						ast_indicate(o->chan, f->subclass.integer);
    
    						break;
    					case AST_CONTROL_CONNECTED_LINE:
    
    						if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
    							ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
    							break;
    						}
    
    						if (ast_channel_connected_line_sub(in, o->chan, f, 1) &&
    							ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
    
    							ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
    
    						if (ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
    							ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
    							break;
    						}
    
    						if (ast_channel_redirecting_sub(in, o->chan, f, 1) &&
    							ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
    
    							ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
    
    						/* We are not going to do anything with this frame. */
    						goto skip_frame;
    
    					/* We are not going to do anything with this frame. */
    					goto skip_frame;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    	if (!*to || ast_check_hangup(in)) {
    
    		ast_verb(3, "Nobody picked up in %d ms\n", orig);
    
    		publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
    
    	if (is_cc_recall) {
    		ast_cc_completed(in, "Recall completed!");
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return peer;
    
    static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode)
    
    	char disconnect_code[AST_FEATURE_MAX_LEN];
    
    	ast_str_append(featurecode, 1, "%c", code);
    
    	res = ast_get_builtin_feature(chan, "disconnect", disconnect_code, sizeof(disconnect_code));
    	if (res) {
    
    		ast_str_reset(*featurecode);
    
    
    	if (strlen(disconnect_code) > ast_str_strlen(*featurecode)) {
    		/* Could be a partial match, anyway */
    		if (strncmp(disconnect_code, ast_str_buffer(*featurecode), ast_str_strlen(*featurecode))) {
    			ast_str_reset(*featurecode);
    		}
    		return 0;
    
    	if (strcmp(disconnect_code, ast_str_buffer(*featurecode))) {
    		ast_str_reset(*featurecode);
    		return 0;
    	}
    
    	return 1;
    
    /* returns true if there is a valid privacy reply */
    
    static int valid_priv_reply(struct ast_flags64 *opts, int res)
    
    	if (ast_test_flag64(opts, OPT_PRIVACY) && res <= '5')
    
    	if (ast_test_flag64(opts, OPT_SCREENING) && res <= '4')
    
    static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	struct ast_flags64 *opts, char **opt_args, struct privacy_args *pa)
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	/* Get the user's intro, store it in priv-callerintros/$CID,
    	   unless it is already there-- this should be done before the
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	/* all ring indications and moh for the caller has been halted as soon as the
    
    	   target extension was picked up. We are going to have to kill some
    	   time and make the caller believe the peer hasn't picked up yet */
    
    
    	if (ast_test_flag64(opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
    
    		char *original_moh = ast_strdupa(ast_channel_musicclass(chan));
    
    		ast_channel_musicclass_set(chan, opt_args[OPT_ARG_MUSICBACK]);
    
    		ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
    
    		ast_channel_musicclass_set(chan, original_moh);
    
    	} else if (ast_test_flag64(opts, OPT_RINGBACK) || ast_test_flag64(opts, OPT_RING_WITH_EARLY_MEDIA)) {
    
    		ast_indicate(chan, AST_CONTROL_RINGING);
    		pa->sentringing++;
    	}
    
    	/* Start autoservice on the other chan ?? */
    	res2 = ast_autoservice_start(chan);
    	/* Now Stream the File */
    	for (loopcount = 0; loopcount < 3; loopcount++) {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		if (res2 && loopcount == 0) /* error in ast_autoservice_start() */
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		if (!res2) /* on timeout, play the message again */
    
    			res2 = ast_play_and_wait(peer, "priv-callpending");
    
    		if (!valid_priv_reply(opts, res2))
    			res2 = 0;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		/* priv-callpending script:
    
    		   "I have a caller waiting, who introduces themselves as:"
    		*/
    		if (!res2)
    			res2 = ast_play_and_wait(peer, pa->privintro);
    		if (!valid_priv_reply(opts, res2))
    			res2 = 0;
    		/* now get input from the called party, as to their choice */
    
    			/* XXX can we have both, or they are mutually exclusive ? */
    
    			if (ast_test_flag64(opts, OPT_PRIVACY))
    				res2 = ast_play_and_wait(peer, "priv-callee-options");
    			if (ast_test_flag64(opts, OPT_SCREENING))
    				res2 = ast_play_and_wait(peer, "screen-callee-options");
    
    Andrew Latham's avatar
    Andrew Latham committed
    
    
    		/*! \page DialPrivacy Dial Privacy scripts
    
    Andrew Latham's avatar
    Andrew Latham committed
    		 * \par priv-callee-options script:
    
    		 * \li Dial 1 if you wish this caller to reach you directly in the future,
    
    Andrew Latham's avatar
    Andrew Latham committed
    		 * 	and immediately connect to their incoming call.
    		 * \li Dial 2 if you wish to send this caller to voicemail now and forevermore.
    		 * \li Dial 3 to send this caller to the torture menus, now and forevermore.
    		 * \li Dial 4 to send this caller to a simple "go away" menu, now and forevermore.
    		 * \li Dial 5 to allow this caller to come straight thru to you in the future,
    		 * 	but right now, just this once, send them to voicemail.
    		 *
    		 * \par screen-callee-options script:
    		 * \li Dial 1 if you wish to immediately connect to the incoming call
    		 * \li Dial 2 if you wish to send this caller to voicemail.
    		 * \li Dial 3 to send this caller to the torture menus.
    		 * \li Dial 4 to send this caller to a simple "go away" menu.
    		 */
    
    		if (valid_priv_reply(opts, res2))
    			break;
    		/* invalid option */
    		res2 = ast_play_and_wait(peer, "vm-sorry");
    	}
    
    
    	if (ast_test_flag64(opts, OPT_MUSICBACK)) {
    
    	} else if (ast_test_flag64(opts, OPT_RINGBACK) || ast_test_flag64(opts, OPT_RING_WITH_EARLY_MEDIA)) {
    
    	if (ast_test_flag64(opts, OPT_PRIVACY) && (res2 >= '1' && res2 <= '5')) {
    
    		/* map keypresses to various things, the index is res2 - '1' */
    
    		static const char * const _val[] = { "ALLOW", "DENY", "TORTURE", "KILL", "ALLOW" };
    
    		static const int _flag[] = { AST_PRIVACY_ALLOW, AST_PRIVACY_DENY, AST_PRIVACY_TORTURE, AST_PRIVACY_KILL, AST_PRIVACY_ALLOW};
    		int i = res2 - '1';
    
    		ast_verb(3, "--Set privacy database entry %s/%s to %s\n",
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			opt_args[OPT_ARG_PRIVACY], pa->privcid, _val[i]);
    
    		ast_privacy_set(opt_args[OPT_ARG_PRIVACY], pa->privcid, _flag[i]);
    	}
    	switch (res2) {
    	case '1':
    		break;
    	case '2':
    		ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
    		break;
    	case '3':
    		ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
    		break;
    	case '4':
    		ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
    		break;
    	case '5':
    
    		if (ast_test_flag64(opts, OPT_PRIVACY)) {
    			ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
    
    		/* if not privacy, then 5 is the same as "default" case */