Merged revisions 307879 via svnmerge from
authorRichard Mudgett <rmudgett@digium.com>
Tue, 15 Feb 2011 16:18:43 +0000 (16:18 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 15 Feb 2011 16:18:43 +0000 (16:18 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.8

........
  r307879 | rmudgett | 2011-02-15 10:13:55 -0600 (Tue, 15 Feb 2011) | 37 lines

  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: https://origsvn.digium.com/svn/asterisk/trunk@307883 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_dahdi.c
channels/chan_sip.c
channels/sig_pri.c
channels/sig_pri.h
include/asterisk/ccss.h
main/ccss.c

index 888a8f2..58e2753 100644 (file)
@@ -16205,7 +16205,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,
index ed4f016..06e23ad 100644 (file)
@@ -1614,7 +1614,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);
@@ -1625,7 +1625,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,
@@ -1726,14 +1726,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;
 }
index 58f6c82..b3117f5 100644 (file)
@@ -8542,38 +8542,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) */
index ae2f97c..1438396 100644 (file)
@@ -591,7 +591,7 @@ void sig_pri_sendtext(struct sig_pri_chan *pchan, const char *text);
 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);
index 31073a4..ae85e04 100644 (file)
@@ -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.
         *
index 45f3671..2cb14d5 100644 (file)
@@ -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.
@@ -2635,6 +2635,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];
@@ -2783,6 +2784,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;
        }
@@ -2821,7 +2824,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",
@@ -2958,15 +2962,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;
@@ -2988,6 +2996,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
@@ -3003,12 +3012,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;