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@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
This commit is contained in:
parent
6e12fb32cd
commit
85c8bcd5ed
|
@ -1 +1 @@
|
|||
Tue Feb 13 11:08:00 EST 2007
|
||||
Thu Feb 15 00:08:41 EST 2007
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
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);
|
||||
|
||||
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); */
|
||||
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 ((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) {
|
||||
if (reason)
|
||||
if (ss && reason)
|
||||
ss->ss_reason = reason;
|
||||
|
||||
if (!ss->ss_reporting && status < 300)
|
||||
if (status < 300)
|
||||
error = 1;
|
||||
else
|
||||
error = -2;
|
||||
}
|
||||
|
||||
nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
|
||||
nua_client_request_remove(cr);
|
||||
|
||||
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;
|
||||
|
||||
SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED);
|
||||
if (nua_server_request_is_pending(sr)) {
|
||||
msg_t *cancel = nta_incoming_getrequest_ackcancel(irq);
|
||||
|
||||
nua_server_respond(sr, NULL);
|
||||
nua_server_report(sr);
|
||||
assert(nta_incoming_status(irq) < 200);
|
||||
|
||||
nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, NULL);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue