/*!
 * \internal
 * \brief Session supplement callback on an incoming INVITE request
 *
 * If we are receiving an initial INVITE, then we will set the session's identity
 * based on the INVITE or configured endpoint values. If we are receiving a reinvite,
 * then we will potentially queue a connected line update via the \ref update_incoming_connected_line
 * function
 *
 * \param session The session that has received an INVITE
 * \param rdata The incoming INVITE
 */
static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
{
	if (!session->channel) {
		/*
		 * Since we have no channel this must be the initial inbound
		 * INVITE.  Set the session ID directly because the channel
		 * has not been created yet.
		 */
		if (session->endpoint->id.trust_inbound
			&& (!set_id_from_pai(rdata, &session->id)
				|| !set_id_from_rpid(rdata, &session->id))) {
			ast_free(session->id.tag);
			session->id.tag = ast_strdup(session->endpoint->id.self.tag);
			return 0;
		}
		ast_party_id_copy(&session->id, &session->endpoint->id.self);
		if (!session->endpoint->id.self.number.valid) {
			set_id_from_from(rdata, &session->id);
		}
	} else {
		/*
		 * ReINVITE or UPDATE.  Check for changes to the ID and queue
		 * a connected line update if necessary.
		 */
		update_incoming_connected_line(session, rdata);
	}
	return 0;
}
/*!
 * \internal
 * \brief Session supplement callback on an incoming INVITE request
 *
 * If we are receiving an initial INVITE, then we will set the session's identity
 * based on the INVITE or configured endpoint values. If we are receiving a reinvite,
 * then we will potentially queue a connected line update via the \ref update_incoming_connected_line
 * function
 *
 * \param session The session that has received an INVITE
 * \param rdata The incoming INVITE
 */
static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
{
	if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED) {
		/*
		 * Initial inbound INVITE.  Set the session ID directly
		 * because the channel has not been created yet.
		 */
		if (session->endpoint->id.trust_inbound
			&& (!set_id_from_pai(rdata, &session->id)
				|| !set_id_from_rpid(rdata, &session->id))) {
			ast_free(session->id.tag);
			session->id.tag = ast_strdup(session->endpoint->id.self.tag);
			return 0;
		}
		ast_party_id_copy(&session->id, &session->endpoint->id.self);
		if (!session->endpoint->id.self.number.valid) {
			set_id_from_from(rdata, &session->id);
		}
	} else if (session->channel) {
		/* Reinvite. Check for changes to the ID and queue a connected line
		 * update if necessary
		 */
		update_incoming_connected_line(session, rdata);
	}
	return 0;
}
/*!
 * \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);
}
static void copy_redirecting_id(struct ast_party_id *dst, const struct ast_party_id *src,
				struct ast_set_party_id *update)
{
	ast_party_id_copy(dst, src);

	if (dst->number.valid) {
		update->number = 1;
	}

	if (dst->name.valid) {
		update->name = 1;
	}
}
/*!
 * \internal
 * \brief Session supplement callback for outgoing INVITE requests
 *
 * On all INVITEs (initial and reinvite) we may add other identity headers
 * such as P-Asserted-Identity and Remote-Party-ID based on configuration
 * and privacy settings
 *
 * \param session The session on which the INVITE will be sent
 * \param tdata The outbound INVITE request
 */
static void caller_id_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
	struct ast_party_id effective_id;
	struct ast_party_id connected_id;

	if (!session->channel) {
		return;
	}

	ast_party_id_init(&connected_id);
	ast_channel_lock(session->channel);
	effective_id = ast_channel_connected_effective_id(session->channel);
	ast_party_id_copy(&connected_id, &effective_id);
	ast_channel_unlock(session->channel);

	add_id_headers(session, tdata, &connected_id);
	ast_party_id_free(&connected_id);
}
/*!
 * \internal
 * \brief Session supplement for outgoing INVITE response
 *
 * This will add P-Asserted-Identity and Remote-Party-ID headers if necessary
 *
 * \param session The session on which the INVITE response is to be sent
 * \param tdata The outbound INVITE response
 */
static void caller_id_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
	struct ast_party_id effective_id;
	struct ast_party_id connected_id;

	if (!session->channel) {
		return;
	}

	/* Must do a deep copy unless we hold the channel lock the entire time. */
	ast_party_id_init(&connected_id);
	ast_channel_lock(session->channel);
	effective_id = ast_channel_connected_effective_id(session->channel);
	ast_party_id_copy(&connected_id, &effective_id);
	ast_channel_unlock(session->channel);

	add_id_headers(session, tdata, &connected_id);
	ast_party_id_free(&connected_id);
}
/*!
 * \internal
 * \brief Session supplement callback for outgoing INVITE requests
 *
 * For an initial INVITE request, we may change the From header to appropriately
 * reflect the identity information. On all INVITEs (initial and reinvite) we may
 * add other identity headers such as P-Asserted-Identity and Remote-Party-ID based
 * on configuration and privacy settings
 *
 * \param session The session on which the INVITE will be sent
 * \param tdata The outbound INVITE request
 */
static void caller_id_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
	struct ast_party_id effective_id;
	struct ast_party_id connected_id;

	if (!session->channel) {
		return;
	}

	/* Must do a deep copy unless we hold the channel lock the entire time. */
	ast_party_id_init(&connected_id);
	ast_channel_lock(session->channel);
	effective_id = ast_channel_connected_effective_id(session->channel);
	ast_party_id_copy(&connected_id, &effective_id);
	ast_channel_unlock(session->channel);

	if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED) {
		/* Only change the From header on the initial outbound INVITE. Switching it
		 * mid-call might confuse some UAs.
		 */
		pjsip_fromto_hdr *from;
		pjsip_dialog *dlg;

		from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, tdata->msg->hdr.next);
		dlg = session->inv_session->dlg;

		if (ast_strlen_zero(session->endpoint->fromuser)
			&& (session->endpoint->id.trust_outbound
				|| (ast_party_id_presentation(&connected_id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED)) {
			modify_id_header(tdata->pool, from, &connected_id);
			modify_id_header(dlg->pool, dlg->local.info, &connected_id);
		}

		ast_sip_add_usereqphone(session->endpoint, tdata->pool, from->uri);
		ast_sip_add_usereqphone(session->endpoint, dlg->pool, dlg->local.info->uri);
	}
	add_id_headers(session, tdata, &connected_id);
	ast_party_id_free(&connected_id);
}
/*!
 * \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_party_caller caller;

	/* Fill connected line information */
	ast_party_connected_line_init(&connected);
	connected.id = *id;
	connected.id.tag = session->endpoint->id.self.tag;
	connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;

	/* Save to channel driver copy */
	ast_party_id_copy(&session->id, &connected.id);

	/* Update our channel CALLERID() */
	ast_party_caller_init(&caller);
	caller.id = connected.id;
	caller.ani = connected.id;
	caller.ani2 = ast_channel_caller(session->channel)->ani2;
	ast_channel_set_caller_event(session->channel, &caller, NULL);

	/* Tell peer about the new connected line information. */
	ast_channel_queue_connected_line_update(session->channel, &connected, NULL);
}