/*! * \internal * \brief Queue a connected line update on a session's channel. * \param session The session whose channel should have the connected line update queued upon. * \param id The identification information to place in the connected line update */ static void queue_connected_line_update(struct ast_sip_session *session, const struct ast_party_id *id) { struct ast_party_connected_line connected; struct ast_set_party_connected_line update_connected; ast_party_connected_line_init(&connected); ast_party_id_copy(&connected.id, id); memset(&update_connected, 0, sizeof(update_connected)); update_connected.id.number = 1; update_connected.id.name = 1; ast_set_party_id_all(&update_connected.priv); connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; ast_party_id_copy(&session->id, &connected.id); ast_channel_queue_connected_line_update(session->channel, &connected, &update_connected); ast_party_connected_line_free(&connected); }
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; }