[Freeswitch-svn] [commit] r4710 - in freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip: . docs libsofia-sip-ua libsofia-sip-ua-glib libsofia-sip-ua-glib/su-glib libsofia-sip-ua-glib/su-glib/sofia-sip libsofia-sip-ua/bnf libsofia-sip-ua/docs libsofia-sip-ua/features libsofia-sip-ua/http libsofia-sip-ua/http/sofia-sip libsofia-sip-ua/ipt libsofia-sip-ua/ipt/sofia-sip libsofia-sip-ua/iptsec libsofia-sip-ua/iptsec/sofia-sip libsofia-sip-ua/msg libsofia-sip-ua/msg/sofia-sip libsofia-sip-ua/nea libsofia-sip-ua/nta libsofia-sip-ua/nta/sofia-sip libsofia-sip-ua/nth libsofia-sip-ua/nth/sofia-sip libsofia-sip-ua/nua libsofia-sip-ua/nua/sofia-sip libsofia-sip-ua/sdp libsofia-sip-ua/sip libsofia-sip-ua/sip/sofia-sip libsofia-sip-ua/soa libsofia-sip-ua/soa/sofia-sip libsofia-sip-ua/sresolv libsofia-sip-ua/stun libsofia-sip-ua/su libsofia-sip-ua/su/sofia-sip libsofia-sip-ua/tport libsofia-sip-ua/tport/sofia-sip libsofia-sip-ua/url libsofia-sip-ua/url/sofia-sip m4 packages rules utils win32 win32/libsofia-sip-ua win32/libsofia-sip-ua-static win32/tests/test_htable win32/tests/test_memmem win32/tests/test_nta win32/tests/test_nua win32/tests/test_su win32/tests/test_tport win32/tests/torture_rbtree win32/tests/torture_su win32/tests/torture_su_alloc win32/tests/torture_su_bm win32/tests/torture_su_port win32/tests/torture_su_root win32/tests/torture_su_tag win32/tests/torture_su_time win32/tests/torture_su_timer win32/utils/localinfo win32/utils/sip_dig win32/utils/sip_options win32/utils/sip_options_static win32/utils/stunc

Freeswitch SVN mikej at freeswitch.org
Tue Mar 20 23:37:24 EDT 2007


Author: mikej
Date: Tue Mar 20 23:37:15 2007
New Revision: 4710

Added:
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/torture_su_glib_timer.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_inlined.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/msg_inlined.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/test_inlined.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_offer_answer.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/run_test_date
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_inlined.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/poll.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/run_test_su_osx
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_devpoll_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_epoll_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_kqueue_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_poll_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_pthread_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_select_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_socket_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_win32_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/rules/
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/rules/recursive.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/rules/sofia.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/rules/valcheck.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/SofiaSIP.sln
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/libsofia-sip-ua-static/libsofia_sip_ua_static.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/libsofia-sip-ua/libsofia_sip_ua.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/test_htable/test_htable.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/test_memmem/test_memmem.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/test_nta/test_nta.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/test_nua/test_nua.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/test_su/test_su.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/test_tport/test_tport.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_rbtree/torture_rbtree.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su/torture_su.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su_alloc/torture_su_alloc.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su_bm/torture_su_bm.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su_port/torture_su_port.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su_root/torture_su_root.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su_tag/torture_su_tag.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su_time/torture_su_time.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/torture_su_timer/torture_su_timer.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/utils/localinfo/localinfo.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/utils/sip_dig/sip_dig.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/utils/sip_options/sip_options.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/utils/sip_options_static/sip_options_static.vcproj
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/utils/stunc/stunc.vcproj
Modified:
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/AUTHORS
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/COPYRIGHTS
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/README
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE.template
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/configure.ac
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/build_system.txt
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/devel_platform_notes.txt
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/release_management.txt
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/sofia-sip/su_glib.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/su_source.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ChangeLog
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/bnf.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/torture_bnf.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/features/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_basic.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_parser.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_header.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/test_http.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/base64.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/string0.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/torture_base64.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_client.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_module.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_plugin_delayed.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_plugin.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.awk
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/sofia-sip/msg_parser.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/nea_server.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_check.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/http-server.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_client.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_server.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_registrar.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_extension.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/torture_sdp.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_event.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra_headers.txt
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_mime.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_prack.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_reason.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_refer.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_tag_class.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_header.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_parser.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_util.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_date.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_sip_msg.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/validator.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Doxyfile
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/run_test_sresolv
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_blocking.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.docs
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/stun.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/addrinfo.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/run_test_su
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_bm.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_debug.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/tstdef.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc_lock.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_bm.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_localinfo.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_os_nw.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_osx_runloop.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_port.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_root.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_time.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_timer.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_wait.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/tag_dll.awk
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/test_htable.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/test_memmem.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/test_su.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_rbtree.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_alloc.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_bm.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_port.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_root.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_root_osx.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_tag.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_time.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/torture_su_timer.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport_tag.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/test_tport.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport_stub_stun.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_sctp.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tcp.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_udp.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/url/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/url/sofia-sip/url.h
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/url/torture_url.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/url/url.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/m4/sac-general.m4
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/m4/sac-openssl.m4
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/m4/sac-su.m4
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/m4/sac-su2.m4
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/m4/sac-tport.m4
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/packages/sofia-sip.spec.in
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/utils/sip-options.c
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/Makefile.am
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/SofiaSIP.dsw
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/build_sources.cmd
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/check.cmd
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/config.h.in
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/libsofia-sip-ua-static/libsofia_sip_ua_static.dsp
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/libsofia-sip-ua/libsofia_sip_ua.dsp
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/tests/test_nua/test_nua.dsp
   freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/win32/version_files.cmd

Log:
merge up current sofia-sip changes from darcs

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/AUTHORS
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/AUTHORS	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/AUTHORS	Tue Mar 20 23:37:15 2007
@@ -10,6 +10,8 @@
 
 Chan, Tat <first.surname at nokia.com>
 Ciarkowski, Andrzej <wp-voigtkampff -at users -dot sourceforge -dot net>
+Denis-Courmont, Remi <first.surname at nokia.com>
+Filonenko Roman <shkoder -at ua -dot fm>
 Haataja, Mikko <first.surname at nokia.com>
 Jacobs, Remeres <first.surname at nokia.com>
 Jalava, Teemu <first.surname at nokia.com>
@@ -26,9 +28,7 @@
 Whittaker, Colin <colinw -at occamnetworks -dot com>
 Zabaluev, Mikhail <first.surname at nokia.com>
 
-
 Note: for details on who did what, see the version control 
       system change history, and release notes for past releases at
       http://sofia-sip.sourceforge.net/relnotes/
-
-      
\ No newline at end of file
+ 
\ No newline at end of file

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/COPYRIGHTS
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/COPYRIGHTS	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/COPYRIGHTS	Tue Mar 20 23:37:15 2007
@@ -221,3 +221,31 @@
 been advised of the possibility of such damages.
 
 ----------------------------------------------------------------------------
+
+libsofia-sip-ua/su/poll.c
+
+The package also contains files from GNU C Library by Free Software
+Foundation.
+
+These files are distributed with the following copyright notice:
+
+Copyright (C) 1994,1996,1997,1998,1999,2001,2002
+Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with the GNU C Library; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+----------------------------------------------------------------------------

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -27,6 +27,10 @@
 EXTRA_DIST += 	m4/sac-general.m4 m4/sac-su.m4 \
 		m4/sac-su2.m4 m4/sac-tport.m4 m4/sac-openssl.m4
 
+EXTRA_DIST +=   docs/build_system.txt \
+		docs/devel_platform_notes.txt \
+		docs/release_management.txt
+
 dist_man_MANS = man/man1/sip-date.1 man/man1/sip-options.1 \
 		man/man1/localinfo.1 man/man1/addrinfo.1 \
 		man/man1/stunc.1 man/man1/sip-dig.1
@@ -44,7 +48,7 @@
 
 CLEANFILES = $(dist_man_MANS)
 
-coverage built-sources clean-built-sources doxygen:
+coverage built-sources clean-built-sources valcheck doxygen:
 	for i in libsofia-sip-ua $(GLIB_SUBDIRS) ; do $(MAKE) $(AM_MAKEFLAGS) -C $$i $@ ; done
 
 .PHONY: coverage built-sources clean-built-sources doxygen manpages

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/README
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/README	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/README	Tue Mar 20 23:37:15 2007
@@ -46,7 +46,6 @@
 - localinfo (libsofia-sip-ua/su), prints information about
   local network interfaces 
 
-
 References
 ----------
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE	Tue Mar 20 23:37:15 2007
@@ -6,7 +6,8 @@
 --------------------------
 
 <changes since last written in freshmeat.net "Changes:" style;
- and in less than 10 lines />
+ 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...
@@ -24,53 +25,52 @@
 
 libsofia-sip-ua:
 - **template**: Added foobar() function (sofia-sip/foobar.h).
-- Added sip_is_allowed() function and k_bitmap field to the 
-  sip_allow_t structure 
-- 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 kqueue and /dev/poll interfaces (su_devpoll_port_create(),
+  su_devpoll_clone_start(), su_kqueue_port_create(), su_kqueue_clone_start()
+  in <sofia-sip/su_wait.h>)
+- Added SIP_IS_ALLOWED() macro to <sofia-sip/sip_util.h>
+- Fixed NUTAG_APPL_METHOD() implementation for UPDATE and PRACK as documented
+  ("100rel" and "precondition" extensions now require explicit calls to
+  nua_update() and nua_prack() if those methods are included in
+  NUTAG_APPL_METHOD())
 - 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.
+- 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
 ----------------------------
 
 <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
+ - 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
+   plus a _brief_ description of what was contributed
  - roughly sorted by number of patches accepted
 /> 
 
 - **template**: First Surname (patch to nua/soa/msg)
-- Petteri Puolakka (patch to stun)
-- Mikhail Zabluev (patch to su-glib mainloop integration)
+- Martti Mela (kqueue interface to Max OS X and FreeBSD)
+- Pekka Pessi (/dev/poll interface to Solaris)
+- Michael Jerris (Solaris patches)
+- Colin Whittaker (sresolv patch)
 
 See the AUTHORS file in the distribution package.
 
 Notes on new features
 ---------------------
 
-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 */ 
-  }
+The Sofia-SIP has been compiled and tested on Solaris. The /dev/poll
+interface (in su_devpoll_port.c) has been added for Solaris. Likewise, the
+kqueue interface (in su_kqueue_port.c) has been added for FreeBSD and Mac OS
+X.
 
 <information about major new features
  - new/changed/removed functionality
@@ -88,15 +88,19 @@
 />
 
 - **template**: #9499652 sf.net bug item title
-
-- 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.
+- Fixed problems resuming DNS after server or link downtime.
+  Problem reported by Colin Whittaker.
+- Fixed NUTAG_APPL_METHOD() implementation for UPDATE and PRACK as documented
+- Fixed crashes in nua state engines:
+  - when nua_invite() was called second time before receiving
+    final response to first INVITE
+  - when UAS expected PRACK but received CANCEL
+  - when UAC received error response to PRACK, it tried to send BYE and crashed
+  - when UAS rejected initial request, the subsequent request with same 
+    dialog id (Call-ID, From-tag) crashed (dialog cleanup code left dialog
+    dangling)
+  Problems reported by Michael Jerris
+- Fixed crash in nta state engine:
+  - DNS resolver failure in non-invite transctions crashed
+- Fixed sdp handling when soa is disabled (NUTAG_MEDIA_ENABLE(0)).
+  Problem reported by Marcin Michalak

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE.template
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE.template	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/RELEASE.template	Tue Mar 20 23:37:15 2007
@@ -6,7 +6,8 @@
 --------------------------
 
 <changes since last written in freshmeat.net "Changes:" style;
- and in less than 10 lines />
+ 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...
@@ -39,9 +40,11 @@
 
 <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
+ - 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
+   plus a _brief_ description of what was contributed
  - roughly sorted by number of patches accepted
 /> 
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/configure.ac
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/configure.ac	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/configure.ac	Tue Mar 20 23:37:15 2007
@@ -11,14 +11,14 @@
 dnl ---------------------------
 
 dnl update both the version for AC_INIT and the LIBSOFIA_SIP_UA_MAJOR_MINOR
-AC_INIT([sofia-sip], [1.12.4work])
+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
 AC_SUBST(include_sofiadir, '${includedir}/sofia-sip-1.12')
-AC_SUBST(LIBVER_SOFIA_SIP_UA_CUR, [3])
+AC_SUBST(LIBVER_SOFIA_SIP_UA_CUR, [4])
 AC_SUBST(LIBVER_SOFIA_SIP_UA_REV, [0])
-AC_SUBST(LIBVER_SOFIA_SIP_UA_AGE, [3])
+AC_SUBST(LIBVER_SOFIA_SIP_UA_AGE, [4])
 AC_SUBST(LIBVER_SOFIA_SIP_UA_SOVER, [0]) # CUR-AGE
 AC_SUBST(LIBVER_SOFIA_SIP_UA_GLIB_CUR, [3])
 AC_SUBST(LIBVER_SOFIA_SIP_UA_GLIB_REV, [0])
@@ -53,8 +53,15 @@
 dnl Add parameters for aclocal
 AC_SUBST(ACLOCAL_AMFLAGS, "-I m4")
 
-AC_CHECK_PROG([DOXYGEN], [doxygen], [doxygen], [echo])
-AM_CONDITIONAL([HAVE_DOXYGEN], [test $DOXYGEN = doxygen])
+AC_ARG_WITH(doxygen,
+[  --with-doxygen[[=CMD]]    use doxygen command CMD [[doxygen]]],[
+case $enable_doxygen in 
+yes ) doxygen=doxygen ;;
+no ) doxygen=echo ;;
+esac], doxygen=doxygen)
+
+AC_CHECK_PROG([DOXYGEN], [doxygen], [$doxygen], [echo])
+AM_CONDITIONAL([HAVE_DOXYGEN], [test $DOXYGEN != echo])
 
 ### checks for libraries
 ### --------------------
@@ -71,7 +78,7 @@
 AC_DEFINE([HAVE_SOFIA_SMIME], 0, [Define to 1 if we use S/MIME library])
 
 AC_ARG_ENABLE(stun,
-[  --disable-stun              disable stun module (enabled)],
+[  --disable-stun          disable stun module (enabled)],
  , enable_stun=yes)
 
 if test x$enable_stun = xno ; then
@@ -83,10 +90,19 @@
 else
   AC_DEFINE([HAVE_SOFIA_STUN], 1, [Define to 1 if we use STUN library])
 fi
+AM_CONDITIONAL([HAVE_STUN], [test "x$enable_stun" = xyes])
+
+AC_ARG_ENABLE(nth,
+[  --disable-nth           disable nth and http modules (enabled)],
+ , enable_nth=yes)
+AM_CONDITIONAL([HAVE_NTH], [test "x$enable_nth" = xyes])
+if test x$enable_nth = xyes ; then
+  AC_DEFINE([HAVE_SOFIA_NTH], 1, [Define to 1 if we use NTH library])
+fi
 
 dnl Disable NTLM support by default
 AC_ARG_ENABLE(ntlm,
-[  --enable-ntlm               enable NTLM support (disabled)],
+[  --enable-ntlm           enable NTLM support [[disabled]]],
  , enable_ntlm=no)
 
 if test x$enable_ntlm = xyes ; then
@@ -104,22 +120,97 @@
 
 ### checks for declarations
 ### -----------------------
-AC_CHECK_DECL([SIGPIPE], [
-AC_DEFINE([HAVE_SIGPIPE], 1, [Define to 1 if you have SIGPIPE])],,[
-#include <signal.h>
-])
 
 ### checks for types
 ### ----------------
 
 AC_TYPE_SIGNAL
-AC_TYPE_LONGLONG([
-AC_DEFINE([LLU], ["%llu"], [Define to format (%llu) for unsigned long long])dnl
-AC_DEFINE([LLI], ["%lli"], [Define to format (%lli) for long long])dnl
-AC_DEFINE([LLX], ["%llx"], [Define to format (%llx) for long long hex])dnl
+AC_TYPE_LONGLONG
+
+dnl
+dnl Define HAVE_C99_FORMAT to 1 if the formatted IO functions (printf/scanf
+dnl et.al.) support the C99 'size specifiers', namely ll, hh, j, z, t
+dnl (representing long long int, char, intmax_t, size_t, ptrdiff_t). Some C
+dnl compilers supported these specifiers prior to C99 as an extension.
+dnl
+AC_CACHE_CHECK([whether IO functions support C99 size specifiers],
+[ac_cv_c_c99_format],[
+
+ac_cv_c_c99_format=yes
+
+AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+[[char buf[64];
+ if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5)
+  exit(1);
+ else if (strcmp(buf, "12345"))
+  exit(2);]])],
+ [ac_cv_c_c99_format=yes],
+ [ac_cv_c_c99_format=no],
+ [ac_cv_c_c99_format=yes])
 ])
-AC_DEFINE([MOD_ZD], ["%zd"], [Define printf() modifier for ssize_t])
-AC_DEFINE([MOD_ZU], ["%zu"], [Define printf() modifier for size_t])
+
+if test $ac_cv_c_c99_format = yes; then
+  AC_DEFINE([HAVE_C99_FORMAT], [1], [Define to 1 if printf supports C99 size specifiers])dnl
+
+  AC_DEFINE([LLU], ["%llu"], [Format (%llu) for unsigned long long])dnl
+  AC_DEFINE([LLI], ["%lli"], [Format (%lli) for long long])dnl
+  AC_DEFINE([LLX], ["%llx"], [Format (%llx) for long long hex])dnl
+  AC_DEFINE([MOD_ZD], ["%zd"], [Define printf() modifier for ssize_t])dnl
+  AC_DEFINE([MOD_ZU], ["%zu"], [Define printf() modifier for size_t])dnl
+
+else
+
+AC_CACHE_CHECK([whether IO functions support size specifier for long long],
+[ac_cv_c_ll_format],[
+
+ac_cv_c_ll_format=yes
+
+AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+[[char buf[64];
+ if (sprintf(buf, "%lld", (long long int)1) != 1)
+  exit(1);
+ else if (strcmp(buf, "1"))
+  exit(2);]])],
+ [ac_cv_c_ll_format=yes],
+ [ac_cv_c_ll_format=no],
+ [ac_cv_c_ll_format=yes])
+])
+
+if test $ac_cv_c_ll_format = yes; then
+  AC_DEFINE([LLU], ["%llu"], [Format (%llu) for unsigned long long])dnl
+  AC_DEFINE([LLI], ["%lli"], [Format (%lli) for long long])dnl
+  AC_DEFINE([LLX], ["%llx"], [Format (%llx) for long long hex])dnl
+else
+  AC_MSG_ERROR("printf cannot handle 64-bit integers")
+fi
+
+AC_CACHE_CHECK([whether IO functions support size specifier for size_t],
+[ac_cv_c_z_format],[
+
+ac_cv_c_z_format=yes
+
+AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+[[char buf[64];
+ if (sprintf(buf, "%zd", (size_t)1) != 1)
+  exit(1);
+ else if (strcmp(buf, "1"))
+  exit(2);]])],
+ [ac_cv_c_z_format=yes],
+ [ac_cv_c_z_format=no],
+ [ac_cv_c_z_format=yes])
+])
+
+if test $ac_cv_c_z_format = yes; then
+  AC_DEFINE([MOD_ZD], ["%ld"], [Define printf() modifier for ssize_t])dnl
+  AC_DEFINE([MOD_ZU], ["%lu"], [Define printf() modifier for size_t])dnl
+else 
+  dnl Cross fingers
+  AC_MSG_WARN("printf cannot handle size_t, using long instead")
+  AC_DEFINE([MOD_ZD], ["%ld"], [Define printf() modifier for ssize_t])dnl
+  AC_DEFINE([MOD_ZU], ["%lu"], [Define printf() modifier for size_t])dnl
+fi 
+
+fi
 
 ### checks for structures
 ### ---------------------

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/build_system.txt
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/build_system.txt	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/build_system.txt	Tue Mar 20 23:37:15 2007
@@ -38,6 +38,11 @@
   separately in ``DIST_SOURCES`` variable (otherwise ``make dist``
   will fail)
 
+Makefile fragments
+------------------
+
+Some common makefile rules are in 'rules' subdirectory.
+
 Maintainer mode
 ---------------
 
@@ -54,13 +59,16 @@
 Running tests
 =============
 
-Sofia-SIP has quite complete suite of test cases. It is prudent to
-run them while making changes and before committing them to revision
-control system. However, running certain tests takes quite a long
-time. Therefore, they are run only if the environment variable
+Sofia-SIP has quite complete suite of test cases. It is prudent to run
+them while making changes and before committing them to revision control
+system. However, running certain tests takes quite a long time to
+execture. Therefore, they are run only if the environment variable
 EXPENSIVE_CHECKS has been set. EXPENSIVE_CHECKS is also set by the build
 system if configure option '--enable-expensive-checks' has been used.
 
+On hosts with i386 architecture, it is possible to run tests under
+valgrind. Use the make target 'valcheck' for that purpose.
+
 Code-tree layout
 ================
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/devel_platform_notes.txt
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/devel_platform_notes.txt	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/devel_platform_notes.txt	Tue Mar 20 23:37:15 2007
@@ -11,14 +11,30 @@
 be at least 1.7. You can avoid running autoreconf explicitly if you use
 ./configure option --enable-maintainer-mode.
 
+Notes to distributors
+----------------------
+
+Build options such as "--disable-stun" (HAVE_SOFIA_STUN) and 
+"--disable-nth" (HAVE_SOFIA_NTH) modify the public library API/ABI,
+by omitting certain interfaces from the resulting library and installed
+header files.
+
+Options such as '--disable-size-compat' modify the library
+ABI by changing the types used in public library function 
+signatures.
+
 Generic POSIX (GNU/Linux, BSD, ...) 
 -----------------------------------
 
 Sofia-SIP should compile out-of-the-box on generic POSIX
 machines. Use the standard GNU autotool 'configure+make'
-procedure to build the software. See top-level INSTALL
+procedure to build the software. See top-level README file
 for more information.
 
+The configure script accepts various options. See "./configure --help"
+for the full list.
+
+
 Mac OS X 
 --------
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/release_management.txt
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/release_management.txt	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/docs/release_management.txt	Tue Mar 20 23:37:15 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/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -31,16 +31,6 @@
 	     docs/Doxyfile.conf \
 	     docs/Doxyfile.version
 
-built-sources: built-sources-recursive 
-clean-built-sources: clean-built-sources-recursive
-
-built-sources-recursive clean-built-sources-recursive:
-	target=`echo $@ | sed s/-recursive//`; \
-	list='$(SUBDIRS)'; for subdir in $$list; do \
-	  echo "Making $@ in $$subdir"; \
-	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$target) \
-	done;
-
 doxygen: built-sources
 	@cd ${srcdir} ;\
 	mkdir -p docs docs/html &&\
@@ -53,4 +43,6 @@
 	done
 	cd ${srcdir}/docs/html && ../../${top_srcdir}/libsofia-sip-ua/docs/hide_emails.sh
 
+include $(top_srcdir)/rules/recursive.am
+
 .PHONY: built-sources built-sources-am doxygen 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -58,4 +58,4 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../../libsofia-sip-ua/sofia.am
+include $(top_srcdir)/rules/sofia.am

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/sofia-sip/su_glib.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/sofia-sip/su_glib.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/sofia-sip/su_glib.h	Tue Mar 20 23:37:15 2007
@@ -43,6 +43,7 @@
 
 SOFIAPUBFUN su_root_t *su_glib_root_create(su_root_magic_t *) __attribute__((__malloc__));
 SOFIAPUBFUN GSource *su_glib_root_gsource(su_root_t *);
+SOFIAPUBFUN void su_glib_prefer_gsource(void);
 
 SOFIA_END_DECLS
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/su_source.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/su_source.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/su_source.c	Tue Mar 20 23:37:15 2007
@@ -25,7 +25,11 @@
 /**
  * @file su_source.c
  * @brief Wrapper for glib GSource.
- * *  
+ *  
+ * Refs: 
+ *  - http://sofia-sip.sourceforge.net/refdocs/su/group__su__wait.html
+ *  - http://developer.gnome.org/doc/API/glib/glib-the-main-event-loop.html
+ *
  * @author Pekka Pessi <Pekka.Pessi at nokia.com>.
  * 
  * @date Created: Thu Mar  4 15:15:15 2004 ppessi
@@ -36,12 +40,6 @@
 #include "config.h"
 #endif
 
-#include <stdlib.h>
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-
 #include <glib.h>
 
 #define SU_PORT_IMPLEMENTATION 1
@@ -57,16 +55,26 @@
 #include "su_port.h"
 #include "sofia-sip/su_alloc.h"
 
-static su_port_t *su_source_create(void) __attribute__((__malloc__));
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#if 1
+#define PORT_LOCK_DEBUG(x)  ((void)0)
+#else
+#define PORT_LOCK_DEBUG(x)  printf x
+#endif
+
+static su_port_t *su_source_port_create(void) __attribute__((__malloc__));
 static gboolean su_source_prepare(GSource *gs, gint *return_tout);
 static gboolean su_source_check(GSource *gs);
 static gboolean su_source_dispatch(GSource *gs,
-			    GSourceFunc callback,
-			    gpointer user_data);
+				   GSourceFunc callback,
+				   gpointer user_data);
 static void su_source_finalize(GSource *source);
 
-static int su_source_getmsgs(su_port_t *self);
-
 static
 GSourceFuncs su_source_funcs[1] = {{
     su_source_prepare,
@@ -106,21 +114,22 @@
 static su_duration_t su_source_step(su_port_t *self, su_duration_t tout);
 static int su_source_own_thread(su_port_t const *port);
 static int su_source_add_prepoll(su_port_t *port,
-			       su_root_t *root, 
-			       su_prepoll_f *, 
-			       su_prepoll_magic_t *);
+				 su_root_t *root, 
+				 su_prepoll_f *, 
+				 su_prepoll_magic_t *);
 static int su_source_remove_prepoll(su_port_t *port,
 				  su_root_t *root);
-static su_timer_t **su_source_timers(su_port_t *port);
 static int su_source_multishot(su_port_t *self, int multishot);
-static int su_source_threadsafe(su_port_t *port);
 
-static
-su_port_vtable_t const su_source_vtable[1] =
+static char const *su_source_name(su_port_t const *self);
+
+static 
+su_port_vtable_t const su_source_port_vtable[1] =
   {{
-      /* su_vtable_size: */ sizeof su_source_vtable,
+      /* su_vtable_size: */ sizeof su_source_port_vtable,
       su_source_lock,
       su_source_unlock,
+
       su_source_incref,
       su_source_decref,
 
@@ -138,32 +147,38 @@
       su_source_own_thread,
       su_source_add_prepoll,
       su_source_remove_prepoll,
-      su_source_timers,
+      su_base_port_timers,
       su_source_multishot,
-      su_source_threadsafe
-
+      su_base_port_threadsafe,
+      /*su_source_yield*/ NULL,
+      /*su_source_wait_events*/ NULL,
+      su_base_port_getmsgs,
+      su_base_port_getmsgs_from,
+      su_source_name,
+      su_base_port_start_shared,
+      su_base_port_wait,
+      NULL,
     }};
 
+static char const *su_source_name(su_port_t const *self)
+{
+  return "GSource";
+}
+
 /** 
  * Port is a per-thread reactor.  
  *
  * Multiple root objects executed by single thread share a su_port_t object. 
  */
 struct su_source_s {
-  su_home_t        sup_home[1];
-  su_port_vtable_t const *sup_vtable;
+  su_base_port_t   sup_base[1];
   
   GThread         *sup_tid;
   GStaticMutex     sup_mutex[1];
-  GStaticRWLock    sup_ref[1];
 
-  GSource         *sup_source;
-  GMainLoop       *sup_main_loop;
+  GSource         *sup_source;	/**< Backpointer to source */
+  GMainLoop       *sup_main_loop; /**< Reference to mainloop while running */
   
-  /* Message list - this is protected by lock  */
-  su_msg_t        *sup_head;
-  su_msg_t       **sup_tail;
-
   /* Waits */
   unsigned         sup_registers; /** Counter incremented by 
 				      su_port_register() or 
@@ -177,9 +192,6 @@
   su_wakeup_f     *sup_wait_cbs; 
   su_wakeup_arg_t**sup_wait_args; 
   su_root_t      **sup_wait_roots; 
-
-  /* Timer list */
-  su_timer_t      *sup_timers;
 };
 
 typedef struct _SuSource
@@ -194,10 +206,6 @@
 #define SU_SOURCE_INCREF(p, f)    (g_source_ref(p->sup_source))
 #define SU_SOURCE_DECREF(p, f)    (g_source_unref(p->sup_source))
 
-#define SU_SOURCE_INITLOCK(p)     (g_static_mutex_init((p)->sup_mutex))
-#define SU_SOURCE_LOCK(p, f)      (g_static_mutex_lock((p)->sup_mutex))
-#define SU_SOURCE_UNLOCK(p, f)    (g_static_mutex_unlock((p)->sup_mutex))
-
 #else
 
 /* Debugging versions */
@@ -205,15 +213,6 @@
 #define SU_SOURCE_DECREF(p, f)    do { printf("decref(%p) by %s\n", (p), f), \
   g_source_unref(p->sup_source); } while(0)
 
-#define SU_SOURCE_INITLOCK(p) \
-   (g_static_mutex_init((p)->sup_mutex), printf("init_lock(%p)\n", p))
-
-#define SU_SOURCE_LOCK(p, f)    \
-   (printf("%ld at %s locking(%p)...", g_thread_self(), f, p), g_static_mutex_lock((p)->sup_mutex), printf(" ...%ld at %s locked(%p)...", g_thread_self(), f, p))
-
-#define SU_SOURCE_UNLOCK(p, f)  \
-  (g_static_mutex_unlock((p)->sup_mutex), printf(" ...%ld at %s unlocked(%p)\n", g_thread_self(), f, p))
-
 #endif
 
 #if HAVE_FUNC
@@ -229,7 +228,7 @@
 /** Create a root that uses GSource as reactor */
 su_root_t *su_glib_root_create(su_root_magic_t *magic)
 {
-  return su_root_create_with_port(magic, su_source_create());
+  return su_root_create_with_port(magic, su_source_port_create());
 }
 
 /** Deprecated */
@@ -238,6 +237,15 @@
   return su_glib_root_create(magic);
 }
 
+/** 
+ * Returns a GSource object for the root 
+ *
+ * Note that you need to unref the GSource with g_source_unref() 
+ * before destroying the root object.
+ *
+ * @return NULL on error (for instance if root was not created with 
+ *         su_glib_root_create())
+ */
 GSource *su_glib_root_gsource(su_root_t *root)
 {
   g_assert(root);
@@ -246,41 +254,20 @@
 
 /*=============== Private function definitions ===============*/
 
-/**@internal
- *
- * Allocates and initializes a reactor and message port object.
- *
- * @return
- *   If successful a pointer to the new message port is returned, otherwise
- *   NULL is returned.  
- */
-su_port_t *su_source_create(void)
+/** Initialize source port */
+int su_source_port_init(su_port_t *self,
+			GSource *gs,
+			su_port_vtable_t const *vtable)
 {
-  SuSource *ss;
-
-  SU_DEBUG_9(("su_source_create() called\n"));
-
-  ss = (SuSource *)g_source_new(su_source_funcs, (sizeof *ss));
-
-  if (ss) {
-    su_port_t *self = ss->ss_port;
-
-    self->sup_vtable = su_source_vtable;
-    self->sup_source = ss->ss_source;
-    
-    SU_SOURCE_INITLOCK(self);
-
-    self->sup_tail = &self->sup_head;
-    self->sup_tid = g_thread_self();
+  if (su_base_port_init(self, vtable) < 0)
+    return -1;
 
-    SU_DEBUG_9(("su_source_with_main_context() returns %p\n", self));
+  self->sup_source = gs;
+  self->sup_tid = g_thread_self();
 
-    return self;
-  } else {
-    su_perror("su_source_with_main_context(): su_home_clone");
-    SU_DEBUG_9(("su_source_with_main_context() fails\n"));
-    return NULL;
-  }
+  g_static_mutex_init(self->sup_mutex);
+  
+  return 0;
 }
 
 /** @internal Destroy a port. */
@@ -294,23 +281,70 @@
 
   SU_DEBUG_9(("su_source_finalize() called\n"));
 
-  if (self->sup_waits) 
-    free(self->sup_waits), self->sup_waits = NULL;
-  if (self->sup_wait_cbs)
-    free(self->sup_wait_cbs), self->sup_wait_cbs = NULL;
-  if (self->sup_wait_args)
-    free(self->sup_wait_args), self->sup_wait_args = NULL;
-  if (self->sup_wait_roots)
-    free(self->sup_wait_roots), self->sup_wait_roots = NULL;
-  if (self->sup_indices)
-    free(self->sup_indices), self->sup_indices = NULL;
+  g_static_mutex_free(self->sup_mutex);
+
+  su_base_port_deinit(self);
+
+  su_home_deinit(self->sup_base->sup_home);
+}
+
+void su_source_port_lock(su_port_t *self, char const *who)
+{
+  PORT_LOCK_DEBUG(("%p at %s locking(%p)...",
+		   (void *)g_thread_self(), who, self));
+
+  g_static_mutex_lock(self->sup_mutex);
+
+  PORT_LOCK_DEBUG((" ...%p at %s locked(%p)...", 
+		   (void *)g_thread_self(), who, self));
+}
+
+void su_source_port_unlock(su_port_t *self, char const *who)
+{
+  g_static_mutex_unlock(self->sup_mutex);
+
+  PORT_LOCK_DEBUG((" ...%p at %s unlocked(%p)\n", 
+		   (void *)g_thread_self(), who, self));
+}
+
+/** @internal Send a message to the port. */
+int su_source_send(su_port_t *self, su_msg_r rmsg)
+{
+  int wakeup = su_base_port_send(self, rmsg);
+  GMainContext *gmc;
+
+  if (wakeup < 0)
+    return -1;
+  if (wakeup == 0)
+    return 0;
+
+  gmc = g_source_get_context(self->sup_source);
+
+  if (gmc)
+    g_main_context_wakeup(gmc);
 
-  su_home_deinit(self->sup_home);
+  return 0;
 }
 
+/** @internal
+ * Checks if the calling thread owns the port object.
+ *
+ * @param self pointer to a port object
+ *
+ * @retval true (nonzero) if the calling thread owns the port,
+ * @retval false (zero) otherwise.
+ */
+int su_source_own_thread(su_port_t const *self)
+{
+  return self == NULL || SU_SOURCE_OWN_THREAD(self);
+}
+
+/* -- Registering and unregistering ------------------------------------- */
+
 /* Seconds from 1.1.1900 to 1.1.1970 */
 #define NTP_EPOCH 2208988800UL 
 
+/** Prepare to wait - calculate time to next timer */
 static
 gboolean su_source_prepare(GSource *gs, gint *return_tout)
 {
@@ -319,12 +353,12 @@
 
   enter;
   
-  if (self->sup_head) {
+  if (self->sup_base->sup_head) {
     *return_tout = 0;
     return TRUE;
   }
 
-  if (self->sup_timers) {
+  if (self->sup_base->sup_timers) {
     su_time_t now;
     GTimeVal  gtimeval;
     su_duration_t tout;
@@ -333,7 +367,7 @@
     now.tv_sec = gtimeval.tv_sec + 2208988800UL;
     now.tv_usec = gtimeval.tv_usec;
 
-    tout = su_timer_next_expires(self->sup_timers, now);
+    tout = su_timer_next_expires(self->sup_base->sup_timers, now);
 
     *return_tout = (tout < 0 || tout > (su_duration_t)G_MAXINT)?
 	-1 : (gint)tout;
@@ -376,10 +410,10 @@
 
   enter;
 
-  if (self->sup_head)
-    su_source_getmsgs(self);
+  if (self->sup_base->sup_head)
+    su_base_port_getmsgs(self);
 
-  if (self->sup_timers) {
+  if (self->sup_base->sup_timers) {
     su_time_t now;
     GTimeVal  gtimeval;
     su_duration_t tout;
@@ -392,7 +426,7 @@
     now.tv_sec = gtimeval.tv_sec + 2208988800UL;
     now.tv_usec = gtimeval.tv_usec;
 
-    timers = su_timer_expire(&self->sup_timers, &tout, now);
+    timers = su_timer_expire(&self->sup_base->sup_timers, &tout, now);
   }
 
 #if SU_HAVE_POLL
@@ -424,12 +458,20 @@
 
 static void su_source_lock(su_port_t *self, char const *who)
 {
-  SU_SOURCE_LOCK(self, who);
+  PORT_LOCK_DEBUG(("%p at %s locking(%p)...",
+		   (void *)g_thread_self(), who, self));
+  g_static_mutex_lock(self->sup_mutex);
+
+  PORT_LOCK_DEBUG((" ...%p at %s locked(%p)...", 
+		   (void *)g_thread_self(), who, self));
 }
 
 static void su_source_unlock(su_port_t *self, char const *who)
 {
-  SU_SOURCE_UNLOCK(self, who);
+  g_static_mutex_unlock(self->sup_mutex);
+
+  PORT_LOCK_DEBUG((" ...%p at %s unlocked(%p)\n", 
+		   (void *)g_thread_self(), who, self));
 }
 
 static void su_source_incref(su_port_t *self, char const *who)
@@ -448,81 +490,6 @@
   return self->sup_source;
 }
 
-/** @internal Send a message to the port. */
-int su_source_send(su_port_t *self, su_msg_r rmsg)
-{
-  enter;
-  
-  if (self) {
-    su_msg_t *msg;
-    GMainContext *gmc;
-
-    SU_SOURCE_LOCK(self, "su_source_send");
-    
-    msg = rmsg[0]; rmsg[0] = NULL;
-    *self->sup_tail = msg;
-    self->sup_tail = &msg->sum_next;
-
-    SU_SOURCE_UNLOCK(self, "su_source_send");
-
-    gmc = g_source_get_context(self->sup_source);
-
-    if (gmc)
-      g_main_context_wakeup(gmc);
-
-    return 0;
-  }
-  else {
-    su_msg_destroy(rmsg);
-    return -1;
-  }
-}
-
-/** @internal
- * Execute the messages in the incoming queue until the queue is empty..
- *
- * @param self - pointer to a port object
- *
- * @retval 0 if there was a signal to handle, 
- * @retval -1 otherwise.
- */
-static
-int su_source_getmsgs(su_port_t *self)
-{
-  enter;
-  
-  if (self && self->sup_head) {
-    su_root_t *root;
-    su_msg_f f;
-
-    SU_SOURCE_INCREF(self, "su_source_getmsgs");
-    SU_SOURCE_LOCK(self, "su_source_getmsgs");
-
-    while (self->sup_head) {
-      su_msg_t *msg = self->sup_head;
-      self->sup_head = msg->sum_next;
-      if (!self->sup_head) {
-	assert(self->sup_tail == &msg->sum_next);
-	self->sup_tail = &self->sup_head;
-      }
-      root = msg->sum_to->sut_root;
-      f = msg->sum_func;
-      SU_SOURCE_UNLOCK(self, "su_source_getmsgs");
-      if (f) 
-	f(su_root_magic(root), &msg, msg->sum_data);
-      su_msg_delivery_report(&msg);
-      SU_SOURCE_LOCK(self, "su_source_getmsgs");
-    }
-
-    SU_SOURCE_UNLOCK(self, "su_source_getmsgs");
-    SU_SOURCE_DECREF(self, "su_source_getmsgs");
-
-    return 0;
-  }
-  else
-    return -1;
-}
-
 /** @internal
  *
  *  Register a @c su_wait_t object. The wait object, a callback function and
@@ -914,13 +881,6 @@
     return (errno = EINVAL), -1;
 }
 
-/** @internal Enable threadsafe operation. */
-static
-int su_source_threadsafe(su_port_t *port)
-{
-  return su_home_threadsafe(port->sup_home);
-}
-
 
 /** @internal Main loop.
  * 
@@ -1019,18 +979,19 @@
   return 0;
 }
 
+static int su_source_add_prepoll(su_port_t *port,
+				 su_root_t *root, 
+				 su_prepoll_f *prepoll, 
+				 su_prepoll_magic_t *magic)
+{
+  /* We could call prepoll in su_source_prepare()?? */
+  return -1;
+}
 
-/** @internal
- * Checks if the calling thread owns the port object.
- *
- * @param self pointer to a port object
- *
- * @retval true (nonzero) if the calling thread owns the port,
- * @retval false (zero) otherwise.
- */
-int su_source_own_thread(su_port_t const *self)
+static int su_source_remove_prepoll(su_port_t *port,
+				  su_root_t *root)
 {
-  return self == NULL || SU_SOURCE_OWN_THREAD(self);
+  return -1;
 }
 
 #if 0
@@ -1061,52 +1022,44 @@
 
 #endif
 
-/* =========================================================================
- * Pre-poll() callback
+/**@internal
+ *
+ * Allocates and initializes a reactor and message port object.
+ *
+ * @return
+ *   If successful a pointer to the new message port is returned, otherwise
+ *   NULL is returned.  
  */
-
-int su_source_add_prepoll(su_port_t *port,
-			su_root_t *root, 
-			su_prepoll_f *callback, 
-			su_prepoll_magic_t *magic)
+static su_port_t *su_source_port_create(void)
 {
-#if 0
-  if (port->sup_prepoll)
-    return -1;
+  SuSource *ss;
+  su_port_t *self = NULL;
 
-  port->sup_prepoll = callback;
-  port->sup_pp_magic = magic;
-  port->sup_pp_root = root;
+  SU_DEBUG_9(("su_source_port_create() called\n"));
 
-  return 0;
-#else
-  return -1;
-#endif
-}
+  ss = (SuSource *)g_source_new(su_source_funcs, (sizeof *ss));
 
-int su_source_remove_prepoll(su_port_t *port,
-			   su_root_t *root)
-{
-#if 0
-  if (port->sup_pp_root != root)
-    return -1;
+  if (ss) {
+    self = ss->ss_port;
+    if (su_source_port_init(self, ss->ss_source, su_source_port_vtable) < 0)
+      g_source_unref(ss->ss_source), self = NULL;
+  } else {
+    su_perror("su_source_port_create(): g_source_new");
+  }
 
-  port->sup_prepoll = NULL;
-  port->sup_pp_magic = NULL;
-  port->sup_pp_root = NULL;
+  SU_DEBUG_1(("su_source_port_create() returns %p\n", (void *)self));
 
-  return 0;
-#else
-  return -1;
-#endif
+  return self;
 }
 
-/* =========================================================================
- * Timers
- */
+/* No su_source_port_start */
 
-static
-su_timer_t **su_source_timers(su_port_t *self)
+/** Use su_source implementation when su_root_create() is called.
+ *
+ * @NEW_1_12_5
+ */
+void su_glib_prefer_gsource(void)
 {
-  return &self->sup_timers;
+  su_port_prefer(su_source_port_create, NULL);
 }
+

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/torture_su_glib_timer.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua-glib/su-glib/torture_su_glib_timer.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,269 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005,2006 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**
+ * @brief Test program for su-glib timers 
+ *
+ * Based on torture_su_timer.c of libsofia-sip-ua.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Kai Vehmanen <first.surname at nokia.com>
+ *
+ * @internal
+ *
+ * @date Created: Fri Oct 19 08:53:55 2001 pessi
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <assert.h>
+
+struct tester;
+
+#define SU_ROOT_MAGIC_T struct tester
+#define SU_INTERNAL_P   su_root_t *
+#define SU_TIMER_ARG_T  struct timing
+
+#include "sofia-sip/su.h"
+#include "sofia-sip/su_wait.h"
+#include "sofia-sip/su_log.h"
+
+#include <sofia-sip/su_glib.h>
+
+struct timing
+{
+  int       t_run;
+  int       t_times;
+  su_time_t t_prev;
+};
+
+struct tester
+{
+  su_root_t *root;
+  su_timer_t *t, *t1;
+  unsigned times;
+  void *sentinel;
+};
+
+void
+print_stamp(struct tester *x, su_timer_t *t, struct timing *ti)
+{
+  su_time_t now = su_now(), prev = ti->t_prev;
+
+  ti->t_prev = now;
+
+  printf("timer interval %f\n", 1000 * su_time_diff(now, prev));
+
+  if (!ti->t_run)
+    su_timer_set(t, print_stamp, ti);
+
+  if (++ti->t_times >= 10)
+    su_timer_reset(t);
+}
+
+void
+print_X(struct tester *x, su_timer_t *t1, struct timing *ti)
+{
+  su_timer_set(t1, print_X, ti);
+  putchar('X'); fflush(stdout);
+}
+
+su_msg_r intr_msg = SU_MSG_R_INIT;
+
+static RETSIGTYPE intr_handler(int signum)
+{
+  su_msg_send(intr_msg);
+}
+
+static void test_break(struct tester *tester, su_msg_r msg, su_msg_arg_t *arg)
+{
+  su_root_break(tester->root);
+}
+
+void
+end_test(struct tester *tester, su_timer_t *t, struct timing *ti)
+{
+  printf("ending test\n");
+  su_timer_destroy(t);
+  su_timer_reset(tester->t);
+  su_timer_reset(tester->t1);
+  su_root_break(tester->root);
+}
+
+void
+increment(struct tester *tester, su_timer_t *t, struct timing *ti)
+{
+  tester->times++;
+
+  if ((void *)ti == (void*)tester->sentinel)
+    su_root_break(tester->root);
+}
+
+void
+usage(char const *name)
+{
+  fprintf(stderr, "usage: %s [-1r] [-Nnum] [interval]\n", name);
+  exit(1);
+}
+
+/*
+ * test su_timer functionality:
+ *
+ * Create a timer, executing print_stamp() in every 20 ms
+ */
+int main(int argc, char *argv[])
+{
+  su_root_t *root;
+  su_timer_t *t, *t1, *t_end;
+  su_timer_t **timers;
+  su_duration_t interval = 60;
+  char *argv0 = argv[0];
+  char *s;
+  int use_t1 = 0;
+  su_time_t now, started;
+  intptr_t i, N = 500;
+  GSource *source;
+
+  struct timing timing[1] = {{ 0 }};
+  struct tester tester[1] = {{ 0 }};
+
+  while (argv[1] && argv[1][0] == '-') {
+    char *o = argv[1] + 1;
+    while (*o) {
+      if (*o == '1')
+	o++, use_t1 = 1;
+      else if (*o == 'r')
+	o++, timing->t_run = 1;
+      else if (*o == 'N') {
+	if (o[1])
+	  N = strtoul(o + 1, &o, 0);
+	else if (argv[2])
+	  N = strtoul(argv++[2], &o, 0);
+	break;
+      }
+      else
+	break;
+
+    }
+    if (*o)
+      usage(argv0);
+    argv++;
+  }
+
+  if (argv[1]) {
+    interval = strtoul(argv[1], &s, 10);
+
+    if (interval == 0 || s == argv[1])
+      usage(argv0);
+  }
+
+  su_init(); atexit(su_deinit);
+
+  tester->root = root = su_glib_root_create(tester);
+  
+  source = su_root_gsource(tester->root);
+  g_source_attach(source, NULL /*g_main_context_default ()*/);
+
+  su_msg_create(intr_msg,
+		su_root_task(root),
+		su_root_task(root),
+		test_break, 0);
+
+  signal(SIGINT, intr_handler);
+#if HAVE_SIGPIPE
+  signal(SIGPIPE, intr_handler);
+  signal(SIGQUIT, intr_handler);
+  signal(SIGHUP, intr_handler);
+#endif
+
+  t = su_timer_create(su_root_task(root), interval);
+  t1 = su_timer_create(su_root_task(root), 1);
+  t_end = su_timer_create(su_root_task(root), 20 * interval);
+
+  if (t == NULL || t1 == NULL || t_end == NULL)
+    su_perror("su_timer_create"), exit(1);
+
+  tester->t = t, tester->t1 = t1;
+
+  timing->t_prev = su_now();
+
+  if (timing->t_run)
+    su_timer_run(t, print_stamp, timing);
+  else
+    su_timer_set(t, print_stamp, timing);
+
+  if (use_t1)
+    su_timer_set(t1, print_X, NULL);
+
+  su_timer_set(t_end, end_test, NULL);
+
+  su_root_run(root);
+
+  su_msg_destroy(intr_msg);
+
+  su_timer_destroy(t);
+  su_timer_destroy(t1);
+
+  if (timing->t_times != 10) {
+    fprintf(stderr, "%s: t expired %d times (expecting 10)\n",
+	    argv0, timing->t_times);
+    return 1;
+  }
+
+  /* Insert timers in order */
+  timers = calloc(N, sizeof *timers);
+  if (!timers) { perror("calloc"); exit(1); }
+
+  now = started = su_now();
+
+  for (i = 0; i < N; i++) {
+    t = su_timer_create(su_root_task(root), 1000);
+    if (!t) { perror("su_timer_create"); exit(1); }
+    if (++now.tv_usec == 0) ++now.tv_sec;
+    su_timer_set_at(t, increment, (void *)i, now);
+    timers[i] = t;
+  }
+
+  tester->sentinel = (void*)(i - 1);
+
+  su_root_run(root);
+
+  printf("Processing %u timers took %f millisec (%f expected)\n",
+	 (unsigned)i, su_time_diff(su_now(), started) * 1000, (double)i / 1000);
+
+  for (i = 0; i < N; i++) {
+    su_timer_destroy(timers[i]);
+  }
+
+  su_root_destroy(root);
+
+  su_deinit();
+
+  return 0;
+}

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ChangeLog
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ChangeLog	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ChangeLog	Tue Mar 20 23:37:15 2007
@@ -1,3 +1,11 @@
+2007-02-09  Kai Vehmanen  <kai.vehmanen at nokia.com>
+
+	* libsofia-sip-ua interface v4 frozen (4:0:4) for the 1.12.5 release
+
+2006-10-12  Kai Vehmanen  <kai.vehmanen at nokia.com>
+
+	* libsofia-sip-ua interface v3 frozen (3:0:3) for the 1.12.3 release
+	
 2006-09-26  Kai Vehmanen  <kai.vehmanen at nokia.com>
 
 	* libsofia-sip-ua interface v2 frozen (2:0:2) for the 1.12.2 release

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -9,12 +9,25 @@
 
 AUTOMAKE_OPTIONS = foreign
 
+# select whicn optional sofia-sip modules have been enabled
+# in the build
+OPT_LIBADD =
+OPT_SUBDIRS_STUN =
+OPT_SUBDIRS_NTH =
+if HAVE_STUN
+OPT_LIBADD += stun/libstun.la 
+OPT_SUBDIRS_STUN += stun
+endif
+if HAVE_NTH
+OPT_LIBADD += nth/libnth.la http/libhttp.la
+OPT_SUBDIRS_NTH += nth http
+endif
+
 # note: order does matter in the subdir list
-SUBDIRS = su features bnf sresolv sdp url msg sip stun ipt soa \
-	  tport http nta nea iptsec nth nua
+SUBDIRS = su features bnf sresolv sdp url msg sip $(OPT_SUBDIRS_STUN) ipt soa \
+	  tport nta nea iptsec $(OPT_SUBDIRS_NTH) nua
 DIST_SUBDIRS = $(SUBDIRS) docs
 
-EXTRA_DIST = sofia.am
 DOXYGEN = doxygen
 
 lib_LTLIBRARIES = libsofia-sip-ua.la
@@ -27,37 +40,22 @@
 				msg/libmsg.la \
 				nea/libnea.la \
 				nta/libnta.la \
-				nth/libnth.la \
 				nua/libnua.la \
-				http/libhttp.la \
 				sdp/libsdp.la \
 				sip/libsip.la \
 				soa/libsoa.la \
 				sresolv/libsresolv.la \
 				su/libsu.la \
-				stun/libstun.la \
 				tport/libtport.la \
-				url/liburl.la
+				url/liburl.la \
+				$(OPT_LIBADD)
+
 # set the libtool version info version:revision:age for libsofia-sip-ua
 # - soname to 'libsofia-sip-ua.so.(CUR-AGE)'
 libsofia_sip_ua_la_LDFLAGS = \
 	-version-info $(LIBVER_SOFIA_SIP_UA_CUR):$(LIBVER_SOFIA_SIP_UA_REV):$(LIBVER_SOFIA_SIP_UA_AGE)
 
-if ENABLE_COVERAGE
-COVERAGE_RECURSIVE = coverage-recursive
-coverage: $(COVERAGE_RECURSIVE)
-endif
-
-all-recursive: built-sources-recursive
-built-sources: built-sources-recursive 
-clean-built-sources: clean-built-sources-recursive
-
-built-sources-recursive clean-built-sources-recursive $(COVERAGE_RECURSIVE):
-	target=`echo $@ | sed s/-recursive//`; \
-	list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
-	  echo "Making $@ in $$subdir"; \
-	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$target) \
-	done;
+include $(top_srcdir)/rules/recursive.am
 
 doxygen: built-sources
 	@echo Generating empty doxytags

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -43,4 +43,5 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/bnf.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/bnf.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/bnf.c	Tue Mar 20 23:37:15 2007
@@ -377,7 +377,7 @@
 
     if (i == maxparts + 1) {
       /* There is an extra doublecolon */
-      for (j = doublecolon; j <= i; j++)
+      for (j = doublecolon; j + 1 < i; j++)
 	hexparts[j] = hexparts[j + 1];
       i--;
     }
@@ -825,9 +825,9 @@
 {
   size_t len;
   int canonize = 0;
+  char buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
 
 #if SU_HAVE_IN6
-  char buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
 
   len = span_ip6_reference(s);
   if (len) {

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/torture_bnf.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/torture_bnf.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/bnf/torture_bnf.c	Tue Mar 20 23:37:15 2007
@@ -340,9 +340,10 @@
   END();
 }
 
-void usage(void)
+void usage(int exitcode)
 {
-  fprintf(stderr, "usage: %s [-v]\n", name);
+  fprintf(stderr, "usage: %s [-v] [-a]\n", name);
+  exit(exitcode);
 }
 
 int main(int argc, char *argv[])
@@ -353,8 +354,10 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       test_flags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      test_flags |= tst_abort;
     else
-      usage();
+      usage(1);
   }
 
   retval |= bnf_test(); fflush(stdout);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Doxyfile.aliases	Tue Mar 20 23:37:15 2007
@@ -45,4 +45,8 @@
  "VERSION_1_12_4=<a href=\"http://sofia-sip.sf.net/relnotes/relnotes-sofia-sip-1.12.4.txt\">1.12.4</a>" \
  "VERSION_1_12_5=<a href=\"http://sofia-sip.sf.net/relnotes/relnotes-sofia-sip-1.12.5.txt\">1.12.5</a>" \
  "NEW_1_12_5=@since New in <a href=\"http://sofia-sip.sf.net/relnotes/relnotes-sofia-sip-1.12.5.txt\">1.12.5</a>" \
+ "EXP_1_12_5=@since Experimental in <a href=\"http://sofia-sip.sf.net/relnotes/relnotes-sofia-sip-1.12.5.txt\">1.12.5</a>, available if --enable-experimental configuration option is given" \
+ "VERSION_1_12_6=<a href=\"http://sofia-sip.sf.net/relnotes/relnotes-sofia-sip-1.12.6.txt\">1.12.6</a>" \
+ "NEW_1_12_6=@since New in <a href=\"http://sofia-sip.sf.net/relnotes/relnotes-sofia-sip-1.12.6.txt\">1.12.6</a>" \
+ "EXP_1_12_6=@since Experimental in <a href=\"http://sofia-sip.sf.net/relnotes/relnotes-sofia-sip-1.12.6.txt\">1.12.6</a>, available if --enable-experimental configuration option is given" \
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/docs/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -47,4 +47,4 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
\ No newline at end of file
+include $(top_srcdir)/rules/sofia.am

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/features/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/features/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/features/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -31,4 +31,5 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -30,17 +30,17 @@
 			sofia-sip/http_status.h sofia-sip/http_hclasses.h
 
 BUILT_H = 		sofia-sip/http_protos.h sofia-sip/http_tag.h 
-BUILT_C = 		http_tag.c http_tag_ref.c http_parser_table.c
+BUILT_C = 		http_tag.c http_parser_table.c
 
-BUILT_SOURCES = 	$(BUILT_H) $(BUILT_C)
+BUILT_SOURCES = 	$(BUILT_H) $(BUILT_C) http_tag_ref.c
 
 nobase_include_sofia_HEADERS = $(BUILT_H) $(PUBLIC_H) 
 
 libhttp_la_SOURCES = 	$(INTERNAL_H) \
 			http_parser.c http_header.c \
-			http_basic.c http_extra.c \
+			http_basic.c http_extra.c http_inlined.c \
 			http_status.c http_tag_class.c \
-			$(BUILT_C)
+			$(BUILT_SOURCES)
 
 COVERAGE_INPUT = 	$(libhttp_la_SOURCES) $(include_sofia_HEADERS)
 
@@ -48,6 +48,7 @@
 			../bnf/libbnf.la \
 			../msg/libmsg.la \
 			../url/liburl.la \
+			../ipt/libipt.la \
 			../su/libsu.la
 
 test_http_LDFLAGS = 	-static
@@ -64,30 +65,29 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
 
 TAG_DLL_FLAGS = DLLREF=1
 
-MSG_PARSER_AWK = $(srcdir)/../msg/msg_parser.awk
+MSG_PARSER_AWK = ${srcdir}/../msg/msg_parser.awk
 
-AWK_HTTP_AWK = $(AWK) -f $(MSG_PARSER_AWK) module=http
+AWK_HTTP_AWK = ${AWK} -f ${MSG_PARSER_AWK} module=http
 
-sofia-sip/http_tag.h: sofia-sip/http_tag.h.in $(MSG_PARSER_AWK)
-sofia-sip/http_protos.h: sofia-sip/http_protos.h.in $(MSG_PARSER_AWK)
-http_tag.c: http_tag.c.in $(MSG_PARSER_AWK)
-http_parser_table.c: http_parser_table.c.in $(MSG_PARSER_AWK)
+SS_HTTP_H = ${srcdir}/sofia-sip/http.h
 
-sofia-sip/http_protos.h: sofia-sip/http.h
+${BUILT_H} ${BUILT_C}: ${srcdir}/sofia-sip/http.h ${MSG_PARSER_AWK}
+
+sofia-sip/http_protos.h: ${srcdir}/sofia-sip/http_protos.h.in 
 	@-mkdir sofia-sip 2>/dev/null || true
-	$(AWK_HTTP_AWK) PR=$@ TEMPLATE=$(srcdir)/sofia-sip/http_protos.h.in $<
+	${AWK_HTTP_AWK} PR=$@ TEMPLATE=${srcdir}/$@.in ${SS_HTTP_H}
 
-sofia-sip/http_tag.h: sofia-sip/http.h
+sofia-sip/http_tag.h: ${srcdir}/sofia-sip/http_tag.h.in
 	@-mkdir sofia-sip 2>/dev/null || true
-	$(AWK_HTTP_AWK) PR=$@ TEMPLATE=$(srcdir)/sofia-sip/http_tag.h.in $<
+	${AWK_HTTP_AWK} PR=$@ TEMPLATE=${srcdir}/$@.in ${SS_HTTP_H}
 
-http_tag.c: sofia-sip/http.h
-	$(AWK_HTTP_AWK) PR=$@ TEMPLATE=$(srcdir)/http_tag.c.in $<
+http_tag.c: ${srcdir}/http_tag.c.in
+	${AWK_HTTP_AWK} PR=$@ TEMPLATE=${srcdir}/$@.in ${SS_HTTP_H}
 
-http_parser_table.c: sofia-sip/http.h
-	$(AWK_HTTP_AWK) PT=$@ TEMPLATE=$(srcdir)/http_parser_table.c.in \
-		MC_HASH_SIZE=127 $<
+http_parser_table.c: ${srcdir}/http_parser_table.c.in
+	${AWK_HTTP_AWK} PT=$@ TEMPLATE=${srcdir}/$@.in \
+		MC_HASH_SIZE=127 ${SS_HTTP_H}

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_basic.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_basic.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_basic.c	Tue Mar 20 23:37:15 2007
@@ -55,6 +55,7 @@
 #include <string.h>
 #include <assert.h>
 #include <stdio.h>
+#include <limits.h>
 
 /* ====================================================================== */
 
@@ -1196,7 +1197,7 @@
   http_range_t const *o = (http_range_t const *)src;
   char *end = b + xtra;
 
-  b = msg_params_dup((char const * const **)&rng->rng_specs,
+  b = msg_params_dup((msg_param_t const **)&rng->rng_specs,
 		     o->rng_specs, b, xtra);
   MSG_STRING_DUP(b, rng->rng_unit, o->rng_unit);
 

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_inlined.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_inlined.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE http_inlined.c
+ *
+ * Expand inlined http functions non-inline.
+ *
+ */
+
+#include "config.h"
+
+#include <sofia-sip/su_config.h>
+
+#if SU_HAVE_INLINE
+
+extern int xyzzy;
+
+#else
+
+#include "sofia-sip/msg_header.h"
+#include "sofia-sip/su_tag.h"
+
+#undef SU_HAVE_INLINE
+#undef su_inline
+
+#define SU_HAVE_INLINE 1
+#define su_inline
+
+#include "sofia-sip/http_header.h"
+
+#endif

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_parser.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_parser.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/http_parser.c	Tue Mar 20 23:37:15 2007
@@ -61,9 +61,9 @@
 /** HTTP version 0.9 is an empty string. */
 char const http_version_0_9[] = "";
 
-msg_mclass_t *http_default_mclass(void)
+msg_mclass_t const *http_default_mclass(void)
 {
-  extern msg_mclass_t http_mclass[];
+  extern msg_mclass_t const http_mclass[];
 
   return http_mclass;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_header.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_header.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/sofia-sip/http_header.h	Tue Mar 20 23:37:15 2007
@@ -95,7 +95,7 @@
  */
 
 /** HTTP parser description. */
-SOFIAPUBFUN msg_mclass_t *http_default_mclass(void);
+SOFIAPUBFUN msg_mclass_t const *http_default_mclass(void);
 
 /** Complete a HTTP request. */
 SOFIAPUBFUN int http_request_complete(msg_t *msg);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/test_http.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/test_http.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/http/test_http.c	Tue Mar 20 23:37:15 2007
@@ -76,14 +76,7 @@
 static int test_query_parser(void);
 
 static msg_t *read_message(char const string[]);
-msg_mclass_t *test_mclass = NULL;
-
-void usage(void)
-{
-  fprintf(stderr,
-	  "usage: %s [-v]\n",
-	  name);
-}
+msg_mclass_t const *test_mclass = NULL;
 
 char *lastpart(char *path)
 {
@@ -95,6 +88,14 @@
 
 int tstflags;
 
+void usage(int exitcode)
+{
+  fprintf(stderr,
+	  "usage: %s [-v] [-a]\n",
+	  name);
+  exit(exitcode);
+}
+
 int main(int argc, char *argv[])
 {
   int retval = 0;
@@ -105,8 +106,10 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else
-      usage();
+      usage(1);
   }
 
   if (!test_mclass)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -51,4 +51,5 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/base64.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/base64.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/base64.h	Tue Mar 20 23:37:15 2007
@@ -57,6 +57,9 @@
 /** Calculate size of n bytes encoded in base64 */
 #define BASE64_SIZE(n) ((((n) + 2) / 3) * 4)
 
+/** Calculate size of n bytes encoded in base64 sans trailing =. @NEW_1_12_5 */
+#define BASE64_MINSIZE(n) ((n * 4 + 2) / 3)
+
 SOFIA_END_DECLS
 
 #endif /* !BASE_64 */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/string0.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/string0.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/sofia-sip/string0.h	Tue Mar 20 23:37:15 2007
@@ -43,56 +43,39 @@
 
 SOFIA_BEGIN_DECLS
 
-#if SU_HAVE_INLINE
-su_inline
-#else
-SOFIAPUBFUN
-#endif
-int 
-  str0cmp(char const *a, char const *b),
-  str0ncmp(char const *a, char const *b, size_t n),
-  str0casecmp(char const *a, char const *b),
-  str0ncasecmp(char const *a, char const *b, size_t n);
-
-#if SU_HAVE_INLINE
-su_inline
-#else
-SOFIAPUBFUN
-#endif
-size_t
-strnspn(char const *s, size_t size, char const *term),
-  strncspn(char const *s, size_t ssize, char const *reject);
-
-#if SU_HAVE_INLINE
-int str0cmp(char const *a, char const *b)
+su_inline int str0cmp(char const *a, char const *b)
 {
   if (a == NULL) a = "";
   if (b == NULL) b = "";
   return strcmp(a, b);
 }
 
-int str0ncmp(char const *a, char const *b, size_t n)
+su_inline int str0ncmp(char const *a, char const *b, size_t n)
 {
   if (a == NULL) a = "";
   if (b == NULL) b = "";
   return strncmp(a, b, n);
 }
 
-int str0casecmp(char const *a, char const *b)
+su_inline int str0casecmp(char const *a, char const *b)
 {
   if (a == NULL) a = "";
   if (b == NULL) b = "";
   return strcasecmp(a, b);
 }
 
-int str0ncasecmp(char const *a, char const *b, size_t n)
+su_inline int str0ncasecmp(char const *a, char const *b, size_t n)
 {
   if (a == NULL) a = "";
   if (b == NULL) b = "";
   return strncasecmp(a, b, n);
 }
 
-size_t strnspn(char const *s, size_t ssize, char const *term)
+#if !SU_HAVE_INLINE
+SOFIAPUBFUN size_t strnspn(char const *s, size_t size, char const *term);
+SOFIAPUBFUN size_t strncspn(char const *s, size_t ssize, char const *reject);
+#else
+su_inline size_t strnspn(char const *s, size_t ssize, char const *term)
 {
   size_t n;
   size_t tsize = strlen(term);
@@ -123,7 +106,7 @@
   return n;
 }
 
-size_t strncspn(char const *s, size_t ssize, char const *reject)
+su_inline size_t strncspn(char const *s, size_t ssize, char const *reject)
 {
   size_t n;
   size_t rsize = strlen(reject);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/torture_base64.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/torture_base64.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/ipt/torture_base64.c	Tue Mar 20 23:37:15 2007
@@ -161,11 +161,12 @@
 }
 
 
-void usage(void)
+void usage(int exitcode)
 {
   fprintf(stderr,
-	  "usage: %s [-v]\n",
+	  "usage: %s [-v] [-a]\n",
 	  name);
+  exit(exitcode);
 }
 
 int main(int argc, char *argv[])
@@ -176,8 +177,10 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else
-      usage();
+      usage(1);
   }
 
   retval |= test_encoding(); fflush(stdout);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -35,29 +35,18 @@
 			sofia-sip/auth_common.h \
 			sofia-sip/auth_client.h sofia-sip/auth_digest.h \
 			sofia-sip/auth_module.h sofia-sip/auth_plugin.h \
-			sofia-sip/auth_client_plugin.h \
-			$(NTLM_HEADER)
+			sofia-sip/auth_client_plugin.h
 
 libiptsec_la_SOURCES = 	iptsec_debug.h \
 			auth_client.c auth_common.c auth_digest.c \
 			auth_module.c auth_tag.c auth_tag_ref.c \
 			auth_plugin.c auth_plugin_delayed.c \
-			auth_module_http.c auth_module_sip.c \
-			$(NTLM_SOURCE) \
+			auth_module_sip.c \
 			iptsec_debug.c
 
-NTLM_HEADER = 		sofia-sip/auth_ntlm.h
-if HAVE_NTLM
-NTLM_SOURCE = 		auth_ntlm.c auth_client_ntlm.c auth_plugin_ntlm.c
-endif
-
-EXTRA_libiptsec_la_SOURCES = \
-			auth_ntlm.c auth_client_ntlm.c auth_plugin_ntlm.c
-
 COVERAGE_INPUT = 	$(libiptsec_la_SOURCES) $(include_sofia_HEADERS)
 
 LDADD = 		libiptsec.la \
-			../http/libhttp.la \
 			../nta/libnta.la \
 			../sip/libsip.la \
 			../msg/libmsg.la \
@@ -68,13 +57,31 @@
 
 test_auth_digest_LDFLAGS = -static
 
+if HAVE_NTLM
+nobase_include_sofia_HEADERS += $(NTLM_HEADER)
+libiptsec_la_SOURCES += $(NTLM_SOURCE)
+endif
+
+if HAVE_NTH
+libiptsec_la_SOURCES += $(HTTP_SOURCE)
+LDADD +=		../http/libhttp.la
+endif
+
+HTTP_SOURCE =		auth_module_http.c
+
+NTLM_HEADER = 		sofia-sip/auth_ntlm.h
+NTLM_SOURCE = 		auth_ntlm.c auth_client_ntlm.c auth_plugin_ntlm.c
+
+EXTRA_libiptsec_la_SOURCES = \
+			$(NTLM_HEADER) $(NTLM_SOURCE) $(HTTP_SOURCE)
+
 # ----------------------------------------------------------------------
 # Install and distribution rules
 
-EXTRA_DIST = 		Doxyfile iptsec.docs testpasswd \
-			auth_module_sip.c auth_module_http.c $(BUILT_SOURCES)
+EXTRA_DIST = 		Doxyfile iptsec.docs testpasswd $(BUILT_SOURCES)
 
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_client.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_client.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_client.c	Tue Mar 20 23:37:15 2007
@@ -210,6 +210,8 @@
  * @retval number of challenges to updated
  * @retval 0 when there was no challenge to update
  * @retval -1 upon an error
+ *
+ * @NEW_1_12_5
  */
 int auc_info(auth_client_t **auc_list,
 	     msg_auth_info_t const *ai,
@@ -495,6 +497,8 @@
  * 
  * @retval 1 when authorization can proceed
  * @retval 0 when there is not enough credentials
+ *
+ * @NEW_1_12_5
  */
 int auc_has_authorization(auth_client_t **auc_list)
 {
@@ -766,22 +770,13 @@
   if (ac->ac_qop && (cda->cda_cnonce == NULL || ac->ac_stale)) {
     su_guid_t guid[1];
     char *cnonce;
-    char *e;
-
+    size_t b64len = BASE64_MINSIZE(sizeof(guid)) + 1;
     if (cda->cda_cnonce != NULL)
       /* Free the old one if we are updating after stale=true */
       su_free(home, (void *)cda->cda_cnonce);
     su_guid_generate(guid);
-    cda->cda_cnonce = cnonce = su_alloc(home, BASE64_SIZE(sizeof(guid)) + 1);
-    base64_e(cnonce, BASE64_SIZE(sizeof(guid)) + 1, guid, sizeof(guid));
-    /* somewhere else in the code the '=' chars are stripped in the header 
-       we need to strip it now before the digest is created or we're in trouble
-       cos they won't match.....
-    */
-    e = cnonce + strlen(cnonce) - 1;
-    while(*e == '=') {
-       *e-- = '\0';
-    }
+    cda->cda_cnonce = cnonce = su_alloc(home, b64len);
+    base64_e(cnonce, b64len, guid, sizeof(guid));
     cda->cda_ncount = 0;
   }
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_module.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_module.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_module.c	Tue Mar 20 23:37:15 2007
@@ -76,6 +76,7 @@
 
 char const auth_internal_server_error[] = "Internal server error";
 
+static void auth_call_scheme_destructor(void *);
 static void auth_md5_hmac_key(auth_mod_t *am);
 
 HTABLE_PROTOS_WITH(auth_htable, aht, auth_passwd_t, usize_t, unsigned);
@@ -94,7 +95,7 @@
 
   if ((am = su_home_new(scheme->asch_size))) {
     am->am_scheme = scheme;
-    am->am_refcount = 1;
+    su_home_destructor(am->am_home, auth_call_scheme_destructor);
   }
 
   return am;
@@ -246,10 +247,14 @@
 /** Destroy (a reference to) an authentication module. */
 void auth_mod_destroy(auth_mod_t *am)
 {
-  if (am && am->am_refcount != 0 && --am->am_refcount == 0) {
-    am->am_scheme->asch_destroy(am);
-    su_home_zap(am->am_home);
-  }
+  su_home_unref(am->am_home);
+}
+
+/** Call scheme-specific destructor function. */
+static void auth_call_scheme_destructor(void *arg)
+{
+  auth_mod_t *am = arg;
+  am->am_scheme->asch_destroy(am);
 }
 
 /** Do-nothing destroy function.
@@ -264,18 +269,13 @@
 /** Create a new reference to authentication module. */
 auth_mod_t *auth_mod_ref(auth_mod_t *am)
 {
-  if (!am || am->am_refcount == 0)
-    return NULL;
-
-  am->am_refcount++;
-
-  return am;
+  return (auth_mod_t *)su_home_ref(am->am_home);
 }
 
 /** Destroy a reference to an authentication module. */
 void auth_mod_unref(auth_mod_t *am)
 {
-  auth_mod_destroy(am);
+  su_home_unref(am->am_home);
 }
 
 /** Get authenticatin module name. @NEW_1_12_4. */
@@ -608,7 +608,7 @@
   uint8_t    digest[6];
 };
 
-#define AUTH_DIGEST_NONCE_LEN (BASE64_SIZE(sizeof (struct nonce)) + 1)
+#define AUTH_DIGEST_NONCE_LEN (BASE64_MINSIZE(sizeof (struct nonce)) + 1)
 
 /** Authenticate a request with @b Digest authentication scheme.
  *
@@ -950,7 +950,8 @@
 #include <sys/file.h>
 #endif
 
-#define auth_apw_local auth_readdb_internal
+/* This is just a magic value */
+#define auth_apw_local ((void *)(intptr_t)auth_readdb_internal)
 
 /** Read authentication database */
 static

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_plugin_delayed.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_plugin_delayed.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/auth_plugin_delayed.c	Tue Mar 20 23:37:15 2007
@@ -126,7 +126,7 @@
 
 struct auth_splugin_t 
 {
-  void const      *asp_tag;
+  void const      *asp_cookie;
   auth_splugin_t  *asp_next;
   auth_splugin_t **asp_prev;
   auth_mod_t      *asp_am;
@@ -136,6 +136,8 @@
   int              asp_canceled;
 };
 
+/* This is unique identifier */
+#define delayed_asp_cookie ((void const *)(intptr_t)delayed_auth_cancel)
 
 static void delayed_auth_method_recv(su_root_magic_t *rm,
 				     su_msg_r msg,
@@ -162,7 +164,7 @@
 
   asp = su_msg_data(mamc); assert(asp);
 
-  asp->asp_tag = delayed_auth_cancel;
+  asp->asp_cookie = delayed_asp_cookie;
   asp->asp_am = am;
   asp->asp_as = as;
   asp->asp_header = auth;
@@ -216,7 +218,7 @@
 
   (void)ap;			/* xyzzy */
   
-  if (as->as_plugin && as->as_plugin->asp_tag == delayed_auth_cancel)
+  if (as->as_plugin && as->as_plugin->asp_cookie == delayed_asp_cookie)
     as->as_plugin->asp_canceled = 1;
 
   as->as_status = 500, as->as_phrase = "Authentication canceled";

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_plugin.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_plugin.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/sofia-sip/auth_plugin.h	Tue Mar 20 23:37:15 2007
@@ -105,7 +105,7 @@
 typedef struct
 {
   unsigned        apw_index;	/**< Key to hash table */
-  void const     *apw_type;	/**< Magic pointer */
+  void const     *apw_type;	/**< Magic identifier */
 
   char const   	 *apw_user;	/**< Username */
   char const     *apw_realm;	/**< Realm */
@@ -124,7 +124,7 @@
 struct auth_mod_t
 {
   su_home_t      am_home[1];
-  unsigned       am_refcount;	/**< Number of references to this module */
+  unsigned       _am_refcount;	/**< Not used */
 
   /* User database / cache */
   char const    *am_db;		/**< User database file name */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/iptsec/test_auth_digest.c	Tue Mar 20 23:37:15 2007
@@ -769,8 +769,6 @@
     {
       char const *nonce1, *nextnonce, *nonce2;
 
-      reinit_as(as); auth_mod_destroy(am); aucs = NULL;
-
       TEST_1(am = auth_mod_create(NULL, 
 				  AUTHTAG_METHOD("Digest"),
 				  AUTHTAG_REALM("ims3.so.noklab.net"),
@@ -1164,9 +1162,10 @@
 extern su_log_t iptsec_log[];
 
 static
-void usage(void)
+void usage(int exitcode)
 {
-  fprintf(stderr, "usage: %s [-v] [-l n]\n", name);
+  fprintf(stderr, "usage: %s [-v] [-a] [-l n]\n", name);
+  exit(exitcode);
 }
 
 int main(int argc, char *argv[])
@@ -1179,8 +1178,10 @@
   su_init();
 
   for (i = 1; argv[i]; i++) {
-    if (argv[i] && strcmp(argv[i], "-v") == 0)
+    if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else if (strncmp(argv[i], "-l", 2) == 0) {
       int level = 3;
       char *rest = NULL;
@@ -1193,11 +1194,11 @@
 	level = 3, rest = "";
 
       if (rest == NULL || *rest)
-	usage();
+	usage(1);
       
       su_log_set_level(iptsec_log, level);
     } else {
-      usage();
+      usage(1);
     }
   }
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -35,10 +35,12 @@
 nobase_include_sofia_HEADERS = \
 			$(GENERATED_H) $(PUBLIC_H) 
 
-BUILT_SOURCES = $(GENERATED_H) msg_mime_table.c test_table.c test_protos.h
+GENERATED_HC = $(GENERATED_H) msg_mime_table.c test_table.c test_protos.h
 
-libmsg_la_SOURCES = $(INTERNAL_H) \
-	msg.c msg_tag.c \
+BUILT_SOURCES = $(GENERATED_HC)
+
+libmsg_la_SOURCES = msg_internal.h \
+	msg.c msg_tag.c msg_inlined.c \
 	msg_mime.c msg_mime_table.c \
 	msg_header_copy.c msg_header_make.c \
 	msg_parser.c msg_mclass.c msg_parser_util.c \
@@ -46,11 +48,13 @@
 
 COVERAGE_INPUT = 	$(libmsg_la_SOURCES) $(include_sofia_HEADERS)
 
-libtest_msg_a_SOURCES = test_class.c test_table.c test_protos.h
+libtest_msg_a_SOURCES = test_class.c test_class.h \
+			test_table.c test_inlined.c test_protos.h
 
 LDADD = 		libtest_msg.a libmsg.la \
 			../bnf/libbnf.la \
 			../url/liburl.la \
+			../ipt/libipt.la \
 			../su/libsu.la 
 
 test_msg_LDFLAGS = 	-static
@@ -77,41 +81,43 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
 
 MSG_PARSER_AWK = $(srcdir)/msg_parser.awk
 
 AWK_MSG_AWK = $(AWK) -f $(MSG_PARSER_AWK)
 
-test_protos.h: test_protos.h.in $(MSG_PARSER_AWK)
-test_table.c: test_table.c.in $(MSG_PARSER_AWK)
+${GENERATED_HC}: ${MSG_PARSER_AWK}
 
-sofia-sip/msg_mime_protos.h: sofia-sip/msg_mime_protos.h.in
-sofia-sip/msg_protos.h: sofia-sip/msg_protos.h.in
-msg_mime_table.c: msg_mime_table.c.in $(MSG_PARSER_AWK)
+TEST_CLASS_H = ${srcdir}/test_class.h
 
-sofia-sip/msg_mime_protos.h sofia-sip/msg_protos.h: $(MSG_PARSER_AWK)
+test_protos.h test_table.c: ${TEST_CLASS_H}
 
-test_protos.h: test_class.h
+test_protos.h: ${srcdir}/test_protos.h.in
 	$(AWK_MSG_AWK) module=msg_test NO_MIDDLE=1 NO_LAST=1 \
-		PR=$@ TEMPLATE=$(srcdir)/test_protos.h.in $<
+		PR=$@ TEMPLATE=$(srcdir)/$@.in ${TEST_CLASS_H}
 
-test_table.c: test_class.h 
+test_table.c: ${srcdir}/test_table.c.in
 	$(AWK_MSG_AWK) module=msg_test prefix=msg \
 		MC_HASH_SIZE=127 multipart=msg_multipart \
-		PT=$@ TEMPLATE=$(srcdir)/test_table.c.in $<
+		PT=$@ TEMPLATE=$(srcdir)/$@.in ${TEST_CLASS_H}
+
+SS_MIME_H = ${srcdir}/sofia-sip/msg_mime.h
+
+sofia-sip/msg_protos.h sofia-sip/msg_mime_protos.h: ${SS_MIME_H}
+msg_mime_table.c: ${SS_MIME_H}
 
-sofia-sip/msg_protos.h: sofia-sip/msg_mime.h
+sofia-sip/msg_protos.h: ${srcdir}/sofia-sip/msg_protos.h.in
 	@-mkdir sofia-sip 2>/dev/null || true
 	$(AWK_MSG_AWK) module=msg NO_FIRST=1 NO_MIDDLE=1 \
-		PR=$@ TEMPLATE=$(srcdir)/sofia-sip/msg_protos.h.in $<
+		PR=$@ TEMPLATE=$(srcdir)/$@.in ${SS_MIME_H}
 
-sofia-sip/msg_mime_protos.h: sofia-sip/msg_mime.h
+sofia-sip/msg_mime_protos.h: ${srcdir}/sofia-sip/msg_mime_protos.h.in
 	@-mkdir sofia-sip 2>/dev/null || true
 	$(AWK_MSG_AWK) module=msg NO_FIRST=1 NO_LAST=1 \
-		PR=$@ TEMPLATE=$(srcdir)/sofia-sip/msg_mime_protos.h.in $<
+		PR=$@ TEMPLATE=$(srcdir)/$@.in ${SS_MIME_H}
 
-msg_mime_table.c: sofia-sip/msg_mime.h
+msg_mime_table.c: ${srcdir}/msg_mime_table.c.in
 	$(AWK_MSG_AWK) module=msg_multipart \
 		tprefix=msg prefix=mp MC_HASH_SIZE=127 \
-		PT=$@ TEMPLATE=$(srcdir)/msg_mime_table.c.in $<
+		PT=$@ TEMPLATE=$(srcdir)/$@.in ${SS_MIME_H}

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/msg_inlined.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/msg_inlined.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE msg_inlined.c
+ *
+ * Expand inlined msg functions non-inline.
+ *
+ */
+
+#include "config.h"
+
+#include <sofia-sip/su_config.h>
+
+#if SU_HAVE_INLINE
+extern int xyzzy;
+#else
+#undef SU_HAVE_INLINE
+#undef su_inline
+
+#define SU_HAVE_INLINE 1
+#define su_inline
+
+#include "sofia-sip/msg_header.h"
+#include "sofia-sip/msg_mime_protos.h"
+
+#endif

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.awk
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.awk	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/msg_parser.awk	Tue Mar 20 23:37:15 2007
@@ -54,11 +54,13 @@
   split("", NAMES);
   split("", Comments);
   split("", COMMENTS);
+  split("", experimental);
 
   # indexed by the C name of the header
   split("", Since);		# Non-NUL if extra
   split("", Extra);		# Offset in extra headers
 
+  without_experimental = 0;
   template="";
   template1="";
   template2="";
@@ -78,13 +80,18 @@
 {
   hash = 0;
 
-  len = split(name, chars, "");
+  len = length(name); 
 
   for (i = 1; i <= len; i++) {
-    c = tolower(chars[i]);
+    c = tolower(substr(name, i, 1));
     hash = (38501 * (hash + index(ascii, c))) % 65536;
   }
 
+  if (hash == 0) {
+    print "*** msg_parser.awk: calculating hash failed\n";
+    exit(5);
+  }
+
   if (0) {
     # Test that hash algorithm above agrees with the C version
     pipe = ("../msg/msg_name_hash " name);
@@ -155,11 +162,24 @@
     Extra[name] = extra++;
   }
 
+  expr = (without_experimental > 0 && do_hash);
+  if (expr) {
+    printf "%s is experimental\n", Comment;
+  }    
+  
+  experimental[N] = expr;
+
   if (PR) {
+    if (expr) {
+      print "#if SU_HAVE_EXPERIMENTAL" > PR;
+    }
     replace(template, hash, name, NAME, comment, Comment, COMMENT, since);
     replace(template1, hash, name, NAME, comment, Comment, COMMENT, since);
     replace(template2, hash, name, NAME, comment, Comment, COMMENT, since);
     replace(template3, hash, name, NAME, comment, Comment, COMMENT, since);
+    if (expr) {
+      print "#endif /* SU_HAVE_EXPERIMENTAL */" > PR;
+    }
   }
 }
 
@@ -210,8 +230,19 @@
   for (i = 1; i <= n; i++) {
     l = lines[i];
     if (match(tolower(l), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) {
+      expr = 0;
+
       for (j = 1; j <= N; j++) {
 	l = lines[i];
+	if (expr != experimental[j]) {
+	  expr = experimental[j];
+	  if (expr) {
+	    print "#if SU_HAVE_EXPERIMENTAL" > PR;
+	  }
+	  else {
+	    print "#endif /* SU_HAVE_EXPERIMENTAL */" > PR;
+	  }
+	}
 	gsub(/#hash#/, hashes[j], l);
 	gsub(/#xxxxxxx_xxxxxxx#/, comments[j], l);
 	gsub(/#Xxxxxxx_Xxxxxxx#/, Comments[j], l);
@@ -220,6 +251,10 @@
 	gsub(/#XXXXXX#/, NAMES[j], l);
 	print l > PR;
       }
+
+      if (expr) {
+	print "#endif /* SU_HAVE_EXPERIMENTAL */" > PR;
+      }
     } else {
       print l > PR;
     }
@@ -333,10 +368,11 @@
 }
 
 /^#### EXTRA HEADER LIST STARTS HERE ####$/ { HLIST=1; templates(); }
+HLIST && /^#### EXPERIMENTAL HEADER LIST STARTS HERE ####$/ { 
+  without_experimental=total; }
 HLIST && /^[a-z]/ { protos($1, $0, 0, $2); headers[total++] = $1; }
 /^#### EXTRA HEADER LIST ENDS HERE ####$/ { HLIST=0;  }
 
-
 /^ *\/\* === Headers start here \*\// { in_header_list=1;  templates(); }
 /^ *\/\* === Headers end here \*\// { in_header_list=0; }
 
@@ -366,10 +402,13 @@
 END {
   if (failed) { exit };
 
+  if (without_experimental == 0)
+    without_experimental = total;
+
   if (!NO_LAST) {
     protos("unknown", "/**< Unknown headers */", -3);
     protos("error", "/**< Erroneous headers */", -4);
-    protos("separator", "/**< Separator line between headers and payload */", -5);
+    protos("separator", "/**< Separator line between headers and body */", -5);
     protos("payload", "/**< Message payload */", -6);
     if (multipart)
       protos("multipart", "/**< Multipart payload */", -7);
@@ -397,6 +436,10 @@
     gsub(/#DATE#/, "@date Generated: " date, header);
     print header > PT;
 
+    print "" > PT;
+    print "#define msg_offsetof(s, f) ((unsigned short)offsetof(s ,f))" > PT;
+    print "" > PT;
+
     if (MC_SHORT_SIZE) {
       printf("static msg_href_t const " \
 	     "%s_short_forms[MC_SHORT_SIZE] = \n{\n", 
@@ -408,7 +451,7 @@
 	  n = shorts[i];
         flags = header_flags[n]; if (flags) flags = ",\n      " flags;
 	  
-	  printf("  { /* %s */ %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n", 
+	  printf("  { /* %s */ %s_%s_class, msg_offsetof(%s_t, %s_%s)%s }%s\n", 
 		 substr(lower_case, i, 1), 
 		 tprefix, n, module, prefix, n, flags, c)	\
 	    > PT;
@@ -426,7 +469,16 @@
     if (extra > 0) {
       printf("struct %s {\n", extra_struct) > PT;
       printf("  %s base;\n", module_struct) > PT;
-      printf("  msg_header_t *extra[%u];\n", extra) > PT;
+      if (total - without_experimental < extra) {
+	printf("  msg_header_t *extra[%u];\n", 
+	       extra - (total - without_experimental)) > PT;
+      }
+      if (total - without_experimental > 0) {
+	print "#if SU_HAVE_EXPERIMENTAL" > PT;
+	printf("  msg_header_t *experimental[%u];\n", 
+	       total - without_experimental) > PT;
+	print "#endif" > PT;
+      }
       printf("};\n\n") > PT;
       module_struct = "struct " extra_struct;
     }
@@ -450,11 +502,11 @@
     len = split("request status separator payload unknown error", unnamed, " ");
 
     for (i = 1; i <= len; i++) {
-      printf("  {{ %s_%s_class, offsetof(%s_t, %s_%s) }},\n", 
+      printf("  {{ %s_%s_class, msg_offsetof(%s_t, %s_%s) }},\n", 
 	     tprefix, unnamed[i], module, prefix, unnamed[i]) > PT;
     }
     if (multipart) {
-      printf("  {{ %s_class, offsetof(%s_t, %s_multipart) }},\n",
+      printf("  {{ %s_class, msg_offsetof(%s_t, %s_multipart) }},\n",
 	     multipart, module, prefix) > PT;
     } else {
       printf("  {{ NULL, 0 }},\n") > PT;
@@ -465,7 +517,13 @@
     else {
       printf("  NULL, \n") > PT;
     }
-    printf("  %d, %d, \n", MC_HASH_SIZE, total) > PT;
+    printf("  %d, \n", MC_HASH_SIZE) > PT;
+    printf ("#if SU_HAVE_EXPERIMENTAL\n" \
+	    "  %d,\n" \
+	    "#else\n" \
+	    "  %d,\n" \
+	    "#endif\n", \
+	    total, without_experimental) > PT;
     printf("  {\n") > PT;
 
     for (i = 0; i < total; i++) {
@@ -484,6 +542,7 @@
       }
 
       header_hash[j] = n;
+      experimental2[j] = (i >= without_experimental);
     }
 
     for (i = 0; i < MC_HASH_SIZE; i++) {
@@ -492,14 +551,23 @@
 	n = header_hash[i];
         flags = header_flags[n]; if (flags) flags = ",\n      " flags;
 
+	if (experimental2[i]) {
+	  print "#if SU_HAVE_EXPERIMENTAL" > PT;
+	}
+
 	if (Since[n]) {
-	  printf("    { %s_%s_class, offsetof(struct %s, extra[%u])%s }%s\n", 
+	  printf("    { %s_%s_class,\n" \
+		 "      msg_offsetof(struct %s, extra[%u])%s }%s\n", 
 		 tprefix, n, extra_struct, Extra[n], flags, c) > PT;
 	}
 	else {
-	  printf("    { %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n", 
+	  printf("    { %s_%s_class, msg_offsetof(%s_t, %s_%s)%s }%s\n", 
 		 tprefix, n, module, prefix, n, flags, c) > PT;
 	}
+
+	if (experimental2[i]) {
+	  printf("#else\n    { NULL, 0 }%s\n#endif\n", c) > PT;
+	}
       }
       else {
 	printf("    { NULL, 0 }%s\n", c) > PT;

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/sofia-sip/msg_parser.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/sofia-sip/msg_parser.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/sofia-sip/msg_parser.h	Tue Mar 20 23:37:15 2007
@@ -56,7 +56,8 @@
  * 1) Header class definitions.
  */
 
-#if HAVE_STRUCT_KEYWORDS
+/* Do not use keywords until you fix msg_kind_foo_critical thing! */ \
+#if HAVE_STRUCT_KEYWORDS && 0
 /** Define a header class */
 #define MSG_HEADER_CLASS(pr, c, l, s, params, kind, dup, upd)	\
   {{								\
@@ -74,6 +75,7 @@
     hc_kind:	msg_kind_##kind,				\
   }}
 #else
+/** Define a header class */
 #define MSG_HEADER_CLASS(pr, c, l, s, params, kind, dup, upd)	\
   {{ \
      pr##c##_hash, \
@@ -198,9 +200,11 @@
 
 /** Duplicate string. @HI */
 #define MSG_STRING_DUP(p, d, s) \
-  (void)((s)?((p)=(char*)memccpy((void *)((d)=(char*)p),(s),0,SIZE_MAX))\
+  (void)((s)?((p)=(char*)memccpy((void *)((d)=(char*)p),(s),0,INT_MAX))\
 	    :((d)=NULL))
 
+/* Solaris has broken memccpy - it considers last argument as signed */
+
 /** Calculate string size. @HI */
 #define MSG_STRING_SIZE(s) ((s) ? (strlen(s) + 1) : 0)
 

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/test_inlined.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/test_inlined.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE test_inlined.c
+ *
+ * Expand inlined test functions non-inline.
+ *
+ */
+
+#include "config.h"
+
+#include <sofia-sip/su_config.h>
+
+#if SU_HAVE_INLINE
+extern int xyzzy;
+#else
+#undef SU_HAVE_INLINE
+#undef su_inline
+
+#define SU_HAVE_INLINE 1
+#define su_inline
+
+#include "test_protos.h"
+
+#endif

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/msg/test_msg.c	Tue Mar 20 23:37:15 2007
@@ -59,11 +59,6 @@
 
 char const name[] = "test_msg";
 
-void usage(void)
-{
-  fprintf(stderr, "usage: %s [-v]\n", name);
-}
-
 static int msg_time_test(void)
 {
   char buf[32];
@@ -138,7 +133,6 @@
   }
 
   END();
-  return 0;
 }
 
 static int addr_test(void)
@@ -1688,6 +1682,12 @@
   END();
 }
 
+void usage(int exitcode)
+{
+  fprintf(stderr, "usage: %s [-v] [-a]\n", name);
+  exit(exitcode);
+}
+
 int main(int argc, char *argv[])
 {
   int retval = 0;
@@ -1696,8 +1696,10 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       test_flags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      test_flags |= tst_abort;
     else
-      usage();
+      usage(1);
   }
 
   retval |= msg_time_test(); fflush(stdout);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -59,4 +59,5 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/nea_server.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/nea_server.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nea/nea_server.c	Tue Mar 20 23:37:15 2007
@@ -496,14 +496,14 @@
 	nes->nes_eventity_uri &&
 	(nes->nes_leg || leg == NULL) &&
 	nes->nes_timer) {
-      SU_DEBUG_5(("nea_server_create(%p): success\n", nes));
+      SU_DEBUG_5(("nea_server_create(%p): success\n", (void *)nes));
       su_timer_set(nes->nes_timer, nes_event_timer, nes);
 
       nes->nes_callback = callback;
       nes->nes_context = context;
     }
     else {
-      SU_DEBUG_5(("nea_server_create(%p): failed\n", nes));
+      SU_DEBUG_5(("nea_server_create(%p): failed\n", (void *)nes));
       nea_server_destroy(nes), nes = NULL;
     }
   }
@@ -551,11 +551,11 @@
     return 500;
 
   if (nes->nes_in_callback) {
-    SU_DEBUG_5(("nea_server_shutdown(%p) while in callback\n", nes));
+    SU_DEBUG_5(("nea_server_shutdown(%p) while in callback\n", (void *)nes));
     return 100;
   }
   
-  SU_DEBUG_5(("nea_server_shutdown(%p)\n", nes));
+  SU_DEBUG_5(("nea_server_shutdown(%p)\n", (void *)nes));
 
   in_callback = nes->nes_in_callback; nes->nes_in_callback = 1;
 
@@ -585,12 +585,12 @@
     return;
 
   if (nes->nes_in_callback) {
-    SU_DEBUG_5(("nea_server_destroy(%p) while in callback\n", nes));
+    SU_DEBUG_5(("nea_server_destroy(%p) while in callback\n", (void *)nes));
     nes->nes_pending_destroy = 1;
     return;
   }
   
-  SU_DEBUG_5(("nea_server_destroy(%p)\n", nes));
+  SU_DEBUG_5(("nea_server_destroy(%p)\n", (void *)nes));
   
   nta_leg_destroy(nes->nes_leg), nes->nes_leg = NULL;
   
@@ -837,8 +837,8 @@
   if (evq->evq_content_type)
     nea_view_queue(nes, evv, evq);
 
-  SU_DEBUG_7(("nea_server_update(%p): %s (%s)\n", 
-	      nes, ev->ev_event->o_type, evv->evv_content_type->c_type));
+  SU_DEBUG_7(("nea_server_update(%p): %s (%s)\n", (void *)nes,
+	      ev->ev_event->o_type, evv->evv_content_type->c_type));
 
   return 1;
 }
@@ -1019,7 +1019,8 @@
   nea_sub_t *s;
   int notified = 0, throttled = nes->nes_throttled;
 
-  SU_DEBUG_7(("nea_server_notify(%p): %s\n", nes, ev ? ev->ev_event->o_type: ""));
+  SU_DEBUG_7(("nea_server_notify(%p): %s\n", (void *)nes,
+	      ev ? ev->ev_event->o_type: ""));
 
   ++nes->nes_in_list;
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -73,7 +73,7 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
 
 # Generate list of nta tags
 TAG_DLL_FLAGS = 	LIST=nta_tag_list
\ No newline at end of file

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta.c	Tue Mar 20 23:37:15 2007
@@ -29,7 +29,7 @@
  * 1) agent
  * 2) tport handling
  * 3) dispatching messages received from network
- * 4) message creation, message utility
+ * 4) message creation and message utility functions
  * 5) stateless operation
  * 6) dialogs (legs)
  * 7) server transactions (incoming)
@@ -145,7 +145,7 @@
 
 static int complete_response(msg_t *response, 
 			     int status, char const *phrase, 
-			     msg_t const *request);
+			     msg_t *request);
 
 #define IF_SIGCOMP_TPTAG_COMPARTMENT(cc)     TAG_IF(cc, TPTAG_COMPARTMENT(cc)),
 #define IF_SIGCOMP_TPTAG_COMPARTMENT_REF(cc) TPTAG_COMPARTMENT_REF(cc),
@@ -225,7 +225,7 @@
 static inline int incoming_merge(nta_incoming_t *irq, msg_t *msg, sip_t *sip,
 				 tport_t *tport);
 static inline int incoming_timestamp(nta_incoming_t *, msg_t *, sip_t *);
-static inline int incoming_timer(nta_agent_t *, su_duration_t);
+static inline su_duration_t incoming_timer(nta_agent_t *, su_duration_t);
 
 static nta_reliable_t *reliable_mreply(nta_incoming_t *,
 				       nta_prack_f *, nta_reliable_magic_t *,
@@ -259,7 +259,7 @@
 				     sip_via_t const *v);
 static int outgoing_recv(nta_outgoing_t *orq, int status, msg_t *, sip_t *);
 static void outgoing_default_recv(nta_outgoing_t *, int, msg_t *, sip_t *);
-static inline int outgoing_timer(nta_agent_t *, su_duration_t);
+static inline su_duration_t outgoing_timer(nta_agent_t *, su_duration_t);
 static int outgoing_recv_reliable(nta_outgoing_t *orq, msg_t *msg, sip_t *sip);
 
 /* Internal message passing */
@@ -311,7 +311,7 @@
 /**
  * Create an NTA agent object.
  *
- * The function nta_agent_create() creates an NTA agent object.  The agent
+ * Create an NTA agent object.  The agent
  * object creates and binds a server socket with address specified in @e url.
  * If the @e host portion of the @e url is @c "*", the agent listens to all
  * addresses available on the host.
@@ -327,6 +327,8 @@
  * @note
  * If @e url is @c NULL, the default @e url @c "sip:*" is used.
  * @par
+ * If @e url is @c NONE (iow, (void*)-1), no server sockets are bound.
+ * @par
  * If @p transport parameters are specified in @a url, agent uses only
  * specified transport type.
  *
@@ -369,8 +371,10 @@
     agent->sa_flags = MSG_DO_CANONIC;
 
     agent->sa_maxsize         = 2 * 1024 * 1024; /* 2 MB */
-    agent->sa_bad_req_mask    = (unsigned)(~(sip_mask_response | sip_mask_proxy));
-    agent->sa_bad_resp_mask   = (unsigned)(~(sip_mask_request | sip_mask_proxy));
+    agent->sa_bad_req_mask    = 
+      (unsigned) ~(sip_mask_response | sip_mask_proxy);
+    agent->sa_bad_resp_mask   = 
+      (unsigned) ~(sip_mask_request | sip_mask_proxy);
     agent->sa_t1 	      = NTA_SIP_T1;
     agent->sa_t2 	      = NTA_SIP_T2;
     agent->sa_t4              = NTA_SIP_T4;
@@ -563,14 +567,10 @@
 
 /** Return @Contact header.
  *
- * The function nta_agent_contact() returns a @Contact header, which can be
- * used to reach @a agent.
+ * Get a @Contact header, which can be used to reach @a agent.
  *
  * @param agent NTA agent object
  *
- * @return The function nta_agent_contact() returns a sip_contact_t object
- * corresponding to the @a agent.
- *
  * User agents can insert the @Contact header in the outgoing REGISTER,
  * INVITE, and ACK requests and replies to incoming INVITE and OPTIONS
  * transactions.
@@ -582,6 +582,8 @@
  *	 			 sip->sip_request->rq_url,
  *				 contact->m_url);
  * @endcode
+ *
+ * @return A sip_contact_t object corresponding to the @a agent.
  */
 sip_contact_t *nta_agent_contact(nta_agent_t const *agent)
 {
@@ -590,44 +592,68 @@
 
 /** Return a list of @Via headers.
  *
- * The function nta_agent_via() returns @Via headers for all activated
- * transport.
+ * Get @Via headers for all activated transport.
  *
  * @param agent NTA agent object
  *
- * @return The function nta_agent_via() returns a list of sip_via_t objects
- * used by the @a agent.
+ * @return A list of #sip_via_t objects used by the @a agent.
  */
 sip_via_t *nta_agent_via(nta_agent_t const *agent)
 {
   return agent ? agent->sa_vias : NULL;
 }
 
-
 /** Return a list of public (UPnP, STUN) @Via headers.
  *
- * The function nta_agent_public_via() returns public @Via headers for all activated
- * transports.
+ * Get public @Via headers for all activated transports.
  *
  * @param agent NTA agent object
  *
- * @return The function nta_agent_public_via() returns a list of sip_via_t objects
- * used by the @a agent.
+ * @return A list of #sip_via_t objects used by the @a agent.
  */
 sip_via_t *nta_agent_public_via(nta_agent_t const *agent)
 {
   return agent ? agent->sa_public_vias : NULL;
 }
 
+/** Match a @Via header @a v with @Via headers in @a agent.
+ *
+ */
+static
+sip_via_t *agent_has_via(nta_agent_t const *agent, sip_via_t const *via)
+{
+  sip_via_t const *v;
+
+  for (v = agent->sa_public_vias; v; v = v->v_next) {
+    if (strcasecmp(via->v_host, v->v_host))
+      continue;
+    if (str0cmp(via->v_port, v->v_port))
+      continue;
+    if (strcasecmp(via->v_protocol, v->v_protocol))
+      continue;
+    return (sip_via_t *)v;
+  }
+
+  for (v = agent->sa_vias; v; v = v->v_next) {
+    if (strcasecmp(via->v_host, v->v_host))
+      continue;
+    if (str0cmp(via->v_port, v->v_port))
+      continue;
+    if (strcasecmp(via->v_protocol, v->v_protocol))
+      continue;
+    return (sip_via_t *)v;
+  }
+
+  return NULL;
+}
+
 /** Return @UserAgent header.
  *
- * The function nta_agent_name() returns a @UserAgent information with
- * NTA version.
+ * Get @UserAgent information with NTA version.
  *
  * @param agent NTA agent object (may be NULL)
  *
- * @return The function nta_agent_contact() returns a string containing the
- * @a agent version.
+ * @return A string containing the @a agent version.
  */
 char const *nta_agent_version(nta_agent_t const *agent)
 {
@@ -667,53 +693,108 @@
 static
 int agent_timer_init(nta_agent_t *agent)
 {
-  return su_timer_set(agent->sa_timer =
-		      su_timer_create(su_root_task(agent->sa_root),
-				      NTA_SIP_T1 / 8),
+  agent->sa_timer = su_timer_create(su_root_task(agent->sa_root),
+				    NTA_SIP_T1 / 8);
+#if 0
+  return su_timer_set(agent->sa_timer,
 		      agent_timer,
 		      agent);
+#endif
+  return -(agent->sa_timer == NULL);
 }
 
+#define NEXT_TIMEOUT(next, p, f, now) \
+  (p && p->f - (next) < 0 ? (p->f - (now) > 0 ? p->f : (now)) : (next))
+
 /**
  * Agent timer routine.
  */
 static
 void agent_timer(su_root_magic_t *rm, su_timer_t *timer, nta_agent_t *agent)
 {
-  su_duration_t now = su_time_ms(agent->sa_now = su_now());
-  int again;
+  su_time_t stamp = su_now();
+  su_duration_t now = su_time_ms(stamp), next;
 
   now += now == 0;
 
+  agent->sa_now = stamp;
   agent->sa_millisec = now;
+  agent->sa_next = 0;
+  agent->sa_in_timer = 1;
 
-  again = outgoing_timer(agent, now);
-  again = incoming_timer(agent, now) || again;
+  next = now + SU_DURATION_MAX;
+  next = outgoing_timer(agent, next);
+  next = incoming_timer(agent, next);
 
   agent->sa_millisec = 0;
+  agent->sa_in_timer = 0;
 
-  if (again)
-    su_timer_set_at(timer, agent_timer, agent, su_time_add(su_now(), 1));
-  else
-    su_timer_set(timer, agent_timer, agent);
+  if (agent->sa_next)
+    next = NEXT_TIMEOUT(next, agent, sa_next, now);
+
+  if (next == now + SU_DURATION_MAX) {
+    /* Do not set timer */
+    SU_DEBUG_9(("nta: timer not set\n"));
+    assert(!agent->sa_out.completed->q_head);
+    assert(!agent->sa_out.trying->q_head);
+    assert(!agent->sa_out.inv_calling->q_head);
+    assert(!agent->sa_out.re_list);
+    assert(!agent->sa_in.inv_confirmed->q_head);
+    assert(!agent->sa_in.preliminary->q_head);
+    assert(!agent->sa_in.completed->q_head);
+    assert(!agent->sa_in.inv_completed->q_head);
+    assert(!agent->sa_in.re_list);
+    return;
+  }
+
+  if (next == now) if (++next == 0) ++next;
+
+  SU_DEBUG_9(("nta: timer %s to %ld ms\n", "set next", (long)(next - now)));
+
+  agent->sa_next = next;
+
+  su_timer_set_at(timer, agent_timer, agent, su_time_add(stamp, next - now));
 }
 
-/** Calculate nonzero value for timer */
-static inline
-su_duration_t set_timeout(nta_agent_t const *agent, su_duration_t offset)
+/** Calculate nonzero value for timeout.
+ *
+ * Sets or adjusts agent timer when needed.
+ *
+ * @retval 0 if offset is 0
+ * @retval timeout (millisecond counter) otherwise
+ */
+static
+su_duration_t set_timeout(nta_agent_t *agent, su_duration_t offset)
 {
-  su_duration_t now;
+  su_time_t now;
+  su_duration_t next, ms;
 
-#if 0
-  if (agent->sa_millisec)
-    now = agent->sa_millisec;
+  if (offset == 0)
+    return 0;
+
+  if (agent->sa_millisec) /* Avoid expensive call to su_timer_ms() */
+    now = agent->sa_now, ms = agent->sa_millisec;
   else
-#endif
-    now = (su_duration_t)su_time_ms(su_now());
+    now = su_now(), ms = (su_duration_t)su_time_ms(now);
+  
+  next = ms + offset; if (next == 0) next = 1;
 
-  now += offset;
+  if (agent->sa_in_timer)
+    return next;
 
-  return now ? now : 1;
+  if (agent->sa_next == 0 || agent->sa_next - next - 5L > 0) {
+    /* Set timer */
+    if (agent->sa_next)
+      SU_DEBUG_9(("nta: timer %s to %ld ms\n", "shortened", (long)offset));
+    else
+      SU_DEBUG_9(("nta: timer %s to %ld ms\n", "set", (long)offset));
+      
+    su_timer_set_at(agent->sa_timer, agent_timer, agent, 
+		    su_time_add(now, offset));
+    agent->sa_next = next;
+  }
+
+  return next;
 }
 
 
@@ -721,7 +802,10 @@
 static
 su_time_t agent_now(nta_agent_t const *agent)
 {
-  return agent->sa_millisec ? agent->sa_now : su_now();
+  if (agent && agent->sa_millisec != 0)
+    return agent->sa_now;
+  else 
+    return su_now();
 }
 
 
@@ -833,7 +917,7 @@
   unsigned threadpool      = agent->sa_tport_threadpool;
   char const *sigcomp = agent->sa_sigcomp_options;
   char const *algorithm = NONE;
-  msg_mclass_t *mclass = NONE;
+  msg_mclass_t const *mclass = NONE;
   sip_contact_t const *aliases = NONE;
   url_string_t const *proxy = NONE;
   tport_t *tport;
@@ -929,7 +1013,7 @@
     agent->sa_algorithm = su_strdup(home, algorithm);
 
   if (str0cmp(sigcomp, agent->sa_sigcomp_options)) {
-    char const * const *l = NULL;
+    msg_param_t const *l = NULL;
     char *s = su_strdup(home, sigcomp);
     char *s1 = su_strdup(home, s), *s2 = s1;
 
@@ -1338,7 +1422,7 @@
 
 /** Add a transport to the agent.
  *
- * The function nta_agent_add_tport() creates a new transport and binds it
+ * Creates a new transport and binds it
  * to the port specified by the @a uri. The @a uri must have sip: or sips:
  * scheme or be a wildcard uri ("*"). The @a uri syntax allowed is as
  * follows:
@@ -1475,7 +1559,7 @@
 	(nta_compressor_vtable == NULL || 
 	 strcasecmp(tpn->tpn_comp, nta_compressor_vtable->ncv_name) != 0)) {
       SU_DEBUG_1(("nta(%p): comp=%s not supported for " URL_PRINT_FORMAT "\n",
-		  self, tpn->tpn_comp, URL_PRINT_ARGS(url)));
+		  (void *)self, tpn->tpn_comp, URL_PRINT_ARGS(url)));
     }
   }
 
@@ -1960,7 +2044,8 @@
   }
   else {
     /* XXX - we should do something else? */
-    SU_DEBUG_3(("nta(%p): transport address updated\n", self));
+    SU_DEBUG_3(("%s(%p): %s\n", "nta", (void *)self, 
+		"transport address updated"));
   }
 }
 
@@ -2474,7 +2559,10 @@
 
   if (sip->sip_cseq->cs_method == sip_method_invite
       && 200 <= sip->sip_status->st_status
-      && sip->sip_status->st_status < 300) {
+      && sip->sip_status->st_status < 300
+      /* Exactly one Via header, belonging to us */
+      && sip->sip_via && !sip->sip_via->v_next 
+      && agent_has_via(agent, sip->sip_via)) {
     agent->sa_stats->as_trless_200++;
     /* Orphan 200 Ok to INVITE. ACK and BYE it */
     SU_DEBUG_5(("nta: %03d %s must be ACK&BYE\n", status, phrase));
@@ -2485,6 +2573,7 @@
   SU_DEBUG_5(("nta: %03d %s was discarded\n", status, phrase));
   msg_destroy(msg);
 }
+
 /** @internal Agent receives garbage */
 static
 void agent_recv_garbage(nta_agent_t *agent,
@@ -2800,7 +2889,7 @@
 static
 int complete_response(msg_t *response, 
 		      int status, char const *phrase, 
-		      msg_t const *request)
+		      msg_t *request)
 {
   su_home_t *home = msg_home(response);
   sip_t *response_sip = sip_object(response);
@@ -2951,11 +3040,11 @@
 
 /**Complete a request with values from dialog.
  *
- * The function nta_msg_request_complete() completes a request message @a
- * msg belonging to a dialog associated with @a leg. It increments the local
- * @CSeq value, adds @CallID, @To, @From and @Route headers (if
- * there is such headers present in @a leg), and creates a new request line
- * object from @a method, @a method_name and @a request_uri.
+ * Complete a request message @a msg belonging to a dialog associated with
+ * @a leg. It increments the local @CSeq value, adds @CallID, @To, @From and
+ * @Route headers (if there is such headers present in @a leg), and creates
+ * a new request line object from @a method, @a method_name and @a
+ * request_uri.
  *
  * @param msg          pointer to a request message object
  * @param leg          pointer to a #nta_leg_t object
@@ -3106,7 +3195,7 @@
   method = sip->sip_request->rq_method;
   method_name = sip->sip_request->rq_method_name;
 
-  if (!leg->leg_id && !sip->sip_call_id && sip->sip_cseq)
+  if (!leg->leg_id && sip->sip_cseq)
     seq = sip->sip_cseq->cs_seq; 
   else if (method == sip_method_ack || method == sip_method_cancel)
     /* Dangerous - we may do PRACK/UPDATE meanwhile */
@@ -3179,9 +3268,9 @@
 /**
  * Create a new leg object.
  *
- * The function nta_leg_tcreate() creates a leg object. A leg object is used
- * to represent dialogs, partial dialogs (for example, in case of REGISTER),
- * and destinations within a particular NTA object.
+ * Creates a leg object, which is used to represent dialogs, partial dialogs
+ * (for example, in case of REGISTER), and destinations within a particular
+ * NTA object.
  *
  * When a leg is created, a callback pointer and a application context is
  * provided. All other parameters are optional.
@@ -3421,7 +3510,7 @@
 
   leg_insert(agent, leg);
 
-  SU_DEBUG_9(("nta_leg_create(%p)\n", leg));
+  SU_DEBUG_9(("nta_leg_create(%p)\n", (void *)leg));
 
   return leg;
 
@@ -3471,7 +3560,7 @@
  */
 void nta_leg_destroy(nta_leg_t *leg)
 {
-  SU_DEBUG_9(("nta_leg_destroy(%p)\n", leg));
+  SU_DEBUG_9(("nta_leg_destroy(%p)\n", (void *)leg));
 
   if (leg) {
     leg_htable_t *leg_hash;
@@ -3518,8 +3607,8 @@
 
 /**Bind a callback function and context to a leg object.
  *
- * The function nta_leg_bind() is used to change the callback
- * and context pointer attached to a leg object.
+ * Change the callback function and context pointer attached to a leg
+ * object.
  *
  * @param leg      leg object to be bound
  * @param callback new callback function (or NULL if no callback is desired)
@@ -3566,6 +3655,7 @@
   if (tag) {
     if (sip_to_tag(leg->leg_home, leg->leg_local, tag) < 0)
       return NULL;
+    leg->leg_tagged = 1;
     return leg->leg_local->a_tag;
   }
 
@@ -3574,6 +3664,8 @@
   if (!tag || sip_to_add_param(leg->leg_home, leg->leg_local, tag) < 0)
     return NULL;
 
+  leg->leg_tagged = 1;
+
   return leg->leg_local->a_tag;
 }
 
@@ -3757,7 +3849,7 @@
 
   if (!(irq = incoming_create(agent, msg, sip, tport, tag))) {
     SU_DEBUG_3(("nta: leg_recv(%p): cannot create transaction for %s\n",
-		leg, method_name));
+		(void *)leg, method_name));
     nta_msg_treply(agent, msg,
 		   SIP_500_INTERNAL_SERVER_ERROR,
 		   NTATAG_TPORT(tport),
@@ -3783,12 +3875,12 @@
 
   if (status < 100 || status > 699) {
     SU_DEBUG_3(("nta_leg(%p): invalid status %03d from callback\n",
-		leg, status));
+		(void *)leg, status));
     status = 500;
   }
   else if (method == sip_method_invite && status >= 200 && status < 300) {
     SU_DEBUG_3(("nta_leg(%p): invalid INVITE status %03d from callback\n",
-		leg, status));
+		(void *)leg, status));
     status = 500;
   }
 
@@ -3818,8 +3910,8 @@
 
 /** Get a leg by dialog.
  *
- * The function nta_leg_by_dialog() searches for a dialog leg from agent's
- * hash table. The matching rules based on parameters  are as follows:
+ * Search for a dialog leg from agent's hash table. The matching rules based
+ * on parameters are as follows:
  *
  * @param agent        pointer to agent object
  * @param request_uri  if non-NULL, and there is destination URI
@@ -3922,6 +4014,14 @@
     /* Do not match if the incoming To has tag, but the local does not */
     if (!local_tag && to_tag)
       continue;
+
+    /*
+     * Do not match if incoming To has no tag and we have local tag
+     * and the tag has been there from the beginning.
+     */
+    if (local_tag && !to_tag && !leg->leg_tagged)
+      continue;
+
     /* Do not match if incoming From has no tag but remote has a tag */
     if (remote_tag && !from_tag)
       continue;
@@ -4034,8 +4134,8 @@
 
 /** Set leg route and target URL.
  *
- * The function leg_route() sets the leg route and contact using the
- * @RecordRoute and @Contact headers.
+ * Sets the leg route and contact using the @RecordRoute and @Contact
+ * headers.
  */
 static
 int leg_route(nta_leg_t *leg,
@@ -4153,7 +4253,7 @@
 static inline void incoming_remove(nta_incoming_t *irq);
 static inline void incoming_set_timer(nta_incoming_t *, unsigned interval);
 static inline void incoming_reset_timer(nta_incoming_t *);
-static inline size_t incoming_mass_destroy(nta_agent_t *sa, incoming_queue_t *q);
+static inline size_t incoming_mass_destroy(nta_agent_t *, incoming_queue_t *);
 
 static int incoming_set_params(nta_incoming_t *irq, tagi_t const *tags);
 static inline
@@ -4208,9 +4308,9 @@
 
 /** Create a server transaction. 
  *
- * The function nta_incoming_create() creates a server transaction for a
- * request message. This function is used when an element processing
- * requests statelessly wants to process a particular request statefully.
+ * Create a server transaction for a request message. This function is used
+ * when an element processing requests statelessly wants to process a
+ * particular request statefully.
  *
  * @param agent pointer to agent object
  * @param leg  pointer to leg object (either @a agent or @a leg may be NULL)
@@ -4436,7 +4536,7 @@
 
     if (leg->leg_rseq > sip->sip_cseq->cs_seq) {
       SU_DEBUG_3(("nta_leg(%p): out-of-order %s (%u < %u)\n",
-		  leg, method_name, seq, leg->leg_rseq));
+		  (void *)leg, method_name, seq, leg->leg_rseq));
       return 500;
     }
 
@@ -4514,8 +4614,8 @@
 /** @internal
  * Insert an incoming transaction into a queue. 
  *
- * The function incoming_queue() inserts a server transaction into a queue,
- * and sets the corresponding timeout at the same time.
+ * Insert a server transaction into a queue, and sets the corresponding
+ * timeout at the same time.
  */
 static inline
 void incoming_queue(incoming_queue_t *queue, 
@@ -4531,10 +4631,7 @@
 
   assert(*queue->q_tail == NULL);
 
-  if (queue->q_timeout)
-    irq->irq_timeout = set_timeout(irq->irq_agent, queue->q_timeout);
-  else
-    irq->irq_timeout = 0;
+  irq->irq_timeout = set_timeout(irq->irq_agent, queue->q_timeout);
 
   irq->irq_queue = queue;
   irq->irq_prev = queue->q_tail; 
@@ -4626,7 +4723,7 @@
 static
 void incoming_free(nta_incoming_t *irq)
 {
-  SU_DEBUG_9(("nta: incoming_free(%p)\n", irq));
+  SU_DEBUG_9(("nta: incoming_free(%p)\n", (void *)irq));
 
   incoming_cut_off(irq);
   incoming_reclaim(irq);
@@ -4706,7 +4803,8 @@
   incoming_queue_t *q = u->a_incoming_queue;
   nta_incoming_t *irq, *irq_next;
 
-  SU_DEBUG_9(("incoming_reclaim_all(%p, %p, %p)\n", rm, msg, u));
+  SU_DEBUG_9(("incoming_reclaim_all(%p, %p, %p)\n",
+	      (void *)rm, (void *)msg, (void *)u));
 
   for (irq = q->q_head; irq; irq = irq_next) {
     irq_next = irq->irq_next;
@@ -4716,10 +4814,10 @@
 
 /**Bind a callback and context to an incoming transaction object
  *
- * The function nta_incoming_bind() is used to set the callback and
- * context pointer attached to an incoming request object.  The callback
- * function will be invoked if the incoming request is cancelled, or if the
- * final response to an incoming @b INVITE request has been acknowledged.
+ * Set the callback function and context pointer attached to an incoming
+ * request object. The callback function will be invoked if the incoming
+ * request is cancelled, or if the final response to an incoming @b INVITE
+ * request has been acknowledged.
  *
  * If the callback is NULL, or no callback has been bound, NTA invokes the
  * request callback of the call leg.
@@ -4916,15 +5014,16 @@
 nta_incoming_t *incoming_find(nta_agent_t const *agent,
 			      sip_t const *sip,
 			      sip_via_t const *v,
-			      nta_incoming_t **merge,
-			      nta_incoming_t **ack)
+			      nta_incoming_t **return_merge,
+			      nta_incoming_t **return_ack)
 {
   sip_cseq_t const *cseq = sip->sip_cseq;
   sip_call_id_t const *i = sip->sip_call_id;
   sip_to_t const *to = sip->sip_to;
   sip_from_t const *from = sip->sip_from;
   sip_request_t *rq = sip->sip_request;
-  int is_uas_ack = ack && agent->sa_is_a_uas && rq->rq_method == sip_method_ack;
+  int is_uas_ack = return_ack && 
+    agent->sa_is_a_uas && rq->rq_method == sip_method_ack;
   incoming_htable_t const *iht = agent->sa_incoming;
   hash_value_t hash = NTA_HASH(i, cseq->cs_seq);
 
@@ -4941,6 +5040,30 @@
       continue;
     if (str0casecmp(irq->irq_from->a_tag, from->a_tag))
       continue;
+
+    if (str0casecmp(irq->irq_via->v_branch, v->v_branch) != 0 ||
+	strcasecmp(irq->irq_via->v_host, v->v_host) != 0) {
+      if (!agent->sa_is_a_uas)
+	continue;
+      
+      if (is_uas_ack &&
+	  irq->irq_method == sip_method_invite && 
+	  200 <= irq->irq_status && irq->irq_status < 300 &&
+	  addr_match(irq->irq_to, to))
+	*return_ack = irq;
+      /* RFC3261 - section 8.2.2.2 Merged Requests */
+      else if (return_merge && agent->sa_merge_482 &&
+	       irq->irq_cseq->cs_method == cseq->cs_method &&
+	       (irq->irq_cseq->cs_method != sip_method_unknown ||
+		strcmp(irq->irq_cseq->cs_method_name, 
+		       cseq->cs_method_name) == 0)) {
+	*return_merge = irq;
+	continue;
+      }
+      else
+	continue;
+    }
+
     if (is_uas_ack) {
       if (!addr_match(irq->irq_to, to))
 	continue;
@@ -4953,16 +5076,6 @@
     else if (str0casecmp(irq->irq_to->a_tag, to->a_tag))
       continue;
 
-    if (str0casecmp(irq->irq_via->v_branch, v->v_branch) != 0) {
-      if (!agent->sa_is_a_uas)
-	continue;
-      if (is_uas_ack && irq->irq_status >= 200 && irq->irq_status < 300)
-	*ack = irq;
-      /* RFC3261 - section 8.2.2.2 Merged Requests */
-      else if (merge && !to->a_tag && agent->sa_merge_482)
-	*merge = irq;
-      continue;
-    }
     if (!is_uas_ack && url_cmp(irq->irq_rq->rq_url, rq->rq_url))
       continue;
 
@@ -4974,18 +5087,24 @@
     if (irq->irq_method == rq->rq_method)
       break;		/* found */
 
-    if (ack && rq->rq_method == sip_method_cancel)
-      *ack = irq;
-    else if (ack && rq->rq_method == sip_method_ack && 
-	     irq->irq_method == sip_method_invite)
-      *ack = irq;
+    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;
   }
 
   if (irq)
     return irq;
 
   /* Check PRACKed requests */
-  if (ack && rq->rq_method == sip_method_prack && sip->sip_rack) {
+  if (return_ack && rq->rq_method == sip_method_prack && sip->sip_rack) {
     sip_rack_t const *rack = sip->sip_rack;
     hash = NTA_HASH(i, rack->ra_cseq);
 
@@ -5005,7 +5124,7 @@
 	continue;
       if (!irq->irq_from->a_tag != !from->a_tag)
 	continue;
-      *ack = irq;
+      *return_ack = irq;
 
       return NULL;
     }
@@ -5097,10 +5216,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;
@@ -5276,6 +5403,34 @@
   return 0;
 }
 
+/** Add essential headers to the response message */
+static int nta_incoming_response_headers(nta_incoming_t *irq, 
+					 msg_t *msg,
+					 sip_t *sip)
+{
+  int clone = 0;
+  su_home_t *home = msg_home(msg);
+
+  if (!sip->sip_from)
+    clone = 1, sip->sip_from = sip_from_copy(home, irq->irq_from);
+  if (!sip->sip_to)
+    clone = 1, sip->sip_to = sip_to_copy(home, irq->irq_to);
+  if (!sip->sip_call_id)
+    clone = 1, sip->sip_call_id = sip_call_id_copy(home, irq->irq_call_id);
+  if (!sip->sip_cseq)
+    clone = 1, sip->sip_cseq = sip_cseq_copy(home, irq->irq_cseq);
+  if (!sip->sip_via)
+    clone = 1, sip->sip_via = sip_via_copy(home, irq->irq_via);
+
+  if (clone)
+    msg_set_parent(msg, (msg_t *)irq->irq_home);
+
+  if (!sip->sip_from || !sip->sip_to || !sip->sip_call_id || !sip->sip_cseq || !sip->sip_via)
+    return -1;
+
+  return 0;
+}
+
 /** Complete a response message.
  *
  * @param irq     server transaction object
@@ -5297,7 +5452,6 @@
 {
   su_home_t *home = msg_home(msg);
   sip_t *sip = sip_object(msg);
-  int clone = 0;
   int retval;
   ta_list ta;
 
@@ -5307,7 +5461,7 @@
   if (status != 0 && (status < 100 || status > 699))
     return su_seterrno(EINVAL), -1;
 
-  if (!sip->sip_status)
+  if (status != 0 && !sip->sip_status)
     sip->sip_status = sip_status_create(home, status, phrase, NULL);
 
   ta_start(ta, tag, value);
@@ -5320,42 +5474,54 @@
   if (irq->irq_default)
     return sip_complete_message(msg);
 
-  if (!sip->sip_from)
-    clone = 1, sip->sip_from = sip_from_copy(home, irq->irq_from);
   if (status > 100 && !irq->irq_tag) {
     if (sip->sip_to)
       nta_incoming_tag(irq, sip->sip_to->a_tag);
     else
       nta_incoming_tag(irq, NULL);
   }
-  if (!sip->sip_to)
-    clone = 1, sip->sip_to = sip_to_copy(home, irq->irq_to);
+
+  if (nta_incoming_response_headers(irq, msg, sip) < 0)
+    return -1;
+
   if (sip->sip_status && sip->sip_status->st_status > 100 &&
       irq->irq_tag && sip->sip_to && !sip->sip_to->a_tag)
-    sip_to_tag(home, sip->sip_to, irq->irq_tag);
-  if (!sip->sip_call_id)
-    clone = 1, sip->sip_call_id = sip_call_id_copy(home, irq->irq_call_id);
-  if (!sip->sip_cseq)
-    clone = 1, sip->sip_cseq = sip_cseq_copy(home, irq->irq_cseq);
-  if (!sip->sip_via)
-    clone = 1, sip->sip_via = sip_via_copy(home, irq->irq_via);
-  if (status < 300 && 
-      !sip->sip_record_route && irq->irq_record_route)
-    sip_add_dup(msg, sip, (sip_header_t *)irq->irq_record_route);
-
-  if (clone)
-    msg_set_parent(msg, (msg_t *)irq->irq_home);
+    if (sip_to_tag(home, sip->sip_to, irq->irq_tag) < 0)
+      return -1;
 
-  if (retval < 0 || !sip->sip_from || !sip->sip_to || !sip->sip_call_id
-      || !sip->sip_cseq || !sip->sip_via
-      || (status < 300 && irq->irq_record_route && !sip->sip_record_route &&
-	  sip->sip_cseq && sip->sip_cseq->cs_method != sip_method_register))
-    return -1;
+  if (status < 300 && !sip->sip_record_route && irq->irq_record_route)
+    if (sip_add_dup(msg, sip, (sip_header_t *)irq->irq_record_route) < 0)
+      return -1;
 
   return sip_complete_message(msg);
 }
 
 
+/** Create a response message for request.
+ *
+ * @NEW_1_12_5.
+ */
+msg_t *nta_incoming_create_response(nta_incoming_t *irq,
+				    int status, char const *phrase)
+{
+  msg_t *msg = NULL;
+  sip_t *sip;
+
+  if (irq) {
+    msg = nta_msg_create(irq->irq_agent, 0);
+    sip = sip_object(msg);
+
+    if (status != 0)
+      sip->sip_status = sip_status_create(msg_home(msg), status, phrase, NULL);
+
+    if (nta_incoming_response_headers(irq, msg, sip) < 0)
+      msg_destroy(msg), msg = NULL;
+  }
+  
+  return msg;
+}
+
+
 /**Reply to an incoming transaction request.
  *
  * This function creates a response message to an incoming request and sends
@@ -5744,8 +5910,9 @@
 
 /** @internal Timer routine for the incoming request. */
 static inline
-int incoming_timer(nta_agent_t *sa, su_duration_t now)
+su_duration_t incoming_timer(nta_agent_t *sa, su_duration_t next)
 {
+  su_duration_t now = sa->sa_millisec;
   nta_incoming_t *irq, *irq_next;
   size_t retransmitted = 0, timeout = 0, terminated = 0, destroyed = 0;
   size_t unconfirmed = 
@@ -5762,8 +5929,9 @@
 
   /* Handle retry queue */
   while ((irq = sa->sa_in.re_list)) {
-    if ((irq->irq_retry && irq->irq_retry - now > 0) ||
-	retransmitted >= timer_max_retransmit) 
+    if (irq->irq_retry - now > 0)
+      break;
+    if (retransmitted >= timer_max_retransmit)
       break;
 
     if (irq->irq_method == sip_method_invite && irq->irq_status >= 200) {
@@ -5801,7 +5969,8 @@
 	retransmitted++;
 	incoming_retransmit_reply(irq, irq->irq_tport);
       }
-    } else {
+    }
+    else {
       /* Timer N1 */
       SU_DEBUG_5(("nta: timer N1 fired, sending %u %s\n", SIP_100_TRYING));
       incoming_reset_timer(irq);
@@ -5809,6 +5978,8 @@
     }
   }
 
+  next = NEXT_TIMEOUT(next, irq, irq_retry, now);
+
   while ((irq = sa->sa_in.final_failed->q_head)) {
     incoming_remove(irq);
     irq->irq_final_failed = 0;
@@ -5841,8 +6012,9 @@
     assert(irq->irq_status < 200);
     assert(irq->irq_timeout);
 
-    if (irq->irq_timeout - now > 0 
-	|| timeout >= timer_max_timeout)
+    if (irq->irq_timeout - now > 0)
+      break;
+    if (timeout >= timer_max_timeout)
       break;
 
     timeout++;
@@ -5855,6 +6027,8 @@
     reliable_timeout(irq, 1);
   }
 
+  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
+
   while ((irq = sa->sa_in.inv_completed->q_head)) {
     assert(irq->irq_status >= 200);
     assert(irq->irq_timeout);
@@ -5883,13 +6057,14 @@
     }
   } 
 
+  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
+
   while ((irq = sa->sa_in.inv_confirmed->q_head)) {
     assert(irq->irq_timeout);
     assert(irq->irq_status >= 200);
     assert(irq->irq_method == sip_method_invite);
 
-    if (irq->irq_timeout - now > 0 || 
-	terminated >= timer_max_terminate)
+    if (irq->irq_timeout - now > 0 || terminated >= timer_max_terminate)
       break;
     
     /* Timer I */
@@ -5905,13 +6080,14 @@
       incoming_free_queue(rq, irq);
   }
 
+  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
+
   while ((irq = sa->sa_in.completed->q_head)) {
     assert(irq->irq_status >= 200);
     assert(irq->irq_timeout);
     assert(irq->irq_method != sip_method_invite);
 
-    if (irq->irq_timeout - now > 0 || 
-	terminated >= timer_max_terminate)
+    if (irq->irq_timeout - now > 0 || terminated >= timer_max_terminate)
       break;
 
     /* Timer J */
@@ -5928,6 +6104,8 @@
       incoming_free_queue(rq, irq);
   }
 
+  next = NEXT_TIMEOUT(next, irq, irq_timeout, now);
+
   for (irq = sa->sa_in.terminated->q_head; irq; irq = irq_next) {
     irq_next = irq->irq_next;
     if (irq->irq_destroyed)
@@ -5947,10 +6125,7 @@
 		terminated, unterminated, 
 		destroyed, total));
 
-  return 
-    retransmitted >= timer_max_retransmit
-    || timeout >= timer_max_timeout
-    || terminated >= timer_max_terminate;
+  return next;
 }
 
 /** Mass destroy server transactions */
@@ -6018,11 +6193,11 @@
 static inline void outgoing_set_timer(nta_outgoing_t *orq, unsigned interval);
 static inline void outgoing_reset_timer(nta_outgoing_t *orq);
 static size_t outgoing_timer_dk(outgoing_queue_t *q, 
-			     char const *timer, 
-			     su_duration_t now);
+				char const *timer, 
+				su_duration_t now);
 static size_t outgoing_timer_bf(outgoing_queue_t *q, 
-			     char const *timer, 
-			     su_duration_t now);
+				char const *timer, 
+				su_duration_t now);
 
 static void outgoing_ack(nta_outgoing_t *orq, msg_t *msg, sip_t *sip);
 static msg_t *outgoing_ackmsg(nta_outgoing_t *, sip_method_t, char const *,
@@ -6093,12 +6268,11 @@
 
 /**Create an outgoing request and client transaction belonging to the leg.
  *
- * The function nta_outgoing_tcreate() creates a request message and passes
- * the request message to an outgoing client transaction object. The request
- * is sent to the @a route_url (if non-NULL), default proxy (if defined by
- * NTATAG_DEFAULT_PROXY()), or to the address specified by @a request_uri.
- * If no @a request_uri is specified, it is taken from route-set target or
- * from the @To header.
+ * Create a request message and pass the request message to an outgoing
+ * client transaction object. The request is sent to the @a route_url (if
+ * non-NULL), default proxy (if defined by NTATAG_DEFAULT_PROXY()), or to
+ * the address specified by @a request_uri. If no @a request_uri is
+ * specified, it is taken from route-set target or from the @To header.
  *
  * When NTA receives response to the request, it invokes the @a callback
  * function.
@@ -6381,7 +6555,8 @@
     return;
 
   if (orq->orq_destroyed) {
-    SU_DEBUG_1(("nta_outgoing_destroy(%p): already destroyed\n", orq));
+    SU_DEBUG_1(("%s(%p): %s\n", "nta_outgoing_destroy", (void *)orq, 
+		"already destroyed"));
     return;
   }
 
@@ -6474,10 +6649,10 @@
 
 /**Create an outgoing request.
  *
- * The function outgoing_create() creates an outgoing transaction object and
- * sends the request to the network. The request is sent to the @a route_url
- * (if non-NULL), default proxy (if defined by NTATAG_DEFAULT_PROXY()), or
- * to the address specified by @a sip->sip_request->rq_url.
+ * Create an outgoing transaction object and send the request to the
+ * network. The request is sent to the @a route_url (if non-NULL), default
+ * proxy (if defined by NTATAG_DEFAULT_PROXY()), or to the address specified
+ * by @a sip->sip_request->rq_url.
  *
  * When NTA receives response to the request, it invokes the @a callback
  * function.
@@ -7228,8 +7403,8 @@
 /** @internal
  * Insert an outgoing transaction into a queue. 
  *
- * The function outgoing_queue() inserts a client transaction into a queue,
- * and sets the corresponding timeout at the same time.
+ * Insert a client transaction into a queue and set the corresponding
+ * timeout at the same time.
  */
 static inline
 void outgoing_queue(outgoing_queue_t *queue, 
@@ -7246,7 +7421,7 @@
   assert(*queue->q_tail == NULL);
 
   orq->orq_timeout = set_timeout(orq->orq_agent, queue->q_timeout);
-    
+
   orq->orq_queue = queue;
   orq->orq_prev = queue->q_tail; 
   *queue->q_tail = orq;
@@ -7277,8 +7452,7 @@
 
 /** Set retransmit timer (orq_retry).
  *
- * The function outgoing_set_timer() will set the retry timer (B/D) on
- * the outgoing request (client transaction). 
+ * Set the retry timer (B/D) on the outgoing request (client transaction).
  */
 static inline
 void outgoing_set_timer(nta_outgoing_t *orq, unsigned interval)
@@ -7305,6 +7479,7 @@
 
   orq->orq_retry = set_timeout(orq->orq_agent, orq->orq_interval = interval);
 
+  /* Shortcut into queue at SIP T1 */
   rq = orq->orq_agent->sa_out.re_t1;
 
   if (!(*rq) || (*rq)->orq_retry - orq->orq_retry > 0)
@@ -7343,7 +7518,7 @@
 static
 void outgoing_free(nta_outgoing_t *orq)
 {
-  SU_DEBUG_9(("nta: outgoing_free(%p)\n", orq));
+  SU_DEBUG_9(("nta: outgoing_free(%p)\n", (void *)orq));
   outgoing_cut_off(orq);
   outgoing_reclaim(orq);
 }
@@ -7410,7 +7585,8 @@
   outgoing_queue_t *q = u->a_outgoing_queue;
   nta_outgoing_t *orq, *orq_next;
 
-  SU_DEBUG_9(("outgoing_reclaim_all(%p, %p, %p)\n", rm, msg, u));
+  SU_DEBUG_9(("outgoing_reclaim_all(%p, %p, %p)\n",
+	      (void *)rm, (void *)msg, (void *)u));
 
   for (orq = q->q_head; orq; orq = orq_next) {
     orq_next = orq->orq_next;
@@ -7434,6 +7610,14 @@
   if (orq->orq_terminated || orq->orq_default) {
     outgoing_free(orq);
   }
+  /* We have to handle 200 OK statelessly => 
+     kill transaction immediately */
+  else if (orq->orq_method == sip_method_invite && !orq->orq_completed
+	   /* (unless we have to wait to send CANCEL) */
+	   && !orq->orq_cancel) {
+    orq->orq_destroyed = 1;
+    outgoing_terminate(orq);
+  }
   else {
     orq->orq_destroyed = 1;
     orq->orq_callback = outgoing_default_cb;
@@ -7441,24 +7625,29 @@
   }
 }
 
-/** @internal Outgoing transaction timer routine. */
-static
-int outgoing_timer(nta_agent_t *sa, su_duration_t now)
+/** @internal Outgoing transaction timer routine. 
+ *
+ */
+static inline 
+su_duration_t outgoing_timer(nta_agent_t *sa, su_duration_t next)
 {
+  su_duration_t now = sa->sa_millisec;
   nta_outgoing_t *orq;
   outgoing_queue_t rq[1];
   size_t retransmitted = 0, terminated = 0, timeout = 0, destroyed;
   size_t total = sa->sa_outgoing->oht_used;
   size_t trying = sa->sa_out.re_length;
-  size_t pending = sa->sa_out.trying->q_length + sa->sa_out.inv_calling->q_length;
+  size_t pending = sa->sa_out.trying->q_length + 
+    sa->sa_out.inv_calling->q_length;
   size_t completed = sa->sa_out.completed->q_length + 
     sa->sa_out.inv_completed->q_length;
 
   outgoing_queue_init(sa->sa_out.free = rq, 0);
 
   while ((orq = sa->sa_out.re_list)) {
-    if ((orq->orq_retry && orq->orq_retry - now > 0)
-	|| retransmitted >= timer_max_retransmit)
+    if (orq->orq_retry - now > 0)
+      break;
+    if (retransmitted >= timer_max_retransmit)
       break;
 
     if (orq->orq_reliable) {
@@ -7494,14 +7683,22 @@
       su_root_yield(sa->sa_root);	/* Handle received packets */
   }
 
+  next = NEXT_TIMEOUT(next, orq, orq_retry, now);
+
   terminated
     = outgoing_timer_dk(sa->sa_out.inv_completed, "D", now)
     + outgoing_timer_dk(sa->sa_out.completed, "K", now);
 
+  next = NEXT_TIMEOUT(next, sa->sa_out.inv_completed->q_head, orq_timeout, now);
+  next = NEXT_TIMEOUT(next, sa->sa_out.completed->q_head, orq_timeout, now);
+
   timeout
     = outgoing_timer_bf(sa->sa_out.inv_calling, "B", now)
     + outgoing_timer_bf(sa->sa_out.trying, "F", now);
 
+  next = NEXT_TIMEOUT(next, sa->sa_out.inv_calling->q_head, orq_timeout, now);
+  next = NEXT_TIMEOUT(next, sa->sa_out.trying->q_head, orq_timeout, now);
+
   destroyed = outgoing_mass_destroy(sa, rq);
 
   sa->sa_out.free = NULL;
@@ -7518,10 +7715,7 @@
 		destroyed, total));
   }
 
-  return 
-    retransmitted >= timer_max_retransmit || 
-    terminated >= timer_max_terminate || 
-    timeout >= timer_max_timeout;
+  return next;
 }
 
 /** @internal Retransmit the outgoing request. */
@@ -7558,16 +7752,12 @@
 			 char const *timer, 
 			 su_duration_t now)
 {
+  nta_outgoing_t *orq;
   size_t timeout = 0;
 
-  for (;;) {
-    nta_outgoing_t *orq = q->q_head;
-
-    if (!orq 
-	|| !orq->orq_timeout
-	|| orq->orq_timeout - now > 0 
-	|| timeout >= timer_max_timeout)
-      return timeout;
+  while ((orq = q->q_head)) {
+    if (orq->orq_timeout - now > 0 || timeout >= timer_max_timeout)
+      break;
 
     timeout++;
     
@@ -7579,6 +7769,8 @@
 
     assert(q->q_head != orq || orq->orq_timeout - now > 0);
   }
+
+  return timeout;
 }
 
 /** @internal Signal transaction timeout to the application. */
@@ -7587,7 +7779,8 @@
   nta_outgoing_t *cancel;
 
   if (outgoing_other_destinations(orq)) {
-    SU_DEBUG_5(("nta(%p): try next after timeout\n", orq));
+    SU_DEBUG_5(("%s(%p): %s\n", "nta", (void *)orq,
+		"try next after timeout"));
     outgoing_try_another(orq);
     return;
   }
@@ -7634,16 +7827,12 @@
 			 char const *timer, 
 			 su_duration_t now)
 {
+  nta_outgoing_t *orq;
   size_t terminated = 0;
 
-  for (;;) {
-    nta_outgoing_t *orq = q->q_head;
-
-    if (!orq 
-	|| !orq->orq_timeout 
-	|| orq->orq_timeout - now > 0 
-	|| terminated >= timer_max_terminate)
-      return terminated;
+  while ((orq = q->q_head)) {
+    if (orq->orq_timeout - now > 0 || terminated >= timer_max_terminate)
+      break;
 
     terminated++;
 
@@ -7652,6 +7841,8 @@
 
     outgoing_terminate(orq);
   }
+  
+  return terminated;
 }
 
 /** Terminate a client transaction. */
@@ -7835,6 +8026,16 @@
       outgoing_send(cancel, 0);
     else
       outgoing_reply(cancel, SIP_481_NO_TRANSACTION, 0);
+
+    if (status < 300 && orq->orq_destroyed && 
+	orq->orq_method == sip_method_invite) {
+      outgoing_terminate(orq);      /* We can now kill transaction */
+      if (status == 100) {
+	msg_destroy(msg);
+	return 0;
+      }
+      return -1;
+    }
   }
 
   if (orq->orq_pending) {
@@ -7896,7 +8097,8 @@
   }
   else if (orq->orq_method != sip_method_ack) {
     /* Non-INVITE */
-    if (orq->orq_queue == sa->sa_out.trying) {
+    if (orq->orq_queue == sa->sa_out.trying ||
+	orq->orq_queue == sa->sa_out.resolving) {
       assert(orq_status < 200); (void)orq_status;
 
       if (status < 200) {
@@ -7911,7 +8113,8 @@
 	msg_destroy(msg);
 	return 0;
       }
-    } else {
+    }
+    else {
       /* Already completed or terminated */
       assert(orq->orq_queue == sa->sa_out.completed ||
 	     orq->orq_queue == sa->sa_out.terminated);
@@ -8167,7 +8370,8 @@
 
   if (orq->orq_method == sip_method_ack) {
     if (status != delayed)
-      SU_DEBUG_3(("nta(%p): responding %u %s to ACK!\n", orq, status, phrase));
+      SU_DEBUG_3(("nta(%p): responding %u %s to ACK!\n",
+		  (void *)orq, status, phrase));
     orq->orq_status = status;
     if (orq->orq_queue == NULL)
       outgoing_complete(orq);	/* Timer D/K */
@@ -8447,8 +8651,8 @@
 
     /* Nothing found */
     if (!sr->sr_tports[0]) {
-      SU_DEBUG_3(("nta(%p): transport %s is not supported%s%s\n", orq, tpname,
-		  ident ? " by interface " : "", ident ? ident : ""));
+      SU_DEBUG_3(("nta(%p): transport %s is not supported%s%s\n", (void *)orq,
+		  tpname, ident ? " by interface " : "", ident ? ident : ""));
       outgoing_resolving_error(orq, SIPDNS_503_ERROR);
       return;
     }
@@ -9062,10 +9266,10 @@
     inet_ntop(AF_INET6, &aaaa->aaaa_addr, addr, sizeof(addr));
 
     if (j == 0)
-      SU_DEBUG_5(("nta(%p): %s IN AAAA %s\n", orq, 
+      SU_DEBUG_5(("nta(%p): %s IN AAAA %s\n", (void *)orq, 
 		  aaaa->aaaa_record->r_name, addr));
     else
-      SU_DEBUG_5(("nta(%p):  AAAA %s\n", orq, addr));
+      SU_DEBUG_5(("nta(%p):  AAAA %s\n", (void *)orq, addr));
 
     assert(j < found);
     results[j++] = su_strdup(home, addr);
@@ -9147,7 +9351,7 @@
     if (j == 0)
       SU_DEBUG_5(("nta: %s IN A %s\n", a->a_record->r_name, addr));
     else
-      SU_DEBUG_5(("nta(%p):  A %s\n", orq, addr));
+      SU_DEBUG_5(("nta(%p):  A %s\n", (void *)orq, addr));
 
     assert(j < found);
     results[j++] = su_strdup(home, addr);
@@ -9171,7 +9375,7 @@
       sq->sq_type != sr->sr_a_aaaa2) {
     sq->sq_type = sr->sr_a_aaaa2;
 
-    SU_DEBUG_7(("nta(%p): %s %s record still unresolved\n", orq,
+    SU_DEBUG_7(("nta(%p): %s %s record still unresolved\n", (void *)orq,
 		sq->sq_domain, sq->sq_type == sres_type_a ? "A" : "AAAA"));
 
     /*
@@ -9492,8 +9696,9 @@
   status = rel->rel_callback(rel->rel_magic, rel, pr_irq, sip); rel = NULL;
   irq->irq_in_callback = pr_irq->irq_in_callback = 0;
 
-  if (pr_irq->irq_destroyed && pr_irq->irq_terminated) {
-    incoming_free(pr_irq);
+  if (pr_irq->irq_completed) {	/* Already sent final response */
+    if (pr_irq->irq_terminated && pr_irq->irq_destroyed)
+      incoming_free(pr_irq);
   }
   else if (status != 0) {
     if (status < 200 || status > 299) {
@@ -9622,8 +9827,7 @@
 
 /** Destroy a reliable response.
  *
- * The function nta_reliable_destroy() marks a reliable response object for
- * destroyal, and frees it if possible.
+ * Mark a reliable response object for destroyal and free it if possible.
  */
 void nta_reliable_destroy(nta_reliable_t *rel)
 {
@@ -9631,7 +9835,7 @@
     return;
 
   if (rel->rel_callback == nta_reliable_destroyed)
-    SU_DEBUG_1(("%s(%p): already destroyed\n", __func__, rel));
+    SU_DEBUG_1(("%s(%p): %s\n", __func__, (void *)rel, "already destroyed"));
 
   rel->rel_callback = nta_reliable_destroyed;
 
@@ -9658,7 +9862,7 @@
 
   if (!*prev) {
     assert(*prev);
-    SU_DEBUG_1(("%s(%p): not linked\n", __func__, rel));
+    SU_DEBUG_1(("%s(%p): %s\n", __func__, (void *)rel, "not linked"));
     return 200;
   }
 
@@ -9739,7 +9943,8 @@
   if (orq == NULL || to_tag == NULL)
     return NULL;
   if (orq->orq_to->a_tag) {
-    SU_DEBUG_1(("%s: transaction %p already in dialog\n", __func__, orq));
+    SU_DEBUG_1(("%s: transaction %p already in dialog\n", __func__,
+		(void *)orq));
     return NULL;
   }
 
@@ -9755,6 +9960,9 @@
 
   tagged->orq_prev = NULL, tagged->orq_next = NULL, tagged->orq_queue = NULL;
   tagged->orq_rprev = NULL, tagged->orq_rnext = NULL;
+#if HAVE_SOFIA_SRESOLV
+  tagged->orq_resolver = NULL;
+#endif
 
   if (tagged->orq_cc)
     nta_compartment_ref(tagged->orq_cc);
@@ -9784,8 +9992,8 @@
 
 /**PRACK a provisional response.
  *
- * The function nta_outgoing_prack() creates and sends a PRACK request used
- * to acknowledge a provisional response. 
+ * Create and send a PRACK request used to acknowledge a provisional
+ * response.
  *
  * The request is sent using the route of the original request @a oorq.
  *
@@ -9801,9 +10009,8 @@
  * @param tag,value,... optional
  *
  * @return
- * If successful, the function nta_outgoing_prack() returns a pointer
- * to newly created client transaction object for PRACK request, NULL
- * otherwise.
+ * If successful, return a pointer to newly created client transaction
+ * object for PRACK request, NULL otherwise.
  *
  * @sa
  * nta_outgoing_tcreate(), nta_outgoing_tcancel(), nta_outgoing_destroy().
@@ -10147,8 +10354,12 @@
 
   assert(orq); (void)tp;
 
+#if HAVE_SOFIA_STUN
   return tport_keepalive(orq->orq_tport, msg_addrinfo(orq->orq_request),
 			 TAG_END());
+#else
+  return -1;
+#endif
 }
 
 /** Close all transports. @since Experimental in @VERSION_1_12_2. */
@@ -10168,7 +10379,7 @@
       
       orq->orq_pending = 0;
       tport_unref(orq->orq_tport), orq->orq_tport = NULL;
-    }  
+    }
   
   
   for (i = iht->iht_size; i-- > 0;)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_check.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_check.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_check.c	Tue Mar 20 23:37:15 2007
@@ -343,8 +343,8 @@
 /**Check @SessionExpires header.
  *
  * If the proposed session-expiration time is smaller than @MinSE or our
- * minimal session expiration time, respond with 422 containing our minimal
- * session expiration time in @MinSE header.
+ * minimal session expiration time, respond with 422 containing shortest
+ * acceptable session expiration time in @MinSE header.
  *
  * @param irq 	incoming transaction object (may be NULL).
  * @param sip 	contents of the SIP message
@@ -359,26 +359,28 @@
 			      sip_time_t my_min_se,
 			      tag_type_t tag, tag_value_t value, ...)
 {
-  if ((sip->sip_min_se &&
-       sip->sip_session_expires->x_delta < sip->sip_min_se->min_delta)
-      || sip->sip_session_expires->x_delta < my_min_se) {
-    ta_list ta;
+  unsigned long min_se = my_min_se;
+
+  if (sip->sip_min_se && min_se < sip->sip_min_se->min_delta)
+    min_se = sip->sip_min_se->min_delta;
 
-    sip_min_se_t min_se[1];
+  if (sip->sip_session_expires->x_delta >= min_se)
+    return 0;
+
+  if (irq) {
+    ta_list ta;
+    sip_min_se_t min_se0[1];
 
-    sip_min_se_init(min_se)->min_delta = my_min_se;
+    ta_start(ta, tag, value);
 
-    if (irq) {
-      ta_start(ta, tag, value);
-      nta_incoming_treply(irq,
-			  SIP_422_SESSION_TIMER_TOO_SMALL,
-			  SIPTAG_MIN_SE(min_se),
-			  ta_tags(ta));
-      ta_end(ta);
-    }
+    sip_min_se_init(min_se0)->min_delta = min_se;
 
-    return 422;
+    nta_incoming_treply(irq,
+			SIP_422_SESSION_TIMER_TOO_SMALL,
+			SIPTAG_MIN_SE(min_se0),
+			ta_tags(ta));
+    ta_end(ta);
   }
 
-  return 0;
+  return 422;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_internal.h	Tue Mar 20 23:37:15 2007
@@ -98,16 +98,17 @@
   nta_agent_magic_t    *sa_magic;
   nta_message_f        *sa_callback;     
 
-  uint32_t              sa_nw_updates; /* Shall we enable network detector? */
-
   nta_update_magic_t   *sa_update_magic;
   nta_update_tport_f   *sa_update_tport;
 
-  su_time_t             sa_now;	/**< Timestamp in microsecond resolution. */
+  su_duration_t         sa_next; /**< Timestamp for next agent_timer. */
+  su_time_t             sa_now;	 /**< Timestamp in microsecond resolution. */
   uint32_t              sa_millisec; /**< Timestamp in milliseconds resolution. */
 
+  uint32_t              sa_nw_updates; /* Shall we enable network detector? */
+
   uint32_t              sa_flags;	/**< Message flags */
-  msg_mclass_t         *sa_mclass;
+  msg_mclass_t const   *sa_mclass;
 
   sip_contact_t        *sa_contact;
   sip_via_t            *sa_vias;   /**< @Via headers for all transports */
@@ -223,6 +224,9 @@
   /** If true, automatically create compartments */
   unsigned              sa_auto_comp:1;
 
+  /** Set when executing timer */
+  unsigned              sa_in_timer:1;
+
   unsigned              :0;
 
   /** Messages memory preload. */
@@ -342,6 +346,11 @@
   unsigned          leg_loose_route : 1; /**< Topmost route in set is LR */
 #endif
   unsigned          leg_local_is_to : 1; /**< Backwards-compatibility. */
+  unsigned          leg_tagged : 1; /**< Tagged after creation.
+				     *
+				     * Request missing To tag matches it
+				     * even after tagging.
+				     */
   unsigned:0;
   nta_request_f    *leg_callback;
   nta_leg_magic_t  *leg_magic;
@@ -463,12 +472,17 @@
 
   sip_method_t        	orq_method;
   char const           *orq_method_name;
+  url_t const          *orq_url;        /**< Original RequestURI */
+
   sip_from_t const     *orq_from;
   sip_to_t const       *orq_to;
+  char const           *orq_tag;        /**< Tag from final response. */
+
   sip_cseq_t const     *orq_cseq;
   sip_call_id_t const  *orq_call_id;
 
-  char const           *orq_tag;        /**< Tag from final response. */
+  msg_t		       *orq_request;
+  msg_t                *orq_response;
 
   su_time_t             orq_sent;       /**< When request was sent? */
   unsigned              orq_delay;      /**< RTT estimate */
@@ -501,11 +515,9 @@
   unsigned orq_sigcomp_new:1;	/**< Create compartment if needed */
   unsigned orq_sigcomp_zap:1;	/**< Reset SigComp after completing */
   unsigned orq_must_100rel : 1;
-  unsigned orq_timestamp : 1;	/**< insert @Timestamp header. */
+  unsigned orq_timestamp : 1;	/**< Insert @Timestamp header. */
   unsigned : 0;	/* pad */
 
-  uint32_t              orq_rseq;       /**< Latest incoming rseq */
-
 #if HAVE_SOFIA_SRESOLV
   sipdns_resolver_t    *orq_resolver;
 #endif
@@ -522,12 +534,10 @@
 
   char const           *orq_branch;	/**< Transaction branch */
   char const           *orq_via_branch;	/**< @Via branch */
-  url_t const          *orq_url;        /**< Original RequestURI */
-
-  msg_t		       *orq_request;
-  msg_t                *orq_response;
 
   nta_outgoing_t       *orq_cancel;     /**< CANCEL transaction */
+
+  uint32_t              orq_rseq;       /**< Latest incoming rseq */
 };
 
 /* Virtual function table for plugging in SigComp */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/nta_tag.c	Tue Mar 20 23:37:15 2007
@@ -110,38 +110,38 @@
 
 /* Status */
 
-tag_typedef_t ntatag_s_irq_hash =         UINTTAG_TYPEDEF(s_irq_hash);
-tag_typedef_t ntatag_s_orq_hash =         UINTTAG_TYPEDEF(s_orq_hash);
-tag_typedef_t ntatag_s_leg_hash =         UINTTAG_TYPEDEF(s_leg_hash);
-tag_typedef_t ntatag_s_irq_hash_used =    UINTTAG_TYPEDEF(s_irq_hash_used);
-tag_typedef_t ntatag_s_orq_hash_used =    UINTTAG_TYPEDEF(s_orq_hash_used);
-tag_typedef_t ntatag_s_leg_hash_used =    UINTTAG_TYPEDEF(s_leg_hash_used);
-tag_typedef_t ntatag_s_recv_msg =         UINTTAG_TYPEDEF(s_recv_msg);
-tag_typedef_t ntatag_s_recv_request =     UINTTAG_TYPEDEF(s_recv_request);
-tag_typedef_t ntatag_s_recv_response =    UINTTAG_TYPEDEF(s_recv_response);
-tag_typedef_t ntatag_s_bad_message =      UINTTAG_TYPEDEF(s_bad_message);
-tag_typedef_t ntatag_s_bad_request =      UINTTAG_TYPEDEF(s_bad_request);
-tag_typedef_t ntatag_s_bad_response =     UINTTAG_TYPEDEF(s_bad_response);
-tag_typedef_t ntatag_s_drop_request =     UINTTAG_TYPEDEF(s_drop_request);
-tag_typedef_t ntatag_s_drop_response =    UINTTAG_TYPEDEF(s_drop_response);
-tag_typedef_t ntatag_s_client_tr =        UINTTAG_TYPEDEF(s_client_tr);
-tag_typedef_t ntatag_s_server_tr =        UINTTAG_TYPEDEF(s_server_tr);
-tag_typedef_t ntatag_s_dialog_tr =        UINTTAG_TYPEDEF(s_dialog_tr);
-tag_typedef_t ntatag_s_acked_tr =         UINTTAG_TYPEDEF(s_acked_tr);
-tag_typedef_t ntatag_s_canceled_tr =      UINTTAG_TYPEDEF(s_canceled_tr);
-tag_typedef_t ntatag_s_trless_request =   UINTTAG_TYPEDEF(s_trless_request);
-tag_typedef_t ntatag_s_trless_to_tr =     UINTTAG_TYPEDEF(s_trless_to_tr);
-tag_typedef_t ntatag_s_trless_response =  UINTTAG_TYPEDEF(s_trless_response);
-tag_typedef_t ntatag_s_trless_200 =       UINTTAG_TYPEDEF(s_trless_200);
-tag_typedef_t ntatag_s_merged_request =   UINTTAG_TYPEDEF(s_merged_request);
-tag_typedef_t ntatag_s_sent_msg =      	  UINTTAG_TYPEDEF(s_sent_msg);
-tag_typedef_t ntatag_s_sent_request =  	  UINTTAG_TYPEDEF(s_sent_request);
-tag_typedef_t ntatag_s_sent_response = 	  UINTTAG_TYPEDEF(s_sent_response);
-tag_typedef_t ntatag_s_retry_request = 	  UINTTAG_TYPEDEF(s_retry_request);
-tag_typedef_t ntatag_s_retry_response =   UINTTAG_TYPEDEF(s_retry_response);
-tag_typedef_t ntatag_s_recv_retry =       UINTTAG_TYPEDEF(s_recv_retry);
-tag_typedef_t ntatag_s_tout_request =     UINTTAG_TYPEDEF(s_tout_request);
-tag_typedef_t ntatag_s_tout_response =    UINTTAG_TYPEDEF(s_tout_response);
+tag_typedef_t ntatag_s_irq_hash =         USIZETAG_TYPEDEF(s_irq_hash);
+tag_typedef_t ntatag_s_orq_hash =         USIZETAG_TYPEDEF(s_orq_hash);
+tag_typedef_t ntatag_s_leg_hash =         USIZETAG_TYPEDEF(s_leg_hash);
+tag_typedef_t ntatag_s_irq_hash_used =    USIZETAG_TYPEDEF(s_irq_hash_used);
+tag_typedef_t ntatag_s_orq_hash_used =    USIZETAG_TYPEDEF(s_orq_hash_used);
+tag_typedef_t ntatag_s_leg_hash_used =    USIZETAG_TYPEDEF(s_leg_hash_used);
+tag_typedef_t ntatag_s_recv_msg =         USIZETAG_TYPEDEF(s_recv_msg);
+tag_typedef_t ntatag_s_recv_request =     USIZETAG_TYPEDEF(s_recv_request);
+tag_typedef_t ntatag_s_recv_response =    USIZETAG_TYPEDEF(s_recv_response);
+tag_typedef_t ntatag_s_bad_message =      USIZETAG_TYPEDEF(s_bad_message);
+tag_typedef_t ntatag_s_bad_request =      USIZETAG_TYPEDEF(s_bad_request);
+tag_typedef_t ntatag_s_bad_response =     USIZETAG_TYPEDEF(s_bad_response);
+tag_typedef_t ntatag_s_drop_request =     USIZETAG_TYPEDEF(s_drop_request);
+tag_typedef_t ntatag_s_drop_response =    USIZETAG_TYPEDEF(s_drop_response);
+tag_typedef_t ntatag_s_client_tr =        USIZETAG_TYPEDEF(s_client_tr);
+tag_typedef_t ntatag_s_server_tr =        USIZETAG_TYPEDEF(s_server_tr);
+tag_typedef_t ntatag_s_dialog_tr =        USIZETAG_TYPEDEF(s_dialog_tr);
+tag_typedef_t ntatag_s_acked_tr =         USIZETAG_TYPEDEF(s_acked_tr);
+tag_typedef_t ntatag_s_canceled_tr =      USIZETAG_TYPEDEF(s_canceled_tr);
+tag_typedef_t ntatag_s_trless_request =   USIZETAG_TYPEDEF(s_trless_request);
+tag_typedef_t ntatag_s_trless_to_tr =     USIZETAG_TYPEDEF(s_trless_to_tr);
+tag_typedef_t ntatag_s_trless_response =  USIZETAG_TYPEDEF(s_trless_response);
+tag_typedef_t ntatag_s_trless_200 =       USIZETAG_TYPEDEF(s_trless_200);
+tag_typedef_t ntatag_s_merged_request =   USIZETAG_TYPEDEF(s_merged_request);
+tag_typedef_t ntatag_s_sent_msg =      	  USIZETAG_TYPEDEF(s_sent_msg);
+tag_typedef_t ntatag_s_sent_request =  	  USIZETAG_TYPEDEF(s_sent_request);
+tag_typedef_t ntatag_s_sent_response = 	  USIZETAG_TYPEDEF(s_sent_response);
+tag_typedef_t ntatag_s_retry_request = 	  USIZETAG_TYPEDEF(s_retry_request);
+tag_typedef_t ntatag_s_retry_response =   USIZETAG_TYPEDEF(s_retry_response);
+tag_typedef_t ntatag_s_recv_retry =       USIZETAG_TYPEDEF(s_recv_retry);
+tag_typedef_t ntatag_s_tout_request =     USIZETAG_TYPEDEF(s_tout_request);
+tag_typedef_t ntatag_s_tout_response =    USIZETAG_TYPEDEF(s_tout_response);
 
 /* Internal */
 tag_typedef_t ntatag_delay_sending = BOOLTAG_TYPEDEF(delay_sending);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta.h	Tue Mar 20 23:37:15 2007
@@ -300,6 +300,9 @@
 				   tag_type_t tag, tag_value_t value, ...);
 
 SOFIAPUBFUN
+msg_t *nta_incoming_create_response(nta_incoming_t *irq, int status, char const *phrase);
+
+SOFIAPUBFUN
 int nta_incoming_treply(nta_incoming_t *ireq, 
 			int status, char const *phrase, 
 			tag_type_t tag, tag_value_t value, ...);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/sofia-sip/nta_tag.h	Tue Mar 20 23:37:15 2007
@@ -59,10 +59,10 @@
 
 NTA_DLL extern tag_typedef_t ntatag_mclass;
 /** Message class used by NTA. @HI */
-#define NTATAG_MCLASS(x) ntatag_mclass, tag_ptr_v((x))
+#define NTATAG_MCLASS(x) ntatag_mclass, tag_cptr_v((x))
 
 NTA_DLL extern tag_typedef_t ntatag_mclass_ref;
-#define NTATAG_MCLASS_REF(x) ntatag_mclass_ref, tag_ptr_vr(&(x), (x))
+#define NTATAG_MCLASS_REF(x) ntatag_mclass_ref, tag_cptr_vr(&(x), (x))
 
 NTA_DLL extern tag_typedef_t ntatag_bad_req_mask;
 /** Mask for bad request messages. 
@@ -464,223 +464,223 @@
 /* Tags for statistics. */
 
 NTA_DLL extern tag_typedef_t ntatag_s_irq_hash;
-#define NTATAG_S_IRQ_HASH(x) ntatag_s_irq_hash, tag_uint_v(x)
+#define NTATAG_S_IRQ_HASH(x) ntatag_s_irq_hash, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_irq_hash_ref;
-#define NTATAG_S_IRQ_HASH_REF(x) ntatag_s_irq_hash_ref, tag_uint_vr(&(x))
+#define NTATAG_S_IRQ_HASH_REF(x) ntatag_s_irq_hash_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_orq_hash;
-#define NTATAG_S_ORQ_HASH(x) ntatag_s_orq_hash, tag_uint_v(x)
+#define NTATAG_S_ORQ_HASH(x) ntatag_s_orq_hash, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_orq_hash_ref;
-#define NTATAG_S_ORQ_HASH_REF(x) ntatag_s_orq_hash_ref, tag_uint_vr(&(x))
+#define NTATAG_S_ORQ_HASH_REF(x) ntatag_s_orq_hash_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_leg_hash;
-#define NTATAG_S_LEG_HASH(x) ntatag_s_leg_hash, tag_uint_v(x)
+#define NTATAG_S_LEG_HASH(x) ntatag_s_leg_hash, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_leg_hash_ref;
-#define NTATAG_S_LEG_HASH_REF(x) ntatag_s_leg_hash_ref, tag_uint_vr(&(x))
+#define NTATAG_S_LEG_HASH_REF(x) ntatag_s_leg_hash_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_irq_hash_used;
-#define NTATAG_S_IRQ_HASH_USED(x) ntatag_s_irq_hash_used, tag_uint_v(x)
+#define NTATAG_S_IRQ_HASH_USED(x) ntatag_s_irq_hash_used, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_irq_hash_used_ref;
 #define NTATAG_S_IRQ_HASH_USED_REF(x) \
-ntatag_s_irq_hash_used_ref, tag_uint_vr(&(x))
+ntatag_s_irq_hash_used_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_orq_hash_used;
-#define NTATAG_S_ORQ_HASH_USED(x) ntatag_s_orq_hash_used, tag_uint_v(x)
+#define NTATAG_S_ORQ_HASH_USED(x) ntatag_s_orq_hash_used, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_orq_hash_used_ref;
 #define NTATAG_S_ORQ_HASH_USED_REF(x) \
-ntatag_s_orq_hash_used_ref, tag_uint_vr(&(x))
+ntatag_s_orq_hash_used_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_leg_hash_used;
-#define NTATAG_S_LEG_HASH_USED(x) ntatag_s_leg_hash_used, tag_uint_v(x)
+#define NTATAG_S_LEG_HASH_USED(x) ntatag_s_leg_hash_used, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_leg_hash_used_ref;
 #define NTATAG_S_LEG_HASH_USED_REF(x) \
-ntatag_s_leg_hash_used_ref, tag_uint_vr(&(x))
+ntatag_s_leg_hash_used_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_msg;
-#define NTATAG_S_RECV_MSG(x) ntatag_s_recv_msg, tag_uint_v(x)
+#define NTATAG_S_RECV_MSG(x) ntatag_s_recv_msg, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_msg_ref;
-#define NTATAG_S_RECV_MSG_REF(x) ntatag_s_recv_msg_ref, tag_uint_vr(&(x))
+#define NTATAG_S_RECV_MSG_REF(x) ntatag_s_recv_msg_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_request;
-#define NTATAG_S_RECV_REQUEST(x) ntatag_s_recv_request, tag_uint_v(x)
+#define NTATAG_S_RECV_REQUEST(x) ntatag_s_recv_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_request_ref;
 #define NTATAG_S_RECV_REQUEST_REF(x)\
- ntatag_s_recv_request_ref, tag_uint_vr(&(x))
+ ntatag_s_recv_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_response;
-#define NTATAG_S_RECV_RESPONSE(x) ntatag_s_recv_response, tag_uint_v(x)
+#define NTATAG_S_RECV_RESPONSE(x) ntatag_s_recv_response, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_response_ref;
 #define NTATAG_S_RECV_RESPONSE_REF(x)\
- ntatag_s_recv_response_ref, tag_uint_vr(&(x))
+ ntatag_s_recv_response_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_bad_message;
-#define NTATAG_S_BAD_MESSAGE(x) ntatag_s_bad_message, tag_uint_v(x)
+#define NTATAG_S_BAD_MESSAGE(x) ntatag_s_bad_message, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_bad_message_ref;
 #define NTATAG_S_BAD_MESSAGE_REF(x)\
- ntatag_s_bad_message_ref, tag_uint_vr(&(x))
+ ntatag_s_bad_message_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_bad_request;
-#define NTATAG_S_BAD_REQUEST(x) ntatag_s_bad_request, tag_uint_v(x)
+#define NTATAG_S_BAD_REQUEST(x) ntatag_s_bad_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_bad_request_ref;
 #define NTATAG_S_BAD_REQUEST_REF(x)\
- ntatag_s_bad_request_ref, tag_uint_vr(&(x))
+ ntatag_s_bad_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_bad_response;
-#define NTATAG_S_BAD_RESPONSE(x) ntatag_s_bad_response, tag_uint_v(x)
+#define NTATAG_S_BAD_RESPONSE(x) ntatag_s_bad_response, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_bad_response_ref;
 #define NTATAG_S_BAD_RESPONSE_REF(x)\
- ntatag_s_bad_response_ref, tag_uint_vr(&(x))
+ ntatag_s_bad_response_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_drop_request;
-#define NTATAG_S_DROP_REQUEST(x) ntatag_s_drop_request, tag_uint_v(x)
+#define NTATAG_S_DROP_REQUEST(x) ntatag_s_drop_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_drop_request_ref;
 #define NTATAG_S_DROP_REQUEST_REF(x)\
- ntatag_s_drop_request_ref, tag_uint_vr(&(x))
+ ntatag_s_drop_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_drop_response;
-#define NTATAG_S_DROP_RESPONSE(x) ntatag_s_drop_response, tag_uint_v(x)
+#define NTATAG_S_DROP_RESPONSE(x) ntatag_s_drop_response, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_drop_response_ref;
 #define NTATAG_S_DROP_RESPONSE_REF(x)\
- ntatag_s_drop_response_ref, tag_uint_vr(&(x))
+ ntatag_s_drop_response_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_client_tr;
-#define NTATAG_S_CLIENT_TR(x) ntatag_s_client_tr, tag_uint_v(x)
+#define NTATAG_S_CLIENT_TR(x) ntatag_s_client_tr, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_client_tr_ref;
 #define NTATAG_S_CLIENT_TR_REF(x)\
- ntatag_s_client_tr_ref, tag_uint_vr(&(x))
+ ntatag_s_client_tr_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_server_tr;
-#define NTATAG_S_SERVER_TR(x) ntatag_s_server_tr, tag_uint_v(x)
+#define NTATAG_S_SERVER_TR(x) ntatag_s_server_tr, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_server_tr_ref;
 #define NTATAG_S_SERVER_TR_REF(x)\
- ntatag_s_server_tr_ref, tag_uint_vr(&(x))
+ ntatag_s_server_tr_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_dialog_tr;
-#define NTATAG_S_DIALOG_TR(x) ntatag_s_dialog_tr, tag_uint_v(x)
+#define NTATAG_S_DIALOG_TR(x) ntatag_s_dialog_tr, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_dialog_tr_ref;
 #define NTATAG_S_DIALOG_TR_REF(x)\
- ntatag_s_dialog_tr_ref, tag_uint_vr(&(x))
+ ntatag_s_dialog_tr_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_acked_tr;
-#define NTATAG_S_ACKED_TR(x) ntatag_s_acked_tr, tag_uint_v(x)
+#define NTATAG_S_ACKED_TR(x) ntatag_s_acked_tr, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_acked_tr_ref;
-#define NTATAG_S_ACKED_TR_REF(x) ntatag_s_acked_tr_ref, tag_uint_vr(&(x))
+#define NTATAG_S_ACKED_TR_REF(x) ntatag_s_acked_tr_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_canceled_tr;
-#define NTATAG_S_CANCELED_TR(x) ntatag_s_canceled_tr, tag_uint_v(x)
+#define NTATAG_S_CANCELED_TR(x) ntatag_s_canceled_tr, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_canceled_tr_ref;
 #define NTATAG_S_CANCELED_TR_REF(x)  \
- ntatag_s_canceled_tr_ref, tag_uint_vr(&(x))
+ ntatag_s_canceled_tr_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_request;
-#define NTATAG_S_TRLESS_REQUEST(x) ntatag_s_trless_request, tag_uint_v(x)
+#define NTATAG_S_TRLESS_REQUEST(x) ntatag_s_trless_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_request_ref;
 #define NTATAG_S_TRLESS_REQUEST_REF(x)\
- ntatag_s_trless_request_ref, tag_uint_vr(&(x))
+ ntatag_s_trless_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_to_tr;
-#define NTATAG_S_TRLESS_TO_TR(x) ntatag_s_trless_to_tr, tag_uint_v(x)
+#define NTATAG_S_TRLESS_TO_TR(x) ntatag_s_trless_to_tr, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_to_tr_ref;
 #define NTATAG_S_TRLESS_TO_TR_REF(x)\
- ntatag_s_trless_to_tr_ref, tag_uint_vr(&(x))
+ ntatag_s_trless_to_tr_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_response;
-#define NTATAG_S_TRLESS_RESPONSE(x) ntatag_s_trless_response, tag_uint_v(x)
+#define NTATAG_S_TRLESS_RESPONSE(x) ntatag_s_trless_response, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_response_ref;
 #define NTATAG_S_TRLESS_RESPONSE_REF(x)\
- ntatag_s_trless_response_ref, tag_uint_vr(&(x))
+ ntatag_s_trless_response_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_200;
-#define NTATAG_S_TRLESS_200(x) ntatag_s_trless_200, tag_uint_v(x)
+#define NTATAG_S_TRLESS_200(x) ntatag_s_trless_200, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_trless_200_ref;
 #define NTATAG_S_TRLESS_200_REF(x)\
- ntatag_s_trless_200_ref, tag_uint_vr(&(x))
+ ntatag_s_trless_200_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_merged_request;
-#define NTATAG_S_MERGED_REQUEST(x) ntatag_s_merged_request, tag_uint_v(x)
+#define NTATAG_S_MERGED_REQUEST(x) ntatag_s_merged_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_merged_request_ref;
 #define NTATAG_S_MERGED_REQUEST_REF(x)\
- ntatag_s_merged_request_ref, tag_uint_vr(&(x))
+ ntatag_s_merged_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_sent_msg;
-#define NTATAG_S_SENT_MSG(x) ntatag_s_sent_msg, tag_uint_v(x)
+#define NTATAG_S_SENT_MSG(x) ntatag_s_sent_msg, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_sent_msg_ref;
 #define NTATAG_S_SENT_MSG_REF(x)\
- ntatag_s_sent_msg_ref, tag_uint_vr(&(x))
+ ntatag_s_sent_msg_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_sent_request;
-#define NTATAG_S_SENT_REQUEST(x) ntatag_s_sent_request, tag_uint_v(x)
+#define NTATAG_S_SENT_REQUEST(x) ntatag_s_sent_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_sent_request_ref;
 #define NTATAG_S_SENT_REQUEST_REF(x)\
- ntatag_s_sent_request_ref, tag_uint_vr(&(x))
+ ntatag_s_sent_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_sent_response;
-#define NTATAG_S_SENT_RESPONSE(x) ntatag_s_sent_response, tag_uint_v(x)
+#define NTATAG_S_SENT_RESPONSE(x) ntatag_s_sent_response, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_sent_response_ref;
 #define NTATAG_S_SENT_RESPONSE_REF(x)\
- ntatag_s_sent_response_ref, tag_uint_vr(&(x))
+ ntatag_s_sent_response_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_retry_request;
-#define NTATAG_S_RETRY_REQUEST(x) ntatag_s_retry_request, tag_uint_v(x)
+#define NTATAG_S_RETRY_REQUEST(x) ntatag_s_retry_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_retry_request_ref;
 #define NTATAG_S_RETRY_REQUEST_REF(x)\
- ntatag_s_retry_request_ref, tag_uint_vr(&(x))
+ ntatag_s_retry_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_retry_response;
-#define NTATAG_S_RETRY_RESPONSE(x) ntatag_s_retry_response, tag_uint_v(x)
+#define NTATAG_S_RETRY_RESPONSE(x) ntatag_s_retry_response, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_retry_response_ref;
 #define NTATAG_S_RETRY_RESPONSE_REF(x)\
- ntatag_s_retry_response_ref, tag_uint_vr(&(x))
+ ntatag_s_retry_response_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_retry;
-#define NTATAG_S_RECV_RETRY(x) ntatag_s_recv_retry, tag_uint_v(x)
+#define NTATAG_S_RECV_RETRY(x) ntatag_s_recv_retry, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_recv_retry_ref;
 #define NTATAG_S_RECV_RETRY_REF(x)\
- ntatag_s_recv_retry_ref, tag_uint_vr(&(x))
+ ntatag_s_recv_retry_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_tout_request;
-#define NTATAG_S_TOUT_REQUEST(x) ntatag_s_tout_request, tag_uint_v(x)
+#define NTATAG_S_TOUT_REQUEST(x) ntatag_s_tout_request, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_tout_request_ref;
 #define NTATAG_S_TOUT_REQUEST_REF(x)\
- ntatag_s_tout_request_ref, tag_uint_vr(&(x))
+ ntatag_s_tout_request_ref, tag_usize_vr(&(x))
 
 NTA_DLL extern tag_typedef_t ntatag_s_tout_response;
-#define NTATAG_S_TOUT_RESPONSE(x) ntatag_s_tout_response, tag_uint_v(x)
+#define NTATAG_S_TOUT_RESPONSE(x) ntatag_s_tout_response, tag_usize_v(x)
 
 NTA_DLL extern tag_typedef_t ntatag_s_tout_response_ref;
 #define NTATAG_S_TOUT_RESPONSE_REF(x)\
- ntatag_s_tout_response_ref, tag_uint_vr(&(x))
+ ntatag_s_tout_response_ref, tag_usize_vr(&(x))
 
 SOFIA_END_DECLS
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta.c	Tue Mar 20 23:37:15 2007
@@ -654,7 +654,7 @@
 
   *contents = buffer;
 
-  return len;
+  return (int)len;
 }
 
 #if HAVE_DIRENT_H
@@ -713,7 +713,7 @@
   
   TEST(su_getaddrinfo(host, port, hints, &ai), 0); TEST_1(ai);
   s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); TEST_1(s != -1);
-  memset(su, 0, sulen = sizeof su); 
+  memset(su, 0, sulen = ai->ai_addrlen); 
   su->su_len = sizeof su; su->su_family = ai->ai_family;
   TEST_1(bind(s, &su->su_sa, sulen) == 0);
   TEST_1(getsockname(s, &su->su_sa, &sulen) == 0);
@@ -733,6 +733,7 @@
     strncpy(name + offset, d->d_name, PATH_MAX - offset);
     TEST_1(f = fopen(name, "rb"));
     TEST_1((blen = readfile(f, &buffer)) > 0);
+    fclose(f);
     r = buffer;
 
     if (strncmp(r, "JUNK ", 5) == 0) {
@@ -3357,6 +3358,7 @@
   "usage: %s OPTIONS\n"
   "where OPTIONS are\n"
   "   -v | --verbose    be verbose\n"
+  "   -a | --abort      abort() on error\n"
   "   -q | --quiet      be quiet\n"
   "   -1                quit on first error\n"
   "   -l level          set logging level (0 by default)\n"
@@ -3368,10 +3370,10 @@
 #endif
   ;
 
-void usage(void)
+void usage(int exitcode)
 {
   fprintf(stderr, nta_test_usage, name);
-  exit(1);
+  exit(exitcode);
 }
 
 int main(int argc, char *argv[])
@@ -3384,6 +3386,8 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--abort") == 0)
+      tstflags |= tst_abort;
     else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0)
       tstflags &= ~tst_verbatim;
     else if (strcmp(argv[i], "-1") == 0)
@@ -3400,7 +3404,7 @@
 	level = 3, rest = "";
 
       if (rest == NULL || *rest)
-	usage();
+	usage(1);
       
       su_log_set_level(nta_log, level);
       su_log_set_level(tport_log, level);
@@ -3411,7 +3415,7 @@
       else if (argv[i + 1])
 	ag->ag_obp = (url_string_t *)(argv[++i]);
       else
-	usage();
+	usage(1);
     }
     else if (strncmp(argv[i], "-m", 2) == 0) {
       if (argv[i][2])
@@ -3419,7 +3423,7 @@
       else if (argv[i + 1])
 	ag->ag_m = argv[++i];
       else
-	usage();
+	usage(1);
     }
     else if (strcmp(argv[i], "--attach") == 0) {
       o_attach = 1;
@@ -3434,7 +3438,7 @@
       break;
     }
     else
-      usage();
+      usage(1);
   }
 
   if (o_attach) {

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nta/test_nta_api.c	Tue Mar 20 23:37:15 2007
@@ -559,18 +559,18 @@
 
   nta_agent_t *nta;
 
-  uint32_t irq_hash = -1, orq_hash = -1, leg_hash = -1;
-  uint32_t recv_msg = -1, sent_msg = -1;
-  uint32_t recv_request = -1, recv_response = -1;
-  uint32_t bad_message = -1, bad_request = -1, bad_response = -1;
-  uint32_t drop_request = -1, drop_response = -1;
-  uint32_t client_tr = -1, server_tr = -1, dialog_tr = -1;
-  uint32_t acked_tr = -1, canceled_tr = -1;
-  uint32_t trless_request = -1, trless_to_tr = -1, trless_response = -1;
-  uint32_t trless_200 = -1, merged_request = -1;
-  uint32_t sent_request = -1, sent_response = -1;
-  uint32_t retry_request = -1, retry_response = -1, recv_retry = -1;
-  uint32_t tout_request = -1, tout_response = -1;
+  usize_t irq_hash = -1, orq_hash = -1, leg_hash = -1;
+  usize_t recv_msg = -1, sent_msg = -1;
+  usize_t recv_request = -1, recv_response = -1;
+  usize_t bad_message = -1, bad_request = -1, bad_response = -1;
+  usize_t drop_request = -1, drop_response = -1;
+  usize_t client_tr = -1, server_tr = -1, dialog_tr = -1;
+  usize_t acked_tr = -1, canceled_tr = -1;
+  usize_t trless_request = -1, trless_to_tr = -1, trless_response = -1;
+  usize_t trless_200 = -1, merged_request = -1;
+  usize_t sent_request = -1, sent_response = -1;
+  usize_t retry_request = -1, retry_response = -1, recv_retry = -1;
+  usize_t tout_request = -1, tout_response = -1;
 
   TEST_1(nta = nta_agent_create(ag->ag_root, (url_string_t *)"sip:*:*",
 				NULL, NULL, TAG_END()));
@@ -1238,6 +1238,7 @@
   "usage: %s OPTIONS\n"
   "where OPTIONS are\n"
   "   -v | --verbose    be verbose\n"
+  "   -a | --abort      abort() on error\n"
   "   -q | --quiet      be quiet\n"
   "   -1                quit on first error\n"
   "   -l level          set logging level (0 by default)\n"
@@ -1247,10 +1248,10 @@
 #endif
   ;
 
-void usage(void)
+void usage(int exitcode)
 {
   fprintf(stderr, nta_test_api_usage, name);
-  exit(1);
+  exit(exitcode);
 }
 
 int main(int argc, char *argv[])
@@ -1261,9 +1262,11 @@
   agent_t ag[1] = {{ { SU_HOME_INIT(ag) }, 0, NULL }};
 
   for (i = 1; argv[i]; i++) {
-    if (strcmp(argv[i], "-v") == 0)
+    if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0)
       tstflags |= tst_verbatim;
-    else if (strcmp(argv[i], "-q") == 0)
+    else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--abort") == 0)
+      tstflags |= tst_abort;
+    else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0)
       tstflags &= ~tst_verbatim;
     else if (strcmp(argv[i], "-1") == 0)
       quit_on_single_failure = 1;
@@ -1279,7 +1282,7 @@
 	level = 3, rest = "";
 
       if (rest == NULL || *rest)
-	usage();
+	usage(1);
       
       su_log_set_level(nta_log, level);
       su_log_set_level(tport_log, level);
@@ -1297,14 +1300,14 @@
       break;
     }
     else
-      usage();
+      usage(1);
   }
 
   if (o_attach) {
-    char line[10];
+    char *response, line[10];
     printf("nua_test: pid %lu\n", (unsigned long)getpid());
     printf("<Press RETURN to continue>\n");
-    fgets(line, sizeof line, stdin);
+    response = fgets(line, sizeof line, stdin);
   }
 #if HAVE_ALARM
   else if (o_alarm) {

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -63,4 +63,4 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/http-server.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/http-server.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/http-server.c	Tue Mar 20 23:37:15 2007
@@ -38,7 +38,10 @@
 #include <string.h>
 #include <stdio.h>
 #include <assert.h>
+
+#if HAVE_SIGNAL
 #include <signal.h>
+#endif
 
 typedef struct context_s context_t;
 #define NTH_SITE_MAGIC_T context_t
@@ -77,7 +80,10 @@
 		   http_t const *http,
 		   char const *path);
 su_msg_r server_intr_msg = SU_MSG_R_INIT;
+
+#if HAVE_SIGNAL
 static RETSIGTYPE server_intr_handler(int signum);
+#endif
 static void server_break(context_t *c, su_msg_r msg, su_msg_arg_t *arg);
 
 static msg_payload_t *read_payload(su_home_t *home, char const *fname);
@@ -140,13 +146,13 @@
 		su_root_task(context->c_root),
 		server_break, 0);
 
+#if HAVE_SIGNAL
   signal(SIGINT, server_intr_handler);
-
-#ifndef _WIN32
-  signal(SIGPIPE, server_intr_handler);
+#if HAVE_SIGQUIT
   signal(SIGQUIT, server_intr_handler);
   signal(SIGHUP, server_intr_handler);
 #endif
+#endif
 
   if (context->c_root) {
     context->c_site =

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_client.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_client.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_client.c	Tue Mar 20 23:37:15 2007
@@ -1094,7 +1094,7 @@
 
   assert(status >= 400);
 
-  SU_DEBUG_5(("nth: hc_reply(%p, %u, %s)\n", hc, status, phrase));
+  SU_DEBUG_5(("nth: hc_reply(%p, %u, %s)\n", (void *)hc, status, phrase));
 
   if (hc->hc_pending) {
     tport_release(hc->hc_tport, hc->hc_pending, hc->hc_request, NULL, hc,

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_server.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_server.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/nth_server.c	Tue Mar 20 23:37:15 2007
@@ -704,7 +704,7 @@
 			tag_type_t tag, tag_value_t value, ...)
 {
   server_t *srv;
-  msg_mclass_t *mclass = NULL;
+  msg_mclass_t const *mclass = NULL;
   tp_name_t tpn[1] = {{ NULL }};
   su_root_t *root = NULL;
   http_server_t const *server = NULL;

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/sofia-sip/nth_tag.h	Tue Mar 20 23:37:15 2007
@@ -109,7 +109,7 @@
 {return(tag_value_t)vp;}
 #else
 #define nthtag_template_v(v) ((tag_value_t)(v))
-#define nthtag_template_vr(vp) ((tag_value_t)&(vp))
+#define nthtag_template_vr(vp) ((tag_value_t)(vp))
 #endif
 
 NTH_DLL extern tag_typedef_t nthtag_template;
@@ -126,7 +126,7 @@
 { return(tag_value_t)vp; }
 #else
 #define nthtag_message_v(v) ((tag_value_t)(v))
-#define nthtag_message_vr(vp) ((tag_value_t)&(vp))
+#define nthtag_message_vr(vp) ((tag_value_t)(vp))
 #endif
 
 NTH_DLL extern tag_typedef_t nthtag_message;
@@ -142,7 +142,7 @@
 su_inline tag_value_t nthtag_authentication_vr(struct auth_client_s ***vp) {return(tag_value_t)vp;}
 #else
 #define nthtag_authentication_v(v) ((tag_value_t)(v))
-#define nthtag_authentication_vr(vp) ((tag_value_t)&(vp))
+#define nthtag_authentication_vr(vp) ((tag_value_t)(vp))
 #endif
 
 NTH_DLL extern tag_typedef_t nthtag_authentication;

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nth/test_nth.c	Tue Mar 20 23:37:15 2007
@@ -893,10 +893,10 @@
 }
 #endif
 
-void usage(void)
+void usage(int exitcode)
 {
-  fprintf(stderr, "usage: %s [-v|-q] [-p proxy-uri]\n", name);
-  exit(1);
+  fprintf(stderr, "usage: %s [-v|-q] [-a] [-p proxy-uri]\n", name);
+  exit(exitcode);
 }
 
 int main(int argc, char **argv)
@@ -915,6 +915,8 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else if (strcmp(argv[i], "-q") == 0)
       tstflags &= ~tst_verbatim;
     else if (strcmp(argv[i], "-p") == 0 && argv[i + 1])
@@ -931,7 +933,7 @@
       break;
     }
     else
-      usage();
+      usage(1);
   }
 
   t->t_srcdir = srcdir;

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -80,6 +80,7 @@
 			test_init.c \
 			test_nua_api.c test_nua_params.c \
 			test_register.c test_basic_call.c \
+			test_offer_answer.c \
 			test_call_reject.c test_cancel_bye.c \
 			test_call_hold.c test_session_timer.c \
 			test_refer.c test_100rel.c \
@@ -96,6 +97,6 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
 
 TAG_DLL_FLAGS =		LIST=nua_tag_list

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.c	Tue Mar 20 23:37:15 2007
@@ -67,7 +67,7 @@
  *
  * The NUA_DEBUG environment variable is used to determine the debug logging
  * level for @nua module. The default level is 3.
- * 
+ *
  * @sa <sofia-sip/su_debug.h>, nua_log, SOFIA_DEBUG
  */
 extern char const NUA_DEBUG[];
@@ -76,8 +76,8 @@
 #define SU_DEBUG 3
 #endif
 
-/**Debug log for @nua module. 
- * 
+/**Debug log for @nua module.
+ *
  * The nua_log is the log object used by @nua module. The level of
  * #nua_log is set using #NUA_DEBUG environment variable.
  */
@@ -104,16 +104,17 @@
  *     NUTAG_UICC()             \n
  *     NUTAG_CERTIFICATE_DIR()  \n
  *     and all tags listed in nua_set_params(), \n
- *     and all relevant NTATAG_* are passed to NTA.
+ *     and all relevant NTATAG_* are passed to NTA \n
+ *     and all tport tags listed in <sofia-sip/tport_tag.h>
  *
  * @note
- * From the @VERSION_1_12_2 all the nua_set_params() tags are processed. 
+ * From the @VERSION_1_12_2 all the nua_set_params() tags are processed.
  * Previously all nutags except NUTAG_SOA_NAME() and NUTAG_MEDIA_ENABLE()
  * were ignored.
  *
  * @note
  * Both the NUTAG_URL() and NUTAG_SIPS_URL() are used to pass arguments to
- * nta_agent_add_tport(). 
+ * nta_agent_add_tport().
  *
  * @par Events:
  *     none
@@ -187,9 +188,9 @@
 
 /** Destroy the @nua stack.
  *
- * Before calling nua_destroy() the application 
+ * Before calling nua_destroy() the application
  * should call nua_shutdown and wait for successful #nua_r_shutdown event.
- * Shuts down and destroys the @nua stack. Ongoing calls, registrations, 
+ * Shuts down and destroys the @nua stack. Ongoing calls, registrations,
  * and subscriptions are left as they are.
  *
  * @param nua         Pointer to @nua stack object
@@ -211,7 +212,8 @@
 
   if (nua) {
     if (!nua->nua_shutdown_final) {
-      SU_DEBUG_0(("nua_destroy(%p): FATAL: nua_shutdown not completed\n", nua));
+      SU_DEBUG_0(("nua_destroy(%p): FATAL: nua_shutdown not completed\n",
+		  (void *)nua));
       assert(nua->nua_shutdown);
       return;
     }
@@ -242,7 +244,7 @@
 
 /** Obtain default operation handle of the @nua stack object.
  *
- * A default operation can be used for operations where the 
+ * A default operation can be used for operations where the
  * ultimate result is not important or can be discarded.
  *
  * @param nua         Pointer to @nua stack object
@@ -262,7 +264,7 @@
   return nua ? nua->nua_handles : NULL;
 }
 
-/** Create an operation handle 
+/** Create an operation handle
  *
  * Allocates a new operation handle and associated storage.
  *
@@ -275,7 +277,7 @@
  *
  * @par Related tags:
  *     Duplicates the provided tags for use with every operation. Note that
- *     NUTAG_URL() is converted to SIPTAG_TO() if there is no SIPTAG_TO(). 
+ *     NUTAG_URL() is converted to SIPTAG_TO() if there is no SIPTAG_TO().
  *     And also vice versa, request-URI is taken from SIPTAG_TO() if there
  *     is no NUTAG_URL(). Note that certain SIP headers cannot be saved with
  *     the handle. They include @ContentLength, @CSeq, @RSeq, @RAck, and
@@ -302,7 +304,7 @@
     ta_start(ta, tag, value);
 
     nh = nh_create_handle(nua, hmagic, ta_args(ta));
-    
+
     if (nh)
       nh->nh_ref_by_user = 1;
 
@@ -312,7 +314,7 @@
   return nh;
 }
 
-/** Bind a callback context to an operation handle. 
+/** Bind a callback context to an operation handle.
  *
  * @param nh          Pointer to operation handle
  * @param hmagic      Pointer to callback context
@@ -334,7 +336,7 @@
     nh->nh_magic = hmagic;
 }
 
-/** Fetch a callback context from an operation handle. 
+/** Fetch a callback context from an operation handle.
  *
  * @param nh          Pointer to operation handle
  *
@@ -356,7 +358,7 @@
 
   if (NH_IS_VALID(nh))
     magic = nh->nh_magic;
-  
+
   return magic;
 }
 
@@ -369,8 +371,8 @@
  *
  * @param nh          Pointer to operation handle
  *
- * @retval 0 no invite in operation or operation handle is invalid 
- * @retval 1 operation has invite 
+ * @retval 0 no invite in operation or operation handle is invalid
+ * @retval 1 operation has invite
  *
  * @par Related tags:
  *     none
@@ -383,15 +385,15 @@
   return nh ? nh->nh_has_invite : 0;
 }
 
-/**Check if operation handle has active event subscriptions. 
+/**Check if operation handle has active event subscriptions.
  *
  * Active subscription can be established either by nua_subscribe() or
  * nua_refer() calls.
  *
  * @param nh          Pointer to operation handle
  *
- * @retval 0    no event subscriptions in operation or 
- *              operation handle is invalid 
+ * @retval 0    no event subscriptions in operation or
+ *              operation handle is invalid
  * @retval !=0  operation has event subscriptions
  *
  * @par Related tags:
@@ -416,7 +418,7 @@
  *
  * @param nh          Pointer to operation handle
  *
- * @retval 0 no active registration in operation or 
+ * @retval 0 no active registration in operation or
  *           operation handle is invalid
  * @retval 1 operation has registration
  *
@@ -433,12 +435,12 @@
   return nh && nh->nh_ds->ds_has_register;
 }
 
-/** Check if operation handle has been used with outgoing SUBSCRIBE of REFER request. 
+/** Check if operation handle has been used with outgoing SUBSCRIBE of REFER request.
  *
  * @param nh          Pointer to operation handle
  *
- * @retval 0 no active subscription in operation or 
- *           operation handle is invalid 
+ * @retval 0 no active subscription in operation or
+ *           operation handle is invalid
  * @retval 1 operation has subscription.
  *
  * @par Related tags:
@@ -470,7 +472,7 @@
   return nh ? nh->nh_has_register : 0;
 }
 
-/** Check if operation handle has an active call 
+/** Check if operation handle has an active call
  *
  * @param nh          Pointer to operation handle
  *
@@ -488,17 +490,17 @@
   return nh ? nh->nh_active_call : 0;
 }
 
-/** Check if operation handle has a call on hold 
+/** Check if operation handle has a call on hold
  *
- * Please note that this status is not affected by remote end putting 
- * this end on hold. Remote end can put each media separately on hold 
- * and status is reflected on SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO() 
+ * Please note that this status is not affected by remote end putting
+ * this end on hold. Remote end can put each media separately on hold
+ * and status is reflected on SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO()
  * and SOATAG_ACTIVE_CHAT() tag values in #nua_i_state event.
  *
  * @param nh          Pointer to operation handle
  *
- * @retval 0  if no call on hold in operation or operation handle is invalid 
- * @retval 1  if operation has call on hold, for example nua_invite() or 
+ * @retval 0  if no call on hold in operation or operation handle is invalid
+ * @retval 1  if operation has call on hold, for example nua_invite() or
  *            nua_update() has been called with SOATAG_HOLD() with non-NULL
  *            argument.
  *
@@ -515,14 +517,14 @@
 
 /** Get the remote address (From/To header) of operation handle
  *
- * Remote address is used as To header in outgoing operations and 
+ * Remote address is used as To header in outgoing operations and
  * derived from From: header in incoming operations.
  *
  * @param nh          Pointer to operation handle
  *
  * @retval NULL   no remote address for operation or operation handle invalid
  * @retval !=NULL pointer to remote address for operation
- *     
+ *
  * @par Related tags:
  *     none
  *
@@ -536,14 +538,14 @@
 
 /** Get the local address (From/To header) of operation handle
  *
- * Local address is used as From header in outgoing operations and 
+ * Local address is used as From header in outgoing operations and
  * derived from To: header in incoming operations.
  *
  * @param nh          Pointer to operation handle
  *
  * @retval NULL   no local address for operation or operation handle invalid
  * @retval !=NULL pointer to local address for operation
- *     
+ *
  * @par Related tags:
  *     none
  *
@@ -590,7 +592,7 @@
     ta_end(ta); \
   } \
   else { \
-    SU_DEBUG_1(("nua: " #event " with invalid handle %p\n", nh));	\
+    SU_DEBUG_1(("nua: " #event " with invalid handle %p\n", (void *)nh)); \
   }
 
 /* Documented with nua_stack_set_params() */
@@ -658,18 +660,18 @@
   NUA_SIGNAL(nh, nua_r_method, tag, value);
 }
 
-/** Send a chat message. 
+/** Send a chat message.
  *
- * A chat channel can be established during call setup using "message" media. 
- * An active chat channel is indicated using #nua_i_state event containing 
- * SOATAG_ACTIVE_CHAT() tag. Chat messages can be sent using this channel with 
- * nua_chat() function. Currently this is implemented using SIP MESSAGE 
+ * A chat channel can be established during call setup using "message" media.
+ * An active chat channel is indicated using #nua_i_state event containing
+ * SOATAG_ACTIVE_CHAT() tag. Chat messages can be sent using this channel with
+ * nua_chat() function. Currently this is implemented using SIP MESSAGE
  * requests but in future MSRP (message session protocol) will replace it.
 *
  * @param nh              Pointer to operation handle
  * @param tag, value, ... List of tagged parameters
  *
- * @return 
+ * @return
  *    nothing
  *
  * @par Related Tags:
@@ -707,18 +709,18 @@
 
 /* nua_r_notify is documented with process_response_to_notify() */
 
-/** Create an event server. 
+/** Create an event server.
  *
- * This function create an event server taking care of sending NOTIFY 
- * requests and responding to further SUBSCRIBE requests. The event 
- * server can accept multiple subscriptions from several sources and 
- * takes care for distributing the notifications. Unlike other functions 
+ * This function create an event server taking care of sending NOTIFY
+ * requests and responding to further SUBSCRIBE requests. The event
+ * server can accept multiple subscriptions from several sources and
+ * takes care for distributing the notifications. Unlike other functions
  * this call only accepts the SIP tags listed below.
  *
  * @param nh              Pointer to operation handle
  * @param tag, value, ... List of tagged parameters
  *
- * @return 
+ * @return
  *    nothing
  *
  * @par Related Tags:
@@ -736,7 +738,7 @@
   NUA_SIGNAL(nh, nua_r_notifier, tag, value);
 }
 
-/** Terminate an event server. 
+/** Terminate an event server.
  *
  * Terminate an event server with matching event and content type. The event
  * server was created earlier with nua_notifier() function.
@@ -744,7 +746,7 @@
  * @param nh              Pointer to operation handle
  * @param tag, value, ... List of tagged parameters
  *
- * @return 
+ * @return
  *    nothing
  *
  * @par Related Tags:
@@ -809,7 +811,7 @@
  * @param nh              Pointer to operation handle
  * @param tag, value, ... List of tagged parameters
  *
- * @return 
+ * @return
  *    nothing
  *
  * @par Related Tags:
@@ -827,7 +829,7 @@
  *
  * After creating a local presence server by nua_notifier(), an incoming
  * SUBSCRIBE request causes #nua_i_subscription event. Each subscriber is
- * identified with NEATAG_SUB() tag in the #nua_i_subscription event. 
+ * identified with NEATAG_SUB() tag in the #nua_i_subscription event.
  * Application can either authorize the subscriber with
  * NUTAG_SUBSTATE(#nua_substate_active) or terminate the subscription with
  * NUTAG_SUBSTATE(#nua_substate_terminated).
@@ -835,7 +837,7 @@
  * @param nh              Pointer to operation handle
  * @param tag, value, ... List of tagged parameters
  *
- * @return 
+ * @return
  *    nothing
  *
  * @par Related Tags:
@@ -875,11 +877,11 @@
     ta_end(ta);
   }
   else {
-    SU_DEBUG_1(("nua: respond with invalid handle %p\n", nh));
+    SU_DEBUG_1(("nua: respond with invalid handle %p\n", (void *)nh));
   }
 }
 
-/** Destroy a handle 
+/** Destroy a handle
  *
  * Terminate the protocol state associated with an operation handle. The
  * stack discards resources and terminates the ongoing dialog usage,
@@ -893,7 +895,7 @@
  *
  * @param nh              Pointer to operation handle
  *
- * @return 
+ * @return
  *    nothing
  *
  * @par Related Tags:
@@ -954,11 +956,14 @@
     e->e_status = status;
     e->e_phrase = phrase;
 
-    if (su_msg_send(sumsg) != 0)
+    SU_DEBUG_7(("nua(%p): signal %s\n", (void *)nh,
+		nua_event_name(event) + 4));
+
+    if (su_msg_send(sumsg) != 0 && event != nua_r_destroy)
       nua_handle_unref(nh);
-  } 
+  }
   else {
-    /* XXX  - we should return error code to application */
+    /* XXX  - we should return error code to application but we just abort() */
     assert(ENOMEM == 0);
   }
 
@@ -981,8 +986,16 @@
   }
 
   if (!nh || !nh->nh_valid) {	/* Handle has been destroyed */
+    if (nua_log->log_level >= 7) {
+      char const *name = nua_event_name(e->e_event) + 4;
+      SU_DEBUG_7(("nua(%p): event %s dropped\n", (void *)nh, name));
+    }
     if (nh && !NH_IS_DEFAULT(nh) && nua_handle_unref(nh)) {
-      SU_DEBUG_9(("nua(%p): freed by application\n", nh));
+#if HAVE_NUA_HANDLE_DEBUG
+      SU_DEBUG_0(("nua(%p): freed by application\n", (void *)nh));
+#else
+      SU_DEBUG_9(("nua(%p): freed by application\n", (void *)nh));
+#endif
     }
     if (e->e_msg)
       msg_destroy(e->e_msg), e->e_msg = NULL;
@@ -1011,7 +1024,11 @@
 		    e->e_tags);
 
   if (nh && !NH_IS_DEFAULT(nh) && nua_handle_unref(nh)) {
-    SU_DEBUG_9(("nua(%p): freed by application\n", nh));
+#if HAVE_NUA_HANDLE_DEBUG
+    SU_DEBUG_0(("nua(%p): freed by application\n", (void *)nh));
+#else
+    SU_DEBUG_9(("nua(%p): freed by application\n", (void *)nh));
+#endif
   }
 
   if (!su_msg_is_non_null(nua->nua_current))
@@ -1066,7 +1083,7 @@
       msg_destroy(e->e_msg), e->e_msg = NULL;
 
     if (nh && !NH_IS_DEFAULT(nh) && nua_handle_unref(nh)) {
-      SU_DEBUG_9(("nua(%p): freed by application\n", nh));
+      SU_DEBUG_9(("nua(%p): freed by application\n", (void *)nh));
     }
 
     su_msg_destroy(saved);
@@ -1094,20 +1111,35 @@
 
 /**Generate a @Replaces header for handle.
  *
+ * A @Replaces header contains the @CallID value, @From and @To tags
+ * corresponding to SIP dialog associated with handle @a nh. Note that the
+ * @Replaces matches with dialog of the remote peer,
+ * nua_handle_by_replaces() does not return same handle (unless you swap
+ * rp_from_tag and rp_to_tag in @Replaces header).
+ *
+ * A @Replaces header is used in attended transfer, among other things.
+ *
+ * @param nh pointer to operation handle
+ * @param home memory home used to allocate the header
+ * @param early_only if true, include "early-only" parameter in @Replaces, too
+ *
+ * @return A newly created @Replaces header.
+ *
  * @since New in @VERSION_1_12_4.
  *
- * @sa nua_handle_by_replaces(), @Replaces, @RFC3891, nua_refer(),
- * #nua_i_refer, @ReferTo, nta_leg_make_replaces()
+ * @sa nua_handle_by_replaces(), @Replaces, @RFC3891, @RFC3515, nua_refer(),
+ * #nua_i_refer(), @ReferTo, nta_leg_make_replaces(),
+ * sip_headers_as_url_query()
  */
-sip_replaces_t *nua_handle_make_replaces(nua_handle_t *nh, 
+sip_replaces_t *nua_handle_make_replaces(nua_handle_t *nh,
 					 su_home_t *home,
 					 int early_only)
 {
   if (nh && nh->nh_valid && nh->nh_nua) {
     struct nua_stack_handle_make_replaces_args a = { NULL, nh, home, early_only };
 
-    if (su_task_execute(nh->nh_nua->nua_server, 
-			nua_stack_handle_make_replaces_call, (void *)&a, 
+    if (su_task_execute(nh->nh_nua->nua_server,
+			nua_stack_handle_make_replaces_call, (void *)&a,
 			NULL) == 0) {
       return a.retval;
     }
@@ -1134,9 +1166,9 @@
  *
  * @since New in @VERSION_1_12_4.
  *
- * @note 
+ * @note
  * You should release the reference with nua_handle_unref() when you are
- * done with handle.
+ * done with the handle.
  *
  * @sa nua_handle_make_replaces(), @Replaces, @RFC3891, nua_refer(),
  * #nua_i_refer, @ReferTo, nta_leg_by_replaces()
@@ -1146,8 +1178,8 @@
   if (nua) {
     struct nua_stack_handle_by_replaces_args a = { NULL, nua, r };
 
-    if (su_task_execute(nua->nua_server, 
-			nua_stack_handle_by_replaces_call, (void *)&a, 
+    if (su_task_execute(nua->nua_server,
+			nua_stack_handle_by_replaces_call, (void *)&a,
 			NULL) == 0) {
       nua_handle_t *nh = a.retval;
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua.docs	Tue Mar 20 23:37:15 2007
@@ -1124,7 +1124,7 @@
     <td>terminating</td>
     <td>Process answer</td>
     <td>
-   If there was an failure in SDP negotiation or other failure with media,
+   If there was a failure in SDP negotiation or other failure with media,
    the stack will automatically terminate the call. The BYE follows
    immediatelhy after the ACK.
 </td></tr>
@@ -1262,7 +1262,7 @@
      |    +---------------|            |    :         |     :
      |    |               +------------+    :         |     :
      |    |                           |     :         |     :
-     |    |          nua_respond/18X (2)    :         |     :
+     |    |          nua_respond/18X (2a)   :         |     :
      |    |                           |     :         |     :
      |    |                           V     V         |     :
      |    |                          +------------+   |     :
@@ -2029,7 +2029,8 @@
  *               Always present
  * @param nh     Pointer to operation handle.
  * @param hmagic Pointer to callback context from nua_handle().
- * @param sip    Parsed incoming message. May be NULL.
+ * @param sip    Headers in parsed incoming message. May be NULL.
+ *               See also nua_current_request().
  * @param tags   Tag list containing more information about the state of NUA.
  *               May be empty.
  *
@@ -2038,17 +2039,22 @@
  * individual event.
  *
  * The events can be divided into the following categories: \n
- * @par Indications:
+ * @par Status or Error Indications:
  * #nua_i_active           \n
+ * #nua_i_error            \n
+ * #nua_i_fork             \n
+ * #nua_i_media_error      \n
+ * #nua_i_subscription     \n
+ * #nua_i_state            \n
+ * #nua_i_terminated
+ *
+ * @par SIP requests:
  * #nua_i_ack              \n
  * #nua_i_bye              \n
  * #nua_i_cancel           \n
  * #nua_i_chat             \n
- * #nua_i_error            \n
- * #nua_i_fork             \n
  * #nua_i_info             \n
  * #nua_i_invite           \n
- * #nua_i_media_error      \n
  * #nua_i_message          \n
  * #nua_i_method           \n
  * #nua_i_notify           \n
@@ -2058,9 +2064,6 @@
  * #nua_i_refer            \n
  * #nua_i_register         \n
  * #nua_i_subscribe        \n
- * #nua_i_subscription     \n
- * #nua_i_state            \n
- * #nua_i_terminated       \n
  * #nua_i_update
  *
  * @par Responses:
@@ -2122,7 +2125,8 @@
  *
  * Outgoing call has been forked.
  *
- * This is sent when an INVITE request is answered with multiple 200 responses.
+ * This is sent when an INVITE request is answered with multiple 2XX series
+ * responses.
  *
  * @param status response status code
  * @param phrase a short textual description of @a status code
@@ -2219,7 +2223,7 @@
  * @param nh     operation handle associated with the notifier
  * @param hmagic operation magic associated with the notifier
  * @param sip    response to MESSAGE request or NULL upon an error
- *               (error code and message are in status an phrase parameters)
+ *               (error code and message are in status and phrase parameters)
  * @param tags   empty
  *
  * @sa nua_chat(), #nua_r_message

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_common.c	Tue Mar 20 23:37:15 2007
@@ -110,13 +110,14 @@
   assert(nua->nua_home);
 
   if ((nh = su_home_clone(nua->nua_home, sizeof(*nh)))) {
-    nh->nh_valid = nua_handle;
+    nh->nh_valid = nua_valid_handle_cookie;
     nh->nh_nua = nua;
     nh->nh_magic = hmagic;
     nh->nh_prefs = nua->nua_dhandle->nh_prefs;
 
     if (nua_handle_save_tags(nh, tags) < 0) {
-      SU_DEBUG_5(("nua(%p): creating handle %p failed\n", nua, nh));
+      SU_DEBUG_5(("nua(%p): creating handle %p failed\n",
+		  (void *)nua, (void *)nh));
       su_home_unref(nh->nh_home), nh = NULL;
     }
     
@@ -136,7 +137,7 @@
       } 
       else {
 	_handle_lifetime = 2;
-	SU_DEBUG_0(("nh_handle_create(%p)\n", nh));
+	SU_DEBUG_0(("nh_handle_create(%p)\n", (void *)nh));
 	su_home_destructor(nh->nh_home, nh_destructor);
       }
     }
@@ -159,10 +160,12 @@
 static void nh_destructor(void *arg)
 {
   nua_handle_t *nh = arg;
-
-  SU_DEBUG_0(("nh_destructor(%p)\n", nh));
+  SU_DEBUG_0(("nh_destructor(%p)\n", (void *)nh));
 }
 
+#undef nua_handle_ref
+#undef nua_handle_unref
+
 /** Make a new reference to handle.
  *
  * The handles use reference counting for memory management. In addition to
@@ -180,7 +183,6 @@
   return (nua_handle_t *)su_home_ref(nh->nh_home);
 }
 
-
 /** Destroy reference to handle. 
  *
  * The handles use reference counting for memory management. In addition to
@@ -301,3 +303,34 @@
   default: return "UNKNOWN";
   }
 }
+
+/** Return name of subscription state. @NEW_1_12_5. */
+char const *nua_substate_name(enum nua_substate substate)
+{
+  switch (substate) {
+  case nua_substate_embryonic:
+      /*FALLTHROUGH*/
+  case nua_substate_pending:
+    return "pending";
+  case nua_substate_terminated:
+    return "terminated";
+  case nua_substate_active:
+      /*FALLTHROUGH*/
+  default:
+    return "active";
+  }
+}
+
+/** Convert string to enum nua_substate. @NEW_1_12_5. */
+enum nua_substate nua_substate_make(char const *sip_substate)
+{
+  if (sip_substate == NULL)
+    return nua_substate_active;
+  else if (strcasecmp(sip_substate, "terminated") == 0)
+    return nua_substate_terminated;
+  else if (strcasecmp(sip_substate, "pending") == 0)
+    return nua_substate_pending;
+  else /* if (strcasecmp(sip_substate, "active") == 0) */ 
+    return nua_substate_active;
+}
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c	Tue Mar 20 23:37:15 2007
@@ -269,7 +269,7 @@
     du = *prev_du;
     if (du) {		/* Already exists */
       SU_DEBUG_5(("nua(%p): adding already existing %s usage%s%s\n",
-		  own, nua_dialog_usage_name(du), 
+		  (void *)own, nua_dialog_usage_name(du), 
 		  event ? "  with event " : "", event ? event->o_type : ""));
       
       if (prev_du != &ds->ds_usage) {
@@ -297,7 +297,7 @@
       }
 	
       SU_DEBUG_5(("nua(%p): adding %s usage%s%s\n",
-		  own, nua_dialog_usage_name(du), 
+		  (void *)own, nua_dialog_usage_name(du), 
 		  o ? " with event " : "", o ? o->o_type :""));
 
       su_home_ref(own);
@@ -345,6 +345,24 @@
     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);
+      if (!nua_client_is_queued(cr) &&
+	  !nua_client_is_reporting(cr))
+	nua_client_request_destroy(cr);
+    }
+
+    /* Clean references from queued client requests */
     for (cr = ds->ds_cr; cr; cr = cr_next) {
       cr_next = cr->cr_next;
       if (cr->cr_usage == du)
@@ -357,29 +375,19 @@
 	nua_server_request_destroy(sr);
     }
 
-    *at = du->du_next;
-
-    o = du->du_event;
-
-    SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
-		own, nua_dialog_usage_name(du), 
-		o ? " with event " : "", o ? o->o_type :""));
-    du->du_class->usage_remove(own, ds, du);
-    msg_destroy(du->du_msg), du->du_msg = NULL;
     su_home_unref(own);
     su_free(own, du);
   }
 
-  /* Zap dialog if there is no more usages */
-  if (ds->ds_usage == NULL) {
-    nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL;
-    su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL;
-    ds->ds_route = 0;
+  /* Zap dialog if there are no more usages */
+  if (ds->ds_terminating)
+    ;
+  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);
   }
 }
@@ -412,7 +420,7 @@
       }
     }
     
-    SU_DEBUG_3(("nua(%p): handle with %s%s%s\n", own,
+    SU_DEBUG_3(("nua(%p): handle with %s%s%s\n", (void *)own,
 		ds->ds_has_session ? "session and " : "", 
 		ds->ds_has_events ? "events " : "",
 		buffer));
@@ -423,34 +431,19 @@
 void nua_dialog_deinit(nua_owner_t *own,
 		       nua_dialog_state_t *ds)
 {
+  ds->ds_terminating = 1;
+
   while (ds->ds_usage) {
     nua_dialog_usage_remove_at(own, ds, &ds->ds_usage);
   }
-}
-
 
-/** @internal Dialog has been terminated. Remove all usages. */
-void nua_dialog_terminated(nua_owner_t *own,
-			   struct nua_dialog_state *ds,
-			   int status,
-			   char const *phrase)
-{
-
-  ds->ds_terminated = 1;
+  nua_dialog_remove(own, ds, NULL);
 
-  while (ds->ds_usage) {
-#if 0
-    int call = 0;
-
-    if (ds->ds_usage->du_kind == nua_session_usage)
-      call = 1;			/* Delay sending the event */
-    else
-      /* XXX */;
-#endif
-    nua_dialog_usage_remove_at(own, ds, &ds->ds_usage);
-  }
+  ds->ds_has_events = 0;
+  ds->ds_terminating = 0;
 }
 
+
 /**@internal
  * Set expiration time. 
  */
@@ -524,7 +517,8 @@
 /**@internal Do not refresh. */
 void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du)
 {
-  du->du_refresh = 0;
+  if (du)
+    du->du_refresh = 0;
 }
 
 /** @internal Refresh usage or shutdown usage if @a now is 0. */
@@ -537,18 +531,45 @@
     du->du_refresh = 0;
 
     if (now > 0) {
-      if (du->du_class->usage_refresh) {
-	du->du_class->usage_refresh(owner, ds, du, now);
-	return;
-      }
+      assert(du->du_class->usage_refresh);
+      du->du_class->usage_refresh(owner, ds, du, now);
     }
     else {
       du->du_shutdown = 1;
-      if (du->du_class->usage_shutdown) {
-	du->du_class->usage_shutdown(owner, ds, du);
-	return;
-      }
+      assert(du->du_class->usage_shutdown);
+      du->du_class->usage_shutdown(owner, ds, du);
     }
   }
 }
 
+/** Terminate all dialog usages gracefully. */
+int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
+{
+  nua_dialog_usage_t *du;
+
+  ds->ds_terminating = 1;
+
+  do {
+    for (du = ds->ds_usage; du; du = du->du_next) {
+      if (!du->du_shutdown) {
+	nua_dialog_usage_shutdown(owner, ds, du);
+	break;
+      }
+    }
+  } while (du);
+
+  return 1;
+}
+
+/** (Gracefully) terminate usage */
+void nua_dialog_usage_shutdown(nua_owner_t *owner,
+			       nua_dialog_state_t *ds,
+			       nua_dialog_usage_t *du)
+{
+  if (du) {
+    du->du_refresh = 0;
+    du->du_shutdown = 1;
+    assert(du->du_class->usage_shutdown);
+    du->du_class->usage_shutdown(owner, ds, du);
+  }
+}

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h	Tue Mar 20 23:37:15 2007
@@ -48,49 +48,92 @@
 #include <sofia-sip/nta.h>
 #endif
 
+typedef su_msg_r nua_saved_signal_t;
+
 typedef struct nua_server_request nua_server_request_t; 
 typedef struct nua_client_request nua_client_request_t; 
 
-/** Respond to an incoming request. */
-typedef int nua_server_respond_f(nua_server_request_t *, tagi_t const *);
+typedef struct {
+  sip_method_t sm_method; 
+  char const *sm_method_name;
+
+  int sm_event;
+
+  struct {
+    unsigned create_dialog:1, in_dialog:1, target_refresh:1, add_contact:1;
+    unsigned :0;
+  } sm_flags;
+
+  /** Initialize server-side request. */
+  int (*sm_init)(nua_server_request_t *sr);
+
+  /** Preprocess server-side request (after handle has been created). */
+  int (*sm_preprocess)(nua_server_request_t *sr);
+
+  /** Update server-side request parameters */
+  int (*sm_params)(nua_server_request_t *sr, tagi_t const *tags);
 
-/** Restart an outgoing request. */
-typedef void nua_creq_restart_f(nua_owner_t *, tagi_t *tags);
+  /** Respond to server-side request. */
+  int (*sm_respond)(nua_server_request_t *sr, tagi_t const *tags);
+
+  /** Report server-side request to application. */
+  int (*sm_report)(nua_server_request_t *sr, tagi_t const *tags);
+
+} nua_server_methods_t;
 
 /** Server side transaction */
 struct nua_server_request {
   struct nua_server_request *sr_next, **sr_prev;
 
+  nua_server_methods_t const *sr_methods;
+
   nua_owner_t *sr_owner;	/**< Backpointer to handle */
   nua_dialog_usage_t *sr_usage;	/**< Backpointer to usage */
 
-  /** When the application responds to an request with
-   * nua_respond(), the sr_respond() is called
-   */
-  nua_server_respond_f *sr_respond;
-  
   nta_incoming_t *sr_irq;	/**< Server transaction object */
-  msg_t *sr_msg;		/**< Request message */
+  
+  struct {
+    msg_t *msg;			/**< Request message */
+    sip_t const *sip;		/**< Headers in request message */
+  } sr_request;
+
+  struct {
+    msg_t *msg;			/**< Response message */
+    sip_t *sip;			/**< Headers in response message */
+  } sr_response;
 
   sip_method_t sr_method;	/**< Request method */
+
+  int sr_application;		/**< Status by application */
+
   int sr_status;		/**< Status code */
   char const *sr_phrase;	/**< Status phrase */
 
-  unsigned sr_auto:1;		/**< Autoresponse - no event has been sent */
+  unsigned sr_event:1;		/**< Reported to application */
   unsigned sr_initial:1;	/**< Handle was created by this request */
+  unsigned sr_add_contact:1;	/**< Add Contact header to the response */
+  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 100rel */
+  unsigned sr_100rel:1, sr_pracked:1;
 
   /* 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) */
 
-  unsigned sr_offer_sent:1;	/**< We have offered SDP */
+  unsigned sr_offer_sent:2;	/**< We have offered SDP (reliably, if >1) */
   unsigned sr_answer_recv:1;	/**< We have received SDP answer */
-};
+  unsigned :0;
 
-#define SR_INIT(sr)			     \
-  ((void)memset((sr), 0, sizeof (sr)[0]),    \
-   (void)(SR_STATUS1((sr), SIP_100_TRYING)), \
-   sr)
+  char const *sr_sdp;		/**< SDP received from client */
+  size_t sr_sdp_len;		/**< SDP length */
+
+  /**< Save 200 OK nua_respond() signal until PRACK has been received */
+  nua_saved_signal_t sr_signal;	
+};
 
 #define SR_STATUS(sr, status, phrase) \
   ((sr)->sr_phrase = (phrase), (sr)->sr_status = (status))
@@ -104,46 +147,192 @@
   return (void)(sr->sr_phrase = phrase), (sr->sr_status = status);
 }
 
+/** Methods for client request */
+typedef struct {
+  sip_method_t crm_method;
+  char const *crm_method_name;
+  size_t crm_extra;		/**< Size of private data */
+
+  struct {
+    unsigned create_dialog:1, in_dialog:1, target_refresh:1;
+    unsigned:0;
+  } crm_flags;
+
+  /** Generate a request message.
+   *
+   * @retval 1 when request message has been created
+   * @retval 0 when request message should be created in normal fashion
+   * @retval -1 upon an error
+   */
+  int (*crm_template)(nua_client_request_t *cr,
+		      msg_t **return_msg,
+		      tagi_t const *tags);
+
+  /**@a crm_init is called when a client request is sent first time. 
+   *
+   * @retval 1 when request has been responded
+   * @retval 0 when request should be sent in normal fashion
+   * @retval -1 upon an error
+   */
+  int (*crm_init)(nua_client_request_t *, msg_t *msg, sip_t *sip,
+		  tagi_t const *tags);
+
+  /** @a crm_send is called each time when a client request is sent.
+   *
+   * @retval 1 when request has been responded
+   * @retval 0 when request has been sent
+   * @retval -1 upon an error (request message has not been destroyed)
+   * @retval -2 upon an error (request message has been destroyed)
+   */
+  int (*crm_send)(nua_client_request_t *,
+		  msg_t *msg, sip_t *sip,
+		  tagi_t const *tags);
+
+  /** @a crm_check_restart is called each time when a response is received.
+   *
+   * It is used to restart reqquest after responses with method-specific
+   * status code or method-specific way of restarting the request.
+   *
+   * @retval 1 when request has been restarted
+   * @retval 0 when response should be processed normally
+   */
+  int (*crm_check_restart)(nua_client_request_t *,
+			   int status, char const *phrase,
+			   sip_t const *sip);
+
+  /** @a crm_recv is called each time a final response is received.
+   *
+   * A final response is in range 200 .. 699 (or internal response) and it
+   * cannot be restarted.
+   *
+   * crm_recv() should call nua_base_client_response() or
+   * nua_base_client_tresponse(). The return values below are documented with
+   * nua_base_client_response(), too.
+   *
+   * @retval 0 if response was preliminary
+   * @retval 1 if response was final
+   * @retval 2 if response destroyed the handle, too.
+   */
+  int (*crm_recv)(nua_client_request_t *,
+		  int status, char const *phrase,
+		  sip_t const *sip);
+
+  /** @a crm_preliminary is called each time a preliminary response is received.
+   *
+   * A preliminary response is in range 101 .. 199.
+   *
+   * crm_preliminary() should call nua_base_client_response() or
+   * nua_base_client_tresponse().
+   *
+   * @retval 0 if response was preliminary
+   * @retval 1 if response was final
+   * @retval 2 if response destroyed the handle, too.
+   */
+  int (*crm_preliminary)(nua_client_request_t *,
+			 int status, char const *phrase,
+			 sip_t const *sip);
+
+  /** @a crm_report is called each time a response is received and it is
+   * reported to the application.
+   *
+   * The status and phrase may be different from the status and phrase
+   * received from the network, e.g., when the request is restarted.
+   *
+   * @return The return value should be 0. It is currently ignored.
+   */
+  int (*crm_report)(nua_client_request_t *,
+		    int status, char const *phrase,
+		    sip_t const *sip,
+		    nta_outgoing_t *orq,
+		    tagi_t const *tags);
+
+  /** @a crm_deinit is called when a client-side request is destroyed.
+   *
+   * @return The return value should be 0. It is currently ignored.
+   */
+  int (*crm_deinit)(nua_client_request_t *);
+
+} nua_client_methods_t;
+
+/* Client-side request. Documented by nua_client_create() */
 struct nua_client_request
 {
-  nua_client_request_t *cr_next;        /**< Linked list of requests */
-  /*nua_event_t*/ int cr_event;		/**< Request event */
-  nua_creq_restart_f *cr_restart;
-  nta_outgoing_t     *cr_orq;
-  msg_t              *cr_msg;
+  nua_client_request_t *cr_next, **cr_prev; /**< Linked list of requests */
+  nua_owner_t        *cr_owner;
   nua_dialog_usage_t *cr_usage;
+
+  nua_saved_signal_t cr_signal;
+  tagi_t const      *cr_tags;
+
+  nua_client_methods_t const *cr_methods;
+
+  msg_t              *cr_msg;
+  sip_t              *cr_sip;
+
+  nta_outgoing_t     *cr_orq;
+
+  /*nua_event_t*/ int cr_event;		/**< Request event */
+  sip_method_t        cr_method;
+  char const         *cr_method_name;
+
+  url_t              *cr_target;
+
+  uint32_t            cr_seq;
+
+  unsigned short      cr_status;        /**< Latest status */
+
   unsigned short      cr_retry_count;   /**< Retry count for this request */
 
   /* Flags used with offer-answer */
   unsigned short      cr_answer_recv;   /**< Recv answer in response 
 					 *  with this status.
 					 */
-  unsigned            cr_offer_sent:1;  /**< Sent offer in this request */
+  unsigned cr_offer_sent:1;	/**< Sent offer in this request */
+
+  unsigned cr_offer_recv:1;	/**< Recv offer in a response */
+  unsigned cr_answer_sent:1;	/**< Sent answer in (PR)ACK */
 
-  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 */
 
-  unsigned            cr_has_contact:1; /**< Request has application contact */
+  /* Lifelong flags? */
+  unsigned cr_auto:1;		/**< Request was generated by stack */
+  unsigned cr_has_contact:1;	/**< Request has user Contact */
+  unsigned cr_contactize:1;	/**< Request needs Contact */
+
+  /* Current state */
+  unsigned cr_challenged:1;	/**< Request was challenged, pending auth */
+  unsigned cr_restarting:1;	/**< Request is being restarted */
+  unsigned cr_reporting:1;	/**< Reporting in progress */
+  unsigned cr_terminating:1;	/**< Request terminates the usage */
+  signed int cr_terminated:2;	/**< Response terminated usage (1) or 
+				    whole dialog (-1) */
+  unsigned cr_graceful:1;	/**< Graceful termination required */
 };
 
 
 struct nua_dialog_state
 {
-  nua_client_request_t ds_cr[1];
-  nua_server_request_t *ds_sr;
-
   /** Dialog usages. */
   nua_dialog_usage_t     *ds_usage;
 
+  /** Client requests */
+  nua_client_request_t   *ds_cr;
+  /** Server requests */
+  nua_server_request_t *ds_sr;
+
   /* Dialog and subscription state */
+  unsigned ds_reporting:1;	/**< We are reporting */
+
   unsigned ds_route:1;		/**< We have route */
-  unsigned ds_terminated:1;	/**< Being terminated */
+  unsigned ds_terminating:1;	/**< Being terminated */
 
   unsigned ds_has_session:1;	/**< We have session */
   unsigned ds_has_register:1;	/**< We have registration */
   unsigned ds_has_publish:1;	/**< We have publish */
 
-  unsigned ds_has_referrals:1;	/**< We have (or have had) referrals */
+  unsigned ds_got_session:1;	/**< We have (or have had) session */
+  unsigned ds_got_referrals:1;	/**< We have (or have had) referrals */
 
   unsigned :0;
 
@@ -196,8 +385,8 @@
 struct nua_dialog_usage {
   nua_dialog_usage_t *du_next;
   nua_usage_class const *du_class;
+  nua_client_request_t *du_cr;	        /**< Client request bound with usage */
 
-  unsigned     du_terminating:1;	/**< Now trying to terminate usage */
   unsigned     du_ready:1;	        /**< Established usage */
   unsigned     du_shutdown:1;	        /**< Shutdown in progress */
   unsigned:0;
@@ -212,7 +401,6 @@
 
   sip_event_t const *du_event;		/**< Event of usage */
 
-  msg_t *du_msg;			/**< Template message */
 };
 
 void nua_dialog_uac_route(nua_owner_t *, nua_dialog_state_t *ds,
@@ -225,6 +413,11 @@
 		      nua_dialog_state_t *ds,
 		      nua_dialog_usage_t *usage);
 
+static inline int nua_dialog_is_reporting(nua_dialog_state_t const *ds)
+{
+  return ds && ds->ds_reporting;
+}
+
 char const *nua_dialog_usage_name(nua_dialog_usage_t const *du);
 
 nua_dialog_usage_t *nua_dialog_usage_add(nua_owner_t *, 
@@ -243,10 +436,7 @@
 void nua_dialog_deinit(nua_owner_t *own,
 		       nua_dialog_state_t *ds);
 
-void nua_dialog_terminated(nua_owner_t *,
-			   struct nua_dialog_state *ds,
-			   int status,
-			   char const *phrase);
+int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds);
 
 void nua_dialog_usage_set_expires(nua_dialog_usage_t *du, unsigned delta);
 
@@ -262,6 +452,10 @@
 			      nua_dialog_usage_t *du, 
 			      sip_time_t now);
 
+void nua_dialog_usage_shutdown(nua_owner_t *owner,
+				nua_dialog_state_t *ds,
+				nua_dialog_usage_t *du);
+
 static inline
 int nua_dialog_is_established(nua_dialog_state_t const *ds)
 {
@@ -287,18 +481,178 @@
 
 /* ---------------------------------------------------------------------- */
 
+int nua_client_create(nua_owner_t *owner,
+		      int event,
+		      nua_client_methods_t const *methods,
+		      tagi_t const *tags);
+
+int nua_client_tcreate(nua_owner_t *nh, 
+		       int event,
+		       nua_client_methods_t const *methods,
+		       tag_type_t tag, tag_value_t value, ...);
+
+static inline 
+void *nua_private_client_request(nua_client_request_t const *cr)
+{
+  return (void *)(cr + 1);
+}
+
+void nua_client_request_destroy(nua_client_request_t *);
+
+int nua_client_request_queue(nua_client_request_t *cr);
+
+static inline int nua_client_is_queued(nua_client_request_t const *cr)
+{
+  return cr && cr->cr_prev;
+}
+
+nua_client_request_t *nua_client_request_remove(nua_client_request_t *cr);
+
+int nua_client_bind(nua_client_request_t *cr, nua_dialog_usage_t *du);
+
+static inline int nua_client_is_bound(nua_client_request_t const *cr)
+{
+  return cr && cr->cr_usage && cr->cr_usage->du_cr == cr;
+}
+
+static inline int nua_client_is_reporting(nua_client_request_t const *cr)
+{
+  return cr && cr->cr_reporting;
+}
+
+/** Mark client request as a terminating one */
+static inline void nua_client_terminating(nua_client_request_t *cr)
+{
+  cr->cr_terminating = 1;
+}
+
+int nua_client_init_request(nua_client_request_t *cr);
+
+int nua_client_restart_request(nua_client_request_t *cr,
+			       int terminating,
+			       tagi_t const *tags);
+
+int nua_client_resend_request(nua_client_request_t *cr,
+			      int terminating);
+
+int nua_base_client_request(nua_client_request_t *cr,
+			    msg_t *msg,
+			    sip_t *sip,
+			    tagi_t const *tags);
+
+int nua_base_client_trequest(nua_client_request_t *cr,
+			     msg_t *msg,
+			     sip_t *sip,
+			     tag_type_t tag, tag_value_t value, ...);
+
+extern nta_response_f nua_client_orq_response;
+
+int nua_client_return(nua_client_request_t *cr,
+		      int status,
+		      char const *phrase,
+		      msg_t *to_be_destroyed);
+
+int nua_client_response(nua_client_request_t *cr,
+			int status,
+			char const *phrase,
+			sip_t const *sip);
+
+int nua_client_check_restart(nua_client_request_t *cr,
+			     int status,
+			     char const *phrase,
+			     sip_t const *sip);
+
+int nua_base_client_check_restart(nua_client_request_t *cr,
+				  int status,
+				  char const *phrase,
+				  sip_t const *sip);
+
+int nua_client_restart(nua_client_request_t *cr,
+		       int status, char const *phrase);
+
+int nua_base_client_response(nua_client_request_t *cr,
+			     int status, char const *phrase,
+			     sip_t const *sip,
+			     tagi_t const *tags);
+
+int nua_base_client_tresponse(nua_client_request_t *cr,
+			      int status, char const *phrase,
+			      sip_t const *sip,
+			      tag_type_t tag, tag_value_t value, ...);
+
+int nua_client_set_target(nua_client_request_t *cr, url_t const *target);
+
+int nua_client_report(nua_client_request_t *cr,
+		      int status, char const *phrase,
+		      sip_t const *sip,
+		      nta_outgoing_t *orq,
+		      tagi_t const *tags);
+
+nua_client_request_t *nua_client_request_pending(nua_client_request_t const *);
+
+int nua_client_next_request(nua_client_request_t *cr, int invite);
+
+/* ---------------------------------------------------------------------- */
+
+extern nua_server_methods_t const
+  nua_extension_server_methods,
+  nua_invite_server_methods,	/**< INVITE */
+  nua_bye_server_methods,	/**< BYE */
+  nua_options_server_methods,	/**< OPTIONS */
+  nua_register_server_methods,	/**< REGISTER */
+  nua_info_server_methods,	/**< INFO */
+  nua_prack_server_methods,	/**< PRACK */
+  nua_update_server_methods,	/**< UPDATE */
+  nua_message_server_methods,	/**< MESSAGE */
+  nua_subscribe_server_methods, /**< SUBSCRIBE */
+  nua_notify_server_methods,	/**< NOTIFY */
+  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)
+{
+  return sr && sr->sr_response.msg;
+}
+
+static inline 
+int nua_server_request_status(nua_server_request_t const *sr)
+{
+  return sr ? nta_incoming_status(sr->sr_irq) : 500;
+}
+
 void nua_server_request_destroy(nua_server_request_t *sr);
 
-int nua_server_respond(nua_server_request_t *sr,
-		       int status, char const *phrase,
-		       tag_type_t tag, tag_value_t value, ...);
+int nua_base_server_init(nua_server_request_t *sr);
 
-msg_t *nua_server_response(nua_server_request_t *sr,
-			   int status, char const *phrase,
-			   tag_type_t tag, tag_value_t value, ...);
+#define nua_base_server_init NULL
+
+int nua_base_server_preprocess(nua_server_request_t *sr);
+
+#define nua_base_server_preprocess NULL
+
+int nua_server_params(nua_server_request_t *sr, tagi_t const *tags);
 
-int nua_default_respond(nua_server_request_t *sr,
-			tagi_t const *tags);
+int nua_base_server_params(nua_server_request_t *sr, tagi_t const *tags);
 
+#define nua_base_server_params NULL
+
+int nua_server_trespond(nua_server_request_t *sr,
+			tag_type_t tag, tag_value_t value, ...);
+int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags);
+
+int nua_base_server_trespond(nua_server_request_t *sr,
+			     tag_type_t tag, tag_value_t value, ...);
+int nua_base_server_respond(nua_server_request_t *sr,
+			    tagi_t const *tags);
+
+int nua_server_report(nua_server_request_t *sr);
+
+int nua_base_server_treport(nua_server_request_t *sr, 
+			    tag_type_t tag, tag_value_t value, ...);
+int nua_base_server_report(nua_server_request_t *sr, tagi_t const *tags);
+
+/* ---------------------------------------------------------------------- */
 
 #endif /* NUA_DIALOG_H */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_event_server.c	Tue Mar 20 23:37:15 2007
@@ -45,9 +45,6 @@
 #include <sofia-sip/sip_status.h>
 #include <sofia-sip/su_tagarg.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
-
 #define NEA_SMAGIC_T         struct nua_handle_s
 #define NEA_EMAGIC_T         struct nua_handle_s
 
@@ -121,13 +118,13 @@
     status = 900, phrase = "Error when notifying watchers";
 
   else 
-    nua_stack_event(nua, nh, NULL, e, status = SIP_200_OK, 
-		    SIPTAG_EVENT(event),
-		    SIPTAG_CONTENT_TYPE(ct),
-		    TAG_END());
+    nua_stack_tevent(nua, nh, NULL, e, status = SIP_200_OK, 
+		     SIPTAG_EVENT(event),
+		     SIPTAG_CONTENT_TYPE(ct),
+		     TAG_END());
 	     
   if (status != 200)
-    nua_stack_event(nua, nh, NULL, e, status, phrase, TAG_END());
+    nua_stack_event(nua, nh, NULL, e, status, phrase, NULL);
 
   su_home_deinit(home);
 }
@@ -223,7 +220,7 @@
       what = "active";
     }
 
-    SU_DEBUG_7(("nua(%p): authorize_watcher: %s\n", nh, what)); 
+    SU_DEBUG_7(("nua(%p): authorize_watcher: %s\n", (void *)nh, what)); 
     nea_sub_auth(sn->sn_subscriber, substate,
 		 TAG_IF(substate == nua_substate_pending,
 			NEATAG_FAKE(1)),
@@ -235,13 +232,13 @@
     substate = nua_substate_terminated;
     nea_server_flush(nes, NULL);
     SU_DEBUG_7(("nua(%p): authorize_watcher: %s\n", 
-		nh, "watcher is removed")); 
+		(void *)nh, "watcher is removed")); 
   }
 
-  nua_stack_event(nua, nh, msg, nua_i_subscription, status, phrase,
-	   NUTAG_SUBSTATE(substate),
-	   NEATAG_SUB(sn->sn_subscriber),
-	   TAG_END());
+  nua_stack_tevent(nua, nh, msg, nua_i_subscription, status, phrase,
+		   NUTAG_SUBSTATE(substate),
+		   NEATAG_SUB(sn->sn_subscriber),
+		   TAG_END());
 }
 
 /* ---------------------------------------------------------------------- */
@@ -262,12 +259,11 @@
 
   if (sub && state > 0) {
     nea_sub_auth(sub, state, TAG_NEXT(tags));
-    nua_stack_event(nua, nh, NULL, e, SIP_200_OK, TAG_END());
+    nua_stack_event(nua, nh, NULL, e, SIP_200_OK, NULL);
   }
   else {
-    nua_stack_event(nua, nh, NULL, e, NUA_INTERNAL_ERROR, TAG_END());
+    nua_stack_event(nua, nh, NULL, e, NUA_INTERNAL_ERROR, NULL);
   }
-  return;
 }
 
 /** @internal Shutdown notifier object */
@@ -347,5 +343,5 @@
 		       NEATAG_REASON("noresource"), 
 		       TAG_NEXT(tags));
 
-  nua_stack_event(nua, nh, NULL, e, SIP_200_OK, TAG_END());
+  nua_stack_event(nua, nh, NULL, e, SIP_200_OK, NULL);
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c	Tue Mar 20 23:37:15 2007
@@ -43,17 +43,8 @@
 #include <sofia-sip/sip_protos.h>
 #include <sofia-sip/sip_status.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
-
 #include "nua_stack.h"
 
-static int process_response_to_method(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip);
-static void restart_method(nua_handle_t *nh, tagi_t *tags);
-static int respond_to_method(nua_server_request_t *sr, tagi_t const *tags);
-
 /** Send an extension request. 
  *
  * Send an entension request message.
@@ -78,32 +69,25 @@
  * @since New in @VERSION_1_12_4.
  */
 
+static nua_client_methods_t const nua_method_client_methods = {
+  SIP_METHOD_UNKNOWN,
+  0,
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 0,
+    /* target_refresh */ 0
+  },
+  /* nua_method_client_template */ NULL,
+  /* nua_method_client_init */ NULL,
+  /* nua_method_client_request */ NULL,
+  /* nua_method_client_check_restart */ NULL,
+  /* nua_method_client_response */ NULL
+};
+
 int 
 nua_stack_method(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
 { 
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg;
-
-  if (cr->cr_orq)
-    return UA_EVENT2(e, 900, "Request already in progress");
-
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
-		     SIP_METHOD_UNKNOWN,
-		     TAG_NEXT(tags));
-  if (msg)
-    cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				      process_response_to_method, nh, NULL,
-				      msg,
-				      SIPTAG_END(),
-				      TAG_NEXT(tags));
-  if (!cr->cr_orq) {
-    msg_destroy(msg);
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-  }
-
-  return cr->cr_event = e;
+  return nua_client_create(nh, e, &nua_method_client_methods, tags);
 }
 
 /** @NUA_EVENT nua_r_method
@@ -127,20 +111,6 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_method(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip)
-{
-  if (nua_creq_check_restart(nh, nh->nh_ds->ds_cr, orq, sip, restart_method))
-    return 0;
-  return nua_stack_process_response(nh, nh->nh_ds->ds_cr, orq, sip, TAG_END());
-}
-
-void restart_method(nua_handle_t *nh, tagi_t *tags)
-{
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_method, tags);
-}
-
 /** @NUA_EVENT nua_i_method
  *
  * @brief Incoming extension request.
@@ -155,50 +125,35 @@
  * @param nh     operation handle associated with the method
  * @param hmagic application context associated with the handle
  *               (maybe NULL if outside session)
- * @param sip    incoming request
+ * @param sip    headers in incoming request (see also nua_current_request())
  * @param tags   NUTAG_METHOD()
  *
- * The extension name is in sip->sip_request->rq_method_name, too.
+ * The extension method name is in sip->sip_request->rq_method_name, too.
+ *
+ * @note If the @a status is < 200, it is up to application to respond to
+ * the request with nua_respond(). If the handle is destroyed, the stack
+ * returns a <i>500 Internal Server Error</i> response to any unresponded
+ * request.
  *
- * @sa nua_method(), #nua_r_method
+ * @sa nua_method(), #nua_r_method, NUTAG_ALLOW(), NUTAG_APPL_METHOD(),
+ * nua_respond(), NUTAG_WITH(), NUTAG_WITH_THIS(), NUTAG_
  *
  * @END_NUA_EVENT
  */
 
-int nua_stack_process_method(nua_t *nua,
-			      nua_handle_t *nh,
-			      nta_incoming_t *irq,
-			      sip_t const *sip)
-{
-  nua_server_request_t *sr, sr0[1];
-  
-  sr = SR_INIT(sr0);
-  
-  sr = nua_server_request(nua, nh, irq, sip, sr, sizeof *sr,
-			  respond_to_method, 0);
-
-  return nua_stack_server_event(nua, sr, nua_i_method, TAG_END());
-}
-
-static
-int respond_to_method(nua_server_request_t *sr, tagi_t const *tags)
-{
-  nua_handle_t *nh = sr->sr_owner;
-  nua_t *nua = nh->nh_nua;
-  msg_t *msg;
-
-  msg = nua_server_response(sr, sr->sr_status, sr->sr_phrase, TAG_NEXT(tags));
-
-  if (msg) {
-    nta_incoming_mreply(sr->sr_irq, msg);
-  }
-  else {
-    SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
-    nta_incoming_treply(sr->sr_irq, sr->sr_status, sr->sr_phrase, TAG_END());
-    nua_stack_event(nua, nh, NULL,
-		    nua_i_error, 900, "Response to Extension Method Fails",
-		    TAG_END());
-  }
-  
-  return sr->sr_status >= 200 ? sr->sr_status : 0;
-}
+nua_server_methods_t const nua_extension_server_methods = 
+  {
+    SIP_METHOD_UNKNOWN,
+    nua_i_method,		/* Event */
+    { 
+      0,			/* Do not create dialog */
+      0,			/* Can be an initial request */
+      1,			/* Perhaps a target refresh request? */
+      1,			/* Add a contact? */
+    },
+    nua_base_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_base_server_respond,
+    nua_base_server_report,
+  };

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c	Tue Mar 20 23:37:15 2007
@@ -43,9 +43,6 @@
 #include <sofia-sip/sip_protos.h>
 #include <sofia-sip/sip_status.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
-
 #include "nua_stack.h"
 
 /* ======================================================================== */
@@ -72,48 +69,41 @@
  * @sa #nua_i_message, @RFC3428
  */
 
-static int process_response_to_message(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip);
+static int nua_message_client_init(nua_client_request_t *cr, 
+				   msg_t *, sip_t *,
+				   tagi_t const *tags);
+
+static nua_client_methods_t const nua_message_client_methods = {
+  SIP_METHOD_MESSAGE,
+  0,
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 0,
+    /* target refresh */ 0
+  },
+  /* nua_message_client_template */ NULL,
+  nua_message_client_init,
+  /*nua_message_client_request*/ NULL,
+  /* nua_message_client_check_restart */ NULL,
+  /*nua_message_client_response*/ NULL
+};
 
 int 
-nua_stack_message(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
+nua_stack_message(nua_t *nua,
+		  nua_handle_t *nh,
+		  nua_event_t e,
+		  tagi_t const *tags)
 { 
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg;
-  sip_t *sip;
-
-  if (nh_is_special(nh)) {
-    return UA_EVENT2(e, 900, "Invalid handle for MESSAGE");
-  }
-  else if (cr->cr_orq) {
-    return UA_EVENT2(e, 900, "Request already in progress");
-  }
-
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
-			 SIP_METHOD_MESSAGE,
-			 NUTAG_ADD_CONTACT(NH_PGET(nh, win_messenger_enable)),
-			 TAG_NEXT(tags));
-  sip = sip_object(msg);
-
-  if (sip)
-    cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				      process_response_to_message, nh, NULL,
-				      msg,
-				      SIPTAG_END(), TAG_NEXT(tags));
-  if (!cr->cr_orq) {
-    msg_destroy(msg);
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-  }
-
-  return cr->cr_event = e;
+  return nua_client_create(nh, e, &nua_message_client_methods, tags);
 }
 
-void restart_message(nua_handle_t *nh, tagi_t *tags)
+static int nua_message_client_init(nua_client_request_t *cr, 
+				   msg_t *msg, sip_t *sip,
+				   tagi_t const *tags)
 {
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_message, tags);
+  if (NH_PGET(cr->cr_owner, win_messenger_enable))
+    cr->cr_contactize = 1;
+  return 0;
 }
 
 /** @NUA_EVENT nua_r_message
@@ -137,15 +127,6 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_message(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip)
-{
-  if (nua_creq_check_restart(nh, nh->nh_ds->ds_cr, orq, sip, restart_message))
-    return 0;
-  return nua_stack_process_response(nh, nh->nh_ds->ds_cr, orq, sip, TAG_END());
-}
-
 /** @NUA_EVENT nua_i_message
  *
  * @brief Incoming @b MESSAGE request.
@@ -168,32 +149,39 @@
  * @END_NUA_EVENT
  */
 
-int nua_stack_process_message(nua_t *nua,
-			      nua_handle_t *nh,
-			      nta_incoming_t *irq,
-			      sip_t const *sip)
-{
-  msg_t *msg;
-
-  if (nh
-      ? !NH_PGET(nh, message_enable)
-      : !DNH_PGET(nua->nua_dhandle, message_enable))
-    return 403;
+int nua_message_server_init(nua_server_request_t *sr);
+int nua_message_server_params(nua_server_request_t *, tagi_t const *);
 
-  if (nh == NULL)
-    if (!(nh = nua_stack_incoming_handle(nua, irq, sip, 0)))
-      return 500;		/* respond with 500 Internal Server Error */
+nua_server_methods_t const nua_message_server_methods = 
+  {
+    SIP_METHOD_MESSAGE,
+    nua_i_message,		/* Event */
+    { 
+      0,			/* Do not create dialog */
+      0,			/* Can be initial request */
+      0,			/* Perhaps a target refresh request? */
+      0,			/* Do not add contact by default */
+    },
+    nua_message_server_init,
+    nua_base_server_preprocess,
+    nua_message_server_params,
+    nua_base_server_respond,
+    nua_base_server_report,
+  };
 
-  msg = nta_incoming_getrequest(irq);
+int nua_message_server_init(nua_server_request_t *sr)
+{
+  if (!NH_PGET(sr->sr_owner, message_enable))
+    return SR_STATUS1(sr, SIP_403_FORBIDDEN);
 
-  nua_stack_event(nh->nh_nua, nh, msg, nua_i_message, SIP_200_OK, TAG_END());
+  return 0;
+}
 
-#if 0 /* XXX */
-  if (nh->nh_nua->nua_messageRespond) {	
-    nh->nh_irq = irq;
-    return 0;
-  }
-#endif
+int nua_message_server_params(nua_server_request_t *sr,
+			      tagi_t const *tags)
+{
+  if (NH_PGET(sr->sr_owner, win_messenger_enable))
+    sr->sr_add_contact = 1;
 
-  return 200;
+  return 0;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c	Tue Mar 20 23:37:15 2007
@@ -44,12 +44,12 @@
 
 #include <sofia-sip/string0.h>
 #include <sofia-sip/sip_protos.h>
+#include <sofia-sip/sip_extra.h>
 #include <sofia-sip/sip_status.h>
 #include <sofia-sip/sip_util.h>
 #include <sofia-sip/su_uniqueid.h>
-
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
+#include <sofia-sip/su_md5.h>
+#include <sofia-sip/token64.h>
 
 #include "nua_stack.h"
 
@@ -59,7 +59,14 @@
 struct notifier_usage
 {
   enum nua_substate  nu_substate;	/**< Subscription state */
-  sip_time_t         nu_expires;
+  sip_time_t         nu_expires; 	/**< Expiration time */
+  sip_time_t         nu_requested;      /**< Requested expiration time */
+#if SU_HAVE_EXPERIMENTAL
+  char              *nu_tag;	        /**< @ETag in last NOTIFY */
+  unsigned           nu_etags:1;	/**< Subscriber supports etags */
+  unsigned           nu_appl_etags:1;   /**< Application generates etags */
+  unsigned           nu_no_body:1;      /**< Suppress body */
+#endif
 };
 
 static char const *nua_notify_usage_name(nua_dialog_usage_t const *du);
@@ -115,8 +122,6 @@
 /* ====================================================================== */
 /* SUBSCRIBE server */
 
-static int respond_to_subscribe(nua_server_request_t *sr, tagi_t const *tags);
-
 /** @NUA_EVENT nua_i_subscribe
  *
  * Incoming @b SUBSCRIBE request.
@@ -160,152 +165,185 @@
  * @END_NUA_EVENT
  */
 
+static int nua_subscribe_server_init(nua_server_request_t *sr);
+static int nua_subscribe_server_preprocess(nua_server_request_t *sr);
+static int nua_subscribe_server_respond(nua_server_request_t*, tagi_t const *);
+static int nua_subscribe_server_report(nua_server_request_t*, tagi_t const *);
+
+nua_server_methods_t const nua_subscribe_server_methods = 
+  {
+    SIP_METHOD_SUBSCRIBE,
+    nua_i_subscribe,		/* Event */
+    { 
+      1,			/* Create dialog */
+      0,			/* Initial request */
+      1,			/* Target refresh request  */
+      1,			/* Add Contact */
+    },
+    nua_subscribe_server_init,
+    nua_subscribe_server_preprocess,
+    nua_base_server_params,
+    nua_subscribe_server_respond,
+    nua_subscribe_server_report,
+  };
 
-/** @internal Process incoming SUBSCRIBE. */
-int nua_stack_process_subscribe(nua_t *nua,
-				nua_handle_t *nh,
-				nta_incoming_t *irq,
-				sip_t const *sip)
-{
-  nua_server_request_t *sr, sr0[1];
-  nua_dialog_state_t *ds;
-  nua_dialog_usage_t *du = NULL;
+int nua_subscribe_server_init(nua_server_request_t *sr)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_dialog_state_t *ds = nh->nh_ds;
+  sip_allow_events_t const *allow_events = NH_PGET(nh, allow_events);
+  sip_t const *sip = sr->sr_request.sip;
   sip_event_t *o = sip->sip_event;
   char const *event = o ? o->o_type : NULL;
   
-  enum nua_substate substate = nua_substate_terminated;
+  if (sr->sr_initial || !nua_dialog_usage_get(ds, nua_notify_usage, o)) {
+    if (event && str0cmp(event, "refer") == 0)
+      /* refer event subscription should be initiated with REFER */
+      return SR_STATUS1(sr, SIP_403_FORBIDDEN);
 
-  enter;
+    /* XXX - event is case-sensitive, should use msg_header_find_item() */
+    if (!event || !msg_header_find_param(allow_events->k_common, event))
+      return SR_STATUS1(sr, SIP_489_BAD_EVENT);
+  }
+
+  return 0;
+}
 
-  if (nh)
-    du = nua_dialog_usage_get(ds = nh->nh_ds, nua_notify_usage, o);
+int nua_subscribe_server_preprocess(nua_server_request_t *sr)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_dialog_state_t *ds = nh->nh_ds;
+  nua_dialog_usage_t *du;
+  struct notifier_usage *nu;
+  sip_t const *sip = sr->sr_request.sip;
+  sip_event_t *o = sip->sip_event;
+  char const *event = o ? o->o_type : NULL;
+  /* Maximum expiration time */
+  unsigned long expires = 3600;
 
-  sr = SR_INIT(sr0);
+  assert(nh && nh->nh_nua->nua_dhandle != nh);
   
-  if (nh == NULL || du == NULL) {
-    sip_allow_events_t *allow_events = NUA_PGET(nua, nh, allow_events);
+  du = nua_dialog_usage_get(ds, nua_notify_usage, o);
 
-    if (event && str0cmp(event, "refer") == 0)
-      /* refer event subscription should be initiated with REFER */
-      SR_STATUS1(sr, SIP_403_FORBIDDEN);
-    else if (!event || !msg_header_find_param(allow_events->k_common, event))
-      SR_STATUS1(sr, SIP_489_BAD_EVENT);
-    else
-      substate = nua_substate_embryonic;
+  if (du == NULL) {
+    /* Create a new subscription */
+    du = nua_dialog_usage_add(nh, ds, nua_notify_usage, o);
+    if (du == NULL)
+      return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
   }
   else {
     /* Refresh existing subscription */
-    struct notifier_usage *nu = nua_dialog_usage_private(du);
-    unsigned long expires;
+    if (str0cmp(event, "refer") == 0)
+      expires = NH_PGET(nh, refer_expires);
+
+    SR_STATUS1(sr, SIP_200_OK);
+  }
 
-    assert(nh && du && nu);
+  nu = nua_dialog_usage_private(du);
 
-    expires = str0cmp(event, "refer") ? 3600 : NH_PGET(nh, refer_expires);
+  if (sip->sip_expires && sip->sip_expires->ex_delta < expires)
+    expires = sip->sip_expires->ex_delta;
+  nu->nu_requested = sip_now() + expires;
 
-    if (sip->sip_expires && sip->sip_expires->ex_delta < expires)
-      expires = sip->sip_expires->ex_delta;
+#if SU_HAVE_EXPERIMENTAL
+  nu->nu_etags = 
+    sip_suppress_body_if_match(sip) ||
+    sip_suppress_notify_if_match(sip) ||
+    sip_has_feature(sr->sr_request.sip->sip_supported, "etags");
+#endif
 
-    if (expires == 0)
-      nu->nu_substate = nua_substate_terminated;
+  sr->sr_usage = du;
 
-    nu->nu_expires = sip_now() + expires;
-    substate = nu->nu_substate;
+  return sr->sr_status <= 100 ? 0 : sr->sr_status;
+}
 
-    /* XXX - send notify */
+/** @internal Respond to a SUBSCRIBE request.
+ *
+ */
+static
+int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags)
+{
+  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
 
-    SR_STATUS1(sr, SIP_200_OK);
-  }
+  msg_t *msg = sr->sr_response.msg;
+  sip_t *sip = sr->sr_response.sip;
 
-  sr = nua_server_request(nua, nh, irq, sip, sr, sizeof *sr,
-			  respond_to_subscribe, 1);
+  if (200 <= sr->sr_status && sr->sr_status < 300) {
+    sip_expires_t ex[1]; 
 
-  if (!du && substate == nua_substate_embryonic && sr->sr_status < 300) {
-    nh = sr->sr_owner; assert(nh && nh != nua->nua_dhandle);
-    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, sip->sip_event);
-    if (du) {
-      struct notifier_usage *nu = nua_dialog_usage_private(du);
-      unsigned long expires = 3600; /* XXX */
-      
-      if (sip->sip_expires && sip->sip_expires->ex_delta < expires)
-	expires = sip->sip_expires->ex_delta;
+    sip_expires_init(ex);
 
-      nu->nu_expires = sip_now() + expires;
-      nu->nu_substate = substate;
-    }
-    else 
-      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
-  }
+    if (nu) {
+      sip_time_t now = sip_now();
 
-  if (substate == nua_substate_embryonic && sr->sr_status >= 300)
-    substate = nua_substate_terminated;
+      if (nu->nu_requested) {
+	if (nu->nu_requested > nu->nu_expires)
+	  nu->nu_expires = nu->nu_requested;
+	else if (nu->nu_expires <= now || nu->nu_requested <= now)
+	  nu->nu_substate = nua_substate_terminated;
+      }
 
-  sr->sr_usage = du;
+      if (nu->nu_expires > now)
+	ex->ex_delta = nu->nu_expires - now;
+    }
+    else {
+      /* Add header Expires: 0 */
+    }
 
-  return nua_stack_server_event(nua, sr, nua_i_subscribe,
-				NUTAG_SUBSTATE(substate), TAG_END());
+    if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta)
+      sip_add_dup(msg, sip, (sip_header_t *)ex);
+  }
+
+  return nua_base_server_respond(sr, tags);
 }
 
-/** @internal Respond to an SUBSCRIBE request.
- *
- */
 static
-int respond_to_subscribe(nua_server_request_t *sr, tagi_t const *tags)
+int nua_subscribe_server_report(nua_server_request_t *sr, tagi_t const *tags)
 {
   nua_handle_t *nh = sr->sr_owner;
   nua_dialog_state_t *ds = nh->nh_ds;
-  nua_t *nua = nh->nh_nua;
-  struct notifier_usage *nu;
-  sip_allow_events_t *allow_events = NUA_PGET(nua, nh, allow_events);
-  sip_expires_t ex[1]; 
-  sip_time_t now = sip_now();
-  msg_t *msg;
-
-  sip_expires_init(ex);
+  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
+  enum nua_substate substate = nua_substate_terminated;
+  int notify = 0;
+  int retval;
 
-  nu = nua_dialog_usage_private(sr->sr_usage);
-  if (nu && nu->nu_expires > now)
-    ex->ex_delta = nu->nu_expires - now;
+  if (nu && !sr->sr_terminating) {
+    substate = nu->nu_substate;
+  }
 
-  msg = nua_server_response(sr,
-			    sr->sr_status, sr->sr_phrase,
-			    NUTAG_ADD_CONTACT(sr->sr_status < 300),
-			    TAG_IF(nu, SIPTAG_EXPIRES(ex)),
-			    SIPTAG_SUPPORTED(NH_PGET(nh, supported)),
-			    SIPTAG_ALLOW_EVENTS(allow_events),
-			    TAG_NEXT(tags));
-
-  if (msg) {
-    sip_t *sip = sip_object(msg);
-
-    if (nu && sip->sip_expires && sr->sr_status < 300)
-      nu->nu_expires = now + sip->sip_expires->ex_delta;
-
-    nta_incoming_mreply(sr->sr_irq, msg);
-
-    if (nu && nu->nu_substate != nua_substate_embryonic)
-      /* Send NOTIFY (and terminate subscription, when needed) */
-      nua_dialog_usage_refresh(nh, ds, sr->sr_usage, sip_now());
+  /* nu_requested is set by SUBSCRIBE and cleared when NOTIFY is sent */
+  if (nu && nu->nu_requested && substate != nua_substate_embryonic) {
+#if SU_HAVE_EXPERIMENTAL
+    sip_t const *sip = sr->sr_request.sip;
+    sip_suppress_notify_if_match_t *snim = sip_suppress_notify_if_match(sip);
+    sip_suppress_body_if_match_t *sbim = sip_suppress_body_if_match(sip);
+    
+    if (!nu->nu_tag)
+      notify = 1;
+    else if (snim && !strcasecmp(snim->snim_tag, nu->nu_tag))
+      notify = 0;
+    else if (sbim && !strcasecmp(snim->snim_tag, nu->nu_tag))
+      notify = 1, nu->nu_no_body = 1;
+    else 
+#endif
+      notify = 1;
   }
-  else {
-    /* XXX - send nua_i_error */
-    SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
-    nta_incoming_treply(sr->sr_irq, sr->sr_status, sr->sr_phrase, TAG_END());
+
+  retval = nua_base_server_treport(sr, NUTAG_SUBSTATE(substate), TAG_END());
+
+  if (retval >= 2 || nu == NULL)
+    return retval;
+  
+  if (notify) {
+    /* Send NOTIFY (and terminate subscription, when needed) */
+    nua_dialog_usage_refresh(nh, ds, sr->sr_usage, sip_now());
   }
   
-  return sr->sr_status >= 200 ? sr->sr_status : 0;
+  return retval;
 }
 
 /* ======================================================================== */
-/* NOTIFY */
-
-static int process_response_to_notify(nua_handle_t *nh,
-				      nta_outgoing_t *orq,
-				      sip_t const *sip);
-
-static int nua_stack_notify2(nua_t *, nua_handle_t *, nua_event_t, 
-			     nua_dialog_usage_t *du,
-			     tagi_t const *tags);
-
+/* NOTIFY client */
 
 /**@fn void nua_notify(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
  *
@@ -336,175 +374,271 @@
  * @sa @RFC3265, #nua_i_subscribe, #nua_i_refer, NUTAG_ALLOW_EVENTS()
  */
 
+static int nua_notify_client_init(nua_client_request_t *cr, 
+				  msg_t *, sip_t *,
+				  tagi_t const *tags);
+static int nua_notify_client_init_etag(nua_client_request_t *cr,
+				       msg_t *msg, sip_t *sip,
+				       tagi_t const *tags);
+static int nua_notify_client_request(nua_client_request_t *cr,
+				     msg_t *, sip_t *,
+				     tagi_t const *tags);
+static int nua_notify_client_report(nua_client_request_t *cr,
+				    int status, char const *phrase,
+				    sip_t const *sip,
+				    nta_outgoing_t *orq,
+				    tagi_t const *tags);
+
+static nua_client_methods_t const nua_notify_client_methods = {
+  SIP_METHOD_NOTIFY,
+  0,
+  { 
+    /* create_dialog */ 1,
+    /* in_dialog */ 1,
+    /* target refresh */ 1
+  },
+  /* nua_notify_client_template */ NULL,
+  nua_notify_client_init,
+  nua_notify_client_request,
+  /* nua_notify_client_check_restart */ NULL,
+  /* nua_notify_client_response */ NULL,
+  /* nua_notify_client_preliminary */ NULL,
+  nua_notify_client_report
+};
+
 /**@internal Send NOTIFY. */
 int nua_stack_notify(nua_t *nua,
 		     nua_handle_t *nh,
 		     nua_event_t e,
 		     tagi_t const *tags)
 {
-  return nua_stack_notify2(nua, nh, e, NULL, tags);
+  return nua_client_create(nh, e, &nua_notify_client_methods, tags);
 }
 
-
-int nua_stack_notify2(nua_t *nua,
-		      nua_handle_t *nh,
-		      nua_event_t e,
-		      nua_dialog_usage_t *du,
-		      tagi_t const *tags)
+static int nua_notify_client_init(nua_client_request_t *cr,
+				  msg_t *msg, sip_t *sip,
+				  tagi_t const *tags)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
+  nua_handle_t *nh = cr->cr_owner;
+  nua_dialog_usage_t *du;
   struct notifier_usage *nu;
-  msg_t *msg;
-  sip_t *sip;
-  sip_event_t const *o;
-  sip_time_t now;
-  int refresh = du != NULL;
-
-  if (cr->cr_orq) {
-    return UA_EVENT2(e, 900, "Request already in progress");
-  }
-
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  if (refresh) {
-    assert(!cr->cr_msg);
-    if (cr->cr_msg)
-      msg_destroy(cr->cr_msg);
-    cr->cr_msg = msg_copy(du->du_msg);
-  }
-
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count || refresh,
-		     SIP_METHOD_NOTIFY,
-		     NUTAG_ADD_CONTACT(1),
-		     TAG_NEXT(tags));
-  sip = sip_object(msg);
-  if (!sip)
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-
-  if (nh->nh_ds->ds_has_notifys == 1 && !sip->sip_event)
+  sip_event_t const *o = sip->sip_event;
+  sip_subscription_state_t *ss = sip->sip_subscription_state;
+  sip_time_t now = sip_now();
+    
+  if (o == NULL && nh->nh_ds->ds_has_notifys == 1)
     o = NONE;
-  else
-    o = sip->sip_event;
 
   du = nua_dialog_usage_get(nh->nh_ds, nua_notify_usage, o);
-  nu = nua_dialog_usage_private(du);
 
-  if (du && du->du_event && !sip->sip_event)
-    sip_add_dup(msg, sip, (sip_header_t *)du->du_event);
+  if (!du) {
+    tagi_t const *newsub = tl_find_last(tags, nutag_newsub);
 
-  now = sip_now();
+    if (!newsub || !newsub->t_value)
+      return 0; /* Rejected eventually by nua_notify_client_request() */
 
-  if (!du)
-    ;
-  else if (sip->sip_subscription_state) {
-    /* SIPTAG_SUBSCRIPTION_STATE() overrides NUTAG_SUBSTATE() */
-    char const *ss_substate = sip->sip_subscription_state->ss_substate;
+    /* Create new notifier */
+    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
+    if (du == NULL)
+      return -1;
 
-    if (strcasecmp(ss_substate, "terminated") == 0)
-      nu->nu_substate = nua_substate_terminated;
-    else if (strcasecmp(ss_substate, "pending") == 0)
-      nu->nu_substate = nua_substate_pending;
-    else /* if (strcasecmp(subs->ss_substate, "active") == 0) */ 
-      nu->nu_substate = nua_substate_active;
+    nu = nua_dialog_usage_private(du);
+    nu->nu_expires = now;
+  }
+  else
+    nu = nua_dialog_usage_private(du);
 
-    if (sip->sip_subscription_state->ss_expires) {
-      unsigned long expires;
-      expires = strtoul(sip->sip_subscription_state->ss_expires, NULL, 10);
-      if (expires > 3600)
-        expires = 3600;
-      nu->nu_expires = now + expires;
-    }
-    else if (nu->nu_substate != nua_substate_terminated) {
-      sip_subscription_state_t *ss = sip->sip_subscription_state;
-      char *param;
-
-      if (now < nu->nu_expires)
-        param = su_sprintf(msg_home(msg), "expires=%lu", nu->nu_expires - now);
-      else
-        param = "expires=0";
 
-      msg_header_add_param(msg_home(msg), ss->ss_common, param);
+  if (nu->nu_substate == nua_substate_terminated) {
+    /*Xyzzy*/;
+  }
+  else if (ss != NULL) {
+    /* SIPTAG_SUBSCRIPTION_STATE() overrides NUTAG_SUBSTATE() */
+    nu->nu_substate = nua_substate_make(ss->ss_substate);
+
+    if (ss->ss_expires) {
+      unsigned long expires = strtoul(ss->ss_expires, NULL, 10);
+      if (now + expires < now)
+	expires = SIP_TIME_MAX - now - 1;
+
+      /* Notifier can only shorten the subscription time */ 
+      if (nu->nu_requested == 0 || nu->nu_requested >= now + expires)
+	nu->nu_expires = nu->nu_requested = now + expires;
     }
   }
   else {
-    sip_subscription_state_t *ss;
-    enum nua_substate substate;
-    char const *name;
-
-    substate = nu->nu_substate;
-
-    if (nu->nu_expires <= now)
-      substate = nua_substate_terminated;
+    enum nua_substate substate = nu->nu_substate;
 
-    if (substate != nua_substate_terminated) {
+    if (nu->nu_expires > now) {
       tagi_t const *t = tl_find_last(tags, nutag_substate);
       if (t)
-	substate = (enum nua_substate)t->t_value;
+        substate = (enum nua_substate)t->t_value;
     }
+    else
+      substate = nua_substate_terminated;
 
     switch (substate) {
     case nua_substate_embryonic:
       /*FALLTHROUGH*/
     case nua_substate_pending:
-      name = "pending";
       nu->nu_substate = nua_substate_pending;
       break;
     case nua_substate_active:
     default:
-      name = "active";
       nu->nu_substate = nua_substate_active;
       break;
     case nua_substate_terminated:
-      name = "terminated";
       nu->nu_substate = nua_substate_terminated;
       break;
     }
+  }
 
-    if (nu->nu_substate != nua_substate_terminated) {
-      unsigned long expires = nu->nu_expires - now;
-      ss = sip_subscription_state_format(msg_home(msg), "%s;expires=%lu",
-					 name, expires);
-    }
-    else {
-      ss = sip_subscription_state_make(msg_home(msg), "terminated; "
-				       "reason=noresource");
-    }
+  if (nu->nu_substate == nua_substate_terminated)
+    cr->cr_terminating = 1;
 
-    msg_header_insert(msg, (void *)sip, (void *)ss);
-  }
+  cr->cr_usage = du;
 
-  if (du) {
-    if (nu->nu_substate == nua_substate_terminated)
-      du->du_terminating = 1;
+  return nua_notify_client_init_etag(cr, msg, sip, tags);
+}
 
-    if (!du->du_terminating && !refresh) {
-      /* Save template */
-      if (du->du_msg)
-        msg_destroy(du->du_msg);
-      du->du_msg = msg_ref_create(cr->cr_msg);
-    }
+static int nua_notify_client_init_etag(nua_client_request_t *cr,
+				       msg_t *msg, sip_t *sip,
+				       tagi_t const *tags)
+{
+#if SU_HAVE_EXPERIMENTAL
+  nua_handle_t *nh = cr->cr_owner;
+  struct notifier_usage *nu = nua_dialog_usage_private(cr->cr_usage);
+  nua_server_request_t *sr;
+
+  if (nu->nu_tag)
+    su_free(nh->nh_home, nu->nu_tag), nu->nu_tag = NULL;
+    nu->nu_no_body = 0;
+
+  if (sip->sip_etag) {
+    nu->nu_appl_etags = 1;
+    nu->nu_tag = su_strdup(nh->nh_home, sip->sip_etag->g_string);
   }
+  else if (!nu->nu_appl_etags && nu->nu_etags) {
+    su_md5_t md5[1];
+    unsigned char digest[SU_MD5_DIGEST_SIZE];
+    sip_payload_t pl[1] = { SIP_PAYLOAD_INIT() };
+    char token[2 * 16];
+
+    su_md5_init(md5);
+
+    if (sip->sip_payload) *pl = *sip->sip_payload;
+
+    if (pl->pl_len)
+      su_md5_update(md5, pl->pl_data, pl->pl_len);
+    su_md5_update(md5, &pl->pl_len, sizeof(pl->pl_len));
 
-  /* NOTIFY outside a dialog */
-  cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				    process_response_to_notify, nh, NULL,
-				    msg,
-				    SIPTAG_END(), TAG_NEXT(tags));
-
-  if (!cr->cr_orq) {
-    msg_destroy(msg);
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
+    if (sip->sip_content_type)
+      su_md5_striupdate(md5, sip->sip_content_type->c_type);
+
+    su_md5_digest(md5, digest);
+    token64_e(token, sizeof token, digest, sizeof digest);
+    token[(sizeof token) - 1] = '\0';
+    nu->nu_tag = su_strdup(nh->nh_home, token);
   }
 
-  cr->cr_usage = du;
+  if (!nu->nu_requested || !nu->nu_tag)
+    return 0;
 
-  return cr->cr_event = e;
+  /* Check if SUBSCRIBE had matching suppression */
+  for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
+    if (sr->sr_usage == cr->cr_usage && sr->sr_method == sip_method_subscribe)
+      break;
+
+  if (sr) {
+    sip_t const *sip = sr->sr_request.sip;
+
+    sip_suppress_body_if_match_t *sbim;
+    sip_suppress_notify_if_match_t *snim;
+    
+    if (cr->cr_usage->du_ready) {
+      snim = sip_suppress_notify_if_match(sip);
+
+      if (snim && !strcasecmp(snim->snim_tag, nu->nu_tag)) {
+	if (nu->nu_requested > nu->nu_expires)
+	  nu->nu_expires = nu->nu_requested;
+	nu->nu_requested = 0;
+	return nua_client_return(cr, 202, "NOTIFY Suppressed", msg);
+      }
+    }
+
+    sbim = sip_suppress_body_if_match(sip);
+    if (sbim && !strcasecmp(sbim->sbim_tag, nu->nu_tag))
+      nu->nu_no_body = 1;
+  }
+#endif
+
+  return 0;
 }
 
 static
-void restart_notify(nua_handle_t *nh, tagi_t *tags)
+int nua_notify_client_request(nua_client_request_t *cr,
+			      msg_t *msg, sip_t *sip,
+			      tagi_t const *tags)
 {
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_notify, tags);
+  nua_dialog_usage_t *du = cr->cr_usage; 
+  struct notifier_usage *nu = nua_dialog_usage_private(du);
+  su_home_t *home = msg_home(msg);
+  sip_time_t now = sip_now();
+  sip_subscription_state_t *ss = sip->sip_subscription_state;
+  char const *expires;
+
+  if (du == NULL)		/* Subscription has been terminated */
+    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);
+
+  assert(du && nu);
+
+  if (du && nua_client_bind(cr, du) < 0)
+    return -1;
+
+  if (nu->nu_requested)
+    nu->nu_expires = nu->nu_requested;
+  nu->nu_requested = 0;
+
+  if (nu->nu_expires <= now || du->du_shutdown) {
+    nu->nu_substate = nua_substate_terminated;
+    expires = "expires=0";
+  }
+  else {
+    expires = su_sprintf(home, "expires=%lu", nu->nu_expires - now);
+  }
+
+  if (ss == NULL || nua_substate_make(ss->ss_substate) != nu->nu_substate) {
+    if (nu->nu_substate == nua_substate_terminated)
+      expires = nu->nu_expires > now ? "noresource" : "timeout";
+
+    ss = sip_subscription_state_format(home, "%s;%s", 
+				       nua_substate_name(nu->nu_substate),
+				       expires);
+
+    msg_header_insert(msg, (void *)sip, (void *)ss);
+  }
+  else if (nu->nu_substate != nua_substate_terminated) {
+    msg_header_replace_param(home, ss->ss_common, expires);
+  }
+
+#if SU_HAVE_EXPERIMENTAL
+  if (nu->nu_tag && !sip->sip_etag)
+    msg_header_add_make(msg, (void *)sip, sip_etag_class, nu->nu_tag);
+
+  if (nu->nu_no_body) {
+    nu->nu_no_body = 0;
+    msg_header_remove(msg, (void *)sip, (void *)sip->sip_payload);
+    msg_header_remove(msg, (void *)sip, (void *)sip->sip_content_length);
+  }
+#endif
+
+  if (nu->nu_substate == nua_substate_terminated)
+    cr->cr_terminating = 1;
+
+  if (du->du_event && !sip->sip_event)
+    sip_add_dup(cr->cr_msg, sip, (sip_header_t *)du->du_event);
+
+  return nua_base_client_request(cr, msg, sip, tags);
 }
 
 /** @NUA_EVENT nua_r_notify
@@ -527,30 +661,40 @@
  *               (status code is in @a status and 
  *               descriptive message in @a phrase parameters)
  * @param tags   NUTAG_SUBSTATE() indicating subscription state
+ *               SIPTAG_EVENT() indicating subscription event
  *
  * @sa nua_notify(), @RFC3265, #nua_i_subscribe, #nua_i_refer
  *
  * @END_NUA_EVENT
  */
 
-static int process_response_to_notify(nua_handle_t *nh,
-				      nta_outgoing_t *orq,
-				      sip_t const *sip)
+static int nua_notify_client_report(nua_client_request_t *cr,
+				    int status, char const *phrase,
+				    sip_t const *sip,
+				    nta_outgoing_t *orq,
+				    tagi_t const *tags)
 {
+  nua_handle_t *nh = cr->cr_owner;
+  nua_dialog_usage_t *du = cr->cr_usage;
+  struct notifier_usage *nu = nua_dialog_usage_private(du);
   enum nua_substate substate = nua_substate_terminated;
 
-  if (nua_creq_check_restart(nh, nh->nh_ds->ds_cr, orq, sip, restart_notify))
-    return 0;
-
-  if (nh->nh_ds->ds_cr->cr_usage) {
-    struct notifier_usage *nu = nua_dialog_usage_private(nh->nh_ds->ds_cr->cr_usage);
+  if (nu && !cr->cr_terminated)
     substate = nu->nu_substate;
-    assert(substate != nua_substate_embryonic);
-  }
 
-  return nua_stack_process_response(nh, nh->nh_ds->ds_cr, orq, sip, 
-				    NUTAG_SUBSTATE(substate),
-				    TAG_END());
+  nua_stack_tevent(nh->nh_nua, nh, 
+		   nta_outgoing_getresponse(orq),
+		   cr->cr_event,
+		   status, phrase,
+		   NUTAG_SUBSTATE(substate),
+		   SIPTAG_EVENT(du ? du->du_event : NULL),
+		   TAG_NEXT(tags));
+
+  if (nu && nu->nu_requested && du->du_cr == cr)
+    /* Re-SUBSCRIBEd while NOTIFY was in progress, resend NOTIFY */
+    nua_client_resend_request(cr, 0);
+
+  return 0;
 }
 
 
@@ -560,29 +704,30 @@
 				     sip_time_t now)
 {
   struct notifier_usage *nu = nua_dialog_usage_private(du);
+  nua_client_request_t *cr = du->du_cr;
+  nua_event_t e = nua_r_notify;
 
-  if (nh->nh_ds->ds_cr->cr_usage == du) /* Already notifying. */
-    return;
+  if (cr) {
+    int terminating = 0;
 
-  if (now >= nu->nu_expires) {
-    sip_subscription_state_t ss[1];
-    char const *params[] = { NULL, NULL };
-    tagi_t tags[2] = {
-      { SIPTAG_SUBSCRIPTION_STATE(ss) }, { TAG_END() }
-    };
-
-    sip_subscription_state_init(ss);
-
-    ss->ss_substate = "terminated";
-    ss->ss_params = params;
-    params[0] = "reason=timeout";
-    ss->ss_reason = "timeout";
+    if (du->du_expires && du->du_expires <= now)
+      terminating = 1;
+    else if (nu->nu_requested && nu->nu_requested <= now)
+      terminating = 1;
 
-    nua_stack_notify2(nh->nh_nua, nh, nua_r_notify, du, tags);
+    if (nua_client_resend_request(cr, terminating) >= 0)
+      return;
   }
   else {
-    nua_stack_notify2(nh->nh_nua, nh, nua_r_notify, du, NULL);
+    if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0)
+      return;
   }
+
+  nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_INTERNAL_ERROR,
+		   NUTAG_SUBSTATE(nua_substate_terminated),
+		   TAG_END());
+
+  nua_dialog_usage_remove(nh, ds, du);
 }
 
 /** @interal Shut down NOTIFY usage. 
@@ -595,26 +740,110 @@
 				     nua_dialog_state_t *ds,
 				     nua_dialog_usage_t *du)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
+  struct notifier_usage *nu = nua_dialog_usage_private(du);
+  nua_client_request_t *cr = du->du_cr;
 
-  if (!cr->cr_usage) {
-    /* Unnotify */
-	/* Commenting this line out to supress an attended transfer bug (awaiting fix from pessi) */
-    //nua_stack_notify2(nh->nh_nua, nh, nua_r_destroy, du, NULL);
-    return cr->cr_usage != du;
-  }
+  nu->nu_substate = nua_substate_terminated;
 
-  if (!du->du_ready && !cr->cr_orq)
-    return 1;			/* Unauthenticated NOTIFY? */
+  if (cr) {
+    if (nua_client_resend_request(cr, 1) >= 0)
+      return 0;
+  }
+  else {
+    if (nua_client_create(nh, nua_r_notify, 
+			  &nua_notify_client_methods, NULL) >= 0)
+      return 0;
+  }
 
-  return -1;  /* Request in progress */
+  nua_dialog_usage_remove(nh, ds, du);
+  return 200;
 }
 
-
 /* ======================================================================== */
 /* REFER */
 /* RFC 3515 */
 
+static int nua_refer_server_init(nua_server_request_t *sr);
+static int nua_refer_server_preprocess(nua_server_request_t *sr);
+static int nua_refer_server_respond(nua_server_request_t*, tagi_t const *);
+static int nua_refer_server_report(nua_server_request_t*, tagi_t const *);
+
+nua_server_methods_t const nua_refer_server_methods = 
+  {
+    SIP_METHOD_REFER,
+    nua_i_refer,		/* Event */
+    { 
+      1,			/* Create dialog */
+      0,			/* Initial request */
+      1,			/* Target refresh request  */
+      1,			/* Add Contact */
+    },
+    nua_refer_server_init,
+    nua_refer_server_preprocess,
+    nua_base_server_params,
+    nua_refer_server_respond,
+    nua_refer_server_report,
+  };
+
+static int nua_refer_server_init(nua_server_request_t *sr)
+{
+  return 0;
+}
+
+static int nua_refer_server_preprocess(nua_server_request_t *sr)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  sip_t const *sip = sr->sr_request.sip;
+  struct notifier_usage *nu;
+  sip_event_t *o;
+
+  if (nh->nh_ds->ds_got_referrals || NH_PGET(nh, refer_with_id))
+    o = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
+  else
+    o = sip_event_make(nh->nh_home, "refer");
+
+  if (o) {
+    sr->sr_usage = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
+    msg_header_free(nh->nh_home, (msg_header_t *)o);
+  }
+
+  if (!sr->sr_usage)
+    return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+
+  nu = nua_dialog_usage_private(sr->sr_usage);
+  nu->nu_requested = sip_now() + NH_PGET(nh, refer_expires);
+
+  return 0;
+}
+
+static
+int nua_refer_server_respond(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
+  sip_refer_sub_t const *rs = sip_refer_sub(sr->sr_response.sip);
+
+  if (sr->sr_status < 200 || nu == NULL) {
+  }
+  else if (sr->sr_status < 300 && 
+	   /* Application included Refer-Sub: false in response */
+	   (rs == NULL || str0casecmp("false", rs->rs_value))) {
+    sr->sr_usage->du_ready = 1;
+
+    nu->nu_expires = sip_now() + NH_PGET(nh, refer_expires);
+
+    if (sr->sr_application)	/* Application responded to REFER */
+      nu->nu_substate = nua_substate_active;
+  }
+  else {
+    /* Destroy the implicit subscription usage */
+    sr->sr_terminating = 1;
+  }
+
+  return nua_base_server_respond(sr, tags);
+}
+
+
 /** @NUA_EVENT nua_i_refer
  *
  * Incoming @b REFER request used to transfer calls.
@@ -635,89 +864,45 @@
  * @END_NUA_EVENT
  */
 
-/** @internal Process incoming REFER. */
-int nua_stack_process_refer(nua_t *nua,
-			    nua_handle_t *nh,
-			    nta_incoming_t *irq,
-			    sip_t const *sip)
+static
+int nua_refer_server_report(nua_server_request_t *sr, tagi_t const *tags)
 {
-  nua_dialog_usage_t *du = NULL;
-  struct notifier_usage *nu;
-  sip_event_t *event;
-  sip_referred_by_t *by = NULL, default_by[1];
-  msg_t *response;
-  sip_time_t expires;
-  int created = 0;
-
-  if (nh == NULL) {
-    if (!(nh = nua_stack_incoming_handle(nua, irq, sip, 1)))
-      return 500;
-    created = 1;
-  }
-
-  if (nh->nh_ds->ds_has_referrals || NH_PGET(nh, refer_with_id))
-    event = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
-  else
-    event = sip_event_make(nh->nh_home, "refer");
-
-  if (event)
-    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, event);
+  nua_handle_t *nh = sr->sr_owner;
+  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
+  sip_t const *sip = sr->sr_request.sip;
+  sip_referred_by_t *by = sip->sip_referred_by, default_by[1];
+  sip_event_t const *o = sr->sr_usage->du_event;
+  enum nua_substate substate = nua_substate_terminated;
+  int initial = sr->sr_initial, retval;
 
-  if (!du || du->du_ready) {
-    if (du->du_ready) {
-      SU_DEBUG_1(("nua(%p): REFER with existing refer;id=%u\n", nh,
-		  sip->sip_cseq->cs_seq));
-    }
-    if (created) 
-      nh_destroy(nua, nh);
-    return 500;
+  if (nu) {
+    if (!sr->sr_terminating)
+      substate = nu->nu_substate;
   }
 
-  nu = nua_dialog_usage_private(du);
-  du->du_ready = 1;
-  nh->nh_ds->ds_has_referrals = 1;
-
-  nua_dialog_uas_route(nh, nh->nh_ds, sip, 1);	/* Set route and tags */
-
-  if (!sip->sip_referred_by) {
-    sip_from_t *a = sip->sip_from;
-
-    sip_referred_by_init(by = default_by);
+  if (by == NULL) {
+     by = sip_referred_by_init(default_by);
 
-    *by->b_url = *a->a_url;
-    by->b_display = a->a_display;
+    by->b_display = sip->sip_from->a_display;
+    *by->b_url = *sip->sip_from->a_url;
   }
 
-  response = nh_make_response(nua, nh, irq, 
-			      SIP_202_ACCEPTED, 
-			      NUTAG_ADD_CONTACT(1),
-			      TAG_END());
+  retval = nua_base_server_treport(sr,
+				   NUTAG_SUBSTATE(substate),
+				   NUTAG_REFER_EVENT(o),
+				   TAG_IF(by, SIPTAG_REFERRED_BY(by)),
+				   TAG_END());
 
-  nta_incoming_mreply(irq, response);
+  if (retval >= 2 || nu == NULL)
+    return retval;
 
-  expires = NH_PGET(nh, refer_expires);
-
-  if (sip->sip_expires && sip->sip_expires->ex_delta < expires)
-    expires = sip->sip_expires->ex_delta;
-  nu->nu_substate = nua_substate_pending;
-  nu->nu_expires = sip_now() + expires;
-
-  /* Immediate notify in order to establish the dialog */
-  if (!sip->sip_to->a_tag)
+  if (initial)
     nua_stack_post_signal(nh,
 			  nua_r_notify,
-			  SIPTAG_EVENT(event),
+			  SIPTAG_EVENT(o),
 			  SIPTAG_CONTENT_TYPE_STR("message/sipfrag"),
 			  SIPTAG_PAYLOAD_STR("SIP/2.0 100 Trying\r\n"),
 			  TAG_END());
-  
-  nua_stack_event(nh->nh_nua, nh, nta_incoming_getrequest(irq),
-		  nua_i_refer, SIP_202_ACCEPTED, 
-		  NUTAG_REFER_EVENT(event),
-		  TAG_IF(by, SIPTAG_REFERRED_BY(by)),
-		  TAG_END());
-  
-  su_free(nh->nh_home, event);
 
-  return 500;   
+  return retval;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c	Tue Mar 20 23:37:15 2007
@@ -45,9 +45,6 @@
 #include <sofia-sip/sip_protos.h>
 #include <sofia-sip/sip_status.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
-
 #include "nua_stack.h"
 
 /**@fn void nua_options(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
@@ -69,46 +66,6 @@
  * @sa #nua_i_options, @RFC3261 section 10
  */
 
-static int process_response_to_options(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip);
-
-int
-nua_stack_options(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
-{
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg;
-
-  if (nh_is_special(nh)) {
-    return UA_EVENT2(e, 900, "Invalid handle for OPTIONS");
-  }
-  else if (cr->cr_orq) {
-    return UA_EVENT2(e, 900, "Request already in progress");
-  }
-
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
-			 SIP_METHOD_OPTIONS, 
-			 TAG_NEXT(tags));
-
-  cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				    process_response_to_options, nh, NULL,
-				    msg,
-				    SIPTAG_END(), TAG_NEXT(tags));
-  if (!cr->cr_orq) {
-    msg_destroy(msg);
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-  }
-
-  return cr->cr_event = e;
-}
-
-void restart_options(nua_handle_t *nh, tagi_t *tags)
-{
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_options, tags);
-}
-
 /** @NUA_EVENT nua_r_options
  *
  * Answer to outgoing OPTIONS.
@@ -130,11 +87,25 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_options(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip)
-{
-  if (nua_creq_check_restart(nh, nh->nh_ds->ds_cr, orq, sip, restart_options))
-    return 0;
-  return nua_stack_process_response(nh, nh->nh_ds->ds_cr, orq, sip, TAG_END());
+static nua_client_methods_t const nua_options_client_methods = {
+  SIP_METHOD_OPTIONS,
+  0,
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 0,
+    /* target refresh */ 0
+  },
+  /*nua_options_client_template*/ NULL,
+  /*nua_options_client_init*/ NULL,
+  /*nua_options_client_request*/ NULL,
+  /* nua_options_client_check_restart */ NULL,
+  /*nua_options_client_response*/ NULL
+};
+
+int nua_stack_options(nua_t *nua,
+		      nua_handle_t *nh,
+		      nua_event_t e,
+		      tagi_t const *tags)
+{ 
+  return nua_client_create(nh, e, &nua_options_client_methods, tags);
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c	Tue Mar 20 23:37:15 2007
@@ -762,12 +762,6 @@
     else if (tag == nutag_enablemessenger) {
       NHP_SET(nhp, win_messenger_enable, value != 0);
     }
-#if 0
-    /* NUTAG_AUTORESPOND(autorespond) */
-    else if (tag == nutag_autorespond) {
-      NHP_SET(nhp, autorespond, value);
-    }
-#endif
     /* NUTAG_CALLEE_CAPS(callee_caps) */
     else if (tag == nutag_callee_caps) {
       NHP_SET(nhp, callee_caps, value != 0);
@@ -882,7 +876,8 @@
 			     sip_allow_class,
 			     &appl_method,
 			     (msg_list_t const *)nhp->nhp_appl_method,
-			     NHP_ISSET(nhp, allow), /* already set by tags */
+			     /* already set by tags? */
+			     NHP_ISSET(nhp, appl_method), 
 			     0, /* dup it, don't make */
 			     1, /* merge with old value */
 			     t->t_value);
@@ -1171,10 +1166,10 @@
 
   nh->nh_tags = 
     tl_filtered_tlist(nh->nh_home, tagfilter,
-		      SIPTAG_FROM(p_from),
-		      TAG_FILTER(nua_handle_tags_filter),
-		      SIPTAG_TO(p_to),
-		      TAG_FILTER(nua_handle_tags_filter),
+		      TAG_IF(p_from != SIP_NONE, SIPTAG_FROM(p_from)),
+		      TAG_IF(p_from != SIP_NONE, TAG_FILTER(nua_handle_tags_filter)),
+		      TAG_IF(p_to != SIP_NONE, SIPTAG_TO(p_to)),
+		      TAG_IF(p_to != SIP_NONE, TAG_FILTER(nua_handle_tags_filter)),
 		      TAG_NEXT(tags));
 
   nh->nh_ptags = 
@@ -1366,7 +1361,8 @@
  *               application contact associated with the operation handle 
  *               when responding to nua_get_hparams()
  * @param sip    NULL
- * @param tags   
+ * @param tags
+ *   NUTAG_APPL_METHOD() \n
  *   NUTAG_AUTOACK() \n
  *   NUTAG_AUTOALERT() \n
  *   NUTAG_AUTOANSWER() \n
@@ -1559,6 +1555,7 @@
      TIF_STR(SIPTAG_SUPPORTED_STR, supported),
      TIF(SIPTAG_ALLOW, allow),
      TIF_STR(SIPTAG_ALLOW_STR, allow),
+     TIF_STR(NUTAG_APPL_METHOD, appl_method),
      TIF(SIPTAG_ALLOW_EVENTS, allow_events),
      TIF_STR(SIPTAG_ALLOW_EVENTS_STR, allow_events),
      TIF_SIP(SIPTAG_USER_AGENT, user_agent),
@@ -1607,7 +1604,7 @@
 
      TAG_NEXT(media_params));
 
-  nua_stack_event(nua, nh, NULL, nua_r_get_params, SIP_200_OK, TAG_NEXT(lst));
+  nua_stack_event(nua, nh, NULL, nua_r_get_params, SIP_200_OK, lst);
 
   su_home_deinit(tmphome);
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c	Tue Mar 20 23:37:15 2007
@@ -45,9 +45,6 @@
 #include <sofia-sip/sip_protos.h>
 #include <sofia-sip/sip_status.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
-
 #include "nua_stack.h"
 
 /* ====================================================================== */
@@ -55,6 +52,7 @@
 
 struct publish_usage {
   sip_etag_t *pu_etag;
+  int pu_published;
 };
 
 static char const *nua_publish_usage_name(nua_dialog_usage_t const *du);
@@ -116,14 +114,6 @@
 /* ======================================================================== */
 /* PUBLISH */
 
-static int nua_stack_publish2(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-			      int refresh, tagi_t const *tags);
-
-static int process_response_to_publish(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip);
-
-
 /**@fn \
  * void nua_publish(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
  *
@@ -237,213 +227,213 @@
  * @END_NUA_EVENT
  */
 
-int nua_stack_publish(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		      tagi_t const *tags)
-{
-  return nua_stack_publish2(nua, nh, e, 0, tags);
+static int nua_publish_client_template(nua_client_request_t *cr,
+				       msg_t **return_msg,
+				       tagi_t const *tags);
+static int nua_publish_client_init(nua_client_request_t *cr,
+				   msg_t *, sip_t *,
+				   tagi_t const *tags);
+static int nua_publish_client_request(nua_client_request_t *cr,
+				      msg_t *, sip_t *,
+				      tagi_t const *tags);
+static int nua_publish_client_response(nua_client_request_t *cr,
+				       int status, char const *phrase,
+				       sip_t const *sip);
+
+static nua_client_methods_t const nua_publish_client_methods = {
+  SIP_METHOD_PUBLISH,
+  0,
+  {
+    /* create_dialog */ 0,
+    /* in_dialog */ 0,
+    /* target refresh */ 0
+  },
+  nua_publish_client_template,
+  nua_publish_client_init,
+  nua_publish_client_request,
+  /* nua_publish_client_check_restart */ NULL,
+  nua_publish_client_response,
+  /* nua_publish_client_preliminary */ NULL
+};
+
+/**@internal Send PUBLISH. */
+int nua_stack_publish(nua_t *nua,
+		     nua_handle_t *nh,
+		     nua_event_t e,
+		     tagi_t const *tags)
+{
+  return nua_client_create(nh, e, &nua_publish_client_methods, tags);
 }
 
-static
-int nua_stack_publish2(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		       int refresh,
-		       tagi_t const *tags)
+static int nua_publish_client_template(nua_client_request_t *cr,
+				       msg_t **return_msg,
+				       tagi_t const *tags)
 {
   nua_dialog_usage_t *du;
-  struct publish_usage *pu;
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg = NULL;
-  sip_t *sip;
-  int remove_body = 0;
 
-  if (nua_stack_set_handle_special(nh, nh_has_nothing, nua_r_publish) < 0)
-    return UA_EVENT2(e, 900, "Invalid handle for PUBLISH");
+  if (cr->cr_event == nua_r_publish)
+    return 0;
 
-  if (cr->cr_orq) {
-    return UA_EVENT2(e, 900, "Request already in progress");
+  du = nua_dialog_usage_get(cr->cr_owner->nh_ds, nua_publish_usage, NULL);
+  if (du && du->du_cr) {
+    if (nua_client_set_target(cr, du->du_cr->cr_target) < 0)
+      return -1;
+    *return_msg = msg_copy(du->du_cr->cr_msg);
+    return 1;
   }
 
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
+  return 0;
+}
 
-  if (e == nua_r_unpublish) {
-    du = nua_dialog_usage_get(nh->nh_ds, nua_publish_usage, NULL);
-    if (du)
-      refresh = 1;
-    else
-      du = nua_dialog_usage_add(nh, nh->nh_ds, nua_publish_usage, NULL);
-  }
-  else if (!refresh)
+static int nua_publish_client_init(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;
+  struct publish_usage *pu;
+
+  if (cr->cr_event == nua_r_publish) {
     du = nua_dialog_usage_add(nh, nh->nh_ds, nua_publish_usage, NULL);
+    if (!du)
+      return -1;
+    pu = nua_dialog_usage_private(du);
+    pu->pu_published = 0;
+    if (sip->sip_if_match) {
+      pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_if_match);
+      if (!pu->pu_etag)
+	return -1;
+      sip_header_remove(msg, sip, (sip_header_t *)sip->sip_if_match);
+    }
+  }
   else
     du = nua_dialog_usage_get(nh->nh_ds, nua_publish_usage, NULL);
 
-  if (!du)
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-
-  nua_dialog_usage_reset_refresh(du);
-  pu = nua_dialog_usage_private(du); assert(pu);
-
-  if (refresh) {
-    if (cr->cr_msg)
-      msg_destroy(cr->cr_msg);
-    cr->cr_msg = msg_copy(du->du_msg);
-    remove_body = pu->pu_etag != NULL;
-  }
-
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count || refresh,
-		     SIP_METHOD_PUBLISH,
-		     NUTAG_ADD_CONTACT(0),
-		     TAG_NEXT(tags));
-  sip = sip_object(msg);
-
-  if (!msg || !sip) 
-    goto error;
-
-  du->du_terminating =
-    e != nua_r_publish ||
-    (sip->sip_expires && sip->sip_expires->ex_delta == 0);
-
-  if (!du->du_terminating && !refresh) {
-    /* Save template */
-    if (du->du_msg)
-      msg_destroy(du->du_msg);
-    du->du_msg = msg_ref_create(cr->cr_msg);
-  }
-
-  cr->cr_orq =
-    nta_outgoing_mcreate(nua->nua_nta,
-			 process_response_to_publish, nh, NULL,
-			 msg,
-			 SIPTAG_IF_MATCH(pu->pu_etag),
-			 TAG_IF(remove_body, SIPTAG_PAYLOAD(NONE)),
-			 TAG_IF(remove_body, SIPTAG_CONTENT_TYPE(NONE)),
-			 TAG_IF(e != nua_r_publish,
-				SIPTAG_EXPIRES_STR("0")),
-			 SIPTAG_END(), TAG_NEXT(tags));
-  if (!cr->cr_orq)
-    goto error;
-
   cr->cr_usage = du;
 
-  return cr->cr_event = e;
-
- error:
-  msg_destroy(msg);
-  if (!du->du_ready == 0)
-    nua_dialog_usage_remove(nh, nh->nh_ds, du);
-  return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-}
-
-
-static void
-restart_publish(nua_handle_t *nh, tagi_t *tags)
-{
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_publish, tags);
+  return 0;
 }
 
-
 static
-int process_response_to_publish(nua_handle_t *nh,
-				nta_outgoing_t *orq,
-				sip_t const *sip)
+int nua_publish_client_request(nua_client_request_t *cr,
+			       msg_t *msg, sip_t *sip,
+			       tagi_t const *tags)
 {
-  int status = sip->sip_status->st_status;
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
   nua_dialog_usage_t *du = cr->cr_usage;
-  struct publish_usage *pu = nua_dialog_usage_private(du);
-  unsigned saved_retry_count = cr->cr_retry_count + 1;
+  int un, done;
+  sip_etag_t const *etag = NULL;
 
-  if (nua_creq_check_restart(nh, cr, orq, sip, restart_publish))
-    return 0;
+  un = cr->cr_terminating ||
+    cr->cr_event != nua_r_publish ||
+    (du && du->du_shutdown) ||
+    (sip->sip_expires && sip->sip_expires->ex_delta == 0);
+  cr->cr_terminating = un;
+  done = un;
 
-  if (status < 200 || pu == NULL)
-    return nua_stack_process_response(nh, cr, orq, sip, TAG_END());
+  if (du) {
+    struct publish_usage *pu = nua_dialog_usage_private(du);
 
-  if (pu->pu_etag)
-    su_free(nh->nh_home, pu->pu_etag), pu->pu_etag = NULL;
+    if (nua_client_bind(cr, du) < 0)
+      return -1;
+    if (pu->pu_published)
+      done = 1;
+    etag = pu->pu_etag;
+  }
 
-  if (!du->du_terminating) {
-    int retry = 0, invalid_expiration = 0;
+  return nua_base_client_trequest(cr, msg, sip,
+				  SIPTAG_IF_MATCH(etag),
+				  TAG_IF(done, SIPTAG_PAYLOAD(NONE)),
+				  TAG_IF(done, SIPTAG_CONTENT_TYPE(NONE)),
+				  TAG_IF(un, SIPTAG_EXPIRES_STR("0")),
+				  TAG_NEXT(tags));
+}
 
-    if (status < 300) {
-      if (!sip->sip_expires)
-	invalid_expiration = 1;
-      else if (sip->sip_expires->ex_delta == 0)
-	retry = 1, invalid_expiration = 1;
+static int nua_publish_client_response(nua_client_request_t *cr,
+				       int status, char const *phrase,
+				       sip_t const *sip)
+{
+  nua_handle_t *nh = cr->cr_owner;
+  nua_dialog_usage_t *du = cr->cr_usage;
+
+  if (!cr->cr_terminated && du && sip) {
+    struct publish_usage *pu = nua_dialog_usage_private(du);
+    sip_expires_t const *ex = sip->sip_expires;
+
+    /* Reset state */
+    pu->pu_published = 0;
+    if (pu->pu_etag)
+      su_free(nh->nh_home, pu->pu_etag), pu->pu_etag = NULL;
+
+    if (status == 412) {
+      if (nua_client_restart(cr, 100, phrase))
+	return 0;
     }
-    else if (status == 412)
-      retry = 1;
+    else if (status < 300) {
+      if (ex && ex->ex_delta == 0 &&
+	  nua_client_restart(cr, 100, "Trying re-PUBLISH"))
+	return 0;
 
-    if (status < 300 && !invalid_expiration && !retry) {
+      pu->pu_published = 1;
       pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_etag);
-      du->du_ready = 1;
-      nua_dialog_usage_set_expires(du, sip->sip_expires->ex_delta);
-    }
-    else if (retry && saved_retry_count < NH_PGET(nh, retry_count)) {
-      msg_t *response = nta_outgoing_getresponse(orq);
-      nua_stack_event(nh->nh_nua, nh, response, cr->cr_event,
-      		100, "Trying re-PUBLISH",
-      		TAG_END());
-      nua_creq_deinit(cr, orq);
-      nua_stack_publish2(nh->nh_nua, nh, cr->cr_event, 1, NULL);
-      cr->cr_retry_count = saved_retry_count;
-      return 0;
-    }
-    else if (invalid_expiration) {
-      msg_t *response = nta_outgoing_getresponse(orq);
-      nua_stack_event(nh->nh_nua, nh, response, cr->cr_event,
-      		900, "Received Invalid Expiration Time",
-      		TAG_END());
-      nua_dialog_usage_remove(nh, nh->nh_ds, cr->cr_usage);
-      nua_creq_deinit(cr, orq);
-      cr->cr_usage = NULL;
-      return 0;
+
+      if (!ex || ex->ex_delta == 0 || !pu->pu_etag) {
+	cr->cr_terminated = 1;
+
+	if (!ex || ex->ex_delta == 0)
+	  SET_STATUS(900, "Received Invalid Expiration Time");
+	else
+	  SET_STATUS1(NUA_INTERNAL_ERROR);
+      }
     }
   }
 
-  return nua_stack_process_response(nh, cr, orq, sip, TAG_END());
+  return nua_base_client_response(cr, status, phrase, sip, NULL);
 }
 
-
 static void nua_publish_usage_refresh(nua_handle_t *nh,
-				      nua_dialog_state_t *ds,
-				      nua_dialog_usage_t *du,
-				      sip_time_t now)
-{
-  if (ds->ds_cr->cr_usage == du) /* Already publishing. */
-    return;
-  nua_stack_publish2(nh->nh_nua, nh, nua_r_publish, 1, NULL);
+				     nua_dialog_state_t *ds,
+				     nua_dialog_usage_t *du,
+				     sip_time_t now)
+{
+  nua_client_request_t *cr = du->du_cr;
+
+  if (cr) {
+    if (nua_client_resend_request(cr, 0) >= 0)
+      return;
+  }
+
+  nua_stack_event(nh->nh_nua, nh, NULL,
+		  nua_r_publish, NUA_INTERNAL_ERROR,
+		  NULL);
+
+  nua_dialog_usage_remove(nh, ds, du);
 }
 
-/** @interal Shut down PUBLISH usage. 
+/** @interal Shut down PUBLISH usage.
  *
  * @retval >0  shutdown done
  * @retval 0   shutdown in progress
  * @retval <0  try again later
  */
 static int nua_publish_usage_shutdown(nua_handle_t *nh,
-				      nua_dialog_state_t *ds,
-				      nua_dialog_usage_t *du)
+				     nua_dialog_state_t *ds,
+				     nua_dialog_usage_t *du)
 {
-  nua_client_request_t *cr = ds->ds_cr;
+  nua_client_request_t *cr = du->du_cr;
 
-  if (!cr->cr_usage) {
-    /* Unpublish */
-    nua_stack_publish2(nh->nh_nua, nh, nua_r_destroy, 1, NULL);
-    return cr->cr_usage != du;
+  if (cr) {
+    if (nua_client_resend_request(cr, 1) >= 0)
+      return 0;
   }
 
-  if (!du->du_ready && !cr->cr_orq)
-    return 1;			/* had unauthenticated initial request */
-
-  return -1;  /* Request in progress */
+  /* XXX - report to user */
+  nua_dialog_usage_remove(nh, ds, du);
+  return 200;
 }
 
 /* ---------------------------------------------------------------------- */
 /* Server side */
 
-static
-int respond_to_publish(nua_server_request_t *sr, tagi_t const *tags);
-
 /** @NUA_EVENT nua_i_publish
  *
  * Incoming PUBLISH request.
@@ -481,48 +471,35 @@
  * @END_NUA_EVENT
  */
 
-int nua_stack_process_publish(nua_t *nua,
-			      nua_handle_t *nh,
-			      nta_incoming_t *irq,
-			      sip_t const *sip)
-{
-  nua_server_request_t *sr, sr0[1];
-  sip_allow_events_t *allow_events = NUA_PGET(nua, nh, allow_events);
-  sip_event_t *o = sip->sip_event;
+int nua_publish_server_init(nua_server_request_t *sr);
+
+nua_server_methods_t const nua_publish_server_methods = 
+  {
+    SIP_METHOD_PUBLISH,
+    nua_i_publish,		/* Event */
+    { 
+      0,			/* Do not create dialog */
+      0,			/* Initial request */
+      0,			/* Not a target refresh request  */
+      1,			/* Add Contact */
+    },
+    nua_publish_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_base_server_respond,
+    nua_base_server_report,
+  };
+
+int nua_publish_server_init(nua_server_request_t *sr)
+{
+  sip_allow_events_t *allow_events = NH_PGET(sr->sr_owner, allow_events);
+  sip_event_t *o = sr->sr_request.sip->sip_event;
   char const *event = o ? o->o_type : NULL;
   
-  sr = SR_INIT(sr0);
-  
   if (!allow_events)
-    SR_STATUS1(sr, SIP_501_NOT_IMPLEMENTED);
+    return SR_STATUS1(sr, SIP_501_NOT_IMPLEMENTED);
   else if (!event || !msg_header_find_param(allow_events->k_common, event))
-    SR_STATUS1(sr, SIP_489_BAD_EVENT);
-
-  sr = nua_server_request(nua, nh, irq, sip, sr, sizeof *sr,
-			  respond_to_publish, 0);
+    return SR_STATUS1(sr, SIP_489_BAD_EVENT);
 
-  return nua_stack_server_event(nua, sr, nua_i_publish, TAG_END());
-}
-
-static
-int respond_to_publish(nua_server_request_t *sr, tagi_t const *tags)
-{
-  nua_handle_t *nh = sr->sr_owner;
-  nua_t *nua = nh->nh_nua;
-  msg_t *msg;
-
-  msg = nua_server_response(sr, sr->sr_status, sr->sr_phrase, TAG_NEXT(tags));
-
-  if (msg) {
-    nta_incoming_mreply(sr->sr_irq, msg);
-  }
-  else {
-    SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
-    nta_incoming_treply(sr->sr_irq, sr->sr_status, sr->sr_phrase, TAG_END());
-    nua_stack_event(nua, nh, NULL,
-		    nua_i_error, 900, "PUBLISH Response Fails",
-		    TAG_END());
-  }
-  
-  return sr->sr_status >= 200 ? sr->sr_status : 0;
+  return 0;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c	Tue Mar 20 23:37:15 2007
@@ -27,6 +27,7 @@
  *
  * @author Pekka Pessi <Pekka.Pessi at nokia.com>
  * @author Martti Mela <Martti.Mela at nokia.com>
+ * @author Kai Vehmanen <Kai.Vehmanen at nokia.com>
  *
  * @date Created: Wed Mar  8 11:48:49 EET 2006 ppessi
  */
@@ -45,8 +46,6 @@
 #include <sofia-sip/sip_util.h>
 #include <sofia-sip/sip_status.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
 #define NTA_UPDATE_MAGIC_T   struct nua_s
 
 #include "nua_stack.h"
@@ -120,8 +119,11 @@
   nua_registration_t *nr_next, **nr_prev, **nr_list; /* Doubly linked list and its head */
   sip_from_t *nr_aor;		/**< AoR for this registration, NULL if none */
   sip_contact_t *nr_contact;	/**< Our Contact */
+  sip_contact_t nr_dcontact[1];	/**< Contact in dialog */
   sip_via_t *nr_via;		/**< Corresponding Via headers */
 
+  unsigned long nr_min_expires;	/**< Value from 423 negotiation */
+
   /** Status of registration */
   unsigned nr_ready:1;
   /** Kind of registration.
@@ -220,25 +222,16 @@
 /* ======================================================================== */
 /* REGISTER */
 
-static void restart_register(nua_handle_t *nh, tagi_t *tags);
-
-static int process_response_to_register(nua_handle_t *nh,
-					nta_outgoing_t *orq,
-					sip_t const *sip);
-
-static void unregister_expires_contacts(msg_t *msg, sip_t *sip);
-
 /* Interface towards outbound_t */
 sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh,
 					 su_home_t *home,
+					 int in_dialog,
 					 char const *extra_username,
 					 sip_via_t const *v,
 					 char const *transport,
 					 char const *m_param,
 					 ...);
 
-static int nua_stack_outbound_features(nua_handle_t *nh, outbound_t *ob);
-
 static int nua_stack_outbound_refresh(nua_handle_t *,
 				      outbound_t *ob);
 
@@ -440,6 +433,11 @@
  * the desired transport-layer keepalive interval for stream-based
  * transports like TLS and TCP.
  *
+ * As alternative to OPTIONS/STUN keepalives, the client can propose
+ * a more frequent registration refresh interval with
+ * NUTAG_M_FEATURES() (e.g. NUTAG_M_FEATURES("expires=120") given as 
+ * parameter to nua_register()).
+ * 
  * @sa #nua_r_register, nua_unregister(), #nua_r_unregister, 
  * #nua_i_register,
  * @RFC3261 section 10,
@@ -537,384 +535,351 @@
  * @END_NUA_EVENT
  */
 
-int
-nua_stack_register(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		   tagi_t const *tags)
+static int nua_register_client_template(nua_client_request_t *cr,
+					msg_t **return_msg,
+					tagi_t const *tags);
+static int nua_register_client_init(nua_client_request_t *cr,
+				    msg_t *, sip_t *,
+				    tagi_t const *tags);
+static int nua_register_client_request(nua_client_request_t *cr,
+				       msg_t *, sip_t *,
+				       tagi_t const *tags);
+static int nua_register_client_check_restart(nua_client_request_t *cr,
+					     int status, char const *phrase,
+					     sip_t const *sip);
+static int nua_register_client_response(nua_client_request_t *cr,
+					int status, char const *phrase,
+					sip_t const *sip);
+
+static nua_client_methods_t const nua_register_client_methods = {
+  SIP_METHOD_REGISTER,
+  0,
+  {
+    /* create_dialog */ 1,
+    /* in_dialog */ 0,
+    /* target refresh */ 0
+  },
+  nua_register_client_template,
+  nua_register_client_init,
+  nua_register_client_request,
+  nua_register_client_check_restart,
+  nua_register_client_response
+};
+
+/**@internal Send REGISTER. */
+int nua_stack_register(nua_t *nua,
+		       nua_handle_t *nh,
+		       nua_event_t e,
+		       tagi_t const *tags)
+{
+  return nua_client_create(nh, e, &nua_register_client_methods, tags);
+}
+
+static int nua_register_client_template(nua_client_request_t *cr,
+					msg_t **return_msg,
+					tagi_t const *tags)
 {
   nua_dialog_usage_t *du;
-  nua_registration_t *nr = NULL;
-  outbound_t *ob = NULL;
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg = NULL;
-  sip_t *sip;
-  int terminating = e != nua_r_register;
-
-  if (nua_stack_set_handle_special(nh, nh_has_register, nua_r_register) < 0)
-    return UA_EVENT2(e, 900, "Invalid handle for REGISTER");
-  if (cr->cr_orq)
-    return UA_EVENT2(e, 900, "Request already in progress");
 
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
+  if (cr->cr_event == nua_r_register)
+    return 0;
+
+  /* Use a copy of REGISTER message as the template for un-REGISTER */
+  du = nua_dialog_usage_get(cr->cr_owner->nh_ds, nua_register_usage, NULL);
+  if (du && du->du_cr) {
+    if (nua_client_set_target(cr, du->du_cr->cr_target) < 0)
+      return -1;
+    *return_msg = msg_copy(du->du_cr->cr_msg);
+    return 1;
+  }
+
+  return 0;
+}
+
+static int nua_register_client_init(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;
+  nua_registration_t *nr;
+  sip_to_t const *aor = sip->sip_to;
+
+  int unreg;
+
+  /* Explicit empty (NULL) contact - used for CPL store/remove? */
+  if (!sip->sip_contact && cr->cr_has_contact)
+    /* Do not create any usage */
+    return 0;
+
+  unreg = cr->cr_event != nua_r_register ||
+    (sip->sip_expires && sip->sip_expires->ex_delta == 0);
+  if (unreg)
+    nua_client_terminating(cr);
 
   du = nua_dialog_usage_add(nh, nh->nh_ds, nua_register_usage, NULL);
-  if (!du)
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-  nr = nua_dialog_usage_private(du); assert(nr);
+  if (du == NULL)
+    return -1;
+  nr = nua_dialog_usage_private(du);
+
   nua_registration_add(&nh->nh_nua->nua_registrations, nr);
-  if (!terminating && du->du_terminating)
-    return UA_EVENT2(e, 900, "Unregister in progress");
 
-  if (cr->cr_msg)
-    msg_destroy(cr->cr_msg), cr->cr_msg = NULL;
-  /* Use original message as template when unregistering */
-  if (terminating)		
-    cr->cr_msg = msg_ref_create(du->du_msg);
+  if (nua_client_bind(cr, du) < 0)
+    return -1;
 
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_msg != NULL,
-		     SIP_METHOD_REGISTER,
-		     TAG_IF(!terminating, NUTAG_USE_DIALOG(1)),
-		     TAG_NEXT(tags));
-  sip = sip_object(msg);
-  if (!msg || !sip)
-    goto error;
+  if (aor == NULL)
+    aor = sip->sip_from;
+  if (aor == NULL)
+    aor = nh->nh_nua->nua_from;
 
-  if (!nr->nr_aor) {
-    if (nua_registration_set_aor(nh->nh_home, nr, sip->sip_to) < 0)
-      goto error;
-  }
+  if (nua_registration_set_aor(nh->nh_home, nr, aor) < 0)
+    return -1;
 
-  if (terminating)
-    /* Add Expires: 0 and remove the expire parameters from contacts */
-    unregister_expires_contacts(msg, sip);
+  if (nua_registration_set_contact(nh, nr, sip->sip_contact, unreg) < 0)
+    return -1;
 
-  if (!sip->sip_contact && cr->cr_has_contact) {
-    terminating = 1;
+  if (!nr->nr_ob && (NH_PGET(nh, outbound) || NH_PGET(nh, instance))) {
+    nr->nr_ob = outbound_new(nh, &nua_stack_outbound_callbacks,
+			     nh->nh_nua->nua_root,
+			     nh->nh_nua->nua_nta,
+			     NH_PGET(nh, instance));
+    if (!nr->nr_ob)
+      return nua_client_return(cr, 900, "Cannot create outbound", msg);
   }
-  else if (nua_registration_set_contact(nh, nr, sip->sip_contact, terminating)
-	   < 0)
-    goto error;
 
-  du->du_terminating = terminating;
-
-  if (du->du_msg == NULL && !terminating)
-    du->du_msg = msg_ref_create(cr->cr_msg); /* Save original message */
+  if (nr->nr_ob) {
+    outbound_t *ob = nr->nr_ob;
+    sip_contact_t *m;
 
-  ob = nr->nr_ob;
-  
-  if (!ob && (NH_PGET(nh, outbound) || NH_PGET(nh, instance))) {
-    nr->nr_ob = ob = outbound_new(nh, &nua_stack_outbound_callbacks,
-				  nh->nh_nua->nua_root,
-				  nh->nh_nua->nua_nta,
-				  NH_PGET(nh, instance));
-    if (!ob)
-      goto error;
-  }
+    if (!unreg && sip->sip_contact) {
+      for (m = sip->sip_contact; m; m = m->m_next)
+	if (!m->m_expires || strtoul(m->m_expires, NULL, 10) != 0)
+	  break;
+      
+      if (m == NULL)
+	unreg = 1;	/* All contacts have expires=0 */
+    }
 
-  if (ob) {
     outbound_set_options(ob,
 			 NH_PGET(nh, outbound),
 			 NH_PGET(nh, keepalive),
 			 NH_PISSET(nh, keepalive_stream)
 			 ? NH_PGET(nh, keepalive_stream)
 			 : NH_PGET(nh, keepalive));
-    nua_stack_outbound_features(nh, ob);
-    outbound_stop_keepalive(ob);
 
-    if (outbound_set_contact(ob, sip->sip_contact, nr->nr_via, terminating) < 0)
-      goto error;
+    if (outbound_set_contact(ob, sip->sip_contact, nr->nr_via, unreg) < 0)
+      return nua_client_return(cr, 900, "Cannot set outbound contact", msg);
   }
 
-  /* This calls nta_outgoing_mcreate() but adds a few tags */
-  cr->cr_orq =
-    outbound_register_request(ob, terminating,
-			      nr->nr_by_stack ? nr->nr_contact : NULL,
-			      nua->nua_nta,
-			      process_response_to_register, nh, NULL,
-			      msg,
-			      SIPTAG_END(), 
-			      TAG_IF(terminating, NTATAG_SIGCOMP_CLOSE(1)),
-			      TAG_IF(!terminating, NTATAG_COMP("sigcomp")),
-			      TAG_NEXT(tags));
-
-  if (!cr->cr_orq)
-    goto error;
-
-  cr->cr_usage = du;
-  return cr->cr_event = e;
-
- error:
-  msg_destroy(msg);
-  msg_destroy(cr->cr_msg), cr->cr_msg = NULL;
-  nua_dialog_usage_remove(nh, nh->nh_ds, du);    
-  return UA_EVENT1(e, NUA_INTERNAL_ERROR);
+  return 0;
 }
 
-static void
-restart_register(nua_handle_t *nh, tagi_t *tags)
+static
+int nua_register_client_request(nua_client_request_t *cr,
+				msg_t *msg, sip_t *sip,
+				tagi_t const *tags)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg;
+  nua_handle_t *nh = cr->cr_owner;
   nua_dialog_usage_t *du = cr->cr_usage;
-  nua_registration_t *nr = nua_dialog_usage_private(du);
-  int terminating = du && du->du_terminating;
-
-  cr->cr_restart = NULL;
-
-  if (!cr->cr_msg)
-    return;
-
-  msg = nua_creq_msg(nh->nh_nua, nh, cr, 1,
-		     SIP_METHOD_UNKNOWN,
-		     TAG_NEXT(tags));
+  nua_registration_t *nr;
+  sip_contact_t *m, *contacts = sip->sip_contact;
+  char const *min_expires = NULL;
+  int unreg;
+
+  (void)nh;
+
+  /* Explicit empty (NULL) contact - used for CPL store/remove? */
+  if (!contacts && cr->cr_has_contact)
+    return nua_base_client_request(cr, msg, sip, tags);
+
+  if ((du && du->du_shutdown) ||
+      (sip->sip_expires && sip->sip_expires->ex_delta == 0))
+    nua_client_terminating(cr);
+
+  if (contacts) {
+    if (!cr->cr_terminating) {
+      for (m = contacts; m; m = m->m_next)
+	if (!m->m_expires || strtoul(m->m_expires, NULL, 10) != 0)
+	  break;
+      /* All contacts have expires=0 */
+      if (m == NULL)
+	nua_client_terminating(cr);
+    }
+  }
 
-  if (!msg)
-    return;			/* XXX - Uh-oh */
+  unreg = cr->cr_terminating;
 
-  if (terminating)
-    unregister_expires_contacts(msg, sip_object(msg));
+  nr = nua_dialog_usage_private(du);
 
-  /* This calls nta_outgoing_mcreate() but adds a few tags */
-  cr->cr_orq =
-    outbound_register_request(nr->nr_ob, terminating,
-			      nr->nr_by_stack ? nr->nr_contact : NULL,
-			      nh->nh_nua->nua_nta,
-			      process_response_to_register, nh, NULL,
-			      msg,
-			      SIPTAG_END(), 
-			      TAG_IF(terminating, NTATAG_SIGCOMP_CLOSE(1)),
-			      TAG_IF(!terminating, NTATAG_COMP("sigcomp")),
-			      TAG_NEXT(tags));
+  if (nr) {
+    if (nr->nr_ob) {
+      outbound_stop_keepalive(nr->nr_ob);
+      outbound_start_registering(nr->nr_ob);
+    }
 
-  if (!cr->cr_orq)
-    msg_destroy(msg);
-}
+    if (nr->nr_by_stack) {
+      sip_contact_t *m = nr->nr_contact, *previous = NULL;
 
-/** Refresh registration */
-static
-void nua_register_usage_refresh(nua_handle_t *nh,
-				nua_dialog_state_t *ds,
-				nua_dialog_usage_t *du,
-				sip_time_t now)
-{
-  nua_t *nua = nh->nh_nua;
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  nua_registration_t *nr = nua_dialog_usage_private(du);
-  msg_t *msg;
-  sip_t *sip;
+      outbound_get_contacts(nr->nr_ob, &m, &previous);
 
-  if (du->du_terminating || du->du_shutdown)
-    return;
+      sip_add_dup(msg, sip, (sip_header_t *)m);
+      /* previous is an outdated contact generated by stack 
+       * and it is now unregistered */
+      if (previous)
+	sip_add_dup(msg, sip, (sip_header_t *)previous);
+    }
+  }
 
-  if (cr->cr_msg) {
-    /* Dialog is busy, delay of 5 .. 15 seconds */
-    nua_dialog_usage_refresh_range(du, 5, 15);
-    return;
-  }
-
-  outbound_stop_keepalive(nr->nr_ob);
-
-  cr->cr_msg = msg_copy(du->du_msg);
-  msg = nua_creq_msg(nua, nh, cr, 1,
-		     SIP_METHOD_REGISTER,
-		     NUTAG_USE_DIALOG(1),
-		     TAG_END());
-  sip = sip_object(msg);
-  if (!msg || !sip)
-    goto error;
-
-  cr->cr_orq =
-    outbound_register_request(nr->nr_ob, 0,
-			      nr->nr_by_stack ? nr->nr_contact : NULL,
-			      nh->nh_nua->nua_nta,
-			      process_response_to_register, nh, NULL,
-			      msg,
-			      SIPTAG_END(), 
-			      NTATAG_COMP("sigcomp"),
-			      TAG_END());
-  if (!cr->cr_orq)
-    goto error;
+  for (m = sip->sip_contact; m; m = m->m_next) {
+    if (m->m_url->url_type == url_any) {
+      /* If there is a '*' in contact list, remove everything else */
+      while (m != sip->sip_contact)
+	sip_header_remove(msg, sip, (sip_header_t *)sip->sip_contact);
+      while (m->m_next)
+	sip_header_remove(msg, sip, (sip_header_t *)m->m_next);
+      contacts = m;
+      break;
+    }
 
-  cr->cr_usage = du;
-  cr->cr_event = nua_r_register;
-  return;
+    if (!m->m_expires)
+      continue;
+    if (unreg) {
+      /* Remove the expire parameters from contacts */
+      msg_header_remove_param(m->m_common, "expires");
+    }
+    else if (nr && nr->nr_min_expires && 
+	     strtoul(m->m_expires, 0, 10) < nr->nr_min_expires) {
+      if (min_expires == NULL) 
+	min_expires = su_sprintf(msg_home(msg), "expires=%lu", 
+				 nr->nr_min_expires);
+      msg_header_replace_param(msg_home(msg), m->m_common, min_expires);
+    }
+  }
 
- error:
-  msg_destroy(msg);
-  msg_destroy(cr->cr_msg);
-  UA_EVENT2(nua_r_register, NUA_INTERNAL_ERROR, TAG_END());
-  return;
+  return nua_base_client_trequest(cr, msg, sip,
+				  TAG_IF(unreg, SIPTAG_EXPIRES_STR("0")),
+#if 0
+				  TAG_IF(unreg, NTATAG_SIGCOMP_CLOSE(1)),
+				  TAG_IF(!unreg, NTATAG_COMP("sigcomp")),
+#endif
+				  TAG_NEXT(tags));
 }
 
-/** Shutdown register usage. 
- *
- * Called when stack is shut down or handle is destroyed. Unregister.
- */
-static
-int nua_register_usage_shutdown(nua_handle_t *nh, 
-				nua_dialog_state_t *ds,
-				nua_dialog_usage_t *du)
+static int nua_register_client_check_restart(nua_client_request_t *cr,
+					     int status, char const *phrase,
+					     sip_t const *sip)
 {
-  nua_t *nua = nh->nh_nua;
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  nua_registration_t *nr = nua_dialog_usage_private(du);
-  msg_t *msg;
-  sip_t *sip;
+  nua_registration_t *nr = nua_dialog_usage_private(cr->cr_usage);
+  unsigned short retry_count = cr->cr_retry_count;
+  int restart = 0, retry;
 
-  if (du->du_terminating)	/* Already terminating? */
-    return 100;
+  if (nr && nr->nr_ob) {
+    msg_t *_reqmsg = nta_outgoing_getrequest(cr->cr_orq);
+    sip_t *req = sip_object(_reqmsg); msg_destroy(_reqmsg);
 
-  du->du_terminating = 1;
+    retry = outbound_register_response(nr->nr_ob, cr->cr_terminating,
+				       req, sip);
 
-  if (cr->cr_msg)    /* Busy */
-    return 100;
-
-  outbound_stop_keepalive(nr->nr_ob);
-
-  cr->cr_msg = msg_copy(du->du_msg);
-  msg = nua_creq_msg(nua, nh, cr, 1,
-		     SIP_METHOD_REGISTER,
-		     NUTAG_USE_DIALOG(1),
-		     TAG_END());
-  sip = sip_object(msg);
-  if (!msg || !sip)
-    goto error;
-
-  unregister_expires_contacts(msg, sip);
-
-  cr->cr_orq =
-    outbound_register_request(nr->nr_ob, 1,
-			      nr->nr_by_stack ? nr->nr_contact : NULL,
-			      nh->nh_nua->nua_nta,
-			      process_response_to_register, nh, NULL,
-			      msg,
-			      SIPTAG_END(), 
-			      NTATAG_SIGCOMP_CLOSE(1),
-			      TAG_END());
-  if (!cr->cr_orq)
-    goto error;
+    restart = retry >= ob_reregister_now;
+    
+    if (retry == ob_reregister)
+      /* outbound restarts REGISTER later */;
 
-  cr->cr_usage = du;
-  cr->cr_event = nua_r_destroy;
-  return 200;
+    if (retry < 0)
+      /* XXX - report an error? */;
+  }
 
- error:
-  nua_dialog_usage_remove(nh, nh->nh_ds, du);
-  msg_destroy(msg);
-  msg_destroy(cr->cr_msg);
-  return 500;
-}
+  if (nr && status == 423) {
+    if (sip->sip_min_expires)
+      nr->nr_min_expires = sip->sip_min_expires->me_delta;
+  }
 
+  /* Check for status-specific reasons to retry */
+  if (nua_base_client_check_restart(cr, status, phrase, sip))
+    return 1;
 
-static
-int process_response_to_register(nua_handle_t *nh,
-				 nta_outgoing_t *orq,
-				 sip_t const *sip)
+  /* Restart only if nua_base_client_check_restart() did not try to restart */
+  if (restart && retry_count == cr->cr_retry_count)
+    return nua_client_restart(cr, status, phrase);
+  
+  return 0;
+}
+
+static int nua_register_client_response(nua_client_request_t *cr,
+					int status, char const *phrase,
+					sip_t const *sip)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
+  nua_handle_t *nh = cr->cr_owner;
   nua_dialog_usage_t *du = cr->cr_usage;
   nua_registration_t *nr = nua_dialog_usage_private(du);
-  int status, ready, reregister, terminating;
-  char const *phrase;
-  msg_t *_reqmsg = nta_outgoing_getrequest(orq);
-  sip_t *req = sip_object(_reqmsg); msg_destroy(_reqmsg);
-
-  assert(sip);
-  assert(du && du->du_class == nua_register_usage);
-  status = sip->sip_status->st_status;
-  phrase = sip->sip_status->st_phrase;
-
-  if (status < 200 || !du)
-    return nua_stack_process_response(nh, cr, orq, sip, TAG_END());
-
-  terminating = du->du_terminating;
-  if (!terminating)
-    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
-
-  reregister = outbound_register_response(nr->nr_ob, terminating, req, sip);
-  if (reregister < 0)
-    SET_STATUS1(NUA_INTERNAL_ERROR);
-  else if (reregister >= ob_reregister) {
-    /* Save msg otherwise nua_creq_check_restart() will zap it */
-    msg_t *msg = msg_ref_create(cr->cr_msg);
+  int ready;
 
-    if (nua_creq_check_restart(nh, cr, orq, sip, restart_register)) {
-      msg_destroy(msg);
-      return 0;
-    }
+  ready = du && !cr->cr_terminated && status < 300;
 
-    assert(cr->cr_msg == NULL);
-    cr->cr_msg = msg;
+  if (ready) {
+    sip_time_t mindelta = 0;
+    sip_time_t now = sip_now(), delta, reqdelta, mdelta;
 
-    if (reregister >= ob_reregister_now) {
-      /* We can try to reregister immediately */
-      nua_creq_restart_with(nh, cr, orq, 100, "Updated Contact",
-			    restart_register,
-			    TAG_END());
-    }
-    else {
-      /* Outbound will invoke refresh_register() later */
-      nua_creq_save_restart(nh, cr, orq, 100, "Updated Contact",
-			    restart_register);
+    sip_contact_t const *m, *sent;
+
+    msg_t *_reqmsg = nta_outgoing_getrequest(cr->cr_orq);
+    sip_t *req = sip_object(_reqmsg);
+
+    msg_destroy(_reqmsg);
+
+    assert(nr); assert(sip); assert(req);
+
+#if HAVE_SIGCOMP
+    {
+      struct sigcomp_compartment *cc;
+      cc = nta_outgoing_compartment(cr->cr_orq);
+      sigcomp_compartment_unref(nr->nr_compartment);
+      nr->nr_compartment = cc;
     }
-    return 0;
-  }
+#endif
 
-  if (status >= 300)
-    if (nua_creq_check_restart(nh, cr, orq, sip, restart_register))
-      return 0;
+    /* XXX - if store/remove, remove 
+       Content-Disposition
+       Content-Type
+       body
+    */
+
+    /** Search for lowest delta of SIP contacts we tried to register */
+    mindelta = SIP_TIME_MAX;
+
+    reqdelta = req->sip_expires ? req->sip_expires->ex_delta : 0;
+
+    for (m = sip->sip_contact; m; m = m->m_next) {
+      if (m->m_url->url_type != url_sip && 
+	  m->m_url->url_type != url_sips)
+	continue;
 
-  ready = !terminating && status < 300;
-  du->du_ready = ready;
+      for (sent = req->sip_contact; sent; sent = sent->m_next) {
+	if (url_cmp(m->m_url, sent->m_url))
+	  continue;
+
+	if (sent->m_expires)
+	  mdelta = strtoul(sent->m_expires, NULL, 10);
+	else
+	  mdelta = reqdelta;
+
+	if (mdelta == 0)
+	  mdelta = 3600;
+	  
+	delta = sip_contact_expires(m, sip->sip_expires, sip->sip_date,
+				    mdelta, now);
+	if (delta > 0 && delta < mindelta)
+	  mindelta = delta;
 
-  if (status < 300) {
-    if (!du->du_terminating) {
-      sip_time_t mindelta = 0;
-      sip_time_t now = sip_now(), delta, reqdelta;
-      sip_contact_t const *m, *sent;
-
-      /** Search for lowest delta of SIP contacts we tried to register */
-      mindelta = SIP_TIME_MAX;
-
-      reqdelta = req->sip_expires ? req->sip_expires->ex_delta : 0;
-
-      for (m = sip->sip_contact; m; m = m->m_next) {
-        if (m->m_url->url_type != url_sip && 
-            m->m_url->url_type != url_sips)
-          continue;
-        for (sent = req->sip_contact; sent; sent = sent->m_next)
-          if (url_cmp(m->m_url, sent->m_url) == 0) {
-            sip_time_t mdelta = reqdelta;
-
-            if (sent->m_expires)
-              mdelta = strtoul(sent->m_expires, NULL, 10);
-            if (mdelta == 0)
-              mdelta = 3600;
-
-            delta = sip_contact_expires(m, sip->sip_expires, sip->sip_date,
-       				 mdelta, now);
-            if (delta > 0 && delta < mindelta)
-              mindelta = delta;
-            if (url_cmp_all(m->m_url, sent->m_url) == 0)
-              break;
-          }
+	if (url_cmp_all(m->m_url, sent->m_url) == 0)
+	  break;
       }
-
-      if (mindelta == SIP_TIME_MAX)
-        mindelta = 3600;
-      nua_dialog_usage_set_expires(du, mindelta);
     }
-    else
-      nua_dialog_usage_set_expires(du, 0);
-  }
 
-#if HAVE_SIGCOMP
-  if (ready) {
-    struct sigcomp_compartment *cc;
-    cc = nta_outgoing_compartment(orq);
-    sigcomp_compartment_unref(nr->nr_compartment);
-    nr->nr_compartment = cc;
-  }
-#endif
+    if (mindelta == SIP_TIME_MAX)
+      mindelta = 3600;
+
+    nua_dialog_usage_set_expires(du, mindelta);
 
   /*  RFC 3608 Section 6.1 Procedures at the UA
 
@@ -934,46 +899,103 @@
    route for that address-of-record.
 
   */
-  if (ready) {
     su_free(nh->nh_home, nr->nr_route);
     nr->nr_route = sip_route_dup(nh->nh_home, sip->sip_service_route);
+
+    {
+      /* RFC 3327 */
+      /* Store last URI in Path header */
+      sip_path_t *path = sip->sip_path;
+
+      while (path && path->r_next)
+	path = path->r_next;
+
+      if (!nr->nr_path || !path ||
+	  url_cmp_all(nr->nr_path->r_url, path->r_url)) {
+	su_free(nh->nh_home, nr->nr_path);
+	nr->nr_path = sip_path_dup(nh->nh_home, path);
+      }
+    }
+
+    if (sip->sip_to->a_url->url_type == url_sips)
+      nr->nr_secure = 1;
+
+    if (nr->nr_ob) {
+      outbound_gruuize(nr->nr_ob, sip);
+      outbound_start_keepalive(nr->nr_ob, cr->cr_orq);
+    }
+
+    /* persistant connection for registration */
+    if (!nr->nr_tport)
+      /* note: nta_outgoing_transport() takes a ref */
+      nr->nr_tport = nta_outgoing_transport (cr->cr_orq); 
+
+    nua_registration_set_ready(nr, 1);
   }
-  else {
+  else if (du) {
+    nua_dialog_usage_set_expires(du, 0);
+
     su_free(nh->nh_home, nr->nr_route);
     nr->nr_route = NULL;
+
+    outbound_stop_keepalive(nr->nr_ob);
+
+    /* release the persistant transport for registration */
+    if (nr->nr_tport)
+      tport_decref(&nr->nr_tport), nr->nr_tport = NULL;
+
+    nua_registration_set_ready(nr, 0);
   }
 
-  if (ready) {
-    /* RFC 3327 */
-    /* Store last URI in Path header */
-    sip_path_t *path = sip->sip_path;
 
-    while (path && path->r_next)
-      path = path->r_next;
+  return nua_base_client_response(cr, status, phrase, sip, NULL);
+}
 
-    if (!nr->nr_path || !path ||
-        url_cmp_all(nr->nr_path->r_url, path->r_url)) {
-      su_free(nh->nh_home, nr->nr_path);
-      nr->nr_path = sip_path_dup(nh->nh_home, path);
-    }
+static void nua_register_usage_refresh(nua_handle_t *nh,
+				       nua_dialog_state_t *ds,
+				       nua_dialog_usage_t *du,
+				       sip_time_t now)
+{
+  nua_t *nua = nh->nh_nua;
+  nua_client_request_t *cr = du->du_cr;
+
+  if (cr) {
+    if (nua_client_resend_request(cr, 0) >= 0)
+      return;
   }
 
-  if (ready)
-    if (sip->sip_to->a_url->url_type == url_sips)
-      nr->nr_secure = 1;
+  /* Report that we have de-registered */
+  nua_stack_event(nua, nh, NULL, nua_r_register, NUA_INTERNAL_ERROR, NULL);
+  nua_dialog_usage_remove(nh, ds, du);
+}
 
-  if (nr->nr_ob) {
-    if (ready) {
-      outbound_gruuize(nr->nr_ob, sip);
-      outbound_start_keepalive(nr->nr_ob, orq);
-    }
-    else
-      outbound_stop_keepalive(nr->nr_ob);
+/** @interal Shut down REGISTER usage.
+ *
+ * @retval >0  shutdown done
+ * @retval 0   shutdown in progress
+ * @retval <0  try again later
+ */
+static int nua_register_usage_shutdown(nua_handle_t *nh,
+				     nua_dialog_state_t *ds,
+				     nua_dialog_usage_t *du)
+{
+  nua_client_request_t *cr = du->du_cr;
+  nua_registration_t *nr = nua_dialog_usage_private(du);
+
+  if (cr) {
+    if (nua_client_is_queued(cr)) /* Already registering. */
+      return -1;
+    cr->cr_event = nua_r_unregister;
+    if (nua_client_resend_request(cr, 1) >= 0)
+      return 0;
   }
 
-  nua_registration_set_ready(nr, ready);
+  /* release the persistant transport for registration */
+  if (nr->nr_tport)
+    tport_decref(&nr->nr_tport), nr->nr_tport = NULL;
 
-  return nua_stack_process_response(nh, cr, orq, sip, TAG_END());
+  nua_dialog_usage_remove(nh, ds, du);
+  return 200;
 }
 
 /* ---------------------------------------------------------------------- */
@@ -984,7 +1006,8 @@
 #endif
 
 static void nua_stack_tport_update(nua_t *nua, nta_agent_t *nta);
-static int nua_registration_add_contact_and_route(nua_registration_t *nr,
+static int nua_registration_add_contact_and_route(nua_handle_t *nh,
+						  nua_registration_t *nr,
 						  msg_t *msg,
 						  sip_t *sip,
 						  int add_contact,
@@ -1081,9 +1104,7 @@
 
   switch (nw_updates) {
   case NUA_NW_DETECT_ONLY_INFO:
-    nua_stack_event(nua, NULL, NULL, nua_i_network_changed,
-		    SIP_200_OK, TAG_END());
-
+    nua_stack_event(nua, NULL, NULL, nua_i_network_changed, SIP_200_OK, NULL);
     break;
     
   case NUA_NW_DETECT_TRY_FULL:
@@ -1095,10 +1116,10 @@
     if (nua_stack_init_transport(nua, nua->nua_args) < 0)
       /* We are hosed */
       nua_stack_event(nua, NULL, NULL, nua_i_network_changed,
-		      900, "Internal Error", TAG_END());
+		      900, "Internal Error", NULL);
     else
       nua_stack_event(nua, NULL, NULL, nua_i_network_changed,
-		      SIP_200_OK, TAG_END());
+		      SIP_200_OK, NULL);
 
     break;
     
@@ -1262,11 +1283,8 @@
 
     v2[1].v_next = NULL;
 
-#if 1
-    contact = nua_handle_contact_by_via(nh, home, NULL, v2, protocol, NULL);
-#else
-    contact = sip_contact_create_from_via_with_transport(home, v2, NULL, protocol);
-#endif
+    contact = nua_handle_contact_by_via(nh, home, 0, NULL, v2, protocol, NULL);
+
     v = sip_via_dup(home, v2);
 
     if (!contact || !v) {
@@ -1277,6 +1295,7 @@
     nr->nr_ready = 1, nr->nr_default = 1, nr->nr_public = public;
     nr->nr_secure = contact->m_url->url_type == url_sips;
     nr->nr_contact = contact;
+    *nr->nr_dcontact = *contact, nr->nr_dcontact->m_params = NULL;
     nr->nr_via = v;
     nr->nr_ip4 = host_is_ip4_address(contact->m_url->url_host);
     nr->nr_ip6 = !nr->nr_ip4 && host_is_ip6_reference(contact->m_url->url_host);
@@ -1446,7 +1465,10 @@
       return m;
   }
 
-  return nr->nr_contact;
+  if (nr->nr_contact)
+    return nr->nr_dcontact;
+  else
+    return NULL;
 }
 
 /** Return initial route. */
@@ -1458,7 +1480,7 @@
 sip_contact_t const *nua_stack_get_contact(nua_registration_t const *nr)
 {
   nr = nua_registration_by_aor(nr, NULL, NULL, 1);
-  return nr ? nr->nr_contact : NULL;
+  return nr && nr->nr_contact ? nr->nr_dcontact : NULL;
 }
 
 /** Add a Contact (and Route) header to request */
@@ -1482,7 +1504,7 @@
   if (nr == NULL)
     nr = nua_registration_for_request(nh->nh_nua->nua_registrations, sip);
 
-  return nua_registration_add_contact_and_route(nr, msg, sip, 
+  return nua_registration_add_contact_and_route(nh, nr, msg, sip, 
 						add_contact, 
 						add_service_route);
 }
@@ -1513,12 +1535,15 @@
     nr = nua_registration_for_response(nh->nh_nua->nua_registrations, sip,
 				       record_route, remote_contact);
 
-  return nua_registration_add_contact_and_route(nr, msg, sip, 1, 0);
+  return nua_registration_add_contact_and_route(nh, nr, msg, sip, 
+						1,
+						0);
 }
 
 /** Add a Contact (and Route) header to request */
 static 
-int nua_registration_add_contact_and_route(nua_registration_t *nr,
+int nua_registration_add_contact_and_route(nua_handle_t *nh,
+					   nua_registration_t *nr,
 					   msg_t *msg,
 					   sip_t *sip,
 					   int add_contact,
@@ -1528,8 +1553,63 @@
     return -1;
 
   if (add_contact) {
-    sip_contact_t const *m = nua_registration_contact(nr);
-    if (!m || msg_header_add_dup(msg, (msg_pub_t *)sip, (void const *)m) < 0)
+    sip_contact_t const *m = NULL;
+    char const *m_display;
+    char const *m_username;
+    char const *m_params;
+    url_t const *u;
+
+    if (nr->nr_by_stack && nr->nr_ob) {
+      m = outbound_dialog_gruu(nr->nr_ob);
+
+      if (m)
+	return msg_header_add_dup(msg, (msg_pub_t *)sip, (void const *)m);
+
+      m = outbound_dialog_contact(nr->nr_ob);
+    }
+
+    if (m == NULL)
+      m = nr->nr_contact;
+
+    if (!m)
+      return -1;
+
+    u = m->m_url;
+
+    if (NH_PISSET(nh, m_display))
+      m_display = NH_PGET(nh, m_display);
+    else
+      m_display = m->m_display;
+
+    if (NH_PISSET(nh, m_username))
+      m_username = NH_PGET(nh, m_username);
+    else
+      m_username = m->m_url->url_user;
+
+    if (NH_PISSET(nh, m_params)) {
+      m_params = NH_PGET(nh, m_params);
+
+      if (u->url_params && m_params && strstr(u->url_params, m_params) == 0)
+	m_params = NULL;
+    }
+    else
+      m_params = NULL;
+
+    m = sip_contact_format(msg_home(msg),
+			   "%s<%s:%s%s%s%s%s%s%s%s%s>",
+			   m_display ? m_display : "",
+			   u->url_scheme,
+			   m_username ? m_username : "",
+			   m_username ? "@" : "",
+			   u->url_host,
+			   u->url_port ? ":" : "",
+			   u->url_port ? u->url_port : "",
+			   u->url_params ? ";" : "",
+			   u->url_params ? u->url_params : "",
+			   m_params ? ";" : "",
+			   m_params ? m_params : "");
+
+    if (msg_header_insert(msg, (msg_pub_t *)sip, (void *)m) < 0)
       return -1;
   }
 
@@ -1618,7 +1698,7 @@
 
     if (nr0 && nr0->nr_via) {
       char const *tport = nr0->nr_via->v_next ? NULL : nr0->nr_via->v_protocol;
-      m = nua_handle_contact_by_via(nh, nh->nh_home,
+      m = nua_handle_contact_by_via(nh, nh->nh_home, 0,
 				    NULL, nr0->nr_via, tport, NULL);
     }
   }
@@ -1627,6 +1707,7 @@
     return -1;
 
   nr->nr_contact = m;
+  *nr->nr_dcontact = *m, nr->nr_dcontact->m_params = NULL;
   nr->nr_ip4 = host_is_ip4_address(m->m_url->url_host);
   nr->nr_ip6 = !nr->nr_ip4 && host_is_ip6_reference(m->m_url->url_host);
   nr->nr_by_stack = !application_contact;
@@ -1639,8 +1720,10 @@
 /** Mark registration as ready */
 void nua_registration_set_ready(nua_registration_t *nr, int ready)
 {
-  assert(!ready || nr->nr_contact);
-  nr->nr_ready = ready;
+  if (nr) {
+    assert(!ready || nr->nr_contact);
+    nr->nr_ready = ready;
+  }
 }
 
 /** @internal Hook for processing incoming request by registration.
@@ -1670,48 +1753,6 @@
   return 481;			/* Call/Transaction does not exist */
 }
 
-/**@internal
- * Fix contacts for un-REGISTER.
- *
- * Remove (possible non-zero) "expires" parameters from contacts and extra
- * contacts, add Expire: 0.
- */
-static
-void unregister_expires_contacts(msg_t *msg, sip_t *sip)
-{
-  sip_contact_t *m;
-  int unregister_all;
-
-  if (msg == NULL || sip == NULL)
-    return;
-
-  /* Remove payload */
-  while (sip->sip_payload)
-    sip_header_remove(msg, sip, (sip_header_t *)sip->sip_payload);
-  while (sip->sip_content_type)
-    sip_header_remove(msg, sip, (sip_header_t *)sip->sip_content_type);
-
-  for (m = sip->sip_contact; m; m = m->m_next) {
-    if (m->m_url->url_type == url_any)
-      break;
-    msg_header_remove_param(m->m_common, "expires");
-#if 0
-    msg_header_add_param(msg_home(msg), m->m_common, "expires=0");
-#endif
-  }
-
-  unregister_all = m && (m != sip->sip_contact || m->m_next);
-
-  sip_add_tl(msg, sip,
-             /* Remove existing contacts */
-             TAG_IF(unregister_all, SIPTAG_CONTACT(NONE)),
-             /* Add '*' contact: 0 */
-             TAG_IF(unregister_all, SIPTAG_CONTACT_STR("*")),
-             SIPTAG_EXPIRES_STR("0"),
-             TAG_END());
-}
-
-
 /** Outbound requests us to refresh registration */
 static int nua_stack_outbound_refresh(nua_handle_t *nh,
 				      outbound_t *ob)
@@ -1758,7 +1799,7 @@
 
   nua_stack_event(nh->nh_nua, nh, NULL,
 		  nua_i_outbound, status, phrase,
-		  ta_tags(ta));
+		  ta_args(ta));
 
   ta_end(ta);
 
@@ -1775,7 +1816,7 @@
 
   nua_stack_event(nh->nh_nua, nh, NULL,
 		  nua_i_outbound, status, phrase,
-		  ta_tags(ta));
+		  ta_args(ta));
 
   ta_end(ta);
 
@@ -1795,6 +1836,7 @@
 /** @internal Generate a @Contact header. */
 sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh,
 					 su_home_t *home,
+					 int in_dialog,
 					 char const *extra_username,
 					 sip_via_t const *v,
 					 char const *transport,
@@ -1841,10 +1883,11 @@
     /* Make transport parameter lowercase */
     if (strlen(transport) < (sizeof _transport)) {
       char *s = strcpy(_transport, transport);
+      short c;
 
-      for (s = _transport; *s && *s != ';'; s++)
-	if (isupper(*s))
-	  *s = tolower(*s);
+      for (s = _transport; (c = *s) && c != ';'; s++)
+	if (isupper(c))
+	  *s = tolower(c);
 
       transport = _transport;
     }
@@ -1878,7 +1921,7 @@
     su_strlst_append(l, ";comp="), su_strlst_append(l, comp);
   s = NH_PGET(nh, m_params);
   if (s) 
-    su_strlst_append(l, s[0] == ';' ? "" : ";"), su_strlst_append(l, s);
+    s[0] == ';' ? "" : su_strlst_append(l, ";"), su_strlst_append(l, s);
   su_strlst_append(l, ">");
 
   va_start(va, m_param);
@@ -1892,81 +1935,42 @@
   
   va_end(va);
 
-  m = sip_contact_make(home, su_strlst_join(l, su_strlst_home(l), ""));
-  
-  su_strlst_destroy(l);
-  
-  return m;
-}
-
-/** @internal Return a string describing our features. */
-static char *nua_handle_features(nua_handle_t *nh)
-{
-  char *retval = NULL;
-  su_strlst_t *l = su_strlst_create(NULL);
-  su_home_t *home = su_strlst_home(l);
-
-  if (!l)
-    return NULL;
-
-  if (NH_PGET(nh, m_features)) {
-    char const *m_features = NH_PGET(nh, m_features);
-
-    if (m_features[0] != ';')
-      su_strlst_append(l, ";");
-
-    su_strlst_append(l, m_features);
-  }
-
-  if (NH_PGET(nh, callee_caps)) {
-    sip_allow_t const *allow = NH_PGET(nh, allow);
-
-    if (allow) {
-      /* Skip ";" if this is first one */
-      su_strlst_append(l, ";methods=\"" + (su_strlst_len(l) == 0));
-      if (allow->k_items) {
-        size_t i;
-        for (i = 0; allow->k_items[i]; i++) {
-          su_strlst_append(l, allow->k_items[i]);
-          if (allow->k_items[i + 1])
-            su_strlst_append(l, ",");
-        }
+  if (!in_dialog) {
+    s = NH_PGET(nh, m_features);
+    if (s) 
+      s[0] == ';' ? "" : su_strlst_append(l, ";"), su_strlst_append(l, s);
+
+    if (NH_PGET(nh, callee_caps)) {
+      sip_allow_t const *allow = NH_PGET(nh, allow);
+
+      if (allow) {
+	su_strlst_append(l, ";methods=\"");
+	if (allow->k_items) {
+	  size_t i;
+	  for (i = 0; allow->k_items[i]; i++) {
+	    su_strlst_append(l, allow->k_items[i]);
+	    if (allow->k_items[i + 1])
+	      su_strlst_append(l, ",");
+	  }
+	}
+	su_strlst_append(l, "\"");
       }
-      su_strlst_append(l, "\"");
-    }
-
-    if (nh->nh_soa) {
-      char **media = soa_media_features(nh->nh_soa, 0, home);
 
-      while (*media) {
-        if (su_strlst_len(l))
-          su_strlst_append(l, ";");
-        su_strlst_append(l, *media++);
+      if (nh->nh_soa) {
+	char **media = soa_media_features(nh->nh_soa, 0, home);
+	
+	while (*media) {
+	  if (su_strlst_len(l))
+	    su_strlst_append(l, ";");
+	  su_strlst_append(l, *media++);
+	}
       }
     }
   }
 
-  if (su_strlst_len(l))
-    retval = su_strlst_join(l, nh->nh_home, "");
-
+  m = sip_contact_make(home, su_strlst_join(l, su_strlst_home(l), ""));
+  
   su_strlst_destroy(l);
-
-  return retval;
-}
-
-static int nua_stack_outbound_features(nua_handle_t *nh, outbound_t *ob)
-{
-  char *features;
-  int retval;
-
-  if (!nh)
-    return -1;
-  if (!ob)
-    return 0;
-
-  features = nua_handle_features(nh);
-  retval = outbound_set_features(ob, features);
-  su_free(nh->nh_home, features);
-
-  return retval;
+  
+  return m;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_registrar.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_registrar.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_registrar.c	Tue Mar 20 23:37:15 2007
@@ -44,8 +44,6 @@
 #include <sofia-sip/sip_status.h>
 #include <sofia-sip/sip_util.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
 #define NTA_INCOMING_MAGIC_T struct nua_handle_s
 #define NTA_RELIABLE_MAGIC_T struct nua_handle_s
 
@@ -89,15 +87,19 @@
  * @END_NUA_EVENT
  */
 
-int nua_stack_process_register(nua_t *nua,
-			       nua_handle_t *nh,
-			       nta_incoming_t *irq,
-			       sip_t const *sip)
-{
-  nua_server_request_t *sr, sr0[1];
-
-  sr = nua_server_request(nua, nh, irq, sip, SR_INIT(sr0), sizeof *sr,
-			  nua_default_respond, 0);
-
-  return nua_stack_server_event(nua, sr, nua_i_register, TAG_END());
-}
+nua_server_methods_t const nua_register_server_methods = 
+  {
+    SIP_METHOD_REGISTER,
+    nua_i_register,		/* Event */
+    { 
+      0,			/* Do not create dialog */
+      0,			/* Initial request */
+      0,			/* Not a target refresh request  */
+      0,			/* Do not add Contact */
+    },
+    nua_base_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_base_server_respond,
+    nua_base_server_report,
+  };

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c	Tue Mar 20 23:37:15 2007
@@ -45,10 +45,9 @@
 #include <sofia-sip/sip_util.h>
 #include <sofia-sip/su_uniqueid.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
 #define NTA_INCOMING_MAGIC_T struct nua_server_request
-#define NTA_RELIABLE_MAGIC_T struct nua_handle_s
+#define NTA_OUTGOING_MAGIC_T struct nua_client_request
+#define NTA_RELIABLE_MAGIC_T struct nua_server_request
 
 #include "nua_stack.h"
 #include <sofia-sip/soa.h>
@@ -141,8 +140,7 @@
 /** Session-related state */
 typedef struct nua_session_usage
 {
-  /* enum nua_callstate */
-  unsigned        ss_state:4;		/**< Session status (enum nua_callstate) */
+  enum nua_callstate ss_state;		/**< Session status (enum nua_callstate) */
   
   unsigned        ss_100rel:1;	        /**< Use 100rel, send 183 */
   unsigned        ss_alerting:1;	/**< 180 is sent/received */
@@ -152,17 +150,18 @@
   unsigned        ss_precondition:1;	/**< Precondition required */
 
   unsigned        ss_timer_set:1;       /**< We have active session timer. */
+
+  unsigned        ss_reporting:1;       /**< True if reporting state */
   unsigned        : 0;
   
   unsigned        ss_session_timer;	/**< Value of Session-Expires (delta) */
   unsigned        ss_min_se;		/**< Minimum session expires */
   enum nua_session_refresher ss_refresher; /**< none, local or remote */
 
-  char const     *ss_ack_needed;	/**< If non-null, need to send an ACK
-					 * (do O/A, if "offer" or "answer")
-					 */
+  char const     *ss_reason;	        /**< Reason for termination. */
 
-  nua_client_request_t ss_crequest[1];  /* Outgoing invite */
+  /* Offer-Answer status */
+  char const     *ss_oa_recv, *ss_oa_sent;
 } nua_session_usage_t;
 
 static char const *nua_session_usage_name(nua_dialog_usage_t const *du);
@@ -180,6 +179,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,14 +204,11 @@
 			   nua_dialog_state_t *ds,
 			   nua_dialog_usage_t *du)
 {
-  nua_session_usage_t *ss = nua_dialog_usage_private(du);
-
   if (ds->ds_has_session)
     return -1;
   ds->ds_has_session = 1;
+  ds->ds_got_session = 1;
 
-  nh->nh_ds->ds_cr->cr_next = ss->ss_crequest;
- 
   return 0;
 }
 
@@ -219,17 +218,57 @@
 			       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;
+  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;
+}
 
-  if (ss->ss_crequest)
-    nua_creq_deinit(ss->ss_crequest, NULL);
+static
+nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds)
+{
+  if (ds == ((nua_handle_t *)NULL)->nh_ds)
+    return NULL;
 
-  ds->ds_cr->cr_next = NULL;
+  return nua_dialog_usage_get(ds, nua_session_usage, NULL);
 }
 
 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;
 
@@ -241,38 +280,30 @@
   return (nua_session_usage_t *)nua_dialog_usage_private(du);
 }
 
-/* ======================================================================== */
-/* INVITE and call (session) processing */
-
-static int nua_stack_invite2(nua_t *, nua_handle_t *, nua_event_t e,
-			     int restarted, tagi_t const *tags);
-static int process_response_to_invite(nua_handle_t *nh,
-				      nta_outgoing_t *orq,
-				      sip_t const *sip);
-static void
-  session_timeout(nua_handle_t *nh, nua_dialog_usage_t *du, sip_time_t now);
+/** Zap the session associated with the handle */
+static
+void nua_session_usage_destroy(nua_handle_t *nh,
+			       nua_session_usage_t *ss)
+{
+  /* Remove usage */
+  nua_dialog_usage_remove(nh, nh->nh_ds, nua_dialog_usage_public(ss));
 
-static void restart_invite(nua_handle_t *nh, tagi_t *tags);
+  SU_DEBUG_5(("nua: terminated session %p\n", (void *)nh));
+}
 
-static int process_100rel(nua_handle_t *nh,
-			  nua_session_usage_t *ss,
-			  nta_outgoing_t *orq,
-			  sip_t const *sip);
+/* ======================================================================== */
+/* INVITE and call (session) processing */
 
 int nua_stack_prack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
 		    tagi_t const *tags);
 
-static int process_response_to_prack(nua_handle_t *nh,
-				     nta_outgoing_t *orq,
-				     sip_t const *sip);
-
-static void nua_session_usage_destroy(nua_handle_t *, nua_session_usage_t *);
-
 static void session_timer_preferences(nua_session_usage_t *ss,
 				      unsigned expires,
 				      unsigned min_se,
 				      enum nua_session_refresher refresher);
+
 static int session_timer_is_supported(nua_handle_t const *nh);
+
 static int prefer_session_timer(nua_handle_t const *nh);
 
 static int use_session_timer(nua_session_usage_t *ss, int uas, int always,
@@ -280,24 +311,18 @@
 static int init_session_timer(nua_session_usage_t *ss, sip_t const *, int refresher);
 static void set_session_timer(nua_session_usage_t *ss);
 
-static int
-check_session_timer_restart(nua_handle_t *nh,
-			    nua_session_usage_t *ss,
-			    nua_client_request_t *cr,
-			    nta_outgoing_t *orq,
-			    sip_t const *sip,
-			    nua_creq_restart_f *restart_function);
+static int session_timer_check_restart(nua_client_request_t *cr,
+				       int status, char const *phrase,
+				       sip_t const *sip);
 
 static int nh_referral_check(nua_handle_t *nh, tagi_t const *tags);
 static void nh_referral_respond(nua_handle_t *,
 				int status, char const *phrase);
 
 static void signal_call_state_change(nua_handle_t *nh,
-				     nua_session_usage_t *ss,
-				     int status, char const *phrase,
-				     enum nua_callstate next_state,
-				     char const *oa_recv,
-				     char const *oa_sent);
+				      nua_session_usage_t *ss,
+				      int status, char const *phrase,
+				      enum nua_callstate next_state);
 
 static
 int session_get_description(sip_t const *sip,
@@ -319,16 +344,9 @@
 			     sip_payload_t **return_pl);
 
 static
-int session_process_response(nua_handle_t *nh,
-			     nua_client_request_t *cr,
-			     nta_outgoing_t *orq,
-			     sip_t const *sip,
-			     char const **return_received);
-
-static
-int respond_with_retry_after(nua_handle_t *nh, nta_incoming_t *irq,
-			     int status, char const *phrase,
-			     int min, int max);
+int nua_server_retry_after(nua_server_request_t *sr,
+			   int status, char const *phrase,
+			   int min, int max);
 
 /**@fn void nua_invite(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
  *
@@ -475,128 +493,149 @@
 /* Tags not implemented
  *    NUTAG_REFER_PAUSE() \n
  */
-int
-nua_stack_invite(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		 tagi_t const *tags)
-{
-  char const *what;
-
-  if (nh_is_special(nh) || 
-      nua_stack_set_handle_special(nh, nh_has_invite, nua_i_error))
-    what = "Invalid handle for INVITE";
-  else if (nh_referral_check(nh, tags) < 0) {
-    what = "Invalid referral";
-  }
-  else if (nua_stack_init_handle(nua, nh, TAG_NEXT(tags)) < 0) {
-    what = "Handle initialization failed";
-  }
-  else
-    return nua_stack_invite2(nua, nh, e, 0, tags);
 
-  UA_EVENT2(e, 900, what);
+static int nua_invite_client_init(nua_client_request_t *cr, 
+				  msg_t *msg, sip_t *sip,
+				  tagi_t const *tags);
+static int nua_invite_client_request(nua_client_request_t *cr,
+				     msg_t *msg, sip_t *sip,
+				     tagi_t const *tags);
+static int nua_invite_client_preliminary(nua_client_request_t *cr,
+					 int status, char const *phrase,
+					 sip_t const *sip);
+static int nua_invite_client_response(nua_client_request_t *cr,
+				      int status, char const *phrase,
+				      sip_t const *sip);
+static int nua_session_client_response(nua_client_request_t *cr,
+				       int status, char const *phrase,
+				       sip_t const *sip);
+static int nua_invite_client_report(nua_client_request_t *cr,
+				    int status, char const *phrase,
+				    sip_t const *sip,
+				    nta_outgoing_t *orq,
+				    tagi_t const *tags);
 
-  signal_call_state_change(nh, NULL, 900, what, nua_callstate_init, 0, 0);
+nua_client_methods_t const nua_invite_client_methods = {
+  SIP_METHOD_INVITE,
+  0,
+  { 
+    /* create_dialog */ 1,
+    /* in_dialog */ 1,
+    /* target refresh */ 1
+  },
+  NULL,
+  nua_invite_client_init,
+  nua_invite_client_request,
+  session_timer_check_restart,
+  nua_invite_client_response,
+  nua_invite_client_preliminary,
+  nua_invite_client_report,
+  nua_invite_client_deinit
+};
+
+extern nua_client_methods_t const nua_bye_client_methods;
+extern nua_client_methods_t const nua_cancel_client_methods;
+extern nua_client_methods_t const nua_info_client_methods;
+extern nua_client_methods_t const nua_update_client_methods;
+extern nua_client_methods_t const nua_prack_client_methods;
 
-  return e;
+int nua_stack_invite(nua_t *nua, nua_handle_t *nh, nua_event_t e,
+		     tagi_t const *tags)
+{
+  return nua_client_create(nh, e, &nua_invite_client_methods, tags);
 }
 
-static int
-nua_stack_invite2(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		  int restarted,
-		  tagi_t const *tags)
+static int nua_invite_client_init(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;
-  nua_session_usage_t *ss;
-  nua_client_request_t *cr;
-  int offer_sent = 0;
 
-  msg_t *msg = NULL;
-  sip_t *sip = NULL;
+  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))
+    return nua_client_return(cr, 900, "Invalid handle for INVITE", msg);
+  else if (nh_referral_check(nh, tags) < 0)
+    return nua_client_return(cr, 900, "Invalid referral", msg);
 
-  char const *what;
+  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;
 
-  du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL);
-  ss = nua_dialog_usage_private(du);
-  cr = ss->ss_crequest;
-  what = nua_internal_error;		/* Internal error */
+  if (nua_client_bind(cr, du) < 0)
+    return nua_client_return(cr, 900, "INVITE already in progress", msg);
 
-  if (du == NULL)
-    goto failure;
+  session_timer_preferences(nua_dialog_usage_private(du), 
+			    NH_PGET(nh, session_timer),
+			    NH_PGET(nh, min_se),
+			    NH_PGET(nh, refresher));
 
-  if (cr->cr_orq) {
-    what = "INVITE request already in progress";
-    goto failure;
-  }
+  cr->cr_neutral = 0;
 
-  if (ss->ss_state == nua_callstate_terminated)
-    ss->ss_state = nua_callstate_init;
+  return 0;
+}
 
-  if (!restarted) {
-    session_timer_preferences(ss, 
-			      NH_PGET(nh, session_timer),
-			      NH_PGET(nh, min_se),
-			      NH_PGET(nh, refresher));
-  }
+static int nua_invite_client_request(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);
+  int offer_sent = 0, retval;
+  sip_time_t invite_timeout;
 
-  if (restarted && !cr->cr_msg) {
-    if (du->du_msg)
-      cr->cr_msg = msg_dup(du->du_msg);
-    else
-      restarted = 0;
-  }
+  if (du == NULL)		/* Call terminated */ 
+    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);
 
-  msg = nua_creq_msg(nua, nh, cr, restarted,
-		     SIP_METHOD_INVITE,
-		     NUTAG_USE_DIALOG(1),
-		     NUTAG_ADD_CONTACT(1),
-		     TAG_NEXT(tags));
-  sip = sip_object(msg);
+  assert(ss);
 
-  if (!sip) {
-    what = "Cannot Initialize Request";
-    goto failure;
-  }
+  invite_timeout = NH_PGET(nh, invite_timeout);
+  if (invite_timeout == 0)
+    invite_timeout = UINT_MAX;
+  /* 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);
+
+  /* Add session timer headers */
+  if (session_timer_is_supported(nh))
+    use_session_timer(ss, 0, prefer_session_timer(nh), msg, sip);
 
-  if (!restarted) {
-    msg_destroy(du->du_msg), du->du_msg = msg_dup(msg);
-  }
+  ss->ss_100rel = NH_PGET(nh, early_media);
+  ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition");
+  if (ss->ss_precondition)
+    ss->ss_update_needed = ss->ss_100rel = 1;
 
   if (nh->nh_soa) {
     soa_init_offer_answer(nh->nh_soa);
 
     if (sip->sip_payload)
-      offer_sent = 0;
+      offer_sent = 0;		/* XXX - kludge */
     else if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0)
-      offer_sent = -1;
+      return -1;
     else
       offer_sent = 1;
-  }
-
-  if (offer_sent >= 0) {
-    sip_time_t invite_timeout = NH_PGET(nh, invite_timeout);
-    if (invite_timeout == 0)
-      invite_timeout = UINT_MAX;
-    /* Cancel if we don't get response within timeout*/
-    nua_dialog_usage_set_expires(du, invite_timeout);
-    nua_dialog_usage_set_refresh(du, 0);
-
-    /* Add session timer headers */
-    if (session_timer_is_supported(nh))
-      use_session_timer(ss, 0, prefer_session_timer(nh), msg, sip);
-
-    ss->ss_100rel = NH_PGET(nh, early_media);
-    ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition");
-
-    if (ss->ss_precondition)
-      ss->ss_update_needed = ss->ss_100rel = 1;
 
     if (offer_sent > 0 &&
-	session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
-      what = "Internal media error"; goto failure;
-    }
+	session_include_description(nh->nh_soa, 1, msg, sip) < 0)
+      return nua_client_return(cr, 900, "Internal media error", msg);
 
-    if (nh->nh_soa &&
-	NH_PGET(nh, media_features) && !nua_dialog_is_established(nh->nh_ds) &&
+    if (NH_PGET(nh, media_features) &&
+	!nua_dialog_is_established(nh->nh_ds) &&
 	!sip->sip_accept_contact && !sip->sip_reject_contact) {
       sip_accept_contact_t ac[1];
       sip_accept_contact_init(ac);
@@ -609,188 +648,307 @@
 	sip_add_dup(msg, sip, (sip_header_t *)ac);
       }
     }
-
-    if (nh->nh_auth) {
-      if (auc_authorize(&nh->nh_auth, msg, sip) < 0) {
-	what = "Internal authentication error"; goto failure;
-      }
-    }
-
-      cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-					process_response_to_invite, nh, NULL,
-					msg,
-					NTATAG_REL100(ss->ss_100rel),
-					SIPTAG_END(), TAG_NEXT(tags));
-
-    if (cr->cr_orq) {
-      cr->cr_offer_sent = offer_sent;
-      cr->cr_usage = du;
-      du->du_refresh = 0;
-      signal_call_state_change(nh, ss, 0, "INVITE sent",
-			       nua_callstate_calling, 0,
-			       offer_sent ? "offer" : 0);
-      return cr->cr_event = e;
-    }
+  }
+  else {
+    offer_sent = session_get_description(sip, NULL, NULL);
   }
 
- failure:
-
-  msg_destroy(msg);
-  if (du && !du->du_ready)
-    nua_dialog_usage_remove(nh, nh->nh_ds, du), ss = NULL;
-
-  UA_EVENT2(e, 900, what);
-  signal_call_state_change(nh, ss, 900, what, nua_callstate_init, 0, 0);
+  retval = nua_base_client_trequest(cr, msg, sip,
+				    NTATAG_REL100(ss->ss_100rel),
+				    TAG_NEXT(tags));
+  if (retval == 0) {
+    cr->cr_offer_sent = offer_sent;
+    ss->ss_oa_sent = offer_sent ? "offer" : NULL;
+
+    if (!cr->cr_restarting)
+      signal_call_state_change(nh, ss, 0, "INVITE sent", 
+			       nua_callstate_calling);
+  }
 
-  return e;
+  return retval;
 }
 
-/** @NUA_EVENT nua_r_invite
- *
- * Answer to outgoing INVITE.
- *
- * The INVITE may be sent explicitly by nua_invite() or
- * implicitly by NUA state machine.
- *
- * @param status response status code
- *               (if the request is retried, @a status is 100, the @a
- *               sip->sip_status->st_status contain the real status code
- *               from the response message, e.g., 302, 401, or 407)
- * @param phrase a short textual description of @a status code
- * @param nh     operation handle associated with the call
- * @param hmagic application context associated with the call
- * @param sip    response message to INVITE or NULL upon an error
- *               (status code is in @a status and 
- *                descriptive message in @a phrase parameters)
- * @param tags   empty
- *
- * @sa nua_invite(), @ref nua_call_model, #nua_i_state, #nua_i_invite, 
- * nua_ack(), NUTAG_AUTOACK()
- * 
- * @END_NUA_EVENT
- */
-
-static int process_response_to_invite(nua_handle_t *nh,
-				      nta_outgoing_t *orq,
+static int nua_invite_client_response(nua_client_request_t *cr,
+				      int status, char const *phrase,
 				      sip_t const *sip)
 {
-  nua_t *nua = nh->nh_nua;
-  nua_client_request_t *cr;
-  nua_dialog_usage_t *du;
-  nua_session_usage_t *ss;
-  int status = sip->sip_status->st_status;
-  char const *phrase = sip->sip_status->st_phrase;
-  int terminated = 0;
-  int gracefully = 1;
-  char const *received = NULL;
+  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);
 
-  cr = nua_client_request_by_orq(nh->nh_ds->ds_cr, orq);
-  du = cr ? cr->cr_usage : NULL;
-  ss = nua_dialog_usage_private(du);
-  
-  assert(cr && du && ss);
+  if (ss == NULL || sip == NULL) {
+    /* Xyzzy */
+  }
+  else if (status < 300) {
+    du->du_ready = 1;
 
-  if (ss->ss_state == nua_callstate_terminating && 200 <= status) {
-    /*
-     * If the call is being terminated but re-INVITE was responded with 2XX
-     * re-send the BYE, otherwise terminate the call.
-     */
-    gracefully = status < 300, terminated = !gracefully;
+    init_session_timer(ss, sip, NH_PGET(nh, refresher));
+    set_session_timer(ss);
   }
-  else if (status >= 300) {
-    if (sip->sip_retry_after)
-      gracefully = 0;
+  
+  return nua_session_client_response(cr, status, phrase, sip);
+}
 
-    terminated = sip_response_terminates_dialog(status, sip_method_invite,
-						&gracefully);
+static int nua_invite_client_preliminary(nua_client_request_t *cr,
+					 int status, char const *phrase,
+					 sip_t const *sip)
+{
+  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);
 
-    if (!terminated) {
-      if (check_session_timer_restart(nh, ss, cr, orq, sip, restart_invite))
-	return 0;
+  assert(sip); assert(ss);
 
-      if (ss->ss_state < nua_callstate_ready)
-	terminated = 1;
+  if (ss && sip && sip->sip_rseq) {
+    /* Handle 100rel responses */
+    sip_rseq_t *rseq = sip->sip_rseq;
+
+    /* Establish early dialog - we should fork here */
+    if (!nua_dialog_is_established(nh->nh_ds)) {
+      nta_outgoing_t *tagged;
+
+      nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
+      nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
+      
+      /* Tag the INVITE request */
+      tagged = nta_outgoing_tagged(cr->cr_orq,
+				   nua_client_orq_response, cr,
+				   sip->sip_to->a_tag, sip->sip_rseq);
+      if (tagged) {
+	nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = tagged;
+      }
+      else {
+	cr->cr_graceful = 1;
+	ss->ss_reason = "SIP;cause=500;text=\"Cannot Create Early Dialog\"";
+      }
+    }
+  
+    if (!rseq) {
+      SU_DEBUG_5(("nua(%p): 100rel missing RSeq\n", (void *)nh));
+    }
+    else if (nta_outgoing_rseq(cr->cr_orq) > rseq->rs_response) {
+      SU_DEBUG_5(("nua(%p): 100rel bad RSeq %u (got %u)\n", (void *)nh, 
+		  (unsigned)rseq->rs_response,
+		  nta_outgoing_rseq(cr->cr_orq)));
+      return 1;    /* Do not send event */
+    }
+    else if (nta_outgoing_setrseq(cr->cr_orq, rseq->rs_response) < 0) {
+      SU_DEBUG_1(("nua(%p): cannot set RSeq %u\n", (void *)nh, 
+		  (unsigned)rseq->rs_response));
+      cr->cr_graceful = 1;
+      ss->ss_reason = "SIP;cause=400;text=\"Bad RSeq\"";
     }
   }
-  else if (status >= 200) {
-    du->du_ready = 1;
-    cr->cr_usage = NULL;
 
-    /* XXX - check remote tag, handle forks */
-    /* Set route, contact, nh_ds->ds_remote_tag */
-    nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
-    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
+  return nua_session_client_response(cr, status, phrase, sip);
+}
 
-    init_session_timer(ss, sip, NH_PGET(nh, refresher));
-    set_session_timer(ss);
+/** Process response to a session request (INVITE, PRACK, UPDATE) */
+static int nua_session_client_response(nua_client_request_t *cr,
+				       int status, char const *phrase,
+				       sip_t const *sip)
+{
+  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);
 
-    /* signal_call_state_change */
-    if (session_process_response(nh, cr, orq, sip, &received) >= 0) {
-      ss->ss_ack_needed = received ? received : "";
-
-      if (NH_PGET(nh, auto_ack) ||
-	  /* Auto-ACK response to re-INVITE unless auto_ack is set to 0 */
-	  (ss->ss_state == nua_callstate_ready &&
-	   !NH_PISSET(nh, auto_ack)))
-	nua_stack_ack(nua, nh, nua_r_ack, NULL);
-      else
-	signal_call_state_change(nh, ss, status, phrase,
-				 nua_callstate_completing, received, 0);
-      nh_referral_respond(nh, SIP_200_OK);
-      return 0;
-    }
+  char const *sdp = NULL;
+  size_t len;
+  char const *received = NULL;
 
-    status = 500, phrase = "Malformed Session in Response";
+#define LOG3(m) \
+  SU_DEBUG_3(("nua(%p): %s: %s %s in %u %s\n", \
+	      (void *)nh, cr->cr_method_name, (m),		\
+	      received ? received : "SDP", status, phrase))
+#define LOG5(m) \
+  SU_DEBUG_5(("nua(%p): %s: %s %s in %u %s\n", \
+	      (void *)nh, cr->cr_method_name, (m), received, status, phrase))
 
-    nua_stack_ack(nua, nh, nua_r_ack, NULL);
-    gracefully = 1;
+  if (!ss || !sip || 300 <= status)
+    /* Xyzzy */;
+  else if (!session_get_description(sip, &sdp, &len))
+    /* No SDP */;
+  else if (cr->cr_answer_recv) {
+    /* Ignore spurious answers after completing O/A */
+    LOG3("ignoring duplicate");
+    sdp = NULL;
   }
-  else if (sip->sip_rseq) {
-    /* Reliable provisional response */
-    nh_referral_respond(nh, status, phrase);
+  else if (cr->cr_offer_sent) {
+    /* case 1: incoming answer */
+    cr->cr_answer_recv = status;
+    received = "answer";
 
-    return process_100rel(nh, ss, orq, sip); /* signal_call_state_change */
+    if (nh->nh_soa == NULL)
+      LOG5("got SDP");
+    else if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
+      LOG3("error parsing SDP");
+      sdp = NULL;
+      cr->cr_graceful = 1;
+      ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\"";
+    }
+    else if (soa_process_answer(nh->nh_soa, NULL) < 0) {
+      LOG5("error processing SDP");
+      /* XXX */
+      sdp = NULL;
+    }
+    else if (soa_activate(nh->nh_soa, NULL) < 0)
+      /* XXX - what about errors? */
+      LOG3("error activating media after");
+    else
+      LOG5("processed SDP");
+  }
+  else if (cr->cr_method != sip_method_invite) {
+    /* If non-invite request did not have offer, ignore SDP in response */
+    LOG3("ignoring extra");
+    sdp = NULL;
   }
   else {
-    /* Provisional response */
-    nh_referral_respond(nh, status, phrase);
-    session_process_response(nh, cr, orq, sip, &received);
-    signal_call_state_change(nh, ss, status, phrase,
-			     nua_callstate_proceeding, received, 0);
-    return 0;
+    /* case 2: answer to our offer */
+    cr->cr_offer_recv = 1, cr->cr_answer_sent = 0;
+    received = "offer";
+
+    if (nh->nh_soa && soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
+      LOG3("error parsing SDP");
+      sdp = NULL;
+      cr->cr_graceful = 1;
+      ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\"";
+    }
+    else 
+      LOG5("got SDP");
   }
 
-  cr->cr_usage = NULL;
+  if (ss && received)
+    ss->ss_oa_recv = received;
+
+  if (sdp && nh->nh_soa)
+    return nua_base_client_tresponse(cr, status, phrase, sip,
+				     NH_REMOTE_MEDIA_TAGS(1, nh->nh_soa),
+				     TAG_END());
+  else
+    return nua_base_client_response(cr, status, phrase, sip, NULL);
+}
+
+static int nua_invite_client_report(nua_client_request_t *cr,
+				    int status, char const *phrase,
+				    sip_t const *sip,
+				    nta_outgoing_t *orq,
+				    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);
+  unsigned next_state;
+  int error;
 
   nh_referral_respond(nh, status, phrase);
-  nua_stack_process_response(nh, cr, orq, sip, TAG_END());
 
-  if (terminated)
-    signal_call_state_change(nh, ss, status, phrase,
-			     nua_callstate_terminated, 0, 0);
+  nua_stack_event(nh->nh_nua, nh, 
+		  nta_outgoing_getresponse(orq),
+		  cr->cr_event,
+		  status, phrase,
+		  tags);
+
+  if (orq != cr->cr_orq && status != 100)
+    return 1;
 
-  if (terminated < 0) {
-    nua_dialog_terminated(nh, nh->nh_ds, status, phrase);
+  if (ss == NULL) {
+    signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated);
+    return 1;
   }
-  else if (terminated > 0) {
-    nua_dialog_usage_remove(nh, nh->nh_ds, du);
+
+  ss->ss_reporting = 1;
+
+  if (cr->cr_neutral) {
+    signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
+    ss->ss_reporting = 0;
+    return 1;
   }
-  else if (gracefully) {
-    char *reason =
-      su_sprintf(NULL, "SIP;cause=%u;text=\"%s\"", 
-		 status > 699 ? 500 : status, phrase);
 
-    signal_call_state_change(nh, ss, status, phrase,
-			     nua_callstate_terminating, 0, 0);
+  if (status == 100) {
+    next_state = nua_callstate_calling;
+  }
+  else if (status < 300 && cr->cr_graceful) {
+    next_state = nua_callstate_terminating;
+    if (200 <= status) {
+      nua_invite_client_ack(cr, NULL);
+    }
+  }
+  else if (status < 200) {
+    next_state = nua_callstate_proceeding;
+
+    if (sip && sip->sip_rseq && 
+	!SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_prack)) {
+      sip_rack_t rack[1];
 
-    nua_stack_post_signal(nh, nua_r_bye,
-			  SIPTAG_REASON_STR(reason),
-			  TAG_END());
+      sip_rack_init(rack);
+      rack->ra_response    = sip->sip_rseq->rs_response;
+      rack->ra_cseq        = sip->sip_cseq->cs_seq;
+      rack->ra_method      = sip->sip_cseq->cs_method;
+      rack->ra_method_name = sip->sip_cseq->cs_method_name;
+
+      error = nua_client_tcreate(nh, nua_r_prack, &nua_prack_client_methods, 
+				 SIPTAG_RACK(rack),
+				 TAG_END());
+      if (error < 0) {
+	cr->cr_graceful = 1;
+	next_state = nua_callstate_terminating;
+      }
+    }
+  }
+  else if (status < 300) {
+    next_state = nua_callstate_completing;
+  }
+  else if (cr->cr_terminated) {
+    next_state = nua_callstate_terminated;
+  }
+  else if (cr->cr_graceful && ss->ss_state >= nua_callstate_completing) {
+    next_state = nua_callstate_terminating;
+  }
+  else {
+    next_state = nua_callstate_init;
+  }
 
-    su_free(NULL, reason);
+  if (next_state == nua_callstate_calling) {
+    if (sip && sip->sip_status && sip->sip_status->st_status == 100) {
+      ss->ss_reporting = 0;
+      return 1;
+    }
   }
 
-  return 0;
+  if (next_state == nua_callstate_completing) {
+    if (NH_PGET(nh, auto_ack) ||
+	/* Auto-ACK response to re-INVITE unless auto_ack is set to 0 */
+	(ss->ss_state == nua_callstate_ready &&
+	 !NH_PISSET(nh, auto_ack))) {
+
+      if (nua_invite_client_ack(cr, NULL) > 0)
+	next_state = nua_callstate_ready;
+      else
+	next_state = nua_callstate_terminating;
+    }
+  }
+
+  if (next_state == nua_callstate_terminating) {
+    /* Send BYE or CANCEL */
+    /* XXX - Forking - send BYE to early dialog?? */
+    if (ss->ss_state > nua_callstate_proceeding || status >= 200)
+      error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
+    else
+      error = nua_client_create(nh, nua_r_cancel, 
+				&nua_cancel_client_methods, tags);
+
+    if (error) {
+      next_state = nua_callstate_terminated;
+      cr->cr_terminated = 1;
+    }
+    cr->cr_graceful = 0;
+  }
+
+  ss->ss_reporting = 0;
+
+  signal_call_state_change(nh, ss, status, phrase, next_state);
+
+  return 1;
 }
 
 /**@fn void nua_ack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
@@ -798,7 +956,7 @@
  * Acknowledge a succesful response to INVITE request.
  *
  * Acknowledge a successful response (200..299) to INVITE request with the
- * SIP ACK request message. This function is need only if NUTAG_AUTOACK()
+ * SIP ACK request message. This function is needed only if NUTAG_AUTOACK()
  * parameter has been cleared.
  *
  * @param nh              Pointer to operation handle
@@ -820,201 +978,200 @@
 int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
 		  tagi_t const *tags)
 {
-  nua_session_usage_t *ss;
-  nua_client_request_t *cr;
-  nta_outgoing_t *ack = NULL;
-  msg_t *msg;
-  sip_t *sip;
-  int status = 200;
-  char const *phrase = "OK", *reason = NULL, *sent = NULL;
-  char const *received;
-
-  ss = nua_session_usage_get(nh->nh_ds);
-  cr = ss->ss_crequest;
+  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;
 
-  received = ss ? ss->ss_ack_needed : NULL;
+  if (!cr || cr->cr_orq == NULL || cr->cr_status < 200) {
+    UA_EVENT2(nua_i_error, 900, "No response to ACK");
+    return 1;
+  }
 
-  if (!received)
-    return UA_EVENT2(nua_i_error, 900, "No response to ACK");
+  if (tags) {
+    nua_stack_set_params(nua, nh, nua_i_error, tags);
+    if (nh->nh_soa)
+      soa_set_params(nh->nh_soa, TAG_NEXT(tags));
+  }
 
-  ss->ss_ack_needed = 0;
+  error = nua_invite_client_ack(cr, tags);
 
-  if (!received[0])
-    received = NULL;
+  if (error < 0) {
+    if (ss->ss_reason == NULL)
+      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);
+    ss->ss_reporting = 0;
+    signal_call_state_change(nh, ss, 500, "Internal Error", 
+			     error 
+			     ? nua_callstate_terminated
+			     : nua_callstate_terminating);
+  }
+  else if (ss)
+    signal_call_state_change(nh, ss, 200, "ACK sent", nua_callstate_ready);
 
-  if (tags)
-    nua_stack_set_params(nua, nh, nua_i_error, tags);
+  if (!nua_client_is_queued(cr) && !nua_client_is_bound(cr))
+    nua_client_request_destroy(cr);
 
-  msg = nua_creq_msg(nua, nh, cr, 0,
-		     SIP_METHOD_ACK,
-		     /* NUTAG_COPY(0), */
-		     TAG_NEXT(tags));
-  sip = sip_object(msg);
+  nua_client_next_request(nh->nh_ds->ds_cr, 1);
 
-  if (sip && nh->nh_soa) {
-    if (tags)
-      soa_set_params(nh->nh_soa, TAG_NEXT(tags));
+  return 0;
+}
 
-    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) {
-	reason = soa_error_as_sip_reason(nh->nh_soa);
-	status = 900, phrase = "Internal media error";
-	reason = "SIP;cause=500;text=\"Internal media error\"";
+/** Send ACK, destroy INVITE transaction.
+ *
+ *  @retval 1 if successful
+ *  @retval < 0 if an error occurred
+ */
+static
+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;
+  int error = -1;
+  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);
+
+  msg = nta_outgoing_getrequest(cr->cr_orq);
+  sip = sip_object(msg);  
+  if (!msg)
+    return -1;
+  
+  wa = sip_authorization(sip);
+  pa = sip_proxy_authorization(sip);
+  
+  msg_destroy(msg);
+
+  msg = nta_msg_create(nh->nh_nua->nua_nta, 0);
+  sip = sip_object(msg);  
+  if (!msg)
+    return -1;
+
+  cseq = sip_cseq_create(msg_home(msg), cr->cr_seq, SIP_METHOD_ACK);
+
+  if (!cseq)
+    ;
+  else if (nh->nh_tags && sip_add_tl(msg, sip, TAG_NEXT(nh->nh_tags)) < 0)
+    ;
+  else if (tags && sip_add_tl(msg, sip, TAG_NEXT(tags)) < 0)
+    ;
+  else if (wa && sip_add_dup(msg, sip, (sip_header_t *)wa) < 0)
+    ;
+  else if (pa && sip_add_dup(msg, sip, (sip_header_t *)pa) < 0)
+    ;
+  else if (sip_header_insert(msg, sip, (sip_header_t *)cseq) < 0)
+    ;
+  else if (nta_msg_request_complete(msg, ds->ds_leg, SIP_METHOD_ACK, NULL) < 0)
+    ;
+  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 (ss == NULL || ss->ss_state >= nua_callstate_ready)
+      ;
+    else if (cr->cr_offer_recv && !cr->cr_answer_sent) {
+      if (nh->nh_soa == NULL) {
+	if (session_get_description(sip, NULL, NULL))
+	  cr->cr_answer_sent = 1, ss->ss_oa_sent = "answer";
+      }
+      else 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 */
-	sent = "answer";
+	cr->cr_answer_sent = 1, 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 */
+    if (ss == NULL || ss->ss_state >= nua_callstate_ready || reason)
+      ;
+    else if (nh->nh_soa
+	     ? soa_is_complete(nh->nh_soa)
+	     : !(cr->cr_offer_sent && !cr->cr_answer_recv)) {
+      /* signal that O/A round(s) is (are) complete */
+      if (nh->nh_soa) 
+	soa_activate(nh->nh_soa, NULL);
+    }
+    else {
+      /* No SDP answer -> terminate call */
       status = 988, phrase = "Incomplete offer/answer";
       reason = "SIP;cause=488;text=\"Incomplete offer/answer\"";
     }
-  }
-
-  if (sip) {
-    msg_t *imsg = nta_outgoing_getrequest(cr->cr_orq);
-    sip_t const *isip = sip_object(imsg);
-    if (isip->sip_proxy_authorization)
-      sip_add_dup(msg, sip, (void *)isip->sip_proxy_authorization);
-    if (isip->sip_authorization)
-      sip_add_dup(msg, sip, (void *)isip->sip_authorization);
-    msg_destroy(imsg);
-  }
-
-  if (sip)
-    ack = nta_outgoing_mcreate(nua->nua_nta, NULL, NULL, NULL, msg,
-			       SIPTAG_END(), TAG_NEXT(tags));
+    
+    if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL,
+				    msg,
+				    SIPTAG_END(),
+				    TAG_NEXT(tags)))) {
+      nta_outgoing_destroy(ack);	/* TR engine keeps this around for T2 */
 
-  if (!ack) {
-    if (!reason) {
+      if (nh->nh_soa && reason && ss && ss->ss_state <= nua_callstate_ready)
+	nua_stack_event(nh->nh_nua, nh, NULL,
+			nua_i_media_error, status, phrase,
+			NULL);
+    }
+    else if (!reason) {
       status = 900, phrase = "Cannot send ACK";
       reason = "SIP;cause=500;text=\"Internal Error\"";
     }
-    msg_destroy(msg);
-  }
 
-  nua_creq_deinit(cr, NULL);	/* Destroy INVITE transaction */
-  nta_outgoing_destroy(ack);	/* TR engine keeps this around for T2 */
+    if (ss && reason)
+      ss->ss_reason = reason;
 
-  if (status < 300) {
-    signal_call_state_change(nh, ss, status, phrase, nua_callstate_ready,
-			     received, sent);
-  }
-  else {
-    signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminating,
-			     0, 0);
-    nua_stack_post_signal(nh, nua_r_bye,
-			  SIPTAG_REASON_STR(reason),
-			  TAG_END());
+    if (status < 300)
+      error = 1;
+    else
+      error = -2;
   }
 
-  return 0;
-}
-
-
-/* Process reliable provisional response */
-static int
-process_100rel(nua_handle_t *nh,
-	       nua_session_usage_t *ss,
-	       nta_outgoing_t *orq,
-	       sip_t const *sip)
-{
-  nua_client_request_t *cr_invite = ss->ss_crequest;
-  nua_client_request_t *cr_prack = nh->nh_ds->ds_cr;
-  
-  sip_rseq_t *rseq;
-  char const *recv = NULL;
-  int status; char const *phrase;
-
-  if (cr_prack->cr_orq) {
-    /* XXX - better luck next time */
-    SU_DEBUG_3(("nua(%p): cannot send PRACK because %s is pending\n", nh,
-		nta_outgoing_method_name(cr_prack->cr_orq)));
-    return 0; /* Wait until this response is re-transmitted */
-  }
+  if (error == -1)
+    msg_destroy(msg);
 
-  if (!nua_dialog_is_established(nh->nh_ds)) {
-    /* Establish early dialog */
-    nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
-    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
-    
-    /* Tag the INVITE request */
-    cr_invite->cr_orq =
-      nta_outgoing_tagged(orq, process_response_to_invite, nh,
-			  sip->sip_to->a_tag, sip->sip_rseq);
-    nta_outgoing_destroy(orq);
-    orq = cr_invite->cr_orq;
-  }
+  nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
+  nua_client_request_remove(cr);
   
-  assert(sip);
-
-  status = sip->sip_status->st_status, phrase = sip->sip_status->st_phrase;
-  rseq = sip->sip_rseq;
-
-  if (!rseq) {
-    SU_DEBUG_5(("nua(%p): 100rel missing RSeq\n", nh));
-  }
-  else if (rseq->rs_response <= nta_outgoing_rseq(orq)) {
-    SU_DEBUG_5(("nua(%p): 100rel bad RSeq %u (got %u)\n", nh, 
-		(unsigned)rseq->rs_response,
-		nta_outgoing_rseq(orq)));
-    /* XXX - send nua_r_invite event or not? */
-    return 0;
-  }
-  else if (nta_outgoing_setrseq(orq, rseq->rs_response) < 0) {
-    SU_DEBUG_1(("nua(%p): cannot set RSeq %u\n", nh, 
-		(unsigned)rseq->rs_response));
-  }
-  else if (session_process_response(nh, cr_invite, orq, sip, &recv) < 0) {
-    assert(nh->nh_soa);
-    status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-    nua_stack_event(nh->nh_nua, nh, NULL,
-		    nua_i_media_error, status, phrase, TAG_END());
-  }
-  /* Here we could let application PRACK and just send state event */
-  else {
-    sip_rack_t rack[1];
-    tagi_t tags[] = {
-      { TAG_SKIP(nua_stack_prack) }, /* this is autoprack */
-      { NUTAG_STATUS(status), },
-      { NUTAG_PHRASE(phrase), },
-      { NUTAG_PHRASE(recv), },
-      { SIPTAG_RACK(rack) }, 
-      { TAG_END() }
-    };
-
-    sip_rack_init(rack);
-
-    rack->ra_response    = sip->sip_rseq->rs_response;
-    rack->ra_cseq        = sip->sip_cseq->cs_seq;
-    rack->ra_method      = sip->sip_cseq->cs_method;
-    rack->ra_method_name = sip->sip_cseq->cs_method_name;
-
-    nua_stack_prack(nh->nh_nua, nh, nua_r_prack, tags);
-
-    return 0;
-  }
+  return error;
+}
 
-  /* XXX - CANCEL INVITE or BYE this session? */
-  /* Because we don't do forking very well we just cancel INVITE */
-  nua_stack_cancel(nh->nh_nua, nh, nua_r_cancel, NULL);
+/** Deinitialize client request */
+static int nua_invite_client_deinit(nua_client_request_t *cr)
+{
+  if (cr->cr_orq == NULL)
+    /* Xyzzy */;
+  else if (cr->cr_status < 200)
+    nta_outgoing_cancel(cr->cr_orq);
+  else
+    nua_invite_client_ack(cr, NULL);
 
   return 0;
 }
 
-/**@fn void nua_prack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
- * Send a PRACK request. 
+/**@fn void nua_cancel(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
  *
- * PRACK is used to acknowledge receipt of 100rel responses. See @RFC3262.
+ * Cancel an INVITE operation 
  *
  * @param nh              Pointer to operation handle
  * @param tag, value, ... List of tagged parameters
@@ -1023,300 +1180,142 @@
  *    nothing
  *
  * @par Related Tags:
- *    Tags in <sofia-sip/soa_tag.h>, <sofia-sip/sip_tag.h>.
+ *    Tags in <sip_tag.h>
  *
  * @par Events:
- *    #nua_r_prack
+ *    #nua_r_cancel, #nua_i_state  (#nua_i_active, #nua_i_terminated)
+ *
+ * @sa @ref nua_call_model, nua_invite(), #nua_i_cancel
  */
 
-/** @NUA_EVENT nua_r_prack
+static int nua_cancel_client_request(nua_client_request_t *cr,
+				     msg_t *msg, sip_t *sip,
+				     tagi_t const *tags);
+
+nua_client_methods_t const nua_cancel_client_methods = {
+  SIP_METHOD_CANCEL,
+  0,
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 1,
+    /* target refresh */ 0
+  },
+  NULL,
+  NULL,
+  nua_cancel_client_request,
+  /* nua_cancel_client_check_restart */ NULL,
+  /* nua_cancel_client_response */ NULL
+};
+
+int nua_stack_cancel(nua_t *nua, nua_handle_t *nh, nua_event_t e,
+		     tagi_t const *tags)
+{
+  return nua_client_create(nh, e, &nua_cancel_client_methods, tags);
+}
+
+static int nua_cancel_client_request(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 = nua_dialog_usage_for_session(nh->nh_ds);
+
+  if (!du || !du->du_cr || !du->du_cr->cr_orq || 
+      nta_outgoing_status(du->du_cr->cr_orq) >= 200) {
+    return nua_client_return(cr, 481, "No transaction to CANCEL", msg);
+  }
+
+  cr->cr_orq = nta_outgoing_tcancel(du->du_cr->cr_orq,
+				    nua_client_orq_response, cr,
+				    TAG_NEXT(tags));
+
+  return cr->cr_orq ? 0 : -1;
+}
+
+/** @NUA_EVENT nua_r_cancel
  *
- * Response to an outgoing @b PRACK request. PRACK request is used to
- * acknowledge reliable preliminary responses and it is usually sent
- * automatically by the nua stack.
+ * Answer to outgoing CANCEL.
  *
- * @param status response status code
- *               (if the request is retried, @a status is 100, the @a
- *               sip->sip_status->st_status contain the real status code
- *               from the response message, e.g., 302, 401, or 407)
+ * The CANCEL may be sent explicitly by nua_cancel() or implicitly by NUA
+ * state machine.
+ *
+ * @param status response status code 
  * @param phrase a short textual description of @a status code
  * @param nh     operation handle associated with the call
  * @param hmagic application context associated with the call
- * @param sip    response to @b PRACK or NULL upon an error
+ * @param sip    response to CANCEL request or NULL upon an error
  *               (status code is in @a status and 
  *                descriptive message in @a phrase parameters)
  * @param tags   empty
  *
- * @sa nua_prack(), #nua_i_prack, @RFC3262
+ * @sa nua_cancel(), @ref nua_uac_call_model, #nua_r_invite, nua_invite(),
+ * #nua_i_state
  *
  * @END_NUA_EVENT
  */
 
-
-int nua_stack_prack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		    tagi_t const *tags)
-{
-  nua_session_usage_t *ss;
-  nua_client_request_t *cr;
-  msg_t *msg;
-  sip_t *sip;
-  int offer_sent_in_prack = 0, answer_sent_in_prack = 0;
-
-  int status = 0; char const *phrase = "PRACK sent";
-  char const *recv = NULL, *sent = NULL;
-
-  int autoprack =		/* XXX - should have common indication */
-    tags && tags->t_tag == tag_skip && 
-    tags->t_value == (tag_value_t)nua_stack_prack;
-
-  if (autoprack) {
-    status = (int)tags[1].t_value; 
-    phrase = (char const *)tags[2].t_value;
-    recv = (char const *)tags[3].t_value;
-    tags += 4;
-  }
-
-  ss = nua_session_usage_get(nh->nh_ds);
-
-  if (!ss || !ss->ss_crequest || !nta_outgoing_rseq(ss->ss_crequest->cr_orq))
-    return UA_EVENT2(e, 900, "Nothing to PRACK");
-  else if (nh->nh_ds->ds_cr->cr_orq)
-    return UA_EVENT2(e, 900, "Request already in progress");
-
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  cr = nh->nh_ds->ds_cr;
-
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
-		     SIP_METHOD_PRACK,
-		     NUTAG_USE_DIALOG(1),
-		     NUTAG_ADD_CONTACT(1),
-		     TAG_NEXT(tags));
-
-  sip = sip_object(msg);
-
-  if (sip) {
-    nua_client_request_t *cri = ss->ss_crequest;
-    if (nh->nh_soa == NULL)
-      /* It is up to application to handle SDP */;
-    else if (sip->sip_payload)
-      /* XXX - we should just do MIME in session_include_description() */;
-    else if (cri->cr_offer_recv && !cri->cr_answer_sent) {
-
-      if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
-	  session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
-
-	status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-	SU_DEBUG_3(("nua(%p): PRACK answer: %d %s\n", nh, status, phrase));
-	nua_stack_event(nh->nh_nua, nh, NULL,
-			nua_i_media_error, status, phrase, TAG_END());
-
-	goto error;
-      }
-      else {
-	answer_sent_in_prack = 1, sent = "answer";
-	soa_activate(nh->nh_soa, NULL);
-      }
-    }
-    /* When 100rel response status was 183 fake support for preconditions */
-    else if (autoprack && status == 183 && ss->ss_precondition) {
-
-      if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0 ||
-	  session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
-
-	status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-	SU_DEBUG_3(("nua(%p): PRACK offer: %d %s\n", nh, status, phrase));
-	nua_stack_event(nh->nh_nua, nh, NULL,
-			nua_i_media_error, status, phrase, TAG_END());
-	goto error;
-      }
-      else {
-	offer_sent_in_prack = 1, sent = "offer";
-      }
-    }
-
-    if (nh->nh_auth) {
-      if (auc_authorize(&nh->nh_auth, msg, sip) < 0)
-	/* xyzzy */;
-    }
-
-    cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				      process_response_to_prack, nh, NULL,
-				      msg,
-				      SIPTAG_END(), TAG_NEXT(tags));
-    if (cr->cr_orq) {
-      cr->cr_usage = nua_dialog_usage_public(ss);
-      cr->cr_event = nua_r_prack;
-
-      if (answer_sent_in_prack)
-	cri->cr_answer_sent = 1;
-      else if (offer_sent_in_prack)
-	cr->cr_offer_sent = 1;
-
-      if (autoprack) 
-	signal_call_state_change(nh, ss, status, phrase,
-				 nua_callstate_proceeding, recv, sent);
-      else
-	signal_call_state_change(nh, ss, 0, "PRACK sent",
-				 nua_callstate_proceeding, NULL, sent);
-	
-
-      return cr->cr_event = e;
-    }
-  }
-
- error:
-  msg_destroy(msg);
-  return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-}
-
-void restart_prack(nua_handle_t *nh, tagi_t *tags)
-{
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_prack, tags);
-}
-
-
-static int
-process_response_to_prack(nua_handle_t *nh,
-			  nta_outgoing_t *orq,
-			  sip_t const *sip)
-{
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage);
-  int status;
-  char const *phrase = "OK", *reason = NULL, *recv = NULL;
-
-  assert(cr->cr_usage && cr->cr_usage->du_class == nua_session_usage);
-
-  if (sip)
-    status = sip->sip_status->st_status, phrase = sip->sip_status->st_phrase;
-  else
-    status = 408, phrase = sip_408_Request_timeout;
-
-  SU_DEBUG_5(("nua: process_response_to_prack: %u %s\n", status, phrase));
-
-  if (nua_creq_check_restart(nh, cr, orq, sip, restart_prack))
-    return 0;
-
-  if (status < 200)
-    return 0;
-
-  cr->cr_usage = NULL;
-
-  if (status < 300) {
-    if (session_process_response(nh, cr, orq, sip, &recv) < 0) {
-      status = 900, phrase = "Malformed Session in Response";
-      reason = "SIP;status=400;phrase=\"Malformed Session in Response\"";
-    }
-  }
-  else
-    nua_stack_process_response(nh, cr, orq, sip, TAG_END());
-
-  if (recv)
-    signal_call_state_change(nh, ss, status, phrase,
-			     nua_callstate_proceeding, recv, NULL);
-
-  if (status < 300 && ss->ss_update_needed)
-    nua_stack_update(nh->nh_nua, nh, nua_r_update, NULL);
-
-  return 0;
-}
-
-/** Refresh session usage */
 static void nua_session_usage_refresh(nua_handle_t *nh,
 				      nua_dialog_state_t *ds,
 				      nua_dialog_usage_t *du,
 				      sip_time_t now)
 {
-  tagi_t const timer_tags[2] = {
-    { SIPTAG_SUBJECT_STR("Session refresh") }, 
-    { TAG_END() }
-  };
-  tagi_t const refresh_tags[2] = {
-    { SIPTAG_SUBJECT_STR("Dialog refresh") }, 
-    { TAG_END() }
-  };
-
-  nua_session_usage_t const *ss = nua_dialog_usage_private(du);
-  nua_client_request_t const *cri = ss->ss_crequest, *cro = ds->ds_cr;
+  nua_session_usage_t *ss = nua_dialog_usage_private(du);
+  nua_client_request_t const *cr = du->du_cr;
   nua_server_request_t const *sr;
 
+  assert(cr);
+
+  if (ss->ss_state >= nua_callstate_terminating || 
+      /* No INVITE template */
+      cr == NULL || 
+      /* INVITE is in progress or being authenticated */
+      cr->cr_orq || cr->cr_challenged)
+    return;
+
+  /* UPDATE in progress or being authenticated */
+  for (cr = ds->ds_cr; cr; cr = cr->cr_next) 
+    if (cr->cr_method == sip_method_update)
+      return;
+
+  /* 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 || 
 	 sr->sr_method == sip_method_update))
-      break;
-
-  /* INVITE or UPDATE in progress or being authenticated */
-  if ((cri && cri->cr_orq) || sr)	
-    return;
-  if (ss->ss_state >= nua_callstate_terminating)
-    return;
+      return;
 
   if (!ss->ss_refresher) {
-    if (now >= du->du_expires)
-      session_timeout(nh, du, now);
-    else
-      /* Refreshing contact & route set */
-      nua_stack_invite2(nh->nh_nua, nh, nua_r_invite, 1, refresh_tags);
+    if (du->du_expires == 0 || now < du->du_expires)
+      /* Refresh contact & route set using re-INVITE */
+      nua_client_resend_request(du->du_cr, 0);
+    else {
+      ss->ss_reason = "SIP;cause=408;text=\"Session timeout\""; 
+      nua_stack_bye(nh->nh_nua, nh, nua_r_bye, NULL);
+    }
   }
   else if (NH_PGET(nh, update_refresh)) {
-    if (!cro->cr_orq)
-      nua_stack_update(nh->nh_nua, nh, nua_r_update, timer_tags);
-    else
-      nua_dialog_usage_refresh_range(du, 5, 15);
+    nua_stack_update(nh->nh_nua, nh, nua_r_update, NULL);
   }
   else {
-    nua_stack_invite2(nh->nh_nua, nh, nua_r_invite, 1, timer_tags);
-  }
-}
-
-static
-char const reason_timeout[] = "SIP;cause=408;text=\"Session timeout\"";
-
-static void
-session_timeout(nua_handle_t *nh, nua_dialog_usage_t *du, sip_time_t now)
-{
-  if (now > 1) {
-    nua_session_usage_t *ss = nua_dialog_usage_private(du);
-
-    signal_call_state_change(nh, ss, 408, "Session Timeout",
-			     nua_callstate_terminating, NULL, NULL);
-
-    nua_stack_post_signal(nh, nua_r_bye,
-			  SIPTAG_REASON_STR(reason_timeout),
-			  TAG_END());
+    nua_client_resend_request(du->du_cr, 0);
   }
 }
 
-/** Terminate usage/dialog/handle/agent gracefully */
+/** @interal Shut down session usage. 
+ *
+ * @retval >0  shutdown done
+ * @retval 0   shutdown in progress
+ * @retval <0  try again later
+ */
 static int nua_session_usage_shutdown(nua_handle_t *nh,
 				      nua_dialog_state_t *ds,
 				      nua_dialog_usage_t *du)
 {
   nua_session_usage_t *ss = nua_dialog_usage_private(du);
-  nua_client_request_t *cr;
   nua_server_request_t *sr, *sr_next;
-  int status;
-
-  /* Zap client-side invite transaction */
-  if (ss->ss_crequest->cr_orq) {
-    cr = ss->ss_crequest;
-    status = nta_outgoing_status(cr->cr_orq);
+  nua_client_request_t *cri;
 
-    if (status < 200) 
-      nta_outgoing_tcancel(cr->cr_orq, NULL, NULL, TAG_END());
-
-    if (ss->ss_ack_needed) {
-      msg_t *ack = nua_creq_msg(nh->nh_nua, nh, cr, 0,
-				SIP_METHOD_ACK,
-				TAG_END());
-      nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL, NULL, NULL, 
-			   ack, TAG_END());
-    }
-
-    nua_creq_deinit(cr, NULL);
-  }
+  assert(ss == nua_session_usage_for_dialog(nh->nh_ds));
 
   /* Zap server-side transactions */
   for (sr = ds->ds_sr; sr; sr = sr_next) {
@@ -1324,54 +1323,58 @@
     if (sr->sr_usage == du) {
       assert(sr->sr_usage == du);
       sr->sr_usage = NULL;
-      if (sr->sr_respond) 
-	nua_server_respond(sr, SIP_480_TEMPORARILY_UNAVAILABLE, TAG_END());
+
+      if (nua_server_request_is_pending(sr)) {
+	SR_STATUS1(sr, SIP_480_TEMPORARILY_UNAVAILABLE);
+	nua_server_respond(sr, NULL);
+	if (nua_server_report(sr) >= 2)
+	  return 480;
+      }
       else
 	nua_server_request_destroy(sr);
     }
   }
 
-  assert(ss == nua_session_usage_get(nh->nh_ds));
+  cri = du->du_cr;
 
   switch (ss->ss_state) {
+  case nua_callstate_calling:
+  case nua_callstate_proceeding:
+    return nua_client_create(nh, nua_r_cancel, &nua_cancel_client_methods, NULL);
+
   case nua_callstate_completing:
-  case nua_callstate_ready:
   case nua_callstate_completed:
-    {
-      msg_t *bye;
-
-      cr = ds->ds_cr;
-      nua_creq_deinit(cr, NULL);
-      bye = nua_creq_msg(nh->nh_nua, nh, ds->ds_cr, 0, 
-			 SIP_METHOD_BYE,
-			 TAG_END());
-      cr->cr_orq = nta_outgoing_mcreate(nh->nh_nua->nua_nta,
-					NULL, NULL, NULL,
-					bye,
-					TAG_END());
-      nua_creq_deinit(cr, NULL);
+  case nua_callstate_ready:
+    if (cri && cri->cr_orq) {
+      if (cri->cr_status < 200)
+	nua_client_create(nh, nua_r_cancel, &nua_cancel_client_methods, NULL);
+      else if (cri->cr_status < 300)
+	nua_invite_client_ack(cri, NULL);
     }
-  }
+    if (nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL) != 0)
+      break;
 
-  nua_dialog_usage_remove(nh, ds, du);
+    signal_call_state_change(nh, ss, 487, "BYE sent",
+			     nua_callstate_terminating);
+    return 0;
 
-  return 0;
-}
+  case nua_callstate_terminating:
+  case nua_callstate_terminated: /* XXX */
+    return 0;
 
-/** Restart invite (e.g., after 302 or 407) */
-void
-restart_invite(nua_handle_t *nh, tagi_t *tags)
-{
-  nua_stack_invite2(nh->nh_nua, nh, nua_r_invite, 1, tags);
-}
+  default:
+    break;
+  }
+  
+  nua_dialog_usage_remove(nh, ds, du);
 
-static int process_response_to_cancel(nua_handle_t *nh,
-				      nta_outgoing_t *orq,
-				      sip_t const *sip);
+  return 200;
+}
 
-/**@fn void nua_cancel(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
+/**@fn void nua_prack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
+ * Send a PRACK request. 
  *
- * Cancel an INVITE operation 
+ * PRACK is used to acknowledge receipt of 100rel responses. See @RFC3262.
  *
  * @param nh              Pointer to operation handle
  * @param tag, value, ... List of tagged parameters
@@ -1380,108 +1383,205 @@
  *    nothing
  *
  * @par Related Tags:
- *    Tags in <sip_tag.h>
+ *    Tags in <sofia-sip/soa_tag.h>, <sofia-sip/sip_tag.h>.
  *
  * @par Events:
- *    #nua_r_cancel, #nua_i_state  (#nua_i_active, #nua_i_terminated)
- *
- * @sa @ref nua_call_model, nua_invite(), #nua_i_cancel
+ *    #nua_r_prack
  */
 
-int
-nua_stack_cancel(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		 tagi_t const *tags)
-{
-  nua_session_usage_t *ss;
-  nua_client_request_t *cri, *crc;
-
-  ss = nua_session_usage_get(nh->nh_ds);
-
-  if (!nh || !ss || !ss->ss_crequest->cr_usage ||
-      nta_outgoing_status(ss->ss_crequest->cr_orq) >= 200) {
-    return UA_EVENT2(e, 481, "No transaction to CANCEL");
-  }
-
-  cri = ss->ss_crequest;
-  crc = nh->nh_ds->ds_cr;
-
-  if (tags)
-    nua_stack_set_params(nua, nh, nua_i_error, tags);
-
-  if (nh && cri->cr_orq && cri->cr_usage) {
-    nta_outgoing_t *orq;
-
-    /* nh_referral_respond(nh, SIP_487_REQUEST_TERMINATED); */
-
-    if (e)
-      orq = nta_outgoing_tcancel(cri->cr_orq, process_response_to_cancel, nh,
-				 TAG_NEXT(tags));
-    else
-      orq = nta_outgoing_tcancel(cri->cr_orq, NULL, NULL, TAG_NEXT(tags));
-
-    if (orq == NULL)
-      return nua_stack_event(nua, nh, NULL, e, 400, "Internal error",
-			     TAG_END());
-
-    if (e && crc->cr_orq == NULL)
-      crc->cr_orq = orq, crc->cr_event = e;
-  }
-
-  return 0;
-}
-
-/** @NUA_EVENT nua_r_cancel
- *
- * Answer to outgoing CANCEL.
+/** @NUA_EVENT nua_r_prack
  *
- * The CANCEL may be sent explicitly by nua_cancel() or implicitly by NUA
- * state machine.
+ * Response to an outgoing @b PRACK request. PRACK request is used to
+ * acknowledge reliable preliminary responses and it is usually sent
+ * automatically by the nua stack.
  *
- * @param status response status code 
+ * @param status response status code
+ *               (if the request is retried, @a status is 100, the @a
+ *               sip->sip_status->st_status contain the real status code
+ *               from the response message, e.g., 302, 401, or 407)
  * @param phrase a short textual description of @a status code
  * @param nh     operation handle associated with the call
  * @param hmagic application context associated with the call
- * @param sip    response to CANCEL request or NULL upon an error
+ * @param sip    response to @b PRACK or NULL upon an error
  *               (status code is in @a status and 
  *                descriptive message in @a phrase parameters)
  * @param tags   empty
  *
- * @sa nua_cancel(), @ref nua_uac_call_model, #nua_r_invite, nua_invite(),
- * #nua_i_state
+ * @sa nua_prack(), #nua_i_prack, @RFC3262
  *
  * @END_NUA_EVENT
  */
 
+static int nua_prack_client_init(nua_client_request_t *cr, 
+				 msg_t *msg, sip_t *sip,
+				 tagi_t const *tags);
+static int nua_prack_client_request(nua_client_request_t *cr,
+				    msg_t *msg, sip_t *sip,
+				    tagi_t const *tags);
+static int nua_prack_client_response(nua_client_request_t *cr,
+				     int status, char const *phrase,
+				     sip_t const *sip);
+static int nua_prack_client_report(nua_client_request_t *cr,
+				   int status, char const *phrase,
+				   sip_t const *sip,
+				   nta_outgoing_t *orq,
+				   tagi_t const *tags);
 
+nua_client_methods_t const nua_prack_client_methods = {
+  SIP_METHOD_PRACK,
+  0,
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 1,
+    /* target refresh */ 0
+  },
+  NULL,
+  nua_prack_client_init,
+  nua_prack_client_request,
+  /* nua_prack_client_check_restart */ NULL,
+  nua_prack_client_response,
+  NULL,
+  nua_prack_client_report
+};
 
-static int process_response_to_cancel(nua_handle_t *nh,
-				      nta_outgoing_t *orq,
-				      sip_t const *sip)
+int nua_stack_prack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
+		     tagi_t const *tags)
 {
-  return nua_stack_process_response(nh, nh->nh_ds->ds_cr, orq, sip, TAG_END());
+  return nua_client_create(nh, e, &nua_prack_client_methods, tags);
 }
 
-/* ---------------------------------------------------------------------- */
-/* UAS side of INVITE */
+static int nua_prack_client_init(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 = nua_dialog_usage_for_session(nh->nh_ds);
 
-static int respond_to_invite(nua_server_request_t *sr, tagi_t const *tags);
+  cr->cr_usage = du;
 
-static int
-  preprocess_invite(nua_t *, nua_handle_t *, nua_server_request_t **, sip_t *),
-  session_check_request(nua_t *nua,
-			nua_handle_t *nh,
-			nta_incoming_t *irq,
-			sip_t const *sip),
-  process_invite(nua_t *, nua_handle_t *, nua_server_request_t *, sip_t *),
-  process_prack(nua_handle_t *, nta_reliable_t *, nta_incoming_t *,
-		sip_t const *);
+  return 0;
+}
 
-static int
-  process_ack_or_cancel(nua_server_request_t *, nta_incoming_t *, 
-			sip_t const *),
-  process_ack(nua_server_request_t *, nta_incoming_t *, sip_t const *),
-  process_cancel(nua_server_request_t *, nta_incoming_t *, sip_t const *),
-  process_timeout(nua_server_request_t *, nta_incoming_t *);
+static int nua_prack_client_request(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);
+  nua_client_request_t *cri;
+  int offer_sent = 0, answer_sent = 0, retval;
+  int status = 0; char const *phrase = "PRACK Sent";
+  uint32_t rseq = 0;
+
+  if (du == NULL)		/* Call terminated */
+    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);
+  assert(ss);
+
+  cri = du->du_cr;
+
+  if (sip->sip_rack)
+    rseq = sip->sip_rack->ra_response;
+
+  if (cri->cr_offer_recv && !cri->cr_answer_sent) {
+    if (nh->nh_soa == NULL) 
+      /* It is up to application to handle SDP */
+      answer_sent = session_get_description(sip, NULL, NULL);
+    else if (sip->sip_payload)
+      /* XXX - we should just do MIME in session_include_description() */;
+    else if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
+	     session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
+      status = soa_error_as_sip_response(nh->nh_soa, &phrase);
+      SU_DEBUG_3(("nua(%p): local response to PRACK: %d %s\n",
+		  (void *)nh, status, phrase));
+      nua_stack_event(nh->nh_nua, nh, NULL,
+		      nua_i_media_error, status, phrase,
+		      NULL);
+      return nua_client_return(cr, status, phrase, msg);
+    }
+    else {
+      answer_sent = 1;
+      soa_activate(nh->nh_soa, NULL);
+    }
+  }
+  else if (nh->nh_soa == NULL) {
+    offer_sent = session_get_description(sip, NULL, NULL);
+  }
+  /* When 100rel response status was 183 do support for preconditions */
+  else if (cri->cr_status == 183 && ss->ss_precondition) {
+    if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0 ||
+	session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
+      status = soa_error_as_sip_response(nh->nh_soa, &phrase);
+      SU_DEBUG_3(("nua(%p): PRACK offer: %d %s\n", (void *)nh,
+		  status, phrase));
+      nua_stack_event(nh->nh_nua, nh, NULL,
+		      nua_i_media_error, status, phrase, NULL);
+      return nua_client_return(cr, status, phrase, msg);
+    }
+    else {
+      offer_sent = 1;
+    }
+  }
+
+  retval = nua_base_client_request(cr, msg, sip, NULL);
+
+  if (retval == 0) {
+    cr->cr_offer_sent = offer_sent;
+    cr->cr_answer_sent = answer_sent;
+
+    if (!cr->cr_restarting) {
+      if (offer_sent) 
+	ss->ss_oa_sent = "offer";
+      else if (answer_sent)
+	ss->ss_oa_sent = "answer";
+
+      if (!ss->ss_reporting)
+	signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
+    }
+  }
+
+  return retval;
+}
+
+static int nua_prack_client_response(nua_client_request_t *cr,
+				     int status, char const *phrase,
+				     sip_t const *sip)
+{
+  /* XXX - fatal error cases? */
+
+  return nua_session_client_response(cr, status, phrase, sip);
+}
+
+static int nua_prack_client_report(nua_client_request_t *cr,
+				   int status, char const *phrase,
+				   sip_t const *sip,
+				   nta_outgoing_t *orq,
+				   tagi_t const *tags)
+{
+  nua_handle_t *nh = cr->cr_owner;
+  nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage);
+
+  nua_stack_event(nh->nh_nua, nh, 
+		  nta_outgoing_getresponse(orq),
+		  cr->cr_event,
+		  status, phrase,
+		  tags);
+
+  if (!ss || orq != cr->cr_orq || cr->cr_terminated || cr->cr_graceful)
+    return 1;
+
+  if (cr->cr_offer_sent)
+    signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
+
+  if (ss->ss_update_needed && 200 <= status && status < 300 &&
+      !SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_update))
+    nua_client_create(nh, nua_r_update, &nua_update_client_methods, NULL);
+  
+  return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+/* UAS side of INVITE */
 
 /** @NUA_EVENT nua_i_invite
  *
@@ -1528,16 +1628,18 @@
  * the 2XX response. If @soa is not disabled with NUTAG_MEDIA_ENABLE(0), the
  * SDP should be included in the SOATAG_USER_SDP() or SOATAG_USER_SDP_STR()
  * parameter given to nua_respond(). If it is disabled, the SDP should be
- * included in message
+ * included in the response message using SIPTAG_PAYLOAD() or
+ * SIPTAG_PAYLOAD_STR(). Also, the @ContentType should be set using
+ * SIPTAG_CONTENT_TYPE() or SIPTAG_CONTENT_TYPE_STR().
  *
  * @par Preliminary Responses and 100rel
  *
  * Call progress can be signaled with preliminary responses (with status
  * code in the range 101..199). It is possible to conclude the SDP
  * Offer-Answer negotiation using preliminary responses, too. If
- * SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() parameter is included with in
- * a preliminary nua_response(), the SDP answer is generated and sent with
- * the preliminary responses, too.
+ * NUTAG_EARLY_ANSWER(1), SOATAG_USER_SDP() or SOATAG_USER_SDP_STR()
+ * parameter is included with in a preliminary nua_response(), the SDP
+ * answer is generated and sent with the preliminary responses, too.
  *
  * The preliminary responses are sent reliably if feature tag "100rel" is
  * included in the @Require header of the response or if
@@ -1560,7 +1662,8 @@
  *
  * @sa nua_respond(), @ref nua_uas_call_model, #nua_i_state,
  * NUTAG_MEDIA_ENABLE(), SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(),
- * @RFC3262, NUTAG_EARLY_MEDIA(), NUTAG_ONLY183_100REL(), 
+ * @RFC3262, NUTAG_EARLY_ANSWER(), NUTAG_EARLY_MEDIA(), 
+ * NUTAG_ONLY183_100REL(), 
  * NUTAG_INCLUDE_EXTRA_SDP(),
  * #nua_i_prack, #nua_i_update, nua_update(),
  * nua_invite(), #nua_r_invite
@@ -1579,193 +1682,206 @@
  * @END_NUA_EVENT
  */
 
-/** @internal Process incoming INVITE. */
-int nua_stack_process_invite(nua_t *nua,
-			     nua_handle_t *nh,
-			     nta_incoming_t *irq,
-			     sip_t const *sip)
-{
-  nua_server_request_t *sr, sr0[1];
-  int status;
-  
-  sr = SR_INIT(sr0);
-  sr->sr_irq = irq;
-
-  status = preprocess_invite(nua, nh, &sr, (sip_t *)sip);
+static int nua_invite_server_init(nua_server_request_t *sr);
+static int nua_session_server_init(nua_server_request_t *sr);
+static int nua_invite_server_preprocess(nua_server_request_t *sr);
+static int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *);
+static int nua_invite_server_is_100rel(nua_server_request_t *, tagi_t const *);
+static int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *);
 
-  if (status) {
-    if (sr->sr_status > 100) 
-      nta_incoming_treply(irq, sr->sr_status, sr->sr_phrase,
-			  SIPTAG_USER_AGENT_STR(NUA_PGET(nua, nh, user_agent)),
-			  TAG_END());
-    nua_server_request_destroy(sr);
-    /* if something has failed, respond with 500 Internal Server Error */
-    return 500; 
-  }
+static int
+  process_ack_or_cancel(nua_server_request_t *, nta_incoming_t *, 
+			sip_t const *),
+  process_ack(nua_server_request_t *, nta_incoming_t *, sip_t const *),
+  process_cancel(nua_server_request_t *, nta_incoming_t *, sip_t const *),
+  process_timeout(nua_server_request_t *, nta_incoming_t *),
+  process_prack(nua_server_request_t *,
+		nta_reliable_t *rel,
+		nta_incoming_t *irq,
+		sip_t const *sip);
 
-  assert(sr != sr0);
+nua_server_methods_t const nua_invite_server_methods = 
+  {
+    SIP_METHOD_INVITE,
+    nua_i_invite,		/* Event */
+    { 
+      1,			/* Create dialog */
+      0,			/* Initial request */
+      1,			/* Target refresh request  */
+      1,			/* Add Contact */
+    },
+    nua_invite_server_init,
+    nua_invite_server_preprocess,
+    nua_base_server_params,
+    nua_invite_server_respond,
+    nua_invite_server_report,
+  };
 
-  return process_invite(nua, sr->sr_owner, sr, (sip_t *)sip);
-}
 
 /** @internal Preprocess incoming invite - sure we have a valid request. 
  * 
- * @return 0 if request is valid, or error statuscode when request has been 
- * responded.
+ * @return 0 if request is valid, or error statuscode otherwise
  */
-static
-int preprocess_invite(nua_t *nua,
-		      nua_handle_t *nh,
-		      nua_server_request_t **inout_sr,
-		      sip_t *sip)
-{
-  nua_dialog_state_t *ds;
-  nua_server_request_t *sr = *inout_sr;
-  nua_server_request_t const *sr0;
-  nua_dialog_usage_t *du;
-  nua_session_usage_t *ss;
-  int have_sdp;
-  char const *sdp;
-  size_t len;
-
-  if (nh) {
-    ds = nh->nh_ds;
-    du = nua_dialog_usage_get(ds, nua_session_usage, NULL);
-    ss = nua_dialog_usage_private(du);
-  }
-  else {
-    nh = nua->nua_dhandle, ds = NULL, du = NULL, ss = NULL;
-  }
+static int
+nua_invite_server_init(nua_server_request_t *sr)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_t *nua = nh->nh_nua;
 
-  sr->sr_usage = du;
+  sr->sr_neutral = 1;
 
   if (!NUA_PGET(nua, nh, invite_enable))
     return SR_STATUS1(sr, SIP_403_FORBIDDEN);
 
-  if (session_check_request(nua, nh, sr->sr_irq, sip))
-    return 500;
-
-  have_sdp = session_get_description(sip, &sdp, &len);
+  if (nua_session_server_init(sr))
+    return sr->sr_status;
+    
+  if (sr->sr_usage) {
+    /* Existing session - check for overlap and glare */ 
 
-  if (ss) {
-    /* Existing session */ 
+    nua_server_request_t const *sr0;
+    nua_client_request_t const *cr;
 
-    for (sr0 = ds->ds_sr; sr0; sr0 = sr0->sr_next) {
+    for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next) {
       /* Final response have not been sent to previous INVITE */
-      if (sr0->sr_method == sip_method_invite && sr0->sr_respond)
+      if (sr0->sr_method == sip_method_invite && 
+	  nua_server_request_is_pending(sr0))
 	break;
-      /* Or we have sent offer but have not received answer */
-      if (have_sdp && sr0->sr_offer_sent && !sr0->sr_answer_recv)
+      /* Or we have sent offer but have not received an answer */
+      if (sr->sr_sdp && sr0->sr_offer_sent && !sr0->sr_answer_recv)
 	break;
-      /* Or we have received request with offer but not sent answer */
-      if (have_sdp && sr0->sr_offer_recv && !sr0->sr_answer_sent)
+      /* Or we have received request with offer but not sent an answer */
+      if (sr->sr_sdp && sr0->sr_offer_recv && !sr0->sr_answer_sent)
 	break;
     }
     
-    if (sr0)
+    if (sr0) {
       /* Overlapping invites - RFC 3261 14.2 */
-      return respond_with_retry_after(nh, sr->sr_irq, 
-				      500, "Overlapping Requests",
-				      0, 10);
+      return nua_server_retry_after(sr, 500, "Overlapping Requests", 0, 10);
+    }
 
-    if ((ss->ss_crequest && ss->ss_crequest->cr_orq) ||
-	(have_sdp && ds && ds->ds_cr->cr_orq && ds->ds_cr->cr_offer_sent)) {
-      /* Glare - RFC 3261 14.2 and RFC 3311 section 5.2 */
-      return SR_STATUS1(sr, SIP_491_REQUEST_PENDING);
+    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)
+	/* Glare - RFC 3261 14.2 and RFC 3311 section 5.2 */
+	return SR_STATUS1(sr, SIP_491_REQUEST_PENDING);
     }
   }
 
-  /* Create handle and server request structure when needed */
-  sr = nua_server_request(nua, nh, sr->sr_irq, sip, sr, sizeof *sr,
-			  respond_to_invite, create_dialog);
-  *inout_sr = sr;
+  sr->sr_neutral = 0;
 
-  if (sr->sr_status > 100)
-    return sr->sr_status;
+  return 0;
+}
 
-  nh = sr->sr_owner; assert(nh != nua->nua_dhandle);
-  ds = nh->nh_ds;
+/** Initialize session server request.
+ *
+ * Ensure that the request is valid.
+ */
+static int
+nua_session_server_init(nua_server_request_t *sr)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_t *nua = nh->nh_nua;
+
+  msg_t *msg = sr->sr_response.msg;
+  sip_t *sip = sr->sr_response.sip;
+
+  sip_t const *request = sr->sr_request.sip;
+
+  unsigned min = NH_PGET(nh, min_se);
+
+  if (!sr->sr_initial)
+    sr->sr_usage = nua_dialog_usage_get(nh->nh_ds, nua_session_usage, NULL);
+
+  if (sr->sr_method != sip_method_invite && sr->sr_usage == NULL) {
+    /* UPDATE/PRACK sent within an existing dialog? */
+    return SR_STATUS(sr, 481, "Call Does Not Exist");
+  }
 
   if (nh->nh_soa) {
-    soa_init_offer_answer(nh->nh_soa);
+    sip_accept_t *a = nua->nua_invite_accept;
 
-    if (have_sdp) {
-      if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
-	SU_DEBUG_5(("nua(%p): error parsing SDP in INVITE\n", nh));
-	return SR_STATUS(sr, 400, "Bad Session Description");
-      }
-      else
-	sr->sr_offer_recv = 1;
+    /* XXX - soa should know what it supports */
+    sip_add_dup(msg, sip, (sip_header_t *)a);
+
+    /* Make sure caller uses application/sdp without compression */
+    if (nta_check_session_content(NULL, request, a, TAG_END())) {
+      sip_add_make(msg, sip, sip_accept_encoding_class, "");
+      return SR_STATUS1(sr, SIP_415_UNSUPPORTED_MEDIA);
+    }
+
+    /* Make sure caller accepts application/sdp */
+    if (nta_check_accept(NULL, request, a, NULL, TAG_END())) {
+      sip_add_make(msg, sip, sip_accept_encoding_class, "");
+      return SR_STATUS1(sr, SIP_406_NOT_ACCEPTABLE);
     }
   }
 
-  /* Add the session usage */
-  if (du == NULL)
-    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL);
+  if (request->sip_session_expires &&
+      nta_check_session_expires(NULL, request, min, TAG_END())) {
+    sip_min_se_t *min_se, min_se0[1];
 
-  if (!du)
-    return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+    min_se = sip_min_se_init(min_se0);
+    min_se->min_delta = min;
+    
+    if (request->sip_min_se && request->sip_min_se->min_delta > min)
+      min_se = request->sip_min_se;
 
-  sr->sr_usage = du;
+    sip_add_dup(msg, sip, (sip_header_t *)min_se);
+    
+    return SR_STATUS1(sr, SIP_422_SESSION_TIMER_TOO_SMALL);
+  }
+
+  session_get_description(sr->sr_request.sip, &sr->sr_sdp, &sr->sr_sdp_len);
 
   return 0;
 }
 
-static int
-session_check_request(nua_t *nua,
-		      nua_handle_t *nh,
-		      nta_incoming_t *irq,
-		      sip_t const *sip)
+/** Preprocess INVITE.
+ *
+ * This is called after a handle has been created for an incoming INVITE.
+ */
+int nua_invite_server_preprocess(nua_server_request_t *sr)
 {
-  char const *user_agent = NUA_PGET(nua, nh, user_agent);
+  nua_handle_t *nh = sr->sr_owner;
+  nua_dialog_state_t *ds = nh->nh_ds;
+  nua_session_usage_t *ss;
 
-  if (nh->nh_soa) {
-    /* Make sure caller uses application/sdp without compression */
-    if (nta_check_session_content(irq, sip,
-				  nua->nua_invite_accept,
-				  SIPTAG_USER_AGENT_STR(user_agent),
-				  SIPTAG_ACCEPT_ENCODING_STR(""),
-				  TAG_END()))
-      return 415;
+  sip_t const *request = sr->sr_request.sip;
 
-    /* Make sure caller accepts application/sdp */
-    if (nta_check_accept(irq, sip,
-			 nua->nua_invite_accept,
-			 NULL,
-			 SIPTAG_USER_AGENT_STR(user_agent),
-			 SIPTAG_ACCEPT_ENCODING_STR(""),
-			 TAG_END()))
-      return 406;
-  }
-
-  if (sip->sip_session_expires) {
-    unsigned min_se = NH_PGET(nh, min_se);
-    if (sip->sip_min_se && min_se < sip->sip_min_se->min_delta)
-      min_se = sip->sip_min_se->min_delta;
-    if (nta_check_session_expires(irq, sip,
-				  min_se,
-				  SIPTAG_USER_AGENT_STR(user_agent),
-				  TAG_END()))
-      return 422;
+  assert(sr->sr_status == 100);
+  assert(nh != nh->nh_nua->nua_dhandle);
+
+  if (sr->sr_status > 100)
+    return sr->sr_status;
+
+  if (nh->nh_soa)
+    soa_init_offer_answer(nh->nh_soa);
+
+  if (sr->sr_sdp) {
+    if (nh->nh_soa && 
+	soa_set_remote_sdp(nh->nh_soa, NULL, sr->sr_sdp, sr->sr_sdp_len) < 0) {
+      SU_DEBUG_5(("nua(%p): %s server: error parsing SDP\n", (void *)nh, 
+		  "INVITE"));
+      return SR_STATUS(sr, 400, "Bad Session Description");
+    }
+    else
+      sr->sr_offer_recv = 1;
   }
 
-  return 0;
-}
+  /* Add the session usage */
+  if (sr->sr_usage == NULL) {
+    sr->sr_usage = nua_dialog_usage_add(nh, ds, nua_session_usage, NULL);
+    if (sr->sr_usage == NULL)
+      return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+  }
 
-/** @internal Process incoming invite - initiate media, etc. */
-static
-int process_invite(nua_t *nua,
-		   nua_handle_t *nh,
-		   nua_server_request_t *sr,
-		   sip_t *sip)
-{
-  nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
-  int status = sr->sr_status; char const *phrase = sr->sr_phrase;
+  ss = nua_dialog_usage_private(sr->sr_usage);
 
-  assert(ss); assert(status == 100);
+  if (sr->sr_offer_recv)
+    ss->ss_oa_recv = "offer";
 
   ss->ss_100rel = NH_PGET(nh, early_media);
-  ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition");
+  ss->ss_precondition = sip_has_feature(request->sip_require, "precondition");
   if (ss->ss_precondition)
     ss->ss_100rel = 1;
 
@@ -1776,11 +1892,7 @@
 
   /* Session Timer negotiation */
   if (sip_has_supported(NH_PGET(nh, supported), "timer"))
-    init_session_timer(ss, sip, ss->ss_refresher);
-
-  nua_dialog_uas_route(nh, nh->nh_ds, sip, 1);	/* Set route and tags */
-
-  nta_incoming_bind(sr->sr_irq, process_ack_or_cancel, sr);
+    init_session_timer(ss, request, ss->ss_refresher);
 
   assert(ss->ss_state >= nua_callstate_ready ||
 	 ss->ss_state == nua_callstate_init);
@@ -1793,269 +1905,244 @@
 	*/
        nh->nh_soa &&
        !NH_PISSET(nh, auto_answer))) {
-    SET_STATUS1(SIP_200_OK);
+    SR_STATUS1(sr, SIP_200_OK);
+  }
+  else if (NH_PGET(nh, auto_alert)) {
+    if (ss->ss_100rel &&
+	(sip_has_feature(request->sip_supported, "100rel") ||
+	 sip_has_feature(request->sip_require, "100rel"))) {
+      SR_STATUS1(sr, SIP_183_SESSION_PROGRESS);
+    }
+    else {
+      SR_STATUS1(sr, SIP_180_RINGING);
+    }
+  }
+
+  return 0;
+}
+
+
+/** @internal Respond to an INVITE request.
+ *
+ */
+static
+int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_dialog_usage_t *du = sr->sr_usage;
+  nua_session_usage_t *ss = nua_dialog_usage_private(du);
+  msg_t *msg = sr->sr_response.msg; 
+  sip_t *sip = sr->sr_response.sip; 
+
+  int reliable = 0, offer = 0, answer = 0, early_answer = 0, extra = 0;
+
+  enter;
+
+  if (du == NULL) {
+    if (sr->sr_status < 300)
+      sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR);
+    return nua_base_server_respond(sr, tags);
+  }
+
+  if (nua_invite_server_is_100rel(sr, tags)) {
+    reliable = 1, early_answer = 1;
+  }
+  else if (!nh->nh_soa || sr->sr_status >= 300) {
+    
+  }
+  else if (tags && 100 < sr->sr_status && sr->sr_status < 200 && 
+	   !NHP_ISSET(nh->nh_prefs, early_answer)) {
+    sdp_session_t const *user_sdp = NULL;
+    char const *user_sdp_str = NULL;
+
+    tl_gets(tags,
+	    SOATAG_USER_SDP_REF(user_sdp),
+	    SOATAG_USER_SDP_STR_REF(user_sdp_str),
+	    TAG_END());
+
+    early_answer = user_sdp || user_sdp_str;
+  }
+  else {
+    early_answer = NH_PGET(nh, early_answer);
+  }
+
+  if (!nh->nh_soa) {
+    if (session_get_description(sip, NULL, NULL)) {
+      if (sr->sr_offer_recv)
+	answer = 1;
+      else if (sr->sr_offer_sent < 2)
+	offer = 1;
+    }
   }
-  else if (NH_PGET(nh, auto_alert)) {
-    if (ss->ss_100rel &&
-	(sip_has_feature(nh->nh_ds->ds_remote_ua->nr_supported, "100rel") ||
-	 sip_has_feature(nh->nh_ds->ds_remote_ua->nr_require, "100rel"))) {
-      SET_STATUS1(SIP_183_SESSION_PROGRESS);
+  else if (sr->sr_status >= 300) {
+    soa_clear_remote_sdp(nh->nh_soa);
+  }
+  else if (sr->sr_offer_sent && !sr->sr_answer_recv)
+    /* Wait for answer */;
+  else if (sr->sr_offer_recv && sr->sr_answer_sent > 1) {
+    /* We have sent answer */
+    /* ...  but we may want to send it again */
+    tagi_t const *t = tl_find_last(tags, nutag_include_extra_sdp);
+    extra = t && t->t_value;
+  }
+  else if (sr->sr_offer_recv && !sr->sr_answer_sent && early_answer) {
+    /* Generate answer */ 
+    if (soa_generate_answer(nh->nh_soa, NULL) >= 0) {
+      answer = 1;
+      soa_activate(nh->nh_soa, NULL);
+      /* signal that O/A answer sent (answer to invite) */
+    }
+    else if (sr->sr_status >= 200) {
+      sip_warning_t *warning = NULL;
+      int wcode;
+      char const *text;
+      char const *host = "invalid.";
+      
+      sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
+      
+      wcode = soa_get_warning(nh->nh_soa, &text);
+      
+      if (wcode) {
+	if (sip->sip_contact)
+	  host = sip->sip_contact->m_url->url_host;
+	warning = sip_warning_format(msg_home(msg), "%u %s \"%s\"",
+				     wcode, host, text);
+	sip_header_insert(msg, sip, (sip_header_t *)warning);
+      }
     }
     else {
-      SET_STATUS1(SIP_180_RINGING);
+      /* 1xx - we don't have to send answer */
     }
   }
+  else if (sr->sr_offer_recv && sr->sr_answer_sent == 1 && early_answer) {
+    /* The answer was sent unreliably, keep sending it */
+    answer = 1;
+  }
+  else if (!sr->sr_offer_recv && !sr->sr_offer_sent && reliable) {
+    /* Generate offer */
+    if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0)
+      sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
+    else
+      offer = 1;
+  }
 
-  /* Magical value indicating autoanswer within respond_to_invite() */
-#define AUTOANSWER ((void*)-1)
+  if (sr->sr_status < 300 && (offer || answer || extra)) {
+    if (nh->nh_soa && session_include_description(nh->nh_soa, 1, msg, sip) < 0)
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+    else if (offer)
+      sr->sr_offer_sent = 1 + reliable, ss->ss_oa_sent = "offer";
+    else if (answer)
+      sr->sr_answer_sent = 1 + reliable, ss->ss_oa_sent = "answer";
+  }
 
-  if (status > 100) {
-    sr->sr_auto = 1;
-    nua_server_respond(sr, status, phrase, TAG_END());
-    sr->sr_auto = 0;
+  if (reliable && sr->sr_status < 200) {
+    sr->sr_response.msg = NULL, sr->sr_response.sip = NULL;
+    if (nta_reliable_mreply(sr->sr_irq, process_prack, sr, msg) == NULL)
+      return -1;
+    sr->sr_100rel = 1;
     return 0;
   }
 
-  nta_incoming_treply(sr->sr_irq, SIP_100_TRYING, 
-		      SIPTAG_USER_AGENT_STR(NUA_PGET(nua, nh, user_agent)),
-		      TAG_END());
-
-  nua_stack_event(nh->nh_nua, nh, 
-		  sr->sr_msg = nta_incoming_getrequest(sr->sr_irq),
-		  nua_i_invite, SIP_100_TRYING,
-		  NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa),
-		  TAG_END());
-
-  signal_call_state_change(nh, ss, SIP_100_TRYING,
-			   nua_callstate_received,
-			   sr->sr_offer_recv ? "offer" : 0, 0);
+  if (ss->ss_refresher && 200 <= sr->sr_status && sr->sr_status < 300)
+    if (session_timer_is_supported(nh))
+      use_session_timer(ss, 1, 1, msg, sip);
 
-  return 0;
+  return nua_base_server_respond(sr, tags);  
 }
 
-/** @internal Respond to an INVITE request.
- *
- * XXX - use tags to indicate when to use reliable responses.
- * XXX - change prototype.
+/** Check if the response should be sent reliably.
+ * XXX - use tags to indicate when to use reliable responses ???
  */
 static
-int respond_to_invite(nua_server_request_t *sr, tagi_t const *tags)
+int nua_invite_server_is_100rel(nua_server_request_t *sr, tagi_t const *tags)
 {
   nua_handle_t *nh = sr->sr_owner;
-  nua_t *nua = nh->nh_nua;
-  nua_dialog_state_t *ds = nh->nh_ds;
-  nua_dialog_usage_t *du;
-  nua_session_usage_t *ss;
-  msg_t *msg;
-  sip_t *sip;
-  int reliable;
-  int status = sr->sr_status; char const *phrase = sr->sr_phrase;
-  sip_warning_t *warning = NULL;
-
-  int offer = 0, answer = 0, early_answer = 0;
-
-  enter;
+  sip_t const *sip = sr->sr_response.sip;
+  sip_require_t *require = sr->sr_request.sip->sip_require;
+  sip_supported_t *supported = sr->sr_request.sip->sip_supported;
 
-  du = sr->sr_usage, ss = nua_dialog_usage_private(du);
-
-  if (du == NULL)
-    return nua_default_respond(sr, tags);
+  if (sr->sr_status >= 200)
+    return 1;
+  else if (sr->sr_status == 100)
+    return 0;
 
-  assert(ss == nua_session_usage_get(nh->nh_ds));
+  if (sip_has_feature(sip->sip_require, "100rel"))
+    return 1;
 
-  if (tags) {
-    nua_stack_set_params(nua, nh, nua_i_error, tags);
+  if (require == NULL && supported == NULL)
+    return 0;
 
-    if (!NHP_ISSET(nh->nh_prefs, early_answer)
-	&& 100 < status && status < 200) {
-      sdp_session_t const *user_sdp = NULL;
-      char const *user_sdp_str = NULL;
+  if (sip_has_feature(require, "100rel"))
+    return 1;
+  if (!sip_has_feature(supported, "100rel"))
+    return 0;
+  if (sr->sr_status == 183)
+    return 1;
 
-      tl_gets(tags,
-	      SOATAG_USER_SDP_REF(user_sdp),
-	      SOATAG_USER_SDP_STR_REF(user_sdp_str),
-	      TAG_END());
+  if (NH_PGET(nh, early_media) && !NH_PGET(nh, only183_100rel))
+    return 1;
 
-      early_answer = user_sdp || user_sdp_str;
-    }
-    else
-      early_answer = NH_PGET(nh, early_answer);
+  if (sip_has_feature(require, "precondition")) {
+    if (!NH_PGET(nh, only183_100rel))
+      return 1;
+    if (sr->sr_offer_recv && !sr->sr_answer_sent)
+      return 1;
   }
 
-  msg = nua_server_response(sr,
-			    status, phrase,
-			    TAG_IF(status < 300, NUTAG_ADD_CONTACT(1)),
-			    SIPTAG_SUPPORTED(NH_PGET(nh, supported)),
-			    TAG_NEXT(tags));
-  sip = sip_object(msg);
-
-  if (!sip) {
-    SET_STATUS1(SIP_500_INTERNAL_SERVER_ERROR), reliable = 0;
-    goto send_response;
-  }
-
-  reliable =
-    (status >= 200)
-    || (status > 100 && sip->sip_require &&
-	sip_has_feature(sip->sip_require, "100rel"))
-    || (status > 100 &&
-	ds->ds_remote_ua->nr_require &&
-	sip_has_feature(ds->ds_remote_ua->nr_require, "100rel"))
-    || (status > 100 && !NH_PGET(nh, only183_100rel) &&
-	(NH_PGET(nh, early_media) ||
-	 (ds->ds_remote_ua->nr_require &&
-	  sip_has_feature(ds->ds_remote_ua->nr_require, "precondition"))) &&
-	ds->ds_remote_ua->nr_supported &&
-	sip_has_feature(ds->ds_remote_ua->nr_supported, "100rel"))
-    || (status == 183 &&
-	ds->ds_remote_ua->nr_supported &&
-	sip_has_feature(ds->ds_remote_ua->nr_supported, "100rel"))
-    || (status == 183 &&
-	ds->ds_remote_ua->nr_require &&
-	sip_has_feature(ds->ds_remote_ua->nr_require, "precondition"))
-    || (status > 100 &&
-	ds->ds_remote_ua->nr_require &&
-	sip_has_feature(ds->ds_remote_ua->nr_require, "precondition") &&
-	sr->sr_offer_recv && !sr->sr_answer_sent);
+  return 0;
+}
 
-  if (!nh->nh_soa)
-    /* Xyzzy */;
-  else if (status >= 300) {
-    soa_clear_remote_sdp(nh->nh_soa);
-  }
-  else {
-    int extra = 0;
 
-    if (sr->sr_offer_sent && !sr->sr_answer_recv)
-      /* Wait for answer */;
-    else if (sr->sr_offer_recv && sr->sr_answer_sent > 1) {
-      /* We have sent answer */
-      /* ...  but we may want to send it again */
-      tagi_t const *t = tl_find_last(tags, nutag_include_extra_sdp);
-      extra = t && t->t_value;
-    }
-    else if (sr->sr_offer_recv && !sr->sr_answer_sent && 
-	     (reliable || early_answer)) {
-      /* Generate answer */ 
-      if (soa_generate_answer(nh->nh_soa, NULL) >= 0) {
-	answer = 1;
-	soa_activate(nh->nh_soa, NULL);
-	/* signal that O/A answer sent (answer to invite) */
-      }
-      else if (status >= 200) {
-	int wcode;
-	char const *text;
-	char const *host = "invalid.";
-	status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-
-	wcode = soa_get_warning(nh->nh_soa, &text);
-	if (wcode) {
-	  if (sip->sip_contact)
-	    host = sip->sip_contact->m_url->url_host;
-	  warning = sip_warning_format(msg_home(msg), "%u %s \"%s\"",
-				       wcode, host, text);
-	}
-      }
-      else {
-	/* 1xx - we don't have to send answer */
-      }
-    }
-    else if (sr->sr_offer_recv && sr->sr_answer_sent == 1 && 
-	     (reliable || early_answer)) {
-      /* The answer was sent unreliably, keep sending it */
-      answer = 1;
-    }
-    else if (!sr->sr_offer_recv && !sr->sr_offer_sent && reliable) {
-      /* Generate offer */
-      if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0)
-	status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-      else
-	offer = 1;
-    }
+int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  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;
 
-    if (offer || answer || extra) {
-      if (session_include_description(nh->nh_soa, 1, msg, sip) < 0)
-	SET_STATUS1(SIP_500_INTERNAL_SERVER_ERROR);
-    }
+  if (!sr->sr_event && status < 300) {	/* Not reported yet */
+    nta_incoming_bind(sr->sr_irq, process_ack_or_cancel, sr);
   }
 
-  if (ss->ss_refresher && 200 <= status && status < 300)
-    if (session_timer_is_supported(nh))
-      use_session_timer(ss, 1, 1, msg, sip);
-
-  if (reliable && status < 200) {
-    nta_reliable_t *rel;
-    rel = nta_reliable_mreply(sr->sr_irq,
-			      process_prack, nh, msg);
-    if (!rel)
-      SET_STATUS1(SIP_500_INTERNAL_SERVER_ERROR);
-  }
-
- send_response:
-
-  if (reliable && status < 200)
-    /* we are done */;
-  else if (status != sr->sr_status) {    /* Error responding */
-    assert(status >= 200);
-    sr->sr_respond = NULL;
-    nta_incoming_treply(sr->sr_irq,
-			status, phrase,
-			SIPTAG_WARNING(warning),
-			SIPTAG_USER_AGENT_STR(NH_PGET(nh, user_agent)),
-			TAG_END());
-    msg_destroy(msg), msg = NULL;
-  }
-  else {
-    if (status >= 200)
-      sr->sr_respond = NULL;
-    nta_incoming_mreply(sr->sr_irq, msg);
-  }
-
-  if (sr->sr_auto) {
-    msg_t *request = nta_incoming_getrequest(sr->sr_irq);
-    if (status < 200)
-      sr->sr_msg = request;
-    nua_stack_event(nh->nh_nua, nh, request,
-		    nua_i_invite, status, phrase,
-		    NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa),
-		    TAG_END());
+  retval = nua_base_server_report(sr, tags), sr = NULL; /* destroys sr */
+  
+  if (retval >= 2 || ss == NULL) {
+    /* Session has been terminated. */ 
+    if (!initial && !neutral)
+      signal_call_state_change(nh, NULL, status, phrase,
+			       nua_callstate_terminated);
+    return retval;
   }
-  else if (status != sr->sr_status)
-    nua_stack_event(nua, nh, NULL, nua_i_error, status, phrase, TAG_END());
-
-  sr->sr_status = status, sr->sr_phrase = phrase;
 
-  if (status >= 300)
-    offer = 0, answer = 0;
-
-  if (offer)
-    sr->sr_offer_sent = 1;
-  else if (answer)
-    sr->sr_answer_sent = 1 + reliable;
+  assert(ss);
 
   /* Update session state */
-  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
-			   : status >= 200
-			   ? nua_callstate_completed
-			   : nua_callstate_early,
-			   sr->sr_auto && sr->sr_offer_recv ? "offer" : 0,
-			   offer ? "offer" : answer ? "answer" : 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
+			     : status >= 200
+			     ? nua_callstate_completed
+			     : status > 100
+			     ? nua_callstate_early
+			     : nua_callstate_received);
+  }
 
   if (status == 180)
     ss->ss_alerting = 1;
   else if (status >= 200)
     ss->ss_alerting = 0;
 
-  if (status >= 200 && status < 300) {
-    du->du_ready = 1;
+  if (200 <= status && status < 300) {
+     du->du_ready = 1;
   }
-  else if (status >= 300) {
-    sr->sr_usage = NULL;
+  else if (300 <= status && !neutral) {
     if (nh->nh_soa)
       soa_init_offer_answer(nh->nh_soa);
   }
@@ -2065,10 +2152,9 @@
     nua_session_usage_destroy(nh, ss);
   }
 
-  return status >= 300 ? status : 0;
+  return retval;
 }
 
-
 /** @internal Process ACK or CANCEL or timeout (no ACK) for incoming INVITE */
 static
 int process_ack_or_cancel(nua_server_request_t *sr,
@@ -2088,139 +2174,6 @@
     return process_timeout(sr, irq);
 }
 
-/** @NUA_EVENT nua_i_prack
- *
- * Incoming PRACK request. PRACK request is used to acknowledge reliable
- * preliminary responses and it is usually sent automatically by the nua
- * stack.
- *
- * @param status status code of response sent automatically by stack
- * @param phrase a short textual description of @a status code
- * @param nh     operation handle associated with the call
- * @param hmagic application context associated with the call
- * @param sip    incoming INFO request
- * @param tags   empty
- *
- * @sa nua_prack(), #nua_r_prack, @RFC3262, NUTAG_EARLY_MEDIA()
- * 
- * @END_NUA_EVENT
- */
-
-/** @internal Process PRACK or (timeout from 100rel) */
-static
-int process_prack(nua_handle_t *nh,
-		  nta_reliable_t *rel,
-		  nta_incoming_t *irq,
-		  sip_t const *sip)
-{
-  nua_dialog_state_t *ds = nh->nh_ds;
-  nua_dialog_usage_t *du;
-  nua_session_usage_t *ss;
-  nua_server_request_t *sri;
-  int status = 200; char const *phrase = sip_200_OK;
-  char const *recv = NULL, *sent = NULL;
-
-  nta_reliable_destroy(rel);
-
-  ss = nua_session_usage_get(ds); du = nua_dialog_usage_public(ss);
-
-  for (sri = ds->ds_sr; sri; sri = sri->sr_next) {
-    if (sri->sr_method == sip_method_invite && sri->sr_usage == du)
-      break;
-  }
-                     
-  if (!sri || !sri->sr_respond) /* XXX */
-    return 481;
-
-  if (sip)
-    /* received PRACK */;
-  else if (!sri || irq == NULL) { /* Final response interrupted 100rel */
-    /* Ignore */
-    return 200;
-  }
-  else if (sip == NULL) {
-    SET_STATUS(504, "Reliable Response Timeout");
-
-    nua_stack_event(nh->nh_nua, nh, NULL,
-		    nua_i_error, status, phrase,
-		    TAG_END());
-
-    nua_server_respond(sri, status, phrase, TAG_END());
-
-    return status;
-  }
-
-  if (nh->nh_soa) {
-    msg_t *msg = nta_incoming_getrequest(irq);
-    char const *sdp;
-    size_t len;
-
-    if (session_get_description(sip, &sdp, &len)) {
-      su_home_t home[1] = { SU_HOME_INIT(home) };
-
-      sip_content_disposition_t *cd = NULL;
-      sip_content_type_t *ct = NULL;
-      sip_payload_t *pl = NULL;
-
-      if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
-	SU_DEBUG_5(("nua(%p): error parsing SDP in INVITE\n", nh));
-	msg_destroy(msg);
-	status = 400, phrase = "Bad Session Description";
-      }
-
-      /* Respond to PRACK */
-
-      if (status >= 300)
-	;
-      else if (sri->sr_offer_sent) {
-	recv = "answer";
-	sri->sr_answer_recv = 1;
-	if (soa_process_answer(nh->nh_soa, NULL) < 0)
-	  status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-      }
-      else {
-	recv = "offer";
-	if (soa_generate_answer(nh->nh_soa, NULL) < 0) {
-	  status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-	}
-	else {
-	  if (session_make_description(home, nh->nh_soa, 1, &cd, &ct, &pl) > 0)
-	    sent = "answer";
-	}
-      }
-
-      if (nta_incoming_treply(irq, status, phrase,
-			      SIPTAG_CONTENT_DISPOSITION(cd),
-			      SIPTAG_CONTENT_TYPE(ct),
-			      SIPTAG_PAYLOAD(pl),
-			      TAG_END()) < 0)
-	/* Respond with 500 if nta_incoming_treply() failed */
-	SET_STATUS1(SIP_500_INTERNAL_SERVER_ERROR);
-
-      su_home_deinit(home);
-    }
-
-    msg_destroy(msg);
-  }
-
-  nua_stack_event(nh->nh_nua, nh, nta_incoming_getrequest(irq),
-		  nua_i_prack, status, phrase, TAG_END());
-
-  if (status >= 300)
-    return status;
-
-  if (recv || sent) {
-    soa_activate(nh->nh_soa, NULL);
-    signal_call_state_change(nh, ss, status, phrase,
-			     nua_callstate_early, recv, sent);
-  }
-
-  if (NH_PGET(nh, auto_alert) && !ss->ss_alerting && !ss->ss_precondition)
-    nua_server_respond(sri, SIP_180_RINGING, TAG_END());
-
-  return status;
-}
-
 /** @NUA_EVENT nua_i_ack
  *
  * Final response to INVITE has been acknowledged by UAC with ACK. 
@@ -2249,38 +2202,53 @@
   if (ss == NULL)
     return 0;
 
-  if (nh->nh_soa && sr->sr_offer_sent && !sr->sr_answer_recv) {
+  if (sr->sr_offer_sent && !sr->sr_answer_recv) {
     char const *sdp;
     size_t len;
+    int error;
+
+    if (session_get_description(sip, &sdp, &len))
+      recv = "answer";
+
+    if (recv) {
+      assert(ss->ss_oa_recv == NULL);
+      ss->ss_oa_recv = recv;
+    }
 
-    if (!session_get_description(sip, &sdp, &len) ||
-	!(recv = "answer") ||
-	soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0 ||
-	soa_process_answer(nh->nh_soa, NULL) < 0 ||
-	soa_activate(nh->nh_soa, NULL)) {
+    if (nh->nh_soa == NULL)
+      ;
+    else if (recv == NULL ||
+	     soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0 ||
+	     soa_process_answer(nh->nh_soa, NULL) < 0 ||
+	     soa_activate(nh->nh_soa, NULL) < 0) {
       int status; char const *phrase, *reason;
 
       status = soa_error_as_sip_response(nh->nh_soa, &phrase);
       reason = soa_error_as_sip_reason(nh->nh_soa);
 
       nua_stack_event(nh->nh_nua, nh, msg,
-	       nua_i_ack, status, phrase, TAG_END());
+		      nua_i_ack, status, phrase, NULL);
       nua_stack_event(nh->nh_nua, nh, NULL,
-	       nua_i_media_error, status, phrase, TAG_END());
+		      nua_i_media_error, status, phrase, NULL);
+
+      ss->ss_reporting = 1;	/* We report state here if BYE fails */
+      error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
+      ss->ss_reporting = 0;
 
       signal_call_state_change(nh, ss, 488, "Offer-Answer Error",
-			       nua_callstate_terminating, recv, 0);
-      nua_stack_post_signal(nh, nua_r_bye,
-			    SIPTAG_REASON_STR(reason),
-			    TAG_END());
+			       error
+			       ? nua_callstate_terminated
+			       : nua_callstate_terminating);
 
       return 0;
     }
   }
 
-  soa_clear_remote_sdp(nh->nh_soa);
-  nua_stack_event(nh->nh_nua, nh, msg, nua_i_ack, SIP_200_OK, TAG_END());
-  signal_call_state_change(nh, ss, 200, "OK", nua_callstate_ready, recv, 0);
+  if (nh->nh_soa)
+    soa_clear_remote_sdp(nh->nh_soa);
+
+  nua_stack_event(nh->nh_nua, nh, msg, nua_i_ack, SIP_200_OK, NULL);
+  signal_call_state_change(nh, ss, 200, "OK", nua_callstate_ready);
   set_session_timer(ss);
 
   nua_server_request_destroy(sr);
@@ -2314,12 +2282,14 @@
   nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
   msg_t *cancel = nta_incoming_getrequest_ackcancel(irq);
 
-  assert(nta_incoming_status(irq) < 200);  assert(sr->sr_respond);
-  assert(ss); assert(ss == nua_session_usage_get(nh->nh_ds)); (void)ss;
+  assert(ss); assert(ss == nua_session_usage_for_dialog(nh->nh_ds)); (void)ss;
 
-  nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, TAG_END());
+  assert(nta_incoming_status(irq) < 200);
 
-  nua_server_respond(sr, SIP_487_REQUEST_TERMINATED, TAG_END());
+  nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK, NULL);
+  sr->sr_application = SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED);
+  nua_server_respond(sr, NULL);
+  nua_server_report(sr);
 
   return 0;
 }
@@ -2331,39 +2301,264 @@
 {
   nua_handle_t *nh = sr->sr_owner;
   nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
+  char const *phrase = "ACK Timeout";
+  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";
+    reason = "SIP;cause=504;text=\"PRACK Timeout\"";
+  }
 
-  nua_stack_event(nh->nh_nua, nh, 0, nua_i_error,
-		  408, "Response timeout",
-		  TAG_END());
+  nua_stack_event(nh->nh_nua, nh, 0, nua_i_error, 408, phrase, NULL);
 
-  if (sr->sr_respond) {
+  if (nua_server_request_is_pending(sr)) {
     /* PRACK timeout */
-    nua_server_respond(sr, SIP_504_GATEWAY_TIME_OUT,
-		       SIPTAG_REASON_STR("SIP;cause=504;"
-					 "text=\"PRACK Timeout\""),
-		       TAG_END());
-    ss = nua_session_usage_get(nh->nh_ds);
+    SR_STATUS1(sr, SIP_504_GATEWAY_TIME_OUT);
+    nua_server_trespond(sr, 
+			SIPTAG_REASON_STR(reason),
+			TAG_END());
+    if (nua_server_report(sr) >= 2)
+      return 0;			/* Done */
     sr = NULL;
   }
 
-  if (ss) {
-    /* send BYE, too if 200 OK (or 183 to re-INVITE) timeouts  */
-    signal_call_state_change(nh, ss, 0, "Timeout",
-			     nua_callstate_terminating, 0, 0);
-    nua_stack_post_signal(nh, nua_r_bye,
-			  SIPTAG_REASON_STR("SIP;cause=408;text=\"ACK Timeout\""),
-			  TAG_END());
+  /* send BYE, too, if 200 OK (or 183 to re-INVITE) timeouts  */
+  ss->ss_reason = reason;
+
+  ss->ss_reporting = 1;		/* We report state here if BYE fails */
+  error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
+  ss->ss_reporting = 0;
+
+  signal_call_state_change(nh, ss, 0, phrase,
+			   error
+			   ? nua_callstate_terminated
+			   : nua_callstate_terminating);
+
+  if (sr)
+    nua_server_request_destroy(sr);
+
+  return 0;
+}
+
+
+/** @NUA_EVENT nua_i_prack
+ *
+ * Incoming PRACK request. PRACK request is used to acknowledge reliable
+ * preliminary responses and it is usually sent automatically by the nua
+ * stack.
+ *
+ * @param status status code of response sent automatically by stack
+ * @param phrase a short textual description of @a status code
+ * @param nh     operation handle associated with the call
+ * @param hmagic application context associated with the call
+ * @param sip    incoming PRACK request
+ * @param tags   empty
+ *
+ * @sa nua_prack(), #nua_r_prack, @RFC3262, NUTAG_EARLY_MEDIA()
+ * 
+ * @END_NUA_EVENT
+ */
+
+int nua_prack_server_init(nua_server_request_t *sr);
+int nua_prack_server_respond(nua_server_request_t *sr, tagi_t const *tags);
+int nua_prack_server_report(nua_server_request_t *sr, tagi_t const *tags);
+
+nua_server_methods_t const nua_prack_server_methods = 
+  {
+    SIP_METHOD_PRACK,
+    nua_i_prack,		/* Event */
+    { 
+      0,			/* Do not create dialog */
+      1,			/* In-dialog request */
+      1,			/* Target refresh request  */
+      1,			/* Add Contact */
+    },
+    nua_prack_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_prack_server_respond,
+    nua_prack_server_report,
+  };
+
+/** @internal Process reliable response PRACK or (timeout from 100rel) */
+static int process_prack(nua_server_request_t *sr,
+			 nta_reliable_t *rel,
+			 nta_incoming_t *irq,
+			 sip_t const *sip)
+{
+  nua_handle_t *nh;
+  nua_dialog_usage_t *du;
+
+  nta_reliable_destroy(rel);
+  if (irq == NULL)  
+    /* Final response interrupted 100rel, we did not actually receive PRACK */
+    return 200;
+
+  sr->sr_pracked = 1;
+
+  if (!nua_server_request_is_pending(sr)) /* There is no INVITE anymore */
+    return 481;
+
+  nh = sr->sr_owner;
+
+  if (nh->nh_ds->ds_leg == NULL)
+    return 500;
+
+  du = nua_dialog_usage_for_session(nh->nh_ds);
+
+  if (sip == NULL) {
+    /* 100rel timeout */
+    SR_STATUS(sr, 504, "Reliable Response Timeout");
+    nua_stack_event(nh->nh_nua, nh, NULL, nua_i_error,
+		    sr->sr_status, sr->sr_phrase,
+		    NULL);
+    nua_server_trespond(sr,
+			SIPTAG_REASON_STR("SIP;cause=504;"
+					  "text=\"PRACK Timeout\""),
+			TAG_END());
+    nua_server_report(sr);
+    return 504;
+  }
+
+  nta_incoming_bind(irq, NULL, (void *)sr);
+
+  return nua_stack_process_request(nh, nh->nh_ds->ds_leg, irq, sip);
+}
+
+
+int nua_prack_server_init(nua_server_request_t *sr)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_server_request_t *sri = nta_incoming_magic(sr->sr_irq, NULL);
+
+  if (sri == NULL)
+    return SR_STATUS(sr, 481, "No Such Preliminary Response");
+  
+  if (nua_session_server_init(sr))
+    return sr->sr_status;
+
+  if (sr->sr_sdp) {
+    nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
+
+    /* XXX - check for overlap? */
+    
+    if (sri->sr_offer_sent)
+      sr->sr_answer_recv = 1, ss->ss_oa_recv = "answer";
+    else 
+      sr->sr_offer_recv = 1, ss->ss_oa_recv = "offer";
+
+    if (nh->nh_soa &&
+	soa_set_remote_sdp(nh->nh_soa, NULL, sr->sr_sdp, sr->sr_sdp_len) < 0) {
+      SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,
+		  "PRACK", "offer"));
+      return 
+	sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
+    }
+  }
+
+  return 0;
+}
+
+int nua_prack_server_respond(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+
+  if (sr->sr_status < 200 || 300 <= sr->sr_status) 
+    return nua_base_server_respond(sr, tags);
+
+  if (sr->sr_sdp) {
+    nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
+    msg_t *msg = sr->sr_response.msg;
+    sip_t *sip = sr->sr_response.sip;
+
+    if (nh->nh_soa == NULL) {
+      if (sr->sr_offer_recv && session_get_description(sip, NULL, NULL))
+	sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
+    }
+    else if ((sr->sr_offer_recv && soa_generate_answer(nh->nh_soa, NULL) < 0) ||
+	     (sr->sr_answer_recv && soa_process_answer(nh->nh_soa, NULL) < 0)) {
+      SU_DEBUG_5(("nua(%p): %s server: %s %s\n", 
+		  (void *)nh, "PRACK", 
+		  "error processing",
+		  sr->sr_offer_recv ? "offer" : "answer"));
+      sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
+    }
+    else if (sr->sr_offer_recv) {
+      if (session_include_description(nh->nh_soa, 1, msg, sip) < 0)
+	sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR);
+      else
+      sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
+    }
+  }
+
+  return nua_base_server_respond(sr, tags);
+}
+
+int nua_prack_server_report(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
+  nua_server_request_t *sri = nta_incoming_magic(sr->sr_irq, NULL);
+  int status = sr->sr_status; char const *phrase = sr->sr_phrase;
+  int offer_recv_or_answer_sent = sr->sr_offer_recv || sr->sr_answer_sent;
+  int retval;
+
+  retval = nua_base_server_report(sr, tags), sr = NULL; /* destroys sr */
+
+  if (retval >= 2 || ss == NULL) {
+    signal_call_state_change(nh, NULL,
+			     status, phrase, 
+			     nua_callstate_terminated);
+    return retval;
+  }
+
+  if (offer_recv_or_answer_sent) {
+    /* signal offer received, answer sent */
+    signal_call_state_change(nh, ss,
+			     status, phrase, 
+			     ss->ss_state);
+    if (nh->nh_soa)
+      soa_activate(nh->nh_soa, NULL);
+  }
+
+  if (status < 200 || 300 <= status)
+    return retval;
+
+  assert(sri);
+
+  if (sri == NULL) {
+    
+  }
+  else if (su_msg_is_non_null(sri->sr_signal)) {
+    su_msg_r signal;
+    event_t *e;
+    
+    su_msg_save(signal, sri->sr_signal);
+    
+    e = su_msg_data(signal);
+    sri->sr_application = SR_STATUS(sri, e->e_status, e->e_phrase);
+    
+    nua_server_params(sri, e->e_tags);
+    nua_server_respond(sri, e->e_tags);
+    nua_server_report(sri);
+    
+    su_msg_destroy(signal);
+  }
+  else if (ss->ss_state < nua_callstate_ready
+	   && !ss->ss_alerting
+	   && !ss->ss_precondition
+	   && NH_PGET(nh, auto_alert))  {
+    SR_STATUS1(sri, SIP_180_RINGING);
+    nua_server_respond(sri, NULL);
+    nua_server_report(sri);
   }
 
-  if (sr)
-    nua_server_request_destroy(sr);
-
-  return 0;
+  return retval;
 }
 
-
 /* ---------------------------------------------------------------------- */
 /* Session timer - RFC 4028 */
 
@@ -2481,6 +2676,24 @@
   return 1;
 }
 
+static int session_timer_check_restart(nua_client_request_t *cr,
+				       int status, char const *phrase,
+				       sip_t const *sip)
+{
+  if (cr->cr_usage && status == 422) {
+    nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage);
+
+    if (sip->sip_min_se && ss->ss_min_se < sip->sip_min_se->min_delta)
+      ss->ss_min_se = sip->sip_min_se->min_delta;
+    if (ss->ss_min_se > ss->ss_session_timer)
+      ss->ss_session_timer = ss->ss_min_se;
+  
+    return nua_client_restart(cr, 100, "Re-Negotiating Session Timer");
+  }
+
+  return nua_base_client_check_restart(cr, status, phrase, sip);
+}
+
 static void
 set_session_timer(nua_session_usage_t *ss)
 {
@@ -2505,31 +2718,6 @@
   }
 }
 
-static int
-check_session_timer_restart(nua_handle_t *nh,
-			    nua_session_usage_t *ss,
-			    nua_client_request_t *cr,
-			    nta_outgoing_t *orq,
-			    sip_t const *sip,
-			    nua_creq_restart_f *restart_function)
-{
-  if (ss && sip && sip->sip_status->st_status == 422) {
-    if (sip->sip_min_se && ss->ss_min_se < sip->sip_min_se->min_delta)
-      ss->ss_min_se = sip->sip_min_se->min_delta;
-    if (ss->ss_min_se > ss->ss_session_timer)
-      ss->ss_session_timer = ss->ss_min_se;
-  
-    if (orq == cr->cr_orq)
-      cr->cr_orq = NULL;
-
-    return nua_creq_restart_with(nh, cr, orq,
-				 100, "Re-Negotiating Session Timer",
-				 restart_function, TAG_END());
-  }
-
-  return nua_creq_check_restart(nh, cr, orq, sip, restart_function);
-}
-
 static inline int
 is_session_timer_set(nua_session_usage_t *ss)
 {
@@ -2611,7 +2799,7 @@
     if (ref) {
       if (ref->ref_handle)
 	SU_DEBUG_1(("nh_handle_referral: stale referral handle %p\n",
-		    ref->ref_handle));
+		    (void *)ref->ref_handle));
       ref->ref_handle = NULL;
     }
     return;
@@ -2647,33 +2835,9 @@
   nua_handle_unref(ref->ref_handle), ref->ref_handle = NULL;
 }
 
-
-/** Zap the session associated with the handle */
-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));
-
-  SU_DEBUG_5(("nua: terminated session %p\n", nh));
-}
-
-
 /* ======================================================================== */
 /* INFO */
 
-static int process_response_to_info(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip);
-
 /**@fn void nua_info(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
  *
  * Send an INFO request. 
@@ -2696,41 +2860,59 @@
  * @sa #nua_i_info
  */
 
+static int nua_info_client_init(nua_client_request_t *cr, 
+				msg_t *msg, sip_t *sip,
+				tagi_t const *tags);
+
+static int nua_info_client_request(nua_client_request_t *cr,
+				   msg_t *msg, sip_t *sip,
+				   tagi_t const *tags);
+
+nua_client_methods_t const nua_info_client_methods = {
+  SIP_METHOD_INFO,
+  0,
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 1,
+    /* target refresh */ 0
+  },
+  /*nua_info_client_template*/ NULL,
+  nua_info_client_init,
+  nua_info_client_request,
+  /*nua_info_client_check_restart*/ NULL,
+  /*nua_info_client_response*/ NULL
+};
+
 int
 nua_stack_info(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg;
-
-  if (nh_is_special(nh)) {
-    return UA_EVENT2(e, 900, "Invalid handle for INFO");
-  }
-  else if (cr->cr_orq) {
-    return UA_EVENT2(e, 900, "Request already in progress");
-  }
+  return nua_client_create(nh, e, &nua_info_client_methods, tags);
+}
 
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
+static int nua_info_client_init(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 = nua_dialog_usage_for_session(nh->nh_ds);
+  nua_session_usage_t *ss = nua_dialog_usage_private(du);
 
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
-			 SIP_METHOD_INFO ,
-			 NUTAG_ADD_CONTACT(1),
-			 TAG_NEXT(tags));
+  if (!ss || ss->ss_state >= nua_callstate_terminating)
+    return nua_client_return(cr, 900, "Invalid handle for INFO", msg);
 
-  cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				    process_response_to_info, nh, NULL,
-				    msg,
-				    SIPTAG_END(), TAG_NEXT(tags));
-  if (!cr->cr_orq) {
-    msg_destroy(msg);
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-  }
+  cr->cr_usage = du;
 
-  return cr->cr_event = e;
+  return 0;
 }
 
-void restart_info(nua_handle_t *nh, tagi_t *tags)
+static int nua_info_client_request(nua_client_request_t *cr,
+				   msg_t *msg, sip_t *sip,
+				   tagi_t const *tags)
 {
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_info, tags);
+  if (cr->cr_usage == NULL)
+    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);
+  else
+    return nua_base_client_request(cr, msg, sip, tags);
 }
 
 /** @NUA_EVENT nua_r_info
@@ -2754,15 +2936,6 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_info(nua_handle_t *nh,
-				    nta_outgoing_t *orq,
-				    sip_t const *sip)
-{
-  if (nua_creq_check_restart(nh, nh->nh_ds->ds_cr, orq, sip, restart_info))
-    return 0;
-  return nua_stack_process_response(nh, nh->nh_ds->ds_cr, orq, sip, TAG_END());
-}
-
 /** @NUA_EVENT nua_i_info
  *
  * Incoming session INFO request.
@@ -2779,25 +2952,26 @@
  * @END_NUA_EVENT
  */
 
-int nua_stack_process_info(nua_t *nua,
-			   nua_handle_t *nh,
-			   nta_incoming_t *irq,
-			   sip_t const *sip)
-{
-  nua_stack_event(nh->nh_nua, nh, nta_incoming_getrequest(irq),
-		  nua_i_info, SIP_200_OK, TAG_END());
-
-  return 200;		/* Respond automatically with 200 Ok */
-}
-
+nua_server_methods_t const nua_info_server_methods = 
+  {
+    SIP_METHOD_INFO,
+    nua_i_info,			/* Event */
+    { 
+      0,			/* Do not create dialog */
+      1,			/* In-dialog request */
+      0,			/* Not a target refresh request  */
+      0,			/* Do not add Contact */
+    },
+    nua_base_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_base_server_respond,
+    nua_base_server_report,
+  };
 
 /* ======================================================================== */
 /* UPDATE */
 
-static int process_response_to_update(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip);
-
 /**@fn void nua_update(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
  *
  * Update a session. 
@@ -2826,91 +3000,142 @@
  * @sa @ref nua_call_model, @RFC3311, nua_update(), #nua_i_update
  */
 
+static int nua_update_client_init(nua_client_request_t *cr, 
+				  msg_t *msg, sip_t *sip,
+				  tagi_t const *tags);
+static int nua_update_client_request(nua_client_request_t *cr,
+				     msg_t *msg, sip_t *sip,
+				     tagi_t const *tags);
+static int nua_update_client_response(nua_client_request_t *cr,
+				      int status, char const *phrase,
+				      sip_t const *sip);
+static int nua_update_client_report(nua_client_request_t *cr,
+				    int status, char const *phrase,
+				    sip_t const *sip,
+				    nta_outgoing_t *orq,
+				    tagi_t const *tags);
+
+nua_client_methods_t const nua_update_client_methods = {
+  SIP_METHOD_UPDATE,
+  0,				/* size of private data */
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 1,
+    /* target refresh */ 1
+  },
+  NULL,
+  nua_update_client_init,
+  nua_update_client_request,
+  session_timer_check_restart,
+  nua_update_client_response,
+  NULL,
+  nua_update_client_report
+};
+
 int nua_stack_update(nua_t *nua, nua_handle_t *nh, nua_event_t e,
 		     tagi_t const *tags)
 {
-  nua_dialog_state_t *ds = nh->nh_ds;
-  nua_session_usage_t *ss;
-  nua_client_request_t *cr;
-  msg_t *msg;
-  sip_t *sip;
-  char const *offer_sent = 0;
-
-  ss = nua_session_usage_get(ds);
-  cr = ds->ds_cr;
-
-  if (!ss)
-    return UA_EVENT2(e, 900, "Invalid handle for UPDATE");
-  else if (cr->cr_orq)
-    return UA_EVENT2(e, 900, "Request already in progress");
+  return nua_client_create(nh, e, &nua_update_client_methods, tags);
+}
 
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
+static int nua_update_client_init(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 = nua_dialog_usage_for_session(nh->nh_ds);
 
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
-		     SIP_METHOD_UPDATE,
-		     NUTAG_USE_DIALOG(1),
-		     NUTAG_ADD_CONTACT(1),
-		     TAG_NEXT(tags));
+  cr->cr_usage = du;
 
-  sip = sip_object(msg);
+  return 0;
+}
 
-  if (sip) {
-    nua_client_request_t *cri = ss->ss_crequest;
-    nua_server_request_t *sr;
+static int nua_update_client_request(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);
+  nua_server_request_t *sr;
+  nua_client_request_t *cri;
+  int offer_sent = 0, retval;
+  
+  if (du == NULL)		/* Call terminated */
+    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);
+  assert(ss);
+
+  cri = du->du_cr;
+
+  for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
+    if ((sr->sr_offer_sent && !sr->sr_answer_recv) ||
+	(sr->sr_offer_recv && !sr->sr_answer_sent))
+      break;
 
-    for (sr = ds->ds_sr; sr; sr = sr->sr_next)
-      if ((sr->sr_offer_sent && !sr->sr_answer_recv) ||
-	  (sr->sr_offer_recv && !sr->sr_answer_sent))
-	break;
-    
-    if (nh->nh_soa && !sip->sip_payload && 
-	!sr &&
-	!(cri && cri->cr_offer_sent && !cri->cr_answer_recv) &&
-	!(cri && cri->cr_offer_recv && !cri->cr_answer_sent)) {
-      soa_init_offer_answer(nh->nh_soa);
+  if (sr ||
+      (cri && cri->cr_offer_sent && !cri->cr_answer_recv) ||
+      (cri && cri->cr_offer_recv && !cri->cr_answer_sent)) {
+    if (nh->nh_soa == NULL) {
+      if (session_get_description(sip, NULL, NULL))
+	return nua_client_return(cr, 500, "Overlapping Offer/Answer", msg);
+    }
+  }
+  else if (nh->nh_soa == NULL) {
+    offer_sent = session_get_description(sip, NULL, NULL);
+  }
+  else if (!sip->sip_payload) {
+    soa_init_offer_answer(nh->nh_soa);
 
-      if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0 ||
-	  session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
-	if (ss->ss_state < nua_callstate_ready) {
-	  /* XXX */
-	}
-	msg_destroy(msg);
-	return UA_EVENT2(e, 900, "Local media failed");
+    if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0 ||
+	session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
+      if (ss->ss_state < nua_callstate_ready) {
+	/* XXX - use soa_error_as_sip_reason(nh->nh_soa) */
+	cr->cr_graceful = 1;
+	ss->ss_reason = "SIP;cause=400;text=\"Local media failure\"";
       }
-
-      offer_sent = "offer";
+      return nua_client_return(cr, 900, "Local media failed", msg);
     }
+    offer_sent = 1;
+  }
 
-    /* Add session timer headers */
-    if (session_timer_is_supported(nh))
-      use_session_timer(ss, 0, prefer_session_timer(nh), msg, sip);
+  /* Add session timer headers */
+  if (session_timer_is_supported(nh))
+    use_session_timer(ss, 0, prefer_session_timer(nh), msg, sip);
 
-    if (nh->nh_auth) {
-      if (auc_authorize(&nh->nh_auth, msg, sip) < 0)
-	/* xyzzy */;
-    }
+  retval = nua_base_client_request(cr, msg, sip, NULL);
 
-    cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				      process_response_to_update, nh, NULL,
-				      msg,
-				      SIPTAG_END(), TAG_NEXT(tags));
-    if (cr->cr_orq) {
+  if (retval == 0) {
+    cr->cr_offer_sent = offer_sent;
+    ss->ss_update_needed = 0;
+
+    if (!cr->cr_restarting) {
       if (offer_sent)
-	cr->cr_offer_sent = 1;
-      ss->ss_update_needed = 0;
-      signal_call_state_change(nh, ss, 0, "UPDATE sent",
-			       ss->ss_state, 0, offer_sent);
-      return cr->cr_event = e;
+	ss->ss_oa_sent = "offer";
+      signal_call_state_change(nh, ss, 0, "UPDATE sent", ss->ss_state);
     }
   }
 
-  msg_destroy(msg);
-  return UA_EVENT1(e, NUA_INTERNAL_ERROR);
+  return retval;
 }
 
-void restart_update(nua_handle_t *nh, tagi_t *tags)
+static int nua_update_client_response(nua_client_request_t *cr,
+				      int status, char const *phrase,
+				      sip_t const *sip)
 {
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_update, 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);
+
+  assert(200 <= status);
+
+  if (ss && sip && status < 300) {
+    if (is_session_timer_set(ss)) {
+      init_session_timer(ss, sip, NH_PGET(nh, refresher));
+      set_session_timer(ss);
+    }
+  }
+
+  return nua_session_client_response(cr, status, phrase, sip);
 }
 
 /** @NUA_EVENT nua_r_update
@@ -2937,134 +3162,74 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_update(nua_handle_t *nh,
-				       nta_outgoing_t *orq,
-				       sip_t const *sip)
+static int nua_update_client_report(nua_client_request_t *cr,
+				    int status, char const *phrase,
+				    sip_t const *sip,
+				    nta_outgoing_t *orq,
+				    tagi_t const *tags)
 {
-  nua_t *nua = nh->nh_nua;
-  nua_session_usage_t *ss;
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-
-  int status = sip->sip_status->st_status;
-  char const *phrase = sip->sip_status->st_phrase;
-  char const *recv = NULL;
-  int terminate = 0, gracefully = 1;
-
-  ss = nua_session_usage_get(nh->nh_ds); assert(ss);
-
-  if (status >= 300) {
-    if (sip->sip_retry_after)
-      gracefully = 0;
-
-    terminate = sip_response_terminates_dialog(status, sip_method_update,
-					       &gracefully);
-
-    if (!terminate &&
-	check_session_timer_restart(nh, ss, cr, orq, sip, restart_update)) {
-      return 0;
-    }
-    /* XXX - if we have a concurrent INVITE, what we do with it? */
-  }
-  else if (status >= 200) {
-    /* XXX - check remote tag, handle forks */
-    /* Set (route), contact, (remote tag) */
-    nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
-    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
-
-    if (is_session_timer_set(ss)) {
-      init_session_timer(ss, sip, NH_PGET(nh, refresher));
-      set_session_timer(ss);
-    }
-
-    if (session_process_response(nh, cr, orq, sip, &recv) < 0) {
-      nua_stack_event(nua, nh, NULL, nua_i_error,
-	       400, "Bad Session Description", TAG_END());
-    }
+  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);
 
-    signal_call_state_change(nh, ss, status, phrase, ss->ss_state, recv, 0);
+  nua_stack_event(nh->nh_nua, nh, 
+		  nta_outgoing_getresponse(orq),
+		  cr->cr_event,
+		  status, phrase,
+		  tags);
 
-    return 0;
-  }
-  else
-    gracefully = 0;
+  if (!ss || orq != cr->cr_orq || 
+      cr->cr_terminated || cr->cr_graceful || !cr->cr_offer_sent)
+    return 1;
 
-  nua_stack_process_response(nh, cr, orq, sip, TAG_END());
+  signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
 
-  if (!terminate && !gracefully)
-    return 0;
+  return 1;
+}
 
-  nh_referral_respond(nh, status, phrase);
-  
-  if (ss == NULL) {
+/* ---------------------------------------------------------------------- */
+/* UPDATE server */
 
-  } 
-  else if (terminate || 
-      (ss->ss_state < nua_callstate_completed &&
-       ss->ss_state != nua_callstate_completing)) {
-    signal_call_state_change(nh, ss, status, phrase,
-			     nua_callstate_terminated, recv, 0);
-    nua_session_usage_destroy(nh, ss);
-  }
-  else /* if (gracefully) */ {
-    signal_call_state_change(nh, ss, status, phrase,
-			     nua_callstate_terminating, recv, 0);
-#if 0
-    if (nh->nh_ss->ss_crequest->cr_orq)
-      nua_stack_post_signal(nh, nua_r_cancel, TAG_END());
-    else
-#endif
-      nua_stack_post_signal(nh, nua_r_bye, TAG_END());
-  }
+int nua_update_server_init(nua_server_request_t *sr);
+int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags);
+int nua_update_server_report(nua_server_request_t *, tagi_t const *);
 
-  return 0;
-}
+nua_server_methods_t const nua_update_server_methods = 
+  {
+    SIP_METHOD_UPDATE,
+    nua_i_update,		/* Event */
+    { 
+      0,			/* Do not create dialog */
+      1,			/* In-dialog request */
+      1,			/* Target refresh request  */
+      1,			/* Add Contact */
+    },
+    nua_update_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_update_server_respond,
+    nua_update_server_report,
+  };
 
-int nua_stack_process_update(nua_t *nua,
-			     nua_handle_t *nh,
-			     nta_incoming_t *irq,
-			     sip_t const *sip)
+int nua_update_server_init(nua_server_request_t *sr)
 {
-  nua_dialog_state_t *ds = nh->nh_ds;
+  nua_handle_t *nh = sr->sr_owner;
   nua_session_usage_t *ss;
-  nua_dialog_usage_t *du;
-  msg_t *msg = nta_incoming_getrequest(irq);
-
-  char const *sdp;
-  size_t len;
-
-  int original_status = 200, status = 200;
-  char const *phrase = sip_200_OK;
-
-  char const *offer_recv = NULL, *answer_sent = NULL;
-  int use_timer = 0;
 
-  msg_t *rmsg;
-  sip_t *rsip;
+  sip_t const *request = sr->sr_request.sip;
 
-  ss = nua_session_usage_get(ds); du = nua_dialog_usage_public(ss);
-  if (!ss) {
-    /* RFC 3261 section 12.2.2:
-       If the UAS wishes to reject the request because it does not wish to
-       recreate the dialog, it MUST respond to the request with a 481
-       (Call/Transaction Does Not Exist) status code and pass that to the
-       server transaction.
-    */
-    return 481;
-  }
+  if (nua_session_server_init(sr))
+    return sr->sr_status;
 
-  if (session_check_request(nua, nh, irq, sip))
-    return 501;
+  ss = nua_dialog_usage_private(sr->sr_usage);
 
   /* Do session timer negotiation */
-  if (sip->sip_session_expires) {
-    use_timer = 1;
-    init_session_timer(ss, sip, NH_PGET(nh, refresher));
-  }
+  if (request->sip_session_expires)
+    init_session_timer(ss, request, NH_PGET(nh, refresher));
 
-  if (status < 300 && nh->nh_soa &&
-      session_get_description(sip, &sdp, &len)) {
+  if (sr->sr_sdp) {		/* Check for overlap */
     nua_client_request_t *cr;
-    nua_server_request_t *sr;
+    nua_server_request_t *sr0;
     int overlap = 0;
 
     /*
@@ -3082,70 +3247,73 @@
       reject the UPDATE with a 500 response, and MUST include a Retry-After
       header field with a randomly chosen value between 0 and 10 seconds.
     */
-    for (cr = ds->ds_cr; cr && !overlap; cr = cr->cr_next)
-      overlap = cr->cr_offer_sent && !cr->cr_answer_recv;
-    for (sr = ds->ds_sr; sr && !overlap; sr = sr->sr_next)
-      overlap = (sr->sr_offer_recv && !sr->sr_answer_sent) ||
-	(sr->sr_method == sip_method_update && sr->sr_respond);
+    for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next)
+      if ((overlap = cr->cr_offer_sent && !cr->cr_answer_recv))
+	break;
+
+    if (!overlap)
+      for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next)
+	if ((overlap = sr0->sr_offer_recv && !sr0->sr_answer_sent))
+	  break;
 
     if (overlap)
-      return respond_with_retry_after(nh, irq, 
-				      500, "Overlapping Offer/Answer",
-				      0, 10);
-
-    offer_recv = "offer";
-
-    if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
-      SU_DEBUG_5(("nua(%p): error parsing SDP in UPDATE\n", nh));
-      msg_destroy(msg);
-      status = soa_error_as_sip_response(nh->nh_soa, &phrase);
-      offer_recv = NULL;
+      return nua_server_retry_after(sr, 500, "Overlapping Offer/Answer", 1, 9);
+
+    if (nh->nh_soa &&
+	soa_set_remote_sdp(nh->nh_soa, NULL, sr->sr_sdp, sr->sr_sdp_len) < 0) {
+      SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,
+		  "UPDATE", "offer"));
+      return 
+	sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
+    }
+
+    sr->sr_offer_recv = 1;
+    ss->ss_oa_recv = "offer";
+  }
+
+  return 0;
+}
+
+/** @internal Respond to an UPDATE request.
+ *
+ */
+int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
+  msg_t *msg = sr->sr_response.msg;
+  sip_t *sip = sr->sr_response.sip;
+
+  if (200 <= sr->sr_status && sr->sr_status < 300 && sr->sr_sdp) {
+    if (nh->nh_soa == NULL) {
+      sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
     }
-    /* Respond to UPDATE */
     else if (soa_generate_answer(nh->nh_soa, NULL) < 0) {
-      SU_DEBUG_5(("nua(%p): error processing SDP in UPDATE\n", nh));
-      msg_destroy(msg);
-      status = soa_error_as_sip_response(nh->nh_soa, &phrase);
+      SU_DEBUG_5(("nua(%p): %s server: %s %s\n", 
+		  (void *)nh, "UPDATE", "error processing", "offer"));
+      sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase);
     }
     else if (soa_activate(nh->nh_soa, NULL) < 0) {
-      SU_DEBUG_5(("nua(%p): error activating media after %s\n",
-		  nh, "UPDATE"));
+      SU_DEBUG_5(("nua(%p): %s server: error activating media\n",
+		  (void *)nh, "UPDATE"));
       /* XXX */
     }
+    else if (session_include_description(nh->nh_soa, 1, msg, sip) < 0) {
+      sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR);
+    }
     else {
-      answer_sent = "answer";
+      sr->sr_answer_sent = 1, ss->ss_oa_sent = "answer";
     }
   }
 
-  rmsg = nh_make_response(nua, nh, irq,
-			  status, phrase,
-			  TAG_IF(status < 300, NUTAG_ADD_CONTACT(1)),
-			  SIPTAG_SUPPORTED(NH_PGET(nh, supported)),
-			  TAG_NEXT(NULL));
-  rsip = sip_object(rmsg);
-  assert(sip);			/* XXX */
-
-  if (answer_sent && 
-      session_include_description(nh->nh_soa, 1, rmsg, rsip) < 0) {
-    status = 500, phrase = sip_500_Internal_server_error;
-    answer_sent = NULL;
-  }
-
-  if (200 <= status && status < 300 && session_timer_is_supported(nh)) {
-    use_session_timer(ss, 1, use_timer, rmsg, rsip);
-    set_session_timer(ss);
-  }
-
-  if (status == original_status) {
-    if (nta_incoming_mreply(irq, rmsg) < 0)
-      status = 500, phrase = sip_500_Internal_server_error;
-  }
+  if (ss->ss_refresher && 200 <= sr->sr_status && sr->sr_status < 300)
+    if (session_timer_is_supported(nh)) {
+      use_session_timer(ss, 1, 1, msg, sip);
+      set_session_timer(ss);	/* XXX */
+    }
 
-  if (status != original_status) {
-    nua_stack_event(nua, nh, NULL, nua_i_error, status, phrase, TAG_END());
-    nta_incoming_treply(irq, status, phrase, TAG_END());
-    msg_destroy(rmsg), rmsg = NULL;
-  }
+  return nua_base_server_respond(sr, tags);
+}
 
 /** @NUA_EVENT nua_i_update
  *
@@ -3163,39 +3331,52 @@
  * @END_NUA_EVENT
  */
 
-  nua_stack_event(nh->nh_nua, nh, msg, nua_i_update, status, phrase, TAG_END());
+int nua_update_server_report(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_dialog_usage_t *du = sr->sr_usage;
+  nua_session_usage_t *ss = nua_dialog_usage_private(du);
+  int status = sr->sr_status; char const *phrase = sr->sr_phrase;
+  int offer_recv_or_answer_sent = sr->sr_offer_recv || sr->sr_answer_sent;
+  int retval;
+
+  retval = nua_base_server_report(sr, tags), sr = NULL; /* destroys sr */
 
-  if (offer_recv || answer_sent)
+  if (retval >= 2 || ss == NULL) {
+    signal_call_state_change(nh, NULL, status, phrase, 
+			     nua_callstate_terminated);
+    return retval;
+  }
+
+  if (offer_recv_or_answer_sent)
     /* signal offer received, answer sent */
-    signal_call_state_change(nh, ss, 200, "OK", ss->ss_state,
-			     offer_recv, answer_sent);
+    signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
 
-  if (NH_PGET(nh, auto_alert)
+  if (200 <= status && status < 300
       && ss->ss_state < nua_callstate_ready
+      && ss->ss_precondition 
       && !ss->ss_alerting
-      && ss->ss_precondition) {
-    nua_server_request_t *sr;
+      && NH_PGET(nh, auto_alert))  {
+    nua_server_request_t *sri;
     
-    for (sr = ds->ds_sr; sr; sr = sr->sr_next)
-      if (sr->sr_method == sip_method_invite && 
-	  sr->sr_usage == du && sr->sr_respond)
+    for (sri = nh->nh_ds->ds_sr; sri; sri = sr->sr_next)
+      if (sri->sr_method == sip_method_invite && 
+	  nua_server_request_is_pending(sri))
 	break;
 
-    if (sr)
-      nua_server_respond(sr, SIP_180_RINGING, TAG_END());
+    if (sri) {
+      SR_STATUS1(sri, SIP_180_RINGING);
+      nua_server_respond(sri, NULL);
+      nua_server_report(sri);
+    }
   }
 
-  return status;
+  return retval;
 }
 
-
 /* ======================================================================== */
 /* BYE */
 
-static int process_response_to_bye(nua_handle_t *nh,
-				   nta_outgoing_t *orq,
-				   sip_t const *sip);
-
 /**@fn void nua_bye(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
  *
  * Hangdown a call.
@@ -3217,87 +3398,95 @@
  *    #nua_i_media_error
  */
 
+static int nua_bye_client_init(nua_client_request_t *cr, 
+			       msg_t *msg, sip_t *sip,
+			       tagi_t const *tags);
+static int nua_bye_client_request(nua_client_request_t *cr,
+				  msg_t *msg, sip_t *sip,
+				  tagi_t const *tags);
+static int nua_bye_client_report(nua_client_request_t *cr,
+				 int status, char const *phrase,
+				 sip_t const *sip,
+				 nta_outgoing_t *orq,
+				 tagi_t const *tags);
+
+nua_client_methods_t const nua_bye_client_methods = {
+  SIP_METHOD_BYE,
+  0,
+  { 
+    /* create_dialog */ 0,
+    /* in_dialog */ 1,
+    /* target refresh */ 0
+  },
+  NULL,
+  nua_bye_client_init,
+  nua_bye_client_request,
+  /*nua_bye_client_check_restart*/ NULL,
+  /*nua_bye_client_response*/ NULL,
+  /*nua_bye_client_preliminary*/ NULL,
+  nua_bye_client_report
+};
+
 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_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg;
-  nta_outgoing_t *orq;
+  nua_session_usage_t *ss = nua_session_usage_for_dialog(nh->nh_ds);
 
-  ss = nua_session_usage_get(nh->nh_ds);
-  
-  if (!ss || ss->ss_state >= nua_callstate_terminating)
-    return UA_EVENT2(e, 900, "Invalid handle for BYE");
-
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  if (!nua_dialog_is_established(nh->nh_ds)) {
-    nua_client_request_t *cri = ss->ss_crequest;
-
-    if (cri->cr_orq == NULL)
-      return UA_EVENT2(e, 900, "No session to BYE");
-
-    /* No (early) dialog. BYE is invalid action, do CANCEL instead */
-    orq = nta_outgoing_tcancel(cri->cr_orq,
-			       process_response_to_cancel, nh,
-			       TAG_NEXT(tags));
-    if (!cr->cr_orq)
-      cr->cr_orq = orq, cr->cr_event = e;
-
-    return 0;
-  }
-
-  if (cr->cr_orq) {
-    if (cr->cr_usage == nua_dialog_usage_public(ss)) {
-      nua_creq_deinit(cr, cr->cr_orq);
-    }
-    else {
-      cr = ss->ss_crequest;
-      if (cr->cr_orq)
-	nua_creq_deinit(cr, cr->cr_orq);
-    }
-  }
+  if (ss && 
+      nua_callstate_calling <= ss->ss_state &&
+      ss->ss_state <= nua_callstate_proceeding)
+    return nua_client_create(nh, e, &nua_cancel_client_methods, tags);
+  else
+    return nua_client_create(nh, e, &nua_bye_client_methods, tags);
+}
 
-  assert(!cr->cr_orq);
+static int nua_bye_client_init(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 = nua_dialog_usage_for_session(nh->nh_ds);
+  nua_session_usage_t *ss = nua_dialog_usage_private(du);
 
-  msg = nua_creq_msg(nua, nh, cr, 0, SIP_METHOD_BYE, TAG_NEXT(tags));
+  if (!ss || (ss->ss_state >= nua_callstate_terminating && !cr->cr_auto))
+    return nua_client_return(cr, 900, "Invalid handle for BYE", msg);
 
-  cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				    process_response_to_bye, nh, NULL,
-				    msg,
-				    SIPTAG_END(), TAG_NEXT(tags));
+  if (!cr->cr_auto)
+    /* Implicit state transition by nua_bye() */
+    ss->ss_state = nua_callstate_terminating;
 
-  ss->ss_state = nua_callstate_terminating;
   if (nh->nh_soa)
     soa_terminate(nh->nh_soa, 0);
-
-  if (cr->cr_orq) {
-    cr->cr_event = e;
-  }
-  else {
-    msg_destroy(msg);
-    UA_EVENT2(e, 400, "Internal error");
-    signal_call_state_change(nh, ss, 400, "Failure sending BYE",
-			     nua_callstate_terminated, 0, 0);
-    nua_session_usage_destroy(nh, ss);
-  }
+  cr->cr_usage = du;
 
   return 0;
 }
 
-
-void restart_bye(nua_handle_t *nh, tagi_t *tags)
+static int nua_bye_client_request(nua_client_request_t *cr,
+				  msg_t *msg, sip_t *sip,
+				  tagi_t const *tags)
 {
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_bye, tags);
+  nua_dialog_usage_t *du = cr->cr_usage;
+  nua_session_usage_t *ss;
+  char const *reason = NULL;
+
+  if (du == NULL)
+    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);
+
+  ss = nua_dialog_usage_private(du);
+  reason = ss->ss_reason;
+
+  return nua_base_client_trequest(cr, msg, sip,
+				  SIPTAG_REASON_STR(reason),
+				  TAG_NEXT(tags));
 }
 
 /** @NUA_EVENT nua_r_bye
  *
  * Answer to outgoing BYE.
  *
- * The BYE may be sent explicitly by nua_bye() or
- * implicitly by NUA state machine.
+ * The BYE may be sent explicitly by nua_bye() or implicitly by NUA state
+ * machine.
  *
  * @param status response status code
  *               (if the request is retried, @a status is 100, the @a
@@ -3316,45 +3505,44 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_bye(nua_handle_t *nh,
-				   nta_outgoing_t *orq,
-				   sip_t const *sip)
+static int nua_bye_client_report(nua_client_request_t *cr,
+				 int status, char const *phrase,
+				 sip_t const *sip,
+				 nta_outgoing_t *orq,
+				 tagi_t const *tags)
 {
-  nua_client_request_t *cr = NULL;
-  nua_session_usage_t *ss;
-  int status = sip ? sip->sip_status->st_status : 400;
-  char const *phrase = sip ? sip->sip_status->st_phrase : "";
+  nua_handle_t *nh = cr->cr_owner;
+  nua_dialog_usage_t *du = cr->cr_usage;
 
-  cr = nua_client_request_by_orq(nh->nh_ds->ds_cr, orq); assert(cr);
+  nua_stack_event(nh->nh_nua, nh, 
+		  nta_outgoing_getresponse(orq),
+		  cr->cr_event,
+		  status, phrase,
+		  tags);
 
-  if (cr) {
-    if (nua_creq_check_restart(nh, cr, orq, sip, restart_bye))
-      return 0;
-    nua_stack_process_response(nh, cr, orq, sip, TAG_END());
+  if (du == NULL) {
+    /* No more session */
   }
-  else {			/* No cr for BYE */
-    msg_t *msg = nta_outgoing_getresponse(orq);
-    nua_stack_event(nh->nh_nua, nh, msg, nua_r_bye, status, phrase, TAG_END());
-    nta_outgoing_destroy(orq);
+  else if (status < 200) {
+    /* Preliminary */
   }
+  else {
+    nua_session_usage_t *ss = nua_dialog_usage_private(du);
 
-  ss = nua_session_usage_get(nh->nh_ds);
+    signal_call_state_change(nh, ss, status, "to BYE", 
+			     nua_callstate_terminated);
 
-  if (status >= 200 && ss) {
-    if (ss->ss_crequest->cr_orq) {
-      /* Do not destroy usage while INVITE is alive */
-    }
-    else {
-      signal_call_state_change(nh, ss, status, "to BYE",
-			       nua_callstate_terminated, 0, 0);
-      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);
     }
   }
 
-  return 0;
+  return 1;
 }
 
-
 /** @NUA_EVENT nua_i_bye
  *
  * Incoming BYE request, call hangup.
@@ -3371,132 +3559,85 @@
  * @END_NUA_EVENT
  */
 
-int nua_stack_process_bye(nua_t *nua,
-			  nua_handle_t *nh,
-			  nta_incoming_t *irq,
-			  sip_t const *sip)
-{
-  nua_dialog_state_t *ds = nh->nh_ds;
-  nua_session_usage_t *ss;
-  nua_server_request_t *sr, *sr_next;
-  int early = 0;
-
-  ss = nua_session_usage_get(ds);
-  if (!ss)
-    return 481;
+int nua_bye_server_init(nua_server_request_t *sr);
+int nua_bye_server_report(nua_server_request_t *sr, tagi_t const *tags);
 
-  assert(nh && ss);
+nua_server_methods_t const nua_bye_server_methods = 
+  {
+    SIP_METHOD_BYE,
+    nua_i_bye,			/* Event */
+    { 
+      0,			/* Do not create dialog */
+      1,			/* In-dialog request */
+      0,			/* Not a target refresh request  */
+      0,			/* Do not add Contact */
+    },
+    nua_bye_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_base_server_respond,
+    nua_bye_server_report,
+  };
 
-  nua_stack_event(nh->nh_nua, nh, nta_incoming_getrequest(irq),
-		  nua_i_bye, SIP_200_OK, TAG_END());
-  nta_incoming_treply(irq, SIP_200_OK, TAG_END());
-  nta_incoming_destroy(irq), irq = NULL;
 
-  for (sr = ds->ds_sr; sr; sr = sr_next) {
-    sr_next = sr->sr_next;
-    if (sr->sr_respond && sr->sr_usage == nua_dialog_usage_public(ss)) {
-      char const *phrase;
-      early = ss->ss_state < nua_callstate_ready;
-      phrase = early ? "Early Session Terminated" : "Session Terminated";
-      sr->sr_usage = NULL;
-      if (sr->sr_respond)
-	nua_server_respond(sr, 487, phrase, TAG_END());
-      else
-	nua_server_request_destroy(sr);
-    }
-  }
+int nua_bye_server_init(nua_server_request_t *sr)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds);
 
-  signal_call_state_change(nh, ss, 200,
-			   early ? "Received early BYE" : "Received BYE",
-			   nua_callstate_terminated, 0, 0);
+  sr->sr_terminating = 1;
 
-  nua_session_usage_destroy(nh, ss);
+  if (du)
+    sr->sr_usage = du;
+  else
+    return SR_STATUS(sr, 481, "No Such Call");
 
   return 0;
 }
 
-/* ---------------------------------------------------------------------- */
-
-/**
- * Delivers call state changed event to the nua client. @internal
- *
- * @param nh call handle
- * @param status status code
- * @param tr_event SIP transaction event triggering this change
- * @param oa_recv Received SDP
- * @param oa_sent Sent SDP
- */
-
-static void signal_call_state_change(nua_handle_t *nh,
-				     nua_session_usage_t *ss,
-				     int status, char const *phrase,
-				     enum nua_callstate next_state,
-				     char const *oa_recv,
-				     char const *oa_sent)
-{
-  enum nua_callstate ss_state;
-
-  sdp_session_t const *remote_sdp = NULL;
-  char const *remote_sdp_str = NULL;
-  sdp_session_t const *local_sdp = NULL;
-  char const *local_sdp_str = NULL;
+int nua_bye_server_report(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage);
+  int early = 0, retval;
 
-  int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0;
+  if (sr->sr_status < 200)
+    return nua_base_server_report(sr, tags);
 
-  ss_state = ss ? ss->ss_state : nua_callstate_init;
+  if (ss) {
+    nua_server_request_t *sr0 = NULL, *sr_next;
+    char const *phrase;
 
-  if (ss_state < nua_callstate_ready || next_state > nua_callstate_ready)
-    SU_DEBUG_5(("nua(%p): call state changed: %s -> %s%s%s%s%s\n",
-		nh, nua_callstate_name(ss_state),
-		nua_callstate_name(next_state),
-		oa_recv ? ", received " : "", oa_recv ? oa_recv : "",
-		oa_sent && oa_recv ? ", and sent " :
-		oa_sent ? ", sent " : "", oa_sent ? oa_sent : ""));
-  else
-    SU_DEBUG_5(("nua(%p): ready call updated: %s%s%s%s%s\n",
-		nh, nua_callstate_name(next_state),
-		oa_recv ? " received " : "", oa_recv ? oa_recv : "",
-		oa_sent && oa_recv ? ", sent " :
-		oa_sent ? " sent " : "", oa_sent ? oa_sent : ""));
+    early = ss->ss_state < nua_callstate_ready;
+    phrase = early ? "Early Session Terminated" : "Session Terminated";
+    
+    for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr_next) {
+      sr_next = sr0->sr_next;
 
-  if (oa_recv) {
-    soa_get_remote_sdp(nh->nh_soa, &remote_sdp, &remote_sdp_str, 0);
-    offer_recv = strcasecmp(oa_recv, "offer") == 0;
-    answer_recv = strcasecmp(oa_recv, "answer") == 0;
-  }
+      if (sr == sr0 || sr0->sr_usage != sr->sr_usage)
+	continue;
 
-  if (oa_sent) {
-    soa_get_local_sdp(nh->nh_soa, &local_sdp, &local_sdp_str, 0);
-    offer_sent = strcasecmp(oa_sent, "offer") == 0;
-    answer_sent = strcasecmp(oa_sent, "answer") == 0;
+      if (nua_server_request_is_pending(sr0)) {
+	SR_STATUS(sr0, 487, phrase);
+	nua_server_respond(sr0, NULL);
+      }
+      nua_server_request_destroy(sr0);
+    }
   }
 
-  if (answer_recv || answer_sent) {
-    /* Update nh_hold_remote */
+  retval = nua_base_server_report(sr, tags);
 
-    char const *held;
+  assert(2 <= retval && retval < 4);
 
-    soa_get_params(nh->nh_soa, SOATAG_HOLD_REF(held), TAG_END());
-
-    nh->nh_hold_remote = held && strlen(held) > 0;
-  }
-
-  if (ss) {
-    /* Update state variables */
-    if (next_state > ss_state)
-      ss->ss_state = next_state;
-    else if (next_state == nua_callstate_init && ss_state < nua_callstate_ready)
-      ss->ss_state = nua_callstate_init, next_state = nua_callstate_terminated;
-  }
+  if (ss)
+    signal_call_state_change(nh, NULL, 200,
+			     early ? "Received early BYE" : "Received BYE",
+			     nua_callstate_terminated);
 
-  if (ss && ss->ss_state == nua_callstate_ready)
-    nh->nh_active_call = 1;
-  else if (next_state == nua_callstate_terminated)
-    nh->nh_active_call = 0;
+  return retval;
+}
 
-  /* Send events */
-  if (phrase == NULL)
-    phrase = "Call state";
+/* ---------------------------------------------------------------------- */
 
 /** @NUA_EVENT nua_i_state
  *
@@ -3506,19 +3647,22 @@
  *
  * In addition to basic changes of session status indicated with enum
  * ::nua_callstate, the @RFC3264 SDP Offer/Answer negotiation status is also
- * included if it is enabled (by default or with NUTAG_MEDIA_ENABLE(1)). The
- * received remote SDP is included in tag SOATAG_REMOTE_SDP(). The tags
- * NUTAG_OFFER_RECV() or NUTAG_ANSWER_RECV() indicate whether the remote SDP
- * was an offer or an answer. The SDP negotiation result is included in the
- * tags SOATAG_LOCAL_SDP() and SOATAG_LOCAL_SDP_STR() and tags
- * NUTAG_OFFER_SENT() or NUTAG_ANSWER_SENT() indicate whether the local SDP
- * was an offer or answer.
+ * included. The tags NUTAG_OFFER_RECV() or NUTAG_ANSWER_RECV() indicate
+ * whether the remote SDP that was received was considered as an offer or an
+ * answer. Tags NUTAG_OFFER_SENT() or NUTAG_ANSWER_SENT() indicate whether
+ * the local SDP which was sent was considered as an offer or answer.
+ *
+ * If the @b soa SDP negotiation is enabled (by default or with
+ * NUTAG_MEDIA_ENABLE(1)), the received remote SDP is included in tags
+ * SOATAG_REMOTE_SDP() and SOATAG_REMOTE_SDP_STR(). The SDP negotiation
+ * result from @b soa is included in the tags SOATAG_LOCAL_SDP() and
+ * SOATAG_LOCAL_SDP_STR().
  *
  * SOATAG_ACTIVE_AUDIO() and SOATAG_ACTIVE_VIDEO() are informational tags
  * used to indicate what is the status of audio or video.
  *
- * Note that #nua_i_state also covers call establisment events
- * (#nua_i_active) and termination (#nua_i_terminated).
+ * Note that #nua_i_state also covers the information relayed in call
+ * establisment (#nua_i_active) and termination (#nua_i_terminated) events.
  *
  * @param status protocol status code \n
  *               (always present)
@@ -3537,6 +3681,7 @@
  *
  * @sa @ref nua_call_model, #nua_i_active, #nua_i_terminated,
  * nua_invite(), #nua_r_invite, #nua_i_invite, nua_respond(), 
+ * NUTAG_MEDIA_ENABLE(),
  * NUTAG_AUTOALERT(), NUTAG_AUTOANSWER(), NUTAG_EARLY_MEDIA(),
  * NUTAG_EARLY_ANSWER(), NUTAG_INCLUDE_EXTRA_SDP(),
  * nua_ack(), NUTAG_AUTOACK(), nua_bye(), #nua_r_bye, #nua_i_bye,
@@ -3544,23 +3689,148 @@
  * nua_prack(), #nua_r_prack, #nua_i_prack,
  * nua_update(), #nua_r_update, #nua_i_update
  *
+ * @par History
+ * Prior @VERSION_1_12_6 the tags NUTAG_OFFER_RECV(), NUTAG_ANSWER_RECV(),
+ * NUTAG_ANSWER_SENT(), NUTAG_OFFER_SENT() were not included with
+ * nua_i_state eventif media was disabled.
+ *
  * @END_NUA_EVENT
  */
 
-  nua_stack_event(nh->nh_nua, nh, NULL, nua_i_state,
-		  status, phrase,
-		  NUTAG_CALLSTATE(next_state),
-		  NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa),
-		  /* NUTAG_SOA_SESSION(nh->nh_soa), */
-		  TAG_IF(offer_recv, NUTAG_OFFER_RECV(offer_recv)),
-		  TAG_IF(answer_recv, NUTAG_ANSWER_RECV(answer_recv)),
-		  TAG_IF(offer_sent, NUTAG_OFFER_SENT(offer_sent)),
-		  TAG_IF(answer_sent, NUTAG_ANSWER_SENT(answer_sent)),
-		  TAG_IF(oa_recv, SOATAG_REMOTE_SDP(remote_sdp)),
-		  TAG_IF(oa_recv, SOATAG_REMOTE_SDP_STR(remote_sdp_str)),
-		  TAG_IF(oa_sent, SOATAG_LOCAL_SDP(local_sdp)),
-		  TAG_IF(oa_sent, SOATAG_LOCAL_SDP_STR(local_sdp_str)),
-		  TAG_END());
+/**
+ * Delivers call state changed event to the nua client. @internal
+ *
+ * @param nh call handle
+ * @param status status code
+ * @param tr_event SIP transaction event triggering this change
+ * @param oa_recv Received SDP
+ * @param oa_sent Sent SDP
+ */
+
+static void signal_call_state_change(nua_handle_t *nh,
+				     nua_session_usage_t *ss,
+				     int status, char const *phrase,
+				     enum nua_callstate next_state)
+{
+  enum nua_callstate ss_state = nua_callstate_init;
+
+  char const *oa_recv = NULL;
+  char const *oa_sent = NULL;
+
+  int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0;
+
+  if (ss) {
+    if (ss->ss_reporting)
+      return;
+
+    ss_state = ss->ss_state;
+    oa_recv = ss->ss_oa_recv, ss->ss_oa_recv = NULL;
+    oa_sent = ss->ss_oa_sent, ss->ss_oa_sent = NULL;
+
+    if (oa_recv) {
+      offer_recv = strcasecmp(oa_recv, "offer") == 0;
+      answer_recv = strcasecmp(oa_recv, "answer") == 0;
+    }
+
+    if (oa_sent) {
+      offer_sent = strcasecmp(oa_sent, "offer") == 0;
+      answer_sent = strcasecmp(oa_sent, "answer") == 0;
+    }
+  }
+
+  if (ss_state < nua_callstate_ready || next_state > nua_callstate_ready)
+    SU_DEBUG_5(("nua(%p): call state changed: %s -> %s%s%s%s%s\n",
+		(void *)nh, nua_callstate_name(ss_state),
+		nua_callstate_name(next_state),
+		oa_recv ? ", received " : "", oa_recv ? oa_recv : "",
+		oa_sent && oa_recv ? ", and sent " :
+		oa_sent ? ", sent " : "", oa_sent ? oa_sent : ""));
+  else
+    SU_DEBUG_5(("nua(%p): ready call updated: %s%s%s%s%s\n",
+		(void *)nh, nua_callstate_name(next_state),
+		oa_recv ? " received " : "", oa_recv ? oa_recv : "",
+		oa_sent && oa_recv ? ", sent " :
+		oa_sent ? " sent " : "", oa_sent ? oa_sent : ""));
+
+  if (next_state == nua_callstate_terminating &&
+      ss_state >= nua_callstate_terminating)
+    return;
+
+  if (ss) {
+    /* Update state variables */
+    if (next_state == nua_callstate_init) {
+      if (ss_state < nua_callstate_ready)
+	ss->ss_state = next_state;
+      else
+	/* Do not change state - we are ready, terminating, or terminated */
+	next_state = ss_state;
+    }
+    else if (next_state > ss_state)
+      ss->ss_state = next_state;
+  }
+
+  if (next_state == nua_callstate_init) 
+    next_state = nua_callstate_terminated;
+
+  if (ss && ss->ss_state == nua_callstate_ready)
+    nh->nh_active_call = 1;
+  else if (next_state == nua_callstate_terminated)
+    nh->nh_active_call = 0;
+
+  /* Send events */
+  if (phrase == NULL)
+    phrase = "Call state";
+
+  {
+    sdp_session_t const *remote_sdp = NULL;
+    char const *remote_sdp_str = NULL;
+    sdp_session_t const *local_sdp = NULL;
+    char const *local_sdp_str = NULL;
+
+    if (nh->nh_soa) {
+      if (oa_recv)
+	soa_get_remote_sdp(nh->nh_soa, &remote_sdp, &remote_sdp_str, 0);
+      if (oa_sent)
+	soa_get_local_sdp(nh->nh_soa, &local_sdp, &local_sdp_str, 0);
+
+      if (answer_recv || answer_sent) {      /* Update nh_hold_remote */
+	char const *held = NULL;
+	soa_get_params(nh->nh_soa, SOATAG_HOLD_REF(held), TAG_END());
+	nh->nh_hold_remote = held && strlen(held) > 0;
+      }
+    }
+    else
+      oa_recv = NULL, oa_sent = NULL;
+
+    nua_stack_tevent(nh->nh_nua, nh, NULL, nua_i_state,
+		     status, phrase,
+		     NUTAG_CALLSTATE(next_state),
+		     NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa),
+		     /* NUTAG_SOA_SESSION(nh->nh_soa), */
+		     TAG_IF(offer_recv, NUTAG_OFFER_RECV(offer_recv)),
+		     TAG_IF(answer_recv, NUTAG_ANSWER_RECV(answer_recv)),
+		     TAG_IF(offer_sent, NUTAG_OFFER_SENT(offer_sent)),
+		     TAG_IF(answer_sent, NUTAG_ANSWER_SENT(answer_sent)),
+		     TAG_IF(oa_recv, SOATAG_REMOTE_SDP(remote_sdp)),
+		     TAG_IF(oa_recv, SOATAG_REMOTE_SDP_STR(remote_sdp_str)),
+		     TAG_IF(oa_sent, SOATAG_LOCAL_SDP(local_sdp)),
+		     TAG_IF(oa_sent, SOATAG_LOCAL_SDP_STR(local_sdp_str)),
+		     TAG_END());
+  }
+
+  if (next_state == nua_callstate_ready && ss_state <= nua_callstate_ready) {
+    nua_stack_tevent(nh->nh_nua, nh, NULL, nua_i_active, status, "Call active",
+		     NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa),
+		     /* NUTAG_SOA_SESSION(nh->nh_soa), */
+		     TAG_END());
+  }
+
+  else if (next_state == nua_callstate_terminated) {
+    nua_stack_event(nh->nh_nua, nh, NULL,
+		    nua_i_terminated, status, phrase,
+		    NULL);
+  }
+}
 
 /** @NUA_EVENT nua_i_active
  *
@@ -3583,13 +3853,6 @@
  * @END_NUA_EVENT
  */
 
-  if (next_state == nua_callstate_ready && ss_state <= nua_callstate_ready) {
-    nua_stack_event(nh->nh_nua, nh, NULL, nua_i_active, status, "Call active",
-	     NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa),
-	     /* NUTAG_SOA_SESSION(nh->nh_soa), */
-	     TAG_END());
-  }
-
 /** @NUA_EVENT nua_i_terminated
  *
  * A call has been terminated.
@@ -3614,18 +3877,13 @@
  * @END_NUA_EVENT
  */
 
-  else if (next_state == nua_callstate_terminated) {
-    nua_stack_event(nh->nh_nua, nh, NULL, nua_i_terminated, status, phrase,
-	     TAG_END());
-  }
-}
 
 /* ======================================================================== */
 
 static
-int respond_with_retry_after(nua_handle_t *nh, nta_incoming_t *irq,
-			     int status, char const *phrase,
-			     int min, int max)
+int nua_server_retry_after(nua_server_request_t *sr,
+			   int status, char const *phrase,
+			   int min, int max)
 {
   sip_retry_after_t af[1];
 
@@ -3633,17 +3891,18 @@
   af->af_delta = (unsigned)su_randint(min, max);
   af->af_comment = phrase;
 
-  nta_incoming_treply(irq, status, phrase,
-		      SIPTAG_RETRY_AFTER(af),
-		      SIPTAG_USER_AGENT_STR(NH_PGET(nh, user_agent)),
-		      TAG_END());
+  sip_add_dup(sr->sr_response.msg, sr->sr_response.sip, (sip_header_t *)af);
 
-  return 500;
+  return sr_status(sr, status, phrase);
 }
 
 /* ======================================================================== */
 
-/** Get SDP from a SIP message */
+/** Get SDP from a SIP message.
+ *
+ * @retval 1 if message contains SDP
+ * @retval 0 if message does not contain valid SDP
+ */
 static
 int session_get_description(sip_t const *sip,
 			    char const **return_sdp,
@@ -3680,8 +3939,10 @@
       return 0;
   }
 
-  *return_sdp = pl->pl_data;
-  *return_len = pl->pl_len;
+  if (return_sdp && return_len) {
+    *return_sdp = pl->pl_data;
+    *return_len = pl->pl_len;
+  }
 
   return 1;
 }
@@ -3756,146 +4017,6 @@
   return retval;
 }
 
-/**
- * Stores and processes SDP from incoming response, then calls
- * nua_stack_process_response().
- *
- * @retval 1 if there was SDP to process.
- */
-static
-int session_process_response(nua_handle_t *nh,
-			     nua_client_request_t *cr,
-			     nta_outgoing_t *orq,
-			     sip_t const *sip,
-			     char const **return_received)
-{
-  char const *method = nta_outgoing_method_name(orq);
-  msg_t *msg = nta_outgoing_getresponse(orq);
-  int retval = 0;
-  char const *sdp = NULL;
-  size_t len;
-
-  if (nh->nh_soa == NULL)
-    /* Xyzzy */;
-  else if (!session_get_description(sip, &sdp, &len))
-    /* No SDP */;
-  else if (cr->cr_answer_recv) {
-    /* Ignore spurious answers after completing O/A */
-    SU_DEBUG_3(("nua(%p): %s: ignoring duplicate SDP in %u %s\n",
-		nh, method,
-		sip->sip_status->st_status, sip->sip_status->st_phrase));
-    sdp = NULL;
-  }
-  else if (!cr->cr_offer_sent &&
-	   nta_outgoing_method(orq) != sip_method_invite) {
-    /* If non-invite request did not have offer, ignore SDP in response */
-    SU_DEBUG_3(("nua(%p): %s: ignoring extra SDP in %u %s\n",
-		nh, method,
-		sip->sip_status->st_status, sip->sip_status->st_phrase));
-    sdp = NULL;
-  }
-  else {
-    if (cr->cr_offer_sent) {
-      cr->cr_answer_recv = sip->sip_status->st_status;
-      *return_received = "answer";
-    }
-    else {
-      cr->cr_offer_recv = 1, cr->cr_answer_sent = 0;
-      *return_received = "offer";
-    }
-
-    if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
-      SU_DEBUG_5(("nua(%p): %s: error parsing SDP in %u %s\n",
-		  nh, method,
-		  sip->sip_status->st_status,
-		  sip->sip_status->st_phrase));
-      retval = -1;
-      sdp = NULL;
-    }
-    else if (cr->cr_offer_recv) {
-      /* note: case 1: incoming offer */
-      SU_DEBUG_5(("nua(%p): %s: get SDP %s in %u %s\n",
-		  nh, method, "offer",
-		  sip->sip_status->st_status,
-		  sip->sip_status->st_phrase));
-      retval = 1;
-    }
-    else if (soa_process_answer(nh->nh_soa, NULL) < 0) {
-      SU_DEBUG_5(("nua(%p): %s: error processing SDP answer in %u %s\n",
-		  nh, method,
-		  sip->sip_status->st_status,
-		  sip->sip_status->st_phrase));
-      sdp = NULL;
-    }
-    else {
-      /* note: case 2: answer to our offer */
-      if (soa_activate(nh->nh_soa, NULL) < 0) {
-	SU_DEBUG_3(("nua(%p): %s: error activating media after %u %s\n",
-		    nh, method,
-		    sip->sip_status->st_status,
-		    sip->sip_status->st_phrase));
-	/* XXX */
-      }
-      else {
-	SU_DEBUG_5(("nua(%p): %s: processed SDP answer in %u %s\n",
-		    nh, method,
-		    sip->sip_status->st_status,
-		    sip->sip_status->st_phrase));
-      }
-
-      assert(!cr->cr_offer_recv);
-    }
-  }
-
-  msg_destroy(msg);		/* unref */
-
-  nua_stack_process_response(nh, cr, orq, sip,
-			     NH_REMOTE_MEDIA_TAGS(sdp != NULL, nh->nh_soa),
-			     TAG_END());
-
-  return retval;
-}
-
-#if 0
-/** Parse and store SDP from incoming request */
-static
-int session_process_request(nua_handle_t *nh,
-			    nta_incoming_t *irq,
-			    sip_t const *sip)
-{
-  char const *sdp = NULL;
-  isize_t len;
-
-  if (nh->nh_soa) {
-    msg_t *msg = nta_outgoing_getresponse(irq);
-
-    if (session_get_description(msg, sip, &sdp, &len)) {
-      if (soa_is_complete(nh->nh_soa)) {
-	/* Ignore spurious answers after completing O/A */
-	SU_DEBUG_5(("nua: ignoring duplicate SDP in %u %s\n",
-		    sip->sip_status->st_status, sip->sip_status->st_phrase));
-	sdp = NULL;
-      }
-      else if (soa_parse_sdp(nh->nh_soa, sdp, len) < 0) {
-	SU_DEBUG_5(("nua: error parsing SDP in %u %s\n",
-		    sip->sip_status->st_status,
-		    sip->sip_status->st_phrase));
-	sdp = NULL;
-      }
-    }
-
-    msg_destroy(msg);
-  }
-
-  return
-    nua_stack_process_response(nh, cr, orq, sip,
-			       NH_REMOTE_MEDIA_TAGS(sdp != NULL, nh->nh_soa),
-			       TAG_END());
-}
-#endif
-
-static int respond_to_options(nua_server_request_t *sr, tagi_t const *tags);
-
 /** @NUA_EVENT nua_i_options
  *
  * Incoming OPTIONS request. The user-agent should respond to an OPTIONS
@@ -3924,50 +4045,38 @@
  * @END_NUA_EVENT
  */
 
-int nua_stack_process_options(nua_t *nua,
-			      nua_handle_t *nh,
-			      nta_incoming_t *irq,
-			      sip_t const *sip)
-{
-  nua_server_request_t *sr, sr0[1];
-  int done;
-
-  /* Hook to outbound */
-  done = nua_registration_process_request(nua->nua_registrations, irq, sip);
-  if (done)
-    return done;
-
-  sr = nua_server_request(nua, nh, irq, sip, SR_INIT(sr0), sizeof *sr,
-			  respond_to_options, 0);
+int nua_options_server_respond(nua_server_request_t *sr, tagi_t const *tags);
 
-  SR_STATUS1(sr, SIP_200_OK);
-
-  return nua_stack_server_event(nua, sr, nua_i_options, TAG_END());
-}
+nua_server_methods_t const nua_options_server_methods = 
+  {
+    SIP_METHOD_OPTIONS,
+    nua_i_options,		/* Event */
+    { 
+      0,			/* Do not create dialog */
+      0,			/* Initial request */
+      0,			/* Not a target refresh request  */
+      1,			/* Add Contact */
+    },
+    nua_base_server_init,
+    nua_base_server_preprocess,
+    nua_base_server_params,
+    nua_options_server_respond,
+    nua_base_server_report,
+  };
 
 /** @internal Respond to an OPTIONS request.
  *
  */
-static int respond_to_options(nua_server_request_t *sr, tagi_t const *tags)
+int nua_options_server_respond(nua_server_request_t *sr, tagi_t const *tags)
 {
   nua_handle_t *nh = sr->sr_owner;
   nua_t *nua = nh->nh_nua;
-  msg_t *msg;
-  int final;
-
-  msg = nua_server_response(sr,
-			    sr->sr_status, sr->sr_phrase,
-			    SIPTAG_ALLOW(NH_PGET(nh, allow)),
-			    SIPTAG_SUPPORTED(NH_PGET(nh, supported)),
-			    TAG_IF(NH_PGET(nh, path_enable),
-				   SIPTAG_SUPPORTED_STR("path")),
-			    SIPTAG_ACCEPT_STR(SDP_MIME_TYPE),
-			    TAG_NEXT(tags));
 
-  final = sr->sr_status >= 200;
+  if (200 <= sr->sr_status && sr->sr_status < 300) {
+    msg_t *msg = sr->sr_response.msg;
+    sip_t *sip = sr->sr_response.sip;
 
-  if (msg) {
-    sip_t *sip = sip_object(msg);
+    sip_add_tl(msg, sip, SIPTAG_ACCEPT(nua->nua_invite_accept), TAG_END());
 
     if (!sip->sip_payload) {	/* XXX - do MIME multipart? */
       soa_session_t *soa = nh->nh_soa;
@@ -3977,10 +4086,8 @@
 
       session_include_description(soa, 0, msg, sip);
     }
-
-    if (nta_incoming_mreply(sr->sr_irq, msg) < 0)
-      final = 1;
   }
 
-  return final;
+  return nua_base_server_respond(sr, tags);
 }
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c	Tue Mar 20 23:37:15 2007
@@ -37,7 +37,7 @@
 #include "config.h"
 
 #include <sofia-sip/su_tag_class.h>
-#include <sofia-sip/su_tag_class.h>
+#include <sofia-sip/su_tag_inline.h>
 #include <sofia-sip/su_tagarg.h>
 #include <sofia-sip/su_strlst.h>
 #include <sofia-sip/su_uniqueid.h>
@@ -51,8 +51,7 @@
 
 #define NTA_AGENT_MAGIC_T    struct nua_s
 #define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
-#define NTA_INCOMING_MAGIC_T struct nua_handle_s
+#define NTA_OUTGOING_MAGIC_T struct nua_client_request
 
 #include <sofia-sip/sip.h>
 #include <sofia-sip/sip_header.h>
@@ -147,7 +146,7 @@
     return -1;
 
   dnh->nh_prefs = (void *)(dnh + 1);
-  dnh->nh_valid = nua_handle;
+  dnh->nh_valid = nua_valid_handle_cookie;
   dnh->nh_nua = nua;
   nua_handle_ref(dnh); dnh->nh_ref_by_stack = 1; 
   nua_handle_ref(dnh); dnh->nh_ref_by_user = 1;
@@ -226,29 +225,40 @@
 int nh_notifier_shutdown(nua_handle_t *nh, nea_event_t *ev,
 			 tag_type_t t, tag_value_t v, ...);
 
+int nua_stack_tevent(nua_t *nua, nua_handle_t *nh, msg_t *msg,
+		     nua_event_t event, int status, char const *phrase,
+		     tag_type_t tag, tag_value_t value, ...)
+{
+  ta_list ta;
+  int retval;
+  ta_start(ta, tag, value);
+  retval = nua_stack_event(nua, nh, msg, event, status, phrase, ta_args(ta));
+  ta_end(ta);
+  return retval;
+}
+
 /** @internal Send an event to the application. */
 int nua_stack_event(nua_t *nua, nua_handle_t *nh, msg_t *msg,
 		    nua_event_t event, int status, char const *phrase,
-		    tag_type_t tag, tag_value_t value, ...)
+		    tagi_t const *tags)
 {
   su_msg_r sumsg = SU_MSG_R_INIT;
-
-  ta_list ta;
   size_t e_len, len, xtra, p_len;
 
   if (event == nua_r_ack || event == nua_i_none)
     return event;
 
-  enter;
+  if (nh == nua->nua_dhandle)
+    nh = NULL;
 
   if (nua_log->log_level >= 5) {
     char const *name = nua_event_name(event) + 4;
     char const *p = phrase ? phrase : "";
 
     if (status == 0)
-      SU_DEBUG_5(("nua(%p): %s %s\n", nh, name, p));
+      SU_DEBUG_5(("nua(%p): event %s %s\n", (void *)nh, name, p));
     else
-      SU_DEBUG_5(("nua(%p): %s %u %s\n", nh, name, status, p));
+      SU_DEBUG_5(("nua(%p): event %s %u %s\n", (void *)nh, name, status, p));
   }
 
   if (event == nua_r_destroy) {
@@ -269,36 +279,42 @@
     return event;
   }
 
-  ta_start(ta, tag, value);
-
-  e_len = offsetof(event_t, e_tags);
-  len = tl_len(ta_args(ta));
-  xtra = tl_xtra(ta_args(ta), len);
+  if (tags) {
+    e_len = offsetof(event_t, e_tags);
+    len = tl_len(tags);
+    xtra = tl_xtra(tags, len);
+  }
+  else {
+    e_len = sizeof(event_t), len = 0, xtra = 0;
+  }
   p_len = phrase ? strlen(phrase) + 1 : 1;
 
   if (su_msg_create(sumsg, nua->nua_client, su_task_null,
 		    nua_event, e_len + len + xtra + p_len) == 0) {
     event_t *e = su_msg_data(sumsg);
+    void *p;
 
-    tagi_t *t = e->e_tags, *t_end = (tagi_t *)((char *)t + len);
-    void *b = t_end, *end = (char *)b + xtra;
+    if (tags) {
+      tagi_t *t = e->e_tags, *t_end = (tagi_t *)((char *)t + len);
+      void *b = t_end, *end = (char *)b + xtra;
 
-    t = tl_dup(t, ta_args(ta), &b);
-    assert(t == t_end); assert(b == end);
+      t = tl_dup(t, tags, &b); p = b;
+      assert(t == t_end); assert(b == end); (void)end;
+    }
+    else
+      p = e + 1;
 
     e->e_event = event;
     e->e_nh = nh ? nua_handle_ref(nh) : nua->nua_dhandle;
     e->e_status = status;
-    e->e_phrase = strcpy(end, phrase ? phrase : "");
+    e->e_phrase = strcpy(p, phrase ? phrase : "");
     if (msg)
       e->e_msg = msg, su_home_threadsafe(msg_home(msg));
 
-    if (su_msg_send(sumsg) != 0)
+    if (su_msg_send(sumsg) != 0 && nh)
       nua_handle_unref(nh);
   }
 
-  ta_end(ta);
-
   return event;
 }
 
@@ -322,9 +338,16 @@
 {
   nua_handle_t *nh = e->e_nh;
   tagi_t *tags = e->e_tags;
+  nua_event_t event;
+  int error = 0;
 
   assert(tags);
 
+  if (nua_log->log_level >= 7) {
+    char const *name = nua_event_name(e->e_event) + 4;
+    SU_DEBUG_7(("nua(%p): recv %s\n", (void *)nh, name));
+  }
+
   if (nh) {
     if (!nh->nh_prev)
       nh_append(nua, nh);
@@ -338,98 +361,105 @@
   if (nua_log->log_level >= 5) {
     char const *name = nua_event_name(e->e_event);
     if (e->e_status == 0)
-      SU_DEBUG_5(("nua(%p): signal %s\n", nh, name + 4));
+      SU_DEBUG_5(("nua(%p): signal %s\n", (void *)nh, name + 4));
     else
       SU_DEBUG_5(("nua(%p): signal %s %u %s\n",
-		  nh, name + 4, e->e_status, e->e_phrase ? e->e_phrase : ""));
+		  (void *)nh, name + 4,
+		  e->e_status, e->e_phrase ? e->e_phrase : ""));
   }
 
   su_msg_save(nua->nua_signal, msg);
 
+  event = e->e_event;
+
   if (nua->nua_shutdown && !e->e_always) {
     /* Shutting down */
-    nua_stack_event(nua, nh, NULL, e->e_event,
+    nua_stack_event(nua, nh, NULL, event,
 		    901, "Stack is going down",
-		    TAG_END());
+		    NULL);
   }
-
-  else switch (e->e_event) {
+  else switch (event) {
   case nua_r_get_params:
-    nua_stack_get_params(nua, nh ? nh : nua->nua_dhandle, e->e_event, tags);
+    nua_stack_get_params(nua, nh ? nh : nua->nua_dhandle, event, tags);
     break;
   case nua_r_set_params:
-    nua_stack_set_params(nua, nh ? nh : nua->nua_dhandle, e->e_event, tags);
+    nua_stack_set_params(nua, nh ? nh : nua->nua_dhandle, event, tags);
     break;
   case nua_r_shutdown:
     nua_stack_shutdown(nua);
     break;
   case nua_r_register:
   case nua_r_unregister:
-    nua_stack_register(nua, nh, e->e_event, tags);
+    nua_stack_register(nua, nh, event, tags);
     break;
   case nua_r_invite:
-    nua_stack_invite(nua, nh, e->e_event, tags);
+    error = nua_stack_invite(nua, nh, event, tags);
     break;
   case nua_r_cancel:
-    nua_stack_cancel(nua, nh, e->e_event, tags);
+    error = nua_stack_cancel(nua, nh, event, tags);
     break;
   case nua_r_bye:
-    nua_stack_bye(nua, nh, e->e_event, tags);
+    error = nua_stack_bye(nua, nh, event, tags);
     break;
   case nua_r_options:
-    nua_stack_options(nua, nh, e->e_event, tags);
+    error = nua_stack_options(nua, nh, event, tags);
     break;
   case nua_r_refer:
-    nua_stack_refer(nua, nh, e->e_event, tags);
+    error = nua_stack_refer(nua, nh, event, tags);
     break;
   case nua_r_publish:
   case nua_r_unpublish:
-    nua_stack_publish(nua, nh, e->e_event, tags);
+    error = nua_stack_publish(nua, nh, event, tags);
     break;
   case nua_r_info:
-    nua_stack_info(nua, nh, e->e_event, tags);
+    error = nua_stack_info(nua, nh, event, tags);
     break;
   case nua_r_update:
-    nua_stack_update(nua, nh, e->e_event, tags);
+    error = nua_stack_update(nua, nh, event, tags);
     break;
   case nua_r_message:
-    nua_stack_message(nua, nh, e->e_event, tags);
+    error = nua_stack_message(nua, nh, event, tags);
     break;
   case nua_r_subscribe:
   case nua_r_unsubscribe:
-    nua_stack_subscribe(nua, nh, e->e_event, tags);
+    error = nua_stack_subscribe(nua, nh, event, tags);
     break;
   case nua_r_notify:
-    nua_stack_notify(nua, nh, e->e_event, tags);
+    error = nua_stack_notify(nua, nh, event, tags);
     break;
   case nua_r_notifier:
-    nua_stack_notifier(nua, nh, e->e_event, tags);
+    nua_stack_notifier(nua, nh, event, tags);
     break;
   case nua_r_terminate:
-    nua_stack_terminate(nua, nh, e->e_event, tags);
+    nua_stack_terminate(nua, nh, event, tags);
     break;
   case nua_r_method:
-    nua_stack_method(nua, nh, e->e_event, tags);
+    error = nua_stack_method(nua, nh, event, tags);
     break;
   case nua_r_authenticate:
-    nua_stack_authenticate(nua, nh, e->e_event, tags);
+    nua_stack_authenticate(nua, nh, event, tags);
     break;
   case nua_r_authorize:
-    nua_stack_authorize(nua, nh, e->e_event, tags);
+    nua_stack_authorize(nua, nh, event, tags);
     break;
   case nua_r_ack:
-    nua_stack_ack(nua, nh, e->e_event, tags);
+    error = nua_stack_ack(nua, nh, event, tags);
     break;
   case nua_r_respond:
     nua_stack_respond(nua, nh, e->e_status, e->e_phrase, tags);
     break;
-  case nua_r_destroy:
+  case nua_r_destroy: 
     nua_stack_destroy_handle(nua, nh, tags);
-    break;
+    su_msg_destroy(nua->nua_signal);
+    return;
   default:
     break;
   }
 
+  if (error < 0) {
+    nua_stack_event(nh->nh_nua, nh, NULL, event, NUA_INTERNAL_ERROR, NULL);
+  }
+
   if (su_msg_is_non_null(nua->nua_signal))
     su_msg_destroy(nua->nua_signal);
 
@@ -512,6 +542,7 @@
 }
 
 
+
 /* ====================================================================== */
 
 /**Shutdown a @nua stack.
@@ -581,15 +612,11 @@
     for (sr = ds->ds_sr; sr; sr = sr_next) {
       sr_next = sr->sr_next;
 
-      if (sr->sr_respond) {
-	SR_STATUS1(sr, SIP_410_GONE);
-	sr->sr_usage = NULL;
-	sr->sr_respond(sr, NULL);
-	sr->sr_respond = NULL;
-	busy++;
+      if (nua_server_request_is_pending(sr)) {
+	SR_STATUS1(sr, SIP_410_GONE); /* 410 terminates dialog */
+	nua_server_respond(sr, NULL);
+	nua_server_report(sr);
       }
-	
-      nua_server_request_destroy(sr);
     }
 
     busy += nh_call_pending(nh, 0);
@@ -625,7 +652,7 @@
     nta_agent_destroy(nua->nua_nta), nua->nua_nta = NULL;
   }
 
-  nua_stack_event(nua, NULL, NULL, nua_r_shutdown, status, phrase, TAG_END());
+  nua_stack_event(nua, NULL, NULL, nua_r_shutdown, status, phrase, NULL);
 }
 
 /* ---------------------------------------------------------------------- */
@@ -650,7 +677,7 @@
   return nh;
 }
 
-/** @internal Append an handle to the list of handles */
+/** @internal Append a handle to the list of handles */
 void nh_append(nua_t *nua, nua_handle_t *nh)
 {
   nh->nh_next = NULL;
@@ -673,17 +700,15 @@
 
 void nua_stack_destroy_handle(nua_t *nua, nua_handle_t *nh, tagi_t const *tags)
 {
-  nh_call_pending(nh, 0);	/* Call pending operations with 0 */
-
   if (nh->nh_notifier)
     nua_stack_terminate(nua, nh, 0, NULL);
 
-#if 0
+  nua_dialog_shutdown(nh, nh->nh_ds);
+
   if (nh->nh_ref_by_user) {
     nh->nh_ref_by_user = 0;
     nua_handle_unref(nh);
   }
-#endif
 
   nh_destroy(nua, nh);
 }
@@ -712,12 +737,14 @@
 {
   assert(nh); assert(nh != nua->nua_dhandle);
 
-  nh_enter;
-
   if (nh->nh_notifier)
     nea_server_destroy(nh->nh_notifier), nh->nh_notifier = NULL;
 
-  nua_creq_deinit(nh->nh_ds->ds_cr, NULL);
+  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);
 
@@ -733,7 +760,7 @@
 /* ======================================================================== */
 
 /**@internal
- * Initialize handle Allow and authentication info, save parameters.
+ * Save handle parameters and initial authentication info.
  *
  * @retval -1 upon an error
  * @retval 0 when successful
@@ -939,1175 +966,1746 @@
   return server + proxy;
 }
 
-#include <sofia-sip/su_tag_inline.h>
-
-/** Check if tag list has contact */
-int nua_tagis_have_contact_tag(tagi_t const *t)
+static inline
+int can_redirect(sip_contact_t const *m, sip_method_t method)
 {
-  for (; t && t->t_tag; t = t_next(t))
-    if (t->t_tag == siptag_contact ||
-	t->t_tag == siptag_contact_str)
-      return 1;
+  if (m && m->m_url->url_host) {
+    enum url_type_e type = m->m_url->url_type;
+    return
+      type == url_sip ||
+      type == url_sips ||
+      (type == url_tel &&
+       (method == sip_method_invite || method == sip_method_message)) ||
+      (type == url_im && method == sip_method_message) ||
+      (type == url_pres && method == sip_method_subscribe);
+  }
   return 0;
 }
 
-/**@internal
- * Create a request message.
- *
- * @param nua
- * @param nh
- * @param cr
- * @param restart
- * @param method
- * @param name
- * @param tag, value, ... list of tag-value pairs
- */
-msg_t *nua_creq_msg(nua_t *nua,
-		    nua_handle_t *nh,
-		    nua_client_request_t *cr,
-		    int restart,
-		    sip_method_t method, char const *name,
-		    tag_type_t tag, tag_value_t value, ...)
-{
-  struct nua_dialog_state *ds = nh->nh_ds;
-  msg_t *msg = NULL;
-  sip_t *sip;
-  ta_list ta;
-  url_string_t const *url = NULL;
-  long seq = -1;
-  int copy = 1;
-
-  /* If restarting, use existing message */
-  if (restart) {
-    msg = cr->cr_msg; sip = sip_object(msg);
-
-    /* Trying to restart different method? */
-    if (sip && method && sip->sip_request->rq_method != method) {
-      SU_DEBUG_3(("nua(%p): trying to %s "
-		  "but there is already %s waiting to restart\n",
-		  nh, name, sip->sip_request->rq_method_name));
-      restart = 0, msg = NULL; sip = NULL;
-    }
-
-    /* Remove CSeq */
-    if (sip && sip->sip_cseq)
-      sip_header_remove(msg, sip, (sip_header_t *)sip->sip_cseq);
-    if (sip && sip->sip_request)
-      method = sip->sip_request->rq_method,
-	name = sip->sip_request->rq_method_name;
-  }
-
-  if (!restart) {
-    if (cr->cr_msg) {
-      /* If method is ACK or CANCEL, use existing CSeq */
-      if (method == sip_method_ack || method == sip_method_cancel) {
-	sip_t *nh_sip = sip_object(cr->cr_msg);
-	if (nh_sip && nh_sip->sip_cseq)
-	  seq = nh_sip->sip_cseq->cs_seq;
-	/* ACK/CANCEL cannot be restarted so we do not copy message */
-	copy = 0;
-      }
-      else
-	msg_destroy(cr->cr_msg), cr->cr_msg = NULL;
-    }
-    msg = nta_msg_create(nua->nua_nta, 0);
-
-    /**@par Populating SIP Request Message with Tagged Arguments
-     *
-     * The tagged arguments can be used to pass values for any SIP headers
-     * to the stack. When the INVITE message (or any other SIP message) is
-     * created, the tagged values saved with nua_handle() are used first,
-     * next the tagged values given with the operation (nua_invite()) are
-     * added.
-     *
-     * When multiple tags for the same header are specified, the behaviour
-     * depends on the header type. If only a single header field can be
-     * included in a SIP message, the latest non-NULL value is used, e.g.,
-     * @Subject. However, if the SIP header can consist of multiple lines or
-     * header fields separated by comma, e.g., @Accept, all the tagged
-     * values are concatenated.
-     *
-     * However, if a tag value is #SIP_NONE (-1 casted as a void pointer),
-     * the values from previous tags are ignored.
-     */
-    tl_gets(nh->nh_tags, NUTAG_URL_REF(url), TAG_END());
-    sip_add_tl(msg, sip_object(msg), TAG_NEXT(nh->nh_tags));
-  }
-
-  ta_start(ta, tag, value);
-
-  sip = sip_object(msg);
-  if (!sip)
-    goto error;
-  if (sip_add_tl(msg, sip, ta_tags(ta)) < 0)
-    goto error;
-
-  if (method != sip_method_ack) {
-    /**
-     * Next, values previously set with nua_set_params() or nua_set_hparams()
-     * are used: @Allow, @Supported, @Organization, and @UserAgent headers are
-     * added to the request if they are not already set. 
-     */
-    if (!sip->sip_allow && !ds->ds_remote_tag)
-      sip_add_dup(msg, sip, (sip_header_t*)NH_PGET(nh, allow));
-
-    if (!sip->sip_supported && NH_PGET(nh, supported))
-      sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, supported));
-    
-    if (method == sip_method_register && NH_PGET(nh, path_enable) &&
-	!sip_has_feature(sip->sip_supported, "path") &&
-	!sip_has_feature(sip->sip_require, "path"))
-      sip_add_make(msg, sip, sip_supported_class, "path");
-
-    if (!sip->sip_organization && NH_PGET(nh, organization))
-      sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, organization));
-
-    if (!sip->sip_user_agent && NH_PGET(nh, user_agent))
-      sip_add_make(msg, sip, sip_user_agent_class, NH_PGET(nh, user_agent));
-  }
-	  
-  {
-    int add_contact = 0, use_dialog = 0, add_service_route, has_contact = 0;
-    tagi_t const *t;
+/* ======================================================================== */
+/* Authentication */
 
-    for (t = ta_args(ta); t; t = t_next(t)) {
-      if (t->t_tag == siptag_contact ||
-	  t->t_tag == siptag_contact_str)
-	has_contact = 1;
-      else if (t->t_tag == nutag_url)
-	url = (url_string_t const *)t->t_value;
-      else if (t->t_tag == nutag_method && method == sip_method_unknown)
-	name = (char const *)t->t_value;
-      else if (t->t_tag == nutag_use_dialog)
-	use_dialog = t->t_value != 0;
-      else if (t->t_tag == _nutag_add_contact)
-	add_contact = t->t_value != 0;
-    }
-
-    if (!restart)
-      cr->cr_has_contact = has_contact;
-
-    if (has_contact) add_contact = 0;
-
-    if (method == sip_method_register && url == NULL)
-      url = (url_string_t const *)NH_PGET(nh, registrar);
-
-    if (seq != -1) {
-      sip_cseq_t *cseq;
-     
-      assert(method != sip_method_unknown || name || sip->sip_request);
-      
-      if (method || name)
-	cseq = sip_cseq_create(msg_home(msg), seq, method, name);
-      else 
-	cseq = sip_cseq_create(msg_home(msg), seq, 
-			       sip->sip_request->rq_method, 
-			       sip->sip_request->rq_method_name);
-
-      sip_header_insert(msg, sip, (sip_header_t *)cseq);
-    }
-
-    /**
-     * Now, the target URI for the request needs to be determined.
-     *
-     * For initial requests, values from tags are used. If NUTAG_URL() is
-     * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given,
-     * it is used as target URI. If neither is given, the complete request
-     * line already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR()
-     * is used. At this point, the target URI is stored in the request line,
-     * together with method name and protocol version ("SIP/2.0"). The
-     * initial dialog information is also created: @CallID, @CSeq headers
-     * are generated, if they do not exist, and a tag is added to the @From
-     * header.
-     */
-    if (!ds->ds_leg) {
-      nta_leg_t *leg = nua->nua_dhandle->nh_ds->ds_leg;
-
-      if ((ds->ds_remote_tag && ds->ds_remote_tag[0] && 
-	   sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0)
-	  || 
-	  (sip->sip_from == NULL &&
-	   sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0))
-	goto error;
+/** @NUA_EVENT nua_r_authenticate
+ *
+ * Response to nua_authenticate(). Under normal operation, this event is
+ * never sent but rather the unauthenticated operation is completed. 
+ * However, if there is no operation to authentication or if there is an
+ * authentication error the #nua_r_authenticate event is sent to the
+ * application with the status code as follows:
+ * - <i>202 No operation to restart</i>:\n
+ *   The authenticator associated with the handle was updated, but there was
+ *   no operation to retry with the new credentials.
+ * - <i>900 Cannot add credentials</i>:\n
+ *   There was internal problem updating authenticator.
+ * - <i>904 No matching challenge</i>:\n
+ *   There was no challenge matching with the credentials provided by
+ *   nua_authenticate(), e.g., their realm did not match with the one 
+ *   received with the challenge.
+ * 
+ * @param status status code from authentication 
+ * @param phrase a short textual description of @a status code
+ * @param nh     operation handle authenticated
+ * @param hmagic application context associated with the handle
+ * @param sip    NULL
+ * @param tags   empty
+ * 
+ * @sa nua_terminate(), nua_handle_destroy()
+ *
+ * @END_NUA_EVENT
+ */
 
-      if (use_dialog) {
-	ds->ds_leg = nta_leg_tcreate(nua->nua_nta,
-				     nua_stack_process_request, nh,
-				     SIPTAG_CALL_ID(sip->sip_call_id),
-				     SIPTAG_FROM(sip->sip_from),
-				     SIPTAG_TO(sip->sip_to),
-				     SIPTAG_CSEQ(sip->sip_cseq),
-				     TAG_END());
-	if (!ds->ds_leg)
-	  goto error;
-
-	leg = ds->ds_leg;
-
-	if (!sip->sip_from->a_tag &&
-	    sip_from_tag(msg_home(msg), sip->sip_from,
-			 nta_leg_tag(ds->ds_leg, NULL)) < 0)
-	  goto error;
-      }
+void
+nua_stack_authenticate(nua_t *nua, nua_handle_t *nh, nua_event_t e,
+		       tagi_t const *tags)
+{
+  int status = nh_authorize(nh, TAG_NEXT(tags));
 
-      if (nta_msg_request_complete(msg, leg, method, name, url) < 0)
-	goto error;
+  if (status > 0) {
+    nua_client_request_t *cr = nh->nh_ds->ds_cr;
 
-      add_service_route = !restart;
+    if (cr && cr->cr_challenged) {
+      nua_client_restart_request(cr, cr->cr_terminating, tags);
     }
     else {
-      /**
-       * For in-dialog requests, the request URI is taken from the @Contact
-       * header received from the remote party during dialog establishment, 
-       * and the NUTAG_URL() is ignored.
-       */
-      if (ds->ds_route)
-	url = NULL;
-
-      /**Also, the @CallID and @CSeq headers and @From and @To tags are
-       * generated based on the dialog information and added to the request. 
-       * If the dialog has a route, it is added to the request, too.
-       */
-      if (nta_msg_request_complete(msg, ds->ds_leg, method, name, url) < 0)
-	goto error;
-      add_service_route = 0;
-    }
-    /***
-     * @MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is
-     * also added now, if it does not exist.
-     */
-
-    /**
-     * Next, the stack generates a @Contact header for the request (unless
-     * the application already gave a @Contact header or it does not want to
-     * use @Contact and indicates that by including SIPTAG_CONTACT(NULL) or
-     * SIPTAG_CONTACT(SIP_NONE) in the tagged parameters.) If the
-     * application has registered the URI in @From header, the @Contact
-     * header used with registration is used. Otherwise, the @Contact header
-     * is generated from the local IP address and port number.
-     */
-    if (!add_contact ||
-	sip->sip_contact ||
-	nua_tagis_have_contact_tag(nh->nh_tags) ||
-	nua_tagis_have_contact_tag(ta_args(ta)))
-      add_contact = 0;
-
-    /**For the initial requests, @ServiceRoute set received from the registrar
-     * is also added to the request message.
-     */
-    if (add_contact || add_service_route) {
-      if (nua_registration_add_contact_to_request(nh, msg, sip, 
-						  add_contact, 
-						  add_service_route) < 0)
-	goto error;
-    }
-
-    if (method != sip_method_ack) {
-      if (nh->nh_auth) {
-	nh_authorize(nh, ta_tags(ta));
-
-	if (method != sip_method_invite &&
-	    method != sip_method_update &&
-	    method != sip_method_prack &&
-	    /* auc_authorize() removes existing authentication headers */
-	    auc_authorize(&nh->nh_auth, msg, sip) < 0)
-	  goto error;
-      }
-    }
-    else /* ACK */ {
-      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);
-    }
-
-    ta_end(ta);
-
-    if (!ds->ds_remote)
-      ds->ds_remote = sip_to_dup(nh->nh_home, sip->sip_to);
-    if (!ds->ds_local)
-      ds->ds_local = sip_from_dup(nh->nh_home, sip->sip_from);
-
-    if (copy) {
-      cr->cr_msg = msg;
-      msg = msg_copy(msg);
+      nua_stack_event(nua, nh, NULL, e, 
+		      202, "No operation to restart",
+		      NULL);
     }
   }
-
-  return msg;
-
- error:
-  ta_end(ta);
-  msg_destroy(msg);
-  return NULL;
+  else if (status < 0) {
+    nua_stack_event(nua, nh, NULL, e, 900, "Cannot add credentials", NULL);
+  }
+  else {
+    nua_stack_event(nua, nh, NULL, e, 904, "No matching challenge", NULL);
+  }
 }
 
-/* ---------------------------------------------------------------------- */
-nua_client_request_t *
-nua_client_request_pending(nua_client_request_t const *cr)
-{
-  for (;cr;cr = cr->cr_next)
-    if (cr->cr_orq)
-      return (nua_client_request_t *)cr;
 
-  return NULL;
-}
+/* ======================================================================== */
+/*
+ * Process incoming requests
+ */
 
-nua_client_request_t *
-nua_client_request_restarting(nua_client_request_t const *cr)
-{
-  for (;cr;cr = cr->cr_next)
-    if (cr->cr_restart)
-      return (nua_client_request_t *)cr;
+nua_server_methods_t const *nua_server_methods[] = {
+  /* These must be in same order as in sip_method_t */
+  &nua_extension_server_methods,
+  &nua_invite_server_methods,	/**< INVITE */
+  NULL,				/**< ACK */
+  NULL,				/**< CANCEL */
+  &nua_bye_server_methods,	/**< BYE */
+  &nua_options_server_methods,	/**< OPTIONS */
+  &nua_register_server_methods,	/**< REGISTER */
+  &nua_info_server_methods,	/**< INFO */
+  &nua_prack_server_methods,	/**< PRACK */
+  &nua_update_server_methods,	/**< UPDATE */
+  &nua_message_server_methods,	/**< MESSAGE */
+  &nua_subscribe_server_methods,/**< SUBSCRIBE */
+  &nua_notify_server_methods,	/**< NOTIFY */
+  &nua_refer_server_methods,	/**< REFER */
+  &nua_publish_server_methods,	/**< PUBLISH */
+  NULL
+};
 
-  return NULL;
-}
 
-nua_client_request_t *
-nua_client_request_by_orq(nua_client_request_t const *cr,
-			  nta_outgoing_t const *orq)
+int nua_stack_process_request(nua_handle_t *nh,
+			      nta_leg_t *leg,
+			      nta_incoming_t *irq,
+			      sip_t const *sip)
 {
-  for (;cr;cr = cr->cr_next)
-    if (cr->cr_orq == orq)
-      return (nua_client_request_t *)cr;
+  nua_t *nua = nh->nh_nua;
+  sip_method_t method = sip->sip_request->rq_method;
+  char const *name = sip->sip_request->rq_method_name;
+  nua_server_methods_t const *sm;
+  nua_server_request_t *sr, sr0[1];
+  int status, initial = 1;
 
-  return NULL;
-}
+  char const *user_agent = NH_PGET(nh, user_agent);
+  sip_supported_t const *supported = NH_PGET(nh, supported);
+  sip_allow_t const *allow = NH_PGET(nh, allow);
 
-void nua_creq_deinit(nua_client_request_t *cr, nta_outgoing_t *orq)
-{
-  if (orq == NULL || orq == cr->cr_orq) {
-    cr->cr_retry_count = 0;
-    cr->cr_offer_sent = cr->cr_answer_recv = 0;
+  enter;
 
-    if (cr->cr_msg)
-      msg_destroy(cr->cr_msg);
-    cr->cr_msg = NULL;
+  nta_incoming_tag(irq, NULL);
 
-    if (cr->cr_orq)
-      nta_outgoing_destroy(cr->cr_orq);
-    cr->cr_orq = NULL;
+  if (method == sip_method_cancel)
+    return 481;
+
+  /* Hook to outbound */
+  if (method == sip_method_options) {
+    status = nua_registration_process_request(nua->nua_registrations,
+					      irq, sip);
+    if (status)
+      return status;
   }
-  else {
-    nta_outgoing_destroy(orq);
+
+  if (nta_check_method(irq, sip, allow,
+		       SIPTAG_SUPPORTED(supported),
+		       SIPTAG_USER_AGENT_STR(user_agent),
+		       TAG_END()))
+    return 405;
+
+  switch (sip->sip_request->rq_url->url_type) {
+  case url_sip:
+  case url_sips:
+  case url_im:
+  case url_pres:
+  case url_tel:
+    break;
+  default:
+    nta_incoming_treply(irq, status = SIP_416_UNSUPPORTED_URI,
+			SIPTAG_ALLOW(allow),
+			SIPTAG_SUPPORTED(supported),
+			SIPTAG_USER_AGENT_STR(user_agent),
+			TAG_END());
+    return status;
   }
-}
 
+  if (nta_check_required(irq, sip, supported,
+			 SIPTAG_ALLOW(allow),
+			 SIPTAG_USER_AGENT_STR(user_agent),
+			 TAG_END()))
+    return 420;
 
-/**@internal
- * Get remote contact header for @a irq */
-static inline
-sip_contact_t const *incoming_contact(nta_incoming_t *irq)
-{
-  sip_contact_t const *retval = NULL;
-  msg_t *request;
-  sip_t *sip;
+  if (method > sip_method_unknown && method <= sip_method_publish)
+    sm = nua_server_methods[method];
+  else
+    sm = nua_server_methods[0];
 
-  request = nta_incoming_getrequest(irq);
-  sip = sip_object(request);
-  if (sip)
-    retval = sip->sip_contact;
-  msg_destroy(request);
+  initial = nh == nua->nua_dhandle;
 
-  return retval;
-}
+  if (sm == NULL) {
+    SU_DEBUG_1(("nua(%p): strange %s from <" URL_PRINT_FORMAT ">\n",
+		(void *)nh, sip->sip_request->rq_method_name,
+		URL_PRINT_ARGS(sip->sip_from->a_url)));
+  }
+  else if (initial && sm->sm_flags.in_dialog) {
+    /* These must be in-dialog */
+    sm = NULL;
+  }
+  else if (initial && sip->sip_to->a_tag) {
+    /* RFC 3261 section 12.2.2:
+       
+       If the UAS wishes to reject the request because it does not wish to
+       recreate the dialog, it MUST respond to the request with a 481
+       (Call/Transaction Does Not Exist) status code and pass that to the
+       server transaction.
+    */
+    if (method != sip_method_message || !NH_PGET(nh, win_messenger_enable))
+      sm = NULL;
+  }
 
-/**@internal
- * Create a response message.
- */
-msg_t *nh_make_response(nua_t *nua,
-			nua_handle_t *nh,
-			nta_incoming_t *irq,
-			int status, char const *phrase,
-			tag_type_t tag, tag_value_t value, ...)
-{
-  ta_list ta;
-  msg_t *msg = nta_msg_create(nua->nua_nta, 0);
-  sip_t *sip = sip_object(msg);
-  msg_t *retval = NULL;
-  tagi_t const *t;
+  if (!sm) {
+    nta_incoming_treply(irq,
+			status = 481, "Call Does Not Exist",
+			SIPTAG_ALLOW(allow),
+			SIPTAG_SUPPORTED(supported),
+			SIPTAG_USER_AGENT_STR(user_agent),
+			TAG_END());
+    return 481;
+  }
 
-  ta_start(ta, tag, value);
+  sr = memset(sr0, 0, (sizeof sr0));
 
-  if (!msg)
-    /* retval is NULL */;
-  else if (nta_msg_response_complete(msg, irq, status, phrase) < 0)
-    msg_destroy(msg);
-  else if (sip_add_tl(msg, sip, ta_tags(ta)) < 0)
-    msg_destroy(msg);
-  else if (sip_complete_message(msg) < 0)
-    msg_destroy(msg);
-  else if (!sip->sip_supported && NH_PGET(nh, supported) &&
-	   sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, supported)) < 0)
-    msg_destroy(msg);
-  else if (!sip->sip_user_agent && NH_PGET(nh, user_agent) &&
-	   sip_add_make(msg, sip, sip_user_agent_class, 
-			NH_PGET(nh, user_agent)) < 0)
-    msg_destroy(msg);
-  else if (!sip->sip_organization && NH_PGET(nh, organization) &&
-	   sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, organization)) < 0)
-    msg_destroy(msg);
-  else if (!sip->sip_allow && NH_PGET(nh, allow) &&
-	   sip_add_dup(msg, sip, (sip_header_t*)NH_PGET(nh, allow)) < 0)
-    msg_destroy(msg);
-  else if (!sip->sip_allow_events && 
-	   (sip->sip_cseq && 
-	    (sip->sip_cseq->cs_method == sip_method_publish ||
-	     sip->sip_cseq->cs_method == sip_method_subscribe)) &&
-	   NH_PGET(nh, allow_events) &&
-	   sip_add_dup(msg, sip, (sip_header_t*)NH_PGET(nh, allow_events)) < 0)
-    msg_destroy(msg);
-  else if (!sip->sip_contact &&
-	   (t = tl_find(ta_args(ta), _nutag_add_contact)) &&
-	   t->t_value && 
-	   nua_registration_add_contact_to_response(nh, msg, sip, NULL, 
-						    incoming_contact(irq)) < 0)
-    msg_destroy(msg);
-  else
-    retval = msg;
+  sr->sr_methods = sm;
+  sr->sr_method = method = sip->sip_request->rq_method;
+  sr->sr_add_contact = sm->sm_flags.add_contact;
 
-  ta_end(ta);
+  sr->sr_owner = nh;
+  sr->sr_initial = initial;
 
-  return retval;
-}
+  sr->sr_irq = irq;
 
+  SR_STATUS1(sr, SIP_100_TRYING);
 
-/* ======================================================================== */
-/* Generic processing */
+  sr->sr_request.msg = nta_incoming_getrequest(irq);
+  sr->sr_request.sip = sip;
+  assert(sr->sr_request.msg);
 
-int nua_stack_process_unknown(nua_t *nua,
-			      nua_handle_t *nh,
-			      nta_incoming_t *irq,
-			      sip_t const *sip)
-{
-  return 501;
-}
+  sr->sr_response.msg = nta_incoming_create_response(irq, 0, NULL);
+  sr->sr_response.sip = sip_object(sr->sr_response.msg);
 
-/**@internal
- * Relay response message to the application.
- *
- * If handle has already been marked as destroyed by nua_handle_destroy(),
- * release the handle with nh_destroy().
- */
-int nua_stack_process_response(nua_handle_t *nh,
-			       nua_client_request_t *cr,
-			       nta_outgoing_t *orq,
-			       sip_t const *sip,
-			       tag_type_t tag, tag_value_t value, ...)
-{
-  msg_t *msg = nta_outgoing_getresponse(orq);
-  int status = sip->sip_status->st_status;
-  char const *phrase = sip->sip_status->st_phrase;
-  ta_list ta;
-  int final;
+  if (sr->sr_response.msg == NULL) {
+    SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+  }
+  else if (sm->sm_init && sm->sm_init(sr)) {
+    if (sr->sr_status < 200)    /* Init may have set response status */
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+  }
+  /* Create handle if request does not fail */
+  else if (initial && sr->sr_status < 300) {
+    if ((nh = nua_stack_incoming_handle(nua, irq, sip, create_dialog)))
+      sr->sr_owner = nh;
+    else
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+  }
 
-  if (status >= 200 && status < 300)
-    nh_challenge(nh, sip);  /* Collect nextnonce */
+  if (sr->sr_status < 300 && sm->sm_preprocess && sm->sm_preprocess(sr)) {
+    if (sr->sr_status < 200)    /* Preprocess may have set response status */
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+  }
 
-  if (nta_outgoing_method(orq) == sip_method_invite)
-    final = status >= 300;
-  else
-    final = status >= 200;
+  if (sr->sr_status < 300) {
+    if (sm->sm_flags.target_refresh)
+      nua_dialog_uas_route(nh, nh->nh_ds, sip, 1); /* Set route and tags */
+    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
+  }
+
+  if (sr->sr_status == 100 && method != sip_method_unknown &&
+      !sip_is_allowed(NH_PGET(sr->sr_owner, appl_method), method, name)) {
+    if (method == sip_method_refer || method == sip_method_subscribe)
+      SR_STATUS1(sr, SIP_202_ACCEPTED);
+    else
+      SR_STATUS1(sr, SIP_200_OK);
+  }
 
-  if (final && cr) {
-    nua_creq_deinit(cr, orq);
+  /* INVITE server request is not finalized after 2XX response */
+  if (sr->sr_status < (method == sip_method_invite ? 300 : 200)) {
+    sr = su_alloc(nh->nh_home, (sizeof *sr));
 
-    if (cr->cr_usage && nh->nh_ds->ds_cr == cr) {
-      if ((status >= 300 && !cr->cr_usage->du_ready) ||
-	  cr->cr_usage->du_terminating)
-	nua_dialog_usage_remove(nh, nh->nh_ds, cr->cr_usage);
+    if (sr) {
+      *sr = *sr0;
+
+      if ((sr->sr_next = nh->nh_ds->ds_sr))
+	*(sr->sr_prev = sr->sr_next->sr_prev) = sr,
+	  sr->sr_next->sr_prev = &sr->sr_next;
+      else
+	*(sr->sr_prev = &nh->nh_ds->ds_sr) = sr;
     }
+    else {
+      sr = sr0;
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+    }
+  }
 
-    cr->cr_usage = NULL;
+  if (sr->sr_status <= 100) {
+    SR_STATUS1(sr, SIP_100_TRYING);
+    if (method == sip_method_invite || sip->sip_timestamp) {
+      nta_incoming_treply(irq, SIP_100_TRYING,
+			  SIPTAG_USER_AGENT_STR(user_agent),
+			  TAG_END());
+    }
+  }
+  else {
+    /* Note that this may change the sr->sr_status */
+    nua_server_respond(sr, NULL);
   }
 
-  ta_start(ta, tag, value);
+  if (nua_server_report(sr) == 0)
+    return 0;
 
-  nua_stack_event(nh->nh_nua, nh, msg, cr->cr_event, status, phrase,
-		  ta_tags(ta));
+  return 501;
+}		 
 
-  if (final)
-    cr->cr_event = nua_i_error;
+#undef nua_base_server_init
+#undef nua_base_server_preprocess
 
-  ta_end(ta);
+int nua_base_server_init(nua_server_request_t *sr)
+{
+  return 0;
+}
 
+int nua_base_server_preprocess(nua_server_request_t *sr)
+{
   return 0;
 }
 
-static inline
-int can_redirect(sip_contact_t const *m, sip_method_t method)
+void nua_server_request_destroy(nua_server_request_t *sr)
 {
-  if (m && m->m_url->url_host) {
-    enum url_type_e type = m->m_url->url_type;
-    return
-      type == url_sip ||
-      type == url_sips ||
-      (type == url_tel &&
-       (method == sip_method_invite || method == sip_method_message)) ||
-      (type == url_im && method == sip_method_message) ||
-      (type == url_pres && method == sip_method_subscribe);
+  if (sr == NULL)
+    return;
+
+  if (sr->sr_irq)
+    nta_incoming_destroy(sr->sr_irq), sr->sr_irq = NULL;
+
+  su_msg_destroy(sr->sr_signal);
+
+  if (sr->sr_request.msg)
+    msg_destroy(sr->sr_request.msg), sr->sr_request.msg = NULL;
+
+  if (sr->sr_response.msg)
+    msg_destroy(sr->sr_response.msg), sr->sr_response.msg = NULL;
+
+  if (sr->sr_prev) {
+    /* Allocated from heap */
+    if ((*sr->sr_prev = sr->sr_next))
+      sr->sr_next->sr_prev = sr->sr_prev;
+    su_free(sr->sr_owner->nh_home, sr);
   }
+}
+
+/** Respond to a request with given status. 
+ *
+ * When nua protocol engine receives an incoming SIP request, it can either
+ * respond to the request automatically or let it up to application to
+ * respond to the request. The automatic response is returned to the client
+ * if the request fails syntax check, or the method, SIP extension or
+ * content negotiation fails.
+ *
+ * When responding to an incoming INVITE request, the nua_respond() can be
+ * called without NUTAG_WITH() (or NUTAG_WITH_CURRENT() or
+ * NUTAG_WITH_SAVED()). Otherwise, NUTAG_WITH() will contain an indication
+ * of the request being responded.
+ *
+ * In order to simplify the simple applications, most requests are responded
+ * automatically. The BYE and CANCEL requests are always responded by the
+ * stack. Likewise, the NOTIFY requests associated with an event
+ * subscription are responded by the stack.
+ *
+ * The application can add methods that it likes to handle by itself with
+ * NUTAG_APPL_METHOD(). The default set of NUTAG_APPL_METHOD() includes
+ * INVITE, PUBLISH, REGISTER and SUBSCRIBE. Note that unless the method is
+ * also included in the set of allowed methods with NUTAG_ALLOW(), the stack
+ * will respond to the incoming methods with <i>405 Not Allowed</i>.
+ *
+ * Note that certain methods are rejected outside a SIP session (created
+ * with INVITE transaction). They include BYE, UPDATE, PRACK and INFO. Also
+ * the auxiliary methods ACK and CANCEL are rejected by stack if there is no
+ * ongoing INVITE transaction corresponding to them.
+ *
+ * @param nh              Pointer to operation handle
+ * @param status          SIP response status (see RFCs of SIP)
+ * @param phrase          free text (default response phrase used if NULL)
+ * @param tag, value, ... List of tagged parameters
+ *
+ * @return 
+ *    nothing
+ *
+ * @par Related Tags:
+ *    NUTAG_WITH(), NUTAG_WITH_CURRENT(), NUTAG_WITH_SAVED() \n
+ *    NUTAG_EARLY_ANSWER() \n
+ *    SOATAG_ADDRESS() \n
+ *    SOATAG_AF() \n
+ *    SOATAG_HOLD() \n
+ *    Tags in <sip_tag.h>.
+ *
+ * @par Events:
+ *    #nua_i_state \n
+ *    #nua_i_media_error \n
+ *    #nua_i_error \n
+ *    #nua_i_active \n
+ *    #nua_i_terminated \n
+ *
+ * @sa #nua_i_invite, #nua_i_register, #nua_i_subscribe, #nua_i_publish
+ */
+void
+nua_stack_respond(nua_t *nua, nua_handle_t *nh,
+		  int status, char const *phrase, tagi_t const *tags)
+{
+  nua_server_request_t *sr;
+  tagi_t const *t;
+  msg_t const *request = NULL;
+
+  t = tl_find_last(tags, nutag_with);
+
+  if (t)
+    request = (msg_t const *)t->t_value;
+
+  for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) {
+    if (request && sr->sr_request.msg == request)
+      break;
+    /* nua_respond() to INVITE can be used without NUTAG_WITH() */
+    if (!t && sr->sr_method == sip_method_invite)
+      break;
+  }
+
+  if (sr == NULL) {
+    nua_stack_event(nua, nh, NULL, nua_i_error,
+		    500, "Responding to a Non-Existing Request", NULL);
+    return;
+  }
+  else if (!nua_server_request_is_pending(sr)) {
+    nua_stack_event(nua, nh, NULL, nua_i_error,
+		    500, "Already Sent Final Response", NULL);
+    return;
+  }
+  else if (sr->sr_100rel && !sr->sr_pracked && 200 <= status && status < 300) {
+    /* Save signal until we have received PRACK */
+    if (tags && nua_stack_set_params(nua, nh, nua_i_none, tags) < 0) {
+      sr->sr_application = status;
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+    }
+    else {
+      su_msg_save(sr->sr_signal, nh->nh_nua->nua_signal);
+      return;
+    }
+  }
+  else {
+    sr->sr_application = status;
+    if (tags && nua_stack_set_params(nua, nh, nua_i_none, tags) < 0)
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+    else
+      sr->sr_status = status, sr->sr_phrase = phrase;
+  }
+
+  nua_server_params(sr, tags);
+  nua_server_respond(sr, tags);
+  nua_server_report(sr);
+}
+
+int nua_server_params(nua_server_request_t *sr, tagi_t const *tags)
+{
+  if (sr->sr_methods->sm_params)
+    return sr->sr_methods->sm_params(sr, tags);
   return 0;
 }
 
-int nua_creq_restart_with(nua_handle_t *nh,
-			  nua_client_request_t *cr,
-			  nta_outgoing_t *orq,
-			  int status, char const *phrase,
-			  nua_creq_restart_f *f,
-			  TAG_LIST)
+#undef nua_base_server_params
+
+int nua_base_server_params(nua_server_request_t *sr, tagi_t const *tags) 
 {
+  return 0;
+}
+
+/** Return the response to the client.
+ *
+ * @retval 0 when successfully sent
+ * @retval -1 upon an error
+ */
+int nua_server_trespond(nua_server_request_t *sr,
+			tag_type_t tag, tag_value_t value, ...)
+{
+  int retval;
   ta_list ta;
-  msg_t *msg = nta_outgoing_getresponse(orq);
+  ta_start(ta, tag, value);
+  retval = nua_server_respond(sr, ta_args(ta));
+  ta_end(ta);
+  return retval;
+}
 
-  nua_stack_event(nh->nh_nua, nh, msg, cr->cr_event, status, phrase,
-		  TAG_END());
+/** Return the response to the client.
+ *
+ * @retval 0 when successfully sent
+ * @retval -1 upon an error
+ */
+int nua_server_respond(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  sip_method_t method = sr->sr_method;
+  struct { msg_t *msg; sip_t *sip; } next = { NULL, NULL };
+  tagi_t next_tags[2] = {{ SIPTAG_END() }, { TAG_NEXT(tags) }};
+  int retval;
+
+  msg_t *msg = sr->sr_response.msg;
+  sip_t *sip = sr->sr_response.sip;
+  sip_contact_t *m = sr->sr_request.sip->sip_contact;
+
+  if (sr->sr_response.msg == NULL) {
+    assert(sr->sr_status == 500);
+    goto internal_error;
+  }
+
+  if (sr->sr_status < 200) {
+    next.msg = nta_incoming_create_response(sr->sr_irq, 0, NULL);
+    next.sip = sip_object(next.msg);
+    if (next.sip == NULL)
+      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+  }
 
-  nta_outgoing_destroy(orq);
+  if (nta_incoming_complete_response(sr->sr_irq, msg,
+				     sr->sr_status,
+				     sr->sr_phrase,
+				     TAG_NEXT(tags)) < 0)
+    ;
+  else if (!sip->sip_supported && NH_PGET(nh, supported) &&
+	   sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, supported)) < 0)
+    ;
+  else if (!sip->sip_user_agent && NH_PGET(nh, user_agent) &&
+	   sip_add_make(msg, sip, sip_user_agent_class, 
+			NH_PGET(nh, user_agent)) < 0)
+    ;
+  else if (!sip->sip_organization && NH_PGET(nh, organization) &&
+	   sip_add_dup(msg, sip, (void *)NH_PGET(nh, organization)) < 0)
+    ;
+  else if (!sip->sip_allow && NH_PGET(nh, allow) &&
+	   sip_add_dup(msg, sip, (void *)NH_PGET(nh, allow)) < 0)
+    ;
+  else if (!sip->sip_allow_events && 
+	   (method == sip_method_publish || method == sip_method_subscribe) &&
+	   NH_PGET(nh, allow_events) &&
+	   sip_add_dup(msg, sip, (void *)NH_PGET(nh, allow_events)) < 0)
+    ;
+  else if (!sip->sip_contact && sr->sr_status < 300 && sr->sr_add_contact &&
+	   nua_registration_add_contact_to_response(nh, msg, sip, NULL, m) < 0)
+    ;
+  else {
+    int term;
+    
+    term = sip_response_terminates_dialog(sr->sr_status, sr->sr_method, NULL);
+
+    sr->sr_terminating = (term < 0) ? -1 : (term > 0 || sr->sr_terminating);
+
+    retval = sr->sr_methods->sm_respond(sr, next_tags);
+
+    if (sr->sr_status < 200) 
+      sr->sr_response.msg = next.msg, sr->sr_response.sip = next.sip;
+    else if (next.msg)
+      msg_destroy(next.msg);
 
-  if (f) {
-    ta_start(ta, tag, value);
-    f(nh, ta_args(ta));
-    ta_end(ta);
+    assert(sr->sr_status >= 200 || sr->sr_response.msg);
+
+    return retval;
   }
 
-  return 1;
-}
+  if (next.msg)
+    msg_destroy(next.msg);
+
+  SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
+
+  msg_destroy(msg);
 
+ internal_error:
+  sr->sr_response.msg = NULL, sr->sr_response.sip = NULL;
+  nta_incoming_treply(sr->sr_irq, sr->sr_status, sr->sr_phrase, TAG_END());
+
+  return 0;
+}
 
-/** @internal Save operation until it can be restarted */
-int nua_creq_save_restart(nua_handle_t *nh,
-			  nua_client_request_t *cr,
-			  nta_outgoing_t *orq,
-			  int status, char const *phrase,
-			  nua_creq_restart_f *restart_function)
+/** Return the response to the client.
+ *
+ * @retval 0 when successfully sent
+ * @retval -1 upon an error
+ */
+int nua_base_server_respond(nua_server_request_t *sr, tagi_t const *tags)
 {
-  nua_dialog_usage_t *du = cr->cr_usage;
-  msg_t *msg = nta_outgoing_getresponse(orq);
+  msg_t *response = sr->sr_response.msg;
 
-  nua_stack_event(nh->nh_nua, nh, msg, cr->cr_event,
-		  status, phrase, 
-		  TAG_END());
-  nta_outgoing_destroy(orq);
+  sr->sr_response.msg = NULL, sr->sr_response.sip = NULL;
 
-  if (du)
-    du->du_refresh = 0;
+  return nta_incoming_mreply(sr->sr_irq, response);
+}
 
-  cr->cr_restart = restart_function;
-  return 1;
+int nua_server_report(nua_server_request_t *sr)
+{
+  if (sr)
+    return sr->sr_methods->sm_report(sr, NULL);
+  else
+    return 1;
 }
 
+int nua_base_server_treport(nua_server_request_t *sr,
+			    tag_type_t tag, tag_value_t value,
+			    ...)
+{
+  int retval;
+  ta_list ta;
+  ta_start(ta, tag, value);
+  retval = nua_base_server_report(sr, ta_args(ta));
+  ta_end(ta);
+  return retval;
+}
 
-/**@internal
- * Check response, return true if we can restart the request.
+/**Report request event to the application.
  *
+ * @retval 0 request lives
+ * @retval 1 request was destroyed
+ * @retval 2 request and its usage was destroyed
+ * @retval 3 request, all usages and dialog was destroyed
+ * @retval 4 request, all usages, dialog, and handle was destroyed
  */
-int nua_creq_check_restart(nua_handle_t *nh,
-			   nua_client_request_t *cr,
-			   nta_outgoing_t *orq,
-			   sip_t const *sip,
-			   nua_creq_restart_f *restart_function)
+int nua_base_server_report(nua_server_request_t *sr, tagi_t const *tags)
 {
-  int status = sip->sip_status->st_status;
-  sip_method_t method = nta_outgoing_method(orq);
+  nua_handle_t *nh = sr->sr_owner;
+  nua_t *nua = nh->nh_nua;
+  nua_dialog_usage_t *usage = sr->sr_usage;
+  int initial = sr->sr_initial;
+  int status = sr->sr_status;
+  char const *phrase = sr->sr_phrase;
+
+  int terminated;
+  int handle_can_be_terminated = initial && !sr->sr_event;
+
+  assert(nh);
+
+  if (sr->sr_application) {
+    /* There was an error sending response */
+    if (sr->sr_application != sr->sr_status)
+      nua_stack_event(nua, nh, NULL, nua_i_error, status, phrase, tags);
+    sr->sr_application = 0;
+  }
+  else if (status < 300 && !sr->sr_event) {
+    msg_t *msg = msg_ref_create(sr->sr_request.msg);
+    nua_event_t e = sr->sr_methods->sm_event;
+    sr->sr_event = 1;
+    nua_stack_event(nua, nh, msg, e, status, phrase, tags);
+  }
+
+  if (status < 200)
+    return 0;			/* sr lives on until final response is sent */
+
+  if (sr->sr_method == sip_method_invite && status < 300)
+    return 0;			/* INVITE lives on until ACK is received */
+
+  if (initial && 300 <= status)
+    terminated = 1;
+  else if (sr->sr_terminating && status < 300)
+    terminated = 1;
+  else
+    terminated = sip_response_terminates_dialog(status, sr->sr_method, NULL);
 
-  nua_dialog_usage_t *du = cr->cr_usage;
+  nua_server_request_destroy(sr);
 
-  assert(restart_function);
+  if (!terminated)
+    return 1;
 
-  if (orq != cr->cr_orq)
-    return 0;
+  if (usage)
+    nua_dialog_usage_remove(nh, nh->nh_ds, usage);
 
-  cr->cr_orq = NULL;
-  cr->cr_restart = NULL;
+  if (!initial) {
+    if (terminated > 0)
+      return 2;
 
-  if (cr->cr_msg == NULL || status < 200)
-    ;
-  else if (++cr->cr_retry_count > NH_PGET(nh, retry_count))
-    ;
-  else if (status == 302) {
-    if (can_redirect(sip->sip_contact, method)) {
-      return
-	nua_creq_restart_with(nh, cr, orq, 100, "Redirected",
-			      restart_function,
-			      NUTAG_URL(sip->sip_contact->m_url),
-			      TAG_END());
+    /* Remove all usages of the dialog */
+    nua_dialog_deinit(nh, nh->nh_ds);
+
+    return 3;
+  }
+  else if (!handle_can_be_terminated) {
+    return 3;
+  }
+  else {
+    if (nh != nh->nh_nua->nua_dhandle)
+      nh_destroy(nh->nh_nua, nh);
+
+    return 4;
+  }
+}
+
+/* ---------------------------------------------------------------------- */
+
+/** @class nua_client_request
+ *
+ * Each handle has a queue of client-side requests; if a request is pending,
+ * a new request from API is added to the queue. After the request is
+ * complete, it is removed from the queue and destroyed by the default. The
+ * exception is the client requests bound to a dialog usage: they are saved
+ * and re-used when the dialog usage is refreshed (and sometimes when the
+ * usage is terminated).
+ * 
+ * The client request is subclassed and its behaviour modified using virtual
+ * function table in #nua_client_methods_t.
+ * 
+ * The first three methods (crm_template(), crm_init(), crm_send()) are
+ * called when the request is sent first time.
+ * 
+ * The crm_template() is called if a template request message is needed (for
+ * example, in case of unregister, unsubscribe and unpublish, the template
+ * message is taken from the request establishing the usage).
+ * 
+ * The crm_init() is called when the template message and dialog leg has
+ * been created and populated by the tags procided by the application. Its
+ * parameters msg and sip are pointer to the template request message that
+ * is saved in the nua_client_request::cr_msg field.
+ *
+ * The crm_send() is called with a copy of the template message that has
+ * been populated with all the fields included in the request, including
+ * @CSeq and @MaxForwards. The crm_send() function, such as
+ * nua_publish_client_request(), usually calls nua_base_client_trequest() that
+ * then creates the nta-level transaction.
+ *
+ * The response to the request is processed by crm_check_restart(), which
+ * modifies and restarts the request when needed (e.g., when negotiating
+ * expiration time). After the request has been suitably modified, e.g., the
+ * expiration time has been increased, the restart function calls
+ * nua_client_restart(), which restarts the request and relays the
+ * intermediate response to the application with nua_client_restart() and
+ * crm_report().
+ *
+ * The final responses are processed by crm_recv() and and preliminary ones
+ * by crm_preliminary(). Both functions call nua_base_client_response() after
+ * method-specific processing.
+ *
+ * The nua_base_client_response() relays the response to the application with
+ * nua_client_restart() and crm_report().
+ *
+ * @par Terminating Dialog Usages and Dialogs
+ *
+ * The response can be marked as terminating with nua_client_terminating(). 
+ * When a terminating request completes the dialog usage is removed and the
+ * dialog is destroyed (unless there is an another active usage).
+ */
+static int nua_client_request_try(nua_client_request_t *cr);
+static int nua_client_request_sendmsg(nua_client_request_t *cr,
+  				      msg_t *msg, sip_t *sip);
+
+/**Create a client request.
+ *
+ * @retval 0 if request is pending
+ * @retval > 0 if error event has been sent
+ * @retval < 0 upon an error
+ */
+int nua_client_create(nua_handle_t *nh, 
+		      int event,
+		      nua_client_methods_t const *methods,
+		      tagi_t const * const tags)
+{
+  su_home_t *home = nh->nh_home;
+  nua_client_request_t *cr;
+  sip_method_t method;
+  char const *name;
+
+  method = methods->crm_method, name = methods->crm_method_name;
+  if (!name) {
+    tagi_t const *t = tl_find_last(tags, nutag_method);
+    if (t)
+      name = (char const *)t->t_value;
+  }
+
+  cr = su_zalloc(home, sizeof *cr + methods->crm_extra);
+  if (!cr) {
+    return nua_stack_event(nh->nh_nua, nh, 
+			   NULL,
+			   event,
+			   NUA_INTERNAL_ERROR,
+			   NULL);
+  }
+
+  cr->cr_owner = nh;
+  cr->cr_methods = methods;
+  cr->cr_event = event;
+  cr->cr_method = method;
+  cr->cr_method_name = name;
+  cr->cr_contactize = methods->crm_flags.target_refresh;
+  cr->cr_auto = 1;
+
+  if (su_msg_is_non_null(nh->nh_nua->nua_signal)) {
+    nua_event_data_t const *e = su_msg_data(nh->nh_nua->nua_signal);
+
+    if (tags == e->e_tags && event == e->e_event) {
+      cr->cr_auto = 0;
+      if (tags) {
+	su_msg_save(cr->cr_signal, nh->nh_nua->nua_signal);
+	cr->cr_tags = tags;
+      }
     }
   }
-  else if (status == 423) {
-    sip_t *req = sip_object(cr->cr_msg);
-    unsigned my_expires = 0;
 
-    if (req->sip_expires)
-      my_expires = req->sip_expires->ex_delta;
+  if (tags && cr->cr_tags == NULL)
+    cr->cr_tags = tl_tlist(nh->nh_home, TAG_NEXT(tags));
 
-    if (sip->sip_min_expires &&
-	sip->sip_min_expires->me_delta > my_expires) {
-      sip_expires_t ex[1];
-      sip_expires_init(ex);
-      ex->ex_delta = sip->sip_min_expires->me_delta;
+  if (nua_client_request_queue(cr))
+    return 0;
 
-      return
-	nua_creq_restart_with(nh, cr, orq,
-			      100, "Re-Negotiating Expiration",
-			      restart_function, 
-			      SIPTAG_EXPIRES(ex),
-			      TAG_END());
-    }
-  }
-  else if (method != sip_method_ack && method != sip_method_cancel &&
-	   ((status == 401 && sip->sip_www_authenticate) ||
-	    (status == 407 && sip->sip_proxy_authenticate)) &&
-	   nh_challenge(nh, sip) > 0) {
-    msg_t *request = nta_outgoing_getrequest(orq);
-    sip_t *rsip = sip_object(request);
-    int done;
-
-    /* XXX - check for instant restart */
-    done = auc_authorization(&nh->nh_auth, cr->cr_msg, (msg_pub_t*)NULL,
-			     rsip->sip_request->rq_method_name,
-			     rsip->sip_request->rq_url,
-			     rsip->sip_payload);
-
-    msg_destroy(request);
-
-    if (done > 0) {
-      return
-	nua_creq_restart_with(nh, cr, orq,
-			      100, "Request Authorized by Cache",
-			      restart_function, TAG_END());
-    }
-    else if (done == 0) {
-      /* Operation waits for application to call nua_authenticate() */
-      return nua_creq_save_restart(nh, cr, orq, 
-				   status, sip->sip_status->st_phrase, 
-				   restart_function);
+  return nua_client_init_request(cr);
+}
+
+int nua_client_tcreate(nua_handle_t *nh, 
+		       int event,
+		       nua_client_methods_t const *methods,
+		       tag_type_t tag, tag_value_t value, ...)
+{
+  int retval;
+  ta_list ta;
+  ta_start(ta, tag, value);
+  retval = nua_client_create(nh, event, methods, ta_args(ta));
+  ta_end(ta);
+  return retval;
+}
+
+int nua_client_request_queue(nua_client_request_t *cr)
+{
+  int queued = 0;
+  nua_client_request_t **queue = &cr->cr_owner->nh_ds->ds_cr;
+
+  assert(cr->cr_prev == NULL && cr->cr_next == NULL);
+
+  cr->cr_status = 0;
+
+  if (cr->cr_method != sip_method_invite &&
+      cr->cr_method != sip_method_cancel) {
+    while (*queue) {
+      if ((*queue)->cr_method == sip_method_invite ||
+	  (*queue)->cr_method == sip_method_cancel)
+	break;
+      queue = &(*queue)->cr_next;
+      queued = 1;
     }
-    else {
-      SU_DEBUG_5(("nua(%p): auc_authorization failed\n", nh));
+  }
+  else {
+    while (*queue) {
+      queue = &(*queue)->cr_next;
+      if (cr->cr_method == sip_method_invite)
+	queued = 1;
     }
   }
 
-  /* This was final response that cannot be restarted. */
-  cr->cr_orq = orq;
+  if ((cr->cr_next = *queue))
+    cr->cr_next->cr_prev = &cr->cr_next;
 
-  if (du)
-    du->du_refresh = 0;
-  cr->cr_retry_count = 0;
+  cr->cr_prev = queue, *queue = cr;
 
-  if (cr->cr_msg)
-    msg_destroy(cr->cr_msg), cr->cr_msg = NULL;
+  return queued;
+}
 
-  return 0;
+nua_client_request_t *nua_client_request_remove(nua_client_request_t *cr)
+{
+  if (cr->cr_prev)
+    if ((*cr->cr_prev = cr->cr_next))
+      cr->cr_next->cr_prev = cr->cr_prev;
+  cr->cr_prev = NULL, cr->cr_next = NULL;
+  return cr;
 }
 
-/** @internal Restart a request */
-int nua_creq_restart(nua_handle_t *nh,
-		     nua_client_request_t *cr,
-		     nta_response_f *cb,
-		     tagi_t *tags)
+void nua_client_request_destroy(nua_client_request_t *cr)
 {
-  msg_t *msg;
+  nua_handle_t *nh;
+  
+  if (cr == NULL)
+    return;
 
-  cr->cr_restart = NULL;
+  if (cr->cr_methods->crm_deinit)
+    cr->cr_methods->crm_deinit(cr);
 
-  if (!cr->cr_msg)
-    return 0;
+  nh = cr->cr_owner;
+
+  su_msg_destroy(cr->cr_signal);
+
+  nua_client_request_remove(cr);
+  nua_client_bind(cr, NULL);
+  
+  if (cr->cr_msg)
+    msg_destroy(cr->cr_msg);
+  cr->cr_msg = NULL, cr->cr_sip = NULL;
+
+  if (cr->cr_orq)
+    nta_outgoing_destroy(cr->cr_orq);
+
+  cr->cr_orq = NULL;
 
-  msg = nua_creq_msg(nh->nh_nua, nh, cr, 1, SIP_METHOD_UNKNOWN,
-		     TAG_NEXT(tags));
+  if (cr->cr_target)
+    su_free(nh->nh_home, cr->cr_target);
 
-  cr->cr_orq = nta_outgoing_mcreate(nh->nh_nua->nua_nta, cb, nh, NULL, msg,
-				    SIPTAG_END(), TAG_NEXT(tags));
+  su_free(nh->nh_home, cr);
+}
 
-  if (!cr->cr_orq) {
-    msg_destroy(msg);
+/** Bind client request to a dialog usage */ 
+int nua_client_bind(nua_client_request_t *cr, nua_dialog_usage_t *du)
+{
+  assert(cr);
+  if (cr == NULL)
+    return -1;
+
+  if (du == NULL) {
+    if (cr->cr_usage && cr->cr_usage->du_cr == cr)
+      cr->cr_usage->du_cr = NULL;
+    cr->cr_usage = NULL;
     return 0;
   }
 
-  return 1;
-}
+  if (du->du_cr && cr != du->du_cr) {
+    assert(!nua_client_is_queued(du->du_cr));
+    if (nua_client_is_queued(du->du_cr))
+      return -1;
+    if (nua_client_is_reporting(du->du_cr)) {
+      du->du_cr->cr_usage = NULL;
+      du->du_cr = NULL;
+    }
+    else
+      nua_client_request_destroy(du->du_cr);
+  }
 
-/* ======================================================================== */
-/* Authentication */
+  du->du_cr = cr, cr->cr_usage = du;
 
-/** @NUA_EVENT nua_r_authenticate
+  return 0;
+}
+
+/**Initialize client request for sending.
  *
- * Response to nua_authenticate(). Under normal operation, this event is
- * never sent but rather the unauthenticated operation is completed. 
- * However, if there is no operation to authentication or if there is an
- * authentication error the #nua_r_authenticate event is sent to the
- * application with the status code as follows:
- * - <i>202 No operation to restart</i>:\n
- *   The authenticator associated with the handle was updated, but there was
- *   no operation to retry with the new credentials.
- * - <i>900 Cannot add credentials</i>:\n
- *   There was internal problem updating authenticator.
- * - <i>904 No matching challenge</i>:\n
- *   There was no challenge matching with the credentials provided by
- *   nua_authenticate(), e.g., their realm did not match with the one 
- *   received with the challenge.
- * 
- * @param status status code from authentication 
- * @param phrase a short textual description of @a status code
- * @param nh     operation handle authenticated
- * @param hmagic application context associated with the handle
- * @param sip    NULL
- * @param tags   empty
- * 
- * @sa nua_terminate(), nua_handle_destroy()
+ * This function is called only first time the request is sent.
  *
- * @END_NUA_EVENT
+ * @retval 0 if request is pending
+ * @retval >=1 if error event has been sent
  */
-
-void
-nua_stack_authenticate(nua_t *nua, nua_handle_t *nh, nua_event_t e,
-		       tagi_t const *tags)
+int nua_client_init_request(nua_client_request_t *cr)
 {
-  int status = nh_authorize(nh, TAG_NEXT(tags));
+  nua_handle_t *nh = cr->cr_owner;
+  nua_t *nua = nh->nh_nua;
+  nua_dialog_state_t *ds = nh->nh_ds;
+  msg_t *msg = NULL;
+  sip_t *sip;
+  url_string_t const *url = NULL;
+  tagi_t const *t;
+  int has_contact = 0;
+  int error = 0;
+  
+  if (!cr->cr_method_name)
+    return nua_client_return(cr, NUA_INTERNAL_ERROR, NULL);
 
-  if (status > 0) {
-    nua_client_request_t *cr;
-    nua_creq_restart_f *restart = NULL;
+  if (cr->cr_msg)
+    return nua_client_request_try(cr);
 
-    cr = nua_client_request_restarting(nh->nh_ds->ds_cr);
+  cr->cr_answer_recv = 0, cr->cr_offer_sent = 0;
+  cr->cr_offer_recv = 0, cr->cr_answer_sent = 0;
+  cr->cr_terminated = 0, cr->cr_graceful = 0;
 
-    if (cr) 
-      restart = cr->cr_restart, cr->cr_restart = NULL;
+  nua_stack_init_handle(nua, nh, TAG_NEXT(cr->cr_tags));
 
-    if (restart) {
-      /* nua_stack_event(nua, nh, NULL, e, SIP_200_OK, TAG_END()); */
-      restart(nh, (tagi_t *)tags);	/* Restart operation */
+  if (cr->cr_method == sip_method_cancel) {
+    if (cr->cr_methods->crm_init) {
+      error = cr->cr_methods->crm_init(cr, NULL, NULL, cr->cr_tags);
+      if (error)
+	return error;
     }
-    else {
-      nua_stack_event(nua, nh, NULL, e, 
-		      202, "No operation to restart",
-		      TAG_END());
+
+    if (cr->cr_methods->crm_send)
+      return cr->cr_methods->crm_send(cr, NULL, NULL, cr->cr_tags);
+    else
+      return nua_base_client_request(cr, NULL, NULL, cr->cr_tags);
+  }
+
+  if (!cr->cr_methods->crm_template ||
+      !cr->cr_methods->crm_template(cr, &msg, cr->cr_tags))
+    msg = nta_msg_create(nua->nua_nta, 0);
+
+  sip = sip_object(msg);
+  if (!sip)
+    return nua_client_return(cr, NUA_INTERNAL_ERROR, msg);
+
+  /**@par Populating SIP Request Message with Tagged Arguments
+   *
+   * The tagged arguments can be used to pass values for any SIP headers
+   * to the stack. When the INVITE message (or any other SIP message) is
+   * created, the tagged values saved with nua_handle() are used first,
+   * next the tagged values given with the operation (nua_invite()) are
+   * added.
+   *
+   * When multiple tags for the same header are specified, the behaviour
+   * depends on the header type. If only a single header field can be
+   * included in a SIP message, the latest non-NULL value is used, e.g.,
+   * @Subject. However, if the SIP header can consist of multiple lines or
+   * header fields separated by comma, e.g., @Accept, all the tagged
+   * values are concatenated.
+   *
+   * However, if a tag value is #SIP_NONE (-1 casted as a void pointer),
+   * the values from previous tags are ignored.
+   */
+  if (nh->nh_tags) {
+    for (t = nh->nh_tags; t; t = t_next(t)) {
+      if (t->t_tag == siptag_contact ||
+	  t->t_tag == siptag_contact_str)
+	has_contact = 1;
+      else if (t->t_tag == nutag_url)
+	url = (url_string_t const *)t->t_value;
     }
+
+    t = nh->nh_tags, sip_add_tagis(msg, sip, &t);
   }
-  else if (status < 0) {
-    nua_stack_event(nua, nh, NULL, e, 900, "Cannot add credentials", TAG_END());
+
+  for (t = cr->cr_tags; t; t = t_next(t)) {
+    if (t->t_tag == siptag_contact ||
+	t->t_tag == siptag_contact_str)
+      has_contact = 1;
+    else if (t->t_tag == nutag_url)
+      url = (url_string_t const *)t->t_value;
+    else if (t->t_tag == nutag_auth && t->t_value) {
+      /* XXX ignoring errors */
+      if (nh->nh_auth)
+	auc_credentials(&nh->nh_auth, nh->nh_home, (char *)t->t_value);
+    }
+  }
+
+  if (cr->cr_method == sip_method_register && url == NULL)
+    url = (url_string_t const *)NH_PGET(nh, registrar);
+  
+  if ((t = cr->cr_tags)) {
+    if (sip_add_tagis(msg, sip, &t) < 0)
+      goto error;
+  }
+
+  /**
+   * Now, the target URI for the request needs to be determined.
+   *
+   * For initial requests, values from tags are used. If NUTAG_URL() is
+   * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given,
+   * it is used as target URI. If neither is given, the complete request
+   * line already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR()
+   * is used. At this point, the target URI is stored in the request line,
+   * together with method name and protocol version ("SIP/2.0"). The
+   * initial dialog information is also created: @CallID, @CSeq headers
+   * are generated, if they do not exist, and a tag is added to the @From
+   * header.
+   */
+
+  if (!ds->ds_leg) {
+    if (ds->ds_remote_tag && ds->ds_remote_tag[0] && 
+	sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0)
+      goto error;
+
+    if (sip->sip_from == NULL && 
+	sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0)
+      goto error;
+
+    if (cr->cr_methods->crm_flags.create_dialog) {
+      ds->ds_leg = nta_leg_tcreate(nua->nua_nta,
+				   nua_stack_process_request, nh,
+				   SIPTAG_CALL_ID(sip->sip_call_id),
+				   SIPTAG_FROM(sip->sip_from),
+				   SIPTAG_TO(sip->sip_to),
+				   SIPTAG_CSEQ(sip->sip_cseq),
+				   TAG_END());
+      if (!ds->ds_leg)
+	goto error;
+
+      if (!sip->sip_from->a_tag &&
+	  sip_from_tag(msg_home(msg), sip->sip_from,
+		       nta_leg_tag(ds->ds_leg, NULL)) < 0)
+	goto error;
+    }
   }
   else {
-    nua_stack_event(nua, nh, NULL, e, 904, "No matching challenge", TAG_END());
+    if (ds->ds_route)
+      url = NULL;
+  }
+
+  if (url && nua_client_set_target(cr, (url_t *)url) < 0)
+    goto error;
+
+  cr->cr_has_contact = has_contact;
+
+  if (cr->cr_methods->crm_init) {
+    error = cr->cr_methods->crm_init(cr, msg, sip, cr->cr_tags);
+    if (error < -1)
+      msg = NULL;
+    if (error < 0)
+      goto error;
+    if (error != 0)
+      return error;
   }
+
+  cr->cr_msg = msg;
+  cr->cr_sip = sip;
+
+  return nua_client_request_try(cr);
+
+ error:
+  return nua_client_return(cr, NUA_INTERNAL_ERROR, msg);
 }
 
-/* ======================================================================== */
-/*
- * Process incoming requests
+
+/** Restart the request message.
+ *
+ * @retval 0 if request is pending
+ * @retval >=1 if error event has been sent
  */
+int nua_client_restart_request(nua_client_request_t *cr,
+			      int terminating,
+			      tagi_t const *tags)
+{
+  if (cr) {
+    assert(nua_client_is_queued(cr));
+
+    if (tags && cr->cr_msg)
+      if (sip_add_tagis(cr->cr_msg, NULL, &tags) < 0)
+	/* XXX */;
 
-int nua_stack_process_request(nua_handle_t *nh,
-			      nta_leg_t *leg,
-			      nta_incoming_t *irq,
-			      sip_t const *sip)
+    cr->cr_retry_count = 0;
+    cr->cr_terminating = terminating;
+
+    return nua_client_request_try(cr);
+  }
+  return 0;
+}
+
+/** Resend the request message.
+ *
+ * @retval 0 if request is pending
+ * @retval >=1 if error event has been sent
+ */
+int nua_client_resend_request(nua_client_request_t *cr,
+			      int terminating)
 {
-  nua_t *nua = nh->nh_nua;
-  sip_method_t method = sip->sip_request->rq_method;
-  char const *user_agent = NH_PGET(nh, user_agent);
-  sip_supported_t const *supported = NH_PGET(nh, supported);
-  sip_allow_t const *allow = NH_PGET(nh, allow);
-  enter;
+  if (cr) {
+    cr->cr_retry_count = 0;
 
-  nta_incoming_tag(irq, NULL);
+    if (nua_client_is_queued(cr)) {
+      if (terminating)
+	cr->cr_graceful = 1;
+      return 0;
+    }
 
-  if (nta_check_method(irq, sip, allow,
-		       SIPTAG_SUPPORTED(supported),
-		       SIPTAG_USER_AGENT_STR(user_agent),
-		       TAG_END()))
-    return 405;
+    if (terminating)
+      cr->cr_terminating = terminating;
+
+    if (nua_client_request_queue(cr))
+      return 0;
+    if (nua_dialog_is_reporting(cr->cr_owner->nh_ds))
+      return 0;
+    return nua_client_request_try(cr);
+  }
+  return 0;
+}
 
-  switch (sip->sip_request->rq_url->url_type) {
-  case url_sip:
-  case url_sips:
-  case url_im:
-  case url_pres:
-  case url_tel:
-    break;
-  default:
-    nta_incoming_treply(irq, SIP_416_UNSUPPORTED_URI,
-			SIPTAG_ALLOW(allow),
-			SIPTAG_SUPPORTED(supported),
-			SIPTAG_USER_AGENT_STR(user_agent),
-			TAG_END());
+
+/** Create a request message and send it.
+ *
+ * If an error occurs, send error event to the application.
+ *
+ * @retval 0 if request is pending
+ * @retval >=1 if error event has been sent
+ */
+static
+int nua_client_request_try(nua_client_request_t *cr)
+{
+  int error = -1;
+  msg_t *msg = msg_copy(cr->cr_msg);
+  sip_t *sip = sip_object(msg);
+
+  cr->cr_answer_recv = 0, cr->cr_offer_sent = 0;
+  cr->cr_offer_recv = 0, cr->cr_answer_sent = 0;
+
+  if (msg && sip) {
+    error = nua_client_request_sendmsg(cr, msg, sip);
+    if (!error)
+      return 0;
+
+    if (error == -1)
+      msg_destroy(msg);
+  }
+
+  if (error < 0)
+    error = nua_client_response(cr, NUA_INTERNAL_ERROR, NULL);
+
+  assert(error > 0);
+  return error;
+}
+
+/**Send a request message.
+ *
+ * @retval 0 if request is pending
+ * @retval >=1 if error event has been sent
+ * @retval -1 if error occurred but event has not been sent,
+               and @a msg has not been destroyed
+ * @retval -2 if error occurred, event has not been sent,
+ *            but @a msg has been destroyed
+ */
+static
+int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip)
+{
+  nua_handle_t *nh = cr->cr_owner;
+  nua_dialog_state_t *ds = nh->nh_ds;
+  sip_method_t method = cr->cr_method;
+  char const *name = cr->cr_method_name;
+  url_string_t const *url = (url_string_t *)cr->cr_target;
+  nta_leg_t *leg;
+
+  assert(cr->cr_orq == NULL);
+
+  if (ds->ds_leg)
+    leg = ds->ds_leg;
+  else
+    leg = nh->nh_nua->nua_dhandle->nh_ds->ds_leg; /* Default leg */
+
+  if (nua_dialog_is_established(ds)) {
+    while (sip->sip_route)
+      sip_route_remove(msg, sip);
   }
+  
+  /**
+   * For in-dialog requests, the request URI is taken from the @Contact
+   * header received from the remote party during dialog establishment, 
+   * and the NUTAG_URL() is ignored.
+   *
+   * Also, the @CallID and @CSeq headers and @From and @To tags are
+   * generated based on the dialog information and added to the request. 
+   * If the dialog has a route, it is added to the request, too.
+   */
+  if (nta_msg_request_complete(msg, leg, method, name, url) < 0)
+    return -1;
+
+  /**@MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is
+   * also added now, if it does not exist.
+   */
+
+  if (!ds->ds_remote)
+    ds->ds_remote = sip_to_dup(nh->nh_home, sip->sip_to);
+  if (!ds->ds_local)
+    ds->ds_local = sip_from_dup(nh->nh_home, sip->sip_from);
+
+  /**
+   * Next, values previously set with nua_set_params() or nua_set_hparams()
+   * are used: @Allow, @Supported, @Organization, and @UserAgent headers are
+   * added to the request if they are not already set. 
+   */
+  if (!sip->sip_allow && !ds->ds_route)
+    sip_add_dup(msg, sip, (sip_header_t*)NH_PGET(nh, allow));
+  
+  if (!sip->sip_supported && NH_PGET(nh, supported))
+    sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, supported));
+  
+  if (method == sip_method_register && NH_PGET(nh, path_enable) &&
+      !sip_has_feature(sip->sip_supported, "path") &&
+      !sip_has_feature(sip->sip_require, "path"))
+    sip_add_make(msg, sip, sip_supported_class, "path");
+  
+  if (!sip->sip_organization && NH_PGET(nh, organization))
+    sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, organization));
+
+  if (!sip->sip_user_agent && NH_PGET(nh, user_agent))
+    sip_add_make(msg, sip, sip_user_agent_class, NH_PGET(nh, user_agent));
+
+  /**
+   * Next, the stack generates a @Contact header for the request (unless
+   * the application already gave a @Contact header or it does not want to
+   * use @Contact and indicates that by including SIPTAG_CONTACT(NULL) or
+   * SIPTAG_CONTACT(SIP_NONE) in the tagged parameters.) If the
+   * application has registered the URI in @From header, the @Contact
+   * header used with registration is used. Otherwise, the @Contact header
+   * is generated from the local IP address and port number.
+   */
+
+  /**For the initial requests, @ServiceRoute set that was received from the
+   * registrar is also added to the request message.
+   */
+  if (cr->cr_method != sip_method_register) {
+    if (nua_registration_add_contact_to_request(nh, msg, sip,
+						cr->cr_contactize &&
+						!cr->cr_has_contact,
+						!ds->ds_route) < 0)
+      return -1;
+  }
+
+  cr->cr_challenged = 0;
+
+  if (cr->cr_methods->crm_send)
+    return cr->cr_methods->crm_send(cr, msg, sip, NULL);
+
+  return nua_base_client_request(cr, msg, sip, NULL);
+}
+
+/**Add tags to request message and send it, 
+ *
+ * @retval 0 success
+ * @retval -1 if error occurred, but event has not been sent
+ * @retval -2 if error occurred, event has not been sent,
+ *            and @a msg has been destroyed
+ * @retval >=1 if error event has been sent
+ */
+int nua_base_client_trequest(nua_client_request_t *cr,
+			     msg_t *msg, sip_t *sip,
+			     tag_type_t tag, tag_value_t value, ...)
+{
+  int retval;
+  ta_list ta;
+  ta_start(ta, tag, value);
+  retval = nua_base_client_request(cr, msg, sip, ta_args(ta));
+  ta_end(ta);
+  return retval;
+}
+
+/** Send request.
+ *
+ * @retval 0 success
+ * @retval -1 if error occurred, but event has not been sent
+ * @retval -2 if error occurred, event has not been sent,
+ *            and @a msg has been destroyed
+ * @retval >=1 if error event has been sent
+ */
+int nua_base_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip,
+			    tagi_t const *tags)
+{
+  nua_handle_t *nh = cr->cr_owner;
 
-  if (nta_check_required(irq, sip, supported,
-			 SIPTAG_ALLOW(allow),
-			 SIPTAG_USER_AGENT_STR(user_agent),
-			 TAG_END()))
-    return 420;
+  if (nh->nh_auth && auc_authorize(&nh->nh_auth, msg, sip) < 0)
+    return nua_client_return(cr, 900, "Cannot add credentials", msg);
 
-  if (nh == nua->nua_dhandle) {
-    if (!sip->sip_to->a_tag)
-      ;
-    else if (method == sip_method_message && NH_PGET(nh, win_messenger_enable))
-      ;
-    else {
-      nta_incoming_treply(irq, 481, "Initial transaction with a To tag",
-			  TAG_END());
-      return 481;
-    }
-    nh = NULL;
-  }
+  cr->cr_seq = sip->sip_cseq->cs_seq; /* Save last sequence number */
 
-  if (sip->sip_timestamp)
-    nta_incoming_treply(irq, SIP_100_TRYING, TAG_END());
+  cr->cr_orq = nta_outgoing_mcreate(nh->nh_nua->nua_nta,
+				    nua_client_orq_response, cr,
+				    NULL, 
+				    msg,
+				    TAG_NEXT(tags));
 
-  switch (method) {
-  case sip_method_invite:
-    return nua_stack_process_invite(nua, nh, irq, sip);
-
-  case sip_method_info:
-    if (nh) return nua_stack_process_info(nua, nh, irq, sip);
-    /*FALLTHROUGH*/
-
-  case sip_method_update:
-    if (nh) return nua_stack_process_update(nua, nh, irq, sip);
-    /*FALLTHROUGH*/
+  return cr->cr_orq ? 0 : -1;
+}
 
-  case sip_method_bye:
-    if (nh) return nua_stack_process_bye(nua, nh, irq, sip);
+/** Callback for nta client transaction */
+int nua_client_orq_response(nua_client_request_t *cr,
+			    nta_outgoing_t *orq,
+			    sip_t const *sip)
+{
+  int status;
+  char const *phrase;
 
-    nta_incoming_treply(irq,
-			481, "Call Does Not Exist",
-			SIPTAG_ALLOW(allow),
-			SIPTAG_SUPPORTED(supported),
-			SIPTAG_USER_AGENT_STR(user_agent),
-			TAG_END());
-    return 481;
+  if (sip && sip->sip_status) {
+    status = sip->sip_status->st_status;
+    phrase = sip->sip_status->st_phrase;
+  }
+  else {
+    status = nta_outgoing_status(orq);
+    phrase = "";
+  }
 
-  case sip_method_message:
-    return nua_stack_process_message(nua, nh, irq, sip);
+  nua_client_response(cr, status, phrase, sip);
 
-  case sip_method_notify:
-    return nua_stack_process_notify(nua, nh, irq, sip);
+  return 0;
+}
 
-  case sip_method_subscribe:
-    return nua_stack_process_subscribe(nua, nh, irq, sip);
+/**Return response to the client request.
+ *
+ * Return a response generated by the stack. This function is used to return
+ * a error response within @a nua_client_methods_t#crm_init or @a
+ * nua_client_methods_t#crm_send functions. It takes care of disposing the @a
+ * to_be_destroyed that cannot be sent.
+ *
+ * @retval 0 if response event was preliminary
+ * @retval 1 if response event was final
+ * @retval 2 if response event destroyed the handle, too.
+ */
+int nua_client_return(nua_client_request_t *cr,
+		      int status,
+		      char const *phrase,
+		      msg_t *to_be_destroyed)
+{
+  if (to_be_destroyed)
+    msg_destroy(to_be_destroyed);
+  nua_client_response(cr, status, phrase, NULL);
+  return 1;
+}
 
-  case sip_method_register:
-    return nua_stack_process_register(nua, nh, irq, sip);
+/** Process response to the client request.
+ *
+ * The response can be generated by the stack (@a sip is NULL) or
+ * returned by the remote server.
+ *
+ * @retval 0 if response event was preliminary
+ * @retval 1 if response event was final
+ * @retval 2 if response event destroyed the handle, too.
+ */
+int nua_client_response(nua_client_request_t *cr,
+			int status,
+			char const *phrase,
+			sip_t const *sip)
+{
+  nua_handle_t *nh = cr->cr_owner;
+  nua_dialog_usage_t *du = cr->cr_usage;
 
-  case sip_method_options:
-    return nua_stack_process_options(nua, nh, irq, sip);
+  if (cr->cr_restarting)
+    return 0;
 
-  case sip_method_refer:
-    return nua_stack_process_refer(nua, nh, irq, sip);
+  cr->cr_status = status;
 
-  case sip_method_publish:
-    return nua_stack_process_publish(nua, nh, irq, sip);
+  if (status < 200) {
+    /* Xyzzy */
+  }
+  else if (sip && nua_client_check_restart(cr, status, phrase, sip)) {
+    return 0;
+  }
+  else if (status < 300) {
+    if (cr->cr_terminating) {
+      cr->cr_terminated = 1;
+    }
+    else {
+      if (sip) {
+	if (cr->cr_methods->crm_flags.target_refresh)
+	  nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
+	nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
+      }
 
-  case sip_method_ack:
-  case sip_method_cancel:
-    SU_DEBUG_1(("nua(%p): strange %s from <" URL_PRINT_FORMAT ">\n", nh,
-		sip->sip_request->rq_method_name,
-		URL_PRINT_ARGS(sip->sip_from->a_url)));
-    /* Send nua_i_error ? */
-    return 481;
+      if (du && du->du_cr == cr)
+	du->du_ready = 1;
+    }
+  }
+  else {
+    sip_method_t method = cr->cr_method;
+    int terminated, graceful = 1;
 
-  case sip_method_unknown:
-    return nua_stack_process_method(nua, nh, irq, sip);
+    if (status < 700)
+      terminated = sip_response_terminates_dialog(status, method, &graceful);
+    else
+      /* XXX - terminate usage by all internal error responses */
+      terminated = 0, graceful = 1;
 
-  default:
-    return nua_stack_process_unknown(nua, nh, irq, sip);
+    if (terminated < 0)
+      cr->cr_terminated = terminated;      
+    else if (cr->cr_terminating || terminated)
+      cr->cr_terminated = 1;
+    else if (graceful)
+      cr->cr_graceful = 1;
   }
+  
+  if (status < 200) {
+    if (cr->cr_methods->crm_preliminary)
+      cr->cr_methods->crm_preliminary(cr, status, phrase, sip);
+    else
+      nua_base_client_response(cr, status, phrase, sip, NULL);
+    return 0;
+  }  
+
+  if (cr->cr_methods->crm_recv)
+    return cr->cr_methods->crm_recv(cr, status, phrase, sip);
+  else
+    return nua_base_client_response(cr, status, phrase, sip, NULL);
 }
 
-nua_server_request_t *nua_server_request(nua_t *nua,
-					 nua_handle_t *nh,
-					 nta_incoming_t *irq,
-					 sip_t const *sip,
-					 nua_server_request_t *sr,
-					 size_t size,
-					 nua_server_respond_f *respond,
-					 int create_dialog)
+/** Check if request should be restarted.
+ *
+ * @retval 1 if restarted or waring for restart
+ * @retval 0 otherwise
+ */
+int nua_client_check_restart(nua_client_request_t *cr,
+			     int status,
+			     char const *phrase,
+			     sip_t const *sip)
 {
-  int initial = 1, final = 200;
-
-  assert(nua && irq && sip && sr);
-
-  initial = nh == NULL || nh == nua->nua_dhandle;
+  nua_handle_t *nh = cr->cr_owner;
 
-  /* INVITE server request is not finalized after 2XX response */
-  if (sip->sip_request->rq_method == sip_method_invite)
-    final = 300;
+  assert(cr && status >= 200 && phrase && sip);
 
-  /* Create handle if request does not fail */
-  if (sr->sr_status >= 300)
-    ;
-  else if (initial) {
-    if (!(nh = nua_stack_incoming_handle(nua, irq, sip, create_dialog)))
-      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
-  }
-  else if (create_dialog) {
-    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
-    nua_dialog_uas_route(nh, nh->nh_ds, sip, 1);
-  }
+  if (cr->cr_retry_count >= NH_PGET(nh, retry_count))
+    return 0;
 
-  if (nh == NULL)
-    nh = nua->nua_dhandle;
+  if (cr->cr_methods->crm_check_restart)
+    return cr->cr_methods->crm_check_restart(cr, status, phrase, sip);
+  else
+    return nua_base_client_check_restart(cr, status, phrase, sip);
+}
 
-  if (sr->sr_status < final) {
-    nua_server_request_t *sr0 = sr;
+int nua_base_client_check_restart(nua_client_request_t *cr,
+				  int status,
+				  char const *phrase,
+				  sip_t const *sip)
+{
+  nua_handle_t *nh = cr->cr_owner; 
 
-    if (size < (sizeof *sr))
-      size = sizeof *sr;
+  /* XXX - handle Retry-After */
 
-    sr = su_zalloc(nh->nh_home, size);
+  if (status == 302) {
+    if (!can_redirect(sip->sip_contact, cr->cr_method))
+      return 0;
 
-    if (sr) {
-      if ((sr->sr_next = nh->nh_ds->ds_sr))
-	*(sr->sr_prev = sr->sr_next->sr_prev) = sr,
-	  sr->sr_next->sr_prev = &sr->sr_next;
-      else
-	*(sr->sr_prev = &nh->nh_ds->ds_sr) = sr;
-      SR_STATUS(sr, sr0->sr_status, sr0->sr_phrase);
-    }
-    else {
-      sr = sr0;
-      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
-    }
+    if (nua_client_set_target(cr, sip->sip_contact->m_url) >= 0)
+      return nua_client_restart(cr, 100, "Redirected");
   }
 
-  sr->sr_owner = nh;
-  sr->sr_method = sip->sip_request->rq_method;
-  sr->sr_respond = respond;
-  sr->sr_irq = irq;
-  sr->sr_initial = initial;
+  if (status == 423) {
+    unsigned my_expires = 0;
+    
+    if (cr->cr_sip->sip_expires)
+      my_expires = cr->cr_sip->sip_expires->ex_delta;
 
-  return sr;
-}		 
+    if (sip->sip_min_expires &&
+	sip->sip_min_expires->me_delta > my_expires) {
+      sip_expires_t ex[1];
+      sip_expires_init(ex);
+      ex->ex_delta = sip->sip_min_expires->me_delta;
 
-void nua_server_request_destroy(nua_server_request_t *sr)
-{
-  if (sr->sr_irq)
-    nta_incoming_destroy(sr->sr_irq), sr->sr_irq = NULL;
+      if (sip_add_dup(cr->cr_msg, NULL, (sip_header_t *)ex) < 0)
+	return 0;
 
-  sr->sr_msg = NULL;
+      return nua_client_restart(cr, 100, "Re-Negotiating Expiration");
+    }
+  }
 
-  if (sr->sr_prev) {
-    if ((*sr->sr_prev = sr->sr_next))
-      sr->sr_next->sr_prev = sr->sr_prev;
+  if (((status == 401 && sip->sip_www_authenticate) ||
+       (status == 407 && sip->sip_proxy_authenticate)) &&
+      nh_challenge(nh, sip) > 0) {
+    nta_outgoing_t *orq;
+    if (auc_has_authorization(&nh->nh_auth)) 
+      return nua_client_restart(cr, 100, "Request Authorized by Cache");
 
-    if (sr->sr_owner)
-      su_free(sr->sr_owner->nh_home, sr);
+    orq = cr->cr_orq, cr->cr_orq = NULL;
+    cr->cr_challenged = 1;
+    cr->cr_retry_count++;
+    nua_client_report(cr, status, phrase, NULL, orq, NULL);
+    nta_outgoing_destroy(orq);
+
+    return 1;
   }
+
+  return 0;  /* This was a final response that cannot be restarted. */
 }
 
-/** Send server event (nua_i_*) to the application. */
-int nua_stack_server_event(nua_t *nua,
-			   nua_server_request_t *sr,
-			   nua_event_t event,
-			   tag_type_t tag, tag_value_t value, ...)
+/** Restart request.
+ *
+ * @retval 1 if restarted
+ * @retval 0 otherwise
+ */
+int nua_client_restart(nua_client_request_t *cr,
+		       int status, char const *phrase)
 {
-  nua_handle_t *nh = sr->sr_owner; 
-  int status, final = 0;
+  nua_handle_t *nh = cr->cr_owner;
+  nta_outgoing_t *orq;
+  int error = -1, terminated, graceful;
+  msg_t *msg;
+  sip_t *sip;
 
-  if (nh == NULL) nh = nua->nua_dhandle;
+  if (++cr->cr_retry_count > NH_PGET(nh, retry_count))
+    return 0;
 
-  if (sr->sr_status > 100)
-    /* Note that this may change the sr->sr_status */
-    final = sr->sr_respond(sr, NULL);
+  orq = cr->cr_orq, cr->cr_orq = NULL;  assert(orq);
+  terminated = cr->cr_terminated, cr->cr_terminated = 0;
+  graceful = cr->cr_graceful, cr->cr_graceful = 0;
 
-  status = sr->sr_status;
+  msg = msg_copy(cr->cr_msg);
+  sip = sip_object(msg);
 
-  if (status >= 200)
-    sr->sr_respond = NULL;
-  
-  if (status < 300 || !sr->sr_initial) {
-    ta_list ta;
-    msg_t *request;
-
-    ta_start(ta, tag, value);
-
-    assert(sr->sr_owner);
-    request = nta_incoming_getrequest(sr->sr_irq);
-    nua_stack_event(nua, sr->sr_owner, request, event, 
-		    sr->sr_status, sr->sr_phrase, 
-		    ta_tags(ta));
-    ta_end(ta);
-
-    if (final)
-      nua_server_request_destroy(sr);
-    else if (sr->sr_status < 200)
-      sr->sr_msg = request;
+  if (msg && sip) {
+    cr->cr_restarting = 1;
+    error = nua_client_request_sendmsg(cr, msg, sip);
+    cr->cr_restarting = 0;
+    if (error !=0 && error != -2)
+      msg_destroy(msg);
   }
-  else {
-    nh = sr->sr_owner;
 
-    nua_server_request_destroy(sr);
 
-    if (nh && nh != nua->nua_dhandle)
-      nh_destroy(nua, nh);
+  if (error) {
+    cr->cr_graceful = graceful;
+    cr->cr_terminated = terminated;
+    assert(cr->cr_orq == NULL);
+    cr->cr_orq = orq;
+    return 0;
   }
 
+  nua_client_report(cr, status, phrase, NULL, orq, NULL);
+
+  nta_outgoing_destroy(orq);
+
+  return 1;
+}
+
+int nua_client_set_target(nua_client_request_t *cr, url_t const *target)
+{
+  url_t *new_target, *old_target = cr->cr_target;
+
+  if (!target || target == old_target)
+    return 0;
+
+  new_target = url_hdup(cr->cr_owner->nh_home, (url_t *)target);
+  if (!new_target)
+    return -1;
+  cr->cr_target = new_target;
+  if (old_target)
+    su_free(cr->cr_owner->nh_home, old_target);
+
   return 0;
 }
 
-/** Respond to a request. */
-int nua_server_respond(nua_server_request_t *sr,
-		       int status, char const *phrase,
-		       tag_type_t tag, tag_value_t value, ...)
+/**@internal
+ * Relay response event to the application.
+ *
+ * @todo 
+ * If handle has already been marked as destroyed by nua_handle_destroy(),
+ * release the handle with nh_destroy().
+ *
+ * @retval 0 if event was preliminary
+ * @retval 1 if event was final
+ * @retval 2 if event destroyed the handle, too.
+ */
+int nua_base_client_tresponse(nua_client_request_t *cr,
+			      int status, char const *phrase,
+			      sip_t const *sip,
+			      tag_type_t tag, tag_value_t value, ...)
 {
   ta_list ta;
-  int final;
+  int retval;
 
-  assert(sr && sr->sr_respond);
-  SR_STATUS(sr, status, phrase);
+  if (cr->cr_event == nua_r_destroy)
+    return nua_base_client_response(cr, status, phrase, sip, NULL);
 
   ta_start(ta, tag, value);
-  final = sr->sr_respond(sr, ta_args(ta));
+  retval = nua_base_client_response(cr, status, phrase, sip, ta_args(ta));
   ta_end(ta);
 
-  if (final) {
-    nua_server_request_destroy(sr);    
-    return final;
+  return retval;
+}
+
+/**@internal
+ * Relay response event to the application.
+ *
+ * @todo 
+ * If handle has already been marked as destroyed by nua_handle_destroy(),
+ * release the handle with nh_destroy().
+ *
+ * @retval 0 if event was preliminary
+ * @retval 1 if event was final
+ * @retval 2 if event destroyed the handle, too.
+ */
+int nua_base_client_response(nua_client_request_t *cr,
+			     int status, char const *phrase,
+			     sip_t const *sip,
+			     tagi_t const *tags)
+{
+  nua_handle_t *nh = cr->cr_owner;
+  sip_method_t method = cr->cr_method;
+  nua_dialog_usage_t *du;
+
+  cr->cr_reporting = 1, nh->nh_ds->ds_reporting = 1;
+
+  if (status >= 200 && status < 300)
+    nh_challenge(nh, sip);  /* Collect nextnonce */
+
+  if ((method != sip_method_invite && status >= 200) || status >= 300)
+    nua_client_request_remove(cr);
+
+  nua_client_report(cr, status, phrase, sip, cr->cr_orq, tags);
+
+  if (status < 200 ||
+      /* Un-ACKed 2XX response to INVITE */
+      (method == sip_method_invite && status < 300 && cr->cr_orq)) {
+    cr->cr_reporting = 0, nh->nh_ds->ds_reporting = 0;
+    return 1;
   }
 
-  if (sr->sr_status >= 200)
-    sr->sr_respond = NULL;
+  if (cr->cr_orq)
+    nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL;
 
-  return 0;
-}
+  du = cr->cr_usage;
 
-msg_t *nua_server_response(nua_server_request_t *sr,
-			   int status, char const *phrase,
-			   tag_type_t tag, tag_value_t value, ...)
-{
-  msg_t *msg;
-  ta_list(ta);
+  if (cr->cr_terminated < 0) {
+    /* XXX - dialog has been terminated */;
+    nua_dialog_deinit(nh, nh->nh_ds);
+  }
+  else if (du) {
+    if (cr->cr_terminated ||
+	(!du->du_ready && status >= 300 && nua_client_is_bound(cr))) {
+      /* Usage has been destroyed */
+      nua_dialog_usage_remove(nh, nh->nh_ds, du);
+    }
+    else if (cr->cr_graceful) {
+      /* Terminate usage gracefully */
+      nua_dialog_usage_shutdown(nh, nh->nh_ds, du); 
+    }
+  }
+  else if (cr->cr_terminated) {
+    if (nh->nh_ds->ds_usage == NULL)
+      nua_dialog_remove(nh, nh->nh_ds, NULL);
+  }
 
-  assert(sr && sr->sr_owner && sr->sr_owner->nh_nua);
+  cr->cr_reporting = 0, nh->nh_ds->ds_reporting = 0;
 
-  ta_start(ta, tag, value);
+  if (!nua_client_is_queued(cr) && !nua_client_is_bound(cr))
+    nua_client_request_destroy(cr);
 
-  msg = nh_make_response(sr->sr_owner->nh_nua, sr->sr_owner, sr->sr_irq,
-			 sr->sr_status, sr->sr_phrase,
-			 ta_tags(ta));
-  
-  ta_end(ta);
+  if (method == sip_method_cancel)
+    return 1;
 
-  return msg;
+  return
+    nua_client_next_request(nh->nh_ds->ds_cr, method == sip_method_invite);
 }
 
-int nua_default_respond(nua_server_request_t *sr,
-			tagi_t const *tags)
+/** Send event, zap transaction but leave cr in list */
+int nua_client_report(nua_client_request_t *cr,
+		      int status, char const *phrase,
+		      sip_t const *sip,
+		      nta_outgoing_t *orq,
+		      tagi_t const *tags)
 {
-  msg_t *m;
+  nua_handle_t *nh;
 
-  assert(sr && sr->sr_owner && sr->sr_owner->nh_nua);
+  if (cr->cr_event == nua_r_destroy)
+    return 1;
 
-  m = nh_make_response(sr->sr_owner->nh_nua, sr->sr_owner, 
-		       sr->sr_irq,
-		       sr->sr_status, sr->sr_phrase,
-		       TAG_NEXT(tags));
+  if (cr->cr_methods->crm_report)
+    return cr->cr_methods->crm_report(cr, status, phrase, sip, orq, tags);
 
-  if (m) {
-    if (nta_incoming_mreply(sr->sr_irq, m) < 0)
-      SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);      
-  }
+  nh = cr->cr_owner;
   
-  return sr->sr_status >= 200 ? sr->sr_status : 0;
+  nua_stack_event(nh->nh_nua, nh, 
+		  nta_outgoing_getresponse(orq),
+		  cr->cr_event,
+		  status, phrase,
+		  tags);
+  return 1;
 }
 
-/** Respond to an request with given status. 
- *
- * When nua protocol engine receives an incoming SIP request, it can either
- * respond to the request automatically or let it up to application to
- * respond to the request. The automatic answer is sent if the request fails
- * because of method, SIP extension or, in some times, MIME content
- * negotiation fails.
- *
- * When responding to an incoming INVITE request, the nua_respond() can be
- * called without NUTAG_WITH() (or NUTAG_WITH_CURRENT() or
- * NUTAG_WITH_SAVED()). Otherwise, NUTAG_WITH() will contain an indication
- * of the request being responded.
- *
- * In order to simplify the simple applications, most requests are responded
- * automatically. The set of requests always responded by the stack include
- * BYE, CANCEL and NOTIFY. The application can add methods that it likes to
- * handle by itself with NUTAG_APPL_METHOD(). The default set of
- * NUTAG_APPL_METHOD() includes INVITE, PUBLISH, REGISTER and SUBSCRIBE. 
- * Note that unless the method is also included in the set of allowed
- * methods with NUTAG_ALLOW(), the stack will respond to the incoming
- * methods with <i>405 Not Allowed</i>.
- *
- * Note that certain methods are rejected outside a SIP session (created
- * with INVITE transaction). They include BYE, UPDATE, PRACK and INFO. Also
- * the auxiliary methods ACK and CANCEL are rejected by stack if there is no
- * ongoing INVITE transaction corresponding to them.
- *
- * @param nh              Pointer to operation handle
- * @param status          SIP response status (see RFCs of SIP)
- * @param phrase          free text (default response phrase used if NULL)
- * @param tag, value, ... List of tagged parameters
- *
- * @return 
- *    nothing
- *
- * @par Related Tags:
- *    NUTAG_WITH(), NUTAG_WITH_CURRENT(), NUTAG_WITH_SAVED() \n
- *    NUTAG_EARLY_ANSWER() \n
- *    SOATAG_ADDRESS() \n
- *    SOATAG_AF() \n
- *    SOATAG_HOLD() \n
- *    Tags in <sip_tag.h>.
- *
- * @par Events:
- *    #nua_i_state \n
- *    #nua_i_media_error \n
- *    #nua_i_error \n
- *    #nua_i_active \n
- *    #nua_i_terminated \n
- *
- * @sa #nua_i_invite, #nua_i_register, #nua_i_subscribe, #nua_i_publish
- */
-void
-nua_stack_respond(nua_t *nua, nua_handle_t *nh,
-		  int status, char const *phrase, tagi_t const *tags)
+int nua_client_treport(nua_client_request_t *cr,
+		       int status, char const *phrase,
+		       sip_t const *sip,
+		       nta_outgoing_t *orq,
+		       tag_type_t tag, tag_value_t value, ...)
 {
-  nua_server_request_t *sr;
-  tagi_t const *t;
-  msg_t const *request = NULL;
-
-  t = tl_find_last(tags, nutag_with);
-
-  if (t)
-    request = (msg_t const *)t->t_value;
+  int retval;
+  ta_list ta;
+  ta_start(ta, tag, value);
+  retval = nua_client_report(cr, status, phrase, sip, orq, ta_args(ta));
+  ta_end(ta);
+  return retval;
+}
 
-  for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) {
-    if (request && sr->sr_msg == request)
-      break;
-    /* nua_respond() to INVITE can be used without NUTAG_WITH() */
-    if (!t && sr->sr_method == sip_method_invite && sr->sr_respond)
+int nua_client_next_request(nua_client_request_t *cr, int invite)
+{
+  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 (sr && sr->sr_respond) {
-    int final;
-    SR_STATUS(sr, status, phrase);
-    final = sr->sr_respond(sr, tags);
-    if (final)
-      nua_server_request_destroy(sr);    
-    else if (sr->sr_status >= 200)
-      sr->sr_respond = NULL;
-    return;
-  }
-  else if (sr) {
-    nua_stack_event(nua, nh, NULL, nua_i_error,
-		    500, "Already Sent Final Response", TAG_END());
-    return;
-  }
 
-  nua_stack_event(nua, nh, NULL, nua_i_error,
-		  500, "Responding to a Non-Existing Request", TAG_END());
+  if (cr && cr->cr_orq == NULL) 
+    nua_client_init_request(cr);
+
+  return 1;
+}
+
+nua_client_request_t *
+nua_client_request_pending(nua_client_request_t const *cr)
+{
+  for (;cr;cr = cr->cr_next)
+    if (cr->cr_orq)
+      return (nua_client_request_t *)cr;
+
+  return NULL;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h	Tue Mar 20 23:37:15 2007
@@ -103,6 +103,32 @@
   TAG_IF((include) && (soa) && soa_is_remote_chat_active(soa) >= 0,	\
 	 SOATAG_ACTIVE_CHAT(soa_is_remote_chat_active(soa)))
 
+#if HAVE_NUA_HANDLE_DEBUG
+
+#define nua_handle_ref(nh) nua_handle_ref_by((nh), __func__)
+#define nua_handle_unref(nh) nua_handle_unref_by((nh), __func__)
+
+static inline nua_handle_t *nua_handle_ref_by(nua_handle_t *nh,
+					      char const *by)
+{
+  if (nh)
+    SU_DEBUG_0(("nua_handle_ref(%p) => "MOD_ZU" by %s\n", nh, 
+		su_home_refcount((su_home_t *)nh) + 1,
+		by));
+  return (nua_handle_t *)su_home_ref((su_home_t *)nh);
+}
+
+static inline int nua_handle_unref_by(nua_handle_t *nh, char const *by)
+{
+  if (nh)
+    SU_DEBUG_0(("nua_handle_unref(%p) => "MOD_ZU" by %s\n", nh, 
+		su_home_refcount((su_home_t *)nh) - 1,
+		by));
+  return su_home_unref((su_home_t *)nh);
+}
+
+#endif
+
 /** NUA handle. 
  *
  */
@@ -113,7 +139,8 @@
   nua_handle_t  **nh_prev;
 
   nua_t        	 *nh_nua;	/**< Pointer to NUA object  */
-  void           *nh_valid;
+  void           *nh_valid;	/**< Cookie */
+#define nua_valid_handle_cookie ((void *)(intptr_t)nua_handle)
   nua_hmagic_t 	 *nh_magic;	/**< Application context */
 
   tagi_t         *nh_tags;	/**< Initial tags */
@@ -154,7 +181,8 @@
   nea_server_t   *nh_notifier;	/**< SIP notifier */
 };
 
-#define NH_IS_VALID(nh) ((nh) && (nh)->nh_valid)
+#define NH_IS_VALID(nh) \
+  ((nh) && (nh)->nh_valid == nua_valid_handle_cookie)
 
 #define NH_STATUS(nh) \
   (nh)->nh_status, \
@@ -285,20 +313,23 @@
   nua_stack_method;
 
 #define UA_EVENT1(e, statusphrase) \
-  nua_stack_event(nua, nh, NULL, e, statusphrase, TAG_END())
+  nua_stack_event(nua, nh, NULL, e, statusphrase, NULL)
 
 #define UA_EVENT2(e, status, phrase)			\
-  nua_stack_event(nua, nh, NULL, e, status, phrase, TAG_END())
+  nua_stack_event(nua, nh, NULL, e, status, phrase, NULL)
 
 #define UA_EVENT3(e, status, phrase, tag)			\
-  nua_stack_event(nua, nh, NULL, e, status, phrase, tag, TAG_END())
+  nua_stack_event(nua, nh, NULL, e, status, phrase, tag, NULL)
+
+int nua_stack_tevent(nua_t *nua, nua_handle_t *nh, msg_t *msg,
+		     nua_event_t event, int status, char const *phrase,
+		     tag_type_t tag, tag_value_t value, ...);
 
 int nua_stack_event(nua_t *nua, nua_handle_t *nh, msg_t *msg,
 		    nua_event_t event, int status, char const *phrase,
-		    tag_type_t tag, tag_value_t value, ...);
+		    tagi_t const *tags);
 
-nua_handle_t *nh_create_handle(nua_t *nua, nua_hmagic_t *hmagic,
-			       tagi_t *tags);
+nua_handle_t *nh_create_handle(nua_t *nua, nua_hmagic_t *hmagic, tagi_t *tags);
 
 nua_handle_t *nua_stack_incoming_handle(nua_t *nua,
 					nta_incoming_t *irq,
@@ -349,48 +380,8 @@
 			      nta_incoming_t *irq,
 			      sip_t const *sip);
 
-int nua_stack_process_response(nua_handle_t *nh,
-			       nua_client_request_t *cr,
-			       nta_outgoing_t *orq,
-			       sip_t const *sip,
-			       tag_type_t tag, tag_value_t value, ...);
-
 int nua_stack_launch_network_change_detector(nua_t *nua);
 
-msg_t *nua_creq_msg(nua_t *nua, nua_handle_t *nh,
-		    nua_client_request_t *cr,
-		    int restart, 
-		    sip_method_t method, char const *name,
-		    tag_type_t tag, tag_value_t value, ...);
-
-int nua_tagis_have_contact_tag(tagi_t const *t);
-
-int nua_creq_check_restart(nua_handle_t *nh,
-			   nua_client_request_t *cr,
-			   nta_outgoing_t *orq,
-			   sip_t const *sip,
-			   nua_creq_restart_f *f);
-
-int nua_creq_restart_with(nua_handle_t *nh,
-			  nua_client_request_t *cr,
-			  nta_outgoing_t *orq,
-			  int status, char const *phrase,
-			  nua_creq_restart_f *f, 
-			  tag_type_t tag, tag_value_t value, ...);
-
-int nua_creq_save_restart(nua_handle_t *nh,
-			  nua_client_request_t *cr,
-			  nta_outgoing_t *orq,
-			  int status, char const *phrase,
-			  nua_creq_restart_f *f);
-
-int nua_creq_restart(nua_handle_t *nh,
-		     nua_client_request_t *cr,
-		     nta_response_f *cb,
-		     tagi_t *tags);
-
-void nua_creq_deinit(nua_client_request_t *cr, nta_outgoing_t *orq);
-
 sip_contact_t const *nua_stack_get_contact(nua_registration_t const *nr);
 
 int nua_registration_add_contact_to_request(nua_handle_t *nh,
@@ -405,51 +396,6 @@
 					     sip_record_route_t const *,
 					     sip_contact_t const *remote);
 
-msg_t *nh_make_response(nua_t *nua, nua_handle_t *nh, 
-			nta_incoming_t *irq,
-			int status, char const *phrase,
-			tag_type_t tag, tag_value_t value, ...);
-
-
-typedef int nua_stack_process_request_t(nua_t *nua,
-					nua_handle_t *nh,
-					nta_incoming_t *irq,
-					sip_t const *sip);
-
-nua_stack_process_request_t nua_stack_process_invite;
-nua_stack_process_request_t nua_stack_process_info;
-nua_stack_process_request_t nua_stack_process_update;
-nua_stack_process_request_t nua_stack_process_bye;
-nua_stack_process_request_t nua_stack_process_message;
-nua_stack_process_request_t nua_stack_process_options;
-nua_stack_process_request_t nua_stack_process_publish;
-nua_stack_process_request_t nua_stack_process_subscribe;
-nua_stack_process_request_t nua_stack_process_notify;
-nua_stack_process_request_t nua_stack_process_refer;
-nua_stack_process_request_t nua_stack_process_unknown;
-nua_stack_process_request_t nua_stack_process_register;
-nua_stack_process_request_t nua_stack_process_method;
-
-nua_client_request_t
-  *nua_client_request_pending(nua_client_request_t const *),
-  *nua_client_request_restarting(nua_client_request_t const *),
-  *nua_client_request_by_orq(nua_client_request_t const *cr,
-			     nta_outgoing_t const *orq);
-
-nua_server_request_t *nua_server_request(nua_t *nua,
-					 nua_handle_t *nh,
-					 nta_incoming_t *irq,
-					 sip_t const *sip,
-					 nua_server_request_t *sr,
-					 size_t size,
-					 nua_server_respond_f *respond,
-					 int create_dialog);
-
-int nua_stack_server_event(nua_t *nua,
-			   nua_server_request_t *sr,
-			   nua_event_t event,
-			   tag_type_t tag, tag_value_t value, ...);
-
 /* ---------------------------------------------------------------------- */
 
 #ifndef SDP_MIME_TYPE
@@ -466,15 +412,6 @@
 #define NUTAG_ADD_CONTACT(v) _nutag_add_contact, tag_bool_v(v)
 extern tag_typedef_t _nutag_add_contact;
 
-#define NUTAG_ADD_CONTACT_REF(v) _nutag_add_contact_ref, tag_bool_vr(&v)
-extern tag_typedef_t _nutag_add_contact_ref;
-
-#define NUTAG_COPY(v) _nutag_copy, tag_bool_v(v)
-extern tag_typedef_t _nutag_copy;
-
-#define NUTAG_COPY_REF(v) _nutag_copy_ref, tag_bool_vr(&v)
-extern tag_typedef_t _nutag_copy_ref;
-
 /* ---------------------------------------------------------------------- */
 
 typedef unsigned longlong ull;

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c	Tue Mar 20 23:37:15 2007
@@ -47,12 +47,10 @@
 #include <sofia-sip/string0.h>
 #include <sofia-sip/sip_protos.h>
 #include <sofia-sip/sip_status.h>
+#include <sofia-sip/sip_extra.h>
 #include <sofia-sip/sip_util.h>
 #include <sofia-sip/su_uniqueid.h>
 
-#define NTA_LEG_MAGIC_T      struct nua_handle_s
-#define NTA_OUTGOING_MAGIC_T struct nua_handle_s
-
 #include "nua_stack.h"
 
 /* ---------------------------------------------------------------------- */
@@ -60,9 +58,10 @@
 
 struct event_usage
 {
-  enum nua_substate  eu_substate;	/**< Subscription state */
+  enum nua_substate eu_substate;	/**< Subscription state */
   sip_time_t eu_expires;	        /**< Proposed expiration time */
   unsigned eu_notified;		        /**< Number of NOTIFYs received */
+  unsigned eu_refer:1;		        /**< Implied subscription by refer */
   unsigned eu_final_wait:1;	        /**< Waiting for final NOTIFY */
   unsigned eu_no_id:1;		        /**< Do not use "id" (even if we have one) */
 };
@@ -105,6 +104,7 @@
 {
   ds->ds_has_events++;
   ds->ds_has_subscribes++;
+
   return 0;
 }
 
@@ -167,134 +167,128 @@
  * @sa NUTAG_SUBSTATE(), @RFC3265
  */
 
-static int process_response_to_subscribe(nua_handle_t *nh,
-					 nta_outgoing_t *orq,
+static int nua_subscribe_client_init(nua_client_request_t *cr, 
+				     msg_t *, sip_t *,
+				     tagi_t const *tags);
+static int nua_subscribe_client_request(nua_client_request_t *cr,
+					msg_t *, sip_t *,
+					tagi_t const *tags);
+static int nua_subscribe_client_response(nua_client_request_t *cr,
+					 int status, char const *phrase,
 					 sip_t const *sip);
 
+static nua_client_methods_t const nua_subscribe_client_methods = {
+  SIP_METHOD_SUBSCRIBE,
+  0,
+  { 
+    /* create_dialog */ 1,
+    /* in_dialog */ 1,
+    /* target refresh */ 1
+  },
+  NULL,
+  nua_subscribe_client_init,
+  nua_subscribe_client_request,
+  /* nua_subscribe_client_check_restart */ NULL,
+  nua_subscribe_client_response
+};
 
 int
 nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
 		    tagi_t const *tags)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  nua_dialog_usage_t *du = NULL;
-  struct event_usage *eu;
-  msg_t *msg;
-  sip_t *sip;
+  return nua_client_create(nh, e, &nua_subscribe_client_methods, tags);
+}
 
-  if (nua_stack_set_handle_special(nh, nh_has_subscribe, nua_r_subscribe) < 0)
-    return UA_EVENT3(e, 500, "Invalid handle for SUBSCRIBE", 
-		     NUTAG_SUBSTATE(nua_substate_terminated));
-  else if (cr->cr_orq)
-    return UA_EVENT2(e, 900, "Request already in progress");
-
-  /* Initialize allow and auth */
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  msg = nua_creq_msg(nua, nh, cr, 0,
-		     SIP_METHOD_SUBSCRIBE,
-		     NUTAG_USE_DIALOG(1),
-		     NUTAG_ADD_CONTACT(1),
-		     TAG_NEXT(tags));
-  sip = sip_object(msg);
+static int nua_subscribe_client_init(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;
+  sip_event_t *o = sip->sip_event;
 
-  if (sip) {
-    sip_event_t *o = sip->sip_event;
+  du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, o);
 
-    du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, o);
+  if (du == NULL && o == NULL)
+    du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, NONE);
 
-    if (du == NULL && o == NULL)
-      du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, NONE);
+  if (du) {
+    if (du->du_event && o == NULL)
+      /* Add Event header */
+      sip_add_dup(msg, sip, (sip_header_t *)du->du_event);
+  }
+  else if (cr->cr_event == nua_r_subscribe) {	
+    /* Create dialog usage */
+    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, o);
+    /* Note that we allow SUBSCRIBE without event */
+  }
 
-    eu = nua_dialog_usage_private(du);
+  cr->cr_usage = du;
 
-    if (du && du->du_event && (o == NULL || (o->o_id && eu->eu_no_id))) {
-      if (eu->eu_no_id)		/* No id (XXX - nor other parameters) */
-	sip_add_make(msg, sip, sip_event_class, du->du_event->o_type);
-      else
-	sip_add_dup(msg, sip, (sip_header_t *)du->du_event);
-    }
+  return 0;
+}
+
+static int nua_subscribe_client_request(nua_client_request_t *cr,
+					msg_t *msg, sip_t *sip,
+					tagi_t const *tags)
+{
+  nua_dialog_usage_t *du = cr->cr_usage; 
+  sip_time_t expires = 0;
+
+  if (cr->cr_event != nua_r_subscribe ||
+      (du && du->du_shutdown) ||
+      (sip->sip_expires && sip->sip_expires->ex_delta == 0))
+    cr->cr_terminating = 1;
+
+  if (du) {
+    struct event_usage *eu = nua_dialog_usage_private(du);
+    sip_event_t *o = sip->sip_event;
 
-    if (e == nua_r_subscribe) {	
-      if (du == NULL)		/* Create dialog usage */
-	/* We allow here SUBSCRIBE without event */
-	du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, o);
+    if (nua_client_bind(cr, du) < 0)
+      return -1;
+
+    if (eu->eu_no_id && o && o->o_id) {
+      /* Notifier does not handle id properly, remove it */
+      msg_header_remove_param(o->o_common, "id");
     }
-    else if (du) { /* Unsubscribe */
-      /* Embryonic subscription is just a placeholder */
+
+#if 0
+    if (cr->cr_terminating) {
+      /* Already terminated subscription? */
       if (eu->eu_substate == nua_substate_terminated ||
 	  eu->eu_substate == nua_substate_embryonic) {
-	nua_dialog_usage_remove(nh, nh->nh_ds, du);
-	msg_destroy(msg);
-	return UA_EVENT3(e, SIP_200_OK, 
-			 NUTAG_SUBSTATE(nua_substate_terminated),
-			 TAG_END());
+	return nua_client_return(cr, SIP_200_OK, msg);
       }
     }
-  }
+#endif
 
-  /* Store message template with supported features (eventlist) */
-  if (du && sip) {
-    if (du->du_msg)
-      msg_destroy(du->du_msg);
-    du->du_msg = msg_ref_create(cr->cr_msg);
-  }
-
-  if (du)
-    cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				      process_response_to_subscribe, nh, NULL,
-				      msg,
-				      TAG_IF(e != nua_r_subscribe,
-					     SIPTAG_EXPIRES_STR("0")),
-				      SIPTAG_END(), TAG_NEXT(tags));
-
-  eu = nua_dialog_usage_private(du);
-
-  if (!cr->cr_orq) {
-    int substate = nua_substate_terminated;
-
-    if (du == NULL)
-      ;
-    else if (du->du_ready)
-      substate = eu->eu_substate; /* No change in subscription state  */
+    nua_dialog_usage_reset_refresh(du); /* during SUBSCRIBE transaction */
+    
+    if (cr->cr_terminating)
+      expires = eu->eu_expires = 0;
+    else if (sip->sip_expires)
+      /* Use value specified by application or negotiated with Min-Expires */
+      expires = eu->eu_expires = sip->sip_expires->ex_delta;
     else
-      nua_dialog_usage_remove(nh, nh->nh_ds, du);
-
-    msg_destroy(msg);
-
-    return UA_EVENT3(e, NUA_INTERNAL_ERROR, 
-		     NUTAG_SUBSTATE(substate), TAG_END());
-  }
-
-  nua_dialog_usage_reset_refresh(du); /* during SUBSCRIBE transaction */
-  du->du_terminating = e != nua_r_subscribe; /* Unsubscribe or destroy */
-
-  if (du->du_terminating)
-    eu->eu_expires = 0;
-  else if (sip->sip_expires)
-    eu->eu_expires = sip->sip_expires->ex_delta;
-  else
     /* We just use common default value, but the default is actually
        package-specific according to the RFC 3265 section 4.4.4:
        [Event] packages MUST also define a
        default "Expires" value to be used if none is specified. */
-    eu->eu_expires = 3600;
+      expires = eu->eu_expires = 3600;
 
-  eu->eu_final_wait = 0;
-    
-  if (sip->sip_expires && sip->sip_expires->ex_delta == 0)
-    du->du_terminating = 1;
+    eu->eu_final_wait = 0;
 
-  if (eu->eu_substate == nua_substate_terminated)
-    eu->eu_substate = nua_substate_embryonic;
+    if (eu->eu_substate == nua_substate_terminated)
+      eu->eu_substate = nua_substate_embryonic;
+  }
 
-  cr->cr_usage = du;
-  return cr->cr_event = e;
-}
+  if (!sip->sip_expires || sip->sip_expires->ex_delta != expires) {
+    sip_expires_t ex[1];
+    sip_expires_init(ex)->ex_delta = expires;
+    sip_add_dup(msg, sip, (sip_header_t *)ex);
+  }
 
-static void restart_subscribe(nua_handle_t *nh, tagi_t *tags)
-{
-  nua_creq_restart(nh, nh->nh_ds->ds_cr, process_response_to_subscribe, tags);
+  return nua_base_client_request(cr, msg, sip, tags);
 }
 
 /** @NUA_EVENT nua_r_subscribe
@@ -342,51 +336,41 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_subscribe(nua_handle_t *nh,
-					 nta_outgoing_t *orq,
+static int nua_subscribe_client_response(nua_client_request_t *cr,
+					 int status, char const *phrase,
 					 sip_t const *sip)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
+  nua_handle_t *nh = cr->cr_owner;
   nua_dialog_usage_t *du = cr->cr_usage; 
   struct event_usage *eu = nua_dialog_usage_private(du);
-  int status = sip ? sip->sip_status->st_status : 408;
-  int gracefully = 0;
-  int substate = nua_substate_embryonic;
-
-  assert(du); assert(du->du_class == nua_subscribe_usage);
-
-  if (status < 200)
-    ;
-  else if (du == NULL) {
-    /* NOTIFY already removed du */
-  }
-  /* We have not received NOTIFY. */
-  else if (status < 300) {
+  enum nua_substate substate;
+
+  if (eu == NULL || cr->cr_terminated)
+    substate = nua_substate_terminated;
+  else if (status >= 300)
+    substate = eu->eu_substate;
+  else {
     int win_messenger_enable = NH_PGET(nh, win_messenger_enable);
     sip_time_t delta, now = sip_now();
 
     du->du_ready = 1;
-    substate = eu->eu_substate;
-    
-    if (du->du_terminating)
-      delta = 0;
-    else
-      /* If there is no expires header,
+
+    if (eu->eu_substate != nua_substate_terminated)
+      /* If there is no @Expires header, 
 	 use default value stored in eu_expires */
       delta = sip_contact_expires(NULL, sip->sip_expires, sip->sip_date, 
 				  eu->eu_expires, now);
+    else
+      delta = 0;
 
     if (win_messenger_enable && !nua_dialog_is_established(nh->nh_ds)) {
       /* Notify from messanger does not match with dialog tag */ 
       nh->nh_ds->ds_remote_tag = su_strdup(nh->nh_home, "");
     }
 
-    nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
-    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
-
     if (delta > 0) {
       nua_dialog_usage_set_refresh(du, delta);
-    }
+    } 
     else if (!eu->eu_notified) {
       /* This is a fetch: subscription was really terminated
 	 but we wait 32 seconds for NOTIFY. */
@@ -396,51 +380,26 @@
 	delta = 4 * 60; 	/* Wait 4 minutes for NOTIFY from Messenger */
 
       eu->eu_final_wait = 1;
-
-      /* Do not remove usage in nua_stack_process_response  */
-      cr->cr_usage = NULL;	
+	
+      if (eu->eu_substate == nua_substate_terminated)
+	eu->eu_substate = nua_substate_embryonic;
 
       nua_dialog_usage_refresh_range(du, delta, delta);
     }
     else {
-      eu->eu_substate = substate = nua_substate_terminated;
+      eu->eu_substate = nua_substate_terminated;
     }
-  }
-  else /* if (status >= 300) */ {
-    int terminated;
-
-    if (nua_creq_check_restart(nh, cr, orq, sip, restart_subscribe))
-      return 0;
-
-    cr->cr_usage = NULL; /* We take care of removing/not removing usage */
 
     substate = eu->eu_substate;
 
-    if (!sip || !sip->sip_retry_after)
-      gracefully = 1;
-
-    terminated = 
-      sip_response_terminates_dialog(status, sip_method_subscribe, 
-				     &gracefully);
-
-    /* XXX - zap dialog if terminated < 0 ? */
-
-    if (terminated || !du->du_ready || du->du_terminating) {
-      substate = nua_substate_terminated;
-      nua_dialog_usage_remove(nh, nh->nh_ds, du);
-    }
-    else if (gracefully && substate != nua_substate_terminated) 
-      /* Post un-subscribe event */
-      nua_stack_post_signal(nh, nua_r_unsubscribe, 
-			    SIPTAG_EVENT(du->du_event), 
-			    SIPTAG_EXPIRES_STR("0"),
-			    TAG_END());
+    if (substate == nua_substate_terminated)
+      /* let nua_base_client_tresponse to remove usage */
+      cr->cr_terminated = 1;	
   }
-
-  nua_stack_process_response(nh, cr, orq, sip, 
-			     TAG_IF(substate >= 0, NUTAG_SUBSTATE(substate)),
-			     TAG_END());
-  return 0;
+  
+  return nua_base_client_tresponse(cr, status, phrase, sip, 
+				   NUTAG_SUBSTATE(substate),
+				   TAG_END());
 }
 
 /** Refresh subscription */
@@ -449,120 +408,71 @@
 					nua_dialog_usage_t *du,
 					sip_time_t now)
 {
-  nua_t *nua = nh->nh_nua;
-  nua_client_request_t *cr = ds->ds_cr;
+  nua_client_request_t *cr = du->du_cr;
   struct event_usage *eu = nua_dialog_usage_private(du);
-  msg_t *msg;
-
+  
   assert(eu);
   
   if (eu->eu_final_wait) {
-    /* Did not receive NOTIFY for fetch... */
+    /* Did not receive NOTIFY for fetch */
     sip_event_t const *o = du->du_event;
     char const *id = o ? o->o_id : NULL;
 
-    SU_DEBUG_3(("nua(%p): fetch event %s%s%s timeouts\n",
-		nh, o ? o->o_type : "(empty)",
+    SU_DEBUG_3(("nua(%p): event %s%s%s fetch timeouts\n",
+		(void *)nh, o ? o->o_type : "(empty)",
 		id ? "; id=" : "", id ? id : ""));
 
-    nua_stack_event(nh->nh_nua, nh,  NULL,
-		    nua_i_notify, 408, "Fetch Timeouts without NOTIFY", 
-		    NUTAG_SUBSTATE(nua_substate_terminated),
-		    SIPTAG_EVENT(o),
-		    TAG_END());
-
-    nua_dialog_usage_remove(nh, ds, du);
-
-    return;
-  }
-
-  if (du->du_terminating)	/* No need to refresh. */
-    return;
-
-  if (cr->cr_msg) {
-    /* Already doing something, delay 5..15 seconds? */
-    if (cr->cr_usage != du)
-      nua_dialog_usage_refresh_range(du, 5, 15);
-    return;
-  }
-
-  cr->cr_msg = msg_copy(du->du_msg);
-
-  msg = nua_creq_msg(nua, nh, cr, 1,
-		     SIP_METHOD_SUBSCRIBE,
-		     NUTAG_USE_DIALOG(1),
-		     NUTAG_ADD_CONTACT(1),
-		     /* If dialog is established, remove initial route */
-		     TAG_IF(nua_dialog_is_established(nh->nh_ds),
-			    SIPTAG_ROUTE(NONE)),
+    nua_stack_tevent(nh->nh_nua, nh,  NULL,
+		     nua_i_notify, 408, "Fetch Timeouts without NOTIFY", 
+		     NUTAG_SUBSTATE(nua_substate_terminated),
+		     SIPTAG_EVENT(du->du_event),
 		     TAG_END());
+    nua_dialog_usage_remove(nh, ds, du);
 
-  cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				    process_response_to_subscribe, nh, NULL,
-				    msg,
-				    SIPTAG_END(), TAG_NEXT(NULL));
-  if (cr->cr_orq) {
-    cr->cr_usage = du;
-    cr->cr_event = nua_r_subscribe;
     return;
   }
 
-  if (du->du_terminating)
-    nua_dialog_usage_remove(nh, nh->nh_ds, du);
-  else   /* Try again later? */
-    nua_dialog_usage_refresh_range(du, 5, 15);
+  if (cr) {
+    if (nua_client_resend_request(cr, 0) >= 0)
+      return;
+  }
+  else if (eu->eu_refer) {
+    /*
+     * XXX - If we have received a NOTIFY, we should try to terminate
+     * subscription
+     */
+  }
+
+  nua_stack_tevent(nh->nh_nua, nh, NULL,
+		   nua_i_notify, NUA_INTERNAL_ERROR,
+		   NUTAG_SUBSTATE(nua_substate_terminated),
+		   SIPTAG_EVENT(du->du_event),
+		   TAG_END());
 
-  msg_destroy(msg);
-  UA_EVENT3(nua_r_subscribe, NUA_INTERNAL_ERROR, 
-	    NUTAG_SUBSTATE(eu->eu_substate),
-	    TAG_END());
+  nua_dialog_usage_remove(nh, ds, du);
 }
 
-
-/** Terminate subscription */
+/** Terminate subscription.
+ *
+ * @retval >0  shutdown done
+ * @retval 0   shutdown in progress
+ * @retval <0  try again later
+ */
 static int nua_subscribe_usage_shutdown(nua_handle_t *nh,
 					nua_dialog_state_t *ds,
 					nua_dialog_usage_t *du)
 {
-  nua_t *nua = nh->nh_nua;
-  nua_client_request_t *cr = ds->ds_cr;
   struct event_usage *eu = nua_dialog_usage_private(du);
-  msg_t *msg;
+  nua_client_request_t *cr = du->du_cr;
 
   assert(eu); (void)eu;
 
-  if (du->du_terminating)
-    return 100;			/* ...in progress */
-  
-  if (cr->cr_msg)
-    /* XXX - already doing something else? */
-    return 100;
-
-  cr->cr_msg = msg_copy(du->du_msg);
-
-  msg = nua_creq_msg(nua, nh, cr, 1,
-		     SIP_METHOD_SUBSCRIBE,
-		     NUTAG_USE_DIALOG(1),
-		     NUTAG_ADD_CONTACT(1),
-		     SIPTAG_EXPIRES_STR("0"),
-		     /* If dialog is established, remove initial route */
-		     TAG_IF(nua_dialog_is_established(nh->nh_ds),
-			    SIPTAG_ROUTE(NONE)),
-		     TAG_END());
-
-  cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				    process_response_to_subscribe, nh, NULL,
-				    msg,
-				    SIPTAG_END(), TAG_NEXT(NULL));
-  if (cr->cr_orq) {
-    cr->cr_usage = du;
-    cr->cr_event = nua_r_destroy;
-    return 100;
+  if (cr) {
+    if (nua_client_resend_request(cr, 1) >= 0)
+      return 0;
   }
-
-  /* Too bad. */
+  
   nua_dialog_usage_remove(nh, ds, du);
-  msg_destroy(msg);
   return 200;
 }
 
@@ -585,178 +495,183 @@
  * @END_NUA_EVENT
  */
 
-/** @internal Process incoming NOTIFY. */
-int nua_stack_process_notify(nua_t *nua,
-			     nua_handle_t *nh,
-			     nta_incoming_t *irq,
-			     sip_t const *sip)
-{
-  nua_dialog_state_t *ds = nh->nh_ds;
-  nua_dialog_usage_t *du;
-  struct event_usage *eu;
-  sip_subscription_state_t *subs = sip ? sip->sip_subscription_state : NULL;
-  sip_subscription_state_t ss0[1];
-  msg_t *response;
-  char expires[32];
-  int retry = -1;
-  char const *what = NULL, *why = NULL;
+int nua_notify_server_init(nua_server_request_t *sr);
+int nua_notify_server_preprocess(nua_server_request_t *sr);
+int nua_notify_server_report(nua_server_request_t *, tagi_t const *);
 
-  enter;
+nua_server_methods_t const nua_notify_server_methods = 
+  {
+    SIP_METHOD_NOTIFY,
+    nua_i_notify,		/* Event */
+    { 
+      1,			/* Do create dialog */
+      0,			/* Not always in-dialog request */
+      1,			/* Target refresh request  */
+      1,			/* Add Contact to response */
+    },
+    nua_notify_server_init,
+    nua_notify_server_preprocess,
+    nua_base_server_params,
+    nua_base_server_respond,
+    nua_notify_server_report,
+  };
+
+
+int nua_notify_server_init(nua_server_request_t *sr)
+{
+  if (!sr->sr_initial) {
+    nua_dialog_state_t *ds = sr->sr_owner->nh_ds;
+
+    /* Check for forked subscription. */
+    if (ds->ds_remote_tag && ds->ds_remote_tag[0] && 
+	str0cmp(ds->ds_remote_tag, sr->sr_request.sip->sip_from->a_tag)) {
+      sip_contact_t const *m = NULL;
+
+      m = nua_stack_get_contact(sr->sr_owner->nh_nua->nua_registrations);
+      
+      if (m) {
+	sip_warning_t w[1];
+	
+	sip_warning_init(w)->w_code = 399;
+	w->w_host = m->m_url->url_host;
+	w->w_port = m->m_url->url_port;
+	w->w_text = "Forking SUBSCRIBEs are not supported";
 
-  if (nh == NULL) {
-    nta_incoming_treply(irq, 481, "Subscription Does Not Exist", 
-			TAG_END());
-    return 481;
-  }
-  assert(nh);
-
-  if (/* XXX - support forking of subscriptions?... */
-      ds->ds_remote_tag && ds->ds_remote_tag[0] && 
-      sip && sip->sip_from->a_tag &&
-      strcmp(ds->ds_remote_tag, sip->sip_from->a_tag)) {
-    sip_contact_t const *m = NULL;
-    sip_warning_t *w = NULL, w0[1];
-
-    m = nua_stack_get_contact(nua->nua_registrations);
-    if (m) {
-      w = sip_warning_init(w0);
-      w->w_code = 399;
-      w->w_host = m->m_url->url_host;
-      w->w_port = m->m_url->url_port;
-      w->w_text = "Forking SUBSCRIBEs are not supported";
-    }
+	sip_add_dup(sr->sr_response.msg, NULL, (sip_header_t*)w);
+      }
 
-    nta_incoming_treply(irq, 481, "Subscription Does Not Exist", 
-			SIPTAG_WARNING(w),
-			TAG_END());
-    return 481;
+      return SR_STATUS(sr, 481, "Subscription Does Not Exist");
+    }
   }
 
-  du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, sip->sip_event);
+  return 0;
+}
+
+int nua_notify_server_preprocess(nua_server_request_t *sr)
+{
+  nua_dialog_state_t *ds = sr->sr_owner->nh_ds;
+  nua_dialog_usage_t *du;
+  struct event_usage *eu;
+  sip_t const *sip = sr->sr_request.sip;
+  sip_event_t *o = sip->sip_event;
+  enum nua_substate substate = nua_substate_terminated;
+  sip_subscription_state_t *subs = sip->sip_subscription_state;
+  char const *what = "", *reason = NULL;
+
+  du = nua_dialog_usage_get(ds, nua_subscribe_usage, o);
+  sr->sr_usage = du;
 
   if (du == NULL) {
-    nta_incoming_treply(irq, 481, "Subscription Does Not Exist", TAG_END());
-    return 481;
+    if (!sip_is_allowed(NH_PGET(sr->sr_owner, appl_method), SIP_METHOD_NOTIFY))
+      return SR_STATUS(sr, 481, "Subscription Does Not Exist");
+    /* Let application to handle unsolicited NOTIFY */
+    return 0;
   }
-
+  
   eu = nua_dialog_usage_private(du); assert(eu);
   eu->eu_notified++;
-
-  if (!sip->sip_event->o_id) {
+  if (!o->o_id) 
     eu->eu_no_id = 1;
-  }
 
   if (subs == NULL) {
-    /* Do some compatibility stuff here */
-    unsigned long delta;
-
-    sip_subscription_state_init(subs = ss0);
-
-    delta = sip->sip_expires ? sip->sip_expires->ex_delta : eu->eu_expires;
+    /* Compatibility */
+    unsigned long delta = eu->eu_expires;
+    if (sip->sip_expires) 
+      delta = sip->sip_expires->ex_delta;
 
     if (delta == 0)
-      subs->ss_substate = "terminated";
+      substate = nua_substate_terminated, what = "terminated";
     else
-      subs->ss_substate = "active";
-
-    if (delta > 0 && sip->sip_expires) {
-      snprintf(expires, sizeof expires, "%lu", delta);
-      subs->ss_expires = expires;
-    }
+      substate = nua_substate_active, what = "active";
   }
+  else if (strcasecmp(subs->ss_substate, what = "terminated") == 0) {
+    substate = nua_substate_terminated;
+    reason = subs->ss_reason;
 
-  nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
-  nua_dialog_uas_route(nh, nh->nh_ds, sip, 1);
-
-  if (strcasecmp(subs->ss_substate, what = "terminated") == 0) {
-    eu->eu_substate = nua_substate_terminated;
-
-    if (str0casecmp(subs->ss_reason, why = "deactivated") == 0) {
-      eu->eu_substate = nua_substate_embryonic;
-      retry = 0;		/* retry immediately */
-    } 
-    else if (str0casecmp(subs->ss_reason, why = "probation") == 0) {
-      eu->eu_substate = nua_substate_embryonic;
-      retry = 30;
-      if (subs->ss_retry_after)
-	retry = strtoul(subs->ss_retry_after, NULL, 10);
-      if (retry > 3600)
-	retry = 3600;
-    }
-    else
-      why = subs->ss_reason;
+    if (str0casecmp(reason, "deactivated") == 0 ||
+	str0casecmp(reason, "probation") == 0) 
+      substate = nua_substate_embryonic;
   }
-  else if (strcasecmp(subs->ss_substate, what = "pending") == 0)
-    eu->eu_substate = nua_substate_pending;
-  else /* if (strcasecmp(subs->ss_substate, "active") == 0) */ {
+  else if (strcasecmp(subs->ss_substate, what = "pending") == 0) {
+    substate = nua_substate_pending;
+  }
+  else /* if (strcasecmp(subs->ss_substate, what = "active") == 0) */ {
     /* Any extended state is considered as active */
-    what = subs->ss_substate ? subs->ss_substate : "active";
-    eu->eu_substate = nua_substate_active;
+    what = subs->ss_substate;
+    substate = nua_substate_active;
   }
 
-  if (du->du_terminating)
-    retry = -1;
-  
-  response = nh_make_response(nua, nh, irq, SIP_200_OK,
-			      SIPTAG_ALLOW(NH_PGET(nh, allow)),
-			      SIPTAG_SUPPORTED(NH_PGET(nh, supported)),
-			      TAG_END());
-
-  if (response && 
-      nua_registration_add_contact_to_response(nh, response, NULL, 
-					       sip->sip_record_route,
-					       sip->sip_contact) >= 0)
-    nta_incoming_mreply(irq, response);
-  else
-    nta_incoming_treply(irq, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
-
-  if (eu->eu_substate == nua_substate_terminated && retry > 0)
-    eu->eu_substate = nua_substate_embryonic;
-
-  nua_stack_event(nh->nh_nua, nh, nta_incoming_getrequest(irq),
-		  nua_i_notify, SIP_200_OK, 
-		  NUTAG_SUBSTATE(eu->eu_substate),
-		  TAG_END());
-
-  nta_incoming_destroy(irq), irq = NULL;
-
-  SU_DEBUG_5(("nua(%p): nua_stack_process_notify: %s (%s)\n", 
-	      nh, what, why ? why : ""));
-
-  if (eu->eu_substate == nua_substate_terminated) {
-    if (du != nh->nh_ds->ds_cr->cr_usage)
-      nua_dialog_usage_remove(nh, nh->nh_ds, du);
-    else
-      nua_dialog_usage_reset_refresh(du);
-  }
-  else if (eu->eu_substate == nua_substate_embryonic) {
-    if (retry >= 0) {
-      /* Try to subscribe again */
-      nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
-      nua_dialog_usage_refresh_range(du, retry, retry + 5);
+  eu->eu_substate = substate;
+
+  SU_DEBUG_5(("nua(%p): %s: %s (%s)\n", 
+	      (void *)sr->sr_owner, "nua_notify_server_preprocess",
+	      what, reason ? reason : ""));
+
+  return SR_STATUS1(sr, SIP_200_OK);
+}
+
+
+int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags)
+{
+  nua_handle_t *nh = sr->sr_owner;
+  nua_dialog_usage_t *du = sr->sr_usage;
+  struct event_usage *eu = nua_dialog_usage_private(du);
+  sip_t const *sip = sr->sr_request.sip;
+  enum nua_substate substate = nua_substate_terminated;
+  sip_time_t delta = SIP_TIME_MAX;
+  int retry = -1;
+  int retval;
+
+  if (eu) {
+    sip_subscription_state_t *subs = sip->sip_subscription_state;
+
+    substate = eu->eu_substate;
+
+    if (substate == nua_substate_active || substate == nua_substate_pending) {
+      if (subs && subs->ss_expires)
+	delta = strtoul(subs->ss_expires, NULL, 10);
+      else
+	delta = eu->eu_expires;
+    }
+    else if (substate == nua_substate_embryonic) {
+      if (subs && subs->ss_reason) {
+	if (str0casecmp(subs->ss_reason, "deactivated") == 0) {
+	  retry = 0;		/* retry immediately */
+	} 
+	else if (str0casecmp(subs->ss_reason, "probation") == 0) {
+	  retry = 30;
+	  if (subs->ss_retry_after)
+	    retry = strtoul(subs->ss_retry_after, NULL, 10);
+	  if (retry > 3600)
+	    retry = 3600;
+	}
+      }
+    }
+    else if (substate == nua_substate_terminated) {
+      sr->sr_terminating = 1;
     }
-    else if (du != nh->nh_ds->ds_cr->cr_usage)
-      nua_dialog_usage_remove(nh, nh->nh_ds, du);
-    else
-      nua_dialog_usage_reset_refresh(du);
   }
-  else if (du->du_terminating) {
-    nua_dialog_usage_reset_refresh(du);
+  
+  retval = nua_base_server_treport(sr, /* can destroy sr */
+				   NUTAG_SUBSTATE(substate),
+				   TAG_NEXT(tags)); 
+
+  if (retval >= 2 || du == NULL)
+    return retval;
+
+  if (retry >= 0) {		/* Try to subscribe again */
+    /* XXX - this needs through testing */
+    nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
+    nua_dialog_usage_refresh_range(du, retry, retry + 5);
   }
   else {
-    sip_time_t delta;
-
-    if (subs->ss_expires)
-      delta = strtoul(subs->ss_expires, NULL, 10);
-    else
-      delta = eu->eu_expires;
-    
     nua_dialog_usage_set_refresh(du, delta);
   }
 
-  return 0;
+  return retval;
 }
 
+
 /* ======================================================================== */
 /* REFER */
 
@@ -802,81 +717,6 @@
  * @RFC3515, @ReferTo, @RFC3892, @ReferredBy
  */
 
-static int process_response_to_refer(nua_handle_t *nh,
-				     nta_outgoing_t *orq,
-				     sip_t const *sip);
-
-int
-nua_stack_refer(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
-{
-  nua_dialog_usage_t *du = NULL;
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  msg_t *msg;
-  sip_t *sip;
-  sip_referred_by_t by[1];
-  sip_event_t *event = NULL;
-
-  if (nua_stack_set_handle_special(nh, nh_has_subscribe, nua_r_subscribe) < 0)
-    return UA_EVENT2(e, 900, "Invalid handle for REFER");
-  else if (cr->cr_orq)
-    return UA_EVENT2(e, 900, "Request already in progress");
-
-  nua_stack_init_handle(nua, nh, TAG_NEXT(tags));
-
-  sip_referred_by_init(by);
-  by->b_display = nua->nua_from->a_display;
-  *by->b_url = *nua->nua_from->a_url;
-
-  /* Now we create a REFER request message */
-  msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
-			 SIP_METHOD_REFER,
-			 NUTAG_USE_DIALOG(1),
-			 SIPTAG_EVENT(SIP_NONE), /* remove event */
-			 SIPTAG_REFERRED_BY(by), /* Overriden by user tags */
-			 NUTAG_ADD_CONTACT(1),
-			 TAG_NEXT(tags));
-  sip = sip_object(msg);
-
-  if (sip && sip->sip_cseq)
-    event = sip_event_format(nh->nh_home, "refer;id=%u", 
-			     sip->sip_cseq->cs_seq);
-
-  if (event)
-    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event);
-  
-  if (du)
-    cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
-				      process_response_to_refer, nh, NULL,
-				      msg,
-				      SIPTAG_END(), TAG_NEXT(tags));
-  
-  if (!cr->cr_orq) {
-    if (du)
-      nua_dialog_usage_remove(nh, nh->nh_ds, du);
-    su_free(nh->nh_home, event);
-    msg_destroy(msg);
-    return UA_EVENT1(e, NUA_INTERNAL_ERROR);
-  }
-
-  /*
-   * We send a 100 trying event so that application gets a event 
-   * it can use to match NOTIFYs with its REFER
-   */
-  nua_stack_event(nua, nh, NULL, e, SIP_100_TRYING, 
-	   NUTAG_REFER_EVENT(event),
-	   TAG_END());
-  su_free(nh->nh_home, event);
-
-  cr->cr_usage = du;
-
-  return cr->cr_event = e;
-}
-
-void restart_refer(nua_handle_t *nh, tagi_t *tags)
-{
-  nua_stack_refer(nh->nh_nua, nh, nh->nh_ds->ds_cr->cr_event, tags);
-}
-
 /**@NUA_EVENT nua_r_refer
  *
  * @brief Response to outgoing REFER.
@@ -900,27 +740,130 @@
  * @END_NUA_EVENT
  */
 
-static int process_response_to_refer(nua_handle_t *nh,
-				     nta_outgoing_t *orq,
-				     sip_t const *sip)
+static int nua_refer_client_init(nua_client_request_t *cr, 
+				 msg_t *, sip_t *,
+				 tagi_t const *tags);
+static int nua_refer_client_request(nua_client_request_t *cr,
+				    msg_t *, sip_t *,
+				    tagi_t const *tags);
+static int nua_refer_client_response(nua_client_request_t *cr,
+				     int status, char const *phrase,
+				     sip_t const *sip);
+
+static nua_client_methods_t const nua_refer_client_methods = {
+  SIP_METHOD_REFER,
+  0,
+  { 
+    /* create_dialog */ 1,
+    /* in_dialog */ 1,
+    /* target refresh */ 1
+  },
+  /*nua_refer_client_template*/ NULL,
+  nua_refer_client_init,
+  nua_refer_client_request,
+  /* nua_refer_client_check_restart */ NULL,
+  nua_refer_client_response,
+  nua_refer_client_response,	/* Preliminary */
+  NULL
+};
+
+int
+nua_stack_refer(nua_t *nua, nua_handle_t *nh, nua_event_t e,
+		    tagi_t const *tags)
 {
-  nua_client_request_t *cr = nh->nh_ds->ds_cr;
-  int status = sip ? sip->sip_status->st_status : 408;
+  return nua_client_create(nh, e, &nua_refer_client_methods, tags);
+}
 
-  if (status < 200)
-    ;
-  else if (status < 300) {
-    if (cr->cr_usage)
-      cr->cr_usage->du_ready = 1;
-    nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
-    nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
-  }
-  else /* if (status >= 300) */ {
-    if (cr->cr_usage)
-      nua_dialog_usage_remove(nh, nh->nh_ds, cr->cr_usage), cr->cr_usage = NULL;
-    if (nua_creq_check_restart(nh, cr, orq, sip, restart_refer))
-      return 0;
+static int nua_refer_client_init(nua_client_request_t *cr,
+				 msg_t *msg, sip_t *sip,
+				 tagi_t const *tags)
+{
+  nua_handle_t *nh = cr->cr_owner;
+
+  if (sip->sip_referred_by == NULL) {
+    sip_from_t *a = sip->sip_from;
+    sip_referred_by_t by[1];
+
+    sip_referred_by_init(by);
+
+    if (a == NULL)
+      a = nh->nh_nua->nua_from;
+    by->b_display = a->a_display;
+    *by->b_url = *a->a_url;
+
+    sip_add_dup(msg, sip, (sip_header_t *)by);
   }
 
-  return nua_stack_process_response(nh, cr, orq, sip, TAG_END());
+  if (sip->sip_event)
+    sip_header_remove(msg, sip, (sip_header_t *)sip->sip_event);
+
+  return 0;
+}
+
+static int nua_refer_client_request(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;
+  struct event_usage *eu;
+  sip_event_t *event;
+  int error;
+
+  cr->cr_usage = NULL;
+
+  if (du)
+    nua_dialog_usage_remove(nh, nh->nh_ds, du);
+
+  event = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
+  if (!event)
+    return -1;
+  du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event);
+  if (!du)
+    return -1;
+
+  eu = nua_dialog_usage_private(cr->cr_usage = du);
+  eu ->eu_refer = 1;
+
+  error = nua_base_client_request(cr, msg, sip, tags);
+
+  if (!error) {
+    /* Give application an Event header for matching NOTIFYs with REFER */
+    nua_stack_tevent(nh->nh_nua, nh, NULL,
+		     cr->cr_event, SIP_100_TRYING,
+		     NUTAG_REFER_EVENT(event),
+		     TAG_END());
+    su_free(nh->nh_home, event);
+  }
+
+  return error;
+}
+
+static int nua_refer_client_response(nua_client_request_t *cr,
+				     int status, char const *phrase,
+				     sip_t const *sip)
+{
+  nua_dialog_usage_t *du = cr->cr_usage; 
+  enum nua_substate substate = nua_substate_terminated;
+
+  if (du) {
+    struct event_usage *eu = nua_dialog_usage_private(du);
+
+    if (status < 200) {
+      substate = eu->eu_substate;      
+    } 
+    else if (status < 300) {
+      sip_refer_sub_t const *rs = sip_refer_sub(sip);
+
+      if (rs && strcasecmp("false", rs->rs_value) == 0)
+	cr->cr_terminated = 1;
+
+      if (!cr->cr_terminated)
+	substate = eu->eu_substate;
+    }
+  }
+  
+  return nua_base_client_tresponse(cr, status, phrase, sip, 
+				   NUTAG_SUBSTATE(substate),
+				   TAG_END());
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c	Tue Mar 20 23:37:15 2007
@@ -68,6 +68,7 @@
 tag_typedef_t nutag_offer_sent = BOOLTAG_TYPEDEF(offer_sent);
 tag_typedef_t nutag_answer_sent = BOOLTAG_TYPEDEF(answer_sent);
 tag_typedef_t nutag_substate = INTTAG_TYPEDEF(substate);
+tag_typedef_t nutag_newsub = BOOLTAG_TYPEDEF(newsub);
 tag_typedef_t nutag_invite_timer = UINTTAG_TYPEDEF(invite_timer);
 tag_typedef_t nutag_session_timer = UINTTAG_TYPEDEF(session_timer);
 tag_typedef_t nutag_min_se = UINTTAG_TYPEDEF(min_se);
@@ -147,6 +148,3 @@
 tag_typedef_t nutag_detect_network_updates = UINTTAG_TYPEDEF(detect_network_updates);
 
 tag_typedef_t nutag_with = PTRTAG_TYPEDEF(with);
-
-tag_typedef_t _nutag_add_contact = BOOLTAG_TYPEDEF(add_contact);
-tag_typedef_t _nutag_copy = BOOLTAG_TYPEDEF(copy);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.c	Tue Mar 20 23:37:15 2007
@@ -117,7 +117,6 @@
 
   char const *ob_instance;	/**< Our instance ID */
   int32_t ob_reg_id;		/**< Flow-id */
-  char const *ob_features;	/**< Feature parameters for rcontact */
   sip_contact_t *ob_rcontact;	/**< Our contact */
   sip_contact_t *ob_dcontact;	/**< Contact for dialogs */
   sip_contact_t *ob_previous;	/**< Stale contact */
@@ -294,7 +293,8 @@
     else if (MATCH(use-upnp) || MATCH(use_upnp)) prefs->use_upnp = value;
     else if (MATCH(use-stun) || MATCH(use_stun)) prefs->use_stun = value;
     else
-      SU_DEBUG_1(("outbound_t: unknown option \"%.*s\"\n", (int)len, s));
+      SU_DEBUG_1(("outbound(%p): unknown option \"%.*s\"\n",
+		  (void *)ob->ob_owner, (int)len, s));
 
     s += len;
     len = strspn(s, " \t\n\r,;");
@@ -304,7 +304,8 @@
   }
 
   if (s && s[0]) {
-    SU_DEBUG_1(("outbound_t: invalid options \"%s\"\n", options));
+    SU_DEBUG_1(("outbound(%p): invalid options \"%s\"\n", 
+		(void *)ob->ob_owner, options));
     return -1;
   }
 
@@ -315,7 +316,8 @@
 	prefs->use_socks ||
 	prefs->use_upnp ||
 	prefs->use_stun)) {
-    SU_DEBUG_1(("outbound(%p): no nat traversal method given\n", ob->ob_owner));
+    SU_DEBUG_1(("outbound(%p): no nat traversal method given\n",
+		(void *)ob->ob_owner));
   }
 
   ob->ob_prefs = *prefs;
@@ -324,60 +326,27 @@
   return 0;
 }
 
-/** Set the feature string (added to the Contact header when registering). */
-int outbound_set_features(outbound_t *ob, char *features)
-{
-  char *old_features = (char *)ob->ob_features;
-  char *new_features = su_strdup(ob->ob_home, features);
-
-  if (features && !new_features)
-    return -1;
-
-  ob->ob_features = new_features;
-  su_free(ob->ob_home, old_features);
-  return 0;
-}
-
 /* ---------------------------------------------------------------------- */
 
-/** Hook for sending register request with extra outbound-ish headers. */
-nta_outgoing_t *outbound_register_request(outbound_t *ob, int terminating, 
-					  sip_contact_t *stack_contact,
-					  nta_agent_t *nta,
-					  nta_response_f *callback,
-					  nta_outgoing_magic_t *magic,
-					  url_string_t *next_hop,
-					  msg_t *msg,
-					  tag_type_t tag, tag_value_t value,
-					  ...)
-{
-  sip_contact_t *previous_contact = NULL;
-  ta_list ta;
-  nta_outgoing_t *orq;
-
-  if (stack_contact) {
-    if (ob) {
-      if (ob->ob_contacts)
-	stack_contact = ob->ob_rcontact;
-      previous_contact = ob->ob_previous;
-    }
+/** Obtain contacts for REGISTER */
+int outbound_get_contacts(outbound_t *ob, 
+			  sip_contact_t **return_current_contact, 
+			  sip_contact_t **return_previous_contact)
+{
+  if (ob) {
+    if (ob->ob_contacts)
+      *return_current_contact = ob->ob_rcontact;
+    *return_previous_contact = ob->ob_previous;
   }
+  return 0;
+}
 
-  ta_start(ta, tag, value);
-
-  orq = nta_outgoing_mcreate(nta, callback, magic, next_hop, msg,
-			     TAG_IF(previous_contact,
-				    SIPTAG_CONTACT(previous_contact)),
-			     TAG_IF(stack_contact,
-				    SIPTAG_CONTACT(stack_contact)),
-			     ta_tags(ta));
-
-  ta_end(ta);
-
-  if (orq && ob)
+/** REGISTER request has been sent */
+int outbound_start_registering(outbound_t *ob)
+{
+  if (ob)
     ob->ob_registering = 1;
-
-  return orq;
+  return 0;
 }
 
 /** Process response to REGISTER request */
@@ -391,17 +360,16 @@
   if (!ob)
     return 0;
 
-  assert(!request || request->sip_request);
-  assert(!response || response->sip_status);
-  
-  if (!response || !request)
-    return 0;
-
   if (terminating) {
-    ob->ob_registered = ob->ob_registering = 0;
+    ob->ob_registering = ob->ob_registered = 0;
     return 0;			/* Cleanup is done separately */
   }  
 
+  if (!response || !request)
+    return 0;
+
+  assert(request->sip_request); assert(response->sip_status);
+  
   reregister = outbound_check_for_nat(ob, request, response);
   if (reregister)
     return reregister;
@@ -423,7 +391,13 @@
 }
 
 
-/** @internal Check if there is a NAT between us and registrar */
+/** @internal Check if there is a NAT between us and registrar.
+ *
+ * @retval -1 upon an error
+ * @retval #ob_register_ok (0) if the registration was OK
+ * @retval #ob_reregister (1) if client needs to re-register
+ * @retval #ob_reregister_now (2) if client needs to re-register immediately
+ */
 static
 int outbound_check_for_nat(outbound_t *ob,
 			   sip_t const *request,
@@ -457,11 +431,11 @@
   if (!m || binding_changed >= ob_nat_changed) {
     if (ob->ob_stun) {
       /* Use STUN? */
-      return 1;
+      return ob_reregister;
     }
     else if (ob->ob_upnp) {
       /* Use UPnP */
-      return 1;
+      return ob_reregister;
     }
     else {
       if (outbound_contacts_from_via(ob, response->sip_via) < 0)
@@ -512,7 +486,7 @@
   if (!host_is_ip_address(received)) {
     if (received[0])
       SU_DEBUG_3(("outbound(%p): Via with invalid received=%s\n",
-		  ob->ob_owner, received));
+		  (void *)ob->ob_owner, received));
     return 0;
   }
 
@@ -530,14 +504,14 @@
 
   if (!nat_detected) {
     SU_DEBUG_1(("outbound(%p): detected NAT: %s != %s\n",
-		ob->ob_owner, v->v_host, received));
+		(void *)ob->ob_owner, v->v_host, received));
     if (ob->ob_oo && ob->ob_oo->oo_status)
       ob->ob_oo->oo_status(ob->ob_owner, ob, 101, "NAT detected", TAG_END());
   }
   else {
     SU_DEBUG_1(("outbound(%p): NAT binding changed: "
 		"[%s]:%s != [%s]:%s\n",
-		ob->ob_owner, nat_detected, nat_port, received, rport));
+		(void *)ob->ob_owner, nat_detected, nat_port, received, rport));
     if (ob->ob_oo && ob->ob_oo->oo_status)
       ob->ob_oo->oo_status(ob->ob_owner, ob, 102, "NAT binding changed", TAG_END());
   }
@@ -709,21 +683,32 @@
 {
   msg_t *msg = nta_msg_create(ob->ob_nta, MSG_FLG_COMPACT), *previous;
   sip_t *osip = sip_object(msg);
-  sip_accept_contact_t *ac;
-
-  char const *p1 = ob->ob_instance;
-  char const *p2 = ob->ob_features;
+  sip_contact_t *m = ob->ob_rcontact;
 
   unsigned d = ob->ob_keepalive.interval;
 
   assert(regsip); assert(regsip->sip_request);
 
-  if (p1 || p2) {
-    ac = sip_accept_contact_format(msg_home(msg), "*;require;explicit;%s%s%s",
-				   p1 ? p1 : "",
-				   p2 && p2 ? ";" : "",
-				   p2 ? p2 : "");
-    msg_header_insert(msg, NULL, (void *)ac);
+  if (m && m->m_params) {
+    sip_accept_contact_t *ac;
+    size_t i;
+    int features = 0;
+
+    ac = sip_accept_contact_make(msg_home(msg), "*;require;explicit");
+
+    for (i = 0; m->m_params[i]; i++) {
+      char const *s = m->m_params[i];
+      if (!sip_is_callerpref(s))
+	continue;
+      features++;
+      s = su_strdup(msg_home(msg), s);
+      msg_header_add_param(msg_home(msg), ac->cp_common, s);
+    }
+    
+    if (features)
+      msg_header_insert(msg, NULL, (void *)ac);
+    else
+      msg_header_free(msg_home(msg), (void *)ac);
   }
 
   if (0 >
@@ -877,18 +862,19 @@
     if (loglevel >= SU_LOG->log_level) {
       su_llog(SU_LOG, loglevel,       
 	      "outbound(%p): %s <" URL_PRINT_FORMAT ">\n",
-	      ob->ob_owner, failed ? "FAILED to validate" : "validated", 
+	      (void *)ob->ob_owner,
+	      failed ? "FAILED to validate" : "validated",
 	      URL_PRINT_ARGS(ob->ob_rcontact->m_url));
       if (failed)
 	su_llog(SU_LOG, loglevel, "outbound(%p): FAILED with %u %s\n", 
-		ob->ob_owner, status, phrase);
+		(void *)ob->ob_owner, status, phrase);
     }
 
     if (failed) 
       ob->ob_oo->oo_probe_error(ob->ob_owner, ob, status, phrase, TAG_END());
   }
   else if (status == 408) {
-    SU_DEBUG_1(("outbound(%p): keepalive timeout\n", ob->ob_owner));
+    SU_DEBUG_1(("outbound(%p): keepalive timeout\n", (void *)ob->ob_owner));
     ob->ob_oo->oo_keepalive_error(ob->ob_owner, ob, status, phrase, TAG_END());
     return 0;
   }
@@ -982,7 +968,7 @@
 
   if (ob->ob_keepalive.validating) {
     SU_DEBUG_1(("outbound(%p): registration check OPTIONS received\n", 
-		ob->ob_owner));
+		(void *)ob->ob_owner));
     ob->ob_keepalive.validated = 1;
   }
 
@@ -1018,48 +1004,16 @@
 
   v = v0; *v0 = *via; v0->v_next = NULL;
 
-  dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
+  dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 1,
 				   NULL, v, v->v_protocol, NULL);
 
   if (ob->ob_instance && ob->ob_reg_id != 0)
     snprintf(reg_id_param, sizeof reg_id_param, ";reg-id=%u", ob->ob_reg_id);
 
-  rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
+  rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 0,
 				   NULL, v, v->v_protocol, 
-				   ob->ob_features ? ob->ob_features : "",
 				   ob->ob_instance, reg_id_param, NULL);
     
-#if 0
-  char *uri;
-
-  /* uri contains < > */
-  uri = sip_contact_string_from_via(NULL, via, NULL, v->v_protocol);
-
-  dcontact = sip_contact_make(home, uri);
-
-
-
-  if (ob->ob_instance) {
-    char reg_id[20];
-
-    if (ob->ob_instance && ob->ob_reg_id)
-      snprintf(reg_id, sizeof reg_id, ";reg-id=%u", ob->ob_reg_id);
-    else
-      strcpy(reg_id, "");
-
-    rcontact = sip_contact_format(home, "%s;%s%s%s%s",
-				  uri, ob->ob_instance, reg_id,
-				  ob->ob_features ? ";" : "",
-				  ob->ob_features ? ob->ob_features : "");
-  }
-  else if (ob->ob_features)
-    rcontact = sip_contact_format(home, "%s;%s", uri, ob->ob_features);
-  else
-    rcontact = dcontact;
-
-  free(uri);
-#endif
-
   v = sip_via_dup(home, v);
 
   if (!rcontact || !dcontact || !v) {
@@ -1151,7 +1105,7 @@
     char const *tport = !v->v_next ? v->v_protocol : NULL; 
     char reg_id_param[20];
 
-    dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
+    dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 1, 
 				     NULL, v, tport, NULL);
     if (!dcontact)
       return -1;
@@ -1159,9 +1113,8 @@
     if (ob->ob_instance && ob->ob_reg_id != 0)
       snprintf(reg_id_param, sizeof reg_id_param, ";reg-id=%u", ob->ob_reg_id);
 
-    rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
+    rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 0,
 				     NULL, v, v->v_protocol, 
-				     ob->ob_features ? ob->ob_features : "",
 				     ob->ob_instance, reg_id_param, NULL);
     if (!rcontact)
       return -1;
@@ -1172,9 +1125,11 @@
       previous = ob->ob_contacts ? ob->ob_rcontact : NULL;
     }
   }
-
+    
   ob->ob_by_stack = application_contact == NULL;
 
+  ob->ob_contacts = rcontact != NULL;
+
   ob->ob_rcontact = rcontact;
   ob->ob_dcontact = dcontact;
   ob->ob_previous = previous;
@@ -1205,6 +1160,11 @@
     return ob->ob_dcontact;
 }
 
+sip_contact_t const *outbound_dialog_gruu(outbound_t const *ob)
+{
+  return ob ? ob->ob_gruu : NULL;
+}
+
 /* ---------------------------------------------------------------------- */
 
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/outbound.h	Tue Mar 20 23:37:15 2007
@@ -70,17 +70,11 @@
 			 unsigned dgram_interval,
 			 unsigned stream_interval);
 
-int outbound_set_features(outbound_t *ob, char *features);
+int outbound_get_contacts(outbound_t *ob, 
+			  sip_contact_t **return_current_contact, 
+			  sip_contact_t **return_previous_contact);
 
-nta_outgoing_t *outbound_register_request(outbound_t *ob, int terminating, 
-					  sip_contact_t *stack_contact,
-					  nta_agent_t *nta,
-					  nta_response_f *callback,
-					  nta_outgoing_magic_t *magic,
-					  url_string_t *next_hop,
-					  msg_t *msg,
-					  tag_type_t tag, tag_value_t value,
-					  ...);
+int outbound_start_registering(outbound_t *ob);
 
 int outbound_register_response(outbound_t *ob,
 			       int terminating,
@@ -102,6 +96,8 @@
 
 sip_contact_t const *outbound_dialog_contact(outbound_t const *ob);
 
+sip_contact_t const *outbound_dialog_gruu(outbound_t const *ob);
+
 int outbound_gruuize(outbound_t *ob, sip_t const *sip);
 
 void outbound_start_keepalive(outbound_t *ob,
@@ -122,6 +118,7 @@
   int oo_size;
   sip_contact_t *(*oo_contact)(outbound_owner_t *,
 			       su_home_t *home,
+			       int used_in_dialog,
 			       char const *extra_username,
 			       sip_via_t const *v,
 			       char const *transport,

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua.h	Tue Mar 20 23:37:15 2007
@@ -262,6 +262,12 @@
 /** Get name for NUA callstate. */
 SOFIAPUBFUN char const *nua_callstate_name(enum nua_callstate state);
 
+/** Return name of subscription state. @NEW_1_12_5. */
+SOFIAPUBFUN char const *nua_substate_name(enum nua_substate substate);
+
+/** Convert string to enum nua_substate. @NEW_1_12_5. */
+SOFIAPUBFUN enum nua_substate nua_substate_make(char const *sip_substate);
+
 /** Send SIP REGISTER request to the registrar. */ 
 SOFIAPUBFUN void nua_register(nua_handle_t *nh, tag_type_t, tag_value_t, ...);
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h	Tue Mar 20 23:37:15 2007
@@ -571,6 +571,7 @@
 #define NUTAG_MIN_SE_REF(x)     nutag_min_se_ref, tag_uint_vr((&(x)))
 SOFIAPUBVAR tag_typedef_t nutag_min_se_ref;
 
+/** Enumeration type of NUTAG_SESSION_REFRESHER(). */
 enum nua_session_refresher {
   nua_no_refresher,		/**< Disable session timer. */
   nua_local_refresher,		/**< Session refresh by local end. */
@@ -1074,7 +1075,7 @@
  *
  * The outbound option string can specify how the NAT traversal is handled.
  * The option tokens are as follows:
- * - "gruuize": try to generate a GRUU
+ * - "gruuize": try to generate a GRUU contact from REGISTER response
  * - "outbound": use SIP outbound extension (off by default)
  * - "validate": validate registration behind a NAT by sending OPTIONS to self
  * - "natify": try to traverse NAT
@@ -1084,6 +1085,10 @@
  * An option token with "no-" or "not-" prefix turns the option off. For
  * example, if you want to try to traverse NATs but not to use OPTIONS
  * keepalive, use NUTAG_OUTBOUND("natify no-options-keepalive").
+ * 
+ * An empty string can be passed to let the stack choose the
+ * default values for outbound usage (in the 1.12.5 release, the
+ * defaults are: "gruuize no-outbound validate use-port options-keepalive").
  *
  * @note
  * Options string is used so that no new tags need to be added when the
@@ -1207,7 +1212,7 @@
  *    nua_create()
  *
  * @par Parameter type
- *    msg_mclass_t *
+ *    msg_mclass_t const *
  *
  * @par Values
  *    Pointer to an extended SIP parser.
@@ -1244,6 +1249,9 @@
 
 /** Keepalive interval in milliseconds.
  *
+ * This setting applies to OPTIONS/STUN keepalives. See documentation 
+ * for nua_register() for more detailed information.
+ *
  * @par Used with
  *    nua_register()   \n
  *    nua_set_params() \n
@@ -1269,6 +1277,8 @@
 
 /** Transport-level keepalive interval for streams.
  *
+ * See documentation for nua_register() for more detailed information.
+ *
  * @par Used with
  *    nua_register()   \n
  *    nua_set_params() \n
@@ -1327,6 +1337,7 @@
  *
  * @par Used with
  *    nua_register(), nua_set_hparams(), nua_set_params().
+ *    nua_invite(), nua_respond(), nua_subscribe(), nua_notify()
  *
  * @par Parameter type
  *    string (char *)
@@ -1358,6 +1369,7 @@
  *
  * @par Used with
  *    nua_register(), nua_set_hparams(), nua_set_params().
+ *    nua_invite(), nua_respond(), nua_subscribe(), nua_notify()
  *
  * @par Parameter type
  *    string (char *)
@@ -1388,7 +1400,8 @@
  * user-agent.
  *
  * @par Used with
- *    nua_register(), nua_set_hparams(), nua_set_params().
+ *    nua_register(), nua_set_hparams(), nua_set_params(), 
+ *    nua_invite(), nua_respond(), nua_subscribe(), nua_notify()
  *
  * @par Parameter type
  *    string (char *)
@@ -1735,7 +1748,8 @@
  *
  * Corresponding tag taking reference parameter is NUTAG_APPL_METHOD_REF()
  *
- * @since Working since @VERSION_1_12_5. 
+ * @since Working since @VERSION_1_12_5. Handling of client-side PRACK and
+ * UPDATE was fixed in @VERSION_1_12_6.
  */
 #define NUTAG_APPL_METHOD(x)     nutag_appl_method, tag_str_v(x)
 SOFIAPUBVAR tag_typedef_t nutag_appl_method;
@@ -1859,26 +1873,39 @@
 /** Get name for NUA call state */
 SOFIAPUBFUN char const *nua_callstate_name(enum nua_callstate state);
 
-/** Subscription state
+/**Subscription state.
  *
  * @par Used with
+ *    #nua_notify() \n
  *    #nua_r_subscribe \n
- *    #nua_i_notify
+ *    #nua_i_notify \n
+ *    #nua_i_subscribe \n
+ *    #nua_r_notify \n
+ *    nua_notify() \n
+ *    nua_respond() to SUBSCRIBE
  *
  * @par Parameter type
  *    int
  *
  * @par Values
- *   @c nua_substate_embryonic (0) \n
- *   @c nua_substate_pending (1) \n
- *   @c nua_substate_active (2) \n
- *   @c nua_substate_terminated	(3) \n
- *
- * see
- * <a href="http://www.ietf.org/rfc/rfc3265.txt">RFC 3265</a>
+ *   - #nua_substate_embryonic (0)
+ *   - #nua_substate_pending (1)
+ *   - #nua_substate_active (2)
+ *   - #nua_substate_terminated (3)
+ *
+ * Note that the @SubscriptionState or @Expires headers specified by
+ * application overrides the subscription state specified by
+ * NUTAG_SUBSTATE(). Application can terminate subscription by including
+ * NUTAG_SUBSTATE(nua_substate_terminated), @SubscriptionState with value
+ * "terminated" or @Expires header with value 0 in the NOTIFY request sent
+ * by nua_notify().
+ *
+ * @sa @RFC3265, @SubscriptionState, SIPTAG_SUBSCRIPTION_STATE(),
+ * SIPTAG_SUBSCRIPTION_STATE_STR(), #nua_r_subscribe, #nua_i_subscribe,
+ * #nua_i_refer, #nua_r_notify, #nua_i_notify.
  *
  * Corresponding tag taking reference parameter is NUTAG_SUBSTATE_REF()
-*/
+ */
 #define NUTAG_SUBSTATE(x) nutag_substate, tag_int_v(x)
 SOFIAPUBVAR tag_typedef_t nutag_substate;
 
@@ -1896,6 +1923,46 @@
   nua_substate_terminated = nea_terminated /**< Terminated subscription */
 };
 
+/** Return name of subscription state. @NEW_1_12_5. */
+SOFIAPUBFUN char const *nua_substate_name(enum nua_substate substate);
+
+/** Convert string to enum nua_substate. @NEW_1_12_5. */
+SOFIAPUBFUN enum nua_substate nua_substate_make(char const *sip_substate);
+
+/**Send unsolicited NOTIFY request.
+ *
+ * Some applications may require sending unsolicited NOTIFY requests, that
+ * is, NOTIFY without SUBSCRIBE or REFER request sent by event watcher. 
+ * However, sending NOTIFY request requires an existing dialog usage by
+ * default. If NUTAG_NEWSUB(1) is included in the nua_notify() the usage
+ * is create the usage by itself.
+ *
+ * If you want to create a subscription that does not terminate immediately
+ * include SIPTAG_SUBSCRIPTION_STATE_STR() with an "expires" parameter in
+ * the argument list, too.
+ *
+ * @par Used with
+ *    nua_notify()
+ *
+ * @par Parameter type
+ *    int (boolean)
+ *
+ * @par Values
+ *   - 0 - false (default) - do not create new subscription 
+ *         but reject NOTIFY with 481 locally \n
+ *   - 1 - true - create a subscription if it does not exist \n
+ *
+ * Corresponding tag taking reference parameter is NUTAG_NEWSUB().
+ *
+ * @since @NEW_1_12_5.
+ */
+#define NUTAG_NEWSUB(x)   nutag_newsub, tag_bool_v(x)
+SOFIAPUBVAR tag_typedef_t nutag_newsub;
+
+#define NUTAG_NEWSUB_REF(x) nutag_newsub_ref, tag_bool_vr(&(x))
+SOFIAPUBVAR tag_typedef_t nutag_newsub_ref;
+
+
 /**Default lifetime for implicit subscriptions created by REFER.
  *
  * Default expiration time in seconds for implicit subscriptions created by

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c	Tue Mar 20 23:37:15 2007
@@ -101,24 +101,17 @@
 
   save_event_in_list(ctx, event, ep, call);
 
-  switch (event) {
-  case nua_i_prack:
-    if (200 <= status && status < 300) {
-      RESPOND(ep, call, nh, SIP_200_OK, 
-	      NUTAG_INCLUDE_EXTRA_SDP(1),
-	      TAG_END());
-      ep->next_condition = until_ready;
-    }
-  default:
-    break;
-  }
-
   switch (callstate(tags)) {
   case nua_callstate_received:
     RESPOND(ep, call, nh, SIP_180_RINGING,
 	    TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
 	    TAG_END());
+    RESPOND(ep, call, nh, SIP_200_OK, 
+	    NUTAG_INCLUDE_EXTRA_SDP(1),
+	    TAG_END());
     return 0;
+  case nua_callstate_ready:
+    return 1;
   case nua_callstate_terminated:
     if (call)
       nua_handle_destroy(call->nh), call->nh = NULL;
@@ -552,6 +545,8 @@
   }
 }
 
+int respond_483_to_prack(CONDITION_PARAMS);
+
 int test_183rel(struct context *ctx)
 {
   BEGIN();
@@ -661,30 +656,110 @@
   if (print_headings)
     printf("TEST NUA-10.2.1: PASSED\n");
 
+  /* Test for graceful termination by client because 483 sent to PRACK */
   if (print_headings)
-    printf("TEST NUA-10.2.2: terminate call\n");
+    printf("TEST NUA-10.2.2: graceful termination because PRACK fails\n");
 
-  BYE(b, b_call, b_call->nh, TAG_END());
-  run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+  nua_set_hparams(b_call->nh, NUTAG_APPL_METHOD("PRACK"),
+		  NUTAG_AUTOANSWER(0), TAG_END());
+  run_b_until(ctx, nua_r_set_params, NULL);
 
-  /* B transitions:
-   READY --(T2)--> TERMINATING: nua_bye()
-   TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+  INVITE(a, a_call, a_call->nh, TAG_END());
+
+  run_ab_until(ctx, -1, until_terminated, -1, respond_483_to_prack);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C2)-> TERMINATING: nua_r_invite, nua_i_state, 
+                                  nua_r_prack, nua_i_state
+     TERMINATING -(T1)-> TERMINATED: nua_r_invite, nua_i_state
   */
-  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
+  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, 183);
+
   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);
+  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(e->data->e_status, 483);
 
-  /* A: READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state */
-  TEST_1(e = a->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(callstate(e->data->e_tags), nua_callstate_terminating);
+
+  {
+    int bye = 1, cancel = 1, invite = 1;
+
+    while (bye || cancel || invite) {
+      TEST_1(e = e->next); 
+      if (e->data->e_event == nua_r_bye) {
+	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);
+	bye = 0;
+      }
+      else if (e->data->e_event == nua_r_invite) {
+	TEST_E(e->data->e_event, nua_r_invite);
+	TEST(e->data->e_status, 487);
+	invite = 0;
+      }
+      else if (e->data->e_event == nua_r_cancel) {
+	TEST_E(e->data->e_event, nua_r_cancel);
+	TEST_1(e->data->e_status == 200 || e->data->e_status == 481);
+	cancel = 0;
+      }
+    }
+  }
   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, nua_respond(to PRACK)
+   EARLY -(S3b)-> 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 183 Session Progress */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(e->data->e_status, 183);
+  TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
+  TEST_1(is_answer_sent(e->data->e_tags));
+
+  /* 183 is PRACKed, PRACK is responded with 483 */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_prack);
+
+  /* Client terminates the call 
+     - we may (it is received before BYE) or may not (received after BYE) 
+       get CANCEL request 
+  */
+  TEST_1(e = e->next);
+  if (e->data->e_event == nua_i_cancel) {
+    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_ready);
+    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);
+  TEST_1(!e->next);
+
+  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;
 
@@ -694,6 +769,39 @@
   END();
 }
 
+int respond_483_to_prack(CONDITION_PARAMS)
+{
+  if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+    return 0;
+
+  switch (event) {
+  case nua_i_prack:
+    if (status <= 200) {
+      RESPOND(ep, call, nh, 483, "Foo",
+	      NUTAG_WITH_THIS(nua),
+	      TAG_END());
+    }
+  default:
+    break;
+  }
+
+  save_event_in_list(ctx, event, ep, call);
+
+  switch (callstate(tags)) {
+  case nua_callstate_received:
+    RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS,
+	    TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+	    TAG_END());
+    return 0;
+  case nua_callstate_terminated:
+    if (call)
+      nua_handle_destroy(call->nh), call->nh = NULL;
+    return 1;
+  default:
+    return 0;
+  }
+}
+
 /*
  X  ringing_updated   ep
  |-------INVITE------>|
@@ -1578,6 +1686,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 +2001,28 @@
   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),
+		 SIPTAG_SUPPORTED(ctx->a.supported),
+		 TAG_END());
+  run_a_until(ctx, nua_r_set_params, until_final_response);
+
+  nua_set_params(ctx->b.nua,
+		 NUTAG_EARLY_MEDIA(0),
+		 NUTAG_ONLY183_100REL(0),
+		 SIPTAG_SUPPORTED(ctx->b.supported),
+		 TAG_END());
+  run_b_until(ctx, nua_r_set_params, until_final_response);
+
+  nua_set_params(ctx->c.nua,
+		 NUTAG_EARLY_MEDIA(0),
+		 NUTAG_ONLY183_100REL(0),
+		 SIPTAG_SUPPORTED(ctx->c.supported),
+		 TAG_END());
+  run_c_until(ctx, nua_r_set_params, until_final_response);
 
   return retval;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_basic_call.c	Tue Mar 20 23:37:15 2007
@@ -117,6 +117,8 @@
   case nua_callstate_received:
     RESPOND(ep, call, nh, SIP_180_RINGING, 
 	    TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+	    NUTAG_M_DISPLAY("Bob"),
+	    NUTAG_M_USERNAME("b+b"),
 	    TAG_END());
     return 0;
   case nua_callstate_early:
@@ -217,6 +219,8 @@
   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_M_USERNAME("a+a"),
+	 NUTAG_M_DISPLAY("Alice"),
 	 TAG_END());
 
   run_ab_until(ctx, -1, until_ready, -1, accept_call_with_early_sdp);
@@ -263,8 +267,11 @@
   TEST_1(is_answer_recv(e->data->e_tags));
   TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
   TEST(e->data->e_status, 200);
-  TEST_1(sip->sip_payload);
   TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_payload);
+  TEST_1(sip->sip_contact);
+  TEST_S(sip->sip_contact->m_display, "Bob");
+  TEST_S(sip->sip_contact->m_url->url_user, "b+b");
   /* Test that B uses application-specific contact */
   if (ctx->proxy_tests)
     TEST_1(sip->sip_contact->m_url->url_user);
@@ -282,6 +289,10 @@
   */
   TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
   TEST(e->data->e_status, 100);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_contact);
+  TEST_S(sip->sip_contact->m_display, "Alice");
+  TEST_S(sip->sip_contact->m_url->url_user, "a+a");
   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));
@@ -404,6 +415,7 @@
   INVITE(a, a_call, a_call->nh,
 	 NUTAG_URL(b->contact->m_url),
 	 SOATAG_USER_SDP_STR(a_call->sdp),
+	 NUTAG_ALLOW("INFO"),
 	 TAG_END());
 
   run_ab_until(ctx, -1, until_ready, -1, accept_early_answer);
@@ -466,32 +478,96 @@
   TEST_1(nua_handle_has_active_call(b_call->nh));
   TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
 
+  /* Send a NOTIFY from B to A */
+  if (print_headings)
+    printf("TEST NUA-3.2.2: send a NOTIFY within a dialog\n");
+
+  /* Make A to accept NOTIFY */
+  nua_set_params(a->nua, NUTAG_APPL_METHOD("NOTIFY"), TAG_END());
+  run_a_until(ctx, nua_r_set_params, until_final_response);
+
+  NOTIFY(b, b_call, b_call->nh,
+	 NUTAG_NEWSUB(1),
+	 SIPTAG_SUBJECT_STR("NUA-3.2.2"),
+	 SIPTAG_EVENT_STR("message-summary"),
+	 SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+	 SIPTAG_PAYLOAD_STR("Messages-Waiting: no"),
+	 TAG_END());
+
+  run_ab_until(ctx, -1, accept_notify, -1, save_until_final_response);
+
+  /* Notifier events: nua_r_notify */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_notify);
+  TEST(e->data->e_status, 200);
+  TEST_1(tl_find(e->data->e_tags, nutag_substate));
+  TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
+       nua_substate_terminated);
+
+  /* watcher events: nua_i_notify */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_subscription_state);
+  TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+  TEST_1(tl_find(e->data->e_tags, nutag_substate));
+  TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, 
+       nua_substate_terminated);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+  free_events_in_list(ctx, b->events);
+
+  if (print_headings)
+    printf("TEST NUA-3.2.2: PASSED\n");
+
+  INFO(b, b_call, b_call->nh, TAG_END());
   BYE(b, b_call, b_call->nh, TAG_END());
+  INFO(b, b_call, b_call->nh, TAG_END());
+
   run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
 
+  while (!b->events->head || /* r_info */
+	 !b->events->head->next || /* r_bye */
+	 !b->events->head->next->next || /* i_state */
+	 !b->events->head->next->next->next) /* r_info */
+    run_ab_until(ctx, -1, save_events, -1, save_until_final_response);
+
   /* B transitions:
+   nua_info()
    READY --(T2)--> TERMINATING: nua_bye()
+   nua_r_info with 200
    TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+   nua_r_info with 481/900
   */
-  TEST_1(e = b->events->head);  TEST_E(e->data->e_event, nua_r_bye);
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_info);
+  TEST_1(e = e->next); 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 = e->next); TEST_E(e->data->e_event, nua_r_info);
+  TEST_1(e->data->e_status >= 900 || e->data->e_status == 481);
   TEST_1(!e->next);
   free_events_in_list(ctx, b->events);
 
   TEST_1(!nua_handle_has_active_call(b_call->nh));
 
   /* A transitions:
+     nua_i_info
      READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
   */
-  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_info);
+  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);
 
-  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  BYE(a, a_call, a_call->nh, TAG_END());
+  run_a_until(ctx, -1, save_until_final_response);
+
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
+  TEST_1(e->data->e_status >= 900);
+  TEST_1(!e->next);
+  free_events_in_list(ctx, a->events);
 
   nua_handle_destroy(a_call->nh), a_call->nh = NULL;
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
@@ -502,8 +578,376 @@
   END();
 }
 
+/* ====================================================================== */
+
+/* Basic calls with soa disabled */
+
+int accept_call_no_media(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_received:
+    RESPOND(ep, call, nh, SIP_180_RINGING, 
+	    NUTAG_MEDIA_ENABLE(0),
+	    TAG_END());
+    return 0;
+  case nua_callstate_early:
+    RESPOND(ep, call, nh, SIP_200_OK,
+	    TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+	    TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+	    TAG_END());
+    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;
+  }
+}
+
+int test_basic_call_3(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;
+  sip_t const *sip;
+
+  if (print_headings)
+    printf("TEST NUA-3.3: Basic call with media disabled\n");
+
+  a_call->sdp = "v=0\r\n"
+    "o=- 1 1 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 5008 RTP/AVP 8\r\n";
+
+  b_call->sdp = 
+    "v=0\r\n"
+    "o=- 2 1 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 5010 RTP/AVP 0 8\r\n";
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, 
+				 SIPTAG_TO_STR("<sip:b at x.org>"),
+				 TAG_END()));
+
+  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  INVITE(a, a_call, a_call->nh,
+	 NUTAG_MEDIA_ENABLE(0),
+	 NUTAG_URL(b->contact->m_url),
+	 SIPTAG_CONTENT_TYPE_STR("application/sdp"),
+	 SIPTAG_PAYLOAD_STR(a_call->sdp),
+	 TAG_END());
+
+  run_ab_until(ctx, -1, until_ready, -1, accept_call_no_media);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+     PROCEEDING -(C3+C4)-> READY: 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); /* PROCEEDING */
+  TEST_1(!is_answer_recv(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 200);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_content_type); 
+  TEST_S(sip->sip_content_type->c_type, "application/sdp");
+  TEST_1(sip->sip_payload);	/* there is sdp in 200 OK */
+  TEST_1(sip->sip_contact);
+  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_answer_recv(e->data->e_tags));
+  TEST_1(!e->next);
+  free_events_in_list(ctx, a->events);
+
+  TEST_1(nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  /*
+   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
+   COMPLETED -(S4)-> READY: nua_i_ack, 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(!is_answer_sent(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_completed); /* COMPLETED */
+  TEST_1(is_answer_sent(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(!e->next);
+  free_events_in_list(ctx, b->events);
+
+  TEST_1(nua_handle_has_active_call(b_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+  BYE(b, b_call, b_call->nh, TAG_END());
+
+  run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+  /* B transitions:
+   READY --(T2)--> TERMINATING: nua_bye()
+   TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+  */
+  TEST_1(e = b->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, b->events);
+
+  TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+  /* A transitions:
+     nua_i_info
+     READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+  */
+  TEST_1(e = a->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, a->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-3.3: PASSED\n");
+
+  END();
+}
+
+int ack_when_completing_no_media(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(ep, call, nh, 
+	TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+	TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+	TAG_END());
+    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;
+  }
+}
+
+int accept_call_no_media2(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_received:
+    RESPOND(ep, call, nh, SIP_180_RINGING, 
+	    NUTAG_MEDIA_ENABLE(0),
+	    TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+	    TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+	    TAG_END());
+    return 0;
+  case nua_callstate_early:
+    RESPOND(ep, call, nh, SIP_200_OK,
+	    TAG_IF(call->sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
+	    TAG_IF(call->sdp, SIPTAG_PAYLOAD_STR(call->sdp)),
+	    TAG_END());
+    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;
+  }
+}
+
+/* Media disabled, offer/answer in 200 OK/ACK */
+int test_basic_call_4(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;
+  sip_t const *sip;
+
+  if (print_headings)
+    printf("TEST NUA-3.4: 3pcc call with media disabled\n");
+
+  a_call->sdp = "v=0\r\n"
+    "o=- 1 1 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 5008 RTP/AVP 8\r\n";
+
+  b_call->sdp = 
+    "v=0\r\n"
+    "o=- 2 1 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 5010 RTP/AVP 0 8\r\n";
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, 
+				 SIPTAG_TO_STR("<sip:b at x.org>"),
+				 TAG_END()));
+
+  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  INVITE(a, a_call, a_call->nh,
+	 NUTAG_MEDIA_ENABLE(0),
+	 NUTAG_URL(b->contact->m_url),
+	 NUTAG_AUTOACK(0),
+	 TAG_END());
+
+  run_ab_until(ctx, -1, ack_when_completing_no_media,
+	       -1, accept_call_no_media2);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+     PROCEEDING -(C3+C4)-> READY: 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); /* PROCEEDING */
+  TEST_1(!is_answer_recv(e->data->e_tags));
+  TEST_1(is_offer_recv(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 200);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_content_type); 
+  TEST_S(sip->sip_content_type->c_type, "application/sdp");
+  TEST_1(sip->sip_payload);	/* there is sdp in 200 OK */
+  TEST_1(sip->sip_contact);
+  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_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_ready); /* READY */
+  TEST_1(is_answer_sent(e->data->e_tags));
+  TEST_1(!e->next);
+  free_events_in_list(ctx, a->events);
+
+  TEST_1(nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  /*
+   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
+   COMPLETED -(S4)-> READY: nua_i_ack, 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(!is_answer_sent(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_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
+  TEST_1(is_offer_sent(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_answer_recv(e->data->e_tags));
+  TEST_1(!e->next);
+  free_events_in_list(ctx, b->events);
+
+  TEST_1(nua_handle_has_active_call(b_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+  BYE(b, b_call, b_call->nh, TAG_END());
+
+  run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+
+  /* B transitions:
+   READY --(T2)--> TERMINATING: nua_bye()
+   TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
+  */
+  TEST_1(e = b->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, b->events);
+
+  TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+  /* A transitions:
+     nua_i_info
+     READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
+  */
+  TEST_1(e = a->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, a->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-3.4: PASSED\n");
+
+  END();
+}
 
 int test_basic_call(struct context *ctx)
 {
-  return test_basic_call_1(ctx) || test_basic_call_2(ctx);
+  return test_basic_call_1(ctx) || test_basic_call_2(ctx) || 
+    test_basic_call_3(ctx) || test_basic_call_4(ctx);
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_hold.c	Tue Mar 20 23:37:15 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/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_call_reject.c	Tue Mar 20 23:37:15 2007
@@ -638,11 +638,12 @@
   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 = e->next); TEST_E(e->data->e_event, nua_i_invite);
+  TEST(e->data->e_status, 100);
   TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_proxy_authorization);
+  /* Ensure that nua_authenticate() tags get added to the request */
   TEST_1(sip->sip_subject);
   TEST_S(sip->sip_subject->g_value, "Got 407");
-  TEST_1(sip->sip_proxy_authorization);
-  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));
@@ -911,7 +912,7 @@
 
   /*
    Client transitions in reject-3:
-   INIT -(C1)-> PROCEEDING -(C6a)-> TERMINATED
+   INIT -(C1)-> CALLING -(C6a)-> TERMINATED
   */
 
   TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
@@ -927,7 +928,7 @@
   /* No content-encoding is supported */
   TEST_S(sip->sip_accept_encoding->aa_value, "");
   TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
-  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* CALLING */
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
   TEST_1(!e->next);
 
   free_events_in_list(ctx, a->events);
@@ -944,3 +945,320 @@
 
   END();
 }
+
+/* ---------------------------------------------------------------------- */
+
+size_t filter_200_OK(void *arg, void *message, size_t len)
+{
+  (void)arg;
+
+  if (len >= 11 && strncasecmp(message, "SIP/2.0 200", 11) == 0)
+    return 0;
+  return len;
+}
+
+size_t filter_ACK(void *arg, void *message, size_t len)
+{
+  (void)arg;
+
+  if (len >= 7 && strncasecmp(message, "ACK sip", 7) == 0) 
+    return 0;
+  return len;
+}
+
+int call_with_bad_ack(CONDITION_PARAMS);
+int accept_call_with_bad_contact(CONDITION_PARAMS);
+
+int test_call_timeouts(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;
+
+  if (print_headings)
+    printf("TEST NUA-4.7: check for error and timeout handling\n");
+
+  a_call->sdp = "m=audio 5008 RTP/AVP 8";
+  b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
+
+  if (!ctx->nat)
+    goto completed_4_7_1;
+
+  if (print_headings)
+    printf("TEST NUA-4.7.1: ACK timeout (200 OK filtered)\n");
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  TEST_1(f = test_nat_add_filter(ctx->nat, filter_200_OK, NULL, nat_inbound));
+  
+  INVITE(a, a_call, a_call->nh,
+	 TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+	 SIPTAG_SUBJECT_STR("NUA-4.7.1"),
+	 SOATAG_USER_SDP_STR(a_call->sdp),
+	 TAG_END());
+  run_ab_until(ctx, -1, until_terminated, -1, accept_call);
+
+  /*
+ A     accept_call    B
+ |                    |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |                    |
+ |<----180 Ringing----|
+ |                    |
+ |   X-----200--------|
+ |   X-----200--------|
+ |   X-----200--------|
+ |                    |
+ |<--------BYE--------|
+ |--------200 OK----->|
+
+  */
+
+  /*
+    Client transitions:
+  */
+
+  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(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_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 */
+  TEST_1(!e->next);
+
+  /*
+   Server transitions:
+   -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+   -(S10)-> TERMINATED -X
+  */
+  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 = e->next); TEST_E(e->data->e_event, nua_i_error);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+  TEST_1(e = e->next); 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);
+  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;
+
+  TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+
+  if (print_headings)
+    printf("TEST NUA-4.7.1: PASSED\n");
+
+ completed_4_7_1:
+
+  if (!ctx->nat)
+    goto completed_4_7_2;
+
+  if (print_headings)
+    printf("TEST NUA-4.7.2: ACK timeout (ACK filtered)\n");
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  TEST_1(f = test_nat_add_filter(ctx->nat, filter_ACK, NULL, nat_outbound));
+  
+  INVITE(a, a_call, a_call->nh,
+	 TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+	 SIPTAG_SUBJECT_STR("NUA-4.7.2"),
+	 SOATAG_USER_SDP_STR(a_call->sdp),
+	 TAG_END());
+  run_ab_until(ctx, -1, until_terminated, -1, accept_call);
+
+  /*
+ A     accept_call    B
+ |                    |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |                    |
+ |<----180 Ringing----|
+ |                    |
+ |<--------200--------|
+ |--------ACK-----X   |
+ |                    |
+ |<--------200--------|
+ |--------ACK-----X   |
+ |                    |
+ |<--------200--------|
+ |--------ACK-----X   |
+ |                    |
+ |<--------BYE--------|
+ |--------200 OK----->|
+
+  */
+
+  /*
+  */
+
+  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(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_ready); /* READY */
+  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); 
+  TEST_1(!e->next);
+
+  /*
+   Server transitions:
+   -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+   -(S10)-> TERMINATED -X
+  */
+  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 = e->next); TEST_E(e->data->e_event, nua_i_error);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+  TEST_1(e = e->next); 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);
+  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;
+
+  TEST_1(test_nat_remove_filter(ctx->nat, f) == 0);
+
+  if (print_headings)
+    printf("TEST NUA-4.7.2: PASSED\n");
+
+ completed_4_7_2:
+
+  if (print_headings)
+    printf("TEST NUA-4.7.3: sending ACK fails\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)),
+	 SIPTAG_SUBJECT_STR("NUA-4.7.3"),
+	 SOATAG_USER_SDP_STR(a_call->sdp),
+	 NUTAG_AUTOACK(0),
+	 TAG_END());
+  run_ab_until(ctx, -1, call_with_bad_ack, -1, accept_call);
+
+  /*
+ A     accept_call    B
+ |                    |
+ |-------INVITE------>|
+ |<----100 Trying-----|
+ |                    |
+ |<----180 Ringing----|
+ |                    |
+ |<--------200--------|
+ |--ACK-X             |
+ |                    |
+ |---------BYE------->|
+ |<-------200 OK------|
+
+  */
+
+  /*
+  */
+
+  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(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); /* COMPLETING */
+  /* try to send ACK */
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminating);
+  TEST_1(e = e->next); 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); 
+  TEST_1(!e->next);
+
+  /*
+   Server transitions:
+   -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S5)-> TERMINATING
+   -(S10)-> TERMINATED -X
+  */
+  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 = 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 */
+  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-4.7.3: PASSED\n");
+
+  /* XXX - PRACK timeout, PRACK failing, media failing, re-INVITEs */
+
+  if (print_headings)
+    printf("TEST NUA-4.7: PASSED\n");
+
+  END();
+}
+
+int call_with_bad_ack(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 && 200 <= status && status < 300) {
+    ACK(ep, call, nh,
+	/* Syntax error - sending ACK fails, we send BYE */
+	SIPTAG_MAX_FORWARDS_STR("blue"),
+	TAG_END());
+  }
+
+  return event == nua_i_state && callstate(tags) == nua_callstate_terminated;
+}

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c	Tue Mar 20 23:37:15 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:
@@ -144,6 +146,7 @@
   }
 }
 
+int accept_after_183(CONDITION_PARAMS);
 
 int test_call_cancel(struct context *ctx)
 {
@@ -211,7 +214,7 @@
   /* ------------------------------------------------------------------------ */
 
   if (print_headings)
-    printf("TEST NUA-5.2: cancel call when ringing\n");
+    printf("TEST NUA-5.2.1: cancel call when ringing\n");
 
   TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
 
@@ -278,11 +281,56 @@
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
 
   if (print_headings)
-    printf("TEST NUA-5.2: PASSED\n");
+    printf("TEST NUA-5.2.1: PASSED\n");
+
+  /* ------------------------------------------------------------------------ */
+  if (print_headings)
+    printf("TEST NUA-5.2.2: CANCEL call when server waits for PRACK\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)),
+	 SOATAG_USER_SDP_STR(a_call->sdp),
+	 NUTAG_APPL_METHOD("PRACK"),
+	 /*SIPTAG_REJECT_CONTACT_STR("*;audio=FALSE"),*/
+	 TAG_END());
+
+  run_ab_until(ctx, -1, cancel_when_ringing, -1, accept_after_183);
+
+  free_events_in_list(ctx, a->events);
+  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-5.2.2: PASSED\n");
+
 
   END();
 }
 
+int accept_after_183(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_received:
+    RESPOND(ep, call, nh, SIP_183_SESSION_PROGRESS, TAG_END());
+    RESPOND(ep, call, nh, SIP_200_OK, TAG_END());
+    return 0;
+  case nua_callstate_terminated:
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+
 /* ======================================================================== */
 /* Destroy call handle */
 
@@ -496,13 +544,13 @@
     return 0;
   case nua_callstate_early:
     if (call)
-      nua_handle_destroy(call->nh), call->nh = NULL;
+      DESTROY(ep, call, nh), call->nh = NULL;
     return 1;
   case nua_callstate_completed:
   case nua_callstate_ready:
   case nua_callstate_terminated:
     if (call)
-      nua_handle_destroy(call->nh), call->nh = NULL;
+      DESTROY(ep, call, nh), call->nh = NULL;
     return 1;
   default:
     return 0;
@@ -594,7 +642,7 @@
   case nua_callstate_ready:
   case nua_callstate_terminated:
     if (call)
-      nua_handle_destroy(call->nh), call->nh = NULL;
+      DESTROY(ep, call, nh), call->nh = NULL;
     return 1;
   default:
     return 0;
@@ -673,6 +721,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 +809,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 +857,30 @@
   }
 }
 
+int bye_when_completing(CONDITION_PARAMS);
+
+static int ack_sent = 0;
+
+size_t count_acks(void *arg, void *message, size_t len)
+{
+  (void)arg;
+
+  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 +950,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, NULL, 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/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_extension.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_extension.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_extension.c	Tue Mar 20 23:37:15 2007
@@ -167,6 +167,13 @@
   free_events_in_list(ctx, b->events);
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
 
+  nua_set_params(b->nua,
+		 SIPTAG_ALLOW(b->allow),
+		 NUTAG_APPL_METHOD(NULL),
+		 NUTAG_APPL_METHOD(b->appl_method),
+		 TAG_END());
+  run_b_until(ctx, nua_r_set_params, until_final_response);
+
   if (print_headings)
     printf("TEST NUA-13.1: PASSED\n");
   END();

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c	Tue Mar 20 23:37:15 2007
@@ -35,6 +35,8 @@
 
 #include "test_nua.h"
 
+#include <sofia-sip/tport_tag.h>
+
 #if HAVE_FUNC
 #elif HAVE_FUNCTION
 #define __func__ __FUNCTION__
@@ -65,6 +67,9 @@
   struct event *e;
   sip_contact_t const *m = NULL;
   sip_from_t const *sipaddress = NULL;
+  sip_allow_t const *allow = NULL;
+  sip_supported_t const *supported = NULL;
+  char const *appl_method = NULL;
   url_t const *p_uri, *a_uri;		/* Proxy URI */
   char const *a_bind, *a_bind2;
 
@@ -106,6 +111,7 @@
 			       AUTHTAG_DB(passwd_name),
 			       AUTHTAG_QOP("auth-int"),
 			       AUTHTAG_ALGORITHM("md5-sess"),
+			       TAG_IF(ctx->proxy_logging, TPTAG_LOG(1)),
 			       TAG_END());
 
     ctx->proxy_tests = ctx->p != NULL;
@@ -218,22 +224,29 @@
 			  NUTAG_URL(a_bind),
 			  TAG_IF(a_bind != a_bind2, NUTAG_SIPS_URL(a_bind2)),
 			  SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8"),
-			  NTATAG_SIP_T1X64(4000),
+			  NTATAG_SIP_T1X64(2000),
 			  NUTAG_INSTANCE(ctx->a.instance),
+			  TAG_IF(ctx->a.logging, TPTAG_LOG(1)),
 			  TAG_END());
   TEST_1(ctx->a.nua);
 
   nua_get_params(ctx->a.nua, TAG_ANY(), TAG_END());
   run_a_until(ctx, nua_r_get_params, save_until_final_response);
-  TEST_1(e = ctx->a.events->head);
+  TEST_1(e = ctx->a.specials->head);
   TEST(tl_gets(e->data->e_tags,
 	       NTATAG_CONTACT_REF(m),
 	       SIPTAG_FROM_REF(sipaddress),
-	       TAG_END()), 2); TEST_1(m);
+	       SIPTAG_ALLOW_REF(allow),
+	       NUTAG_APPL_METHOD_REF(appl_method),
+	       SIPTAG_SUPPORTED_REF(supported),
+	       TAG_END()), 5); TEST_1(m);
   TEST_1(ctx->a.contact = sip_contact_dup(ctx->home, m));
   TEST_1(ctx->a.to = sip_to_dup(ctx->home, sipaddress));
+  TEST_1(ctx->a.allow = sip_allow_dup(ctx->home, allow));
+  TEST_1(ctx->a.appl_method = su_strdup(ctx->home, appl_method));
+  TEST_1(ctx->a.supported = sip_supported_dup(ctx->home, supported));
 
-  free_events_in_list(ctx, ctx->a.events);
+  free_events_in_list(ctx, ctx->a.specials);
 
   if (print_headings)
     printf("TEST NUA-2.2.1: PASSED\n");
@@ -249,19 +262,30 @@
 			  NUTAG_URL("sip:0.0.0.0:*"),
 			  SOATAG_USER_SDP_STR("m=audio 5006 RTP/AVP 8 0"),
 			  NUTAG_INSTANCE(ctx->b.instance),
+			  /* Quicker timeout */
+			  NTATAG_SIP_T1X64(2000),
+			  TAG_IF(ctx->b.logging, TPTAG_LOG(1)),
 			  TAG_END());
   TEST_1(ctx->b.nua);
 
   nua_get_params(ctx->b.nua, TAG_ANY(), TAG_END());
   run_b_until(ctx, nua_r_get_params, save_until_final_response);
-  TEST_1(e = ctx->b.events->head);
+  TEST_1(e = ctx->b.specials->head);
   TEST(tl_gets(e->data->e_tags,
 	       NTATAG_CONTACT_REF(m),
 	       SIPTAG_FROM_REF(sipaddress),
-	       TAG_END()), 2); TEST_1(m);
+	       SIPTAG_ALLOW_REF(allow),
+	       NUTAG_APPL_METHOD_REF(appl_method),
+	       SIPTAG_SUPPORTED_REF(supported),
+	       TAG_END()), 5); TEST_1(m);
+
   TEST_1(ctx->b.contact = sip_contact_dup(ctx->home, m));
   TEST_1(ctx->b.to = sip_to_dup(ctx->home, sipaddress));
-  free_events_in_list(ctx, ctx->b.events);
+  TEST_1(ctx->b.allow = sip_allow_dup(ctx->home, allow));
+  TEST_1(ctx->b.appl_method = su_strdup(ctx->home, appl_method));
+  TEST_1(ctx->b.supported = sip_supported_dup(ctx->home, supported));
+
+  free_events_in_list(ctx, ctx->b.specials);
 
   if (print_headings)
     printf("TEST NUA-2.2.2: PASSED\n");
@@ -277,19 +301,26 @@
 			  NUTAG_URL("sip:0.0.0.0:*"),
 			  SOATAG_USER_SDP_STR("m=audio 5400 RTP/AVP 8 0"),
 			  NUTAG_INSTANCE(ctx->c.instance),
+			  TAG_IF(ctx->c.logging, TPTAG_LOG(1)),
 			  TAG_END());
   TEST_1(ctx->c.nua);
 
   nua_get_params(ctx->c.nua, TAG_ANY(), TAG_END());
   run_c_until(ctx, nua_r_get_params, save_until_final_response);
-  TEST_1(e = ctx->c.events->head);
+  TEST_1(e = ctx->c.specials->head);
   TEST(tl_gets(e->data->e_tags,
 	       NTATAG_CONTACT_REF(m),
 	       SIPTAG_FROM_REF(sipaddress),
-	       TAG_END()), 2); TEST_1(m);
+	       SIPTAG_ALLOW_REF(allow),
+	       NUTAG_APPL_METHOD_REF(appl_method),
+	       SIPTAG_SUPPORTED_REF(supported),
+	       TAG_END()), 5); TEST_1(m);
   TEST_1(ctx->c.contact = sip_contact_dup(ctx->home, m));
   TEST_1(ctx->c.to = sip_to_dup(ctx->home, sipaddress));
-  free_events_in_list(ctx, ctx->c.events);
+  TEST_1(ctx->c.allow = sip_allow_dup(ctx->home, allow));
+  TEST_1(ctx->c.appl_method = su_strdup(ctx->home, appl_method));
+  TEST_1(ctx->c.supported = sip_supported_dup(ctx->home, supported));
+  free_events_in_list(ctx, ctx->c.specials);
 
   if (print_headings)
     printf("TEST NUA-2.2.3: PASSED\n");

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.c	Tue Mar 20 23:37:15 2007
@@ -91,6 +91,8 @@
 
   struct binding *bindings;
 
+  struct nat_filter *in_filters, *out_filters;
+
   /* True if we act in symmetric way */
   int symmetric;
   /* True if we do logging */
@@ -153,6 +155,15 @@
 
 static int invalidate_binding(struct binding *b);
 
+LIST_PROTOS(static, nat_filter, struct nat_filter);
+
+struct nat_filter
+{
+  struct nat_filter *next, **prev;
+  size_t (*condition)(void *arg, void *message, size_t len);
+  void *arg;
+};
+
 /* nat entry point */
 static int
 test_nat_init(su_root_t *root, struct nat *nat)
@@ -670,19 +681,35 @@
 {
   int events;
   ssize_t n, m;
+  size_t len, filtered;
+  struct nat_filter *f;
 
   events = su_wait_events(wait, b->in_socket);
 
   n = su_recv(b->in_socket, nat->buffer, sizeof nat->buffer, 0);
-  if (n < 0) {
+  if (n == -1) {
     su_perror("udp_in_to_out: recv");
     return 0;
   }
 
+  len = (size_t)n;
+
+  for (f = nat->out_filters; f; f = f->next) {
+    filtered = f->condition(f->arg, nat->buffer, len);
+    if (filtered != len) {
+      if (nat->logging)
+	printf("nat: udp filtered "MOD_ZU" from %s => "MOD_ZU" to %s\n",
+	       len, b->in_name, filtered, b->out_name);
+      if (filtered == 0)
+	return 0;
+      len = filtered;
+    }
+  }
+
   if (nat->symmetric)
-    m = su_send(b->out_socket, nat->buffer, n, 0);
+    m = su_send(b->out_socket, nat->buffer, len, 0);
   else
-    m = su_sendto(b->out_socket, nat->buffer, n, 0,
+    m = su_sendto(b->out_socket, nat->buffer, len, 0,
 		  nat->out_address, nat->out_addrlen);
 
   if (nat->logging)
@@ -696,6 +723,8 @@
 {
   int events;
   ssize_t n, m;
+  size_t len, filtered;
+  struct nat_filter *f;
 
   events = su_wait_events(wait, b->out_socket);
 
@@ -705,6 +734,20 @@
     return 0;
   }
 
+  len = (size_t)n;
+
+  for (f = nat->in_filters; f; f = f->next) {
+    filtered = f->condition(f->arg, nat->buffer, len);
+    if (filtered != len) {
+      if (nat->logging)
+	printf("nat: udp filtered "MOD_ZU" from %s => "MOD_ZU" to %s\n",
+	       len, b->out_name, filtered, b->in_name);
+      if (filtered == 0)
+	return 0;
+      len = filtered;
+    }
+  }
+
   m = su_send(b->in_socket, nat->buffer, n, 0);
 
   if (nat->logging)
@@ -882,3 +925,75 @@
 
   return 0;
 }
+
+LIST_BODIES(static, nat_filter, struct nat_filter, next, prev);
+
+struct args {
+  struct nat *nat;
+  struct nat_filter *f;
+  int outbound;
+};
+
+int execute_nat_filter_insert(void *_args)
+{
+  struct args *a = (struct args *)_args;
+  if (a->outbound)
+    nat_filter_insert(&a->nat->out_filters, a->f);
+  else
+    nat_filter_insert(&a->nat->in_filters, a->f);
+  return 0;
+}
+
+int execute_nat_filter_remove(void *_args)
+{
+  struct args *a = (struct args *)_args;
+  nat_filter_remove(a->f);
+  return 0;
+}
+
+struct nat_filter *test_nat_add_filter(struct nat *nat,
+				       size_t (*condition)(void *arg,
+							   void *message,
+							   size_t len),
+				       void *arg,
+				       int outbound)
+{
+  struct args a[1];
+
+  if (nat == NULL)
+    return su_seterrno(EFAULT), NULL;
+
+  a->nat = nat;
+  a->f = su_zalloc(nat->home, sizeof *a->f);
+  a->outbound = outbound;
+
+  if (a->f) {
+    a->f->condition = condition;
+    a->f->arg = arg;
+    if (su_task_execute(su_clone_task(nat->clone),
+			execute_nat_filter_insert, a, NULL) < 0)
+      su_free(nat->home, a->f), a->f = NULL;
+  }
+
+  return a->f;
+}
+
+
+int test_nat_remove_filter(struct nat *nat,
+			   struct nat_filter *filter)
+{
+  struct args a[1];
+
+  if (nat == NULL)
+    return su_seterrno(EFAULT);
+
+  a->nat = nat;
+  a->f = filter;
+  
+  if (su_task_execute(su_clone_task(nat->clone),
+		      execute_nat_filter_remove, a, NULL) < 0)
+    return -1;
+
+  su_free(nat->home, filter);
+  return 0;
+}

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nat.h	Tue Mar 20 23:37:15 2007
@@ -31,6 +31,7 @@
 SOFIA_BEGIN_DECLS
 
 struct nat;
+struct nat_filter;
 
 struct nat *test_nat_create(su_root_t *, int family, 
 			    tag_type_t, tag_value_t, ...);
@@ -42,6 +43,18 @@
 
 int test_nat_flush(struct nat *nat);
 
+struct nat_filter *test_nat_add_filter(struct nat *nat,
+				       size_t (*condition)(void *arg,
+							   void *message,
+							   size_t len),
+				       void *arg,
+				       int outbound);
+
+enum { nat_inbound, nat_outbound };
+
+int test_nat_remove_filter(struct nat *nat,
+			   struct nat_filter *filter);
+
 /* Tags */
 
 /** If true, act as symmetric nat. */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.c	Tue Mar 20 23:37:15 2007
@@ -72,20 +72,29 @@
 static char const options_usage[] =
   "   -v | --verbose    be verbose\n"
   "   -q | --quiet      be quiet\n"
+  "   -a | --abort      abort on error\n" 
   "   -s                use only single thread\n"
   "   -l level          set logging level (0 by default)\n"
   "   -e | --events     print nua events\n"
   "   -A                print nua events for A\n"
   "   -B                print nua events for B\n"
   "   -C                print nua events for C\n"
+  "   --log=a           log messages for A\n"
+  "   --log=b           log messages for B\n"
+  "   --log=c           log messages for C\n"
+  "   --log=proxy       log messages for proxy\n"
   "   --attach          print pid, wait for a debugger to be attached\n"
   "   --no-proxy        do not use internal proxy\n"
   "   --no-nat          do not use internal \"nat\"\n"
   "   --symmetric       run internal \"nat\" in symmetric mode\n"
   "   -N                print events from internal \"nat\"\n"
+  "   --loop            loop main tests for ever\n"
   "   --no-alarm        don't ask for guard ALARM\n"
   "   -p uri            specify uri of outbound proxy (implies --no-proxy)\n"
   "   --proxy-tests     run tests involving proxy, too\n"
+#if SU_HAVE_OSX_CF_API /* If compiled with CoreFoundation events */
+  "   --osx-runloop     use OSX CoreFoundation runloop instead of poll() loop\n"
+#endif
   "   -k                do not exit after first error\n"
   ;
 
@@ -99,7 +108,7 @@
 int main(int argc, char *argv[])
 {
   int retval = 0;
-  int i, o_quiet = 0, o_attach = 0, o_alarm = 1;
+  int i, o_quiet = 0, o_attach = 0, o_alarm = 1, o_loop = 0;
   int o_events_init = 0, o_events_a = 0, o_events_b = 0, o_events_c = 0;
   int o_iproxy = 1, o_inat = 1;
   int o_inat_symmetric = 0, o_inat_logging = 0, o_expensive = 0;
@@ -199,6 +208,21 @@
     else if (strcmp(argv[i], "--no-alarm") == 0) {
       o_alarm = 0;
     }
+    else if (strcmp(argv[i], "--loop") == 0) {
+      o_alarm = 0, o_loop = 1;
+    }
+    else if (strcmp(argv[i], "--log=a") == 0) {
+      ctx->a.logging = 1;
+    }
+    else if (strcmp(argv[i], "--log=b") == 0) {
+      ctx->b.logging = 1;
+    }
+    else if (strcmp(argv[i], "--log=c") == 0) {
+      ctx->c.logging = 1;
+    }
+    else if (strcmp(argv[i], "--log=proxy") == 0) {
+      ctx->proxy_logging = 1;
+    }
 #if SU_HAVE_OSX_CF_API /* If compiled with CoreFoundation events */
     else if (strcmp(argv[i], "--osx-runloop") == 0) {
       ctx->osx_runloop = 1;
@@ -210,8 +234,10 @@
     else if (argv[i][0] != '-') {
       break;
     }
-    else
+    else {
+      fprintf(stderr, "test_nua: unknown argument \"%s\"\n\n", argv[i]);
       usage(1);
+    }
   }
 
   if (o_attach) {
@@ -279,24 +305,28 @@
     if (retval == 0 && o_inat)
       retval |= test_nat_timeout(ctx);
 
-    if (retval == 0) {
-      retval |= test_extension(ctx); SINGLE_FAILURE_CHECK();
+    while (retval == 0) {
       retval |= test_basic_call(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_reject_a(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_reject_b(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_reject_302(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_reject_401(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_mime_negotiation(ctx); SINGLE_FAILURE_CHECK();
+      retval |= test_call_timeouts(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_reject_401_aka(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_call_cancel(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_call_destroy(ctx); SINGLE_FAILURE_CHECK();
+      retval |= test_offer_answer(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_early_bye(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_reinvites(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_session_timer(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_refer(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_100rel(ctx); SINGLE_FAILURE_CHECK();
-      retval |= test_simple(ctx); SINGLE_FAILURE_CHECK();
       retval |= test_events(ctx); SINGLE_FAILURE_CHECK();
+      retval |= test_simple(ctx); SINGLE_FAILURE_CHECK();
+      retval |= test_extension(ctx); SINGLE_FAILURE_CHECK();
+      if (!o_loop)
+	break;
     }
 
     if (ctx->proxy_tests && (retval == 0 || !ctx->p))

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua.h	Tue Mar 20 23:37:15 2007
@@ -126,10 +126,14 @@
   int threading, proxy_tests, expensive, quit_on_single_failure, osx_runloop;
   char const *external_proxy;
 
+  int proxy_logging;
+
   struct endpoint {
     char name[4];
     struct context *ctx;	/* Backpointer */
 
+    int logging;
+
     int running;
 
     condition_function *next_condition;
@@ -138,6 +142,10 @@
     sip_contact_t *contact;
     sip_from_t *to;
 
+    sip_allow_t *allow;
+    char const *appl_method;
+    sip_supported_t *supported;
+
     printer_function *printer;
 
     char const *instance;
@@ -158,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;
@@ -185,6 +192,9 @@
 		       struct call *);
 void free_events_in_list(struct context *,
 			 struct eventlist *);
+void free_event_in_list(struct context *ctx,
+			struct eventlist *list,
+			struct event *e);
 
 #define CONDITION_PARAMS			\
   nua_event_t event,				\
@@ -204,6 +214,9 @@
 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);
 
 void a_callback(nua_event_t event,
 		int status, char const *phrase,
@@ -316,11 +329,13 @@
 int test_unregister(struct context *ctx);
 
 int test_basic_call(struct context *ctx);
+int test_offer_answer(struct context *ctx);
 int test_reject_a(struct context *ctx);
 int test_reject_b(struct context *ctx);
 int test_reject_302(struct context *ctx);
 int test_reject_401(struct context *ctx);
 int test_mime_negotiation(struct context *ctx);
+int test_call_timeouts(struct context *ctx);
 int test_reject_401_aka(struct context *ctx);
 int test_call_cancel(struct context *ctx);
 int test_call_destroy(struct context *ctx);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c	Tue Mar 20 23:37:15 2007
@@ -117,12 +117,12 @@
   nua_get_params(ctx->a.nua, TAG_ANY(), TAG_END());
   run_a_until(ctx, nua_r_get_params, save_until_final_response);
 
-  TEST_1(e = ctx->a.events->head);
+  TEST_1(e = ctx->a.specials->head);
   TEST_E(e->data->e_event, nua_r_get_params);
   for (n = 0, t = e->data->e_tags; t; n++, t = tl_next(t))
     ;
   TEST_1(n > 32);
-  free_events_in_list(ctx, ctx->a.events);
+  free_events_in_list(ctx, ctx->a.specials);
 
   nh = nua_handle(ctx->a.nua, NULL, TAG_END()); TEST_1(nh);
   nua_handle_unref(nh);
@@ -148,9 +148,12 @@
 
 		 SIPTAG_SUPPORTED_STR("test"),
 		 SIPTAG_ALLOW_STR("DWIM, OPTIONS, INFO"),
+		 NUTAG_APPL_METHOD(NULL),
+		 NUTAG_APPL_METHOD("INVITE, REGISTER, PUBLISH, SUBSCRIBE"),
 		 SIPTAG_ALLOW_EVENTS_STR("reg"),
 		 SIPTAG_USER_AGENT_STR("test_nua/1.0"),
 
+
 		 SIPTAG_ORGANIZATION_STR("Open Laboratory"),
 		 
 		 NUTAG_M_DISPLAY("XXX"),
@@ -211,10 +214,13 @@
 		 SIPTAG_ALLOW(sip_allow_make(tmphome, "INFO")),
 		 NUTAG_ALLOW("ACK, INFO"),
 
+		 NUTAG_APPL_METHOD("NOTIFY"),
+
 		 SIPTAG_ALLOW_EVENTS_STR("reg"),
 		 SIPTAG_ALLOW_EVENTS(sip_allow_events_make(tmphome, "presence")),
 		 NUTAG_ALLOW_EVENTS("presence.winfo"),
 
+
 		 SIPTAG_USER_AGENT(sip_user_agent_make(tmphome, "test_nua")),
 
 		 SIPTAG_ORGANIZATION(sip_organization_make(tmphome, "Pussy Galore's Flying Circus")),
@@ -268,6 +274,7 @@
 
     sip_allow_t const *allow = NONE;
     char const *allow_str = "NONE";
+    char const *appl_method = "NONE";
     sip_allow_events_t const *allow_events = NONE;
     char const *allow_events_str = "NONE";
     sip_supported_t const *supported = NONE;
@@ -291,7 +298,7 @@
     nua_get_params(ctx->a.nua, TAG_ANY(), TAG_END());
     run_a_until(ctx, nua_r_get_params, save_until_final_response);
 
-    TEST_1(e = ctx->a.events->head);
+    TEST_1(e = ctx->a.specials->head);
     TEST_E(e->data->e_event, nua_r_get_params);
 
     n = tl_gets(e->data->e_tags,
@@ -332,6 +339,7 @@
 	       	SIPTAG_SUPPORTED_STR_REF(supported_str),
 	       	SIPTAG_ALLOW_REF(allow),
 	       	SIPTAG_ALLOW_STR_REF(allow_str),
+		NUTAG_APPL_METHOD_REF(appl_method),
 		SIPTAG_ALLOW_EVENTS_REF(allow_events),
 		SIPTAG_ALLOW_EVENTS_STR_REF(allow_events_str),
 	       	SIPTAG_USER_AGENT_REF(user_agent),
@@ -353,7 +361,7 @@
 		NUTAG_INSTANCE_REF(instance),
 
 		TAG_END());
-    TEST(n, 46);
+    TEST(n, 47);
 
     TEST_S(sip_header_as_string(tmphome, (void *)from), Alice);
     TEST_S(from_str, Alice);
@@ -389,6 +397,7 @@
 
     TEST_S(sip_header_as_string(tmphome, (void *)allow), "OPTIONS, INFO, ACK");
     TEST_S(allow_str, "OPTIONS, INFO, ACK");
+    TEST_S(appl_method, "INVITE, REGISTER, PUBLISH, SUBSCRIBE, NOTIFY");
     TEST_S(sip_header_as_string(tmphome, (void *)allow_events), 
 	   "reg, presence, presence.winfo");
     TEST_S(allow_events_str, "reg, presence, presence.winfo");
@@ -414,7 +423,7 @@
     TEST_S(m_features, expect_m_features); }
     TEST_S(outbound, "foo");
 
-    free_events_in_list(ctx, ctx->a.events);
+    free_events_in_list(ctx, ctx->a.specials);
   }
 
   /* Test that only those tags that have been set per handle are returned by nua_get_hparams() */

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_offer_answer.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_offer_answer.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,369 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE test_offer_answer.c
+ * @brief Test offer/answer failures.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ *
+ * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
+ */
+
+#include "config.h"
+
+#include "test_nua.h"
+#include <sofia-sip/su_tag_class.h>
+
+#if HAVE_FUNC
+#elif HAVE_FUNCTION
+#define __func__ __FUNCTION__
+#else
+#define __func__ "test_offer_answer"
+#endif
+
+
+/* ======================================================================== */
+
+/* Send offer in INVITE, no answer:
+
+			   A			B
+          --nua_invite()-->|                    |
+                           |-------INVITE------>|
+          <--nua_i_state---|                    |
+                           |<----100 Trying-----|
+                           |                    |
+                           |<--------180--------|
+          <--nua_r_invite--|                    |
+          <--nua_i_state---|                    |
+                           |<------200 OK-------|
+          <--nua_r_invite--|                    |
+                           |--------ACK-------->|
+                           |--------BYE-------->|
+          <--nua_i_state---|                    |
+                           |<-------200 OK------|
+          <--nua_r_bye-----|                    |
+          <--nua_i_state---|                    |
+
+   Client transitions:
+   INIT -(C1)-> CALLING -(C2a)-> PROCEEDING -(C3+C4)-> 
+   TERMINATING -> TERMINATED
+   Server transitions:
+   INIT -(S1)-> RECEIVED -(S2a)-> EARLY -(S3b)-> COMPLETED -(S4)-> READY
+   -> TERMINATED
+
+*/
+
+int no_media_terminated(CONDITION_PARAMS);
+
+int test_no_answer_1(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;
+  sip_t *sip;
+
+  if (print_headings)
+    printf("TEST NUA-6.3: No SDP answer from callee\n");
+
+  a_call->sdp = "m=audio 5008 RTP/AVP 8";
+  b_call->sdp = NULL;
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  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_M_USERNAME("a+a"),
+	 NUTAG_M_DISPLAY("Alice"),
+	 TAG_END());
+
+  run_ab_until(ctx, -1, until_terminated, -1, no_media_terminated);
+
+  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  TEST_1(!nua_handle_has_active_call(b_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C3)-> PROCEEDING: nua_r_invite, nua_i_state 
+     PROCEEDING -(C3a+C5)-> TERMINATING: nua_r_invite, nua_i_state 
+     TERMINATING -(C12)-> TERMINATED: nua_r_bye, 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(sip = sip_object(e->data->e_msg));
+  TEST_1(!sip->sip_payload);
+  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(!is_answer_recv(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 200);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(!sip->sip_payload);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_media_error);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+  TEST_1(e = e->next); 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);
+
+  /*
+   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
+   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(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_contact);
+  TEST_S(sip->sip_contact->m_display, "Alice");
+  TEST_S(sip->sip_contact->m_url->url_user, "a+a");
+  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(!is_answer_sent(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_completed); /* COMPLETED */
+  TEST_1(!is_answer_sent(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
+  TEST_1(sip = sip_object(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_ready); /* READY */
+  TEST_1(!is_answer_recv(e->data->e_tags));
+  TEST_1(!is_offer_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_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+  free_events_in_list(ctx, b->events);
+
+  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_active_call(b_call->nh));
+
+  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-6.3: PASSED\n");
+
+  END();
+}
+
+int no_media_terminated(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_received:
+    RESPOND(ep, call, nh, SIP_180_RINGING, 
+	    NUTAG_MEDIA_ENABLE(0),
+	    TAG_END());
+    return 0;
+  case nua_callstate_early:
+    RESPOND(ep, call, nh, SIP_200_OK,
+	    NUTAG_MEDIA_ENABLE(0),
+	    TAG_END());
+    return 0;
+  case nua_callstate_terminated:
+    if (call)
+      nua_handle_destroy(call->nh), call->nh = NULL;
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+int accept_call_until_terminated(CONDITION_PARAMS);
+
+/* No offer in INVITE, offer in 200 OK, no answer in ACK */
+int test_no_answer_2(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;
+  sip_t const *sip;
+
+  if (print_headings)
+    printf("TEST NUA-6.4: No SDP offer from caller\n");
+
+  a_call->sdp = "v=0\r\n"
+    "o=- 1 1 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 5008 RTP/AVP 8\r\n";
+
+  b_call->sdp = 
+    "v=0\r\n"
+    "o=- 2 1 IN IP4 127.0.0.1\r\n"
+    "s=-\r\n"
+    "c=IN IP4 127.0.0.1\r\n"
+    "t=0 0\r\n"
+    "m=audio 5010 RTP/AVP 0 8\r\n";
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, 
+				 SIPTAG_TO_STR("<sip:b at x.org>"),
+				 TAG_END()));
+
+  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  INVITE(a, a_call, a_call->nh,
+	 NUTAG_MEDIA_ENABLE(0),
+	 NUTAG_URL(b->contact->m_url),
+	 NUTAG_AUTOACK(1),
+	 TAG_END());
+
+  run_ab_until(ctx, -1, until_terminated,
+	       -1, accept_call_until_terminated);
+
+  /* Client transitions:
+     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
+     CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
+     PROCEEDING -(C3+C4)-> READY: 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); /* PROCEEDING */
+  TEST_1(!is_answer_recv(e->data->e_tags));
+  TEST_1(!is_offer_recv(e->data->e_tags));
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
+  TEST(e->data->e_status, 200);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_content_type); 
+  TEST_S(sip->sip_content_type->c_type, "application/sdp");
+  TEST_1(sip->sip_payload);	/* there is sdp in 200 OK */
+  TEST_1(sip->sip_contact);
+  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_answer_sent(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); /* READY */
+  TEST_1(!e->next);
+  free_events_in_list(ctx, a->events);
+
+  TEST_1(!nua_handle_has_active_call(a_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
+
+  /*
+   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
+   COMPLETED -(S4)-> READY: nua_i_ack, 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(!is_answer_sent(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_completed); /* COMPLETED */
+  TEST_1(is_offer_sent(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_media_error);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
+  TEST(callstate(e->data->e_tags), nua_callstate_terminating); /* TERMINATING */
+  TEST_1(e = e->next); 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, b->events);
+
+  TEST_1(!nua_handle_has_active_call(b_call->nh));
+  TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
+
+  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-6.4: PASSED\n");
+
+  END();
+}
+
+int accept_call_until_terminated(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_received:
+    RESPOND(ep, call, nh, SIP_180_RINGING, 
+	    TAG_END());
+    return 0;
+  case nua_callstate_early:
+    RESPOND(ep, call, nh, SIP_200_OK,
+	    TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
+	    TAG_END());
+    return 0;
+  case nua_callstate_terminated:
+    if (call)
+      nua_handle_destroy(call->nh), call->nh = NULL;
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+
+
+int test_offer_answer(struct context *ctx)
+{
+  return test_no_answer_1(ctx) || test_no_answer_2(ctx);
+}

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_ops.c	Tue Mar 20 23:37:15 2007
@@ -44,7 +44,7 @@
 
 int save_events(CONDITION_PARAMS)
 {
-  return save_event_in_list(ctx, event, ep, ep->call) == event_is_normal;
+  return save_event_in_list(ctx, event, ep, call) == event_is_normal;
 }
 
 int until_final_response(CONDITION_PARAMS)
@@ -54,7 +54,7 @@
 
 int save_until_final_response(CONDITION_PARAMS)
 {
-  save_event_in_list(ctx, event, ep, ep->call);
+  save_event_in_list(ctx, event, ep, call);
   return event >= nua_r_set_params && status >= 200;
 }
 
@@ -64,13 +64,13 @@
  */
 int save_until_received(CONDITION_PARAMS)
 {
-  return save_event_in_list(ctx, event, ep, ep->call) == event_is_normal;
+  return save_event_in_list(ctx, event, ep, call) == event_is_normal;
 }
 
 /** Save events until nua_i_outbound is received.  */
 int save_until_special(CONDITION_PARAMS)
 {
-  return save_event_in_list(ctx, event, ep, ep->call) == event_is_special;
+  return save_event_in_list(ctx, event, ep, call) == event_is_special;
 }
 
 /* Return call state from event tag list */
@@ -143,34 +143,49 @@
 		 sip_t const *sip,
 		 tagi_t tags[])
 {
+  tagi_t const *t;
+
   if (event == nua_i_state) {
     fprintf(stderr, "%s.nua(%p): event %s %s\n",
-	    ep->name, nh, nua_event_name(event),
+	    ep->name, (void *)nh, nua_event_name(event),
 	    nua_callstate_name(callstate(tags)));
   }
   else if ((int)event >= nua_r_set_params) {
-    fprintf(stderr, "%s.nua(%p): event %s status %u %s\n",
-	    ep->name, nh, nua_event_name(event), status, phrase);
+    t = tl_find(tags, nutag_substate);
+    if (t) {
+      fprintf(stderr, "%s.nua(%p): event %s status %u %s (%s)\n",
+	      ep->name, (void*)nh, nua_event_name(event), status, phrase,
+	      nua_substate_name(t->t_value));
+    }
+    else {
+      fprintf(stderr, "%s.nua(%p): event %s status %u %s\n",
+	      ep->name, (void *)nh, nua_event_name(event), status, phrase);
+    }
+  }
+  else if (event == nua_i_notify) {
+    t = tl_find(tags, nutag_substate);
+    fprintf(stderr, "%s.nua(%p): event %s %s (%s)\n",
+	    ep->name, (void *)nh, nua_event_name(event), phrase,
+	    nua_substate_name(t ? t->t_value : 0));
   }
   else if ((int)event >= 0) {
     fprintf(stderr, "%s.nua(%p): event %s %s\n",
-	    ep->name, nh, nua_event_name(event), phrase);
+	    ep->name, (void *)nh, nua_event_name(event), phrase);
   }
   else if (status > 0) {
     fprintf(stderr, "%s.nua(%p): call %s() with status %u %s\n",
-	    ep->name, nh, operation, status, phrase);
+	    ep->name, (void *)nh, operation, status, phrase);
   }
   else {
-    tagi_t const *t;
     t = tl_find(tags, siptag_subject_str);
     if (t && t->t_value) {
       char const *subject = (char const *)t->t_value;
       fprintf(stderr, "%s.nua(%p): call %s() \"%s\"\n",
-	      ep->name, nh, operation, subject);
+	      ep->name, (void *)nh, operation, subject);
     }
     else
       fprintf(stderr, "%s.nua(%p): call %s()\n",
-	      ep->name, nh, operation);
+	      ep->name, (void *)nh, operation);
   }
 
   if ((tstflags & tst_verbatim) && tags)
@@ -202,10 +217,11 @@
     }
   }
 
-  if ((ep->next_event == -1 || ep->next_event == event) &&
-      (ep->next_condition == NULL ||
+  if ((ep->next_condition == NULL ||
        ep->next_condition(event, status, phrase,
-			  nua, ctx, ep, nh, call, sip, tags)))
+			  nua, ctx, ep, nh, call, sip, tags))
+      &&
+      (ep->next_event == -1 || ep->next_event == event))
     ep->running = 0;
 
   ep->last_event = event;
@@ -256,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);
@@ -379,7 +395,7 @@
   return 0;
 }
 
-/* Destroy an handle */
+/* Destroy a handle */
 int DESTROY(struct endpoint *ep,
 	    struct call *call,
 	    nua_handle_t *nh)
@@ -450,7 +466,7 @@
   return action;
 }
 
-/* Save nua event in endpoint list */
+/* Free nua events from endpoint list */
 void free_events_in_list(struct context *ctx,
 			 struct eventlist *list)
 {
@@ -466,6 +482,21 @@
   list->tail = &list->head;
 }
 
+void free_event_in_list(struct context *ctx,
+			struct eventlist *list,
+			struct event *e)
+{
+  if (e) {
+    if ((*e->prev = e->next))
+      e->next->prev = e->prev;
+    nua_destroy_event(e->saved_event);
+    su_free(ctx->home, e);
+
+    if (list->head == NULL)
+      list->tail = &list->head;
+  }
+}			      
+
 int is_special(nua_event_t e)
 {
   if (e == nua_i_active || e == nua_i_terminated)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c	Tue Mar 20 23:37:15 2007
@@ -228,9 +228,10 @@
 				  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_END());
+				  TAG_NEXT(proxy->tags));
 
   proxy->transport_contacts = create_transport_contacts(proxy);
 
@@ -308,7 +309,7 @@
   free(proxy->tags);
 }
 
-/* Create tst proxy object */
+/* Create test proxy object */
 struct proxy *test_proxy_create(su_root_t *root,
 				tag_type_t tag, tag_value_t value, ...)
 {

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_refer.c	Tue Mar 20 23:37:15 2007
@@ -43,21 +43,22 @@
 #define __func__ "test_call_hold"
 #endif
 
-int accept_call_immediately(CONDITION_PARAMS);
-
 /* ======================================================================== */
 /* NUA-9 tests: REFER */
 
-int test_refer0(struct context *ctx, int refer_with_id, char const *tests);
-int test_refer1(struct context *ctx, int refer_with_id, char const *tests);
+int test_refer0(struct context *ctx, char const *tests,
+		int refer_with_id, int notify_by_appl);
+
+int notify_until_terminated(CONDITION_PARAMS);
 
 int test_refer(struct context *ctx)
 {
   /* test twice, once without id and once with id */
   return
-    test_refer0(ctx, 0, "NUA-9.1") ||
-    test_refer0(ctx, 1, "NUA-9.2") ||
-    test_refer1(ctx, 0, "NUA-9.3");
+    test_refer0(ctx, "NUA-9.1", 0, 0) ||
+    test_refer0(ctx, "NUA-9.2", 1, 0) ||
+    test_refer0(ctx, "NUA-9.3", 0, 1) ||
+    test_refer0(ctx, "NUA-9.4", 1, 1);
 }
 
 /* Referred call:
@@ -103,13 +104,15 @@
 
 */
 
-int test_refer0(struct context *ctx, int refer_with_id, char const *tests)
+int test_refer0(struct context *ctx, char const *tests,
+		int refer_with_id, int notify_by_appl)
 {
   BEGIN();
 
   struct endpoint *a = &ctx->a,  *b = &ctx->b, *c = &ctx->c;
   struct call *a_call = a->call, *b_call = b->call, *c_call = c->call;
-  struct call *a_c2;
+  struct call *a_refer, *a_c2, *b_refer;
+  struct eventlist *a_revents, *b_revents;
   struct event *e;
   sip_t const *sip;
   sip_event_t const *a_event, *b_event;
@@ -124,7 +127,10 @@
   su_home_auto(tmphome, sizeof(tmphome));
 
   if (print_headings)
-    printf("TEST %s: REFER: refer A to C\n", tests);
+    printf("TEST %s: REFER: refer A to C%s%s%s\n", tests,
+	   refer_with_id ? " with Event id" : "",
+	   refer_with_id && !notify_by_appl ? " and" : "",
+	   !notify_by_appl ? " nua generating the NOTIFYs" : "");
 
   if (print_headings)
     printf("TEST %s.1: REFER: make a call between A and B\n", tests);
@@ -133,6 +139,28 @@
   nua_set_params(ctx->a.nua, NUTAG_REFER_WITH_ID(refer_with_id), TAG_END());
   run_a_until(ctx, nua_r_set_params, until_final_response);
 
+  if (refer_with_id) {
+    TEST_1(a_refer = calloc(1, (sizeof *a_refer) + (sizeof *a_refer->events)));
+    call_init(a_refer);
+    a_refer->events = (void *)(a_refer + 1);
+    eventlist_init(a_refer->events);
+
+    a_call->next = a_refer;
+    a_revents = a_refer->events;
+
+    TEST_1(b_refer = calloc(1, (sizeof *b_refer) + (sizeof *b_refer->events)));
+    call_init(b_refer);
+    b_refer->events = (void *)(b_refer + 1);
+    eventlist_init(b_refer->events);
+
+    b_call->next = b_refer;
+    b_revents = b_refer->events;
+  }
+  else {
+    a_refer = a_call, b_refer = b_call;
+    a_revents = a->events, b_revents = b->events;
+  }
+
   TEST_1(a_c2 = calloc(1, (sizeof *a_c2) + (sizeof *a_c2->events)));
   call_init(a_c2);
   a_c2->events = (void *)(a_c2 + 1);
@@ -198,7 +226,7 @@
     printf("TEST %s.1: PASSED\n", tests);
 
   /* ---------------------------------------------------------------------- */
-  /* REFER (initial NOTIFY is no more sent)
+  /* REFER (initial NOTIFY is no more sent unless REFER creates a new dialog)
    A                    B
    |<------REFER--------|
    |-------200 OK------>|
@@ -207,13 +235,20 @@
    */
 
   if (print_headings)
-    printf("TEST %s.2: refer A to C\n", tests);
+    printf("TEST %s.2: B refers A to C\n", tests);
+
+  if (b_refer != b_call)
+    TEST_1(b_refer->nh = 
+	   nua_handle(b->nua, b_refer, SIPTAG_TO(a->to), TAG_END()));
 
   *sip_refer_to_init(r0)->r_url = *c->contact->m_url;
   r0->r_url->url_headers = "subject=referred";
   r0->r_display = "C";
 
-  REFER(b, b_call, b_call->nh, SIPTAG_REFER_TO(r0), TAG_END());
+  REFER(b, b_refer, b_refer->nh, SIPTAG_REFER_TO(r0),
+	TAG_IF(!ctx->proxy_tests && b_refer != b_call,
+	       NUTAG_URL(a->contact->m_url)),
+	TAG_END());
   run_ab_until(ctx, -1, save_until_received,
 	       -1, save_until_final_response);
 
@@ -221,7 +256,7 @@
     Events in A:
     nua_i_refer
   */
-  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_refer);
+  TEST_1(e = a_revents->head); TEST_E(e->data->e_event, nua_i_refer);
   TEST(e->data->e_status, 202);
   a_event = NULL;
   TEST(tl_gets(e->data->e_tags,
@@ -233,17 +268,12 @@
   TEST_1(refer_to = sip_refer_to_dup(tmphome, sip->sip_refer_to));
   TEST_1(sip->sip_referred_by);
   TEST_1(referred_by = sip_referred_by_dup(tmphome, sip->sip_referred_by));
-  if (e->next) {
-  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
-  TEST_1(!e->next);
-  }
-  free_events_in_list(ctx, a->events);
 
   /*
      Events in B after nua_refer():
      nua_r_refer
   */
-  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_refer);
+  TEST_1(e = b_revents->head); TEST_E(e->data->e_event, nua_r_refer);
   TEST(e->data->e_status, 100);
   TEST(tl_gets(e->data->e_tags,
 	       NUTAG_REFER_EVENT_REF(b_event),
@@ -254,22 +284,32 @@
   TEST(e->data->e_status, 202);
   TEST_1(sip = sip_object(e->data->e_msg));
   TEST_SIZE(strtoul(b_event->o_id, NULL, 10), sip->sip_cseq->cs_seq);
-#if 0
-  if (!e->next)
-    run_b_until(ctx, -1, save_until_received);
-  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
-  TEST(e->data->e_status, 200);
-  TEST_1(sip = sip_object(e->data->e_msg));
-  TEST_1(sip->sip_event);
-  if (refer_with_id)
-    TEST_S(sip->sip_event->o_id, b_event->o_id);
-  TEST_1(sip->sip_subscription_state);
-  TEST_S(sip->sip_subscription_state->ss_substate, "pending");
-  TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
-  TEST_S(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n");
-  TEST_1(!e->next);
-#endif
-  free_events_in_list(ctx, b->events);
+
+  if (a_refer != a_call) {
+    if (b_revents->head->next->next == NULL)
+      run_ab_until(ctx, -1, save_until_received, nua_i_notify, save_events);
+    else if (a_revents->head->next == NULL)
+      run_a_until(ctx, -1, save_until_received);
+
+    TEST_1(e = a_revents->head->next); TEST_E(e->data->e_event, nua_r_notify);
+    TEST_1(!e->next);
+
+    TEST_1(e = b_revents->head->next->next);
+    TEST_E(e->data->e_event, nua_i_notify);
+    TEST(e->data->e_status, 200);
+    TEST_1(sip = sip_object(e->data->e_msg));
+    TEST_1(sip->sip_event);
+    if (refer_with_id)
+      TEST_S(sip->sip_event->o_id, b_event->o_id);
+    TEST_1(sip->sip_subscription_state);
+    TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+    TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+    TEST_S(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n");
+    TEST_1(!e->next);
+  }
+
+  free_events_in_list(ctx, a_revents);
+  free_events_in_list(ctx, b_revents);
 
   if (print_headings)
     printf("TEST %s.2: PASSED\n", tests);
@@ -371,42 +411,43 @@
   *sip_to_init(to)->a_url = *refer_to->r_url;
   to->a_display = refer_to->r_display;
 
-  a->call->next = a_c2;
+  a_refer->next = a_c2;
 
   TEST_1(a_c2->nh = nua_handle(a->nua, a_c2, SIPTAG_TO(to), TAG_END()));
 
   INVITE(a, a_c2, a_c2->nh, /* NUTAG_URL(refer_to->r_url), */
-	 NUTAG_REFER_EVENT(a_event),
-	 NUTAG_NOTIFY_REFER(a_call->nh),
+	 TAG_IF(!notify_by_appl, NUTAG_REFER_EVENT(a_event)),
+	 TAG_IF(!notify_by_appl, NUTAG_NOTIFY_REFER(a_refer->nh)),
 	 SOATAG_USER_SDP_STR(a_c2->sdp),
 	 SIPTAG_REFERRED_BY(referred_by),
 	 TAG_END());
 
   run_abc_until(ctx,
-		-1, until_ready,
+		-1, notify_by_appl ? notify_until_terminated : until_ready,
 		-1, save_until_received,
-		-1, accept_call_immediately);
-  /* XXX - we should use accept_call instead of accept_call_immediately but
-     nua has a problem with automatically generated NOTIFYs:
-     3rd NOTIFY is not sent because 2nd is still in progress
-  */
+		-1, accept_call);
+
+  /* Wait until both NOTIFY has been responded */
+  while (a_revents->head == NULL || a_revents->head->next == NULL)
+    run_ab_until(ctx, -1, save_until_received, -1, save_events);
+  while (b_revents->head == NULL || b_revents->head->next == NULL)
+    run_ab_until(ctx, -1, save_events, -1, save_until_received);
 
   /* Client A transitions:
      INIT -(C1)-> CALLING: nua_invite(), nua_i_state
-     CALLING -(C2a+C4)-> READY: nua_r_invite, nua_i_state
-     nua_i_notify
-
-     XXX should be:
      CALLING -(C2+C4)-> PROCEEDING: nua_r_invite, nua_i_state
-     optional: nua_i_notify
+     nua_r_notify
      PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
-     nua_i_notify
-     optional: nua_i_notify
+     nua_r_notify
   */
   TEST_1(e = a_c2->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_ready); /* READY */
@@ -414,19 +455,32 @@
   TEST_1(!e->next);
   free_events_in_list(ctx, a_c2->events);
 
-  if (a->events->head == NULL)
-    run_a_until(ctx, -1, save_until_received);
-  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify);
-  TEST_1(!e->next);
-  free_events_in_list(ctx, a->events);
+  TEST_1(e = a_revents->head); TEST_E(e->data->e_event, nua_r_notify);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+  if (a_refer == a_call && notify_by_appl) {
+    free_event_in_list(ctx, a_revents, a_revents->head);
+    free_event_in_list(ctx, a_revents, a_revents->head);
+  }
+  else {
+    TEST_1(!e->next);
+    free_events_in_list(ctx, a_revents);
+  }
 
   /*
      Events in B after nua_refer():
      nua_i_notify
   */
-  if (b->events->head == NULL)
-    run_b_until(ctx, -1, save_until_received);
-  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify);
+  TEST_1(e = b_revents->head); TEST_E(e->data->e_event, nua_i_notify);
+  TEST(e->data->e_status, 200);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_subscription_state);
+  TEST_S(sip->sip_subscription_state->ss_substate, "active");
+  TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
+  TEST_S(sip->sip_payload->pl_data, "SIP/2.0 180 Ringing\r\n");
+  TEST_1(sip->sip_event);
+  if (refer_with_id)
+    TEST_S(sip->sip_event->o_id, b_event->o_id);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
   TEST(e->data->e_status, 200);
   TEST_1(sip = sip_object(e->data->e_msg));
   TEST_1(sip->sip_subscription_state);
@@ -436,13 +490,20 @@
   TEST_1(sip->sip_event);
   if (refer_with_id)
     TEST_S(sip->sip_event->o_id, b_event->o_id);
-  TEST_1(!e->next);
-  free_events_in_list(ctx, b->events);
+  if (b_refer == b_call && notify_by_appl) {
+    free_event_in_list(ctx, b_revents, b_revents->head);
+    free_event_in_list(ctx, b_revents, b_revents->head);
+  }
+  else {
+    TEST_1(!e->next);
+    free_events_in_list(ctx, b_revents);
+  }
 
   /*
    C transitions:
    INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
-   RECEIVED -(S3b)-> COMPLETED: nua_respond(), nua_i_state
+   RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
+   EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
    COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
   */
   TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_invite);
@@ -451,6 +512,8 @@
   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 = e->next); TEST_E(e->data->e_event, nua_i_ack);
@@ -472,20 +535,28 @@
   if (print_headings)
     printf("TEST %s.5.1: terminate call between A and B\n", tests);
 
-  BYE(a, a_call, a_call->nh, TAG_END());
-  run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
+  if (notify_by_appl) {
+    if (!a->events->head || !a->events->head->next)
+      run_ab_until(ctx, -1, until_terminated, -1, save_events);
+    if (!b->events->head || !b->events->head->next)
+      run_ab_until(ctx, -1, save_events, -1, until_terminated);
+  }
+  else {
+    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
+    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
   */
@@ -540,10 +611,20 @@
   if (print_headings)
     printf("TEST %s.5.2: PASSED\n", tests);
 
+  nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+
   nua_handle_destroy(a_c2->nh), a_c2->nh = NULL;
-  a->call->next = NULL; free(a_c2);
+  a_refer->next = NULL; free(a_c2);
 
-  nua_handle_destroy(c_call->nh), c_call->nh = NULL;
+  if (a_refer != a_call) {
+    nua_handle_destroy(a_refer->nh), a_refer->nh = NULL;
+    a_call->next = NULL; free(a_refer);
+  }
+
+  if (b_refer != b_call) {
+    nua_handle_destroy(b_refer->nh), b_refer->nh = NULL;
+    b_call->next = NULL; free(b_refer);
+  }
 
   if (print_headings)
     printf("TEST %s: PASSED\n", tests);
@@ -555,40 +636,6 @@
 
 
 /*
- accept_call_immediately
-                      X
- |                    |
- |-------INVITE------>|
- |<----100 Trying-----|
- |                    |
- |<--------200--------|
- |---------ACK------->|
-*/
-int accept_call_immediately(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_received:
-    RESPOND(ep, call, nh, SIP_200_OK,
-	    TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
-	    TAG_END());
-    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;
-  }
-}
-
-/*
  X      INVITE
  |                    |
  |-----------------INVITE-------------------->|
@@ -610,18 +657,31 @@
   if (event == nua_r_invite) {
     sip_status_t *st = sip->sip_status;
     sip_payload_t *pl;
+    struct call *r_call;
+
+    if (!nua_handle_has_events(ep->call->nh))
+      r_call = ep->call->next;
+    else
+      r_call = ep->call;
+
+    assert(nua_handle_has_events(r_call->nh));
 
     pl = sip_payload_format(NULL, "SIP/2.0 %u %s\r\n", 
 			    st->st_status, st->st_phrase);
 
-    NOTIFY(ep, ep->call, ep->call->nh,
+    NOTIFY(ep, r_call, r_call->nh,
 	   SIPTAG_CONTENT_TYPE_STR("message/sipfrag"),
 	   SIPTAG_PAYLOAD(pl),
-	   TAG_IF(st->st_status >= 200,
-		  NUTAG_SUBSTATE(nua_substate_terminated)),
+	   NUTAG_SUBSTATE(st->st_status >= 200
+			  ? nua_substate_terminated
+			  : nua_substate_active),
 	   TAG_END());
 
-    BYE(ep, ep->call, ep->call->nh, TAG_END());
+    su_free(NULL, pl);
+
+    if (st->st_status >= 200)
+      BYE(ep, ep->call, ep->call->nh, TAG_END());
+
     return 0;
   }
 
@@ -635,392 +695,3 @@
     return 0;
   }
 }
-
-/* Referred call - NOTIFY and BYE are overlapped
-
-   A			B
-   |			|
-   |-------INVITE------>|
-   |<----100 Trying-----|
-   |			|
-   |<----180 Ringing----|
-   |			|
-   |<------200 OK-------|
-   |--------ACK-------->|
-   |			|
-   |<------REFER--------|
-   |-------200 OK------>|			C
-  [|-------NOTIFY------>|]			|
-  [|<------200 OK-------|]			|
-   |			|			|
-   |			|			|
-   |<-----SUBSCRIBE-----|                       |
-   |-------200 OK------>|			|
-   |			|			|
-   |			|			|
-   |-----------------INVITE-------------------->|
-   |			|			|
-   |			|			|
-   |<------------------200----------------------|
-   |-------NOTIFY------>|			|
-   |--------BYE-------->|			|
-   |-------------------ACK--------------------->|
-   |<------200 OK-------|			|
-   |<------200 OK-------|			|
-   |			X			|
-   |			 			|
-   |-------------------BYE--------------------->|
-   |<------------------200----------------------|
-   |						|
-
-*/
-
-int test_refer1(struct context *ctx, int refer_with_id, char const *tests)
-{
-  BEGIN();
-
-  struct endpoint *a = &ctx->a,  *b = &ctx->b, *c = &ctx->c;
-  struct call *a_call = a->call, *b_call = b->call, *c_call = c->call;
-  struct call *a_c2;
-  struct event *e;
-  sip_t const *sip;
-  sip_event_t const *a_event, *b_event;
-  sip_refer_to_t const *refer_to;
-  sip_referred_by_t const *referred_by;
-
-  sip_refer_to_t r0[1];
-  sip_to_t to[1];
-
-  su_home_t tmphome[SU_HOME_AUTO_SIZE(16384)];
-
-  su_home_auto(tmphome, sizeof(tmphome));
-
-  if (print_headings)
-    printf("TEST %s: REFER: refer A to C\n", tests);
-
-  if (print_headings)
-    printf("TEST %s.1: REFER: make a call between A and B\n", tests);
-
-  /* Do (not) include id with first implicit Event: refer */
-  nua_set_params(ctx->a.nua, NUTAG_REFER_WITH_ID(refer_with_id), TAG_END());
-  run_a_until(ctx, nua_r_set_params, until_final_response);
-
-  TEST_1(a_c2 = calloc(1, (sizeof *a_c2) + (sizeof *a_c2->events)));
-  call_init(a_c2);
-  a_c2->events = (void *)(a_c2 + 1);
-  eventlist_init(a_c2->events);
-
-  a_call->sdp = "m=audio 5008 RTP/AVP 8";
-  b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
-  a_c2->sdp   = "m=audio 5012 RTP/AVP 8";
-  c_call->sdp = "m=audio 5014 RTP/AVP 0 8";
-
-  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, until_ready, -1, accept_call);
-
-  /* Client transitions:
-     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
-     CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
-     PROCEEDING -(C3+C4)-> READY: 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); /* PROCEEDING */
-  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
-  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_answer_recv(e->data->e_tags));
-  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
-   EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
-   COMPLETED -(S4)-> READY: nua_i_ack, 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); /* COMPLETED */
-  TEST_1(is_answer_sent(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(!e->next);
-  free_events_in_list(ctx, b->events);
-
-  if (print_headings)
-    printf("TEST %s.1: PASSED\n", tests);
-
-  /* ---------------------------------------------------------------------- */
-  /* REFER (initial NOTIFY is no more sent)
-   A                    B
-   |<------REFER--------|
-   |-------200 OK------>|
-  [|-------NOTIFY------>|]			|
-  [|<------200 OK-------|]			|
-   */
-
-  if (print_headings)
-    printf("TEST %s.2: refer A to C\n", tests);
-
-  *sip_refer_to_init(r0)->r_url = *c->contact->m_url;
-  r0->r_url->url_headers = "subject=referred";
-  r0->r_display = "C";
-
-  REFER(b, b_call, b_call->nh, SIPTAG_REFER_TO(r0), TAG_END());
-  run_ab_until(ctx, -1, save_until_received,
-	       -1, save_until_final_response);
-
-  /*
-    Events in A:
-    nua_i_refer
-  */
-  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_refer);
-  TEST(e->data->e_status, 202);
-  a_event = NULL;
-  TEST(tl_gets(e->data->e_tags,
-	       NUTAG_REFER_EVENT_REF(a_event),
-	       TAG_END()), 1);
-  TEST_1(a_event); TEST_1(a_event = sip_event_dup(tmphome, a_event));
-  TEST_1(sip = sip_object(e->data->e_msg));
-  TEST_1(sip->sip_refer_to);
-  TEST_1(refer_to = sip_refer_to_dup(tmphome, sip->sip_refer_to));
-  TEST_1(sip->sip_referred_by);
-  TEST_1(referred_by = sip_referred_by_dup(tmphome, sip->sip_referred_by));
-  if (e->next) {
-  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
-  TEST_1(!e->next);
-  }
-  free_events_in_list(ctx, a->events);
-
-  /*
-     Events in B after nua_refer():
-     nua_r_refer
-  */
-  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_refer);
-  TEST(e->data->e_status, 100);
-  TEST(tl_gets(e->data->e_tags,
-	       NUTAG_REFER_EVENT_REF(b_event),
-	       TAG_END()), 1);
-  TEST_1(b_event); TEST_1(b_event->o_id);
-  TEST_1(b_event = sip_event_dup(tmphome, b_event));
-  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_refer);
-  TEST(e->data->e_status, 202);
-  TEST_1(sip = sip_object(e->data->e_msg));
-  TEST_SIZE(strtoul(b_event->o_id, NULL, 10), sip->sip_cseq->cs_seq);
-#if 0
-  if (!e->next)
-    run_b_until(ctx, -1, save_until_received);
-  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
-  TEST(e->data->e_status, 200);
-  TEST_1(sip = sip_object(e->data->e_msg));
-  TEST_1(sip->sip_event);
-  if (refer_with_id)
-    TEST_S(sip->sip_event->o_id, b_event->o_id);
-  TEST_1(sip->sip_subscription_state);
-  TEST_S(sip->sip_subscription_state->ss_substate, "pending");
-  TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
-  TEST_S(sip->sip_payload->pl_data, "SIP/2.0 100 Trying\r\n");
-  TEST_1(!e->next);
-#endif
-  free_events_in_list(ctx, b->events);
-
-  if (print_headings)
-    printf("TEST %s.2: PASSED\n", tests);
-
-
-  /* ---------------------------------------------------------------------- */
-  /*
-   A                    B                       C
-   |			|			|
-   |-----------------INVITE-------------------->|
-   |			|			|
-  XXX			|			|
-   |			|			|
-   |<------------------200----------------------|
-   |-------NOTIFY------>|			|
-   |---------BYE------->|			|
-   |-------------------ACK--------------------->|
-   |<--------200--------|			|
-   |<------200 OK-------|			|
-   |                    X
-     */
-
-  if (print_headings)
-    printf("TEST %s.4: A invites C\n", tests);
-
-  *sip_to_init(to)->a_url = *refer_to->r_url;
-  to->a_display = refer_to->r_display;
-
-  a->call->next = a_c2;
-
-  TEST_1(a_c2->nh = nua_handle(a->nua, a_c2, SIPTAG_TO(to), TAG_END()));
-
-  INVITE(a, a_c2, a_c2->nh, /* NUTAG_URL(refer_to->r_url), */
-	 NUTAG_REFER_EVENT(a_event),
-	 /* NUTAG_NOTIFY_REFER(a_call->nh), */
-	 SOATAG_USER_SDP_STR(a_c2->sdp),
-	 SIPTAG_REFERRED_BY(referred_by),
-	 TAG_END());
-
-  run_abc_until(ctx,
-		-1, notify_until_terminated,
-		-1, until_terminated,
-		-1, accept_call_immediately);
-
-  /* XXX - we should use accept_call instead of accept_call_immediately but
-     nua has a problem with automatically generated NOTIFYs:
-     3rd NOTIFY is not sent because 2nd is still in progress
-  */
-
-  /* Client A transitions:
-     INIT -(C1)-> CALLING: nua_invite(), nua_i_state
-     CALLING -(C2a+C4)-> READY: nua_r_invite, nua_i_state
-     nua_r_notify
-
-     Transitions of first call:
-     READY --(T2)--> TERMINATING: nua_bye()
-     TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
-
-     XXX should be:
-     CALLING -(C2+C4)-> PROCEEDING: nua_r_invite, nua_i_state
-     optional: nua_i_notify
-     PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
-     nua_i_notify
-     optional: nua_i_notify
-  */
-  TEST_1(e = a_c2->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, 200);
-  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_answer_recv(e->data->e_tags));
-  TEST_1(!e->next);
-  free_events_in_list(ctx, a_c2->events);
-
-  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify);
-  TEST_1(e = e->next); 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 */
-  free_events_in_list(ctx, a->events);
-
-  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
-
-  /*
-     Events in B after nua_refer():
-     nua_i_notify
-     READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
-  */
-  if (b->events->head == NULL)
-    run_b_until(ctx, -1, save_until_received);
-  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify);
-  TEST(e->data->e_status, 200);
-  TEST_1(sip = sip_object(e->data->e_msg));
-  TEST_1(sip->sip_subscription_state);
-  TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
-  TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
-  TEST_S(sip->sip_payload->pl_data, "SIP/2.0 200 OK\r\n");
-  TEST_1(sip->sip_event);
-  if (refer_with_id)
-    TEST_S(sip->sip_event->o_id, b_event->o_id);
-  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, b->events);
-
-  nua_handle_destroy(b_call->nh), b_call->nh = NULL;
-
-  /*
-   C transitions:
-   INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
-   RECEIVED -(S3b)-> COMPLETED: nua_respond(), nua_i_state
-   COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
-  */
-  TEST_1(e = c->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_completed); /* COMPLETED */
-  TEST_1(is_answer_sent(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(!e->next);
-  free_events_in_list(ctx, c->events);
-
-  if (print_headings)
-    printf("TEST %s.4: PASSED\n", tests);
-
-  /* ---------------------------------------------------------------------- */
-  /*
-   A                                            C
-   |-------------------BYE--------------------->|
-   |<------------------200----------------------|
-   */
-
-  if (print_headings)
-    printf("TEST %s.5.2: terminate call between A and C\n", tests);
-
-  BYE(a, a_c2, a_c2->nh, TAG_END());
-  run_abc_until(ctx, -1, until_terminated, -1, NULL, -1, until_terminated);
-
-  /*
-   Transitions of A:
-   READY --(T2)--> TERMINATING: nua_bye()
-   TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
-  */
-  TEST_1(e = a_c2->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_c2->events);
-
-  /* Transitions of B:
-     READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
-  */
-  TEST_1(e = c->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, c->events);
-
-  if (print_headings)
-    printf("TEST %s.5.2: PASSED\n", tests);
-
-  nua_handle_destroy(a_c2->nh), a_c2->nh = NULL;
-  a->call->next = NULL; free(a_c2);
-
-  nua_handle_destroy(c_call->nh), c_call->nh = NULL;
-
-  if (print_headings)
-    printf("TEST %s: PASSED\n", tests);
-
-  su_home_deinit(tmphome);
-
-  END();
-}

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_register.c	Tue Mar 20 23:37:15 2007
@@ -133,6 +133,8 @@
 	   NUTAG_KEEPALIVE(1000),
 	   NUTAG_M_DISPLAY("A&A"),
 	   NUTAG_M_USERNAME("a"),
+	   NUTAG_M_PARAMS("foo=bar"),
+	   NUTAG_M_FEATURES("q=0.9"),
 	   SIPTAG_CSEQ(cseq),
 	   TAG_END());
   run_a_until(ctx, -1, save_until_final_response);
@@ -142,6 +144,7 @@
   TEST_1(sip = sip_object(e->data->e_msg));
   TEST(e->data->e_status, 401);
   TEST(sip->sip_status->st_status, 401);
+  /* Check that CSeq included in tags is actually used in the request */
   TEST(sip->sip_cseq->cs_seq, 13);
   TEST_1(!sip->sip_contact);
   TEST_1(!e->next);
@@ -160,6 +163,8 @@
     /* VC does not dig \" with TEST_S() */
   TEST_S(sip->sip_contact->m_display, expect_m_display); }
   TEST_S(sip->sip_contact->m_url->url_user, "a");
+  TEST_1(strstr(sip->sip_contact->m_url->url_params, "foo=bar"));
+  TEST_S(sip->sip_contact->m_q, "0.9");
   TEST(sip->sip_cseq->cs_seq, 14);
 
   if (ctx->nat) {
@@ -221,8 +226,12 @@
   TEST_1(c_reg->nh = nua_handle(c->nua, c_reg, TAG_END()));
 
   REGISTER(c, c_reg, c_reg->nh, SIPTAG_TO(c->to), 
+	   NUTAG_OUTBOUND(NULL),
 	   NUTAG_M_DISPLAY("C"),
 	   NUTAG_M_USERNAME("c"),
+	   NUTAG_M_PARAMS("c=1"),
+	   NUTAG_M_FEATURES("q=0.987;expires=5"),
+	   NUTAG_CALLEE_CAPS(1),
 	   SIPTAG_EXPIRES_STR("5"), /* Test 423 negotiation */
 	   TAG_END());
   run_abc_until(ctx, -1, save_events, -1, save_events, 
@@ -253,6 +262,9 @@
   TEST_1(sip->sip_contact);
   TEST_S(sip->sip_contact->m_display, "C");
   TEST_S(sip->sip_contact->m_url->url_user, "c");
+  TEST_1(strstr(sip->sip_contact->m_url->url_params, "c=1"));
+  TEST_S(sip->sip_contact->m_q, "0.987");
+  TEST_1(msg_header_find_param(sip->sip_contact->m_common, "methods="));
   TEST_1(!e->next);
   free_events_in_list(ctx, c->events);
 
@@ -349,7 +361,7 @@
   sip_t const *sip;
 
   if (print_headings)
-    printf("TEST NUA-2.3.2: REGISTER b to c\n");
+    printf("TEST NUA-2.6.1: REGISTER b to c\n");
 
   nua_set_params(ctx->c.nua,
 		 NUTAG_ALLOW("REGISTER"),
@@ -372,9 +384,6 @@
   TEST_1(sip = sip_object(e->data->e_msg));
   TEST_1(!sip->sip_contact);
 
-  if (print_headings)
-    printf("TEST NUA-2.6.1: PASSED\n");
-
   free_events_in_list(ctx, b->events);
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
 
@@ -388,7 +397,7 @@
   nua_handle_destroy(c_call->nh), c_call->nh = NULL;
 
   if (print_headings)
-    printf("TEST NUA-2.3.4: PASSED\n");
+    printf("TEST NUA-2.6.1: PASSED\n");
 
   END();
 }
@@ -552,7 +561,8 @@
 
   BEGIN();
 
-  struct endpoint *a = &ctx->a,  *b = &ctx->b, *c = &ctx->c;
+  struct endpoint *a = &ctx->a,  *b = &ctx->b;
+  struct call *a_call = a->call, *b_call = b->call;
   struct event *e;
   sip_t const *sip;
 
@@ -586,7 +596,38 @@
   if (print_headings)
     printf("TEST NUA-2.5.1: PASSED\n");
   
-  (void)b; (void)c; (void)sip;
+  if (print_headings)
+    printf("TEST NUA-2.5.2: OPTIONS from B to A\n");
+
+  TEST_1(b_call->nh = nua_handle(b->nua, b_call, SIPTAG_TO(a->to), TAG_END()));
+
+  OPTIONS(b, b_call, b_call->nh, TAG_END());
+
+  run_ab_until(ctx, -1, save_until_received,
+	       -1, save_until_final_response);
+
+  /* Client events: nua_options(), nua_r_options */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_options);
+  TEST(e->data->e_status, 200);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_allow); TEST_1(sip->sip_accept); TEST_1(sip->sip_supported);
+  /* TEST_1(sip->sip_content_type); */
+  /* TEST_1(sip->sip_payload); */
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, b->events);
+  nua_handle_destroy(b_call->nh), b_call->nh = NULL;
+
+  /* Server events: nua_i_options */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_options);
+  TEST(e->data->e_status, 200);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+  if (print_headings)
+    printf("TEST NUA-2.5.2: PASSED\n");
 
   END();
 }
@@ -658,6 +699,7 @@
   UNREGISTER(c, c->call, c->call->nh, SIPTAG_TO(c->to), 
 	     NUTAG_M_DISPLAY("C"),
 	     NUTAG_M_USERNAME("c"),
+	     NUTAG_M_PARAMS("c=1"),
 	     TAG_END());
   run_c_until(ctx, -1, save_until_final_response);
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c	Tue Mar 20 23:37:15 2007
@@ -43,6 +43,8 @@
 #define __func__ "test_simple"
 #endif
 
+extern int accept_request(CONDITION_PARAMS);
+
 int test_message(struct context *ctx)
 {
   BEGIN();
@@ -62,7 +64,7 @@
 
 */
   if (print_headings)
-    printf("TEST NUA-11.1: MESSAGE\n");
+    printf("TEST NUA-11.1.1: MESSAGE\n");
 
   if (ctx->proxy_tests)
     *url = *b->to->a_url;
@@ -76,7 +78,7 @@
 
   MESSAGE(a, a_call, a_call->nh,
 	  NUTAG_URL(url),
-	  SIPTAG_SUBJECT_STR("NUA-11.1"),
+	  SIPTAG_SUBJECT_STR("NUA-11.1.1"),
 	  SIPTAG_CONTENT_TYPE_STR("text/plain"),
 	  SIPTAG_PAYLOAD_STR("Hello hellO!\n"),
 	  TAG_END());
@@ -98,7 +100,64 @@
   TEST(e->data->e_status, 200);
   TEST_1(sip = sip_object(e->data->e_msg));
   TEST_1(sip->sip_subject && sip->sip_subject->g_string);
-  TEST_S(sip->sip_subject->g_string, "NUA-11.1");
+  TEST_S(sip->sip_subject->g_string, "NUA-11.1.1");
+  TEST_1(sip->sip_organization);
+  TEST_S(sip->sip_organization->g_string, "United Testers");
+  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-11.1.1: PASSED\n");
+
+/* MESSAGE as application method
+
+   A			B
+   |-------MESSAGE----->|
+   |<-------202---------|
+   |			|
+*/
+
+  if (print_headings)
+    printf("TEST NUA-11.1.2: MESSAGE\n");
+
+  nua_set_params(b->nua, NUTAG_APPL_METHOD("MESSAGE"), TAG_END());
+  run_b_until(ctx, nua_r_set_params, until_final_response);
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, TAG_END()));
+
+  MESSAGE(a, a_call, a_call->nh,
+	  NUTAG_URL(url),
+	  SIPTAG_SUBJECT_STR("NUA-11.1.2"),
+	  SIPTAG_CONTENT_TYPE_STR("text/plain"),
+	  SIPTAG_PAYLOAD_STR("Hello hellO!\n"),
+	  TAG_END());
+
+  run_ab_until(ctx, -1, save_until_final_response, -1, accept_request);
+
+  /* Client events:
+     nua_message(), nua_r_message
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_message);
+  TEST(e->data->e_status, 202);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip_user_agent(sip)); 
+  TEST_S(sip_user_agent(sip)->g_value, "007");
+  TEST_1(!e->next);
+
+  /*
+   Server events:
+   nua_i_message
+  */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_message);
+  TEST(e->data->e_status, 100);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_subject && sip->sip_subject->g_string);
+  TEST_S(sip->sip_subject->g_string, "NUA-11.1.2");
   TEST_1(sip->sip_organization);
   TEST_S(sip->sip_organization->g_string, "United Testers");
   TEST_1(!e->next);
@@ -110,7 +169,7 @@
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
 
   if (print_headings)
-    printf("TEST NUA-11.1: PASSED\n");
+    printf("TEST NUA-11.1.2: PASSED\n");
 
 
 /* Message test
@@ -131,7 +190,7 @@
   MESSAGE(a, a_call, a_call->nh,
 	  /* We cannot reach us by using our contact! */
 	  NUTAG_URL(!ctx->p && !ctx->proxy_tests ? a->contact->m_url : NULL),
-	  SIPTAG_SUBJECT_STR("NUA-11.1b"),
+	  SIPTAG_SUBJECT_STR("NUA-11.2"),
 	  SIPTAG_CONTENT_TYPE_STR("text/plain"),
 	  SIPTAG_PAYLOAD_STR("Hello hellO!\n"),
 	  TAG_END());
@@ -141,16 +200,21 @@
   /* Events:
      nua_message(), nua_i_message, nua_r_message
   */
-  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_message);
+  TEST_1(e = a->specials->head);
+  while (e->data->e_event == nua_i_outbound)
+    e = e->next;
+  TEST_E(e->data->e_event, nua_i_message);
   TEST(e->data->e_status, 200);
   TEST_1(sip = sip_object(e->data->e_msg));
   TEST_1(sip->sip_subject && sip->sip_subject->g_string);
-  TEST_S(sip->sip_subject->g_string, "NUA-11.1b");
-  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_message);
+  TEST_S(sip->sip_subject->g_string, "NUA-11.2");
+
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_message);
   TEST(e->data->e_status, 200);
   TEST_1(!e->next);
 
   free_events_in_list(ctx, a->events);
+  free_events_in_list(ctx, a->specials);
   nua_handle_destroy(a_call->nh), a_call->nh = NULL;
 
   if (print_headings)
@@ -159,12 +223,32 @@
   END();
 }
 
+int accept_request(CONDITION_PARAMS)
+{
+  msg_t *with = nua_current_request(nua);
+  
+  if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
+    return 0;
+
+  save_event_in_list(ctx, event, ep, call);
+
+  if (status < 200) {
+    RESPOND(ep, call, nh, SIP_202_ACCEPTED,
+	    NUTAG_WITH(with),
+	    SIPTAG_USER_AGENT_STR("007"),
+	    TAG_END());
+    return 1;
+  }
+
+  return 0;
+}
+
 int respond_with_etag(CONDITION_PARAMS)
 {
   msg_t *with = nua_current_request(nua);
 
   if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
-    return 0;
+    return 1;
 
   save_event_in_list(ctx, event, ep, call);
 
@@ -191,6 +275,14 @@
   }
 }
 
+static int close_handle(CONDITION_PARAMS)
+{
+  if (call->nh == nh)
+    call->nh = NULL;
+  nua_handle_destroy(nh);
+  return 1;
+}
+
 int test_publish(struct context *ctx)
 {
   BEGIN();
@@ -357,8 +449,60 @@
   nua_handle_destroy(a_call->nh), a_call->nh = NULL;
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
 
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  /* Let server close handle without responding to PUBLISH */ 
+  PUBLISH(a, a_call, a_call->nh,
+	  TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+	  SIPTAG_EVENT_STR("presence"),
+	  SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+	  SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+	  TAG_END());
+
+  run_ab_until(ctx, -1, save_until_final_response, -1, close_handle);
+
+  /* Client events:
+     nua_publish(), nua_r_publish
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+  TEST(e->data->e_status, 500);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
+  /* No Event header */
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  PUBLISH(a, a_call, a_call->nh,
+	  TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
+	  SIPTAG_CONTENT_TYPE_STR("text/urllist"),
+	  SIPTAG_PAYLOAD_STR("sip:example.com\n"),
+	  TAG_END());
+
+  run_ab_until(ctx, -1, save_until_final_response, -1, save_events);
+
+  /* Client events:
+     nua_publish(), nua_r_publish
+  */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_publish);
+  TEST(e->data->e_status, 489);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+
+  /*
+   Server events: nothing
+  */
+  TEST_1(!b->events->head);
+
+  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
+
   if (print_headings)
     printf("TEST NUA-11.3: PASSED\n");
+
   END();
 }
 
@@ -429,7 +573,10 @@
   tagi_t const *n_tags, *r_tags;
 
   if (print_headings)
-    printf("TEST NUA-11.4: establishing subscription without notifier\n");
+    printf("TEST NUA-11.4: notifier server using nua_notify()\n");
+
+  if (print_headings)
+    printf("TEST NUA-11.4.1: establishing subscription\n");
 
   TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
 
@@ -489,7 +636,7 @@
   free_events_in_list(ctx, b->events);
 
   if (print_headings)
-    printf("TEST NUA-11.4: PASSED\n");
+    printf("TEST NUA-11.4.1: PASSED\n");
 
   /* ---------------------------------------------------------------------- */
 
@@ -502,7 +649,7 @@
    |                    |
 */
   if (print_headings)
-    printf("TEST NUA-11.5: send NOTIFY\n");
+    printf("TEST NUA-11.4.2: send NOTIFY\n");
 
   /* Update presence data */
 
@@ -542,7 +689,7 @@
   free_events_in_list(ctx, b->events);
 
   if (print_headings)
-    printf("TEST NUA-11.5: PASSED\n");
+    printf("TEST NUA-11.4.2: PASSED\n");
 
   /* ---------------------------------------------------------------------- */
 
@@ -557,7 +704,7 @@
    |                    |
 */
   if (print_headings)
-    printf("TEST NUA-11.6: un-SUBSCRIBE\n");
+    printf("TEST NUA-11.4.3: un-SUBSCRIBE\n");
 
   UNSUBSCRIBE(a, a_call, a_call->nh, TAG_END());
 
@@ -614,11 +761,388 @@
   nua_handle_destroy(b_call->nh), b_call->nh = NULL;
 
   if (print_headings)
+    printf("TEST NUA-11.4.3: PASSED\n");
+
+  if (print_headings)
+    printf("TEST NUA-11.4: PASSED\n");
+
+  END();
+}
+
+/* ---------------------------------------------------------------------- */
+/* Subscriber gracefully terminates dialog upon 483 */
+
+static
+size_t change_status_to_483(void *a, void *message, size_t len);
+int save_until_notified_and_responded_twice(CONDITION_PARAMS);
+int save_until_notify_responded_twice(CONDITION_PARAMS);
+
+int test_subscribe_notify_graceful(struct context *ctx)
+{
+  if (!ctx->nat)
+    return 0;
+
+  BEGIN();
+
+  struct endpoint *a = &ctx->a,  *b = &ctx->b;
+  struct call *a_call = a->call, *b_call = b->call;
+  struct event *e;
+  sip_t const *sip;
+  tagi_t const *n_tags, *r_tags;
+  struct nat_filter *f;
+
+  if (print_headings)
+    printf("TEST NUA-11.5.1: establishing subscription\n");
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+	    SIPTAG_EVENT_STR("presence"),
+	    SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
+	    TAG_END());
+
+  run_ab_until(ctx, -1, save_until_notified_and_responded,
+	       -1, accept_and_notify);
+
+  /* Client events:
+     nua_subscribe(), nua_i_notify/nua_r_subscribe
+  */
+  TEST_1(e = a->events->head);
+  if (e->data->e_event == nua_i_notify) {
+    TEST_E(e->data->e_event, nua_i_notify);
+    TEST_1(sip = sip_object(e->data->e_msg));
+    n_tags = e->data->e_tags;
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_subscribe);
+    r_tags = e->data->e_tags;
+    TEST_1(tl_find(r_tags, nutag_substate));
+    TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending);
+  }
+  else {
+    TEST_E(e->data->e_event, nua_r_subscribe);
+    TEST(e->data->e_status, 202);
+    r_tags = e->data->e_tags;
+    TEST_1(tl_find(r_tags, nutag_substate));
+    TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify);
+    TEST_1(sip = sip_object(e->data->e_msg));
+    n_tags = e->data->e_tags;
+  }
+  TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
+  TEST_1(sip->sip_content_type);
+  TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
+  TEST_1(sip->sip_subscription_state);
+  TEST_S(sip->sip_subscription_state->ss_substate, "pending");
+  TEST_1(sip->sip_subscription_state->ss_expires);
+  TEST_1(tl_find(n_tags, nutag_substate));
+  TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_pending);
+  TEST_1(!e->next);
+  free_events_in_list(ctx, a->events);
+
+  /* Server events: nua_i_subscribe, nua_r_notify */
+  TEST_1(e = b->events->head);
+  TEST_E(e->data->e_event, nua_i_subscribe);
+  TEST_E(e->data->e_status, 100);
+  TEST_1(sip = sip_object(e->data->e_msg));
+
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+  r_tags = e->data->e_tags;
+  TEST_1(tl_find(r_tags, nutag_substate));
+  TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending);
+
+  free_events_in_list(ctx, b->events);
+
+  if (print_headings)
+    printf("TEST NUA-11.5.1: PASSED\n");
+
+  if (print_headings)
+    printf("TEST NUA-11.5.2: terminate gracefully upon 483\n");
+
+  TEST_1(f = test_nat_add_filter(ctx->nat,
+				 change_status_to_483, NULL,
+				 nat_inbound));
+
+  SUBSCRIBE(a, a_call, a_call->nh, TAG_END());
+
+  run_ab_until(ctx, -1, save_until_notified_and_responded_twice, 
+	       -1, save_until_notify_responded_twice);
+
+#if 0
+  /* Client events:
+     nua_unsubscribe(), nua_i_notify/nua_r_unsubscribe
+  */
+  TEST_1(e = a->events->head);
+  if (e->data->e_event == nua_i_notify) {
+    TEST_E(e->data->e_event, nua_i_notify);
+    n_tags = e->data->e_tags;
+    TEST_1(sip = sip_object(e->data->e_msg));
+    TEST_1(sip->sip_event);
+    TEST_1(sip->sip_subscription_state);
+    TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+    TEST_1(!sip->sip_subscription_state->ss_expires);
+    TEST_1(tl_find(n_tags, nutag_substate));
+    TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+    TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_unsubscribe);
+    TEST(e->data->e_status, 200);
+    r_tags = e->data->e_tags;
+    TEST_1(tl_find(r_tags, nutag_substate));
+    TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+  }
+  else {
+    TEST_E(e->data->e_event, nua_r_unsubscribe);
+    TEST(e->data->e_status, 200);
+    r_tags = e->data->e_tags;
+    TEST_1(tl_find(r_tags, nutag_substate));
+    TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+  }
+  TEST_1(!e->next);
+
+  /* Notifier events: nua_r_notify */
+  TEST_1(e = b->events->head);
+  TEST_E(e->data->e_event, nua_i_subscribe);
+  TEST(e->data->e_status, 200);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(tl_find(e->data->e_tags, nutag_substate));
+  TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, 
+       nua_substate_terminated);
+  TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify);
+  TEST_1(e->data->e_status >= 200);
+  r_tags = e->data->e_tags;
+  TEST_1(tl_find(r_tags, nutag_substate));
+  TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+#endif
+
+  free_events_in_list(ctx, a->events);
+  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;
+
+  test_nat_remove_filter(ctx->nat, f);
+
+  if (print_headings)
+    printf("TEST NUA-11.5.2: PASSED\n");
+
+  END();
+}
+
+static
+size_t change_status_to_483(void *a, void *message, size_t len)
+{
+  (void)a;
+
+  if (strncmp("SIP/2.0 2", message, 9) == 0) {
+    memcpy(message, "SIP/2.0 483", 11);
+  }
+  return len;
+}
+
+int save_until_notified_and_responded_twice(CONDITION_PARAMS)
+{
+  save_event_in_list(ctx, event, ep, call);
+
+  if (event == nua_i_notify) {
+    if (ep->flags.bit0)
+      ep->flags.bit1 = 1;
+    ep->flags.bit0 = 1;
+  }
+
+  if (event == nua_r_subscribe || event == nua_r_unsubscribe) {
+    if (status >= 300)
+      return 1;
+    else if (status >= 200) {
+      if (ep->flags.bit2)
+	ep->flags.bit3 = 1;
+      ep->flags.bit2 = 1;
+    }
+  }
+
+  return ep->flags.bit0 && ep->flags.bit1 && ep->flags.bit2 && ep->flags.bit3;
+}
+
+int save_until_notify_responded_twice(CONDITION_PARAMS)
+{
+  save_event_in_list(ctx, event, ep, call);
+
+  if (event == nua_r_notify) {
+    if (ep->flags.bit0)
+      ep->flags.bit1 = 1;
+    ep->flags.bit0 = 1;
+  }
+
+  return ep->flags.bit0 && ep->flags.bit1;
+}
+
+/* ---------------------------------------------------------------------- */
+/* Unsolicited NOTIFY */
+
+int accept_notify(CONDITION_PARAMS);
+
+int test_newsub_notify(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;
+  sip_t const *sip;
+  sip_call_id_t *i;
+  tagi_t const *n_tags, *r_tags;
+
+  if (print_headings)
+    printf("TEST NUA-11.7.1: rejecting NOTIFY without subscription locally\n");
+
+  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
+
+  NOTIFY(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+	 SIPTAG_SUBJECT_STR("NUA-11.7.1"),
+	 SIPTAG_EVENT_STR("message-summary"),
+	 SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+	 SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+	 TAG_END());
+
+  run_a_until(ctx, -1, save_until_final_response);
+
+  /* Client events:
+     nua_notify(), nua_r_notify
+  */
+  TEST_1(e = a->events->head);
+  TEST_E(e->data->e_event, nua_r_notify);
+  TEST(e->data->e_status, 481);
+  TEST_1(!e->data->e_msg);
+  TEST_1(!e->next);
+  free_events_in_list(ctx, a->events);
+
+  if (print_headings)
+    printf("TEST NUA-11.7.1: PASSED\n");
+
+  if (print_headings)
+    printf("TEST NUA-11.7.2: rejecting NOTIFY without subscription\n");
+
+  TEST_1(i = sip_call_id_create(nua_handle_home(a_call->nh), NULL));
+
+  NOTIFY(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+	 NUTAG_NEWSUB(1),
+	 SIPTAG_SUBJECT_STR("NUA-11.7.2 first"),
+	 SIPTAG_FROM_STR("<sip:alice at example.com>;tag=nua-11.7.2"),
+	 SIPTAG_CALL_ID(i),
+	 SIPTAG_CSEQ_STR("1 NOTIFY"),
+	 SIPTAG_EVENT_STR("message-summary"),
+	 SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+	 SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+	 TAG_END());
+
+  run_a_until(ctx, -1, save_until_final_response);
+
+  /* Client events:
+     nua_notify(), nua_r_notify
+  */
+  TEST_1(e = a->events->head);
+  TEST_E(e->data->e_event, nua_r_notify);
+  TEST(e->data->e_status, 481);
+  TEST_1(e->data->e_msg);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+  
+  /* 2nd NOTIFY using same dialog */
+  /* Check that server really discards the dialog */
+
+  NOTIFY(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
+	 NUTAG_NEWSUB(1),
+	 SIPTAG_SUBJECT_STR("NUA-11.7.2 second"),
+	 SIPTAG_FROM_STR("<sip:alice at example.com>;tag=nua-11.7.2"),
+	 SIPTAG_CALL_ID(i),
+	 SIPTAG_CSEQ_STR("2 NOTIFY"),
+	 SIPTAG_EVENT_STR("message-summary"),
+	 SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+	 SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+	 TAG_END());
+
+  run_a_until(ctx, -1, save_until_final_response);
+
+  /* Client events:
+     nua_notify(), nua_r_notify
+  */
+  TEST_1(e = a->events->head);
+  TEST_E(e->data->e_event, nua_r_notify);
+  TEST(e->data->e_status, 481);
+  TEST_1(e->data->e_msg);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+
+  if (print_headings)
+    printf("TEST NUA-11.7.2: PASSED\n");
+
+  /* ---------------------------------------------------------------------- */
+
+  if (print_headings)
+    printf("TEST NUA-11.7.3: accept NOTIFY\n");
+
+  nua_set_params(b->nua, NUTAG_APPL_METHOD("NOTIFY"), TAG_END());
+  run_b_until(ctx, nua_r_set_params, until_final_response);
+
+  NOTIFY(a, a_call, a_call->nh,
+	 NUTAG_URL(b->contact->m_url),
+	 NUTAG_NEWSUB(1),
+	 SIPTAG_SUBJECT_STR("NUA-11.7.3"),
+	 SIPTAG_EVENT_STR("message-summary"),
+	 SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"),
+	 SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"),
+	 TAG_END());
+
+  run_ab_until(ctx, -1, save_until_final_response, -1, accept_notify);
+
+  /* Notifier events: nua_r_notify */
+  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify);
+  TEST(e->data->e_status, 200);
+  r_tags = e->data->e_tags;
+  TEST_1(tl_find(r_tags, nutag_substate));
+  TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated);
+
+  /* subscriber events:
+     nua_i_notify
+  */
+  TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify);
+  TEST_1(sip = sip_object(e->data->e_msg));
+  TEST_1(sip->sip_subscription_state);
+  TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
+  n_tags = e->data->e_tags;
+  TEST_1(tl_find(n_tags, nutag_substate));
+  TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
+  TEST_1(!e->next);
+
+  free_events_in_list(ctx, a->events);
+  free_events_in_list(ctx, b->events);
+
+  if (print_headings)
+    printf("TEST NUA-11.7.3: PASSED\n");
+
+  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-11.6: PASSED\n");
 
   END();
 }
 
+/**Terminate when received notify. 
+ * Respond to NOTIFY with 200 OK if it has not been responded.
+ * Save events (except nua_i_active or terminated).
+ */
+int accept_notify(CONDITION_PARAMS)
+{
+  if (event == nua_i_notify && status < 200)
+    RESPOND(ep, call, nh, SIP_200_OK, 
+	    NUTAG_WITH_THIS(ep->nua),
+	    TAG_END());
+
+  save_event_in_list(ctx, event, ep, call);
+
+  return event == nua_i_notify;
+}
+
 /* ======================================================================== */
 /* Test simple methods: MESSAGE, PUBLISH, SUBSCRIBE/NOTIFY */
 
@@ -628,5 +1152,7 @@
     test_message(ctx)
     || test_publish(ctx)
     || test_subscribe_notify(ctx)
+    || test_subscribe_notify_graceful(ctx)
+    || test_newsub_notify(ctx)
     ;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c	Tue Mar 20 23:37:15 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/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -58,4 +58,5 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.c	Tue Mar 20 23:37:15 2007
@@ -1061,7 +1061,7 @@
   b += STRUCT_ALIGN(b);
   srcsdp = (sdp_session_t *)src->t_value;
 
-  sdp = session_dup(&b, srcsdp);
+  sdp = srcsdp ? session_dup(&b, srcsdp) : NULL;
 
   dst->t_tag = src->t_tag;
   dst->t_value = (tag_value_t)sdp;

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/torture_sdp.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/torture_sdp.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sdp/torture_sdp.c	Tue Mar 20 23:37:15 2007
@@ -848,9 +848,10 @@
   END();
 }
 
-void usage(void)
+void usage(int exitcode)
 {
-  fprintf(stderr, "usage: %s [-v]\n", name);
+  fprintf(stderr, "usage: %s [-v] [-a]\n", name);
+  exit(exitcode);
 }
 
 int main(int argc, char *argv[])
@@ -861,8 +862,10 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else
-      usage();
+      usage(1);
   }
 
   null = fopen("/dev/null", "ab");

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -25,6 +25,7 @@
 GENERATED_H = 		sofia-sip/sip_hclasses.h \
 			sofia-sip/sip_protos.h sofia-sip/sip_tag.h \
 			sofia-sip/sip_extra.h
+
 H_IN = 			sofia-sip/sip_hclasses.h.in \
 			sofia-sip/sip_protos.h.in sofia-sip/sip_tag.h.in \
 			sofia-sip/sip_extra.h.in
@@ -33,9 +34,9 @@
            sofia-sip/sip_header.h sofia-sip/sip_parser.h \
 	   sofia-sip/sip_tag_class.h sofia-sip/sip_status.h
 
-GENERATED_C = sip_tag.c sip_tag_ref.c sip_parser_table.c
+GENERATED_C = sip_tag.c sip_parser_table.c
 
-BUILT_SOURCES = 	$(GENERATED_H) $(GENERATED_C)
+BUILT_SOURCES = 	$(GENERATED_H) $(GENERATED_C) sip_tag_ref.c
 
 nobase_include_sofia_HEADERS = $(GENERATED_H) $(PUBLIC_H) $(H_IN)
 
@@ -46,8 +47,8 @@
 			sip_refer.c sip_session.c \
 			sip_caller_prefs.c sip_reason.c \
 			sip_status.c sip_time.c \
-			sip_tag_class.c \
-			$(GENERATED_C)
+			sip_tag_class.c sip_inlined.c \
+			$(BUILT_SOURCES)
 
 COVERAGE_INPUT = 	$(libsip_la_SOURCES) $(include_sofia_HEADERS)
 
@@ -55,6 +56,7 @@
 			../msg/libmsg.la \
 			../bnf/libbnf.la \
 			../url/liburl.la \
+			../ipt/libipt.la \
 			../su/libsu.la
 
 torture_sip_LDFLAGS = 	-static
@@ -93,62 +95,53 @@
 # ----------------------------------------------------------------------
 # Tests
 
-#TESTS = torture_sip run_test_sip_msg run_test_date
+TESTS = torture_sip run_test_sip_msg run_test_date
 
-#dist_noinst_SCRIPTS = 	run_test_sip_msg run_test_date
+dist_noinst_SCRIPTS = 	run_test_sip_msg run_test_date
 
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
 
 MSG_PARSER_AWK = $(srcdir)/../msg/msg_parser.awk
 
 AWK_SIP_AWK = $(AWK) -f $(MSG_PARSER_AWK) module=sip
 
-#
-# Note: sip_bad_mask is used by nta to weed out bad messages
-#
-sip_parser_table.c: sip_bad_mask
-
-sofia-sip/sip_hclasses.h: sofia-sip/sip_hclasses.h.in
-sofia-sip/sip_protos.h: sofia-sip/sip_protos.h.in
-sofia-sip/sip_tag.h: sofia-sip/sip_tag.h.in
+SS_SIP_H = ${srcdir}/sofia-sip/sip.h
 
-$(GENERATED_H): $(MSG_PARSER_AWK)
+EXTRA = ${srcdir}/sip_extra_headers.txt
 
-sip_parser_table.c: sip_parser_table.c.in $(MSG_PARSER_AWK)
-sip_tag.c: sip_tag.c.in $(MSG_PARSER_AWK)
+${GENERATED_H} ${GENERATED_C}: ${SS_SIP_H} ${MSG_PARSER_AWK}
 
-EXTRA = $(srcdir)/sip_extra_headers.txt
-
-sofia-sip/sip_hclasses.h: sofia-sip/sip.h
+sofia-sip/sip_hclasses.h: ${srcdir}/sofia-sip/sip_hclasses.h.in
 	@-mkdir sofia-sip 2>/dev/null || true
-	$(AWK_SIP_AWK) PR=$@ TEMPLATE=$(srcdir)/sofia-sip/sip_hclasses.h.in $<
+	${AWK_SIP_AWK} PR=$@ TEMPLATE=${srcdir}/$@.in ${SS_SIP_H}
 
-sofia-sip/sip_protos.h: sofia-sip/sip.h
+sofia-sip/sip_protos.h: ${srcdir}/sofia-sip/sip_protos.h.in
 	@-mkdir sofia-sip 2>/dev/null || true
-	$(AWK_SIP_AWK) PR=$@ TEMPLATE=$(srcdir)/sofia-sip/sip_protos.h.in $<
+	${AWK_SIP_AWK} PR=$@ TEMPLATE=${srcdir}/$@.in ${SS_SIP_H}
 
-sofia-sip/sip_tag.h: sofia-sip/sip.h
+sofia-sip/sip_tag.h: ${srcdir}/sofia-sip/sip_tag.h.in
 	@-mkdir sofia-sip 2>/dev/null || true
-	$(AWK_SIP_AWK) PR=$@ TEMPLATE=$(srcdir)/sofia-sip/sip_tag.h.in $<
+	${AWK_SIP_AWK} PR=$@ TEMPLATE=${srcdir}/$@.in ${SS_SIP_H}
 
-sip_tag.c: sofia-sip/sip.h sip_extra_headers.txt
-	$(AWK_SIP_AWK) PR=$@ TEMPLATE=$(srcdir)/sip_tag.c.in $< $(EXTRA)
+sip_tag.c: ${srcdir}/sip_tag.c.in ${EXTRA}
+	${AWK_SIP_AWK} PR=$@ TEMPLATE=${srcdir}/$@.in ${SS_SIP_H} ${EXTRA}
 
-sip_parser_table.c: sip_extra_headers.txt
+# Note: sip_bad_mask is used by nta to weed out bad messages
 
-sip_parser_table.c: sofia-sip/sip.h
-	$(AWK_SIP_AWK) PT=$@ TEMPLATE=$(srcdir)/sip_parser_table.c.in \
-		FLAGFILE=$(srcdir)/sip_bad_mask \
-		MC_HASH_SIZE=127 MC_SHORT_SIZE=26 $< $(EXTRA)
+sip_parser_table.c: ${srcdir}/sip_parser_table.c.in ${EXTRA} sip_bad_mask
+	${AWK_SIP_AWK} PT=$@ TEMPLATE=${srcdir}/$@.in \
+		FLAGFILE=${srcdir}/sip_bad_mask \
+		MC_HASH_SIZE=127 MC_SHORT_SIZE=26 \
+		${SS_SIP_H} ${EXTRA}
 
-sofia-sip/sip_extra.h: sofia-sip/sip_extra.h.in sip_extra_headers.txt
+sofia-sip/sip_extra.h: ${srcdir}/sofia-sip/sip_extra.h.in ${EXTRA}
 	@-mkdir -p sofia-sip 2>/dev/null
 	${AWK_SIP_AWK} PR=$@ NO_FIRST=1 NO_LAST=1 \
 		PACKAGE_NAME="${PACKAGE_NAME}" \
 		PACKAGE_VERSION="${PACKAGE_VERSION}" \
 		TEMPLATE1=${srcdir}/sofia-sip/sip_hclasses.h.in \
 		TEMPLATE2=${srcdir}/sofia-sip/sip_protos.h.in \
-		TEMPLATE=$< $(EXTRA)
+		TEMPLATE=${srcdir}/$@.in ${EXTRA}

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/run_test_date
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/run_test_date	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,2 @@
+#!/bin/sh
+./test_date "Sun, 18 Mar 2001 23:01:00 GMT"

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_basic.c	Tue Mar 20 23:37:15 2007
@@ -899,7 +899,7 @@
   sip_header_t *h;
 
   n = url_xtra(us->us_url);
-  h = sip_header_alloc(home, sip_to_class, n);    
+  h = sip_header_alloc(home, hc, n);    
 
   if (h) {
     sip_addr_t *a = h->sh_to;

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_event.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_event.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_event.c	Tue Mar 20 23:37:15 2007
@@ -44,6 +44,8 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
+
 #include <assert.h>
 
 /* ====================================================================== */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra.c	Tue Mar 20 23:37:15 2007
@@ -46,6 +46,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
 #include <assert.h>
 
@@ -757,6 +758,8 @@
 
 /* ====================================================================== */
 
+#if SU_HAVE_EXPERIMENTAL
+
 /**@SIP_HEADER sip_suppress_body_if_match Suppress-Body-If-Match Header
  *
  * The @b Suppress-Body-If-Match header field identifies a SIP event content
@@ -773,7 +776,7 @@
  *
  * @sa @RFC3265, draft-niemi-sip-subnot-etags-01.txt
  *
- * @NEW_1_12_5. Note that #sip_t does not contain @a
+ * @EXP_1_12_5. Note that #sip_t does not contain @a
  * sip_suppress_body_if_match field, but sip_suppress_body_if_match()
  * function should be used for accessing the @b Suppress-Body-If-Match
  * header structure.
@@ -839,7 +842,7 @@
  *
  * @sa @RFC3265, draft-niemi-sip-subnot-etag-01
  *
- * @NEW_1_12_5. Note that #sip_t does not contain @a
+ * @EXP_1_12_5. Note that #sip_t does not contain @a
  * sip_suppress_notify_if_match field, but sip_suppress_notify_if_match()
  * function should be used for accessing the @b Suppress-Notify-If-Match
  * header structure.
@@ -887,4 +890,4 @@
   return msg_generic_e(b, bsiz, h, f);
 }
 
-
+#endif

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra_headers.txt
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra_headers.txt	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_extra_headers.txt	Tue Mar 20 23:37:15 2007
@@ -4,10 +4,15 @@
 # The line format is: 
 # C-name @SINCE sip_t-like-comment
 #
+# Put all experimental things after EXPERIMENTAL HEADER LIST STARTS HERE...
+#
 #### EXTRA HEADER LIST STARTS HERE ####
 
-refer_sub @VERSION_1_12_5 /**< Refer-Sub header */
-suppress_body_if_match @VERSION_1_12_5 /**< Suppress-Body-If-Match header */
-suppress_notify_if_match @VERSION_1_12_5 /**< Suppress-Notify-If-Match header*/
+refer_sub @NEW_1_12_5 /**< Refer-Sub header */
+
+#### EXPERIMENTAL HEADER LIST STARTS HERE ####
+
+suppress_body_if_match @EXP_1_12_5 /**< Suppress-Body-If-Match header */
+suppress_notify_if_match @EXP_1_12_5 /**< Suppress-Notify-If-Match header */
 
 #### EXTRA HEADER LIST ENDS HERE ####

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_inlined.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_inlined.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@CFILE sip_inlined.c
+ *
+ * Expand inlined sip functions non-inline.
+ *
+ */
+
+#include "config.h"
+
+#include <sofia-sip/su_config.h>
+
+#if SU_HAVE_INLINE
+extern int xyzzy;
+#else
+#include "sofia-sip/msg_header.h"
+#include "sofia-sip/su_tag.h"
+
+#undef SU_HAVE_INLINE
+#undef su_inline
+
+#define SU_HAVE_INLINE 1
+#define su_inline
+
+#include "sofia-sip/sip_protos.h"
+#include "sofia-sip/sip_extra.h"
+
+#endif

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_mime.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_mime.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_mime.c	Tue Mar 20 23:37:15 2007
@@ -43,6 +43,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 #include <assert.h>
 
 /* ====================================================================== */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_parser.c	Tue Mar 20 23:37:15 2007
@@ -60,9 +60,9 @@
 char const sip_version_2_0[] = "SIP/2.0";
 
 /** Default message class */
-extern msg_mclass_t sip_mclass[];
+extern msg_mclass_t const sip_mclass[];
 
-msg_mclass_t *sip_default_mclass(void)
+msg_mclass_t const *sip_default_mclass(void)
 {
   return sip_mclass;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_prack.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_prack.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_prack.c	Tue Mar 20 23:37:15 2007
@@ -46,6 +46,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <assert.h>
+#include <limits.h>
 
 /* ====================================================================== */
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_reason.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_reason.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_reason.c	Tue Mar 20 23:37:15 2007
@@ -42,6 +42,8 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
+
 #include <assert.h>
 
 /**@SIP_HEADER sip_reason Reason Header

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_refer.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_refer.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_refer.c	Tue Mar 20 23:37:15 2007
@@ -45,6 +45,8 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
+
 #include <assert.h>
 
 /* ====================================================================== */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_tag_class.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_tag_class.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_tag_class.c	Tue Mar 20 23:37:15 2007
@@ -46,6 +46,7 @@
 #include <assert.h>
 #include <stddef.h>
 #include <string.h>
+#include <limits.h>
 
 /** Tag class for SIP header tags. @HIDE */
 tag_class_t siphdrtag_class[1] = 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sip_util.c	Tue Mar 20 23:37:15 2007
@@ -258,10 +258,11 @@
   /* Make transport parameter lowercase */
   if (transport && strlen(transport) < (sizeof _transport)) {
     char *s = strcpy(_transport, transport);
+    short c;
 
-    for (s = _transport; *s && *s != ';'; s++)
-      if (isupper(*s))
-	*s = tolower(*s);
+    for (s = _transport; (c = *s) && c != ';'; s++)
+      if (isupper(c))
+	*s = tolower(c);
 
     transport = _transport;
   }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_header.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_header.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_header.h	Tue Mar 20 23:37:15 2007
@@ -58,7 +58,7 @@
 SOFIA_BEGIN_DECLS
 
 /** Return built-in SIP parser object. */
-SOFIAPUBFUN msg_mclass_t *sip_default_mclass(void);
+SOFIAPUBFUN msg_mclass_t const *sip_default_mclass(void);
 
 /** Check that sip_t is a SIP structure (not RTSP or HTTP). @HIDE */
 #define sip_is_sip(sip) ((sip) && (sip)->sip_ident == SIP_PROTOCOL_TAG)
@@ -345,9 +345,15 @@
 SOFIAPUBFUN
 int sip_has_feature(msg_list_t const *supported, char const *feature);
 
+/** Return true if the method is listed in @Allow header. */
 SOFIAPUBFUN int sip_is_allowed(sip_allow_t const *allow, 
 			       sip_method_t method, char const *name);
 
+/** Check if the well-known method is listed in @Allow header. @NEW_1_12_6 */
+#define SIP_IS_ALLOWED(allow, method) \
+  (sip_method_unknown < (method) && (method) < 32 && \
+   (allow) && ((allow)->k_bitmap & (1 << (method))) != 0)
+
 /* ---------------------------------------------------------------------------
  * Bitmasks for header classifications
  */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_parser.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_parser.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_parser.h	Tue Mar 20 23:37:15 2007
@@ -192,10 +192,6 @@
 #define sip_params_copy_xtra	msg_params_copy_xtra
 #define sip_params_copy		msg_params_copy
 
-#define sip_params_add      	msg_params_add
-#define sip_params_cmp      	msg_params_cmp
-#define sip_params_replace  	msg_params_replace
-
 SOFIAPUBFUN int sip_generic_xtra(sip_generic_t const *g);
 
 SOFIAPUBFUN sip_generic_t *sip_generic_dup(su_home_t *home, 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_util.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_util.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_util.h	Tue Mar 20 23:37:15 2007
@@ -42,21 +42,11 @@
 #include <sofia-sip/string0.h>
 #endif
 
-SOFIA_BEGIN_DECLS
+#ifndef MSG_HEADER_H
+#include <sofia-sip/msg_header.h>
+#endif
 
-/* @deprecated
- * These are just wrappers around msg_params_*() functions. 
- *
- * Use msg_header_*_param() functions instead.
- */
-SOFIAPUBFUN char const *sip_params_find(sip_param_t const pp[],
-					char const *token);
-SOFIAPUBFUN int sip_params_add(su_home_t *sh, 
-			       sip_param_t **pparams,
-			       char const *param);
-SOFIAPUBFUN int sip_params_cmp(sip_param_t const a[], sip_param_t const b[]);
-SOFIAPUBFUN int sip_params_replace(su_home_t *,
-				   sip_param_t **pparams, char const *param);
+SOFIA_BEGIN_DECLS
 
 SOFIAPUBFUN
 sip_contact_t *
@@ -210,6 +200,13 @@
 sip_security_client_select(sip_security_client_t const *client,
 			   sip_security_server_t const *server);
 
+/* Compatibility stuff */
+
+#define sip_params_add      	msg_params_add
+#define sip_params_cmp      	msg_params_cmp
+#define sip_params_replace  	msg_params_replace
+#define sip_params_find         msg_params_find
+
 SOFIA_END_DECLS
 
 #endif /** !defined(SIP_UTIL_H) */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_date.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_date.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_date.c	Tue Mar 20 23:37:15 2007
@@ -33,6 +33,8 @@
  * @date Wed Mar 21 19:12:13 2001 ppessi
  */
 
+#include "config.h"
+
 #include <stdio.h>
 #include <sofia-sip/string0.h>
 #include <stddef.h>
@@ -42,12 +44,12 @@
 #include <sofia-sip/sip_header.h>
 #include <sofia-sip/msg_date.h>
 
-void usage(void)
+void usage(int exitcode)
 {
   fprintf(stderr, 
 	  "usage: test_date [SIP-date] "
 	  "[YYYYy][DDd][HHh][MMm][SS[s]]\n");
-  exit(1);
+  exit(exitcode);
 }
 
 int main(int ac, char *av[])
@@ -83,7 +85,7 @@
 	case 's': delta += t2; break;
 	default:
 	  fprintf(stderr, "test_date: %s is not valid time offset\n" , av[2]);
-	  usage();
+	  usage(1);
 	  break;
 	}
       }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_sip_msg.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_sip_msg.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/test_sip_msg.c	Tue Mar 20 23:37:15 2007
@@ -65,7 +65,7 @@
   return *o != *n;
 }
 
-int test_msg_class(msg_mclass_t *mc)
+int test_msg_class(msg_mclass_t const *mc)
 {
   int i, j, N;
 
@@ -159,7 +159,7 @@
   int m, tcp;
   sip_t *sip;
   int exitcode = 0; 
-  msg_mclass_t *sip_mclass = sip_default_mclass();
+  msg_mclass_t const *sip_mclass = sip_default_mclass();
   msg_t *msg = msg_create(sip_mclass, MSG_FLG_EXTRACT_COPY);
   msg_iovec_t iovec[1];
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/torture_sip.c	Tue Mar 20 23:37:15 2007
@@ -412,7 +412,12 @@
     TEST_1(sip_from_tag(home, f, "tag=jxahudsf") == 0);
     su_free(home, f);
 
+    TEST_1(f = sip_from_create(home, (void *)"<sip:joe at bar;tag=bar> (joe)"));
+    TEST_1(sip_is_from((sip_header_t*)f));
+    su_free(home, f);
+
     TEST_1(t = sip_to_create(home, (void *)"<sip:joe at bar;tag=bar> (joe)"));
+    TEST_1(sip_is_to((sip_header_t*)f));
     TEST_1(sip_to_tag(home, t, "tag=jxahudsf") == 0);
     TEST_S(t->a_tag, "jxahudsf");
     TEST(msg_header_replace_param(home, t->a_common, "tag=bar"), 1);
@@ -1653,7 +1658,11 @@
   TEST(count(sip->sip_content_type->c_common), 1);
   TEST(count(sip->sip_route->r_common), 0);
   TEST(count(sip->sip_record_route->r_common), 4);
+#if SU_HAVE_EXPERIMENTAL
   TEST(count(sip->sip_unknown->un_common), 2);
+#else
+  TEST(count(sip->sip_unknown->un_common), 4);
+#endif
   TEST(count(sip->sip_error->er_common), 1);
   TEST(count(sip->sip_max_forwards->mf_common), 1);
   TEST(count(sip->sip_min_expires->me_common), 1);
@@ -1667,6 +1676,7 @@
   TEST(sip->sip_max_forwards->mf_count, 12);
   TEST(sip->sip_min_expires->me_delta, 150);
 
+#if SU_HAVE_EXPERIMENTAL
   {
     sip_suppress_body_if_match_t *sbim;
     sip_suppress_notify_if_match_t *snim;
@@ -1683,6 +1693,7 @@
     TEST_SIZE(offsetof(msg_generic_t, g_value),
 	      offsetof(sip_suppress_notify_if_match_t, snim_tag));
   }
+#endif
 
   TEST_1(sip->sip_from->a_display);
   TEST_S(sip->sip_from->a_display, "h");
@@ -3206,11 +3217,12 @@
   END();
 }
 
-void usage(void)
+void usage(int exitcode)
 {
   fprintf(stderr, 
-	  "usage: %s [-v]\n", 
+	  "usage: %s [-v] [-a]\n", 
 	  name);
+  exit(exitcode);
 }
 
 char *lastpart(char *path)
@@ -3231,8 +3243,10 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else
-      usage();
+      usage(1);
   }
 
   if (!test_mclass)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/validator.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/validator.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sip/validator.c	Tue Mar 20 23:37:15 2007
@@ -180,7 +180,7 @@
     return path;
 }
 
-msg_mclass_t *mclass = NULL;
+msg_mclass_t const *mclass = NULL;
 
 int validate_file(int fd, char const *name, context_t *ctx);
 int validate_dump(char *, off_t, context_t *ctx);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -58,6 +58,6 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
 
 TAG_DLL_FLAGS =		LIST=soa_tag_list
\ No newline at end of file

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa.c	Tue Mar 20 23:37:15 2007
@@ -167,7 +167,7 @@
   struct soa_namenode const *n;
   struct soa_namenode *e;
 
-  SU_DEBUG_9(("soa_add(%s%s%s, %p) called\n", NICE(name), actions));
+  SU_DEBUG_9(("soa_add(%s%s%s, %p) called\n", NICE(name), (void *)actions));
 
   if (name == NULL || actions == NULL)
     return su_seterrno(EFAULT);
@@ -227,7 +227,7 @@
   size_t namelen;
 
   SU_DEBUG_9(("soa_create(\"%s\", %p, %p) called\n",
-	      name ? name : "default", root, magic));
+	      name ? name : "default", (void *)root, (void *)magic));
 
   if (name && name[0]) {
     struct soa_namenode const *n;
@@ -276,7 +276,7 @@
 
   SU_DEBUG_9(("soa_clone(%s::%p, %p, %p) called\n",
 	      parent_ss ? parent_ss->ss_actions->soa_name : "",
-	      parent_ss, root, magic));
+	      (void *)parent_ss, (void *)root, (void *)magic));
 
   if (parent_ss == NULL || root == NULL)
     return (void)su_seterrno(EFAULT), NULL;
@@ -302,7 +302,7 @@
 soa_session_t *soa_session_ref(soa_session_t *ss)
 {
   SU_DEBUG_9(("soa_session_ref(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
   return su_home_ref(ss->ss_home);
 }
 
@@ -310,7 +310,7 @@
 void soa_session_unref(soa_session_t *ss)
 {
   SU_DEBUG_9(("soa_session_unref(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
   su_home_unref(ss->ss_home);
 }
 
@@ -350,7 +350,7 @@
 void soa_destroy(soa_session_t *ss)
 {
   SU_DEBUG_9(("soa_destroy(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss) {
     ss->ss_active = 0;
@@ -396,7 +396,7 @@
   int n;
 
   SU_DEBUG_9(("soa_set_params(%s::%p, ...) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss == NULL)
     return su_seterrno(EFAULT), -1;
@@ -606,7 +606,7 @@
   int n;
 
   SU_DEBUG_9(("soa_get_params(%s::%p, ...) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss == NULL)
     return su_seterrno(EFAULT), -1;
@@ -688,7 +688,7 @@
   tagi_t *params = NULL;
 
   SU_DEBUG_9(("soa_get_paramlist(%s::%p, ...) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss) {
     ta_start(ta, tag, value);
@@ -756,7 +756,7 @@
 			      char const **return_phrase)
 {
   SU_DEBUG_9(("soa_error_as_sip_response(%s::%p, ...) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss == NULL || ss->ss_status < 400 || ss->ss_status >= 700) {
     if (return_phrase)
@@ -777,7 +777,7 @@
   char *reason;
 
   SU_DEBUG_9(("soa_error_as_sip_reason(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss == NULL)
     return "SIP;cause=500;text=\"Internal Server Error\"";
@@ -832,8 +832,8 @@
   char const *sdp_str;
 
   SU_DEBUG_9(("soa_get_capability_sdp(%s::%p, [%p], [%p], [%p]) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss,
-	      return_sdp, return_sdp_str, return_len));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
+	      (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
 
   if (ss == NULL)
     return (void)su_seterrno(EFAULT), -1;
@@ -878,7 +878,7 @@
 			   char const *str, issize_t len)
 {
   SU_DEBUG_9(("soa_set_capability_sdp(%s::%p, %p, %p, "MOD_ZD") called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, sdp, str, (ssize_t)len));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)sdp, (void *)str, (ssize_t)len));
 
   return soa_set_sdp(ss, soa_capability_sdp_kind, sdp, str, len);
 }
@@ -964,8 +964,8 @@
   char const *sdp_str;
 
   SU_DEBUG_9(("soa_get_user_sdp(%s::%p, [%p], [%p], [%p]) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss,
-	      return_sdp, return_sdp_str, return_len));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
+			  (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
 
   if (ss == NULL)
     return (void)su_seterrno(EFAULT), -1;
@@ -1036,7 +1036,7 @@
 		     char const *str, issize_t len)
 {
   SU_DEBUG_9(("soa_set_user_sdp(%s::%p, %p, %p, "MOD_ZD") called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, sdp, str, (ssize_t)len));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)sdp, (void *)str, (ssize_t)len));
 
   return soa_set_sdp(ss, soa_user_sdp_kind, sdp, str, len);
 }
@@ -1081,8 +1081,8 @@
   char const *sdp_str;
 
   SU_DEBUG_9(("soa_get_remote_sdp(%s::%p, [%p], [%p], [%p]) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss,
-	      return_sdp, return_sdp_str, return_len));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
+			  (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
 
   if (ss == NULL)
     return (void)su_seterrno(EFAULT), -1;
@@ -1155,7 +1155,7 @@
 		       char const *str, issize_t len)
 {
   SU_DEBUG_9(("soa_set_remote_sdp(%s::%p, %p, %p, "MOD_ZD") called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, sdp, str, (ssize_t)len));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)sdp, (void *)str, (ssize_t)len));
 
   return soa_set_sdp(ss, soa_remote_sdp_kind, sdp, str, len);
 }
@@ -1198,7 +1198,7 @@
 int soa_clear_remote_sdp(soa_session_t *ss)
 {
   SU_DEBUG_9(("soa_clear_remote_sdp(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (!ss)
     return (void)su_seterrno(EFAULT), -1;
@@ -1253,8 +1253,8 @@
   char const *sdp_str;
 
   SU_DEBUG_9(("soa_get_local_sdp(%s::%p, [%p], [%p], [%p]) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss,
-	      return_sdp, return_sdp_str, return_len));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss,
+			  (void *)return_sdp, (void *)return_sdp_str, (void *)return_len));
 
   if (ss == NULL)
     return (void)su_seterrno(EFAULT), -1;
@@ -1286,7 +1286,7 @@
   int complete;
 
   SU_DEBUG_9(("soa_init_offer_answer(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (!ss)
     return 0;
@@ -1308,7 +1308,7 @@
 char **soa_media_features(soa_session_t *ss, int live, su_home_t *home)
 {
   SU_DEBUG_9(("soa_media_features(%s::%p, %u, %p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, live, home));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, live, (void *)home));
 
   if (ss)
     return ss->ss_actions->soa_media_features(ss, live, home);
@@ -1324,7 +1324,7 @@
 char const * const * soa_sip_require(soa_session_t const *ss)
 {
   SU_DEBUG_9(("soa_sip_require(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss)
     return ss->ss_actions->soa_sip_require(ss);
@@ -1341,7 +1341,7 @@
 char const * const * soa_sip_supported(soa_session_t const *ss)
 {
   SU_DEBUG_9(("soa_sip_supported(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   if (ss)
     return ss->ss_actions->soa_sip_supported(ss);
@@ -1360,7 +1360,7 @@
 			    char const * const * require)
 {
   SU_DEBUG_9(("soa_remote_sip_features(%s::%p, %p, %p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, supported, require));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, (void *)supported, (void *)require));
 
   if (ss)
     return ss->ss_actions->soa_remote_sip_features(ss, supported, require);
@@ -1405,8 +1405,8 @@
 		       int always,
 		       soa_callback_f *completed)
 {
-  SU_DEBUG_9(("soa_generate_offer(%s::%p, %u, %p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, always, completed));
+  SU_DEBUG_9(("soa_generate_offer(%s::%p, %u) called\n",
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, always));
 
   /** @ERROR EFAULT Bad address. */
   if (ss == NULL)
@@ -1493,8 +1493,8 @@
 int soa_generate_answer(soa_session_t *ss,
 			soa_callback_f *completed)
 {
-  SU_DEBUG_9(("soa_generate_answer(%s::%p, %p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, completed));
+  SU_DEBUG_9(("soa_generate_answer(%s::%p) called\n",
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   /** @ERROR EFAULT Bad address as @a ss. */
   if (ss == NULL)
@@ -1573,8 +1573,8 @@
 int soa_process_answer(soa_session_t *ss,
 		       soa_callback_f *completed)
 {
-  SU_DEBUG_9(("soa_process_answer(%s::%p, %p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, completed));
+  SU_DEBUG_9(("soa_process_answer(%s::%p) called\n",
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   /** @ERROR EFAULT Bad address as @a ss. */
   if (ss == NULL)
@@ -1654,8 +1654,8 @@
 int soa_process_reject(soa_session_t *ss,
 		       soa_callback_f *completed)
 {
-  SU_DEBUG_9(("soa_process_reject(%s::%p, %p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, completed));
+  SU_DEBUG_9(("soa_process_reject(%s::%p) called\n",
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   /** @ERROR EFAULT Bad address as @a ss. */
   if (ss == NULL)
@@ -1711,7 +1711,7 @@
 int soa_activate(soa_session_t *ss, char const *option)
 {
   SU_DEBUG_9(("soa_activate(%s::%p, %s%s%s) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, NICE(option)));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, NICE(option)));
 
   /** @ERROR EFAULT Bad address as @a ss. */
   if (ss == NULL)
@@ -1741,7 +1741,7 @@
 int soa_deactivate(soa_session_t *ss, char const *option)
 {
   SU_DEBUG_9(("soa_deactivate(%s::%p, %s%s%s) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss, NICE(option)));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss, NICE(option)));
 
   /** @ERROR EFAULT Bad address as @a ss. */
   if (ss == NULL)
@@ -1763,7 +1763,7 @@
 void soa_terminate(soa_session_t *ss, char const *option)
 {
   SU_DEBUG_9(("soa_terminate(%s::%p) called\n",
-	      ss ? ss->ss_actions->soa_name : "", ss));
+	      ss ? ss->ss_actions->soa_name : "", (void *)ss));
 
   /** @ERROR EFAULT Bad address as @a ss. */
   if (ss == NULL)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/soa_static.c	Tue Mar 20 23:37:15 2007
@@ -822,7 +822,7 @@
   sdp_mode_t recv_mode;
 
   SU_DEBUG_7(("soa_sdp_mode_set_is_needed(%p, %p, \"%s\"): called\n",
-	      session, remote, hold ? hold : ""));
+	      (void *)session, (void *)remote, hold ? hold : ""));
 
   if (!session )
     return 0;
@@ -865,7 +865,7 @@
   sdp_mode_t send_mode, recv_mode;
 
   SU_DEBUG_7(("soa_sdp_mode_set(%p, %p, \"%s\"): called\n",
-	      session, remote, hold ? hold : ""));
+	      (void *)session, (void *)remote, hold ? hold : ""));
 
   if (!session || !session->sdp_media)
     return 0;
@@ -928,7 +928,8 @@
 
   su_home_auto(tmphome, sizeof tmphome);
 
-  SU_DEBUG_7(("soa_static_offer_answer_action(%p, %s): called\n", ss, by));
+  SU_DEBUG_7(("soa_static_offer_answer_action(%p, %s): called\n",
+	      (void *)ss, by));
 
   if (user == NULL)
     return soa_set_status(ss, 500, "No session set by user");
@@ -954,7 +955,8 @@
   if (local == NULL) switch (action) {
   case generate_offer:
   case generate_answer:
-    SU_DEBUG_7(("soa_static(%p, %s): generating local description\n", ss, by));
+    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
+		"generating local description"));
 
     local = local0;
     *local = *user, local->sdp_media = NULL;
@@ -987,7 +989,7 @@
       break;
     if (local != local0)
       *local0 = *local, local = local0;
-    SU_DEBUG_7(("soa_static(%p, %s): %s\n", ss, by, 
+    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, 
 		"upgrade with local description"));
     soa_sdp_upgrade(ss, tmphome, local, user, user);
     break;
@@ -999,7 +1001,7 @@
     if (1) {
       if (local != local0)
 	*local0 = *local, local = local0;
-      SU_DEBUG_7(("soa_static(%p, %s): %s\n", ss, by,
+      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
 		  "upgrade with remote description"));
       soa_sdp_upgrade(ss, tmphome, local, user, remote);
     }
@@ -1032,7 +1034,8 @@
 	} while (0)
 	DUP_LOCAL(local);
       }
-      SU_DEBUG_7(("soa_static(%p, %s): marking rejected media\n", ss, by));
+      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by, 
+		  "marking rejected media"));
       soa_sdp_reject(tmphome, local, remote);
     }
     break;
@@ -1065,7 +1068,7 @@
     if (ss->ss_local_remote_version == remote_version)
       break;
     if (1 /* We don't have good test for codecs */) {
-      SU_DEBUG_7(("soa_static(%p, %s): %s\n", ss, by,
+      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
 		  "upgrade codecs with remote description"));
       if (local != local0) {
 	*local0 = *local, local = local0; 
@@ -1143,7 +1146,8 @@
       ss->ss_previous_remote_version = ss->ss_local_remote_version;
     }
 
-    SU_DEBUG_7(("soa_static(%p, %s): storing local description\n", ss, by));
+    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
+		"storing local description"));
 
     /* Update the unparsed and pretty-printed descriptions  */
     if (soa_description_set(ss, ss->ss_local, local, NULL, 0) < 0) {

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/sofia-sip/soa_session.h	Tue Mar 20 23:37:15 2007
@@ -163,10 +163,10 @@
 
   struct soa_media_activity
   {
-    int ma_audio:4; /**< Audio activity (send/recv) */
-    int ma_video:4; /**< Video activity (send/recv) */
-    int ma_image:4; /**< Image activity (send/recv) for JPIP */
-    int ma_chat:4;  /**< Chat activity (send/recv) */
+    signed ma_audio:4; /**< Audio activity (send/recv) */
+    signed ma_video:4; /**< Video activity (send/recv) */
+    signed ma_image:4; /**< Image activity (send/recv) for JPIP */
+    signed ma_chat:4;  /**< Chat activity (send/recv) */
   } ss_local_activity[1], ss_remote_activity[1];
 
   /** Capabilities as specified by application */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/soa/test_soa.c	Tue Mar 20 23:37:15 2007
@@ -353,7 +353,9 @@
   soa_session_t *a, *b;
 
   char const *caps = NONE, *offer = NONE, *answer = NONE;
-  isize_t capslen = -1, offerlen = -1, answerlen = -1;
+  isize_t capslen = (isize_t)-1;
+  isize_t offerlen = (isize_t)-1;
+  isize_t answerlen = (isize_t)-1;
 
   su_home_t home[1] = { SU_HOME_INIT(home) };
 
@@ -611,7 +613,7 @@
   soa_session_t *a, *b;
 
   char const *offer = NONE, *answer = NONE;
-  isize_t offerlen = -1, answerlen = -1;
+  isize_t offerlen = (isize_t)-1, answerlen = (isize_t)-1;
 
   sdp_session_t const *a_sdp, *b_sdp;
   sdp_media_t const *m;
@@ -1243,12 +1245,12 @@
 }
 #endif
 
-void usage(void)
+void usage(int exitcode)
 {
   fprintf(stderr, 
-	  "usage: %s [-v|-q] [-l level] [-p outbound-proxy-uri]\n", 
+	  "usage: %s [-v|-q] [-a] [-l level] [-p outbound-proxy-uri]\n", 
 	  name);
-  exit(1);
+  exit(exitcode);
 }
 
 int main(int argc, char *argv[])
@@ -1261,6 +1263,8 @@
   for (i = 1; argv[i]; i++) {
     if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else if (strcmp(argv[i], "-q") == 0)
       tstflags &= ~tst_verbatim;
     else if (strcmp(argv[i], "-1") == 0)
@@ -1277,7 +1281,7 @@
 	level = 3, rest = "";
 
       if (rest == NULL || *rest)
-	usage();
+	usage(1);
       
       su_log_set_level(soa_log, level);
     }
@@ -1294,7 +1298,7 @@
       break;
     }
     else
-      usage();
+      usage(1);
   }
 
   if (o_attach) {

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Doxyfile
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Doxyfile	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Doxyfile	Tue Mar 20 23:37:15 2007
@@ -3,6 +3,8 @@
 
 INPUT 		     =  sofia-sip sofia-resolv sresolv.docs .
 
+EXCLUDE		     = resolve_sip.c
+
 @INCLUDE 	     = ../docs/Doxyfile.conf
 
 ALIASES             += CFILE="@internal @file" IFILE="@internal @file"

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -61,4 +61,5 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
+

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/run_test_sresolv
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/run_test_sresolv	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/run_test_sresolv	Tue Mar 20 23:37:15 2007
@@ -55,8 +55,13 @@
     done
 fi
 
-# No named, no fun
-type -p named >/dev/null || exit 77
+# No BIND 9, no fun
+{ type -p named >/dev/null &&
+  named -v | grep -q BIND.*9 
+} || {
+echo test_sresolv: there is no BIND 9 named in you path, skipping
+exit 77
+} 
 
 if eval $ipv6
 then

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c	Tue Mar 20 23:37:15 2007
@@ -70,6 +70,10 @@
 #endif
 #endif
 
+#if HAVE_IPHLPAPI_H
+#include <iphlpapi.h>
+#endif
+
 #include <time.h>
 
 #include "sofia-resolv/sres.h"
@@ -159,6 +163,7 @@
 #define INVALID_SOCKET ((sres_socket_t)-1)
 #endif
 
+#define SRES_TIME_MAX ((time_t)LONG_MAX)
 
 #if !HAVE_INET_PTON
 int inet_pton(int af, char const *src, void *dst);
@@ -211,7 +216,10 @@
 
   /** ICMP/temporary error received, zero when successful. */
   time_t                  dns_icmp;
-  /** Persisten error, zero when successful. */
+  /** Persistent error, zero when successful or timeout. 
+   *
+   * Never selected if dns_error is SRES_TIME_MAX.
+   */
   time_t                  dns_error;
 };
 
@@ -434,7 +442,7 @@
 static
 sres_server_t *sres_next_server(sres_resolver_t *res, 
 				uint8_t *in_out_i,
-				int timeout);
+				int always);
 
 static
 int sres_send_dns_query(sres_resolver_t *res, sres_query_t *q);
@@ -541,7 +549,8 @@
 #include <winreg.h>
 #endif
 
-/**@var SRESOLV_DEBUG
+#if DOXYGEN_ONLY
+/**@ingroup sresolv_env
  *
  * Environment variable determining the debug log level for @b sresolv
  * module.
@@ -551,7 +560,8 @@
  * 
  * @sa <su_debug.h>, sresolv_log, SOFIA_DEBUG
  */
-extern char const SRESOLV_DEBUG[];
+extern SRESOLV_DEBUG;
+#endif
 
 /**Debug log for @b sresolv module. 
  * 
@@ -626,7 +636,7 @@
  *                       (overriding options in conf_file)
  *
  * @par Environment Variables
- * - LOCALDOMAIN overrides @c domain or @c search directives
+ * - #LOCALDOMAIN overrides @c domain or @c search directives
  * - #RES_OPTIONS overrides values of @a options in resolv.conf
  * - #SRES_OPTIONS overrides values of @a options in resolv.conf, #RES_OPTIONS,
  *   and @a options, ... list given as argument for this function
@@ -923,8 +933,8 @@
   size_t dlen;
   
   char b[8];
-  SU_DEBUG_9(("sres_query(%p, %p, %p, %s, \"%s\") called\n",
-	      res, callback, context, sres_record_type(type, b), domain));
+  SU_DEBUG_9(("sres_query(%p, %p, %s, \"%s\") called\n",
+			  (void *)res, (void *)context, sres_record_type(type, b), domain));
 
   if (res == NULL || domain == NULL)
     return su_seterrno(EFAULT), (void *)NULL;
@@ -990,8 +1000,8 @@
   unsigned dots; char const *dot;
   char b[8];
 
-  SU_DEBUG_9(("sres_search(%p, %p, %p, %s, \"%s\") called\n",
-	      res, callback, context, sres_record_type(type, b), domain));
+  SU_DEBUG_9(("sres_search(%p, %p, %s, \"%s\") called\n",
+			  (void *)res, (void *)context, sres_record_type(type, b), domain));
 
   if (res == NULL || domain == NULL)
     return su_seterrno(EFAULT), (void *)NULL;
@@ -1235,7 +1245,7 @@
   int i;
 
   SU_DEBUG_9(("sres_search_cached_answers(%p, %s, \"%s\") called\n",
-	      res, sres_record_type(type, rooted_domain), domain));
+	      (void *)res, sres_record_type(type, rooted_domain), domain));
 
   if (!res || !name)
     return su_seterrno(EFAULT), (void *)NULL;
@@ -1895,6 +1905,36 @@
 #define MAX_DATALEN           65535
 
 /**
+ * Uses IP Helper IP to get DNS servers list.
+ */
+static int sres_parse_win32_ip(sres_config_t *c)
+{
+  int ret = -1;
+
+#if HAVE_IPHLPAPI_H
+  DWORD dw;
+  su_home_t *home = c->c_home;
+  ULONG size = sizeof(FIXED_INFO);
+
+  do {
+    FIXED_INFO *info = (FIXED_INFO *)su_alloc(home, size);
+    dw = GetNetworkParams(info, &size);
+    if (dw == ERROR_SUCCESS) {
+      IP_ADDR_STRING* addr = &info->DnsServerList;
+      for (; addr; addr = addr->Next) {
+       SU_DEBUG_3(("Adding nameserver: %s\n", addr->IpAddress.String));
+       sres_parse_nameserver(c, addr->IpAddress.String);
+      }
+      ret = 0;
+    }
+    su_free(home, info);
+  } while (dw == ERROR_BUFFER_OVERFLOW);
+#endif
+
+  return ret;
+}
+
+/**
  * Parses name servers listed in registry key 'key+lpValueName'. The
  * key is expected to contain a whitespace separate list of
  * name server IP addresses.
@@ -2072,7 +2112,7 @@
 #if _WIN32    
     /* note: no 127.0.0.1 on win32 systems */
     /* on win32, query the registry for nameservers */
-    if (sres_parse_win32_reg(c) == 0) 
+    if (sres_parse_win32_ip(c) == 0 || sres_parse_win32_reg(c) == 0)
       /* success */;
     else
       /* now what? */;
@@ -2203,21 +2243,24 @@
   return i;
 }
 
-/**Environment variable containing options for Sofia resolver. The options
+#if DOXYGEN_ONLY
+/**@ingroup sresolv_env
+ *
+ * Environment variable containing options for Sofia resolver. The options
  * recognized by Sofia resolver are as follows:
- * - debug           turn on debugging (no effect)
- * - ndots:<i>n</i>  when searching, try first to query name as absolute
- *                   domain if it contains at least <i>n</i> dots
- * - timeout:<i>secs</i> timeout in seconds
- * - attempts:<i>n</i> fail after <i>n</i> retries
- * - rotate          use round robin selection of nameservers
- * - no-check-names  do not check names for invalid characters
- * - inet6           (no effect) 
- * - ip6-dotint      IPv6 addresses are resolved using suffix ".ip6.int"
- *                   instead of the standard ".ip6.arpa" suffix
- * - ip6-bytestring  (no effect)
+ * - @b debug           turn on debugging (no effect)
+ * - @b ndots:<i>n</i>  when searching, try first to query name as absolute
+ *                      domain if it contains at least <i>n</i> dots
+ * - @b timeout:<i>secs</i> timeout in seconds
+ * - @b attempts:<i>n</i> fail after <i>n</i> retries
+ * - @b rotate          use round robin selection of nameservers
+ * - @b no-check-names  do not check names for invalid characters
+ * - @b inet6           (no effect) 
+ * - @b ip6-dotint      IPv6 addresses are resolved using suffix ".ip6.int"
+ *                      instead of the standard ".ip6.arpa" suffix
+ * - @b ip6-bytestring  (no effect)
  * The following option is a Sofia-specific extension:
- * - no-edns0        do not try to use EDNS0 extension (@RFC2671)
+ * - @b no-edns0        do not try to use EDNS0 extension (@RFC2671)
  *
  * The same options can be listed in @b options directive in resolv.conf, or
  * in #RES_OPTIONS environment variable. Note that options given in
@@ -2228,15 +2271,26 @@
  *
  * @sa Manual page for resolv.conf, #RES_OPTIONS.
  */
-extern char const SRES_OPTIONS[];
+extern SRES_OPTIONS;
 
-/**Environment variable containing resolver options. This environment
+/**@ingroup sresolv_env
+ *
+ * Environment variable containing resolver options. This environment
  * variable is also used by standard BIND resolver.
  *
  * @sa Manual page for resolv.conf, #SRES_OPTIONS.
  */
-extern char const RES_OPTIONS[];
+extern RES_OPTIONS;
 
+/**@ingroup sresolv_env
+ *
+ * Environment variable containing search domain. This environment
+ * variable is also used by standard BIND resolver.
+ *
+ * @sa Manual page for resolv.conf, #RES_OPTIONS, #SRES_OPTIONS.
+ */
+extern LOCALDOMAIN;
+#endif
 
 /* Parse options line or #SRES_OPTIONS or #RES_OPTIONS environment variable. */
 static int 
@@ -2263,7 +2317,7 @@
       value += strspn(value, " \t");
 
     if (n > 65536) {
-      SU_DEBUG_3(("sres: %s: invalid %*.s\n", c->c_filename,
+      SU_DEBUG_3(("sres: %s: invalid %*.0s\n", c->c_filename,
 		  (int)(len + extra), b));
       continue;
     }
@@ -2288,7 +2342,7 @@
     else if (MATCH("no-edns0")) c->c_opt.edns = edns_not_supported;
     else if (MATCH("edns0")) c->c_opt.edns = edns0_configured;
     else {
-      SU_DEBUG_3(("sres: %s: unknown option %*.s\n",
+      SU_DEBUG_3(("sres: %s: unknown option %*.0s\n",
 		  c->c_filename, (int)(len + extra), b));
     }
   }
@@ -2437,7 +2491,7 @@
     if (!servers[i])
       break;
 
-    if (servers[i]->dns_socket != -1) {
+    if (servers[i]->dns_socket != INVALID_SOCKET) {
       if (res->res_updcb)
 	res->res_updcb(res->res_async, INVALID_SOCKET, servers[i]->dns_socket);
       sres_close(servers[i]->dns_socket);
@@ -2467,14 +2521,13 @@
   int family = dns->dns_addr->ss_family;
   sres_socket_t s;
 
-  if (dns->dns_socket != -1)
+  if (dns->dns_socket != INVALID_SOCKET)
     return dns->dns_socket;
 
   s = socket(family, SOCK_DGRAM, IPPROTO_UDP);
   if (s == -1) {
     SU_DEBUG_1(("%s: %s: %s\n", "sres_server_socket", "socket",
 		su_strerror(su_errno())));
-    dns->dns_error = time(NULL);
     return s;
   }
 
@@ -2517,7 +2570,6 @@
 		su_strerror(su_errno()), lb, ipaddr, rb,
 		ntohs(((struct sockaddr_in *)dns->dns_addr)->sin_port)));
     sres_close(s);
-    dns->dns_error = time(NULL);
     return INVALID_SOCKET;
   }
   
@@ -2526,7 +2578,6 @@
       SU_DEBUG_1(("%s: %s: %s\n", "sres_server_socket", "update callback",
 		  su_strerror(su_errno())));
       sres_close(s);
-      dns->dns_error = time(NULL);
       return INVALID_SOCKET;
     }
   }
@@ -2547,7 +2598,7 @@
   sres_message_t m[1];
   uint8_t i, i0, N = res->res_n_servers;
   sres_socket_t s;
-  int transient, error = 0;
+  int error = 0;
   ssize_t size, no_edns_size, edns_size;
   uint16_t id = q->q_id;
   uint16_t type = q->q_type;
@@ -2558,7 +2609,7 @@
 
   if (now == 0) time(&now);
 
-  SU_DEBUG_9(("sres_send_dns_query(%p, %p) called\n", res, q));
+  SU_DEBUG_9(("sres_send_dns_query(%p, %p) called\n", (void *)res, (void *)q));
 
   if (domain == NULL)
     return -1;
@@ -2600,17 +2651,14 @@
     return -1;
   }
 
-  transient = 0;
+  i0 = q->q_i_server;
+  if (i0 > N) i0 = 0; /* Number of DNS servers reduced */
+  dns = servers[i = i0];
 
-  i0 = q->q_i_server; if (i0 > N) i0 = 0; /* Number of DNS servers reduced */
+  if (res->res_config->c_opt.rotate || dns->dns_error || dns->dns_icmp)
+    dns = sres_next_server(res, &q->q_i_server, 1), i = q->q_i_server;
 
-  if (res->res_config->c_opt.rotate || 
-      servers[i0]->dns_error || servers[i0]->dns_icmp)
-    dns = sres_next_server(res, &q->q_i_server, 0), i = q->q_i_server;
-  else 
-    dns = servers[i0], i = i0;
-
-  for (; dns; dns = sres_next_server(res, &i, 0)) {
+  for (; dns; dns = sres_next_server(res, &i, 1)) {
     /* If server supports EDNS, include EDNS0 record */
     q->q_edns = dns->dns_edns;
     /* 0 (no EDNS) or 1 (EDNS supported) additional data records */
@@ -2620,17 +2668,18 @@
 
     s = sres_server_socket(res, dns);
 
+    if (s == INVALID_SOCKET) {
+      dns->dns_icmp = now;
+      dns->dns_error = SRES_TIME_MAX;
+      continue;
+    }
+
     /* Send the DNS message via the UDP socket */
-    if (s != INVALID_SOCKET && sres_send(s, m->m_data, size, 0) == size)
+    if (sres_send(s, m->m_data, size, 0) == size)
       break;
-
     error = su_errno();
-    dns->dns_icmp = now;
-    /* EINVAL is returned if destination address is bad */
-    if (transient++ < 3 && error != EINVAL && s != -1)
-      continue;
-    transient = 0;
 
+    dns->dns_icmp = now;
     dns->dns_error = now;	/* Mark as a bad destination */
   }
 
@@ -2645,46 +2694,76 @@
 
   SU_DEBUG_5(("%s(%p, %p) id=%u %s %s (to [%s]:%u)\n", 
 	      "sres_send_dns_query",
-	      res, q, id, sres_record_type(type, b), domain, 
+	      (void *)res, (void *)q, id, sres_record_type(type, b), domain, 
 	      dns->dns_name, 
 	      htons(((struct sockaddr_in *)dns->dns_addr)->sin_port)));
 
   return 0;
 }
 
+/** Retry time after ICMP error */
+#define DNS_ICMP_TIMEOUT 60
+
+/** Retry time after immediate error */
+#define DNS_ERROR_TIMEOUT 10
 
-/** Select next server */
+/** Select next server.
+ *
+ * @param res resolver object
+ * @param[in,out] in_out_i index to DNS server table
+ * @param always return always a server
+ */
 static
 sres_server_t *sres_next_server(sres_resolver_t *res, 
 				uint8_t *in_out_i,
-				int timeout)
+				int always)
 {
   int i, j, N;
-  sres_server_t **servers;
-
-  assert(res && in_out_i);
+  sres_server_t *dns, **servers;
+  time_t now = res->res_now;
 
   N = res->res_n_servers;
   servers = res->res_servers;
   i = *in_out_i;
 
   assert(res->res_servers && res->res_servers[i]);
+
+  for (j=0; j < N; j++) {
+    dns = servers[j]; if (!dns) continue;
+    if (dns->dns_icmp + DNS_ICMP_TIMEOUT < now)
+      dns->dns_icmp = 0;
+    if (dns->dns_error + DNS_ERROR_TIMEOUT < now &&
+	dns->dns_error != SRES_TIME_MAX)
+      dns->dns_error = 0;
+  }
   
   /* Retry using another server? */
   for (j = (i + 1) % N; (j != i); j = (j + 1) % N) {
-    if (servers[j]->dns_icmp == 0) {
-      return *in_out_i = j, servers[j];
+    dns = servers[j]; if (!dns) continue;
+    if (dns->dns_icmp == 0) {
+      return *in_out_i = j, dns;
     }
   }
 
   for (j = (i + 1) % N; (j != i); j = (j + 1) % N) {
-    if (servers[j]->dns_error == 0) {
-      return *in_out_i = j, servers[j];
+    dns = servers[j]; if (!dns) continue;
+    if (dns->dns_error == 0) {
+      return *in_out_i = j, dns;
     }
   }
 
-  if (timeout)
-    return servers[i];
+  if (!always)
+    return NULL;
+
+  dns = servers[i]; 
+  if (dns && dns->dns_error < now && dns->dns_error != SRES_TIME_MAX)
+    return dns;
+
+  for (j = (i + 1) % N; j != i; j = (j + 1) % N) {
+    dns = servers[j]; if (!dns) continue;
+    if (dns->dns_error < now && dns->dns_error != SRES_TIME_MAX)
+      return *in_out_i = j, dns;
+  }
   
   return NULL;
 }
@@ -2763,7 +2842,7 @@
     }
 
     SU_DEBUG_5(("sres(q=%p): reporting errors for %u %s\n",
-		q, q->q_type, q->q_name));
+		(void *)q, q->q_type, q->q_name));
  
     sres_remove_query(q->q_res, q, 1);
     (q->q_callback)(q->q_context, q, answers);
@@ -2822,15 +2901,21 @@
   sres_cache_clean(res->res_cache, res->res_now);
 }
 
-/** Resend DNS query, report error if cannot resend any more. */
+/** Resend DNS query, report error if cannot resend any more.
+ * 
+ * @param res  resolver object
+ * @param q    query object
+ * @param timeout  true if resent because of timeout
+ *                (false if because icmp error report)
+ */
 static void
 sres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout)
 {
   uint8_t i, N;
   sres_server_t *dns;
-
-  SU_DEBUG_9(("sres_resend_dns_query(%p, %p, %u) called\n",
-	      res, q, timeout));
+  
+  SU_DEBUG_9(("sres_resend_dns_query(%p, %p, %s) called\n",
+	      (void *)res, (void *)q, timeout ? "timeout" : "error"));
   
   N = res->res_n_servers;
 
@@ -2956,6 +3041,11 @@
 
     s = sres_server_socket(res, dns);
 
+    if (s == INVALID_SOCKET) {	/* Mark as a bad destination */
+      dns->dns_icmp = SRES_TIME_MAX;
+      dns->dns_error = SRES_TIME_MAX;	
+    }
+
     return_sockets[i++] = s;
   }
 
@@ -2998,7 +3088,8 @@
   int n;
   char info[128] = "";
 
-  SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_error", res, socket));
+  SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_error",
+	      (void *)res, socket));
 
   msg->msg_name = name, msg->msg_namelen = sizeof(name);
   msg->msg_iov = iov, msg->msg_iovlen = 1;
@@ -3111,7 +3202,8 @@
   int errcode = 0;
   socklen_t errorlen = sizeof(errcode);
 
-  SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_error", res, socket));
+  SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_error",
+	      (void *)res, socket));
 
   getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&errcode, &errorlen);
 
@@ -3176,7 +3268,7 @@
 	  continue;
 
 	/* Resend query/report error to application */
-	sres_resend_dns_query(res, q, 1);
+	sres_resend_dns_query(res, q, 0);
 
 	if (q != res->res_queries->qt_table[i])
 	  i--;
@@ -3203,7 +3295,8 @@
   struct sockaddr_storage from[1];
   socklen_t fromlen = sizeof from;
 
-  SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_receive", res, socket));
+  SU_DEBUG_9(("%s(%p, %u) called\n", "sres_resolver_receive",
+	      (void *)res, socket));
 
   memset(m, 0, offsetof(sres_message_t, m_data)); 
   
@@ -3282,7 +3375,7 @@
 #endif
 
     SU_DEBUG_5(("sres_resolver_receive(%p, %p) id=%u (from [%s]:%u)\n", 
-		res, query, m->m_id, 
+		(void *)res, (void *)query, m->m_id, 
 		host, ntohs(((struct sockaddr_in *)from)->sin_port)));
   }
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_blocking.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_blocking.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_blocking.c	Tue Mar 20 23:37:15 2007
@@ -86,7 +86,13 @@
 #elif HAVE_SELECT
   struct { sres_socket_t fd; } fds[SRES_MAX_NAMESERVERS];
 #else
-#error No wait mechanism!
+#warning No guaranteed wait mechanism!
+/* typedef struct os_specific su_wait_t; */
+struct _pollfd {
+  sres_socket_t fd;   /* file descriptor */
+  short events;     /* requested events */
+  short revents;    /* returned events */
+} fds[SRES_MAX_NAMESERVERS];
 #endif
   sres_record_t ***return_records;  
 };

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c	Tue Mar 20 23:37:15 2007
@@ -178,7 +178,7 @@
   *return_cached = NULL;
 
   SU_DEBUG_9(("%s(%p, %s, \"%s\") called\n", "sres_cache_get",
-	      cache, sres_record_type(type, b), domain));
+	      (void *)cache, sres_record_type(type, b), domain));
 
   hash = sres_hash_key(domain);
 
@@ -241,7 +241,7 @@
   UNLOCK(cache);
 
   SU_DEBUG_9(("%s(%p, %s, \"%s\") returned %d entries\n", "sres_cache_get", 
-	      cache, sres_record_type(type, b), domain, rr_count));
+	      (void *)cache, sres_record_type(type, b), domain, rr_count));
 
   *return_cached = result;
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.c	Tue Mar 20 23:37:15 2007
@@ -186,8 +186,8 @@
   int i, index = -1, error = 0;
   int N = SRES_MAX_NAMESERVERS;
 
-  SU_DEBUG_9(("sres_sofia_update(%p, %d, %d)\n", srs, 
-	      (int)new_socket, (int)old_socket));
+  SU_DEBUG_9(("sres_sofia_update(%p, %d, %d)\n",
+	      (void *)srs, (int)new_socket, (int)old_socket));
 
   if (srs == NULL)
     return 0;
@@ -297,7 +297,7 @@
   if (!srs)
     return su_seterrno(EINVAL);
 
-  if (sres_resolver_set_async(res, sres_sofia_update, srs, 1) < 0)
+  if (sres_resolver_set_async(res, sres_sofia_update, srs, 1) == NULL)
     return INVALID_SOCKET;
 
   if (srs->srs_socket != INVALID_SOCKET)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.docs
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.docs	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/sresolv.docs	Tue Mar 20 23:37:15 2007
@@ -261,3 +261,10 @@
 @endcode
 
 */
+
+/** @defgroup sresolv_env Environment Variables Used by sresolv Module
+
+In addition to the standard #RES_OPTIONS and #LOCALDOMAIN environment
+variables, the #SRES_OPTIONS and #SRESOLV_DEBUG variables are supported.
+
+*/
\ No newline at end of file

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c	Tue Mar 20 23:37:15 2007
@@ -64,7 +64,6 @@
 #include <sofia-resolv/sres_async.h>
 #include <sofia-resolv/sres_record.h>
 
-#include <sofia-sip/su.h>
 #include <sofia-sip/su_alloc.h>
 
 #include <assert.h>
@@ -101,7 +100,7 @@
   sres_record_t  **result;
 
   int              timeout;
-  int              sink;
+  sres_socket_t    sink;
   int              sinkidx;
   char const      *sinkconf;
   
@@ -120,6 +119,84 @@
 
 static int tstflags = 0;
 
+#if HAVE_WINSOCK2_H
+
+/* Posix send() */
+static inline 
+ssize_t sres_send(sres_socket_t s, void *b, size_t length, int flags)
+{
+  if (length > INT_MAX)
+    length = INT_MAX;
+  return (ssize_t)send(s, b, (int)length, flags);
+}
+
+static inline 
+ssize_t sres_sendto(sres_socket_t s, void *b, size_t length, int flags,
+		    struct sockaddr const *sa, socklen_t salen)
+{
+  if (length > INT_MAX)
+    length = INT_MAX;
+  return (ssize_t)sendto(s, b, (int)length, flags, (void *)sa, (int)salen);
+}
+
+/* Posix recvfrom() */
+static inline 
+ssize_t sres_recvfrom(sres_socket_t s, void *buffer, size_t length, int flags,
+		      struct sockaddr *from, socklen_t *fromlen)
+{
+  int retval, ilen;
+
+  if (fromlen)
+    ilen = *fromlen;
+
+  if (length > INT_MAX)
+    length = INT_MAX;
+
+  retval = recvfrom(s, buffer, (int)length, flags, 
+		    (void *)from, fromlen ? &ilen : NULL);
+
+  if (fromlen)
+    *fromlen = ilen;
+
+  return (ssize_t)retval;
+}
+
+static sres_socket_t sres_socket(int af, int socktype, int protocol)
+{
+  return socket(af, socktype, protocol);
+}
+
+static inline
+int sres_close(sres_socket_t s)
+{
+  return closesocket(s);
+}
+
+#if !defined(IPPROTO_IPV6)
+#if HAVE_SIN6
+#include <tpipv6.h>
+#else
+#if !defined(__MINGW32__)
+struct sockaddr_storage {
+    short ss_family;
+    char ss_pad[126];
+};
+#endif
+#endif
+#endif
+#else
+
+#define sres_send(s,b,len,flags) send((s),(b),(len),(flags))
+#define sres_sendto(s,b,len,flags,a,alen) \
+  sendto((s),(b),(len),(flags),(a),(alen))
+#define sres_recvfrom(s,b,len,flags,a,alen) \
+  recvfrom((s),(b),(len),(flags),(a),(alen))
+#define sres_close(s) close((s))
+#define SOCKET_ERROR   (-1)
+#define INVALID_SOCKET ((sres_socket_t)-1)
+#define sres_socket(x,y,z) socket((x),(y),(z))
+#endif
+
 #if 1
 
 #if HAVE_POLL && 0
@@ -145,7 +222,7 @@
 int test_socket(sres_context_t *ctx)
 {
   int af;
-  su_sockeet_t s1, s2, s3, s4;
+  sres_socket_t s1, s2, s3, s4;
   struct sockaddr_storage a[1];
   struct sockaddr_storage a1[1], a2[1], a3[1], a4[1];
   struct sockaddr_in *sin = (void *)a;
@@ -160,10 +237,10 @@
   af = AF_INET;
 
   for (;;) {
-    TEST_1((s1 = su_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
-    TEST_1((s2 = su_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
-    TEST_1((s3 = su_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
-    TEST_1((s4 = su_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
+    TEST_1((s1 = sres_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
+    TEST_1((s2 = sres_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
+    TEST_1((s3 = sres_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
+    TEST_1((s4 = sres_socket(af, SOCK_DGRAM, 0)) != INVALID_SOCKET);
 
     TEST_1(setblocking(s1, 0) == 0);
     TEST_1(setblocking(s2, 0) == 0);
@@ -212,13 +289,13 @@
     TEST(connect(s2, sa4, a4len), 0);
     TEST(getsockname(s2, (struct sockaddr *)a2, &a2len), 0);
 
-    TEST(sendto(s1, "foo", 3, 0, sa4, a4len), 3);
-    TEST(recvfrom(s4, buf, sizeof buf, 0, sa, &alen), 3);
-    TEST(sendto(s4, "bar", 3, 0, sa, alen), 3);
-    TEST(recvfrom(s2, buf, sizeof buf, 0, sa, &alen), -1);
-    TEST(recvfrom(s1, buf, sizeof buf, 0, sa, &alen), 3);
+    TEST(sres_sendto(s1, "foo", 3, 0, sa4, a4len), 3);
+    TEST(sres_recvfrom(s4, buf, sizeof buf, 0, sa, &alen), 3);
+    TEST(sres_sendto(s4, "bar", 3, 0, sa, alen), 3);
+    TEST(sres_recvfrom(s2, buf, sizeof buf, 0, sa, &alen), -1);
+    TEST(sres_recvfrom(s1, buf, sizeof buf, 0, sa, &alen), 3);
 
-    su_close(s1), su_close(s2), su_close(s3), su_close(s4);
+    sres_close(s1), sres_close(s2), sres_close(s3), sres_close(s4);
 
     break;
   }
@@ -1356,14 +1433,15 @@
 {
   char *tmpdir = getenv("TMPDIR");
   char *template;
-  int fd, sink;
+  int fd;
+  sres_socket_t sink;
   struct sockaddr_in sin[1];
   socklen_t sinsize = sizeof sin;
   FILE *f;
   
   BEGIN();
 
-  sink = socket(AF_INET, SOCK_DGRAM, 0); TEST_1(sink != -1);
+  sink = socket(AF_INET, SOCK_DGRAM, 0); TEST_1(sink != INVALID_SOCKET);
   TEST(getsockname(sink, (struct sockaddr *)sin, &sinsize), 0);
   sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
   TEST(bind(sink, (struct sockaddr *)sin, sinsize), 0);
@@ -1427,7 +1505,7 @@
   if (ctx->sinkidx)
     su_root_deregister(ctx->root, ctx->sinkidx);
   ctx->sinkidx = 0;
-  su_close(ctx->sink), ctx->sink = -1;
+  sres_close(ctx->sink), ctx->sink = INVALID_SOCKET;
 
   END();
 }
@@ -1452,7 +1530,7 @@
 
   result = sres_cached_answers(res, sres_type_a, domain);
 
-#if 0
+#if 0  /* Currently, we do not create error records */
   TEST_1(result); TEST_1(result[0] != NULL);
 
   rr_soa = result[0]->sr_soa;
@@ -1463,7 +1541,6 @@
 
   sres_free_answers(res, result);
 #else
-  /* Currently, we do not create error records */
   TEST_1(result == NULL);
 #endif
 
@@ -1541,12 +1618,12 @@
 /* Convert lowercase hex to binary */
 static
 void *hex2bin(char const *test_name,
-	      char const *hex1, char const *hex2, unsigned *binsize)
+	      char const *hex1, char const *hex2, size_t *binsize)
 {
   char output[2048];
   char *bin;
   char const *b;
-  int j;
+  size_t j;
 
   if (hex1 == NULL || binsize == NULL)
     return NULL;
@@ -1619,18 +1696,19 @@
 {
   sres_resolver_t *res = ctx->resolver;
   sres_query_t *q = NULL;
-  int c = ctx->sink;
+  sres_socket_t c = ctx->sink;
   struct sockaddr_storage ss[1];
   struct sockaddr *sa = (void *)ss;
-  socklen_t salen = sizeof ss, binlen;
+  socklen_t salen = sizeof ss;
   char *bin;
-  int i, n;
+  size_t i, binlen;
+  ssize_t n;
   char const *domain = "example.com";
   char query[512];
 
   BEGIN();
 
-  TEST_1(ctx->sink != -1 && ctx->sink != 0);
+  TEST_1(ctx->sink != INVALID_SOCKET && ctx->sink != (sres_socket_t)0);
 
   /* Prepare for test_answer() callback */
   sres_free_answers(ctx->resolver, ctx->result); 
@@ -1641,19 +1719,19 @@
   TEST_1(bin = hex2bin(__func__, hextest, NULL, &binlen));
 
   /* Send responses with one erroneus byte */
-  for (i = 1; i < (int)binlen; i++) {
+  for (i = 1; i < binlen; i++) {
     if (!q) {
       /* We got an error => make new query */
       TEST_1(q = sres_query(res, test_answer, ctx, /* Send query */
 				 sres_type_naptr, domain));
-      TEST_1((n = recvfrom(c, query, sizeof query, 0, sa, &salen)) != -1);
+      TEST_1((n = sres_recvfrom(c, query, sizeof query, 0, sa, &salen)) != -1);
       memcpy(bin, query, 2); /* Copy ID */
     }
     if (i != 1)
       bin[i] ^= 0xff;
     else
       bin[3] ^= SRES_FORMAT_ERR; /* format error -> EDNS0 failure */
-    n = sendto(c, bin, binlen, 0, sa, salen);
+    n = sres_sendto(c, bin, binlen, 0, sa, salen);
     if (i != 1)
       bin[i] ^= 0xff;
     else
@@ -1669,15 +1747,15 @@
   }
 
   /* Send runt responses */
-  for (i = 1; i <= (int)binlen; i++) {
+  for (i = 1; i <= binlen; i++) {
     if (!q) {
       /* We got an error => make new query */
       TEST_1(q = sres_query(res, test_answer, ctx, /* Send query */
 				 sres_type_naptr, domain));
-      TEST_1((n = recvfrom(c, query, sizeof query, 0, sa, &salen)) != -1);
+      TEST_1((n = sres_recvfrom(c, query, sizeof query, 0, sa, &salen)) != -1);
       memcpy(bin, query, 2); /* Copy ID */
     }
-    n = sendto(c, bin, i, 0, sa, salen);
+    n = sres_sendto(c, bin, i, 0, sa, salen);
     if (n == -1)
       perror("sendto");
     while (!poll_sockets(ctx))
@@ -1848,8 +1926,8 @@
     
   TEST(sres_resolver_sockets(res, &socket, 1), n);
 
-#if HAVE_SA_LEN			
-  /* We fail this test in BSD systems */
+#if !__linux
+  /* We fail this test in most systems */
   /* conf_file looks like this:
 --8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--
 nameserver 0.0.0.2
@@ -1886,11 +1964,16 @@
 }
 #endif
 
-void usage(void)
+void usage(int exitcode)
 {
   fprintf(stderr, 
-	  "usage: %s [-v] [-l level] [-] [conf-file] [error-conf-file]\n", 
+	  "usage: %s OPTIONS [-] [conf-file] [error-conf-file]\n"
+	  "\twhere OPTIONS are\n"
+	  "\t    -v be verbose\n"
+	  "\t    -a abort on error\n"
+	  "\t    -l level\n",
 	  name);
+  exit(exitcode);
 }
 
 #include <sofia-sip/su_log.h>
@@ -1912,6 +1995,8 @@
     }
     else if (strcmp(argv[i], "-v") == 0)
       tstflags |= tst_verbatim;
+    else if (strcmp(argv[i], "-a") == 0)
+      tstflags |= tst_abort;
     else if (strcmp(argv[i], "--no-alarm") == 0) {
       o_alarm = 0;
     }
@@ -1930,20 +2015,20 @@
 	level = 3, rest = "";
 
       if (rest == NULL || *rest)
-	usage();
+	usage(1);
       
       su_log_set_level(sresolv_log, level);
     } else
-      usage();
+      usage(1);
   }
 
   if (o_attach) {
-    char buf[8];
+    char buf[8], *line;
 
     fprintf(stderr, "test_sresolv: started with pid %u"
 	    " (press enter to continue)\n", getpid());
 
-    fgets(buf, sizeof buf, stdin);
+    line = fgets(buf, sizeof buf, stdin); (void) line;
   }
 #if HAVE_ALARM
   else if (o_alarm) {

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -38,14 +38,7 @@
 
 COVERAGE_INPUT = 	$(libstun_la_SOURCES) $(include_sofia_HEADERS)
 
-LDADD =			\
-			libstun.la
-			../sresolv/libsresolv.la \
-			../su/libsu.la
-
-stunc_LDADD = libstun.la ../sresolv/libsresolv.la ../su/libsu.la
-
-lookup_stun_server_LDADD = libstun.la ../sresolv/libsresolv.la ../su/libsu.la
+LDADD = 		libstun.la ../sresolv/libsresolv.la ../su/libsu.la
 
 # ----------------------------------------------------------------------
 # tests
@@ -58,6 +51,6 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am
 
 TAG_DLL_FLAGS =		LIST=stun_tag_list

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/stun.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/stun.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/stun/stun.c	Tue Mar 20 23:37:15 2007
@@ -57,7 +57,7 @@
 #include <ws2tcpip.h>
 #endif
 
-#if defined(HAVE_OPENSSL)
+#if HAVE_OPENSSL
 #include <openssl/opensslv.h>
 #endif
 
@@ -181,7 +181,7 @@
   su_sockaddr_t     sr_local_addr[1];   /**< local address */
   su_sockaddr_t     sr_destination[1];
 
-  stun_state_t      sr_state;           /**< Progress states */
+  stun_req_state_t  sr_state;           /**< Progress states */
   int               sr_retry_count;     /**< current retry number */
   long              sr_timeout;         /**< timeout for next sendto() */
 
@@ -218,7 +218,7 @@
   stun_discovery_magic_t *sh_dns_pend_ctx;
   tagi_t             *sh_dns_pend_tags;
 
-#if defined(HAVE_OPENSSL)
+#if HAVE_OPENSSL
   SSL_CTX        *sh_ctx;           /**< SSL context for TLS */
   SSL            *sh_ssl;           /**< SSL handle for TLS */
 #else
@@ -316,7 +316,7 @@
 }
 
 
-#if defined(HAVE_OPENSSL)
+#if HAVE_OPENSSL
 char const stun_version[] = 
  "sofia-sip-stun using " OPENSSL_VERSION_TEXT;
 #else
@@ -325,7 +325,9 @@
 #endif
 
 static int do_action(stun_handle_t *sh, stun_msg_t *binding_response);
+#if HAVE_OPENSSL
 static int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
+#endif
 static int process_binding_request(stun_request_t *req, stun_msg_t *binding_response);
 static stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
 					       stun_action_t action,
@@ -349,9 +351,11 @@
 static void stun_sendto_timer_cb(su_root_magic_t *magic, 
 				 su_timer_t *t,
 				 su_timer_arg_t *arg);
+#if HAVE_OPENSSL
 static void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
 				      su_timer_t *t,
 				      su_timer_arg_t *arg);
+#endif
 static void stun_test_lifetime_timer_cb(su_root_magic_t *magic, 
 					su_timer_t *t,
 					su_timer_arg_t *arg);
@@ -526,7 +530,7 @@
 			      stun_discovery_magic_t *magic,
 			      tag_type_t tag, tag_value_t value, ...)
 {
-#if defined(HAVE_OPENSSL)
+#if HAVE_OPENSSL
   int events = -1;
   int one, err = -1;
   su_wait_t wait[1] = { SU_WAIT_INIT };
@@ -1280,8 +1284,9 @@
  * Internal functions
  *******************************************************************/
 
-#if defined(HAVE_OPENSSL)
-static int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg)
+#if HAVE_OPENSSL
+static 
+int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg)
 {
   stun_discovery_t *sd = arg;
   stun_handle_t *self = sd->sd_handle;
@@ -1298,7 +1303,7 @@
 
   enter;
 
-  SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, self,
+  SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, (void *)self,
 	      events & SU_WAIT_CONNECT ? " CONNECTED" : "",
 	      events & SU_WAIT_ERR     ? " ERR"       : "",
 	      events & SU_WAIT_IN      ? " IN"        : "",
@@ -1528,15 +1533,11 @@
 
   return 0;
 }
-#else
-static int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg)
-{
-  return 0;
-}
+
 #endif /* HAVE_OPENSSL */
 
 
-#if defined(HAVE_OPENSSL)
+#if HAVE_OPENSSL
 static void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
 				      su_timer_t *t,
 				      su_timer_arg_t *arg)
@@ -1566,12 +1567,7 @@
 
   return;
 }
-#else
-static void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
-				      su_timer_t *t,
-				      su_timer_arg_t *arg)
-{
-}
+
 #endif /* HAVE_OPENSSL */
 
 /** Compose a STUN message of the format defined by stun_msg_t
@@ -1664,7 +1660,7 @@
 
   enter;
 
-  SU_DEBUG_7(("%s(%p): events%s%s%s\n", __func__, self,
+  SU_DEBUG_7(("%s(%p): events%s%s%s\n", __func__, (void *)self,
 	      events & SU_WAIT_IN ? " IN" : "",
 	      events & SU_WAIT_OUT ? " OUT" : "",
 	      events & SU_WAIT_ERR ? " ERR" : ""));
@@ -1846,7 +1842,7 @@
     if (stun_process_error_response(binding_response) < 0) {
       SU_DEBUG_3(("%s: Error in Binding Error Response.\n", __func__));
     }
-    req->sr_state = stun_discovery_error;
+    req->sr_state = stun_req_error;
       
     break;
   }
@@ -1936,7 +1932,7 @@
     return 0;
   }
   else if (req->sr_from_y == 0) {
-    if (req->sr_state != stun_discovery_timeout) {
+    if (req->sr_state != stun_req_timeout) {
       /* mapping with X still valid */
       sd->sd_lt_cur = sd->sd_lt;
       sd->sd_lt = (int) (sd->sd_lt + sd->sd_lt_max) / 2;
@@ -2322,8 +2318,6 @@
 
     default:
       break;
-      
-      return;
     }
 
     /* Destroy me immediately */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/Makefile.am
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/Makefile.am	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/Makefile.am	Tue Mar 20 23:37:15 2007
@@ -77,7 +77,11 @@
 	su_alloc.c su_alloc_lock.c su_strdup.c su_sprintf.c \
 	su_strlst.c su_vector.c \
 	su_time.c su_time0.c \
-	su_wait.c su_root.c su_timer.c su_port.c su_port.h \
+	su_wait.c su_root.c su_timer.c \
+	su_port.c  su_port.h \
+	su_base_port.c su_pthread_port.c su_socket_port.c \
+	su_poll_port.c su_epoll_port.c su_select_port.c su_kqueue_port.c \
+	su_devpoll_port.c \
 	su_localinfo.c \
 	su_os_nw.c \
 	su_taglist.c su_tag.c su_tag_io.c \
@@ -88,9 +92,10 @@
 EXTRA_libsu_la_SOURCES = \
 			memmem.c strtoull.c strcasestr.c \
 			memspn.c memcspn.c memccpy.c \
-			inet_ntop.c inet_pton.c getopt.c \
-			su_tag_ref.c
+			inet_ntop.c inet_pton.c poll.c getopt.c \
+			su_tag_ref.c su_win32_port.c
 
+libsu_la_CFLAGS = 	$(AM_CFLAGS) $(SOFIA_CFLAGS)
 libsu_la_LIBADD = 	$(REPLACE_LIBADD)
 libsu_la_DEPENDENCIES = $(REPLACE_LIBADD)
 
@@ -116,4 +121,4 @@
 # ----------------------------------------------------------------------
 # Sofia specific rules
 
-include ../sofia.am
+include $(top_srcdir)/rules/sofia.am

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/addrinfo.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/addrinfo.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/addrinfo.c	Tue Mar 20 23:37:15 2007
@@ -1,7 +1,7 @@
 /*
  * This file is part of the Sofia-SIP package
  *
- * Copyright (C) 2005 Nokia Corporation.
+ * Copyright (C) 2005,2007 Nokia Corporation.
  *
  * Contact: Pekka Pessi <pekka.pessi at nokia.com>
  *
@@ -26,12 +26,13 @@
  * 
  * @section synopsis Synopsis
  *
- * <tt>addrinfo [-pcn46] host service</tt>
+ * <tt>addrinfo [-pcn46] service-name host</tt>
  *
  * @section description Description
  * 
  * The @em addrinfo utility will use su_getaddrinfo() to resolve the network
- * services and print resolved names.
+ * services and print resolved names. See sect 6.1 of RFC3493 and the getaddrinfo(3)
+ * manual page of POSIX 1003.1g, for more information.
  *
  * @section options Options
  *
@@ -56,7 +57,7 @@
  * Written by Pekka Pessi <pekka -dot pessi -at- nokia -dot- com>
  *
  * @section copyright Copyright
- * Copyright (C) 2005 Nokia Corporation.
+ * Copyright (C) 2005,2007 Nokia Corporation.
  *
  * This program is free software; see the source for copying conditions.
  * There is @b NO warranty; not even for @b MERCHANTABILITY or <b>FITNESS
@@ -72,7 +73,7 @@
 #include "sofia-sip/su.h"
 
 char const help[] =
-"usage: addrinfo [-pnc46] [domainname]\n"
+"usage: addrinfo [-pnc46] <servicename> <domainname>\n"
 "\t-p query for passive open\n"
 "\t-n use numeric host names\n"
 "\t-c ask for canonic names\n"

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/poll.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/poll.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,250 @@
+/* This file is part of the Sofia-SIP package.
+
+   Copyright (C) 2005 Nokia Corporation.
+
+   Contact: Pekka Pessi <pekka.pessi at nokia.com>
+
+   This file is originally from GNU C library.
+
+   Copyright (C) 1994,1996,1997,1998,1999,2001,2002
+   Free Software Foundation, Inc.
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include "config.h"
+
+#if HAVE_SELECT
+
+#include "sofia-sip/su.h"
+
+#include <alloca.h>
+#include <string.h>
+
+#include "sofia-sip/su_wait.h"
+
+#undef NBBY
+#undef NFDBITS
+#undef FDSETSIZE
+#undef roundup
+
+#define	NBBY  8					/* bits in a byte */
+#define NFDBITS	(sizeof(long) * NBBY)		/* bits per mask */
+
+#define FDSETSIZE(n) (((n) + NFDBITS - 1) / NFDBITS * (NFDBITS / NBBY))
+#define roundup(n, x) (((n) + (x) - 1) / (x) * (x))
+
+/* Emulated poll() using select(). 
+
+This is used by su_wait().
+
+Poll the file descriptors described by the NFDS structures starting at
+FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+an event to occur; if TIMEOUT is -1, block until an event occurs.
+Returns the number of file descriptors with events, zero if timed out,
+or -1 for errors.  */
+
+int poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+  struct timeval tv;
+  struct pollfd *f;
+  int ready;
+  int maxfd = 0;
+
+#if HAVE_ALLOCA_H
+  static int max_fd_size;
+  int bytes;
+  fd_set *rset, *wset, *xset;
+
+  if (!max_fd_size)
+    max_fd_size = getdtablesize ();
+
+  bytes = FDSETSIZE (max_fd_size);
+
+  rset = alloca (bytes);
+  wset = alloca (bytes);
+  xset = alloca (bytes);
+
+  /* We can't call FD_ZERO, since FD_ZERO only works with sets
+     of exactly __FD_SETSIZE size.  */
+  memset (rset, 0, bytes);
+  memset (wset, 0, bytes);
+  memset (xset, 0, bytes);
+#else
+  fd_set rset[1], wset[1], xset[1];
+
+  FD_ZERO(rset);
+  FD_ZERO(wset);
+  FD_ZERO(xset);
+#endif
+
+  for (f = fds; f < &fds[nfds]; ++f)
+    {
+      f->revents = 0;
+      if (f->fd >= 0)
+	{
+#if HAVE_ALLOCA_H
+	  if (f->fd >= max_fd_size)
+	    {
+	      /* The user provides a file descriptor number which is higher
+		 than the maximum we got from the `getdtablesize' call.
+		 Maybe this is ok so enlarge the arrays.  */
+	      fd_set *nrset, *nwset, *nxset;
+	      int nbytes;
+
+	      max_fd_size = roundup (f->fd, NFDBITS);
+	      nbytes = FDSETSIZE (max_fd_size);
+
+	      nrset = alloca (nbytes);
+	      nwset = alloca (nbytes);
+	      nxset = alloca (nbytes);
+
+	      memset ((char *) nrset + bytes, 0, nbytes - bytes);
+	      memset ((char *) nwset + bytes, 0, nbytes - bytes);
+	      memset ((char *) nxset + bytes, 0, nbytes - bytes);
+
+	      rset = memcpy (nrset, rset, bytes);
+	      wset = memcpy (nwset, wset, bytes);
+	      xset = memcpy (nxset, xset, bytes);
+
+	      bytes = nbytes;
+	    }
+#else
+	  if (f->fd >= FD_SETSIZE) {
+	    errno = EBADF;
+	    return -1;
+	  }
+#endif /* HAVE_ALLOCA_H */
+
+	  if (f->events & POLLIN)
+	    FD_SET (f->fd, rset);
+	  if (f->events & POLLOUT)
+	    FD_SET (f->fd, wset);
+	  if (f->events & POLLPRI)
+	    FD_SET (f->fd, xset);
+	  if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI)))
+	    maxfd = f->fd;
+	}
+    }
+
+  tv.tv_sec = timeout / 1000;
+  tv.tv_usec = (timeout % 1000) * 1000;
+
+  while (1)
+    {
+      ready = select (maxfd + 1, rset, wset, xset,
+		      timeout == -1 ? NULL : &tv);
+
+      /* It might be that one or more of the file descriptors is invalid.
+	 We now try to find and mark them and then try again.  */
+      if (ready == -1 && errno == EBADF)
+	{
+	  struct timeval sngl_tv;
+#if HAVE_ALLOCA_H
+	  fd_set *sngl_rset = alloca (bytes);
+	  fd_set *sngl_wset = alloca (bytes);
+	  fd_set *sngl_xset = alloca (bytes);
+
+	  /* Clear the original set.  */
+	  memset (rset, 0, bytes);
+	  memset (wset, 0, bytes);
+	  memset (xset, 0, bytes);
+#else
+	  fd_set sngl_rset[1];
+	  fd_set sngl_wset[1];
+	  fd_set sngl_xset[1];
+
+	  FD_ZERO(rset);
+	  FD_ZERO(wset);
+	  FD_ZERO(xset);
+#endif
+
+	  /* This means we don't wait for input.  */
+	  sngl_tv.tv_sec = 0;
+	  sngl_tv.tv_usec = 0;
+
+	  maxfd = -1;
+
+	  /* Reset the return value.  */
+	  ready = 0;
+
+	  for (f = fds; f < &fds[nfds]; ++f)
+	    if (f->fd != -1 && (f->events & (POLLIN|POLLOUT|POLLPRI))
+		&& (f->revents & POLLNVAL) == 0)
+	      {
+		int n;
+
+#if HAVE_ALLOCA_H
+		memset (sngl_rset, 0, bytes);
+		memset (sngl_wset, 0, bytes);
+		memset (sngl_xset, 0, bytes);
+#else
+		FD_ZERO(rset);
+		FD_ZERO(wset);
+		FD_ZERO(xset);
+#endif
+
+		if (f->events & POLLIN)
+		  FD_SET (f->fd, sngl_rset);
+		if (f->events & POLLOUT)
+		  FD_SET (f->fd, sngl_wset);
+		if (f->events & POLLPRI)
+		  FD_SET (f->fd, sngl_xset);
+
+		n = select (f->fd + 1, sngl_rset, sngl_wset, sngl_xset,
+			    &sngl_tv);
+		if (n != -1)
+		  {
+		    /* This descriptor is ok.  */
+		    if (f->events & POLLIN)
+		      FD_SET (f->fd, rset);
+		    if (f->events & POLLOUT)
+		      FD_SET (f->fd, wset);
+		    if (f->events & POLLPRI)
+		      FD_SET (f->fd, xset);
+		    if (f->fd > maxfd)
+		      maxfd = f->fd;
+		    if (n > 0)
+		      /* Count it as being available.  */
+		      ++ready;
+		  }
+		else if (errno == EBADF)
+		  f->revents |= POLLNVAL;
+	      }
+	  /* Try again.  */
+	  continue;
+	}
+
+      break;
+    }
+
+  if (ready > 0) 
+    for (f = fds; f < &fds[nfds]; ++f)
+      {
+	if (f->fd >= 0)
+	  {
+	    if (FD_ISSET (f->fd, rset))
+	      f->revents |= POLLIN;
+	    if (FD_ISSET (f->fd, wset))
+	      f->revents |= POLLOUT;
+	    if (FD_ISSET (f->fd, xset))
+	      f->revents |= POLLPRI;
+	  }
+      }
+
+  return ready;
+}
+
+#endif

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/run_test_su
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/run_test_su	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/run_test_su	Tue Mar 20 23:37:15 2007
@@ -2,18 +2,33 @@
 
 rc=0
 
+run=no
+
+for SU_PORT in select kqueue devpoll epoll poll ; do
+
+export SU_PORT
+
+egrep -q -i '^#define have_(sys_)?'$SU_PORT ../../config.h ||
+continue
+
+run=yes
+
 if $VALGRIND ./test_su ; then
-    echo PASS: multithread test_su
+    echo PASS: multithread test_su with $SU_PORT
 else
-    echo FAIL: multithread test_su failed
+    echo FAIL: multithread test_su with $SU_PORT failed
     rc=1
 fi
 
 if $VALGRIND ./test_su -s ; then
-    echo PASS: singlethread test_su
+    echo PASS: singlethread test_su with $SU_PORT
 else
-    echo FAIL: singlethread test_su failed
+    echo FAIL: singlethread test_su with $SU_PORT failed
     rc=1
 fi
 
+done
+
+test $run = no && exit 77
+
 exit $rc

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/run_test_su_osx
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/run_test_su_osx	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,24 @@
+#! /bin/bash
+
+rc=0
+
+run=no
+
+export SU_PORT=CoreFoundation
+
+
+if $VALGRIND ./test_su_osx ; then
+    echo PASS: multithread test_su_osx with $SU_PORT
+else
+    echo FAIL: multithread test_su_osx with $SU_PORT failed
+    rc=1
+fi
+
+if $VALGRIND ./test_su_osx -s ; then
+    echo PASS: singlethread test_su_osx with $SU_PORT
+else
+    echo FAIL: singlethread test_su_osx with $SU_PORT failed
+    rc=1
+fi
+
+exit $rc

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su.h	Tue Mar 20 23:37:15 2007
@@ -220,13 +220,15 @@
  * } WSABUF, *LPWSABUF;
  * @endcode
  *
- * @note Ordering of the fields is reversed on Windows. Do not initialize this
- * structure with static initializer, but assign both fields separately.
+ * @note Ordering of the fields is reversed on Windows. Do not initialize
+ * this structure with static initializer, but assign both fields
+ * separately. Note that the type of the siv_len is #su_ioveclen_t which is
+ * defined as u_long on Windows and size_t on POSIX.
  *
  * For historical reasons, the structure is known as #msg_iovec_t in @msg
  * module.
  *
- * @sa #su_iovec_t, #su_ioveclen_t, SU_IOVECLEN_MAX, su_vsend(), su_vrecv(), 
+ * @sa #su_ioveclen_t, SU_IOVECLEN_MAX, su_vsend(), su_vrecv(), 
  * #msg_iovec_t, msg_iovec(), msg_recv_iovec(), 
  * @c struct @c iovec defined in <sys/uio.h>, writev(2), readv(2),
  * sendmsg(), recvmsg(),

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_bm.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_bm.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_bm.h	Tue Mar 20 23:37:15 2007
@@ -45,15 +45,15 @@
 
 SOFIAPUBFUN bm_fwd_table_t *bm_memmem_study(char const *needle, size_t nlen);
 
-SOFIAPUBFUN char const* bm_memmem(char const *haystack, size_t hlen,
-				  char const *needle, size_t nlen,
-				  bm_fwd_table_t *fwd);
+SOFIAPUBFUN char *bm_memmem(char const *haystack, size_t hlen,
+			    char const *needle, size_t nlen,
+			    bm_fwd_table_t *fwd);
 
 SOFIAPUBFUN bm_fwd_table_t *bm_memcasemem_study(char const *needle, size_t);
 
-SOFIAPUBFUN char const* bm_memcasemem(char const *haystack, size_t hlen,
-				      char const *needle, size_t nlen,
-				      bm_fwd_table_t *fwd);
+SOFIAPUBFUN char *bm_memcasemem(char const *haystack, size_t hlen,
+				char const *needle, size_t nlen,
+				bm_fwd_table_t *fwd);
 
 SOFIA_END_DECLS
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h.in	Tue Mar 20 23:37:15 2007
@@ -75,6 +75,12 @@
 /** Define as 1 if you have OSX CoreFoundation interface */
 #undef SU_HAVE_OSX_CF_API
 
+/** Define as 1 if you want to enable experimental features.
+ *
+ * Use --enable-experimental with ./configure
+ */
+#undef SU_HAVE_EXPERIMENTAL
+
 /** Define as 1 if you have inline functions */
 #undef SU_HAVE_INLINE
 /** Define as suitable declarator inline functions */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_debug.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_debug.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_debug.h	Tue Mar 20 23:37:15 2007
@@ -74,9 +74,9 @@
 #endif
 
 #define SU_DEBUG_DEF(level) \
-  static SU_INLINE void su_debug_##level(char const *fmt, ...) \
+  su_inline void su_debug_##level(char const *fmt, ...) \
     __attribute__ ((__format__ (printf, 1, 2))); \
-  void su_debug_##level(char const *fmt, ...) \
+  su_inline void su_debug_##level(char const *fmt, ...) \
     { va_list ap; va_start(ap, fmt); su_vllog(SU_LOG, level, fmt, ap); va_end(ap); }
 
 SU_DEBUG_DEF(0)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_tag.h	Tue Mar 20 23:37:15 2007
@@ -174,7 +174,7 @@
 #if SU_HAVE_INLINE
 su_inline tag_value_t tag_int_v(int v) { return (tag_value_t)v; }
 su_inline tag_value_t tag_int_vr(int *vp) { return (tag_value_t)vp; }
-su_inline tag_value_t tag_uint_v(size_t v) { return (tag_value_t)v; }
+su_inline tag_value_t tag_uint_v(unsigned v) { return (tag_value_t)v; }
 su_inline tag_value_t tag_uint_vr(unsigned *vp) { return (tag_value_t)vp; }
 su_inline tag_value_t tag_usize_v(usize_t v) { return (tag_value_t)v; }
 su_inline tag_value_t tag_usize_vr(usize_t *vp) { return (tag_value_t)vp; }
@@ -197,9 +197,9 @@
   static inline tag_value_t tag_ptr_v(void const *v)
   { return (tag_value_t)v; }
   static inline tag_value_t tag_ptr_vr(void *vp, void const *p) 
-  { return(tag_value_t)vp; }
+  { return (tag_value_t)vp; }
   static inline tag_value_t tag_str_v(char *v) { return (tag_value_t)v; }
-  static inline tag_value_t tag_str_vr(char **vp) {return(tag_value_t)vp;}
+  static inline tag_value_t tag_str_vr(char **vp) {return (tag_value_t)vp;}
 }
 #endif
 su_inline tag_value_t tag_filter_v(tag_filter_f *v) {return(tag_value_t)v;}
@@ -208,10 +208,14 @@
 #define tag_int_vr(v)  (tag_value_t)(v)
 #define tag_uint_v(v)  (tag_value_t)(v)
 #define tag_uint_vr(v) (tag_value_t)(v)
+#define tag_usize_v(v) (tag_value_t)(v)
+#define tag_usize_vr(v) (tag_value_t)(v)
 #define tag_bool_v(v)  (tag_value_t)(v != 0)
 #define tag_bool_vr(v) (tag_value_t)(v)
 #define tag_ptr_v(v)   (tag_value_t)(v)
 #define tag_ptr_vr(v,x) (tag_value_t)(v)
+#define tag_cptr_v(v)   (tag_value_t)(v)
+#define tag_cptr_vr(v,x) (tag_value_t)(v)
 #define tag_cstr_v(v)  (tag_value_t)(v)
 #define tag_cstr_vr(v) (tag_value_t)(v)
 #define tag_str_v(v)   (tag_value_t)(v)

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_time.h	Tue Mar 20 23:37:15 2007
@@ -104,9 +104,8 @@
 SOFIAPUBFUN uint32_t su_ntp_fraq(su_time_t t);
 SOFIAPUBFUN uint32_t su_time_ms(su_time_t t);
 #else
-static SU_INLINE
 /** Middle 32 bit of NTP timestamp. */
-uint32_t su_ntp_fraq(su_time_t t)
+su_inline uint32_t su_ntp_fraq(su_time_t t)
 {
   /*
    * Multiply usec by 0.065536 (ie. 2**16 / 1E6)
@@ -116,9 +115,8 @@
   return (t.tv_sec << 16) + (1024 * t.tv_usec + 7812) / 15625;
 }
 
-static SU_INLINE
 /** Time as milliseconds. */
-uint32_t su_time_ms(su_time_t t)
+su_inline uint32_t su_time_ms(su_time_t t)
 {
   return t.tv_sec * 1000 + (t.tv_usec + 500) / 1000;
 }

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/su_wait.h	Tue Mar 20 23:37:15 2007
@@ -30,6 +30,7 @@
  * @file sofia-sip/su_wait.h Syncronization and threading interface.
  *
  * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Martti Mela <Martti.Mela at nokia.com>
  * 
  * @date Created: Tue Sep 14 15:51:04 1999 ppessi
  */
@@ -44,7 +45,10 @@
 #ifndef SU_TIME_H
 #include "sofia-sip/su_time.h"
 #endif
-#if SU_HAVE_POLL
+
+#if SU_HAVE_KQUEUE
+#include <sys/event.h>
+#elif SU_HAVE_POLL
 #include <sys/poll.h>
 #endif
 
@@ -53,7 +57,36 @@
 /* ---------------------------------------------------------------------- */
 /* Constants */
 
-#if SU_HAVE_POLL || DOCUMENTATION_ONLY
+#if SU_HAVE_KQUEUE
+/** Compare wait object. @HI */
+#define SU_WAIT_CMP(x, y) \
+ (((x).ident - (y).ident) ? ((x).ident - (y).ident) : ((x).flags - (y).flags))
+
+/** Incoming data is available on socket. @HI */
+#define SU_WAIT_IN      (EVFILT_READ)
+/** Data can be sent on socket. @HI */
+#define SU_WAIT_OUT     (EVFILT_WRITE)
+/** Socket is connected. @HI */
+#define SU_WAIT_CONNECT (EVFILT_WRITE)
+/** An error occurred on socket. @HI */
+#define SU_WAIT_ERR     (EV_ERROR)
+/** The socket connection was closed. @HI */
+#define SU_WAIT_HUP     (EV_EOF)
+/** A listening socket accepted a new connection. @HI */
+#define SU_WAIT_ACCEPT  (EVFILT_READ)
+
+/** No timeout for su_wait(). */
+#define SU_WAIT_FOREVER (-1)
+/** The return value of su_wait() if timeout occurred. */
+#define SU_WAIT_TIMEOUT (-2)
+
+/** Initializer for a wait object. @HI */
+#define SU_WAIT_INIT    { INVALID_SOCKET, 0, 0, 0, 0, NULL }
+
+/** Maximum number of sources supported by su_wait() */
+#define SU_WAIT_MAX    (0x7fffffff)
+
+#elif SU_HAVE_POLL || DOCUMENTATION_ONLY
 /** Compare wait object. @HI */
 #define SU_WAIT_CMP(x, y) \
  (((x).fd - (y).fd) ? ((x).fd - (y).fd) : ((x).events - (y).events))
@@ -101,35 +134,73 @@
 #define SU_WAIT_MAX    (64)
 
 #else
-#define SU_WAIT_CMP(x, y) 
-#define SU_WAIT_IN      
-#define SU_WAIT_OUT     
-#define SU_WAIT_ERR     
-#define SU_WAIT_HUP     
-#define SU_WAIT_ACCEPT  
-#define SU_WAIT_FOREVER 
-#define SU_WAIT_TIMEOUT 
+/* If nothing works, try these */
+
+#define	POLLIN		0x001
+#define	POLLPRI		0x002
+#define	POLLOUT		0x004
+
+#ifdef __USE_XOPEN
+#define	POLLRDNORM	0x040
+#define	POLLRDBAND	0x080
+#define	POLLWRNORM	0x100
+#define	POLLWRBAND	0x200
+#endif
+
+/* These for pollfd.revents */
+#define POLLERR         0x008
+#define POLLHUP         0x010
+#define POLLNVAL        0x020
+
+#define SU_WAIT_CMP(x, y) \
+ (((x).fd - (y).fd) ? ((x).fd - (y).fd) : ((x).events - (y).events))
 
-#define SU_WAIT_INIT
+#define SU_WAIT_IN      POLLIN
+#define SU_WAIT_OUT     POLLOUT
+#define SU_WAIT_CONNECT POLLOUT
+#define SU_WAIT_ERR     POLLERR
+#define SU_WAIT_HUP     POLLHUP
+#define SU_WAIT_ACCEPT  POLLIN
+#define SU_WAIT_FOREVER (-1)
+#define SU_WAIT_TIMEOUT (-2)
+
+#define SU_WAIT_INIT    { INVALID_SOCKET, 0, 0 }
+
+/** Maximum number of sources supported by su_wait() */
+#define SU_WAIT_MAX    (0x7fffffff)
 
 #endif
 
 /* ---------------------------------------------------------------------- */
 /* Types */
 
-#if 0
-typedef struct _pollfd {
-  su_socket_t fd;           /* file descriptor */
-  short events;     /* requested events */
-  short revents;    /* returned events */
-} su_wait_t;
+/** Wait object. */
+#if SU_HAVE_KQUEUE
+typedef struct kevent su_wait_t;
 #elif SU_HAVE_POLL
 typedef struct pollfd su_wait_t;
 #elif SU_HAVE_WINSOCK
 typedef HANDLE su_wait_t;
 #else
-/** Wait object. */
-typedef struct os_specific su_wait_t;
+/* typedef struct os_specific su_wait_t; */
+typedef struct pollfd su_wait_t;
+struct pollfd {
+  su_socket_t fd;   /* file descriptor */
+  short events;     /* requested events */
+  short revents;    /* returned events */
+};
+
+
+/* Type used for the number of file descriptors.  */
+typedef unsigned long int nfds_t;
+
+/* Poll the file descriptors described by the NFDS structures starting at
+   FDS.  If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
+   an event to occur; if TIMEOUT is -1, block until an event occurs.
+   Returns the number of file descriptors with events, zero if timed out,
+   or -1 for errors.  */
+int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
+
 #endif
 
 /* Used by AD */
@@ -191,7 +262,7 @@
  */
 typedef SU_WAKEUP_ARG_T su_wakeup_arg_t;
 
-/** Wakeup callback function prototype. 
+/** Wakeup callback function pointer type. 
  *
  * Whenever a registered wait object receives an event, the @link
  * ::su_wakeup_f callback function @endlink is invoked.
@@ -316,9 +387,13 @@
 #define SU_MSG_R_INIT   { NULL }
 
 /** Message delivery function type. */
-typedef void (*su_msg_f)(su_root_magic_t *magic, 
-			 su_msg_r msg,
-			 su_msg_arg_t *arg);
+typedef void su_msg_function(su_root_magic_t *magic, 
+			     su_msg_r msg,
+			     su_msg_arg_t *arg);
+
+/** Message delivery function pointer type. */
+typedef su_msg_function *su_msg_f;
+
 
 /* ---------------------------------------------------------------------- */
 
@@ -350,11 +425,15 @@
 SOFIAPUBFUN int su_wait_events(su_wait_t *wait, su_socket_t s);
 SOFIAPUBFUN int su_wait_mask(su_wait_t *dst, su_socket_t s, int events);
 
-#if SU_HAVE_POLL
+#if !HAVE_WIN32 && (SU_HAVE_POLL || HAVE_SELECT)
 static inline
 su_socket_t su_wait_socket(su_wait_t *wait)
 {
+#if SU_HAVE_KQUEUE
+  return wait->ident;
+#else
   return wait->fd;
+#endif
 }
 #endif
 
@@ -362,6 +441,7 @@
 SOFIAPUBFUN su_root_t *su_root_create(su_root_magic_t *magic)
   __attribute__((__malloc__));
 SOFIAPUBFUN void su_root_destroy(su_root_t*);
+SOFIAPUBFUN char const *su_root_name(su_root_t *self);
 SOFIAPUBFUN int su_root_set_magic(su_root_t *self, su_root_magic_t *magic);
 SOFIAPUBFUN su_root_magic_t *su_root_magic(su_root_t *root);
 SOFIAPUBFUN int su_root_register(su_root_t*, su_wait_t *, 
@@ -472,6 +552,34 @@
 SOFIAPUBFUN int su_clone_pause(su_clone_r);
 SOFIAPUBFUN int su_clone_resume(su_clone_r);
 
+/* ---------------------------------------------------------------------- */
+/* Different su_root_t implementations */
+
+typedef su_port_t *su_port_create_f(void);
+typedef int su_clone_start_f(su_root_t *parent,
+			    su_clone_r return_clone,
+			    su_root_magic_t *magic,
+			    su_root_init_f init,
+			    su_root_deinit_f deinit);
+
+SOFIAPUBFUN void su_port_prefer(su_port_create_f *f, su_clone_start_f *);
+
+SOFIAPUBFUN su_port_create_f su_default_port_create;
+SOFIAPUBFUN su_port_create_f su_epoll_port_create;
+SOFIAPUBFUN su_port_create_f su_poll_port_create;
+SOFIAPUBFUN su_port_create_f su_wsaevent_port_create;
+SOFIAPUBFUN su_port_create_f su_select_port_create;
+SOFIAPUBFUN su_port_create_f su_kqueue_port_create;
+SOFIAPUBFUN su_port_create_f su_devpoll_port_create;
+
+SOFIAPUBFUN su_clone_start_f su_default_clone_start;
+SOFIAPUBFUN su_clone_start_f su_epoll_clone_start;
+SOFIAPUBFUN su_clone_start_f su_poll_clone_start;
+SOFIAPUBFUN su_clone_start_f su_wsaevent_clone_start;
+SOFIAPUBFUN su_clone_start_f su_select_clone_start;
+SOFIAPUBFUN su_clone_start_f su_kqueue_clone_start;
+SOFIAPUBFUN su_clone_start_f su_devpoll_clone_start;
+
 SOFIA_END_DECLS
 
 #endif /* SU_WAIT_H */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/tstdef.h
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/tstdef.h	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/sofia-sip/tstdef.h	Tue Mar 20 23:37:15 2007
@@ -145,13 +145,14 @@
   tst_verbatim = 1,
   /** If (TSTFLAGS & tst_abort) is non-zero, abort() when failed. */
   tst_abort = 2,
+  /** If (TSTFLAGS & tst_log) is non-zero, log intermediate results. */
+  tst_log = 4
 };
 
 #ifndef TSTFLAGS
 #error <TSTFLAGS is not defined>
 #endif
 
-#ifdef TSTFLAGS
 /** Begin a test function. @HIDE */
 #define BEGIN() BEGIN_(TSTFLAGS); { extern int tstdef_dummy
 /** End a test function. @HIDE */
@@ -179,17 +180,12 @@
 /** Test that @a suite has same size as @a expected. @HIDE */
 #define TEST_SIZE(suite, expected) TEST_SIZE_(TSTFLAGS, suite, expected)
 
-#else
-/* Deprecated */
-#define TEST0(flags, suite) TEST_1_(flags, suite)
-#define TEST_1(flags, suite) TEST_1_(flags, suite)
-#define TEST_VOID(flags, suite) TEST_VOID_(flags, suite)
-#define TEST(flags, suite, expect) TEST_(flags, suite, expect)
-#define TEST64(flags, suite, expect) TEST64_(flags, suite, expect)
-#define TEST_S(flags, suite, expect) TEST_S_(flags, suite, expect)
-#define BEGIN(flags) BEGIN_(flags) { extern int tstdef_dummy
-#define END(flags) (void) tstdef_dummy;  } END_(flags) 
-#endif
+/** Print in torture test with -l option */
+#define TEST_LOG(x)		       \
+  do {				       \
+    if (tstflags & tst_log)	       \
+      printf x;			       \
+  } while(0)
 
 #define TEST_FAILED(flags) \
   ((flags) & tst_abort) ? abort() : (void)0; return 1

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su.c	Tue Mar 20 23:37:15 2007
@@ -32,14 +32,16 @@
 
 #include "config.h" 
 
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-
 #include "sofia-sip/su.h"
 #include "sofia-sip/su_log.h"
 #include "sofia-sip/su_alloc.h"
 
+#include <stdio.h>
+#include <string.h>
+#if HAVE_SIGNAL
+#include <signal.h>
+#endif
+
 #if !SU_HAVE_BSDSOCK && !SU_HAVE_WINSOCK
 #error Bad configuration
 #endif
@@ -67,7 +69,9 @@
 {
   su_home_threadsafe(NULL);
 
+#if HAVE_SIGPIPE
   signal(SIGPIPE, SIG_IGN);	/* we want to get EPIPE instead */
+#endif
 
   su_log_init(su_log_default);
   su_log_init(su_log_global);

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_addrinfo.c	Tue Mar 20 23:37:15 2007
@@ -887,10 +887,16 @@
 		   su_addrinfo_t const *hints,
 		   su_addrinfo_t **res)
 {
+  int retval;
+  su_addrinfo_t *ai;
+
+  if (!service || service[0] == '\0')
+    service = "0";
+
 #if HAVE_SCTP
   if (res && hints && hints->ai_protocol == IPPROTO_SCTP) {
-    su_addrinfo_t *ai, system_hints[1];
-    int retval, socktype;
+    su_addrinfo_t system_hints[1];
+    int socktype;
 
     socktype = hints->ai_socktype;
     
@@ -920,7 +926,31 @@
   }
 #endif
 
-  return getaddrinfo(node, service, hints, res);
+  retval = getaddrinfo(node, service, hints, res);
+
+  if (retval == 0) {
+    for (ai = *res; ai; ai = ai->ai_next) {
+      if (ai->ai_protocol)
+	continue;
+
+      if (hints && hints->ai_protocol) {
+	ai->ai_protocol = hints->ai_protocol;
+	continue;
+      }
+
+      if (ai->ai_family != AF_INET 
+#if SU_HAVE_IN6
+	  && ai->ai_family != AF_INET6
+#endif
+	  ) continue;
+
+      if (ai->ai_socktype == SOCK_STREAM)
+	ai->ai_protocol = IPPROTO_TCP;
+      else if (ai->ai_socktype == SOCK_DGRAM)
+	ai->ai_protocol = IPPROTO_UDP;
+    }
+  }
+  return retval;
 }
 
 /** Free su_addrinfo_t structure allocated by su_getaddrinfo(). */

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc.c	Tue Mar 20 23:37:15 2007
@@ -87,8 +87,8 @@
  *
  * As you might have guessed, it is also possible to use reference counting
  * with home objects. The function su_home_ref() increases the reference
- * count, su_home_unref() decreases. A newly allocated home object has
- * reference count of 1.
+ * count, su_home_unref() decreases it. A newly allocated or initialized
+ * home object has reference count of 1.
  *
  * @note Please note that while it is possible to create new references to
  * secondary home objects which have a parent home, the secondary home
@@ -104,10 +104,13 @@
  * @note
  *
  * The su_home_destroy() function is deprecated as it does not free the home
- * object itself.
+ * object itself. Like su_home_deinit(), it should be called only on home
+ * objects with reference count of 1.
  *
- * The function su_home_init() initializes a home object with infinite
- * reference count. It must be deinitialized with su_home_deinit().
+ * The function su_home_init() initializes a home object structure. When the
+ * initialized home object is destroyed or deinitialized or its reference
+ * count reaches zero, the memory allocate thorugh it reclaimed but the home
+ * object structure itself is not freed.
  *
  * @section su_home_destructor_usage Destructors
  *
@@ -148,19 +151,20 @@
  * @section su_alloc_threadsafe Threadsafe Operation
  *
  * If multiple threads need to access same home object, it must be marked as
- * @c threadsafe by calling su_home_threadsafe() with the home pointer as
+ * @e threadsafe by calling su_home_threadsafe() with the home pointer as
  * argument. The threadsafeness is not inherited by clones.
  *
  * The threadsafe home objects can be locked and unlocked with
- * su_home_mutex_lock() and su_home_mutex_unlock().
+ * su_home_mutex_lock() and su_home_mutex_unlock(). These operations are
+ * no-op on home object that is not threadsafe.
  *
  * @section su_alloc_preloading Preloading a Memory Home
  *
- * In some situations there is quite heavy overhead when using the global
- * heap allocator. The overhead caused by the large number of small
+ * In some situations there is quite heavy overhead if the global heap
+ * allocator is used. The overhead caused by the large number of small
  * allocations can be reduced by using su_home_preload(): it allocates or
  * preloads some a memory to home to be used as a kind of private heap. The
- * preloaded memory area is then used to satisfy small enough allocations.
+ * preloaded memory area is then used to satisfy small enough allocations. 
  * For instance, the SIP parser typically preloads some 2K of memory when it
  * starts to parse the message.
  *
@@ -172,10 +176,9 @@
  * far as possible; if it is not enough, allocation is made from heap.
  *
  * The word @e auto refers to the automatic scope; however, the home object
- * initialized with su_home_auto() must be explicitly deinitialized with
- * su_home_deinit() when the program exits the scope where the stack frame
- * used in su_home_auto() was allocate.
- * 
+ * that was initialized with su_home_auto() must be explicitly deinitialized
+ * with su_home_deinit() or su_home_unref() when the program exits the scope
+ * where the stack frame used in su_home_auto() was allocated.
  */
 
 #include <sofia-sip/su_config.h>
@@ -196,6 +199,8 @@
 void (*su_home_mutex_locker)(void *mutex);
 void (*su_home_mutex_unlocker)(void *mutex);
 
+void (*su_home_destroy_mutexes)(void *mutex);
+
 #define MEMLOCK(h)   \
   (((h) && (h)->suh_lock ? su_home_locker((h)->suh_lock) : (void)0), (h)->suh_blocks)
 #define UNLOCK(h) (((h) && (h)->suh_lock ? su_home_unlocker((h)->suh_lock) : (void)0), NULL)
@@ -221,10 +226,10 @@
 
 #define ALIGNMENT (8)
 #define ALIGN(n) (size_t)(((n) + (ALIGNMENT - 1)) & (size_t)~(ALIGNMENT - 1))
-#define SIZEBITS (sizeof (size_t) * 8 - 1)
+#define SIZEBITS (sizeof (unsigned) * 8 - 1)
 
 typedef struct {
-  size_t   sua_size:SIZEBITS;	/**< Size of the block */
+  unsigned sua_size:SIZEBITS;	/**< Size of the block */
   unsigned sua_home:1;		/**< Is this another home? */
   unsigned :0;
   void    *sua_data;		/**< Data pointer */
@@ -242,8 +247,9 @@
 
   unsigned    sub_prsize:16;	/**< Preload size */
   unsigned    sub_prused:16;	/**< Used from preload */
-  unsigned    sub_auto:1;	/**< struct su_block_s is from stack! */
-  unsigned    sub_preauto:1;	/**< Preload is from stack! */
+  unsigned    sub_hauto:1;      /**< "Home" is not from malloc */
+  unsigned    sub_auto:1;	/**< struct su_block_s is not from malloc */
+  unsigned    sub_preauto:1;	/**< Preload is not from malloc */
   unsigned    sub_auto_all:1;	/**< Everything is from stack! */
   unsigned :0;
 
@@ -255,7 +261,7 @@
 static void su_home_stats_alloc(su_block_t *, void *p, void *preload,
 				size_t size, int zero);
 static void su_home_stats_free(su_block_t *sub, void *p, void *preload,
-			       size_t size);
+			       unsigned size);
 
 static void _su_home_deinit(su_home_t *home);
 
@@ -345,7 +351,7 @@
   size_t size, term;
   assert(sua);
   if (sua) {
-    size = sua->sua_size;
+    size = (size_t)sua->sua_size;
     memcpy(&term, (char *)sua->sua_data + size, sizeof (term));
     assert(size - term == 0);
     return size - term == 0;
@@ -358,8 +364,9 @@
 
 /** Allocate the block hash table.
  *
- * The function su_hash_alloc() allocates a block hash table of @a n
- * elements.
+ * @internal
+ *
+ * Allocate a block hash table of @a n elements.
  *
  * @param home  pointer to home object
  * @param n     number of buckets in hash table
@@ -372,8 +379,12 @@
 {
   su_block_t *b = calloc(1, offsetof(su_block_t, sub_nodes[n]));
 
-  if (b)
+  if (b) {
+    /* Implicit su_home_init(); */
+    b->sub_ref = 1;
+    b->sub_hauto = 1;
     b->sub_n = n;
+  }
 
   return b;
 }
@@ -382,6 +393,8 @@
 
 /** Allocate a memory block.
  *
+ * @internal
+ *
  * Precondition: locked home
  *
  * @param home home to allocate
@@ -399,7 +412,10 @@
 {
   void *data, *preload = NULL;
   
-  assert (size < (1UL << SIZEBITS));
+  assert (size < (((size_t)1) << SIZEBITS));
+
+  if (size >= ((size_t)1) << SIZEBITS)
+    return (void)(errno = ENOMEM), NULL;
 
   if (sub == NULL || 3 * sub->sub_used > 2 * sub->sub_n) {
     /* Resize the hash table */
@@ -429,6 +445,7 @@
       b2->sub_preload = sub->sub_preload;
       b2->sub_prsize = sub->sub_prsize;
       b2->sub_prused = sub->sub_prused;
+      b2->sub_hauto = sub->sub_hauto;
       b2->sub_preauto = sub->sub_preauto;
       b2->sub_destructor = sub->sub_destructor;
       /* auto_all is not copied! */
@@ -473,7 +490,7 @@
     if (!preload)
       sub->sub_auto_all = 0;
 
-    if (zero == do_clone) {
+    if (zero >= do_clone) {
       /* Prepare cloned home */ 
       su_home_t *subhome = data;
 
@@ -485,13 +502,13 @@
 
       subhome->suh_size = (unsigned)size;
       subhome->suh_blocks->sub_parent = home;
-      subhome->suh_blocks->sub_ref = 1;
+      subhome->suh_blocks->sub_hauto = 0;
     }
 
     /* OK, add the block to the hash table. */
 
     sua = su_block_add(sub, data); assert(sua);
-    sua->sua_size = size;
+    sua->sua_size = (unsigned)size;
     sua->sua_home = zero > 1;
 
     if (sub->sub_stats)
@@ -525,16 +542,16 @@
   assert(size >= sizeof (*home));
 
   if (size < sizeof (*home))
-    return (errno = EINVAL), NULL;
+    return (void)(errno = EINVAL), NULL;
   else if (size > INT_MAX)
-    return (errno = ENOMEM), NULL;
+    return (void)(errno = ENOMEM), NULL;
 
   home = calloc(1, size);
   if (home) {
     home->suh_size = (int)size;
     home->suh_blocks = su_hash_alloc(SUB_N);
     if (home->suh_blocks)
-      home->suh_blocks->sub_ref = 1;
+      home->suh_blocks->sub_hauto = 0;
     else
       free(home), home = NULL;
   }
@@ -612,12 +629,10 @@
 
 /**Unreference a su_home_t object.
  *
- * The function su_home_unref() decrements the reference count on a home
- * object and destroys and frees it and the memory allocations using it.
- *
- * @param home memory pool object to be unreferences
+ * Decrements the reference count on home object and destroys and frees it
+ * and the memory allocations using it if the reference count reaches 0.
  *
- * The function return values is 
+ * @param home memory pool object to be unreferenced
  *
  * @retval 1 if object was freed
  * @retval 0 if object is still alive
@@ -650,8 +665,10 @@
     return 1;
   }
   else {
+    int hauto = sub->sub_hauto;
     _su_home_deinit(home);
-    free(home);
+    if (!hauto)
+      free(home);
     /* UNLOCK(home); */
     return 1;
   }
@@ -725,7 +742,7 @@
 
 /** Allocate a memory block.
  *
- * The function su_alloc() allocates a memory block of a given @a size.
+ * Allocates a memory block of a given @a size.
  *
  * If @a home is NULL, this function behaves exactly like malloc().
  *
@@ -752,9 +769,9 @@
 
 /**Free a memory block.
  *
- * The function su_free() frees a single memory block. The @a home must be
- * the owner of the memory block (usually the memory home used to allocate
- * the memory block, or NULL if no home was used).
+ * Frees a single memory block. The @a home must be the owner of the memory
+ * block (usually the memory home used to allocate the memory block, or NULL
+ * if no home was used).
  *
  * @param home  pointer to home object
  * @param data  pointer to the memory block to be freed
@@ -792,7 +809,7 @@
       }
 
 #if MEMCHECK != 0
-      memset(data, 0xaa, allocation->sua_size);
+      memset(data, 0xaa, (size_t)allocation->sua_size);
 #endif
 
       memset(allocation, 0, sizeof (*allocation));
@@ -810,9 +827,9 @@
 
 /** Check home consistency.
  *
- * The function su_home_check() ensures that the home structure and all
- * memory blocks allocated through it are consistent.  It can be used to
- * catch memory allocation and usage errors.
+ * Ensures that the home structure and all memory blocks allocated through
+ * it are consistent. It can be used to catch memory allocation and usage
+ * errors.
  *
  * @param home Pointer to a memory home.
  */
@@ -851,11 +868,10 @@
 /**
  * Create an su_home_t object.
  *
- * The function su_home_create() creates a home object.  A home object is
- * used to collect multiple memory allocations, so that they all can be
- * freed by calling su_home_unref().
+ * Creates a home object. A home object is used to collect multiple memory
+ * allocations, so that they all can be freed by calling su_home_unref().
  *
- * @return This function returns a pointer to an @c su_home_t object, or @c
+ * @return This function returns a pointer to an #su_home_t object, or 
  * NULL upon an error. 
  */
 su_home_t *su_home_create(void)
@@ -863,10 +879,10 @@
   return su_home_new(sizeof(su_home_t));
 }
 
-/** Deinitialize a home object
+/** Destroy a home object
  *
- * The function su_home_destroy() frees all memory blocks associated with a
- * home object. Note that the home object is not freed.
+ * Frees all memory blocks associated with a home object. Note that the home
+ * object structure is not freed.
  *
  * @param home pointer to a home object
  *
@@ -876,14 +892,22 @@
  */
 void su_home_destroy(su_home_t *home)
 {
-  su_home_deinit(home);
-  /* XXX - home itself is not destroyed */
+  if (MEMLOCK(home)) {
+    assert(home->suh_blocks);
+    assert(home->suh_blocks->sub_ref == 1);
+    if (!home->suh_blocks->sub_hauto)
+      /* should warn user */;
+    home->suh_blocks->sub_hauto = 1;
+    _su_home_deinit(home);
+    /* UNLOCK(home); */
+  }
 }
 
 /** Initialize an su_home_t struct.
  *
- * The function su_home_init() initializes an su_home_t structure. It can be
- * used when the home structure is allocated from stack.
+ * Initializes an su_home_t structure. It can be used when the home
+ * structure is allocated from stack or when the home structure is part of
+ * an another object.
  *
  * @param home pointer to home object
  *
@@ -894,10 +918,14 @@
  */
 int su_home_init(su_home_t *home)
 {
+  su_block_t *sub;
+    
   if (home == NULL)
     return -1;
 
-  home->suh_blocks = su_hash_alloc(SUB_N);
+  home->suh_blocks = sub = su_hash_alloc(SUB_N);
+  if (!sub)
+    return -1;
 
   return 0;
 }
@@ -949,6 +977,9 @@
       free(b);
 
     home->suh_blocks = NULL;
+
+    if (home->suh_lock)
+      su_home_destroy_mutexes(home->suh_lock);
   }
 
   home->suh_lock = NULL;
@@ -956,9 +987,9 @@
 
 /** Free memory blocks allocated through home.
  *
- * The function @c su_home_deinit() frees the memory blocks associated with
- * the home object allocated otherwise. It does not free the home object
- * itself. Use su_home_unref() to free the home object.
+ * Frees the memory blocks associated with the home object allocated. It
+ * does not free the home object itself. Use su_home_unref() to free the
+ * home object.
  *
  * @param home pointer to home object
  *
@@ -967,7 +998,9 @@
 void su_home_deinit(su_home_t *home)
 {
   if (MEMLOCK(home)) {
-    assert(home->suh_blocks && home->suh_blocks->sub_ref == 0);
+    assert(home->suh_blocks);
+    assert(home->suh_blocks->sub_ref == 1);
+    assert(home->suh_blocks->sub_hauto);
     _su_home_deinit(home);
     /* UNLOCK(home); */
   }
@@ -975,11 +1008,11 @@
 
 /**Move allocations from a su_home_t object to another.
  *
- * The function su_home_move() moves allocations made through the @a src
- * home object under the @a dst home object. It is handy, for example, if an
- * operation allocates some number of blocks that should be freed upon an
- * error. It uses a temporary home and moves the blocks from temporary to a
- * proper home when successful, but frees the temporary home upon an error.
+ * Moves allocations made through the @a src home object under the @a dst
+ * home object. It is handy, for example, if an operation allocates some
+ * number of blocks that should be freed upon an error. It uses a temporary
+ * home and moves the blocks from temporary to a proper home when
+ * successful, but frees the temporary home upon an error.
  *
  * If @a src has destructor, it is called before starting to move.
  *
@@ -1111,8 +1144,8 @@
 
 /** Preload a memory home from stack.
  *
- * The function su_home_auto() initalizes a memory home using an area
- * allocated from stack. Poor mans alloca().
+ * Initializes a memory home using an area allocated from stack. Poor man's
+ * alloca().
  */
 su_home_t *su_home_auto(void *area, isize_t size)
 {
@@ -1142,10 +1175,12 @@
     size = prepsize + 65535;
 
   sub->sub_n = SUB_N_AUTO;
+  sub->sub_ref = 1;
   sub->sub_preload = p + prepsize;
   sub->sub_prsize = (unsigned)(size - prepsize);
-  sub->sub_preauto = 1;
+  sub->sub_hauto = 1;
   sub->sub_auto = 1;
+  sub->sub_preauto = 1;
   sub->sub_auto_all = 1;
 
   return home;
@@ -1154,7 +1189,7 @@
 
 /** Reallocate a memory block.
  *
- *   The function su_realloc() allocates a memory block of @a size bytes.
+ *   Allocates a memory block of @a size bytes.
  *   It copies the old block contents to the new block and frees the old
  *   block.
  *
@@ -1165,7 +1200,7 @@
  *   @param size  size of the memory block to be allocated
  *   
  * @return
- *   This function returns a pointer to the allocated memory block or
+ *   A pointer to the allocated memory block or
  *   NULL if an error occurred.
  */
 void *su_realloc(su_home_t *home, void *data, isize_t size)
@@ -1216,7 +1251,7 @@
 #endif
       memset(sua, 0, sizeof *sua);
       sub->sub_used--;
-      su_block_add(sub, ndata)->sua_size = size;
+      su_block_add(sub, ndata)->sua_size = (unsigned)size;
     }
     UNLOCK(home);
 
@@ -1238,7 +1273,7 @@
       }
 
       sub->sub_prused = (unsigned)p2;
-      sua->sua_size = size;
+      sua->sua_size = (unsigned)size;
 
 #if MEMCHECK_EXTRA
       memcpy((char *)data + size, &term, sizeof (term));
@@ -1247,7 +1282,7 @@
       return data;
     }
   }
-  else if (size < sua->sua_size) {
+  else if (size < (size_t)sua->sua_size) {
     /* Reduce existing preload */
     if (sub->sub_stats) {
       su_home_stats_free(sub, data, data, sua->sua_size);
@@ -1256,7 +1291,7 @@
 #if MEMCHECK_EXTRA
     memcpy((char *)data + size, &term, sizeof (term));
 #endif
-    sua->sua_size = size;
+    sua->sua_size = (unsigned)size;
     UNLOCK(home);
     return data;
   }
@@ -1271,7 +1306,10 @@
 	su_home_stats_free(sub, data, data, sua->sua_size);
     }
     
-    memcpy(ndata, data, sua->sua_size < size ? sua->sua_size : size);
+    memcpy(ndata, data,
+	   (size_t)sua->sua_size < size
+	   ? (size_t)sua->sua_size
+	   : size);
 #if MEMCHECK_EXTRA
     memcpy((char *)ndata + size, &term, sizeof (term));
 #endif
@@ -1281,7 +1319,7 @@
 
     memset(sua, 0, sizeof *sua); sub->sub_used--;
 
-    su_block_add(sub, ndata)->sua_size = size;
+    su_block_add(sub, ndata)->sua_size = (unsigned)size;
   }
 
   UNLOCK(home);
@@ -1327,7 +1365,7 @@
 
 /**Allocate and zero a memory block.
  *
- * The function su_zalloc() allocates a memory block with a given size from
+ * Allocates a memory block with a given size from
  * given memory home @a home and zeroes the allocated block.
  *
  *  @param home  pointer to memory pool object
@@ -1359,9 +1397,9 @@
 
 /** Allocate a structure
  *
- * The function su_salloc() allocates a structure with a given size, zeros
+ * Allocates a structure with a given size, zeros
  * it, and initializes the size field to the given size.  The size field
- * is the first in the structure. It has type of int. 
+ * is an int at the beginning of the structure. Note that it has type of int. 
  *
  * @param home  pointer to memory pool object
  * @param size  size of the structure
@@ -1540,7 +1578,8 @@
 }
 
 static 
-void su_home_stats_free(su_block_t *sub, void *p, void *preload, size_t size)
+void su_home_stats_free(su_block_t *sub, void *p, void *preload,
+			unsigned size)
 {
   su_home_stat_t *hs = sub->sub_stats;
 

Modified: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc_lock.c
==============================================================================
--- freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc_lock.c	(original)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_alloc_lock.c	Tue Mar 20 23:37:15 2007
@@ -39,6 +39,7 @@
 #if SU_HAVE_PTHREADS
 #include <pthread.h>
 #include <assert.h>
+#include <stdlib.h>
 
 extern void (*su_home_locker)(void *mutex);
 extern void (*su_home_unlocker)(void *mutex);
@@ -46,6 +47,8 @@
 extern void (*su_home_mutex_locker)(void *mutex);
 extern void (*su_home_mutex_unlocker)(void *mutex);
 
+extern void (*su_home_destroy_mutexes)(void *mutex);
+
 /** Mutex */
 static void mutex_locker(void *_mutex)
 {
@@ -58,6 +61,15 @@
   pthread_mutex_t *mutex = _mutex;
   pthread_mutex_unlock(mutex + 1);
 }
+
+static void mutex_destroy(void *_mutex)
+{
+  pthread_mutex_t *mutex = _mutex;
+  pthread_mutex_destroy(mutex + 0);
+  pthread_mutex_destroy(mutex + 1);
+  free(_mutex);
+}
+
 #endif
 
 
@@ -65,7 +77,7 @@
  *
  * Convert a memory home object as thread-safe by allocating mutexes and
  * modifying function pointers in su_alloc.c module.
-
+ *
  * @param home memory home object to be converted thread-safe.
  *
  * @retval 0 when successful,
@@ -94,9 +106,10 @@
     su_home_mutex_unlocker = mutex_unlocker;
     su_home_locker = (void (*)(void *))pthread_mutex_lock;
     su_home_unlocker = (void (*)(void *))pthread_mutex_unlock;
+    su_home_destroy_mutexes = mutex_destroy;
   }
 
-  mutex = su_alloc(home, 2 * sizeof (pthread_mutex_t));
+  mutex = calloc(1, 2 * (sizeof *mutex));
   assert(mutex);
   if (mutex) {
     /* Mutex for memory operations */

Added: freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/mikej/sofiasip-upgrade/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c	Tue Mar 20 23:37:15 2007
@@ -0,0 +1,577 @@
+/*
+ * This file is part of the Sofia-SIP package
+ *
+ * Copyright (C) 2005 Nokia Corporation.
+ *
+ * Contact: Pekka Pessi <pekka.pessi at nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/**@ingroup su_wait
+ * @CFILE su_base_port.c
+ *
+ * OS-Independent Socket Syncronization Interface.
+ *
+ * This looks like nth reincarnation of "reactor".  It implements the
+ * poll/select/WaitForMultipleObjects and message passing functionality.
+ *
+ * @author Pekka Pessi <Pekka.Pessi at nokia.com>
+ * @author Kai Vehmanen <kai.vehmanen at nokia.com>
+ *
+ * @date Created: Tue Sep 14 15:51:04 1999 ppessi
+ */
+
+#include "config.h"
+
+#define su_base_port_s su_port_s
+#define SU_CLONE_T su_msg_t 
+
+#include "sofia-sip/su.h"
+#include "su_port.h"
+#include "sofia-sip/su_alloc.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#if 1
+#define PORT_REFCOUNT_DEBUG(x) ((void)0)
+#else
+#define PORT_REFCOUNT_DEBUG(x)  printf x
+#endif
+
+static int su_base_port_execute_msgs(su_msg_t *queue);
+
+/**@internal
+ *
+ * Initialize a message port.
+ *
+ * @retval 0 when successful
+ * @retval -1 upon an error
+ */
+int su_base_port_init(su_port_t *self, su_port_vtable_t const *vtable)
+{
+  if (self) {
+    self->sup_vtable = vtable;
+    self->sup_tail = &self->sup_head;
+
+    return 0;
+  }
+
+  return -1;
+}
+
+/** @internal Deinit a base implementation of port. */
+void su_base_port_deinit(su_port_t *self)
+{
+}
+
+void su_base_port_lock(su_port_t *self, char const *who)
+{
+}
+
+void su_base_port_unlock(su_port_t *self, char const *who)
+{
+}
+
+int su_base_port_own_thread(su_port_t const *self)
+{
+  return 1;
+}
+
+void su_base_port_incref(su_port_t *self, char const *who)
+{
+  su_home_ref(self->sup_home);
+  PORT_REFCOUNT_DEBUG(("incref(%p) to %u by %s\n", self, 
+		       su_home_refcount(self->sup_home), who));
+}
+
+int su_base_port_decref(su_port_t *self, int blocking, char const *who)
+{
+  int zapped = su_home_unref(self->sup_home);
+
+  PORT_REFCOUNT_DEBUG(("%s(%p) to %u%s by %s\n", 
+		       blocking ? "zapref" : "decref",
+		       self, zapped ? 0 : su_home_refcount(self->sup_home),
+		       blocking && !zapped ? " FAILED" :"",
+		       who));
+
+  /* We should block until all references are destroyed */
+  if (blocking) 
+    /* ...but we just abort() */
+    assert(zapped);
+
+  return zapped;
+}
+
+struct _GSource *su_base_port_gsource(su_port_t *self)
+{
+  return NULL;
+}
+
+/** @internal Send a message to the port.
+ *
+ * @retval 1 if port thread needs to be woken
+ * @retval 0 if there are other messages in queue, too
+ * @retval -1 upon an error
+ */
+int su_base_port_send(su_port_t *self, su_msg_r rmsg)
+{
+  if (self) {
+    int wakeup;
+
+    su_port_lock(self, "su_port_send");
+    
+    wakeup = self->sup_head == NULL;
+
+    *self->sup_tail = rmsg[0]; rmsg[0] = NULL;
+    self->sup_tail = &(*self->sup_tail)->sum_next;
+
+    su_port_unlock(self, "su_port_send");
+
+    return wakeup;
+  }
+  else {
+    su_msg_destroy(rmsg);
+    return -1;
+  }
+}
+
+/** @internal
+ * Execute the messages in the incoming queue.
+ *
+ * @param self - pointer to a port object
+ *
+ * @retval Number of messages executed
+ */
+int su_base_port_getmsgs(su_port_t *self)
+{
+  if (self->sup_head) {
+    su_msg_t *queue;
+
+    su_port_lock(self, "su_base_port_getmsgs");
+
+    queue = self->sup_head;
+    self->sup_tail = &self->sup_head;
+    self->sup_head = NULL;
+
+    su_port_unlock(self, "su_base_port_getmsgs");
+
+    return su_base_port_execute_msgs(queue);
+  }
+
+  return 0;
+}
+
+
+int su_base_port_getmsgs_from(su_port_t *self, su_port_t *from)
+{
+  su_msg_t *msg, *selected;
+  su_msg_t **next = &self->sup_head, **tail= &selected;
+
+  if (!*next)
+    return 0;
+
+  su_port_lock(self, "su_base_port_getmsgs_from_port");
+
+  while (*next) {
+    msg = *next;
+
+    if (msg->sum_from->sut_port == from) {
+      *tail = msg, *next = msg->sum_next, tail = &msg->sum_next;
+    }
+    else
+      next = &msg->sum_next;      
+  }
+
+  *tail = NULL, self->sup_tail = next;
+
+  su_port_unlock(self, "su_base_port_getmsgs_from_port");
+
+  return su_base_port_execute_msgs(selected);
+}
+
+static
+int su_base_port_getmsgs_of_root(su_port_t *self, su_root_t *root)
+{
+  su_msg_t *msg, *selected;
+  su_msg_t **next = &self->sup_head, **tail= &selected;
+
+  if (!*next)
+    return 0;
+
+  su_port_lock(self, "su_base_port_getmsgs_of_root");
+
+  while (*next) {
+    msg = *next;
+
+    if (msg->sum_from->sut_root == root ||
+	msg->sum_to->sut_root == root) {
+      *tail = msg, *next = msg->sum_next, tail = &msg->sum_next;
+    }
+    else
+      next = &msg->sum_next;      
+  }
+
+  *tail = NULL, self->sup_tail = next;
+  
+  su_port_unlock(self, "su_base_port_getmsgs_of_root");
+
+  return su_base_port_execute_msgs(selected);
+}
+
+static int su_base_port_execute_msgs(su_msg_t *queue)
+{
+  su_msg_t *msg;
+  int n = 0;
+
+  for (msg = queue; msg; msg = queue) {
+    su_msg_f f = msg->sum_func;
+
+    queue = msg->sum_next, msg->sum_next = NULL;
+
+    if (f) 
+      f(SU_ROOT_MAGIC(msg->sum_to->sut_root), &msg, msg->sum_data);
+    su_msg_delivery_report(&msg);
+    n++;
+  }
+
+  return n;
+}
+
+/** @internal Enable multishot mode.
+ *
+ * The function su_port_multishot() enables, disables or queries the
+ * multishot mode for the port. The multishot mode determines how the events
+ * are scheduled by port. If multishot mode is enabled, port serves all the
+ * sockets that have received network events. If it is disabled, the
+ * socket events are server one at a time.
+ *
+ * @param self      pointer to port object
+ * @param multishot multishot mode (0 => disables, 1 => enables, -1 => query)
+ * 
+ * @retval 0 multishot mode is disabled
+ * @retval 1 multishot mode is enabled
+ * @retval -1 an error occurred
+ */
+int su_base_port_multishot(su_port_t *self, int multishot)
+{
+  return 0;
+}
+
+/** @internal Enable threadsafe operation. */
+int su_base_port_threadsafe(su_port_t *self)
+{
+  return su_home_threadsafe(self->sup_home);
+}
+
+/** @internal Main loop.
+ * 
+ * The function @c su_port_run() waits for wait objects and the timers
+ * associated with the port object.  When any wait object is signaled or
+ * timer is expired, it invokes the callbacks, and returns waiting.
+ * 
+ * The function @c su_port_run() runs until @c su_port_break() is called
+ * from a callback.
+ * 
+ * @param self     pointer to port object
+ * 
+ */
+void su_base_port_run(su_port_t *self)
+{
+  su_duration_t tout = 0;
+
+  assert(su_port_own_thread(self));
+
+  for (self->sup_running = 1; self->sup_running;) {
+    tout = 2000;
+
+    if (self->sup_prepoll)
+      self->sup_prepoll(self->sup_pp_magic, self->sup_pp_root);
+
+    if (self->sup_head)
+      self->sup_vtable->su_port_getmsgs(self);
+
+    if (self->sup_timers)
+      su_timer_expire(&self->sup_timers, &tout, su_now());
+
+    if (!self->sup_running)
+      break;
+
+    if (self->sup_head)      /* if there are messages do a quick wait */
+      tout = 0;
+
+    self->sup_vtable->su_port_wait_events(self, tout);
+  }
+}
+
+#if tuning
+/* This version can help tuning... */
+void su_base_port_run_tune(su_port_t *self)
+{
+  int i;
+  int timers = 0, messages = 0, events = 0;
+  su_duration_t tout = 0, tout0;
+  su_time_t started = su_now(), woken = started, bedtime = woken;
+
+  assert(su_port_own_thread(self));
+
+  for (self->sup_running = 1; self->sup_running;) {
+    tout = 2000;
+
+    timers = 0, messages = 0;
+
+    if (self->sup_prepoll)
+      self->sup_prepoll(self->sup_pp_magic, self->sup_pp_root);
+
+    if (self->sup_head)
+      messages = self->sup_vtable->su_port_getmsgs(self);
+
+    if (self->sup_timers)
+      timers = su_timer_expire(&self->sup_timers, &tout, su_now());
+
+    if (!self->sup_running)
+      break;
+
+    if (self->sup_head)      /* if there are messages do a qui