No response sent for SIP CC subscribe/resubscribe request.
Asterisk does not send a response if we try to subscribe for call completion after we have received a 180 Ringing. You can only subscribe for call completion when the call has been cleared. When we receive the 180 Ringing, for this call, its call-completion state is 'CC_AVAILABLE'. If we then send a subscribe message to Asterisk, it trys to change the call-completion state to 'CC_CALLER_REQUESTED'. Because this is an invalid state change, it just ignores the message. The only state Asterisk will accept our subscribe message is in the 'CC_CALLER_OFFERED' state. Asterisk will go into the 'CC_CALLER_OFFERED' when the SIP client clears the call by sending a CANCEL. Asterisk should always send a response. Even if its a negative one. The fix is to allow for the CCSS core to notify a CC agent that a failure has occurred when CC is requested. The "ack" callback is replaced with a "respond" callback. The "respond" callback has a parameter indicating either a successful response or a specific type of failure that may need to be communicated to the requester. (closes issue #18336) Reported by: GeorgeKonopacki Tested by: mmichelson, rmudgett JIRA SWP-2633 (closes issue #18337) Reported by: GeorgeKonopacki Tested by: mmichelson JIRA SWP-2634 git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.8@307879 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
a23e973d4d
commit
4700a9a22e
|
@ -16115,7 +16115,7 @@ static struct ast_cc_agent_callbacks dahdi_pri_cc_agent_callbacks = {
|
|||
.init = dahdi_pri_cc_agent_init,
|
||||
.start_offer_timer = sig_pri_cc_agent_start_offer_timer,
|
||||
.stop_offer_timer = sig_pri_cc_agent_stop_offer_timer,
|
||||
.ack = sig_pri_cc_agent_req_ack,
|
||||
.respond = sig_pri_cc_agent_req_rsp,
|
||||
.status_request = sig_pri_cc_agent_status_req,
|
||||
.stop_ringing = sig_pri_cc_agent_stop_ringing,
|
||||
.party_b_free = sig_pri_cc_agent_party_b_free,
|
||||
|
|
|
@ -1601,7 +1601,7 @@ struct ast_channel_tech sip_tech_info;
|
|||
static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan);
|
||||
static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent);
|
||||
static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent);
|
||||
static void sip_cc_agent_ack(struct ast_cc_agent *agent);
|
||||
static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason);
|
||||
static int sip_cc_agent_status_request(struct ast_cc_agent *agent);
|
||||
static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent);
|
||||
static int sip_cc_agent_recall(struct ast_cc_agent *agent);
|
||||
|
@ -1612,7 +1612,7 @@ static struct ast_cc_agent_callbacks sip_cc_agent_callbacks = {
|
|||
.init = sip_cc_agent_init,
|
||||
.start_offer_timer = sip_cc_agent_start_offer_timer,
|
||||
.stop_offer_timer = sip_cc_agent_stop_offer_timer,
|
||||
.ack = sip_cc_agent_ack,
|
||||
.respond = sip_cc_agent_respond,
|
||||
.status_request = sip_cc_agent_status_request,
|
||||
.start_monitoring = sip_cc_agent_start_monitoring,
|
||||
.callee_available = sip_cc_agent_recall,
|
||||
|
@ -1713,14 +1713,30 @@ static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void sip_cc_agent_ack(struct ast_cc_agent *agent)
|
||||
static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason)
|
||||
{
|
||||
struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
|
||||
|
||||
sip_pvt_lock(agent_pvt->subscribe_pvt);
|
||||
ast_set_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
|
||||
transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq);
|
||||
transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED);
|
||||
if (reason == AST_CC_AGENT_RESPONSE_SUCCESS || !ast_strlen_zero(agent_pvt->notify_uri)) {
|
||||
/* The second half of this if statement may be a bit hard to grasp,
|
||||
* so here's an explanation. When a subscription comes into
|
||||
* chan_sip, as long as it is not malformed, it will be passed
|
||||
* to the CC core. If the core senses an out-of-order state transition,
|
||||
* then the core will call this callback with the "reason" set to a
|
||||
* failure condition.
|
||||
* However, an out-of-order state transition will occur during a resubscription
|
||||
* for CC. In such a case, we can see that we have already generated a notify_uri
|
||||
* and so we can detect that this isn't a *real* failure. Rather, it is just
|
||||
* something the core doesn't recognize as a legitimate SIP state transition.
|
||||
* Thus we respond with happiness and flowers.
|
||||
*/
|
||||
transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq);
|
||||
transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED);
|
||||
} else {
|
||||
transmit_response(agent_pvt->subscribe_pvt, "500 Internal Error", &agent_pvt->subscribe_pvt->initreq);
|
||||
}
|
||||
sip_pvt_unlock(agent_pvt->subscribe_pvt);
|
||||
agent_pvt->is_available = TRUE;
|
||||
}
|
||||
|
|
|
@ -7563,38 +7563,75 @@ int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent)
|
|||
|
||||
#if defined(HAVE_PRI_CCSS)
|
||||
/*!
|
||||
* \brief Acknowledge CC request.
|
||||
* \brief Response to a CC request.
|
||||
* \since 1.8
|
||||
*
|
||||
* \param agent CC core agent control.
|
||||
* \param reason CC request response status.
|
||||
*
|
||||
* \details
|
||||
* When the core receives knowledge that a called
|
||||
* party has accepted a CC request, it will call
|
||||
* this callback.
|
||||
* this callback. The core may also call this
|
||||
* if there is some error when attempting to process
|
||||
* the incoming CC request.
|
||||
*
|
||||
* The duty of this is to accept a CC request from
|
||||
* the caller by acknowledging receipt of that request.
|
||||
* The duty of this is to issue a propper response to a
|
||||
* CC request from the caller by acknowledging receipt
|
||||
* of that request or rejecting it.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent)
|
||||
void sig_pri_cc_agent_req_rsp(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason)
|
||||
{
|
||||
struct sig_pri_cc_agent_prv *cc_pvt;
|
||||
int res;
|
||||
int status;
|
||||
const char *failed_msg;
|
||||
static const char *failed_to_send = "Failed to send the CC request response.";
|
||||
static const char *not_accepted = "The core declined the CC request.";
|
||||
|
||||
cc_pvt = agent->private_data;
|
||||
ast_mutex_lock(&cc_pvt->pri->lock);
|
||||
if (cc_pvt->cc_request_response_pending) {
|
||||
cc_pvt->cc_request_response_pending = 0;
|
||||
res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 0/* success */);
|
||||
|
||||
/* Convert core response reason to ISDN response status. */
|
||||
status = 2;/* short_term_denial */
|
||||
switch (reason) {
|
||||
case AST_CC_AGENT_RESPONSE_SUCCESS:
|
||||
status = 0;/* success */
|
||||
break;
|
||||
case AST_CC_AGENT_RESPONSE_FAILURE_INVALID:
|
||||
status = 2;/* short_term_denial */
|
||||
break;
|
||||
case AST_CC_AGENT_RESPONSE_FAILURE_TOO_MANY:
|
||||
status = 5;/* queue_full */
|
||||
break;
|
||||
}
|
||||
|
||||
res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, status);
|
||||
if (!status) {
|
||||
/* CC core request was accepted. */
|
||||
if (res) {
|
||||
failed_msg = failed_to_send;
|
||||
} else {
|
||||
failed_msg = NULL;
|
||||
}
|
||||
} else {
|
||||
/* CC core request was declined. */
|
||||
if (res) {
|
||||
failed_msg = failed_to_send;
|
||||
} else {
|
||||
failed_msg = not_accepted;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = 0;
|
||||
failed_msg = NULL;
|
||||
}
|
||||
ast_mutex_unlock(&cc_pvt->pri->lock);
|
||||
if (res) {
|
||||
ast_cc_failed(agent->core_id, "%s agent failed to send the CC request ack.",
|
||||
sig_pri_cc_type_name);
|
||||
if (failed_msg) {
|
||||
ast_cc_failed(agent->core_id, "%s agent: %s", sig_pri_cc_type_name, failed_msg);
|
||||
}
|
||||
}
|
||||
#endif /* defined(HAVE_PRI_CCSS) */
|
||||
|
|
|
@ -514,7 +514,7 @@ void sig_pri_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, str
|
|||
int sig_pri_cc_agent_init(struct ast_cc_agent *agent, struct sig_pri_chan *pvt_chan);
|
||||
int sig_pri_cc_agent_start_offer_timer(struct ast_cc_agent *agent);
|
||||
int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent);
|
||||
void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent);
|
||||
void sig_pri_cc_agent_req_rsp(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason);
|
||||
int sig_pri_cc_agent_status_req(struct ast_cc_agent *agent);
|
||||
int sig_pri_cc_agent_stop_ringing(struct ast_cc_agent *agent);
|
||||
int sig_pri_cc_agent_party_b_free(struct ast_cc_agent *agent);
|
||||
|
|
|
@ -858,6 +858,15 @@ struct ast_cc_agent {
|
|||
char device_name[1];
|
||||
};
|
||||
|
||||
enum ast_cc_agent_response_reason {
|
||||
/*! CC request accepted */
|
||||
AST_CC_AGENT_RESPONSE_SUCCESS,
|
||||
/*! CC request not allowed at this time. Invalid state transition. */
|
||||
AST_CC_AGENT_RESPONSE_FAILURE_INVALID,
|
||||
/*! Too many CC requests in the system. */
|
||||
AST_CC_AGENT_RESPONSE_FAILURE_TOO_MANY,
|
||||
};
|
||||
|
||||
struct ast_cc_agent_callbacks {
|
||||
/*!
|
||||
* \brief Type of agent the callbacks belong to.
|
||||
|
@ -920,19 +929,23 @@ struct ast_cc_agent_callbacks {
|
|||
*/
|
||||
int (*stop_offer_timer)(struct ast_cc_agent *agent);
|
||||
/*!
|
||||
* \brief Acknowledge CC request.
|
||||
* \brief Respond to a CC request.
|
||||
*
|
||||
* \param agent CC core agent control.
|
||||
* \param reason CC request response status.
|
||||
*
|
||||
* \details
|
||||
* When the core receives knowledge that a called
|
||||
* party has accepted a CC request, it will call
|
||||
* this callback.
|
||||
* this callback. The core may also call this
|
||||
* if there is some error when attempting to process
|
||||
* the incoming CC request.
|
||||
*
|
||||
* The duty of this is to accept a CC request from
|
||||
* the caller by acknowledging receipt of that request.
|
||||
* The duty of this is to issue a propper response to a
|
||||
* CC request from the caller by acknowledging receipt
|
||||
* of that request or rejecting it.
|
||||
*/
|
||||
void (*ack)(struct ast_cc_agent *agent);
|
||||
void (*respond)(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason);
|
||||
/*!
|
||||
* \brief Request the status of the agent's device.
|
||||
*
|
||||
|
|
39
main/ccss.c
39
main/ccss.c
|
@ -2205,7 +2205,7 @@ static void check_callback_sanity(const struct ast_cc_agent_callbacks *callbacks
|
|||
ast_assert(callbacks->init != NULL);
|
||||
ast_assert(callbacks->start_offer_timer != NULL);
|
||||
ast_assert(callbacks->stop_offer_timer != NULL);
|
||||
ast_assert(callbacks->ack != NULL);
|
||||
ast_assert(callbacks->respond != NULL);
|
||||
ast_assert(callbacks->status_request != NULL);
|
||||
ast_assert(callbacks->start_monitoring != NULL);
|
||||
ast_assert(callbacks->callee_available != NULL);
|
||||
|
@ -2267,7 +2267,7 @@ static struct ast_cc_agent *cc_agent_init(struct ast_channel *caller_chan,
|
|||
static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan);
|
||||
static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent);
|
||||
static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent);
|
||||
static void cc_generic_agent_ack(struct ast_cc_agent *agent);
|
||||
static void cc_generic_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason);
|
||||
static int cc_generic_agent_status_request(struct ast_cc_agent *agent);
|
||||
static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent);
|
||||
static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent);
|
||||
|
@ -2279,7 +2279,7 @@ static struct ast_cc_agent_callbacks generic_agent_callbacks = {
|
|||
.init = cc_generic_agent_init,
|
||||
.start_offer_timer = cc_generic_agent_start_offer_timer,
|
||||
.stop_offer_timer = cc_generic_agent_stop_offer_timer,
|
||||
.ack = cc_generic_agent_ack,
|
||||
.respond = cc_generic_agent_respond,
|
||||
.status_request = cc_generic_agent_status_request,
|
||||
.stop_ringing = cc_generic_agent_stop_ringing,
|
||||
.start_monitoring = cc_generic_agent_start_monitoring,
|
||||
|
@ -2403,7 +2403,7 @@ static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cc_generic_agent_ack(struct ast_cc_agent *agent)
|
||||
static void cc_generic_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason)
|
||||
{
|
||||
/* The generic agent doesn't have to do anything special to
|
||||
* acknowledge a CC request. Just return.
|
||||
|
@ -2625,6 +2625,7 @@ static struct cc_core_instance *cc_core_init_instance(struct ast_channel *caller
|
|||
}
|
||||
|
||||
struct cc_state_change_args {
|
||||
struct cc_core_instance *core_instance;/*!< Holds reference to core instance. */
|
||||
enum cc_state state;
|
||||
int core_id;
|
||||
char debug[1];
|
||||
|
@ -2773,6 +2774,8 @@ static int cc_caller_requested(struct cc_core_instance *core_instance, struct cc
|
|||
{
|
||||
if (!ast_cc_request_is_within_limits()) {
|
||||
ast_log(LOG_WARNING, "Cannot request CC since there is no more room for requests\n");
|
||||
core_instance->agent->callbacks->respond(core_instance->agent,
|
||||
AST_CC_AGENT_RESPONSE_FAILURE_TOO_MANY);
|
||||
ast_cc_failed(core_instance->core_id, "Too many requests in the system");
|
||||
return -1;
|
||||
}
|
||||
|
@ -2811,7 +2814,8 @@ static int cc_active(struct cc_core_instance *core_instance, struct cc_state_cha
|
|||
* call monitor's unsuspend callback.
|
||||
*/
|
||||
if (previous_state == CC_CALLER_REQUESTED) {
|
||||
core_instance->agent->callbacks->ack(core_instance->agent);
|
||||
core_instance->agent->callbacks->respond(core_instance->agent,
|
||||
AST_CC_AGENT_RESPONSE_SUCCESS);
|
||||
manager_event(EVENT_FLAG_CC, "CCRequestAcknowledged",
|
||||
"CoreID: %d\r\n"
|
||||
"Caller: %s\r\n",
|
||||
|
@ -2948,15 +2952,19 @@ static int cc_do_state_change(void *datap)
|
|||
ast_log_dynamic_level(cc_logger_level, "Core %d: State change to %d requested. Reason: %s\n",
|
||||
args->core_id, args->state, args->debug);
|
||||
|
||||
if (!(core_instance = find_cc_core_instance(args->core_id))) {
|
||||
ast_log_dynamic_level(cc_logger_level, "Core %d: Unable to find core instance.\n", args->core_id);
|
||||
ast_free(args);
|
||||
return -1;
|
||||
}
|
||||
core_instance = args->core_instance;
|
||||
|
||||
if (!is_state_change_valid(core_instance->current_state, args->state, core_instance->agent)) {
|
||||
ast_log_dynamic_level(cc_logger_level, "Core %d: Invalid state change requested. Cannot go from %s to %s\n",
|
||||
args->core_id, cc_state_to_string(core_instance->current_state), cc_state_to_string(args->state));
|
||||
if (args->state == CC_CALLER_REQUESTED) {
|
||||
/*
|
||||
* For out-of-order requests, we need to let the requester know that
|
||||
* we can't handle the request now.
|
||||
*/
|
||||
core_instance->agent->callbacks->respond(core_instance->agent,
|
||||
AST_CC_AGENT_RESPONSE_FAILURE_INVALID);
|
||||
}
|
||||
ast_free(args);
|
||||
cc_unref(core_instance, "Unref core instance from when it was found earlier");
|
||||
return -1;
|
||||
|
@ -2978,6 +2986,7 @@ static int cc_request_state_change(enum cc_state state, const int core_id, const
|
|||
int debuglen;
|
||||
char dummy[1];
|
||||
va_list aq;
|
||||
struct cc_core_instance *core_instance;
|
||||
struct cc_state_change_args *args;
|
||||
/* This initial call to vsnprintf is simply to find what the
|
||||
* size of the string needs to be
|
||||
|
@ -2993,12 +3002,22 @@ static int cc_request_state_change(enum cc_state state, const int core_id, const
|
|||
return -1;
|
||||
}
|
||||
|
||||
core_instance = find_cc_core_instance(core_id);
|
||||
if (!core_instance) {
|
||||
ast_log_dynamic_level(cc_logger_level, "Core %d: Unable to find core instance.\n",
|
||||
core_id);
|
||||
ast_free(args);
|
||||
return -1;
|
||||
}
|
||||
|
||||
args->core_instance = core_instance;
|
||||
args->state = state;
|
||||
args->core_id = core_id;
|
||||
vsnprintf(args->debug, debuglen, debug, ap);
|
||||
|
||||
res = ast_taskprocessor_push(cc_core_taskprocessor, cc_do_state_change, args);
|
||||
if (res) {
|
||||
cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed");
|
||||
ast_free(args);
|
||||
}
|
||||
return res;
|
||||
|
|
Reference in New Issue