[Freeswitch-svn] [commit] r4284 - in freeswitch/trunk/libs/sofia-sip: . docs libsofia-sip-ua/nta libsofia-sip-ua/nua libsofia-sip-ua/su

Freeswitch SVN mikej at freeswitch.org
Thu Feb 15 00:18:37 EST 2007


Author: mikej
Date: Thu Feb 15 00:18:37 2007
New Revision: 4284

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

Log:
Merge changes from sofia-sip tree:

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 at 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 at 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 at 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.




Modified: freeswitch/trunk/libs/sofia-sip/.update
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/.update	(original)
+++ freeswitch/trunk/libs/sofia-sip/.update	Thu Feb 15 00:18:37 2007
@@ -1 +1 @@
-Tue Feb 13 11:08:00 EST 2007
+Thu Feb 15 00:08:41 EST 2007

Modified: freeswitch/trunk/libs/sofia-sip/RELEASE
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/RELEASE	(original)
+++ freeswitch/trunk/libs/sofia-sip/RELEASE	Thu Feb 15 00:18:37 2007
@@ -5,135 +5,69 @@
 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

Modified: freeswitch/trunk/libs/sofia-sip/configure.ac
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/configure.ac	(original)
+++ freeswitch/trunk/libs/sofia-sip/configure.ac	Thu Feb 15 00:18:37 2007
@@ -11,7 +11,7 @@
 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

Modified: freeswitch/trunk/libs/sofia-sip/docs/release_management.txt
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/docs/release_management.txt	(original)
+++ freeswitch/trunk/libs/sofia-sip/docs/release_management.txt	Thu Feb 15 00:18:37 2007
@@ -61,7 +61,8 @@
     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)
 

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nta/nta.c	Thu Feb 15 00:18:37 2007
@@ -5056,10 +5056,16 @@
     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 @@
   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;

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c	Thu Feb 15 00:18:37 2007
@@ -345,6 +345,15 @@
     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 @@
 	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 @@
 {
   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 @@
 
 /** (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;

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h	Thu Feb 15 00:18:37 2007
@@ -115,6 +115,8 @@
   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 @@
   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 @@
   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 @@
 
 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 @@
   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)
 {

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c	Thu Feb 15 00:18:37 2007
@@ -177,6 +177,9 @@
 				      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 @@
   if (ds->ds_has_session)
     return -1;
   ds->ds_has_session = 1;
+  ds->ds_got_session = 1;
 
   return 0;
 }
@@ -212,10 +216,44 @@
 			       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 @@
 }
 
 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 @@
 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 @@
 				    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 @@
   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 @@
   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 @@
 			    NH_PGET(nh, min_se),
 			    NH_PGET(nh, refresher));
 
+  cr->cr_neutral = 0;
+
   return 0;
 }
 
@@ -568,7 +605,7 @@
   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 @@
   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 @@
 {
   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 @@
       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 @@
 			     ? 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 @@
 {
   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 @@
   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 @@
     ;
   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 */
+    }
+    else if (!reason) {
+      status = 900, phrase = "Cannot send ACK";
+      reason = "SIP;cause=500;text=\"Internal Error\"";
     }
 
-    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\"";
-    }
-  }
+    if (ss && reason)
+      ss->ss_reason = reason;
 
-  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 (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 @@
     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 @@
   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_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 @@
 	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 @@
     }
   }
 
+  sr->sr_neutral = 0;
+
   return 0;
 }
 
@@ -2024,6 +2065,7 @@
   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 @@
   
   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 @@
 			     : status > 100
 			     ? nua_callstate_early
 			     : nua_callstate_received);
+  }
 
   if (status == 180)
     ss->ss_alerting = 1;
@@ -2065,7 +2108,7 @@
   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 @@
 {
   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 @@
   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 @@
 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 @@
     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);
     }
   }
 

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c	Thu Feb 15 00:18:37 2007
@@ -743,14 +743,14 @@
   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 @@
 
   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 @@
     else if (next.msg)
       msg_destroy(next.msg);
 
+    assert(sr->sr_status >= 200 || sr->sr_response.msg);
+
     return retval;
   }
 
@@ -1521,7 +1526,7 @@
   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 @@
     }
   }
   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 @@
 			     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 @@
 
   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 @@
   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 @@
   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)
 {

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c	Thu Feb 15 00:18:37 2007
@@ -1578,6 +1578,310 @@
   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 @@
   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 @@
 
   return retval;
 }
+

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c	Thu Feb 15 00:18:37 2007
@@ -467,7 +467,7 @@
   */
 
   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 @@
   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 @@
    */
 
   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 @@
   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 @@
   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 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 at 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 @@
   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");

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c	Thu Feb 15 00:18:37 2007
@@ -118,6 +118,8 @@
   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 @@
   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 @@
     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_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 @@
   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;
+  }
+}

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h	Thu Feb 15 00:18:37 2007
@@ -166,12 +166,11 @@
     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 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);
 

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c	Thu Feb 15 00:18:37 2007
@@ -272,21 +272,21 @@
   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);

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c	Thu Feb 15 00:18:37 2007
@@ -228,6 +228,7 @@
 				  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));

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c	Thu Feb 15 00:18:37 2007
@@ -64,15 +64,15 @@
 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;
 }
 
 

Modified: freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/su_pthread_port.c
==============================================================================
--- freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/su_pthread_port.c	(original)
+++ freeswitch/trunk/libs/sofia-sip/libsofia-sip-ua/su/su_pthread_port.c	Thu Feb 15 00:18:37 2007
@@ -422,6 +422,7 @@
 
   pthread_mutex_unlock(mom->mutex);
   pthread_mutex_destroy(mom->mutex);
+  pthread_cond_destroy(mom->cv);
 }
 
 struct su_pthread_port_execute



More information about the Freeswitch-svn mailing list