]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Merge changes from sofia-sip tree:
authorMichael Jerris <mike@jerris.com>
Thu, 15 Feb 2007 05:18:37 +0000 (05:18 +0000)
committerMichael Jerris <mike@jerris.com>
Thu, 15 Feb 2007 05:18:37 +0000 (05:18 +0000)
Mon Feb 12 21:22:39 EET 2007  Maxim Zaikin <Maxim at amsd.com>
  * su_pthread_port.c: destroying condition variable used when destroying the thread.

Tue Feb 13 01:10:38 EET 2007  kai.vehmanen@nokia.com
  * nua: Ignore CANCEL of incoming request if we have already sent a final response, part 2.

Tue Feb 13 01:02:00 EET 2007  kai.vehmanen@nokia.com
  * nua: Ignore CANCEL of incoming request if we have already sent a final response. Problem reported by Mike Jerris.

nua: fixed problems in state machines.
Pekka Pessi <first.lastname@nokia.com>**20070214201847

 These problems are mostly introduced in 1.12.5:
 - assert failed if INVITE was received when calling
   (reported by Michael Jerris)
 - assert failed if nua_invite() was called second time before receiving
   final response to first INVITE (ditto)
 - ACK is now always sent if session was terminated after receiving 2XX
 - if nua has a pending incoming INVITE request, return 491 to
   nua_invite()

nta.c: return 481 response to CANCEL if INVITE transaction was successful
According to the RFC 3261 state diagram the INVITE transaction is destroyed
 immediately when a final 2XX series response is sent. Now nta returns a 481
 response to CANCEL if it finds an INVITE server transaction that was
 responded with a 2XX success response.

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4284 d0543943-73ff-0310-b7d9-9358b9ac24b2

17 files changed:
libs/sofia-sip/.update
libs/sofia-sip/RELEASE
libs/sofia-sip/configure.ac
libs/sofia-sip/docs/release_management.txt
libs/sofia-sip/libsofia-sip-ua/nta/nta.c
libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c
libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c
libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c
libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c
libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c
libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h
libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c
libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c
libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c
libs/sofia-sip/libsofia-sip-ua/su/su_pthread_port.c

index 7af6d922255e82bd5939f486bfad8405e053f7fe..ad1351a68e13c1c58f8b832a76f6f26418bfaa1d 100644 (file)
@@ -1 +1 @@
-Tue Feb 13 11:08:00 EST 2007
+Thu Feb 15 00:08:41 EST 2007
index 41f09c83cfa2ef46bc210bad567e9755a424cbfd..611367c1e3da69de18546abe360cb05b0b62fb30 100644 (file)
@@ -5,135 +5,69 @@ Release notes for current version of Sofia-SIP
 Changes since last release
 --------------------------
 
-Support for request queuing has been added to the high-level user-agent 
-API. Various portability improvements have been made related to Linux, 
-Mac OS X, Windows and Solaris ports. SIP registrations have been 
-modified to utilize persistent transport connections. The GObject 
-interface to 'nua' (nua-glib) has been dropped from the package and is 
-now distributed separately. Severe bugs in 'su', 'nua', 'nta', 'stun' and 
-'su-glib' modules have been fixed.
+<changes since last written in freshmeat.net "Changes:" style;
+ and in less than 10 lines, written in 3rd person English, with
+ complete sentences />
+
+Bugs in blaa and foo have been fixed. The stack now supports
+use of foobar...
 
 API/ABI changes and versioning
 ------------------------------
 
-New features in API are marked with Doxytag macro @NEW_1_12_5.
-Experimental features are marked with Doxytag macro @EXP_1_12_5.
+<see previous release notes at
+ http://sofia-sip.sourceforge.net/relnotes/ for examples ;
+ - should include all changes to public headers, and 
+   other important information to developers; 
+ - and should be updated _continuously_! />
 
-**template**: New features in API are marked with Doxytag macro @NEW_1_XX_X.
+**template**: New features in API are marked with Doxytag macro @VERSION_1_XX_X.
 
 libsofia-sip-ua:
 - **template**: Added foobar() function (sofia-sip/foobar.h).
-- Added nua tags NUTAG_APPL_METHOD() and NUTAG_NEWSUB()
-- Added nua_substate_make(), nua_substate_name()
-- Added nta_incoming_create_response() function
-- Added tport tags TPTAG_TOS(), TPTAG_DUMP(), TPTAG_LOG()
-- Added tport predicate function tport_is_connected()
-- Added authentication functions auc_info() and auc_has_authorization(),
-  added type msg_auth_info_t for Authentication-Info header
-- Added msg_header_join_items() function
-- Added sip_is_allowed() function and k_bitmap field to the 
-  sip_allow_t structure 
-- Added sl_header_log implementation, updated its prototype
-- Added experimental SIP headers Suppress-Notify-If-Match and
-  Suppress-Body-If-Match and functions related to them,
-  enabled with ./configure option --enable-experimental
-- Added SIP header Refer-Sub and related functions
-- Added <sofia-sip/sip_extra.h> include file
-- Added auc_info() function (sofia-sip/auth_client.h)
-- Added alternative implementations to event reactor object (su_port_t,
-  referenced by su_root_t) that can be changed at runtime using SU_PORT
-  environment variable, for instance
-  - Internal semantics of su_port_t reference counting have changed:
-    now su_port_create() has one reference, and su_root_create_with_port()
-    always consumes one reference
-- Changed return type of bm_memmem() and bm_memcasemem() to non-const
 - This release is ABI/API compatible with applications linked against 
   any 1.12.x release. However, applications built against this release won't 
   work against an older library. The ABI has been tested with the nua module 
   unit test (test_nua) built against original 1.12.0 release.
 
 libsofia-sip-ua-glib:
-- The 'nua-glib' module has been removed from the library and moved
-  to a separate package 'sofia-nua-glib'. The remaining library (su-glib) 
-  is now considered stable and will be API/ABI compatible with later 
-  releases in the 1.12.x series.
-- ABI has been modified and applications built against 1.12.4 and earlier 
-  releases, need to be rebuilt.
-- Added su_glib_prefer_gsource() which makes glib-based su_port_t
-  implementation the default choice when su_root_create() is called
+- No ABI/API changes, compatible with 1.12.0. Note, libsofia-sip-ua-glib
+  interface is not considered stable and may change in a future 1.12.x
+  release.
 
 Contributors to this release
 ----------------------------
 
