/*! \brief Helper function that creates an outgoing channel and returns it immediately */ static struct ast_channel *dial_transfer(struct ast_channel *caller, const char *exten, const char *context) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *chan; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ if (!(chan = ast_request("Local", caller->nativeformats, caller, destination, &cause))) { return NULL; } /* Before we actually dial out let's inherit appropriate information. */ ast_channel_lock_both(caller, chan); ast_connected_line_copy_from_caller(&chan->connected, &caller->caller); ast_channel_inherit_variables(caller, chan); ast_channel_datastore_inherit(caller, chan); ast_channel_unlock(chan); ast_channel_unlock(caller); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(chan, destination, 0)) { ast_hangup(chan); return NULL; } return chan; }
/*! * \internal * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly * identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the * local channel and the channel that instigated the park. */ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *parkee; struct ast_channel *parkee_side_2; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ parkee = ast_request("Local", ast_channel_nativeformats(parker), NULL, parker, destination, &cause); if (!parkee) { return NULL; } /* Before we actually dial out let's inherit appropriate information. */ ast_channel_lock_both(parker, parkee); ast_channel_req_accountcodes(parkee, parker, AST_CHANNEL_REQUESTOR_REPLACEMENT); ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker)); ast_channel_inherit_variables(parker, parkee); ast_channel_datastore_inherit(parker, parkee); ast_channel_unlock(parker); parkee_side_2 = ast_local_get_peer(parkee); ast_assert(parkee_side_2 != NULL); ast_channel_unlock(parkee); /* We need to have the parker subscribe to the new local channel before hand. */ if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) { ast_channel_unref(parkee_side_2); ast_hangup(parkee); return NULL; } ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0); ast_channel_unref(parkee_side_2); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(parkee, destination, 0)) { ast_hangup(parkee); return NULL; } return parkee; }
/*! \brief Initiate new call, part of PBX interface * dest is the dial string */ static int local_call(struct ast_channel *ast, const char *dest, int timeout) { struct local_pvt *p = ast_channel_tech_pvt(ast); int pvt_locked = 0; struct ast_channel *owner = NULL; struct ast_channel *chan = NULL; int res; struct ast_var_t *varptr; struct ast_var_t *clone_var; char *reduced_dest = ast_strdupa(dest); char *slash; const char *exten; const char *context; if (!p) { return -1; } /* since we are letting go of channel locks that were locked coming into * this function, then we need to give the tech pvt a ref */ ao2_ref(p, 1); ast_channel_unlock(ast); awesome_locking(p, &chan, &owner); pvt_locked = 1; if (owner != ast) { res = -1; goto return_cleanup; } if (!owner || !chan) { res = -1; goto return_cleanup; } /* * Note that cid_num and cid_name aren't passed in the ast_channel_alloc * call, so it's done here instead. * * All these failure points just return -1. The individual strings will * be cleared when we destroy the channel. */ ast_party_redirecting_copy(ast_channel_redirecting(chan), ast_channel_redirecting(owner)); ast_party_dialed_copy(ast_channel_dialed(chan), ast_channel_dialed(owner)); ast_connected_line_copy_to_caller(ast_channel_caller(chan), ast_channel_connected(owner)); ast_connected_line_copy_from_caller(ast_channel_connected(chan), ast_channel_caller(owner)); ast_channel_language_set(chan, ast_channel_language(owner)); ast_channel_accountcode_set(chan, ast_channel_accountcode(owner)); ast_channel_musicclass_set(chan, ast_channel_musicclass(owner)); ast_cdr_update(chan); ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params(owner)); /* Make sure we inherit the AST_CAUSE_ANSWERED_ELSEWHERE if it's set on the queue/dial call request in the dialplan */ if (ast_channel_hangupcause(ast) == AST_CAUSE_ANSWERED_ELSEWHERE) { ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE); } /* copy the channel variables from the incoming channel to the outgoing channel */ /* Note that due to certain assumptions, they MUST be in the same order */ AST_LIST_TRAVERSE(ast_channel_varshead(owner), varptr, entries) { clone_var = ast_var_assign(varptr->name, varptr->value); if (clone_var) { AST_LIST_INSERT_TAIL(ast_channel_varshead(chan), clone_var, entries); } }
/*! * \internal * \brief Handle COLP and redirecting conditions. * \since 12.0.0 * * \param p Unreal private structure. * \param ast Channel indicating the condition. * \param condition What is being indicated. * * \retval 0 on success. * \retval -1 on error. */ static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_channel *ast, int condition) { struct ast_channel *my_chan; struct ast_channel *my_owner; struct ast_channel *this_channel; struct ast_channel *the_other_channel; int isoutbound; int res = 0; unsigned char frame_data[1024]; struct ast_frame f = { .frametype = AST_FRAME_CONTROL, .subclass.integer = condition, .data.ptr = frame_data, }; /* * A connected line update frame may only contain a partial * amount of data, such as just a source, or just a ton, and not * the full amount of information. However, the collected * information is all stored in the outgoing channel's * connectedline structure, so when receiving a connected line * update on an outgoing unreal channel, we need to transmit the * collected connected line information instead of whatever * happens to be in this control frame. The same applies for * redirecting information, which is why it is handled here as * well. */ ast_channel_unlock(ast); ast_unreal_lock_all(p, &my_chan, &my_owner); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); if (isoutbound) { this_channel = p->chan; the_other_channel = p->owner; } else { this_channel = p->owner; the_other_channel = p->chan; } if (the_other_channel) { if (condition == AST_CONTROL_CONNECTED_LINE) { ast_connected_line_copy_to_caller(ast_channel_caller(the_other_channel), ast_channel_connected(this_channel)); f.datalen = ast_connected_line_build_data(frame_data, sizeof(frame_data), ast_channel_connected(this_channel), NULL); } else { f.datalen = ast_redirecting_build_data(frame_data, sizeof(frame_data), ast_channel_redirecting(this_channel), NULL); } } if (my_chan) { ast_channel_unlock(my_chan); ast_channel_unref(my_chan); } if (my_owner) { ast_channel_unlock(my_owner); ast_channel_unref(my_owner); } if (the_other_channel) { res = unreal_queue_frame(p, isoutbound, &f, ast, 0); } ao2_unlock(p); ast_channel_lock(ast); return res; } int ast_unreal_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = 0; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ switch (condition) { case AST_CONTROL_CONNECTED_LINE: case AST_CONTROL_REDIRECTING: res = unreal_colp_redirect_indicate(p, ast, condition); break; case AST_CONTROL_HOLD: if (ast_test_flag(p, AST_UNREAL_MOH_INTERCEPT)) { ast_moh_start(ast, data, NULL); break; } res = unreal_queue_indicate(p, ast, condition, data, datalen); break; case AST_CONTROL_UNHOLD: if (ast_test_flag(p, AST_UNREAL_MOH_INTERCEPT)) { ast_moh_stop(ast); break; } res = unreal_queue_indicate(p, ast, condition, data, datalen); break; default: res = unreal_queue_indicate(p, ast, condition, data, datalen); break; } ao2_ref(p, -1); return res; } int ast_unreal_digit_begin(struct ast_channel *ast, char digit) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_DTMF_BEGIN, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.subclass.integer = digit; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } int ast_unreal_digit_end(struct ast_channel *ast, char digit, unsigned int duration) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_DTMF_END, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.subclass.integer = digit; f.len = duration; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } int ast_unreal_sendtext(struct ast_channel *ast, const char *text) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_TEXT, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.data.ptr = (char *) text; f.datalen = strlen(text) + 1; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } int ast_unreal_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) { struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast); int res = -1; struct ast_frame f = { AST_FRAME_HTML, }; int isoutbound; if (!p) { return -1; } ao2_ref(p, 1); /* ref for unreal_queue_frame */ ao2_lock(p); isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p); f.subclass.integer = subclass; f.data.ptr = (char *)data; f.datalen = datalen; res = unreal_queue_frame(p, isoutbound, &f, ast, 0); ao2_unlock(p); ao2_ref(p, -1); return res; } void ast_unreal_call_setup(struct ast_channel *semi1, struct ast_channel *semi2) { struct ast_var_t *varptr; struct ast_var_t *clone_var; /* * Note that cid_num and cid_name aren't passed in the * ast_channel_alloc calls in ast_unreal_new_channels(). It's * done here instead. */ ast_party_redirecting_copy(ast_channel_redirecting(semi2), ast_channel_redirecting(semi1)); ast_party_dialed_copy(ast_channel_dialed(semi2), ast_channel_dialed(semi1)); ast_connected_line_copy_to_caller(ast_channel_caller(semi2), ast_channel_connected(semi1)); ast_connected_line_copy_from_caller(ast_channel_connected(semi2), ast_channel_caller(semi1)); ast_channel_language_set(semi2, ast_channel_language(semi1)); ast_channel_accountcode_set(semi2, ast_channel_accountcode(semi1)); ast_channel_musicclass_set(semi2, ast_channel_musicclass(semi1)); ast_channel_cc_params_init(semi2, ast_channel_get_cc_config_params(semi1)); /* * Make sure we inherit the AST_CAUSE_ANSWERED_ELSEWHERE if it's * set on the queue/dial call request in the dialplan. */ if (ast_channel_hangupcause(semi1) == AST_CAUSE_ANSWERED_ELSEWHERE) { ast_channel_hangupcause_set(semi2, AST_CAUSE_ANSWERED_ELSEWHERE); } /* * Copy the channel variables from the semi1 channel to the * outgoing channel. * * Note that due to certain assumptions, they MUST be in the * same order. */ AST_LIST_TRAVERSE(ast_channel_varshead(semi1), varptr, entries) { clone_var = ast_var_assign(varptr->name, varptr->value); if (clone_var) { AST_LIST_INSERT_TAIL(ast_channel_varshead(semi2), clone_var, entries); } } ast_channel_datastore_inherit(semi1, semi2); }
int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target) { struct ast_party_connected_line connected_caller; struct ast_datastore *ds_pickup; const char *chan_name;/*!< A masquerade changes channel names. */ const char *target_name;/*!< A masquerade changes channel names. */ int res = -1; RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, target_snapshot, NULL, ao2_cleanup); target_name = ast_strdupa(ast_channel_name(target)); ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan)); /* Mark the target to block any call pickup race. */ ds_pickup = ast_datastore_alloc(&pickup_active, NULL); if (!ds_pickup) { ast_log(LOG_WARNING, "Unable to create channel datastore on '%s' for call pickup\n", target_name); return -1; } ast_channel_datastore_add(target, ds_pickup); ast_party_connected_line_init(&connected_caller); ast_party_connected_line_copy(&connected_caller, ast_channel_connected(target)); ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */ /* Reset any earlier private connected id representation */ ast_party_id_reset(&connected_caller.priv); connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; if (ast_channel_connected_line_sub(NULL, chan, &connected_caller, 0) && ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) { ast_channel_update_connected_line(chan, &connected_caller, NULL); } ast_party_connected_line_free(&connected_caller); ast_channel_lock(chan); chan_name = ast_strdupa(ast_channel_name(chan)); ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(chan)); ast_channel_unlock(chan); connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; if (ast_answer(chan)) { ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name); goto pickup_failed; } if (ast_queue_control(chan, AST_CONTROL_ANSWER)) { ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name); goto pickup_failed; } ast_channel_queue_connected_line_update(chan, &connected_caller, NULL); /* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */ ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE); ast_channel_lock(chan); chan_snapshot = ast_channel_snapshot_create(chan); ast_channel_unlock(chan); if (!chan_snapshot) { goto pickup_failed; } target_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(target)); if (!target_snapshot) { goto pickup_failed; } if (ast_channel_move(target, chan)) { ast_log(LOG_WARNING, "Unable to complete call pickup of '%s' with '%s'\n", chan_name, target_name); goto pickup_failed; } /* target points to the channel that did the pickup at this point, so use that channel's topic instead of chan */ send_call_pickup_stasis_message(target, chan_snapshot, target_snapshot); res = 0; pickup_failed: ast_channel_lock(target); if (!ast_channel_datastore_remove(target, ds_pickup)) { ast_datastore_free(ds_pickup); } ast_party_connected_line_free(&connected_caller); return res; }