From 19e2a84c89ef7a3f9e192d4e145dd7d80b397f89 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 21 Dec 2010 16:49:30 +0000 Subject: [PATCH] Add call transfer exchange of subaddresses support and fix PTMP call transfer signaling. * Add the ability to exchange subaddresses for ETSI PTMP, ETSI PTP, and Q.SIG for call transfer. * Fix ETSI PTMP to send the correct messages depending on the call state for call transfer. NOTE: Some ISDN phones only handle the NOTIFY message that the EN 300-369 spec says should be sent only if the call has not connected yet. JIRA LIBPRI-47 JIRA SWP-2363 Review: https://reviewboard.asterisk.org/r/1051/ git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@2172 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- pri.c | 111 +++++++++++++++--- pri_facility.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++- pri_facility.h | 2 + pri_internal.h | 2 + q931.c | 159 ++++++++++++++++++++----- 5 files changed, 539 insertions(+), 48 deletions(-) diff --git a/pri.c b/pri.c index a0bfe07..3fee6a0 100644 --- a/pri.c +++ b/pri.c @@ -949,6 +949,9 @@ int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pr { struct q931_party_id party_id; unsigned idx; + unsigned new_name; + unsigned new_number; + unsigned new_subaddress; struct q931_call *subcall; if (!ctrl || !pri_is_call_valid(ctrl, call)) { @@ -957,13 +960,14 @@ int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pr pri_copy_party_id_to_q931(&party_id, &connected->id); q931_party_id_fixup(ctrl, &party_id); - if (!q931_party_id_cmp(&party_id, &call->local_id)) { - /* The local party information did not change so do nothing. */ - return 0; - } - call->local_id = party_id; - /* Update all subcalls with new local_id. */ + new_name = q931_party_name_cmp(&party_id.name, &call->local_id.name); + new_number = q931_party_number_cmp(&party_id.number, &call->local_id.number); + new_subaddress = party_id.subaddress.valid + && q931_party_subaddress_cmp(&party_id.subaddress, &call->local_id.subaddress); + + /* Update the call and all subcalls with new local_id. */ + call->local_id = party_id; if (call->outboundbroadcast && call->master_call == call) { for (idx = 0; idx < ARRAY_LEN(call->subcalls); ++idx) { subcall = call->subcalls[idx]; @@ -982,23 +986,100 @@ int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pr * The local party transferred to someone else before * the remote end answered. */ + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (BRI_NT_PTMP(ctrl)) { + /* + * NT PTMP mode + * + * We should not send these messages to the network if we are + * the CPE side since phones do not transfer calls within + * themselves. Well... If you consider handing the handset to + * someone else a transfer then how is the network to know? + */ + if (new_number) { + q931_notify_redirection(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, + &party_id.number); + } + if (new_subaddress || (party_id.subaddress.valid && new_number)) { + q931_subaddress_transfer(ctrl, call); + } + } else if (PTP_MODE(ctrl)) { + /* PTP mode */ + if (new_number) { + /* Immediately send EctInform APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + } + if (new_subaddress || (party_id.subaddress.valid && new_number)) { + q931_subaddress_transfer(ctrl, call); + } + } + break; + case PRI_SWITCH_QSIG: + if (new_name || new_number) { + /* Immediately send CallTransferComplete APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + } + if (new_subaddress + || (party_id.subaddress.valid && (new_name || new_number))) { + q931_subaddress_transfer(ctrl, call); + } + break; + default: + break; + } + break; case Q931_CALL_STATE_ACTIVE: switch (ctrl->switchtype) { case PRI_SWITCH_EUROISDN_E1: case PRI_SWITCH_EUROISDN_T1: - if (PTMP_MODE(ctrl)) { - /* PTMP mode */ - q931_notify_redirection(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, - &call->local_id.number); - } else { + if (BRI_NT_PTMP(ctrl)) { + /* + * NT PTMP mode + * + * We should not send these messages to the network if we are + * the CPE side since phones do not transfer calls within + * themselves. Well... If you consider handing the handset to + * someone else a transfer then how is the network to know? + */ + if (new_number) { +#if defined(USE_NOTIFY_FOR_ECT) + /* + * Some ISDN phones only handle the NOTIFY message that the + * EN 300-369 spec says should be sent only if the call has not + * connected yet. + */ + q931_notify_redirection(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, + &party_id.number); +#else + q931_request_subaddress(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, + &party_id.number); +#endif /* defined(USE_NOTIFY_FOR_ECT) */ + } + if (new_subaddress || (party_id.subaddress.valid && new_number)) { + q931_subaddress_transfer(ctrl, call); + } + } else if (PTP_MODE(ctrl)) { /* PTP mode */ - /* Immediately send EctInform APDU, callStatus=answered(0) */ - send_call_transfer_complete(ctrl, call, 0); + if (new_number) { + /* Immediately send EctInform APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + } + if (new_subaddress || (party_id.subaddress.valid && new_number)) { + q931_subaddress_transfer(ctrl, call); + } } break; case PRI_SWITCH_QSIG: - /* Immediately send CallTransferComplete APDU, callStatus=answered(0) */ - send_call_transfer_complete(ctrl, call, 0); + if (new_name || new_number) { + /* Immediately send CallTransferComplete APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + } + if (new_subaddress + || (party_id.subaddress.valid && (new_name || new_number))) { + q931_subaddress_transfer(ctrl, call); + } break; default: break; diff --git a/pri_facility.c b/pri_facility.c index f32f8f1..e3d72bd 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -3365,6 +3365,286 @@ int send_call_transfer_complete(struct pri *ctrl, q931_call *call, int call_stat return 0; } +/*! + * \internal + * \brief Encode the ETSI RequestSubaddress invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_request_subaddress(struct pri *ctrl, unsigned char *pos, + unsigned char *end) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_RequestSubaddress; + msg.invoke_id = get_invokeid(ctrl); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \brief Encode and queue the RequestSubaddress invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode message. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_request_subaddress_encode(struct pri *ctrl, struct q931_call *call) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_request_subaddress(ctrl, buffer, buffer + sizeof(buffer)); + break; + case PRI_SWITCH_QSIG: + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode the ETSI SubaddressTransfer invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode message. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_subaddress_transfer(struct pri *ctrl, unsigned char *pos, + unsigned char *end, struct q931_call *call) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_SubaddressTransfer; + msg.invoke_id = get_invokeid(ctrl); + + if (!call->local_id.subaddress.valid) { + return NULL; + } + q931_copy_subaddress_to_rose(ctrl, &msg.args.etsi.SubaddressTransfer.subaddress, + &call->local_id.subaddress); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode the Q.SIG SubaddressTransfer invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode message. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_subaddress_transfer(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct q931_call *call) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_SubaddressTransfer; + msg.invoke_id = get_invokeid(ctrl); + + if (!call->local_id.subaddress.valid) { + return NULL; + } + q931_copy_subaddress_to_rose(ctrl, + &msg.args.qsig.SubaddressTransfer.redirection_subaddress, + &call->local_id.subaddress); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the SubaddressTransfer invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode message. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_subaddress_transfer_encode(struct pri *ctrl, struct q931_call *call) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_subaddress_transfer(ctrl, buffer, buffer + sizeof(buffer), call); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_subaddress_transfer(ctrl, buffer, buffer + sizeof(buffer), call); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \brief Send a FACILITY SubaddressTransfer. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +int send_subaddress_transfer(struct pri *ctrl, struct q931_call *call) +{ + if (rose_subaddress_transfer_encode(ctrl, call) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for subaddress transfer.\n"); + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Handle the received RequestSubaddress facility. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \return Nothing + */ +static void etsi_request_subaddress(struct pri *ctrl, struct q931_call *call) +{ + int changed = 0; + + switch (call->notify) { + case PRI_NOTIFY_TRANSFER_ACTIVE: + if (q931_party_number_cmp(&call->remote_id.number, &call->redirection_number)) { + /* The remote party number information changed. */ + call->remote_id.number = call->redirection_number; + changed = 1; + } + /* Fall through */ + case PRI_NOTIFY_TRANSFER_ALERTING: + if (call->redirection_number.valid + && q931_party_number_cmp(&call->remote_id.number, &call->redirection_number)) { + /* The remote party number information changed. */ + call->remote_id.number = call->redirection_number; + changed = 1; + } + if (call->remote_id.subaddress.valid) { + /* + * Clear the subaddress as the remote party has been changed. + * Any new subaddress will arrive later. + */ + q931_party_subaddress_init(&call->remote_id.subaddress); + changed = 1; + } + if (changed) { + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; + } + break; + default: + break; + } + + /* Send our subaddress back if we have one. */ + if (call->local_id.subaddress.valid) { + send_subaddress_transfer(ctrl, call); + } +} + +/*! + * \internal + * \brief Handle the received SubaddressTransfer facility subaddress. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param subaddr Received subaddress of remote party. + * + * \return Nothing + */ +static void handle_subaddress_transfer(struct pri *ctrl, struct q931_call *call, const struct rosePartySubaddress *subaddr) +{ + int changed = 0; + struct q931_party_subaddress q931_subaddress; + + q931_party_subaddress_init(&q931_subaddress); + rose_copy_subaddress_to_q931(ctrl, &q931_subaddress, subaddr); + if (q931_party_subaddress_cmp(&call->remote_id.subaddress, &q931_subaddress)) { + call->remote_id.subaddress = q931_subaddress; + changed = 1; + } + if (call->redirection_number.valid + && q931_party_number_cmp(&call->remote_id.number, &call->redirection_number)) { + /* The remote party number information changed. */ + call->remote_id.number = call->redirection_number; + changed = 1; + } + if (changed) { + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; + } +} + /*! * \internal * \brief Encode a plain facility ETSI error code. @@ -4382,12 +4662,12 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie } break; case ROSE_ETSI_RequestSubaddress: - /* Ignore since we are not handling subaddresses yet. */ + etsi_request_subaddress(ctrl, call); break; -#if 0 /* Not handled yet */ case ROSE_ETSI_SubaddressTransfer: + handle_subaddress_transfer(ctrl, call, + &invoke->args.etsi.SubaddressTransfer.subaddress); break; -#endif /* Not handled yet */ case ROSE_ETSI_EctLinkIdRequest: if (!ctrl->transfer_support) { send_facility_error(ctrl, call, invoke->invoke_id, @@ -4409,12 +4689,24 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie rose_copy_presented_number_unscreened_to_q931(ctrl, &call->remote_id.number, &invoke->args.etsi.EctInform.redirection); } + + /* + * Clear the subaddress as the remote party has been changed. + * Any new subaddress will arrive later. + */ + q931_party_subaddress_init(&call->remote_id.subaddress); + if (!invoke->args.etsi.EctInform.status) { /* The remote party for the transfer has not answered yet. */ call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; } else { call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } + + /* Send our subaddress back if we have one. */ + if (call->local_id.subaddress.valid) { + send_subaddress_transfer(ctrl, call); + } break; case ROSE_ETSI_EctLoopTest: /* @@ -4826,12 +5118,23 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie &invoke->args.qsig.CallTransferComplete.redirection_name); } + /* + * Clear the subaddress as the remote party has been changed. + * Any new subaddress will arrive later. + */ + q931_party_subaddress_init(&call->remote_id.subaddress); + if (invoke->args.qsig.CallTransferComplete.call_status == 1) { /* The remote party for the transfer has not answered yet. */ call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; } else { call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; } + + /* Send our subaddress back if we have one. */ + if (call->local_id.subaddress.valid) { + send_subaddress_transfer(ctrl, call); + } break; case ROSE_QSIG_CallTransferUpdate: party_id = call->remote_id; @@ -4858,10 +5161,10 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie } } break; -#if 0 /* Not handled yet */ case ROSE_QSIG_SubaddressTransfer: + handle_subaddress_transfer(ctrl, call, + &invoke->args.qsig.SubaddressTransfer.redirection_subaddress); break; -#endif /* Not handled yet */ case ROSE_QSIG_PathReplacement: anfpr_pathreplacement_respond(ctrl, call, ie); break; diff --git a/pri_facility.h b/pri_facility.h index cad52fd..bad6c8a 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -221,6 +221,8 @@ int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const int send_reroute_request(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option); int send_call_transfer_complete(struct pri *pri, q931_call *call, int call_status); +int rose_request_subaddress_encode(struct pri *ctrl, struct q931_call *call); +int send_subaddress_transfer(struct pri *ctrl, struct q931_call *call); int rose_diverting_leg_information1_encode(struct pri *pri, q931_call *call); int rose_diverting_leg_information3_encode(struct pri *pri, q931_call *call, int messagetype); diff --git a/pri_internal.h b/pri_internal.h index 7e820e5..6504c61 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -950,6 +950,8 @@ struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl); struct q931_call *q931_find_link_id_call(struct pri *ctrl, int link_id); struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call); +int q931_request_subaddress(struct pri *ctrl, struct q931_call *call, int notify, const struct q931_party_number *number); +int q931_subaddress_transfer(struct pri *ctrl, struct q931_call *call); int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number); struct pri_cc_record *pri_cc_find_by_reference(struct pri *ctrl, unsigned reference_id); diff --git a/q931.c b/q931.c index 405f2ee..4fd5ad0 100644 --- a/q931.c +++ b/q931.c @@ -935,6 +935,31 @@ int q931_party_id_presentation(const struct q931_party_id *id) return number_value | number_screening; } +/*! + * \brief Find the winning subcall if it exists or current call if not outboundbroadcast. + * + * \param call Starting Q.931 call record of search. + * + * \retval winning-call or given call if not outboundbroadcast. + * \retval NULL if no winning call yet. + */ +struct q931_call *q931_find_winning_call(struct q931_call *call) +{ + struct q931_call *master; + + master = call->master_call; + if (master->outboundbroadcast) { + /* We have potential subcalls. Now get the winning call if declared yet. */ + if (master->pri_winner < 0) { + /* Winner not declared yet.*/ + call = NULL; + } else { + call = master->subcalls[master->pri_winner]; + } + } + return call; +} + /*! * \internal * \brief Append the given ie contents to the save ie location. @@ -4733,6 +4758,88 @@ int q931_facility(struct pri*ctrl, q931_call *c) return send_message(ctrl, c, Q931_FACILITY, facility_ies); } +static int facility_notify_ies[] = { + Q931_IE_FACILITY, + Q931_IE_NOTIFY_IND, + Q931_IE_REDIRECTION_NUMBER, + -1 +}; + +/*! + * \brief Send a FACILITY RequestSubaddress with optional redirection number. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param notify Notification indicator + * \param number Redirection number to send if not NULL. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_request_subaddress(struct pri *ctrl, struct q931_call *call, int notify, const struct q931_party_number *number) +{ + struct q931_call *winner; + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + if (number) { + winner->redirection_number = *number; + } else { + q931_party_number_init(&winner->redirection_number); + } + winner->notify = notify; + if (rose_request_subaddress_encode(ctrl, winner) + || send_message(ctrl, winner, Q931_FACILITY, facility_notify_ies)) { + pri_message(ctrl, + "Could not schedule facility message for request subaddress.\n"); + return -1; + } + + return 0; +} + +/*! + * \brief Send a FACILITY SubaddressTransfer to all parties. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_subaddress_transfer(struct pri *ctrl, struct q931_call *call) +{ + int status; + unsigned idx; + struct q931_call *subcall; + + if (call->outboundbroadcast && call->master_call == call) { + status = 0; + for (idx = 0; idx < ARRAY_LEN(call->subcalls); ++idx) { + subcall = call->subcalls[idx]; + if (subcall) { + /* Send to all subcalls that have given a positive response. */ + switch (subcall->ourcallstate) { + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + if (send_subaddress_transfer(ctrl, subcall)) { + status = -1; + } + break; + default: + break; + } + } + } + } else { + status = send_subaddress_transfer(ctrl, call); + } + return status; +} + static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_IE_REDIRECTION_NUMBER, -1 }; /*! @@ -5493,31 +5600,6 @@ static int q931_release_complete(struct pri *ctrl, q931_call *c, int cause) return res; } -/*! - * \brief Find the winning subcall if it exists or current call if not outboundbroadcast. - * - * \param call Starting Q.931 call record of search. - * - * \retval winning-call or given call if not outboundbroadcast. - * \retval NULL if no winning call yet. - */ -struct q931_call *q931_find_winning_call(struct q931_call *call) -{ - struct q931_call *master; - - master = call->master_call; - if (master->outboundbroadcast) { - /* We have potential subcalls. Now get the winning call if declared yet. */ - if (master->pri_winner < 0) { - /* Winner not declared yet.*/ - call = NULL; - } else { - call = master->subcalls[master->pri_winner]; - } - } - return call; -} - static int connect_ack_ies[] = { -1 }; static int connect_ack_w_chan_id_ies[] = { Q931_CHANNEL_IDENT, -1 }; static int gr303_connect_ack_ies[] = { Q931_CHANNEL_IDENT, -1 }; @@ -6312,6 +6394,8 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca c->ri = -1; break; case Q931_FACILITY: + c->notify = -1; + q931_party_number_init(&c->redirection_number); if (q931_is_dummy_call(c)) { q931_party_address_init(&c->called); } @@ -6412,6 +6496,7 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca case Q931_SETUP_ACKNOWLEDGE: break; case Q931_NOTIFY: + c->notify = -1; q931_party_number_init(&c->redirection_number); break; case Q931_HOLD: @@ -7551,6 +7636,7 @@ static struct q931_call *q931_find_held_call(struct pri *ctrl, struct q931_call static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand) { int res; + int changed; struct apdu_event *cur = NULL; struct pri_subcommand *subcmd; struct q931_call *master_call; @@ -8127,6 +8213,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct return Q931_RES_HAVEEVENT; case Q931_NOTIFY: res = 0; + changed = 0; switch (c->notify) { case PRI_NOTIFY_CALL_DIVERTING: if (c->redirection_number.valid) { @@ -8159,13 +8246,29 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct res = Q931_RES_HAVEEVENT; } break; - case PRI_NOTIFY_TRANSFER_ALERTING: case PRI_NOTIFY_TRANSFER_ACTIVE: + if (q931_party_number_cmp(&c->remote_id.number, &c->redirection_number)) { + /* The remote party number information changed. */ + c->remote_id.number = c->redirection_number; + changed = 1; + } + /* Fall through */ + case PRI_NOTIFY_TRANSFER_ALERTING: if (c->redirection_number.valid && q931_party_number_cmp(&c->remote_id.number, &c->redirection_number)) { - /* The remote party information changed. */ + /* The remote party number information changed. */ c->remote_id.number = c->redirection_number; - + changed = 1; + } + if (c->remote_id.subaddress.valid) { + /* + * Clear the subaddress as the remote party has been changed. + * Any new subaddress will arrive later. + */ + q931_party_subaddress_init(&c->remote_id.subaddress); + changed = 1; + } + if (changed) { /* Setup connected line subcommand */ subcmd = q931_alloc_subcommand(ctrl); if (subcmd) {