-- Petteri Puolakka (patch to stun)
-- Mikhail Zabaluev (patch to su-glib mainloop integration)
-- Michael Jerris (patch to url parsing # in sip/sips userpart)
-- Colin Whittaker (TPTAG_TOS())
-- Roman Filonenko (TPTAG_LOG(), TPTAG_DUMP(), 
-                  patch to query DNS-servers with IP-Helper on win32)
-- Remi Denis-Courmont (patch to network change API)
-- Martti Mela (poll() emulation with select(), IPv6 and OS X fixes)
-- Kai Vehmanen (persistent registrations, release management)
-- Pekka Pessi (all the rest)
+<list of people who contributed to _this_ release
+ - update as people's patches are added, or when you commit stuff
+ - current development team members (see AUTHORS) may be omitted,
+   or listed at the end of the contribur list (depending on the scope 
+   of the work done since the last release)
+ - name of the contributor should be enough (email addresses in AUTHORS),
+   plus a _brief_ description of what was contributed
+ - roughly sorted by number of patches accepted
+/> 
+
+- **template**: First Surname (patch to nua/soa/msg)
 
 See the AUTHORS file in the distribution package.
 
 Notes on new features
 ---------------------
 
-- The su_root_t reactor uses different implementation (epoll, poll or select
-  on Linux), depending on SU_PORT environment variable.
-
-- nua now supports request queuing, for instance, an application can send
-  overlapping BYE and NOTIFY requests. The stack engine takes care of
-  sending the new request only after the previous one has been responded.
-
-- RFC 4488 defines the Refer-Sub header. Its datatypes, related functions and
-  methods declared in <sofia-sip/sip_extra.h> include file. The Refer-Sub
-  header structure can be accessed from sip_t structure with sip_refer_sub()
-  method, e.g.,
-
-  if (sip_refer_sub(sip) && 
-      strcasecmp("false", sip_refer_sub(sip)->rs_value) == 0) {
-     /* Do not create implicit subscription */ 
-  }
-
-- Unsolicited NOTIFYs are now supported. The application can accept incoming
-  NOTIFYs and send NOTIFYs without existing subscription.
-
-- Transport connections used for client registrations are now maintained 
-  in a persistent fashion. For example a TCP connection used for
-  registration is not closed until client is unregisters, or the whole stack
-  is shut down.
-
-- New build time options have been added: ability to build without 
-  STUN and HTTP support. See 'docs/devel_platform_notes.txt' for some
-  additional notes to distributors.
+<information about major new features
+ - new/changed/removed functionality
+ - links to further documentation
+ - section may be omitted for minor releases
+/> 
 
 Bugs fixed in this release
 --------------------------
 
-- Fixed su_from_create() returning a sip_to_t instance. Problem reported by
-  Ludovico Cavedon.
-- Partially fixed problem #1633969 with too frequent timer
-- Fixed problem in dialog matching. Problem reported by Fabio Margarido.
-- Fixed #1624446 - su_wait_clone() (and nua_destroy()) blocking for ever if
-  the root object was created using su_glib
-- Fixed #1626330 - leak in nta.c which happened if INVITE never got a final
-  response nor timed out.
-- Fixed handle leak (pthread_*_init without pthread_*_destroy). Problem
-  reported by Maxim Zaikin.
-- Fixed crash when nua_bye() was called while a NOTIFY client transaction
-  was in progress. Problem reported by Anthony Minnessale.
-- Not using close() with sockets in sres.c. Problem reported by
-  Roman Filonenko.
-- Bug in zero-padding STUN messages with a message integrity 
-  attribute. Patch by Petteri Puolakka.
-- Fixed a severe problem with timer accuracy, when sofia-sip timers 
-  where used under glib's mainloop.
-- Improved glib mainloop integration to avoid warnings about already
-  active mainloop context, and potentially other issue. Patch by 
-  Mikhail Zabaluev. Closes sf.net item #1606786.
-- Ignore harmless syntax errors in incoming requests/responses by default
-- If a TCP connection cannot be made within SIP T4 (because of a
-  firewall, for instance) nta now tries to use UDP instead.
+< notable bugs fixed in this release
+ - check the sf.net bug tracker; see closed bugs,
+   sorted by closing date
+ - other bugs as fixed in CVS/darcs
+/>
+
+- **template**: #9499652 sf.net bug item title
index bd4fbe4d194a0de5698381fc08e45e937b4b5a3c..6278ae90646f6986c7759b12d1f92a936122bc5a 100644 (file)
@@ -11,7 +11,7 @@ dnl information on the package
 dnl ---------------------------
 
 dnl update both the version for AC_INIT and the LIBSOFIA_SIP_UA_MAJOR_MINOR
-AC_INIT([sofia-sip], [1.12.5])
+AC_INIT([sofia-sip], [1.12.5work])
 AC_CONFIG_SRCDIR([libsofia-sip-ua/sip/sofia-sip/sip.h])
 AC_SUBST(VER_LIBSOFIA_SIP_UA_MAJOR_MINOR, [1.12])
 dnl Includedir specific to this sofia version
index faaf7c3467fe5c6767011ff4f378175f810a8ed0..ef1a0aef7d8e32e860fc55f39f7011336f55722a 100644 (file)
@@ -61,7 +61,8 @@ Making the release tarball
     sh> cvs tag rel-sofia-sip-x_y_z
 - take a fresh checkout of the release using the release tag
     sh> darcs get http://sofia-sip.org/repos/sofia-sip --tag=rel-sofia-sip-1_yy_z
-- create the release tarball with "make distcheck"
+- create the release tarball with "make distcheck" (make sure depcomp et
+  al libtool scripts are correctly created)
 - calculate md5 and sha1 hashes using md5sum and sha1sum utilities,
   and copy the values to the release-notes (see below)
 
index 7eb7f5edffafe07b7872048481f2b9afab253564..08be2e658dd48e7a2f436c99bdc727c152393cca 100644 (file)
@@ -5056,10 +5056,16 @@ nta_incoming_t *incoming_find(nta_agent_t const *agent,
     if (irq->irq_method == rq->rq_method)
       break;           /* found */
 
-    if (return_ack && rq->rq_method == sip_method_cancel)
-      *return_ack = irq;
-    else if (return_ack && rq->rq_method == sip_method_ack && 
-            irq->irq_method == sip_method_invite)
+    if (!return_ack)
+      continue;
+
+    if (irq->irq_method == sip_method_invite) {
+      if (rq->rq_method == sip_method_cancel)
+       *return_ack = irq;
+      else if (rq->rq_method == sip_method_ack)
+       *return_ack = irq;
+    }
+    else if (rq->rq_method == sip_method_cancel && !irq->irq_terminated)
       *return_ack = irq;
   }
 
@@ -5179,10 +5185,18 @@ int incoming_cancel(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
   nta_agent_t *agent = irq->irq_agent;
 
   /* Respond to the CANCEL */
-  nta_msg_treply(agent, msg_ref_create(msg), SIP_200_OK, 
-                NTATAG_TPORT(tport),
-                TAG_END());
 
+  if (200 <= irq->irq_status && irq->irq_status < 300) {
+    nta_msg_treply(agent, msg_ref_create(msg), SIP_481_NO_TRANSACTION, 
+                  NTATAG_TPORT(tport),
+                  TAG_END());
+  }
+  else
+    nta_msg_treply(agent, msg_ref_create(msg), SIP_200_OK, 
+                  NTATAG_TPORT(tport),
+                  TAG_END());
+
+  /* We have already sent final response */
   if (irq->irq_completed || irq->irq_method != sip_method_invite) {
     msg_destroy(msg);
     return 0;
index 1be85fc9b72266d70a1ac682dedf963b84eaf7f9..840f17aedd5e82906a3299b0ba145c2fa88f2f44 100644 (file)
@@ -345,6 +345,15 @@ void nua_dialog_usage_remove_at(nua_owner_t *own,
     nua_client_request_t *cr, *cr_next;
     nua_server_request_t *sr, *sr_next;
 
+    *at = du->du_next;
+
+    o = du->du_event;
+
+    SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
+               (void *)own, nua_dialog_usage_name(du), 
+               o ? " with event " : "", o ? o->o_type :""));
+    du->du_class->usage_remove(own, ds, du);
+
     /* Destroy saved client request */
     if (nua_client_is_bound(du->du_cr)) {
       nua_client_bind(cr = du->du_cr, NULL);
@@ -366,26 +375,20 @@ void nua_dialog_usage_remove_at(nua_owner_t *own,
        nua_server_request_destroy(sr);
     }
 
-    *at = du->du_next;
-
-    o = du->du_event;
-
-    SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
-               (void *)own, nua_dialog_usage_name(du), 
-               o ? " with event " : "", o ? o->o_type :""));
-    du->du_class->usage_remove(own, ds, du);
     su_home_unref(own);
     su_free(own, du);
   }
 
   /* Zap dialog if there are no more usages */
