Merged revisions 100740 via svnmerge from
[asterisk/asterisk.git] / channels / chan_sip.c
index ea3a27c..9f2cc3d 100644 (file)
@@ -964,6 +964,7 @@ struct sip_auth {
 #define SIP_PAGE2_RTCACHEFRIENDS       (1 << 0)        /*!< GP: Should we keep RT objects in memory for extended time? */
 #define SIP_PAGE2_RTAUTOCLEAR          (1 << 2)        /*!< GP: Should we clean memory from peers after expiry? */
 /* Space for addition of other realtime flags in the future */
 #define SIP_PAGE2_RTCACHEFRIENDS       (1 << 0)        /*!< GP: Should we keep RT objects in memory for extended time? */
 #define SIP_PAGE2_RTAUTOCLEAR          (1 << 2)        /*!< GP: Should we clean memory from peers after expiry? */
 /* Space for addition of other realtime flags in the future */
+#define SIP_PAGE2_STATECHANGEQUEUE     (1 << 9)        /*!< D: Unsent state pending change exists */
 
 #define SIP_PAGE2_VIDEOSUPPORT         (1 << 14)       /*!< DP: Video supported if offered? */
 #define SIP_PAGE2_TEXTSUPPORT          (1 << 15)       /*!< GDP: Global text enable */
 
 #define SIP_PAGE2_VIDEOSUPPORT         (1 << 14)       /*!< DP: Video supported if offered? */
 #define SIP_PAGE2_TEXTSUPPORT          (1 << 15)       /*!< GDP: Global text enable */
@@ -1262,7 +1263,7 @@ struct sip_pvt {
        int noncecount;                         /*!< Nonce-count */
        char lastmsg[256];                      /*!< Last Message sent/received */
        int amaflags;                           /*!< AMA Flags */
        int noncecount;                         /*!< Nonce-count */
        char lastmsg[256];                      /*!< Last Message sent/received */
        int amaflags;                           /*!< AMA Flags */
-       int pendinginvite;                      /*!< Any pending invite ? (seqno of this) */
+       int pendinginvite;                      /*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */
        struct sip_request initreq;             /*!< Latest request that opened a new transaction
                                                        within this dialog.
                                                        NOT the request that opened the dialog
        struct sip_request initreq;             /*!< Latest request that opened a new transaction
                                                        within this dialog.
                                                        NOT the request that opened the dialog
@@ -8752,6 +8753,8 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim
        add_header_contentLength(&req, tmp->used);
        add_line(&req, tmp->str);
 
        add_header_contentLength(&req, tmp->used);
        add_line(&req, tmp->str);
 
+       p->pendinginvite = p->ocseq;    /* Remember that we have a pending NOTIFY in order not to confuse the NOTIFY subsystem */
+
        return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
 }
 
        return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
 }
 
@@ -9992,10 +9995,17 @@ static int cb_extensionstate(char *context, char* exten, int state, void *data)
                p->laststate = state;
                break;
        }
                p->laststate = state;
                break;
        }
-       if (p->subscribed != NONE)      /* Only send state NOTIFY if we know the format */
-               transmit_state_notify(p, state, 1, FALSE);
-
-       ast_verb(2, "Extension Changed %s new state %s for Notify User %s\n", exten, ast_extension_state2str(state), p->username);
+       if (p->subscribed != NONE) {    /* Only send state NOTIFY if we know the format */
+               if (!p->pendinginvite) {
+                       transmit_state_notify(p, state, 1, FALSE);
+               } else {
+                       /* We already have a NOTIFY sent that is not answered. Queue the state up.
+                          if many state changes happen meanwhile, we will only send a notification of the last one */
+                       ast_set_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
+               }
+       }
+       ast_verb(2, "Extension Changed %s[%s] new state %s for Notify User %s %s\n", exten, context, ast_extension_state2str(state), p->username,
+                       ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE) ? "(queued)" : "");
 
        sip_pvt_unlock(p);
 
 
        sip_pvt_unlock(p);
 
@@ -14873,6 +14883,10 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
        else
                __sip_ack(p, seqno, 0, sipmethod);
 
        else
                __sip_ack(p, seqno, 0, sipmethod);
 
+       /* If this is a NOTIFY for a subscription clear the flag that indicates that we have a NOTIFY pending */
+       if (!p->owner && sipmethod == SIP_NOTIFY && p->pendinginvite) 
+               p->pendinginvite = 0;
+
        /* Get their tag if we haven't already */
        if (ast_strlen_zero(p->theirtag) || (resp >= 200)) {
                char tag[128];
        /* Get their tag if we haven't already */
        if (ast_strlen_zero(p->theirtag) || (resp >= 200)) {
                char tag[128];
@@ -14931,6 +14945,11 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
                                } else {
                                        if (p->subscribed == NONE) 
                                                p->needdestroy = 1;
                                } else {
                                        if (p->subscribed == NONE) 
                                                p->needdestroy = 1;
+                                       if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) {
+                                               /* Ready to send the next state we have on queue */
+                                               ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
+                                               cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p);
+                                       }
                                }
                        } else if (sipmethod == SIP_REGISTER) 
                                res = handle_response_register(p, resp, rest, req, seqno);
                                }
                        } else if (sipmethod == SIP_REGISTER) 
                                res = handle_response_register(p, resp, rest, req, seqno);