-  if (ds->ds_usage == NULL) {
+  if (ds->ds_terminated)
+    ;
+  else if (ds->ds_usage == NULL) {
     nua_dialog_remove(own, ds, NULL);
     ds->ds_has_events = 0;
     ds->ds_terminated = 0;
     return;
   }
-  else if (!ds->ds_terminated) {
+  else {
     nua_dialog_log_usage(own, ds);
   }
 }
@@ -561,6 +564,8 @@ int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
 {
   nua_dialog_usage_t *du;
 
+  ds->ds_terminated = 1;
+
   do {
     for (du = ds->ds_usage; du; du = du->du_next) {
       if (!du->du_shutdown) {
@@ -575,8 +580,8 @@ int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
 
 /** (Gracefully) terminate usage */
 void nua_dialog_usage_shutdown(nua_owner_t *owner,
-                               nua_dialog_state_t *ds,
-                               nua_dialog_usage_t *du)
+                              nua_dialog_state_t *ds,
+                              nua_dialog_usage_t *du)
 {
   if (du) {
     du->du_refresh = 0;
index 3c852b0b22e0c74e3739a22e0c81dbe9302118cb..03fbf1a48bdbaa5a16f022aa38c18ba24bb5bd17 100644 (file)
@@ -115,6 +115,8 @@ struct nua_server_request {
   unsigned sr_terminating:1;   /**< Terminate usage after final response */
   unsigned sr_gracefully:1;    /**< Terminate usage gracefully */
 
+  unsigned sr_neutral:1;       /**< No effect on session or other usage */
+
   /* Flags used with offer-answer */
   unsigned sr_offer_recv:1;    /**< We have received an offer */
   unsigned sr_answer_sent:2;   /**< We have answered (reliably, if >1) */
@@ -284,6 +286,9 @@ struct nua_client_request
   unsigned cr_offer_recv:1;    /**< Recv offer in a response */
   unsigned cr_answer_sent:1;   /**< Sent answer in (PR)ACK */
 
+  /* Flags with usage */
+  unsigned cr_neutral:1;       /**< No effect on session or other usage */
+
   /* Lifelong flags? */
   unsigned cr_auto:1;          /**< Request was generated by stack */
   unsigned cr_has_contact:1;   /**< Request has user Contact */
@@ -318,6 +323,7 @@ struct nua_dialog_state
   unsigned ds_has_register:1;  /**< We have registration */
   unsigned ds_has_publish:1;   /**< We have publish */
 
+  unsigned ds_got_session:1;   /**< We have (or have had) session */
   unsigned ds_got_referrals:1; /**< We have (or have had) referrals */
 
   unsigned :0;
@@ -573,6 +579,10 @@ int nua_client_report(nua_client_request_t *cr,
 
 nua_client_request_t *nua_client_request_pending(nua_client_request_t const *);
 
+int nua_client_init_requests(nua_client_request_t *cr,
+                            void const *cr0,
+                            int invite);
+
 /* ---------------------------------------------------------------------- */
 
 extern nua_server_methods_t const
@@ -590,6 +600,7 @@ extern nua_server_methods_t const
   nua_refer_server_methods,    /**< REFER */
   nua_publish_server_methods;  /**< PUBLISH */
 
+/** Return true if we have not sent final response to request */ 
 static inline 
 int nua_server_request_is_pending(nua_server_request_t const *sr)
 {
index 54a9bc04b83ebabfa78385e886db37b234438877..1d412ed19f8d3320a3e833363251b9e7de8e3159 100644 (file)
@@ -177,6 +177,9 @@ static int nua_session_usage_shutdown(nua_owner_t *,
                                      nua_dialog_state_t *,
                                      nua_dialog_usage_t *);
 
+static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags);
+static int nua_invite_client_deinit(nua_client_request_t *cr);
+
 static nua_usage_class const nua_session_usage[1] = {
   {
     sizeof (nua_session_usage_t),
@@ -202,6 +205,7 @@ int nua_session_usage_add(nua_handle_t *nh,
   if (ds->ds_has_session)
     return -1;
   ds->ds_has_session = 1;
+  ds->ds_got_session = 1;
 
   return 0;
 }
@@ -212,10 +216,44 @@ void nua_session_usage_remove(nua_handle_t *nh,
                               nua_dialog_usage_t *du)
 {
   nua_session_usage_t *ss = nua_dialog_usage_private(du);
+  nua_client_request_t *cr, *cr_next;
+
+  cr = du->du_cr;
+
+  if (cr && cr->cr_orq && cr->cr_status >= 200) {
+    ss->ss_reporting = 1;
+    nua_invite_client_ack(cr, NULL);
+    ss->ss_reporting = 0;
+  }
+
+  /* Destroy queued INVITE transactions */
+  for (cr = ds->ds_cr; cr; cr = cr_next) {
+    cr_next = cr->cr_next;
+
+    if (cr->cr_method != sip_method_invite)
+      continue;
+    if (cr == du->du_cr)
+      continue;
+
+    nua_stack_event(nh->nh_nua, nh, 
+                   NULL,
+                   cr->cr_event,
+                   SIP_481_NO_TRANSACTION,
+                   NULL);
+
+    nua_client_request_destroy(cr);
+
+    cr_next = ds->ds_cr;
+  }
 
-  ds->ds_has_session = 0;
   
-  (void)ss;
+  ds->ds_has_session = 0;
+  nh->nh_has_invite = 0;
+  nh->nh_active_call = 0;
+  nh->nh_hold_remote = 0;
+
+  if (nh->nh_soa)
+    soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
 }
 
 static
@@ -228,7 +266,7 @@ nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds)
 }
 
 static
-nua_session_usage_t *nua_session_usage_get(nua_dialog_state_t const *ds)
+nua_session_usage_t *nua_session_usage_for_dialog(nua_dialog_state_t const *ds)
 {
   nua_dialog_usage_t *du;
 
@@ -245,13 +283,6 @@ static
 void nua_session_usage_destroy(nua_handle_t *nh,
                               nua_session_usage_t *ss)
 {
-  nh->nh_has_invite = 0;
-  nh->nh_active_call = 0;
-  nh->nh_hold_remote = 0;
-
-  if (nh->nh_soa)
-    soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
-
   /* Remove usage */
   nua_dialog_usage_remove(nh, nh->nh_ds, nua_dialog_usage_public(ss));
 
@@ -482,13 +513,6 @@ static int nua_invite_client_report(nua_client_request_t *cr,
                                    nta_outgoing_t *orq,
                                    tagi_t const *tags);
 
-static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags);
-static int nua_invite_client_ack_msg(nua_client_request_t *cr, 
-                                    msg_t *msg, sip_t *sip,
-                                    tagi_t const *tags);
-
-static int nua_invite_client_deinit(nua_client_request_t *cr);
-
 nua_client_methods_t const nua_invite_client_methods = {
   SIP_METHOD_INVITE,
   0,
@@ -527,6 +551,9 @@ static int nua_invite_client_init(nua_client_request_t *cr,
   nua_dialog_usage_t *du;
 
   cr->cr_usage = du = nua_dialog_usage_for_session(nh->nh_ds);
+  /* Errors returned by nua_invite_client_init() 
+     are neutral to session state */
+  cr->cr_neutral = 1;  
   
   if (nh_is_special(nh) || 
       nua_stack_set_handle_special(nh, nh_has_invite, nua_i_error))
@@ -534,7 +561,15 @@ static int nua_invite_client_init(nua_client_request_t *cr,
   else if (nh_referral_check(nh, tags) < 0)
     return nua_client_return(cr, 900, "Invalid referral", msg);
 
-  if (!du)
+  if (du) {
+    nua_server_request_t *sr;
+    for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
+      /* INVITE in progress? */
+      if (sr->sr_usage == du && sr->sr_method == sip_method_invite &&
+         nua_server_request_is_pending(sr))
+       return nua_client_return(cr, SIP_491_REQUEST_PENDING, msg);
+  }
+  else
     du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL);
   if (!du)
     return -1;
@@ -547,6 +582,8 @@ static int nua_invite_client_init(nua_client_request_t *cr,
                            NH_PGET(nh, min_se),
                            NH_PGET(nh, refresher));
 
+  cr->cr_neutral = 0;
+
   return 0;
 }
 
@@ -568,7 +605,7 @@ static int nua_invite_client_request(nua_client_request_t *cr,
   invite_timeout = NH_PGET(nh, invite_timeout);
   if (invite_timeout == 0)
     invite_timeout = UINT_MAX;
-  /* Cancel if we don't get response within timeout*/
+  /* Send CANCEL if we don't get response within timeout*/
   nua_dialog_usage_set_expires(du, invite_timeout);
   nua_dialog_usage_set_refresh(du, 0);
 
@@ -808,12 +845,20 @@ static int nua_invite_client_report(nua_client_request_t *cr,
   if (orq != cr->cr_orq && status != 100)
     return 1;
 
+  if (ss == NULL) {
+    signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated);
+    return 1;
+  }
+
   ss->ss_reporting = 1;
 
-  if (ss == NULL) {
-    next_state = nua_callstate_terminated;
+  if (cr->cr_neutral) {
+    signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
+    ss->ss_reporting = 0;
+    return 1;
   }
-  else if (status == 100) {
+
+  if (status == 100) {
     next_state = nua_callstate_calling;
   }
   else if (status < 300 && cr->cr_graceful) {
@@ -927,11 +972,10 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
 {
   nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds);
   nua_session_usage_t *ss = nua_dialog_usage_private(du);
+  nua_client_request_t *cr = du ? du->du_cr : NULL;
+  int error;
 
-  if (!du || 
-      !du->du_cr || 
-      du->du_cr->cr_orq == NULL || 
-      du->du_cr->cr_status < 200) {
+  if (!cr || cr->cr_orq == NULL || cr->cr_status < 200) {
     UA_EVENT2(nua_i_error, 900, "No response to ACK");
     return 1;
   }
@@ -942,8 +986,9 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
       soa_set_params(nh->nh_soa, TAG_NEXT(tags));
   }
 
-  if (nua_invite_client_ack(du->du_cr, tags) < 0) {
-    int error;
+  error = nua_invite_client_ack(cr, tags);
+
+  if (error < 0) {
     ss->ss_reason = "SIP;cause=500;text=\"Internal Error\"";
     ss->ss_reporting = 1;      /* We report state here if BYE fails */
     error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
@@ -953,6 +998,12 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
                             ? nua_callstate_terminated
                             : nua_callstate_terminating);
   }
+  else {
+    if (!nua_client_is_queued(cr) && !nua_client_is_bound(cr))
+      nua_client_request_destroy(cr);
+
+    nua_client_init_requests(nh->nh_ds->ds_cr, cr, 1);
+  }
 
   return 0;
 }
@@ -967,6 +1018,7 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
 {
   nua_handle_t *nh = cr->cr_owner;
   nua_dialog_state_t *ds = nh->nh_ds;
+  nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage);
 
   msg_t *msg;
   sip_t *sip;
@@ -974,6 +1026,9 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
   sip_authorization_t *wa;
   sip_proxy_authorization_t *pa;
   sip_cseq_t *cseq;
+  nta_outgoing_t *ack;
+  int status = 200;
+  char const *phrase = "OK", *reason = NULL;
 
   assert(ds->ds_leg);
   assert(cr->cr_orq);
@@ -1009,94 +1064,75 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags)
     ;
   else if (nta_msg_request_complete(msg, ds->ds_leg, SIP_METHOD_ACK, NULL) < 0)
     ;
-  else
-    error = nua_invite_client_ack_msg(cr, msg, sip, tags);
-
-  nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
-  
-  if (error == -1)
-    msg_destroy(msg);
-
-  return error;
-}
-
-/** Send ACK, destroy INVITE transaction.
- *
- *  @retval 1 if successful
- *  @retval -2 if an error occurred
- */
-static
-int nua_invite_client_ack_msg(nua_client_request_t *cr,
-                             msg_t *msg, sip_t *sip,
-                             tagi_t const *tags)
-{
-  nua_handle_t *nh = cr->cr_owner;
-  nua_dialog_usage_t *du = cr->cr_usage;
-  nua_session_usage_t *ss = nua_dialog_usage_private(du);
-
-  nta_outgoing_t *ack;
-  int status = 200;
-  char const *phrase = "OK", *reason = NULL;
-
-  /* Remove extra headers */
-  while (sip->sip_allow)
-    sip_header_remove(msg, sip, (sip_header_t*)sip->sip_allow);
-  while (sip->sip_priority)
-    sip_header_remove(msg, sip, (sip_header_t*)sip->sip_priority);
-  while (sip->sip_proxy_require)
-    sip_header_remove(msg, sip, (sip_header_t*)sip->sip_proxy_require);
-  while (sip->sip_require)
-    sip_header_remove(msg, sip, (sip_header_t*)sip->sip_require);
-  while (sip->sip_subject)
-    sip_header_remove(msg, sip, (sip_header_t*)sip->sip_subject);
-  while (sip->sip_supported)
-    sip_header_remove(msg, sip, (sip_header_t*)sip->sip_supported);
-
-  if (!nh->nh_soa)
-    ;
-  else if (cr->cr_offer_recv && !cr->cr_answer_sent) {
-    if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
-       session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
-      status = 900, phrase = "Internal media error";
-      reason = "SIP;cause=500;text=\"Internal media error\"";
-      /* reason = soa_error_as_sip_reason(nh->nh_soa); */
+  else {
+    /* Remove extra headers */
+    while (sip->sip_allow)
+      sip_header_remove(msg, sip, (sip_header_t*)sip->sip_allow);
+    while (sip->sip_priority)
+      sip_header_remove(msg, sip, (sip_header_t*)sip->sip_priority);
+    while (sip->sip_proxy_require)
+      sip_header_remove(msg, sip, (sip_header_t*)sip->sip_proxy_require);
+    while (sip->sip_require)
+      sip_header_remove(msg, sip, (sip_header_t*)sip->sip_require);
+    while (sip->sip_subject)
+      sip_header_remove(msg, sip, (sip_header_t*)sip->sip_subject);
+    while (sip->sip_supported)
+      sip_header_remove(msg, sip, (sip_header_t*)sip->sip_supported);
+
+    if (!nh->nh_soa || ss == NULL)
+      ;
+    else if (cr->cr_offer_recv && !cr->cr_answer_sent) {
+      if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
+         session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
+       status = 900, phrase = "Internal media error";
+       reason = "SIP;cause=500;text=\"Internal media error\"";
+       /* reason = soa_error_as_sip_reason(nh->nh_soa); */
+      }
+      else {
+       cr->cr_answer_sent = 1;
+       soa_activate(nh->nh_soa, NULL);
+       /* signal that O/A round is complete */
+       ss->ss_oa_sent = "answer";
+      }
+      
+      if (!reason &&
+         /* ss->ss_offer_sent && !ss->ss_answer_recv */
+         !soa_is_complete(nh->nh_soa)) {
+       /* No SDP answer in 2XX response -> terminate call */
+       status = 988, phrase = "Incomplete offer/answer";
+       reason = "SIP;cause=488;text=\"Incomplete offer/answer\"";
+      }
     }
-    else {
-      cr->cr_answer_sent = 1;
-      soa_activate(nh->nh_soa, NULL);
-      /* signal that O/A round is complete */
-      ss->ss_oa_sent = "answer";
+    
+    if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL,
+                                   msg,
+                                   SIPTAG_END(),
+                                   TAG_NEXT(tags)))) {
+      nta_outgoing_destroy(ack);       /* TR engine keeps this around for T2 */
     }
-
-    if (!reason &&
-       /* ss->ss_offer_sent && !ss->ss_answer_recv */
-       !soa_is_complete(nh->nh_soa)) {
-      /* No SDP answer in 2XX response -> terminate call */
-      status = 988, phrase = "Incomplete offer/answer";
-      reason = "SIP;cause=488;text=\"Incomplete offer/answer\"";
+    else if (!reason) {
+      status = 900, phrase = "Cannot send ACK";
+      reason = "SIP;cause=500;text=\"Internal Error\"";
     }
-  }
 
-  if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL,
-                                 msg,
-                                 SIPTAG_END(),
-                                 TAG_NEXT(tags)))) {
-    nta_outgoing_destroy(ack); /* TR engine keeps this around for T2 */
-  }
-  else if (!reason) {
-    status = 900, phrase = "Cannot send ACK";
-    reason = "SIP;cause=500;text=\"Internal Error\"";
+    if (ss && reason)
+      ss->ss_reason = reason;
+
+    if (status < 300)
+      error = 1;
+    else
+      error = -2;
   }
 
-  if (ss) {
-    if (reason)
-      ss->ss_reason = reason;
+  nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
+  nua_client_request_remove(cr);
 
-    if (!ss->ss_reporting && status < 300)
+  if (ss) {
+    if (!ss->ss_reporting && error >= 0)
       signal_call_state_change(nh, ss, status, phrase, nua_callstate_ready);
   }
   
-  return status < 300 ? 1 : -2;
+  return error;
 }
 
 /** Deinitialize client request */
@@ -1220,7 +1256,7 @@ static void nua_session_usage_refresh(nua_handle_t *nh,
     if (cr->cr_method == sip_method_update)
       return;
 
-  /* INVITE or UPDATE in progress or being authenticated */
+  /* INVITE or UPDATE in progress */
   for (sr = ds->ds_sr; sr; sr = sr->sr_next)
     if (sr->sr_usage == du && 
        (sr->sr_method == sip_method_invite || 
@@ -1258,7 +1294,7 @@ static int nua_session_usage_shutdown(nua_handle_t *nh,
   nua_server_request_t *sr, *sr_next;
   nua_client_request_t *cri;
 
-  assert(ss == nua_session_usage_get(nh->nh_ds));
+  assert(ss == nua_session_usage_for_dialog(nh->nh_ds));
 
   /* Zap server-side transactions */
   for (sr = ds->ds_sr; sr; sr = sr_next) {
@@ -1663,6 +1699,8 @@ nua_invite_server_init(nua_server_request_t *sr)
   nua_handle_t *nh = sr->sr_owner;
   nua_t *nua = nh->nh_nua;
 
+  sr->sr_neutral = 1;
+
   if (!NUA_PGET(nua, nh, invite_enable))
     return SR_STATUS1(sr, SIP_403_FORBIDDEN);
 
@@ -1688,9 +1726,10 @@ nua_invite_server_init(nua_server_request_t *sr)
        break;
     }
     
-    if (sr0)
+    if (sr0) {
       /* Overlapping invites - RFC 3261 14.2 */
       return nua_server_retry_after(sr, 500, "Overlapping Requests", 0, 10);
+    }
 
     for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next) {
       if (cr->cr_usage == sr->sr_usage && cr->cr_orq && cr->cr_offer_sent)
@@ -1699,6 +1738,8 @@ nua_invite_server_init(nua_server_request_t *sr)
     }
   }
 
+  sr->sr_neutral = 0;
+
   return 0;
 }
 
@@ -2024,6 +2065,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
   nua_dialog_usage_t *du = sr->sr_usage;
   nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
   int initial = sr->sr_initial && !sr->sr_event;
+  int neutral = sr->sr_neutral;
   int application = sr->sr_application;
   int status = sr->sr_status; char const *phrase = sr->sr_phrase;
   int retval;
@@ -2036,18 +2078,18 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
   
   if (retval >= 2 || ss == NULL) {
     /* Session has been terminated. */ 
-    if (!initial)
+    if (!initial && !neutral)
       signal_call_state_change(nh, NULL, status, phrase,
                               nua_callstate_terminated);
     return retval;
   }
 
   assert(ss);
-  assert(ss->ss_state != nua_callstate_calling);
-  assert(ss->ss_state != nua_callstate_proceeding);
 
   /* Update session state */
-  if (status < 300 || application != 0)
+  if (status < 300 || application != 0) {
+    assert(ss->ss_state != nua_callstate_calling);
+    assert(ss->ss_state != nua_callstate_proceeding);
     signal_call_state_change(nh, ss, status, phrase,
                             status >= 300
                             ? nua_callstate_init
@@ -2056,6 +2098,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
                             : status > 100
                             ? nua_callstate_early
                             : nua_callstate_received);
+  }
 
   if (status == 180)
     ss->ss_alerting = 1;
@@ -2065,7 +2108,7 @@ int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
   if (200 <= status && status < 300) {
      du->du_ready = 1;
   }
-  else if (300 <= status) {
+  else if (300 <= status && !neutral) {
     if (nh->nh_soa)
       soa_init_offer_answer(nh->nh_soa);
   }
@@ -2196,16 +2239,20 @@ int process_cancel(nua_server_request_t *sr,
 {
   nua_handle_t *nh = sr->sr_owner;
   nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
-  msg_t *cancel = nta_incoming_getrequest_ackcancel(irq);
 
-  if (nta_incoming_status(irq) < 200 && nua_server_request_is_pending(sr) &&
-         ss && (ss == nua_session_usage_get(nh->nh_ds))) {
-  nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, NULL);
+  assert(ss); assert(ss == nua_session_usage_for_dialog(nh->nh_ds)); (void)ss;
+
+  if (nua_server_request_is_pending(sr)) {
+    msg_t *cancel = nta_incoming_getrequest_ackcancel(irq);
+
+    assert(nta_incoming_status(irq) < 200);
 
-  SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED);
+    nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, NULL);
 
-  nua_server_respond(sr, NULL);
-  nua_server_report(sr);
+    SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED);
+
+    nua_server_respond(sr, NULL);
+    nua_server_report(sr);
   }
 
   return 0;
@@ -2222,7 +2269,7 @@ int process_timeout(nua_server_request_t *sr,
   char const *reason = "SIP;cause=408;text=\"ACK Timeout\"";
   int error;
 
-  assert(ss); assert(ss == nua_session_usage_get(nh->nh_ds));
+  assert(ss); assert(ss == nua_session_usage_for_dialog(nh->nh_ds));
 
   if (nua_server_request_is_pending(sr)) {
     phrase = "PRACK Timeout";
@@ -3328,7 +3375,7 @@ nua_client_methods_t const nua_bye_client_methods = {
 int
 nua_stack_bye(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
 {
-  nua_session_usage_t *ss = nua_session_usage_get(nh->nh_ds);
+  nua_session_usage_t *ss = nua_session_usage_for_dialog(nh->nh_ds);
 
   if (ss && 
       nua_callstate_calling <= ss->ss_state &&
@@ -3430,9 +3477,11 @@ static int nua_bye_client_report(nua_client_request_t *cr,
     signal_call_state_change(nh, ss, status, "to BYE", 
                             nua_callstate_terminated);
 
-    if (ss && !ss->ss_reporting && !nua_client_is_queued(du->du_cr)) {
-      /* Do not destroy session usage while INVITE is alive */
-      nua_session_usage_destroy(nh, ss);
+    if (ss && !ss->ss_reporting) {
+      if (nua_client_is_queued(du->du_cr) && du->du_cr->cr_status < 200)
+       /* No final response to INVITE received yet */;
+      else
+       nua_session_usage_destroy(nh, ss);
     }
   }
 
index bcc473bf666962874c73b09098b517dea578a3ba..9cda773175bbe72a3a6cecf02bd6b788d387e549 100644 (file)
@@ -743,14 +743,14 @@ void nh_destroy(nua_t *nua, nua_handle_t *nh)
   if (nh->nh_notifier)
     nea_server_destroy(nh->nh_notifier), nh->nh_notifier = NULL;
 
-  nua_dialog_deinit(nh, nh->nh_ds);
-
   while (nh->nh_ds->ds_cr)
     nua_client_request_destroy(nh->nh_ds->ds_cr);
 
   while (nh->nh_ds->ds_sr)
     nua_server_request_destroy(nh->nh_ds->ds_sr);
 
+  nua_dialog_deinit(nh, nh->nh_ds);
+
   if (nh->nh_soa)
     soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
 
@@ -1090,6 +1090,9 @@ int nua_stack_process_request(nua_handle_t *nh,
 
   nta_incoming_tag(irq, NULL);
 
+  if (method == sip_method_cancel)
+    return 481;
+
   /* Hook to outbound */
   if (method == sip_method_options) {
     status = nua_registration_process_request(nua->nua_registrations,
@@ -1485,6 +1488,8 @@ int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
     else if (next.msg)
       msg_destroy(next.msg);
 
+    assert(sr->sr_status >= 200 || sr->sr_response.msg);
+
     return retval;
   }
 
@@ -1521,7 +1526,7 @@ int nua_server_report(nua_server_request_t *sr)
   if (sr)
     return sr->sr_methods->sm_report(sr, NULL);
   else
-    return 2;
+    return 1;
 }
 
 int nua_base_server_treport(nua_server_request_t *sr,
@@ -1763,8 +1768,11 @@ inline int nua_client_request_queue(nua_client_request_t *cr)
     }
   }
   else {
-    while (*queue)
+    while (*queue) {
       queue = &(*queue)->cr_next;
+      if (cr->cr_method == sip_method_invite)
+       queued = 1;
+    }
   }
 
   if ((cr->cr_next = *queue))
@@ -2543,7 +2551,7 @@ int nua_base_client_response(nua_client_request_t *cr,
                             tagi_t const *tags)
 {
   nua_handle_t *nh = cr->cr_owner;
-  int next;
+  sip_method_t method = cr->cr_method;
 
   cr->cr_reporting = 1;
 
@@ -2552,14 +2560,15 @@ int nua_base_client_response(nua_client_request_t *cr,
 
   nua_client_report(cr, status, phrase, sip, cr->cr_orq, tags);
 
-  if (status >= 200)
-    nua_client_request_remove(cr);
-
-  if (cr->cr_method == sip_method_invite ? status < 300 : status < 200) {
+  if (status < 200 ||
+      /* Un-ACKed 2XX response to INVITE */
+      (cr->cr_method == sip_method_invite && status < 300 && cr->cr_orq)) {
     cr->cr_reporting = 0;
     return 1;
   }
 
+  nua_client_request_remove(cr);
+
   if (cr->cr_orq)
     nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
 
@@ -2585,16 +2594,12 @@ int nua_base_client_response(nua_client_request_t *cr,
   if (nua_client_is_queued(cr))
     return 1;
 
-  next = cr->cr_method != sip_method_invite && cr->cr_method != sip_method_cancel;
-
   if (!nua_client_is_bound(cr))
     nua_client_request_destroy(cr);
 
-  if (next && nh->nh_ds->ds_cr != NULL && nh->nh_ds->ds_cr != cr) {
-    cr = nh->nh_ds->ds_cr;
-    if (cr->cr_method != sip_method_invite && cr->cr_method != sip_method_cancel)
-      nua_client_init_request(cr);
-  }
+  if (method != sip_method_cancel)
+    return nua_client_init_requests(nh->nh_ds->ds_cr, cr,
+                                   method == sip_method_invite);
 
   return 1;
 }
@@ -2638,6 +2643,29 @@ int nua_client_treport(nua_client_request_t *cr,
   return retval;
 }
 
+int nua_client_init_requests(nua_client_request_t *cr,
+                            void const *cr0,
+                            int invite)
+{
+  if (cr0 == cr)               /* already initialized! */
+    return 1;
+  
+  for (; cr; cr = cr->cr_next) {
+    if (cr->cr_method == sip_method_cancel)
+      continue;
+    
+    if (invite 
+       ? cr->cr_method == sip_method_invite
+       : cr->cr_method != sip_method_invite)
+      break;
+  }
+
+  if (cr) 
+    nua_client_init_request(cr);
+
+  return 1;
+}
+
 nua_client_request_t *
 nua_client_request_pending(nua_client_request_t const *cr)
 {
index 2c18dfd85dff26d41e0e7b61271da0587608dce5..6927cd53f34a05bf8c81543ebe4c9d967586587a 100644 (file)
@@ -1578,6 +1578,310 @@ int test_update_by_uas(struct context *ctx)
   END();
 }
  
+int cancel_when_pracked(CONDITION_PARAMS);
+int alert_call(CONDITION_PARAMS);
+
+int test_180rel_cancel1(struct context *ctx)
+{
+  BEGIN();
+
+  struct endpoint *a = &ctx->a,  *b = &ctx->b;
+  struct call *a_call = a->call, *b_call = b->call;
+  struct event *e;
+
+  if (print_headings)
+    printf("TEST NUA-10.6: CANCEL after PRACK\n");
+
+/* Test for 100rel:
+
+   A                   B
+   |-------INVITE------>|
+   |<----100 Trying-----|
+   |                   |
+   |<-------180---------|
+   |-------PRACK------->|
+   |<-------200---------|
+   |                   |
+   |------CANCEL------->|
+   |<------200 OK-------|
+   |                   |
+   |<-------487---------|
+   |--------ACK-------->|
+   |                   |
+
+*/
+
+  a_call->sdp = "m=audio 5008 RTP/AVP 8";
+  b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+  nua_set_params(ctx->a.nua,
+                NUTAG_EARLY_MEDIA(1),
+                NUTAG_ONLY183_100REL(0),
+                TAG_END());
+  run_a_until(ctx, nua_r_set_params, until_final_response);
+
+  nua_set_params(ctx->b.nua,
+                NUTAG_EARLY_MEDIA(1),
+                NUTAG_ONLY183_100REL(0),
+                TAG_END());
+  run_b_until(ctx, nua_r_set_params, until_final_response);
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  INVITE(a, a_call, a_call->nh,
+        TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+        SOATAG_USER_SDP_STR(a_call->sdp),
+        TAG_END());
+
+  run_ab_until(ctx, -1, cancel_when_pracked, -1, alert_call);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+     PROCEEDING -(C3+C4)-> TERMINATED: nua_r_invite, nua_i_state
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+  TEST_1(is_offer_sent(e->data->e_tags));
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 180);
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+  TEST_1(is_answer_recv(e->data->e_tags));
+  TEST_1(!is_offer_sent(e->data->e_tags));
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_prack);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_cancel);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 487);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+
+  /*
+   Server transitions:
+   INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+   RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+   Option A:
+   EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
+   Option B:
+   EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+   COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+   READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state   
+  */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+  TEST(e->data->e_status, 100);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+  TEST_1(is_offer_recv(e->data->e_tags));
+
+  /* Responded with 180 Ringing */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+  TEST_1(is_answer_sent(e->data->e_tags));
+
+  /* 180 is PRACKed */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+  /* Does not have effect on call state */
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+
+  free_events_in_list(ctx, b->events);
+
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+  nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+  if (print_headings)
+    printf("TEST NUA-10.6: PASSED\n");
+  
+  END();
+}
+
+int cancel_when_pracked(CONDITION_PARAMS)
+{
+  if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+    return 0;
+
+  save_event_in_list(ctx, event, ep, call);
+
+  if (event == nua_r_prack)
+    CANCEL(ep, call, nh, TAG_END());
+
+  switch (callstate(tags)) {
+  case nua_callstate_proceeding:
+    return 0;
+  case nua_callstate_ready:
+    return 1;
+  case nua_callstate_terminated:
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+int test_180rel_cancel2(struct context *ctx)
+{
+  BEGIN();
+
+  struct endpoint *a = &ctx->a,  *b = &ctx->b;
+  struct call *a_call = a->call, *b_call = b->call;
+  struct event *e;
+
+  if (print_headings)
+    printf("TEST NUA-10.7: CANCEL after 100rel 180\n");
+
+/* Test for 100rel:
+
+   A                   B
+   |-------INVITE------>|
+   |<----100 Trying-----|
+   |                   |
+   |<-------180---------|
+   |-------PRACK------->|
+   |<-------200---------|
+   |                   |
+   |------CANCEL------->|
+   |<------200 OK-------|
+   |                   |
+   |<-------487---------|
+   |--------ACK-------->|
+   |                   |
+
+*/
+
+  a_call->sdp = "m=audio 5008 RTP/AVP 8";
+  b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+  nua_set_params(ctx->a.nua,
+                NUTAG_EARLY_MEDIA(1),
+                NUTAG_ONLY183_100REL(0),
+                TAG_END());
+  run_a_until(ctx, nua_r_set_params, until_final_response);
+
+  nua_set_params(ctx->b.nua,
+                NUTAG_EARLY_MEDIA(1),
+                NUTAG_ONLY183_100REL(0),
+                TAG_END());
+  run_b_until(ctx, nua_r_set_params, until_final_response);
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  INVITE(a, a_call, a_call->nh,
+        TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+        SOATAG_USER_SDP_STR(a_call->sdp),
+        TAG_END());
+
+  run_ab_until(ctx, -1, cancel_when_ringing, -1, accept_pracked2);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state, nua_r_prack
+     PROCEEDING -(C3+C4)-> TERMINATED: nua_r_invite, nua_i_state
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+  TEST_1(is_offer_sent(e->data->e_tags));
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 180);
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_proceeding);
+  TEST_1(is_answer_recv(e->data->e_tags));
+  TEST_1(!is_offer_sent(e->data->e_tags));
+
+#define NEXT_SKIP_PRACK_CANCEL() \
+  do { TEST_1(e = e->next); } \
+  while (e->data->e_event == nua_r_prack || e->data->e_event == nua_r_cancel)
+
+  NEXT_SKIP_PRACK_CANCEL();
+
+  TEST_E(e->data->e_event, nua_r_invite);
+  if (e->data->e_status == 487) {
+    TEST(e->data->e_status, 487);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+    TEST(callstate(e->data->e_tags), nua_callstate_terminated);
+    TEST_1(!e->next);
+  }
+  else {
+    TEST(e->data->e_status, 200);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+    TEST(callstate(e->data->e_tags), nua_callstate_ready);
+
+    BYE(a, a_call, a_call->nh, TAG_END());
+    run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+    NEXT_SKIP_PRACK_CANCEL(); TEST_E(e->data->e_event, nua_r_bye);
+    TEST(e->data->e_status, 200);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+    TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+    TEST_1(!e->next);
+  }
+
+  free_events_in_list(ctx, a->events);
+
+  /*
+   Server transitions:
+   INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+   RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+   Option A:
+   EARLY -(S10)-> TERMINATED: nua_i_cancel, nua_i_state
+   Option B:
+   EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+   COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
+   READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state   
+  */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+  TEST(e->data->e_status, 100);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+  TEST_1(is_offer_recv(e->data->e_tags));
+
+  /* Responded with 180 Ringing */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+  TEST_1(is_answer_sent(e->data->e_tags));
+
+  /* 180 is PRACKed */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+  /* Does not have effect on call state */
+
+  if (e->next->data->e_event == nua_i_cancel) {
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_cancel);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+    TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  }
+  else {
+    /* Respond with 200 OK */
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+    TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+    TEST_1(!is_offer_answer_done(e->data->e_tags));
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+    TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
+    TEST_1(!is_offer_answer_done(e->data->e_tags));
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+    TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  }
+
+  free_events_in_list(ctx, b->events);
+
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+  nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+  if (print_headings)
+    printf("TEST NUA-10.7: PASSED\n");
+  
+  END();
+}
+
 
 int test_100rel(struct context *ctx)
 {
@@ -1589,6 +1893,8 @@ int test_100rel(struct context *ctx)
   retval = test_preconditions(ctx); RETURN_ON_SINGLE_FAILURE(retval);
   retval = test_preconditions2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
   retval = test_update_by_uas(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+  retval = test_180rel_cancel1(ctx); RETURN_ON_SINGLE_FAILURE(retval);
+  retval = test_180rel_cancel2(ctx); RETURN_ON_SINGLE_FAILURE(retval);
 
   nua_set_params(ctx->a.nua,
                 NUTAG_EARLY_MEDIA(0),
@@ -1612,3 +1918,4 @@ int test_100rel(struct context *ctx)
 
   return retval;
 }
+
index 2f5fb07eea9fa44b0afdd4943a8564326d5cfb54..c3cbd5dcb07e37e6d1ac2013edf13318533dd445 100644 (file)
@@ -467,7 +467,7 @@ int test_call_hold(struct context *ctx)
   */
 
   if (print_headings)
-    printf("TEST NUA-7.6: re-INVITE without auto-ack\n");
+    printf("TEST NUA-7.6.1: re-INVITE without auto-ack\n");
 
   /* Turn off auto-ack */
   nua_set_hparams(b_call->nh, NUTAG_AUTOACK(0), TAG_END());
@@ -517,7 +517,7 @@ int test_call_hold(struct context *ctx)
   free_events_in_list(ctx, a->events);
 
   if (print_headings)
-    printf("TEST NUA-7.6: PASSED\n");
+    printf("TEST NUA-7.6.1: PASSED\n");
 
 
   /* ---------------------------------------------------------------------- */
@@ -528,7 +528,7 @@ int test_call_hold(struct context *ctx)
    */
 
   if (print_headings)
-    printf("TEST NUA-7.6: terminate call\n");
+    printf("TEST NUA-7.6.2: terminate call\n");
 
   BYE(a, a_call, a_call->nh, TAG_END());
   run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
@@ -555,7 +555,7 @@ int test_call_hold(struct context *ctx)
   free_events_in_list(ctx, b->events);
 
   if (print_headings)
-    printf("TEST NUA-7.6: PASSED\n");
+    printf("TEST NUA-7.6.2: PASSED\n");
 
   nua_handle_destroy(a_call->nh), a_call->nh = NULL;
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
@@ -725,9 +725,6 @@ int test_reinvite(struct context *ctx)
   nua_handle_destroy(a_call->nh), a_call->nh = NULL;
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
 
-  if (print_headings)
-    printf("TEST NUA-7.8: PASSED\n");
-
   END();
 }
 
@@ -784,6 +781,211 @@ int ringing_until_terminated(CONDITION_PARAMS)
   }
 }
 
+/* ======================================================================== */
+
+int accept_and_attempt_reinvite(CONDITION_PARAMS);
+int until_ready2(CONDITION_PARAMS);
+
+/* test_reinvite2 message sequence looks like this:
+
+ A                    B
+ |                    |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |                    |
+ |<----180 Ringing----|
+ |                    |
+ |           /-INVITE-|
+ |           \---900->|
+ |                    |
+ |<--------200--------|
+ |---------ACK------->|
+ :                    :
+ :   queue INVITE     :
+ :                    :
+ |-----re-INVITE----->|
+ |<--------200--------|
+ |---------ACK------->|
+ |-----re-INVITE----->|
+ |<--------200--------|
+ |---------ACK------->|
+ :                    :
+ :        glare       :
+ :                    :
+ |-----re-INVITE----->|
+ |<----re-INVITE------|
+ |<--------491--------|
+ |---------491------->|
+ |---------ACK------->|
+ |<--------ACK--------|
+ :                    :
+ |---------BYE------->|
+ |<--------200--------|
+*/
+
+int test_reinvite2(struct context *ctx)
+{
+  BEGIN();
+
+  struct endpoint *a = &ctx->a, *b = &ctx->b;
+  struct call *a_call = a->call, *b_call = b->call;
+  struct event *e;
+
+  if (print_headings)
+    printf("TEST NUA-7.8.1: Test re-INVITE glare\n");
+
+  a_call->sdp = "m=audio 5008 RTP/AVP 0 8\n";
+  b_call->sdp = "m=audio 5010 RTP/AVP 8\n";
+
+  TEST_1(a_call->nh = 
+        nua_handle(a->nua, a_call, 
+                   SIPTAG_FROM_STR("Alice <sip:alice@example.com>"),
+                   SIPTAG_TO(b->to),
+                   TAG_END()));
+  INVITE(a, a_call, a_call->nh,
+        TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+        SOATAG_USER_SDP_STR(a_call->sdp),
+        TAG_END());
+
+  run_ab_until(ctx, -1, until_ready, -1, accept_and_attempt_reinvite);
+
+  TEST_1(nua_handle_has_active_call(a_call->nh));
+  TEST_1(nua_handle_has_active_call(b_call->nh));
+
+  free_events_in_list(ctx, a->events);
+  free_events_in_list(ctx, b->events);
+
+  /* Check that we can queue INVITEs */
+  INVITE(a, a_call, a_call->nh, TAG_END());
+  INVITE(a, a_call, a_call->nh, TAG_END());
+
+  run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
+
+  free_events_in_list(ctx, a->events);
+  free_events_in_list(ctx, b->events);
+
+  /* Check that INVITE glare works */
+  INVITE(a, a_call, a_call->nh, TAG_END());
+  INVITE(b, b_call, b_call->nh, TAG_END());
+
+  a->flags.n = 0, b->flags.n = 0;
+  run_ab_until(ctx, -1, until_ready2, -1, until_ready2);
+
+  free_events_in_list(ctx, a->events);
+  free_events_in_list(ctx, b->events);
+
+  if (print_headings)
+    printf("TEST NUA-7.8.1: PASSED\n");
+
+
+
+  /* ---------------------------------------------------------------------- */
+  /*
+ A                    B
+ |---------BYE------->|
+ |<--------200--------|
+   */
+
+  if (print_headings)
+    printf("TEST NUA-7.8.2: terminate call\n");
+
+  BYE(a, a_call, a_call->nh, TAG_END());
+  run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+  /*
+   Transitions of A:
+   READY --(T2)--> TERMINATING: nua_bye()
+   TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  TEST_1(!e->next);
+  free_events_in_list(ctx, a->events);
+
+  /* Transitions of B:
+     READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+  */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_bye);
+  TEST(e->data->e_status, 200);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  TEST_1(!e->next);
+  free_events_in_list(ctx, b->events);
+
+  if (print_headings)
+    printf("TEST NUA-7.8.2: PASSED\n");
+
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+  nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+  END();
+}
+
+int accept_and_attempt_reinvite(CONDITION_PARAMS)
+{
+  if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+    return 0;
+
+  save_event_in_list(ctx, event, ep, call);
+
+  if (event == nua_i_prack) {
+    INVITE(ep, call, nh, TAG_END());
+    RESPOND(ep, call, nh, SIP_200_OK,
+           TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+           TAG_END());
+  }
+  else switch (callstate(tags)) {
+  case nua_callstate_received:
+    RESPOND(ep, call, nh, SIP_180_RINGING, 
+           SIPTAG_REQUIRE_STR("100rel"),
+           TAG_END());
+    return 0;
+  case nua_callstate_early:
+    return 0;
+  case nua_callstate_ready:
+    return 1;
+  case nua_callstate_terminated:
+    if (call)
+      nua_handle_destroy(call->nh), call->nh = NULL;
+    return 1;
+  default:
+    return 0;
+  }
+  return 0;
+}
+
+/*
+ X      INVITE
+ |                    |
+ |-------INVITE------>|
+ |<--------200--------|
+ |---------ACK------->|
+*/
+int until_ready2(CONDITION_PARAMS)
+{
+  if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+    return 0;
+
+  save_event_in_list(ctx, event, ep, call);
+
+  if (event == nua_r_invite && status == 491) {
+    if (ep == &ctx->a && ++ctx->b.flags.n >= 2) ctx->b.running = 0;
+    if (ep == &ctx->b && ++ctx->a.flags.n >= 2) ctx->a.running = 0;
+  }
+
+  switch (callstate(tags)) {
+  case nua_callstate_ready:
+    return ++ep->flags.n >= 2;
+  case nua_callstate_terminated:
+    if (call)
+      nua_handle_destroy(call->nh), call->nh = NULL;
+    return 1;
+  default:
+    return 0;
+  }
+}
+
 int test_reinvites(struct context *ctx)
 {
   int retval = 0;
@@ -794,7 +996,10 @@ int test_reinvites(struct context *ctx)
   retval = test_call_hold(ctx);
   
   if (retval == 0)
-    retval |= test_reinvite(ctx);
+    retval = test_reinvite(ctx);
+
+  if (retval == 0)
+    retval = test_reinvite2(ctx);
 
   if (print_headings && retval == 0)
     printf("TEST NUA-7: PASSED\n");
index 71577509a23673829bc16cca5705694e1c898cef..ac1cf2f1753fe5df3778c668a3fb914d2f74d97e 100644 (file)
@@ -118,6 +118,8 @@ int cancel_when_ringing(CONDITION_PARAMS)
   case nua_callstate_proceeding:
     CANCEL(ep, call, nh, TAG_END());
     return 0;
+  case nua_callstate_ready:
+    return 1;
   case nua_callstate_terminated:
     return 1;
   default:
@@ -673,6 +675,82 @@ int test_call_destroy_4(struct context *ctx)
   END();
 }
 
+/* Destroy when one INVITE is queued. */
+int test_call_destroy_5(struct context *ctx)
+{
+  BEGIN();
+
+  struct endpoint *a = &ctx->a,  *b = &ctx->b;
+  struct call *a_call = a->call, *b_call = b->call;
+  struct event *e;
+
+  if (print_headings)
+    printf("TEST NUA-5.7: destroy when re-INVITE is queued\n");
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  INVITE(a, a_call, a_call->nh,
+        TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+        NUTAG_AUTOACK(0),
+        SOATAG_USER_SDP_STR(a_call->sdp),
+        TAG_END());
+
+  INVITE(a, a_call, a_call->nh, TAG_END());
+
+  run_ab_until(ctx, -1, until_terminated, -1, destroy_when_completed);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), ...
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
+  TEST_1(is_offer_sent(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 180);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 200);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_completing); /* COMPLETING */
+  TEST_1(is_answer_recv(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 481);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+  /*
+   Server transitions:
+   INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+   RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+   EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state ... DESTROY
+  */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+  TEST(e->data->e_status, 100);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+  TEST_1(is_offer_recv(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+  TEST_1(is_answer_sent(e->data->e_tags));
+  TEST_1(!e->next);  
+
+  free_events_in_list(ctx, b->events);
+  nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+  if (print_headings)
+    printf("TEST NUA-5.7: PASSED\n");
+
+  END();
+}
+
 int test_call_destroy(struct context *ctx)
 {
   struct endpoint *a = &ctx->a,  *b = &ctx->b;
@@ -685,10 +763,12 @@ int test_call_destroy(struct context *ctx)
     test_call_destroy_1(ctx) ||
     test_call_destroy_2(ctx) ||
     test_call_destroy_3(ctx) ||
-    test_call_destroy_4(ctx);
+    test_call_destroy_4(ctx) ||
+    test_call_destroy_5(ctx);
 }
 
 /* ======================================================================== */
+
 /* Early BYE
 
    A                   B
@@ -731,12 +811,27 @@ int bye_when_ringing(CONDITION_PARAMS)
   }
 }
 
+int bye_when_completing(CONDITION_PARAMS);
+
+static int ack_sent = 0;
+
+size_t count_acks(void *message, size_t len)
+{
+  if (strncasecmp(message, "ACK sip:", 8) == 0)
+    ack_sent++;
+  return len;
+}
+
 int test_early_bye(struct context *ctx)
 {
   BEGIN();
   struct endpoint *a = &ctx->a,  *b = &ctx->b;
   struct call *a_call = a->call, *b_call = b->call;
   struct event *e;
+  struct nat_filter *f = NULL;
+
+  a_call->sdp = "m=audio 5008 RTP/AVP 8";
+  b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
 
   if (print_headings)
     printf("TEST NUA-6.1: BYE call when ringing\n");
@@ -806,6 +901,119 @@ int test_early_bye(struct context *ctx)
   if (print_headings)
     printf("TEST NUA-6.1: PASSED\n");
 
+/* Early BYE 2
+
+   A                   B
+   |-------INVITE------>|
+   |<----100 Trying-----|
+   |                   |
+   |<----180 Ringing----|
+   |<-------200---------|
+   |                   |
+   |--------BYE-------->|
+   |<------200 OK-------|
+   |--------ACK-------->|
+   |                   |
+   |                   |
+*/
+  if (print_headings)
+    printf("TEST NUA-6.2: BYE call when completing\n");
+
+  if (ctx->nat)
+    TEST_1(f = test_nat_add_filter(ctx->nat, count_acks, nat_outbound));
+  ack_sent = 0;
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  INVITE(a, a_call, a_call->nh,
+        TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+        SOATAG_USER_SDP_STR(a_call->sdp),
+        NUTAG_AUTOACK(0),
+        TAG_END());
+
+  run_ab_until(ctx, -1, bye_when_completing, -1, accept_until_terminated);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C2)-> PROCEEDING: nua_r_invite(180, nua_i_state, nua_cancel()
+     PROCEEDING -(C6b)-> TERMINATED: nua_r_invite(487), nua_i_state
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_calling);
+  TEST_1(is_offer_sent(e->data->e_tags));
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 180);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_proceeding); 
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 200);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_completing); 
+  TEST_1(e = e->next);
+
+  TEST_E(e->data->e_event, nua_r_bye); TEST(e->data->e_status, 200);
+  TEST_1(e->data->e_msg);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  TEST_1(!e->next);
+
+  if (ctx->nat) {
+    while (ack_sent == 0)
+      su_root_step(ctx->root, 100);
+    TEST_1(ack_sent > 0);
+    TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+  }
+
+  /*
+   Server transitions:
+   INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
+   RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state
+   EARLY -(S6b)--> TERMINATED: nua_i_cancel, nua_i_state
+  */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
+  TEST(e->data->e_status, 100);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
+  TEST_1(is_offer_recv(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_completed); /* EARLY */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_bye);
+  TEST(e->data->e_status, 200);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+  free_events_in_list(ctx, b->events);
+  nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+  if (print_headings)
+    printf("TEST NUA-6.2: PASSED\n");
+
   END();
 }
 
+int bye_when_completing(CONDITION_PARAMS)
+{
+  if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+    return 0;
+
+  save_event_in_list(ctx, event, ep, call);
+
+  switch (callstate(tags)) {
+  case nua_callstate_completing:
+    ack_sent = 0;
+    BYE(ep, call, nh, TAG_END());
+    return 0;
+  case nua_callstate_terminated:
+    return 1;
+  default:
+    return 0;
+  }
+}
index 0ad5cb08e40d7a0872aec3c0f6e5ede4671c3869..cab13732700a0e5b4e86fd8e55167fb6e5ef2fe9 100644 (file)
@@ -166,12 +166,11 @@ struct context
     struct eventlist specials[1];
 
     /* State flags for complex scenarios */
-    union {
-      struct {
-       unsigned bit0:1, bit1:1, bit2:1, bit3:1;
-       unsigned bit4:1, bit5:1, bit6:1, bit7:1;
-      } b;
+    struct {
       unsigned n;
+      unsigned bit0:1, bit1:1, bit2:1, bit3:1;
+      unsigned bit4:1, bit5:1, bit6:1, bit7:1;
+      unsigned :0;
     } flags;
 
   } a, b, c;
@@ -215,6 +214,7 @@ int save_until_special(CONDITION_PARAMS);
 int until_terminated(CONDITION_PARAMS);
 int until_ready(CONDITION_PARAMS);
 int accept_call(CONDITION_PARAMS);
+int cancel_when_ringing(CONDITION_PARAMS);
 
 int accept_notify(CONDITION_PARAMS);
 
index 1b82885d812e61114db54aeb753eda991cf46fd9..ec7700975c82f1cb10c25fb677dd82fee14e01b7 100644 (file)
@@ -272,21 +272,21 @@ void run_abc_until(struct context *ctx,
   a->last_event = -1;
   a->running = a_condition != NULL && a_condition != save_events;
   a->running |= a_event != -1;
-  a->flags.n = 0;
+  memset(&a->flags, 0, sizeof a->flags);
 
   b->next_event = b_event;
   b->next_condition = b_condition;
   b->last_event = -1;
   b->running = b_condition != NULL && b_condition != save_events;
   b->running |= b_event != -1;
-  b->flags.n = 0;
+  memset(&b->flags, 0, sizeof b->flags);
 
   c->next_event = c_event;
   c->next_condition = c_condition;
   c->last_event = -1;
   c->running = c_condition != NULL && c_condition != save_events;
   c->running |= c_event != -1;
-  c->flags.n = 0;
+  memset(&c->flags, 0, sizeof c->flags);
 
   for (; a->running || b->running || c->running;) {
     su_root_step(ctx->root, 1000);
index b184f1a37b64890edeb3ba4cd107689049ebe51f..861ebb3fc2ee965cf8b8cfa316eb5c26c914a59e 100644 (file)
@@ -228,6 +228,7 @@ test_proxy_init(su_root_t *root, struct proxy *proxy)
                                  URL_STRING_MAKE("sip:0.0.0.0:*"),
                                  NULL, NULL,
                                  NTATAG_UA(0),
+                                 NTATAG_CANCEL_487(0),
                                  NTATAG_SERVER_RPORT(1),
                                  NTATAG_CLIENT_RPORT(1),
                                  TAG_NEXT(proxy->tags));
index 90524a58edf93b6e31bf7bd40ff555f6b80f20f0..34b5ee85fee2d0c61f8e864cbd4074ea8281a901 100644 (file)
@@ -64,15 +64,15 @@ int save_until_notified(CONDITION_PARAMS)
 int save_until_notified_and_responded(CONDITION_PARAMS)
 {
   save_event_in_list(ctx, event, ep, call);
-  if (event == nua_i_notify) ep->flags.b.bit0 = 1;
+  if (event == nua_i_notify) ep->flags.bit0 = 1;
   if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
     if (status >= 300)
       return 1;
     else if (status >= 200)
-      ep->flags.b.bit1 = 1;
+      ep->flags.bit1 = 1;
   }
 
-  return ep->flags.b.bit0 && ep->flags.b.bit1;
+  return ep->flags.bit0 && ep->flags.bit1;
 }
 
 
index e5471ff857c07f0e37047d39ed3d94b2daed8558..5f2b0a9b0afdf3113e35afe6e03f231b1408d4a1 100644 (file)
@@ -422,6 +422,7 @@ void su_pthread_port_wait(su_clone_r rclone)
 
   pthread_mutex_unlock(mom->mutex);
   pthread_mutex_destroy(mom->mutex);
+  pthread_cond_destroy(mom->cv);
 }
 
 struct su_pthread_port_execute